diff --git a/crates/handlers/src/admin/v1/users/by_username.rs b/crates/handlers/src/admin/v1/users/by_username.rs index 55803228..80026744 100644 --- a/crates/handlers/src/admin/v1/users/by_username.rs +++ b/crates/handlers/src/admin/v1/users/by_username.rs @@ -58,6 +58,7 @@ pub struct UsernamePathParam { pub fn doc(operation: TransformOperation) -> TransformOperation { operation + .id("getUserByUsername") .summary("Get a user by its username (localpart)") .tag("user") .response_with::<200, Json>, _>(|t| { diff --git a/crates/handlers/src/admin/v1/users/get.rs b/crates/handlers/src/admin/v1/users/get.rs index 8e848359..0c824f45 100644 --- a/crates/handlers/src/admin/v1/users/get.rs +++ b/crates/handlers/src/admin/v1/users/get.rs @@ -52,6 +52,7 @@ impl IntoResponse for RouteError { pub fn doc(operation: TransformOperation) -> TransformOperation { operation + .id("getUser") .summary("Get a user") .tag("user") .response_with::<200, Json>, _>(|t| { diff --git a/crates/handlers/src/admin/v1/users/list.rs b/crates/handlers/src/admin/v1/users/list.rs index d5dc46df..4c3fbb53 100644 --- a/crates/handlers/src/admin/v1/users/list.rs +++ b/crates/handlers/src/admin/v1/users/list.rs @@ -34,44 +34,57 @@ use crate::{ impl_from_error_for_route, }; -#[derive(Deserialize, JsonSchema)] +#[derive(Deserialize, JsonSchema, Clone, Copy)] #[serde(rename_all = "snake_case")] enum UserStatus { - /// The user is active Active, - - /// The user is 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)] +#[serde(rename = "UserFilter")] #[aide(input_with = "Query")] #[from_request(via(Query), rejection(RouteError))] pub struct FilterParams { + /// Retrieve users with (or without) the `can_request_admin` flag set #[serde(rename = "filter[can_request_admin]")] can_request_admin: Option, + /// 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]")] status: Option, } -impl<'a> From<&'a FilterParams> for UserFilter<'a> { - fn from(val: &'a FilterParams) -> Self { - let filter = UserFilter::default(); +impl std::fmt::Display for FilterParams { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut sep = '?'; - let filter = match val.can_request_admin { - Some(true) => filter.can_request_admin_only(), - Some(false) => filter.cannot_request_admin_only(), - None => filter, - }; + if let Some(can_request_admin) = self.can_request_admin { + write!(f, "{sep}filter[can_request_admin]={can_request_admin}")?; + sep = '&'; + } + if let Some(status) = self.status { + write!(f, "{sep}filter[status]={status}")?; + sep = '&'; + } - let filter = match val.status { - Some(UserStatus::Active) => filter.active_only(), - Some(UserStatus::Locked) => filter.locked_only(), - None => filter, - }; - - filter + let _ = sep; + Ok(()) } } @@ -100,6 +113,7 @@ impl IntoResponse for RouteError { pub fn doc(operation: TransformOperation) -> TransformOperation { operation + .id("listUsers") .summary("List users") .tag("user") .response_with::<200, Json>, _>(|t| { @@ -120,9 +134,22 @@ pub fn doc(operation: TransformOperation) -> TransformOperation { pub async fn handler( CallContext { mut repo, .. }: CallContext, Pagination(pagination): Pagination, - filter: FilterParams, + params: FilterParams, ) -> Result>, 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 count = repo.user().count(filter).await?; @@ -131,6 +158,6 @@ pub async fn handler( page.map(User::from), pagination, count, - User::PATH, + &base, ))) } diff --git a/docs/api/spec.json b/docs/api/spec.json index 23f10d96..b5684435 100644 --- a/docs/api/spec.json +++ b/docs/api/spec.json @@ -22,6 +22,7 @@ "user" ], "summary": "List users", + "operationId": "listUsers", "parameters": [ { "in": "query", @@ -74,7 +75,9 @@ { "in": "query", "name": "filter[can_request_admin]", + "description": "Retrieve users with (or without) the `can_request_admin` flag set", "schema": { + "description": "Retrieve users with (or without) the `can_request_admin` flag set", "type": "boolean", "nullable": true }, @@ -83,7 +86,9 @@ { "in": "query", "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": { + "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", "nullable": true }, @@ -162,6 +167,7 @@ "user" ], "summary": "Get a user", + "operationId": "getUser", "parameters": [ { "in": "path", @@ -229,6 +235,7 @@ "user" ], "summary": "Get a user by its username (localpart)", + "operationId": "getUserByUsername", "parameters": [ { "in": "path", @@ -355,35 +362,26 @@ "type": "string", "pattern": "^[0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26}$" }, - "FilterParams": { + "UserFilter": { "type": "object", "properties": { "filter[can_request_admin]": { + "description": "Retrieve users with (or without) the `can_request_admin` flag set", "type": "boolean", "nullable": true }, "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", "nullable": true } } }, "UserStatus": { - "oneOf": [ - { - "description": "The user is active", - "type": "string", - "enum": [ - "active" - ] - }, - { - "description": "The user is locked", - "type": "string", - "enum": [ - "locked" - ] - } + "type": "string", + "enum": [ + "active", + "locked" ] }, "PaginatedResponse_for_User": {