1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-04 18:22:34 +03:00

admin: add operation IDs on user operations & other improvements

This also documents better the user list operation parameters
This commit is contained in:
Quentin Gliech
2024-07-30 17:31:35 +02:00
parent 78e988b7cc
commit d03dd41345
4 changed files with 65 additions and 38 deletions

View File

@ -58,6 +58,7 @@ pub struct UsernamePathParam {
pub fn doc(operation: TransformOperation) -> TransformOperation { pub fn doc(operation: TransformOperation) -> TransformOperation {
operation operation
.id("getUserByUsername")
.summary("Get a user by its username (localpart)") .summary("Get a user by its username (localpart)")
.tag("user") .tag("user")
.response_with::<200, Json<SingleResponse<User>>, _>(|t| { .response_with::<200, Json<SingleResponse<User>>, _>(|t| {

View File

@ -52,6 +52,7 @@ impl IntoResponse for RouteError {
pub fn doc(operation: TransformOperation) -> TransformOperation { pub fn doc(operation: TransformOperation) -> TransformOperation {
operation operation
.id("getUser")
.summary("Get a user") .summary("Get a user")
.tag("user") .tag("user")
.response_with::<200, Json<SingleResponse<User>>, _>(|t| { .response_with::<200, Json<SingleResponse<User>>, _>(|t| {

View File

@ -34,44 +34,57 @@ use crate::{
impl_from_error_for_route, impl_from_error_for_route,
}; };
#[derive(Deserialize, JsonSchema)] #[derive(Deserialize, JsonSchema, Clone, Copy)]
#[serde(rename_all = "snake_case")] #[serde(rename_all = "snake_case")]
enum UserStatus { enum UserStatus {
/// The user is active
Active, Active,
/// The user is locked
Locked, Locked,
} }
impl std::fmt::Display for UserStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Active => write!(f, "active"),
Self::Locked => write!(f, "locked"),
}
}
}
#[derive(FromRequestParts, Deserialize, JsonSchema, OperationIo)] #[derive(FromRequestParts, Deserialize, JsonSchema, OperationIo)]
#[serde(rename = "UserFilter")]
#[aide(input_with = "Query<FilterParams>")] #[aide(input_with = "Query<FilterParams>")]
#[from_request(via(Query), rejection(RouteError))] #[from_request(via(Query), rejection(RouteError))]
pub struct FilterParams { pub struct FilterParams {
/// Retrieve users with (or without) the `can_request_admin` flag set
#[serde(rename = "filter[can_request_admin]")] #[serde(rename = "filter[can_request_admin]")]
can_request_admin: Option<bool>, can_request_admin: Option<bool>,
/// Retrieve the items with the given status
///
/// Defaults to retrieve all users, including locked ones.
///
/// * `active`: Only retrieve active users
///
/// * `locked`: Only retrieve locked users
#[serde(rename = "filter[status]")] #[serde(rename = "filter[status]")]
status: Option<UserStatus>, status: Option<UserStatus>,
} }
impl<'a> From<&'a FilterParams> for UserFilter<'a> { impl std::fmt::Display for FilterParams {
fn from(val: &'a FilterParams) -> Self { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let filter = UserFilter::default(); let mut sep = '?';
let filter = match val.can_request_admin { if let Some(can_request_admin) = self.can_request_admin {
Some(true) => filter.can_request_admin_only(), write!(f, "{sep}filter[can_request_admin]={can_request_admin}")?;
Some(false) => filter.cannot_request_admin_only(), sep = '&';
None => filter, }
}; if let Some(status) = self.status {
write!(f, "{sep}filter[status]={status}")?;
sep = '&';
}
let filter = match val.status { let _ = sep;
Some(UserStatus::Active) => filter.active_only(), Ok(())
Some(UserStatus::Locked) => filter.locked_only(),
None => filter,
};
filter
} }
} }
@ -100,6 +113,7 @@ impl IntoResponse for RouteError {
pub fn doc(operation: TransformOperation) -> TransformOperation { pub fn doc(operation: TransformOperation) -> TransformOperation {
operation operation
.id("listUsers")
.summary("List users") .summary("List users")
.tag("user") .tag("user")
.response_with::<200, Json<PaginatedResponse<User>>, _>(|t| { .response_with::<200, Json<PaginatedResponse<User>>, _>(|t| {
@ -120,9 +134,22 @@ pub fn doc(operation: TransformOperation) -> TransformOperation {
pub async fn handler( pub async fn handler(
CallContext { mut repo, .. }: CallContext, CallContext { mut repo, .. }: CallContext,
Pagination(pagination): Pagination, Pagination(pagination): Pagination,
filter: FilterParams, params: FilterParams,
) -> Result<Json<PaginatedResponse<User>>, RouteError> { ) -> Result<Json<PaginatedResponse<User>>, RouteError> {
let filter = UserFilter::from(&filter); let base = format!("{path}{params}", path = User::PATH);
let filter = UserFilter::default();
let filter = match params.can_request_admin {
Some(true) => filter.can_request_admin_only(),
Some(false) => filter.cannot_request_admin_only(),
None => filter,
};
let filter = match params.status {
Some(UserStatus::Active) => filter.active_only(),
Some(UserStatus::Locked) => filter.locked_only(),
None => filter,
};
let page = repo.user().list(filter, pagination).await?; let page = repo.user().list(filter, pagination).await?;
let count = repo.user().count(filter).await?; let count = repo.user().count(filter).await?;
@ -131,6 +158,6 @@ pub async fn handler(
page.map(User::from), page.map(User::from),
pagination, pagination,
count, count,
User::PATH, &base,
))) )))
} }

View File

@ -22,6 +22,7 @@
"user" "user"
], ],
"summary": "List users", "summary": "List users",
"operationId": "listUsers",
"parameters": [ "parameters": [
{ {
"in": "query", "in": "query",
@ -74,7 +75,9 @@
{ {
"in": "query", "in": "query",
"name": "filter[can_request_admin]", "name": "filter[can_request_admin]",
"description": "Retrieve users with (or without) the `can_request_admin` flag set",
"schema": { "schema": {
"description": "Retrieve users with (or without) the `can_request_admin` flag set",
"type": "boolean", "type": "boolean",
"nullable": true "nullable": true
}, },
@ -83,7 +86,9 @@
{ {
"in": "query", "in": "query",
"name": "filter[status]", "name": "filter[status]",
"description": "Retrieve the items with the given status\n\nDefaults to retrieve all users, including locked ones.\n\n* `active`: Only retrieve active users\n\n* `locked`: Only retrieve locked users",
"schema": { "schema": {
"description": "Retrieve the items with the given status\n\nDefaults to retrieve all users, including locked ones.\n\n* `active`: Only retrieve active users\n\n* `locked`: Only retrieve locked users",
"$ref": "#/components/schemas/UserStatus", "$ref": "#/components/schemas/UserStatus",
"nullable": true "nullable": true
}, },
@ -162,6 +167,7 @@
"user" "user"
], ],
"summary": "Get a user", "summary": "Get a user",
"operationId": "getUser",
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
@ -229,6 +235,7 @@
"user" "user"
], ],
"summary": "Get a user by its username (localpart)", "summary": "Get a user by its username (localpart)",
"operationId": "getUserByUsername",
"parameters": [ "parameters": [
{ {
"in": "path", "in": "path",
@ -355,36 +362,27 @@
"type": "string", "type": "string",
"pattern": "^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$" "pattern": "^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$"
}, },
"FilterParams": { "UserFilter": {
"type": "object", "type": "object",
"properties": { "properties": {
"filter[can_request_admin]": { "filter[can_request_admin]": {
"description": "Retrieve users with (or without) the `can_request_admin` flag set",
"type": "boolean", "type": "boolean",
"nullable": true "nullable": true
}, },
"filter[status]": { "filter[status]": {
"description": "Retrieve the items with the given status\n\nDefaults to retrieve all users, including locked ones.\n\n* `active`: Only retrieve active users\n\n* `locked`: Only retrieve locked users",
"$ref": "#/components/schemas/UserStatus", "$ref": "#/components/schemas/UserStatus",
"nullable": true "nullable": true
} }
} }
}, },
"UserStatus": { "UserStatus": {
"oneOf": [
{
"description": "The user is active",
"type": "string",
"enum": [
"active"
]
},
{
"description": "The user is locked",
"type": "string", "type": "string",
"enum": [ "enum": [
"active",
"locked" "locked"
] ]
}
]
}, },
"PaginatedResponse_for_User": { "PaginatedResponse_for_User": {
"description": "A top-level response with a page of resources", "description": "A top-level response with a page of resources",