mas_handlers/admin/v1/user_registration_tokens/
get.rs1use aide::{OperationIo, transform::TransformOperation};
8use axum::{Json, response::IntoResponse};
9use hyper::StatusCode;
10use mas_axum_utils::record_error;
11use ulid::Ulid;
12
13use crate::{
14 admin::{
15 call_context::CallContext,
16 model::UserRegistrationToken,
17 params::UlidPathParam,
18 response::{ErrorResponse, SingleResponse},
19 },
20 impl_from_error_for_route,
21};
22
23#[derive(Debug, thiserror::Error, OperationIo)]
24#[aide(output_with = "Json<ErrorResponse>")]
25pub enum RouteError {
26 #[error(transparent)]
27 Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
28
29 #[error("Registration token with ID {0} not found")]
30 NotFound(Ulid),
31}
32
33impl_from_error_for_route!(mas_storage::RepositoryError);
34
35impl IntoResponse for RouteError {
36 fn into_response(self) -> axum::response::Response {
37 let error = ErrorResponse::from_error(&self);
38 let sentry_event_id = record_error!(self, Self::Internal(_));
39 let status = match self {
40 Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
41 Self::NotFound(_) => StatusCode::NOT_FOUND,
42 };
43 (status, sentry_event_id, Json(error)).into_response()
44 }
45}
46
47pub fn doc(operation: TransformOperation) -> TransformOperation {
48 operation
49 .id("getUserRegistrationToken")
50 .summary("Get a user registration token")
51 .tag("user-registration-token")
52 .response_with::<200, Json<SingleResponse<UserRegistrationToken>>, _>(|t| {
53 let [sample, ..] = UserRegistrationToken::samples();
54 let response = SingleResponse::new_canonical(sample);
55 t.description("Registration token was found")
56 .example(response)
57 })
58 .response_with::<404, RouteError, _>(|t| {
59 let response = ErrorResponse::from_error(&RouteError::NotFound(Ulid::nil()));
60 t.description("Registration token was not found")
61 .example(response)
62 })
63}
64
65#[tracing::instrument(name = "handler.admin.v1.user_registration_tokens.get", skip_all)]
66pub async fn handler(
67 CallContext {
68 mut repo, clock, ..
69 }: CallContext,
70 id: UlidPathParam,
71) -> Result<Json<SingleResponse<UserRegistrationToken>>, RouteError> {
72 let token = repo
73 .user_registration_token()
74 .lookup(*id)
75 .await?
76 .ok_or(RouteError::NotFound(*id))?;
77
78 Ok(Json(SingleResponse::new_canonical(
79 UserRegistrationToken::new(token, clock.now()),
80 )))
81}
82
83#[cfg(test)]
84mod tests {
85 use hyper::{Request, StatusCode};
86 use insta::assert_json_snapshot;
87 use sqlx::PgPool;
88 use ulid::Ulid;
89
90 use crate::test_utils::{RequestBuilderExt, ResponseExt, TestState, setup};
91
92 #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
93 async fn test_get_token(pool: PgPool) {
94 setup();
95 let mut state = TestState::from_pool(pool).await.unwrap();
96 let token = state.token_with_scope("urn:mas:admin").await;
97
98 let mut repo = state.repository().await.unwrap();
99 let registration_token = repo
100 .user_registration_token()
101 .add(
102 &mut state.rng(),
103 &state.clock,
104 "test_token_123".to_owned(),
105 Some(5),
106 None,
107 )
108 .await
109 .unwrap();
110 repo.save().await.unwrap();
111
112 let request = Request::get(format!(
113 "/api/admin/v1/user-registration-tokens/{}",
114 registration_token.id
115 ))
116 .bearer(&token)
117 .empty();
118 let response = state.request(request).await;
119 response.assert_status(StatusCode::OK);
120 let body: serde_json::Value = response.json();
121
122 assert_json_snapshot!(body, @r#"
123 {
124 "data": {
125 "type": "user-registration_token",
126 "id": "01FSHN9AG0MZAA6S4AF7CTV32E",
127 "attributes": {
128 "token": "test_token_123",
129 "valid": true,
130 "usage_limit": 5,
131 "times_used": 0,
132 "created_at": "2022-01-16T14:40:00Z",
133 "last_used_at": null,
134 "expires_at": null,
135 "revoked_at": null
136 },
137 "links": {
138 "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
139 }
140 },
141 "links": {
142 "self": "/api/admin/v1/user-registration-tokens/01FSHN9AG0MZAA6S4AF7CTV32E"
143 }
144 }
145 "#);
146 }
147
148 #[sqlx::test(migrator = "mas_storage_pg::MIGRATOR")]
149 async fn test_get_nonexistent_token(pool: PgPool) {
150 setup();
151 let mut state = TestState::from_pool(pool).await.unwrap();
152 let token = state.token_with_scope("urn:mas:admin").await;
153
154 let nonexistent_id = Ulid::from_string("00000000000000000000000000").unwrap();
156 let request = Request::get(format!(
157 "/api/admin/v1/user-registration-tokens/{nonexistent_id}"
158 ))
159 .bearer(&token)
160 .empty();
161 let response = state.request(request).await;
162 response.assert_status(StatusCode::NOT_FOUND);
163 let body: serde_json::Value = response.json();
164
165 assert_json_snapshot!(body, @r###"
166 {
167 "errors": [
168 {
169 "title": "Registration token with ID 00000000000000000000000000 not found"
170 }
171 ]
172 }
173 "###);
174 }
175}