Skip to content

Commit 464245b

Browse files
authored
Improve OpenAPI documentation for session API (#10741)
1 parent 1339b3b commit 464245b

File tree

2 files changed

+85
-31
lines changed

2 files changed

+85
-31
lines changed

src/controllers/session.rs

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
use axum::Json;
22
use axum::extract::{FromRequestParts, Query};
3-
use axum_extra::json;
4-
use axum_extra::response::ErasedJson;
53
use diesel::prelude::*;
64
use diesel_async::scoped_futures::ScopedFutureExt;
75
use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl};
@@ -20,28 +18,30 @@ use crate::views::EncodableMe;
2018
use crates_io_github::GithubUser;
2119
use crates_io_session::SessionExtension;
2220

21+
#[derive(Debug, Serialize, utoipa::ToSchema)]
22+
pub struct BeginResponse {
23+
#[schema(
24+
example = "https://github.com/login/oauth/authorize?client_id=...&state=...&scope=read%3Aorg"
25+
)]
26+
pub url: String,
27+
28+
#[schema(example = "b84a63c4ea3fcb4ac84")]
29+
pub state: String,
30+
}
31+
2332
/// Begin authentication flow.
2433
///
2534
/// This route will return an authorization URL for the GitHub OAuth flow including the crates.io
2635
/// `client_id` and a randomly generated `state` secret.
2736
///
2837
/// see <https://developer.github.com/v3/oauth/#redirect-users-to-request-github-access>
29-
///
30-
/// ## Response Body Example
31-
///
32-
/// ```json
33-
/// {
34-
/// "state": "b84a63c4ea3fcb4ac84",
35-
/// "url": "https://github.com/login/oauth/authorize?client_id=...&state=...&scope=read%3Aorg"
36-
/// }
37-
/// ```
3838
#[utoipa::path(
3939
get,
4040
path = "/api/private/session/begin",
4141
tag = "session",
42-
responses((status = 200, description = "Successful Response")),
42+
responses((status = 200, description = "Successful Response", body = inline(BeginResponse))),
4343
)]
44-
pub async fn begin_session(app: AppState, session: SessionExtension) -> ErasedJson {
44+
pub async fn begin_session(app: AppState, session: SessionExtension) -> Json<BeginResponse> {
4545
let (url, state) = app
4646
.github_oauth
4747
.authorize_url(oauth2::CsrfToken::new_random)
@@ -51,7 +51,8 @@ pub async fn begin_session(app: AppState, session: SessionExtension) -> ErasedJs
5151
let state = state.secret().to_string();
5252
session.insert("github_oauth_state".to_string(), state.clone());
5353

54-
json!({ "url": url.to_string(), "state": state })
54+
let url = url.to_string();
55+
Json(BeginResponse { url, state })
5556
}
5657

5758
#[derive(Clone, Debug, Deserialize, FromRequestParts)]
@@ -74,25 +75,11 @@ pub struct AuthorizeQuery {
7475
///
7576
/// - `code` – temporary code received from the GitHub API **(Required)**
7677
/// - `state` – state parameter received from the GitHub API **(Required)**
77-
///
78-
/// ## Response Body Example
79-
///
80-
/// ```json
81-
/// {
82-
/// "user": {
83-
/// "email": "foo@bar.org",
84-
/// "name": "Foo Bar",
85-
/// "login": "foobar",
86-
/// "avatar": "https://avatars.githubusercontent.com/u/1234",
87-
/// "url": null
88-
/// }
89-
/// }
90-
/// ```
9178
#[utoipa::path(
9279
get,
9380
path = "/api/private/session/authorize",
9481
tag = "session",
95-
responses((status = 200, description = "Successful Response")),
82+
responses((status = 200, description = "Successful Response", body = inline(EncodableMe))),
9683
)]
9784
pub async fn authorize_session(
9885
query: AuthorizeQuery,

src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,10 +1174,56 @@ expression: response.json()
11741174
},
11751175
"/api/private/session/authorize": {
11761176
"get": {
1177-
"description": "This route is called from the GitHub API OAuth flow after the user accepted or rejected\nthe data access permissions. It will check the `state` parameter and then call the GitHub API\nto exchange the temporary `code` for an API token. The API token is returned together with\nthe corresponding user information.\n\nsee <https://developer.github.com/v3/oauth/#github-redirects-back-to-your-site>\n\n## Query Parameters\n\n- `code` – temporary code received from the GitHub API **(Required)**\n- `state` – state parameter received from the GitHub API **(Required)**\n\n## Response Body Example\n\n```json\n{\n \"user\": {\n \"email\": \"foo@bar.org\",\n \"name\": \"Foo Bar\",\n \"login\": \"foobar\",\n \"avatar\": \"https://avatars.githubusercontent.com/u/1234\",\n \"url\": null\n }\n}\n```",
1177+
"description": "This route is called from the GitHub API OAuth flow after the user accepted or rejected\nthe data access permissions. It will check the `state` parameter and then call the GitHub API\nto exchange the temporary `code` for an API token. The API token is returned together with\nthe corresponding user information.\n\nsee <https://developer.github.com/v3/oauth/#github-redirects-back-to-your-site>\n\n## Query Parameters\n\n- `code` – temporary code received from the GitHub API **(Required)**\n- `state` – state parameter received from the GitHub API **(Required)**",
11781178
"operationId": "authorize_session",
11791179
"responses": {
11801180
"200": {
1181+
"content": {
1182+
"application/json": {
1183+
"schema": {
1184+
"properties": {
1185+
"owned_crates": {
1186+
"description": "The crates that the authenticated user owns.",
1187+
"items": {
1188+
"properties": {
1189+
"email_notifications": {
1190+
"deprecated": true,
1191+
"type": "boolean"
1192+
},
1193+
"id": {
1194+
"description": "The opaque identifier of the crate.",
1195+
"example": 123,
1196+
"format": "int32",
1197+
"type": "integer"
1198+
},
1199+
"name": {
1200+
"description": "The name of the crate.",
1201+
"example": "serde",
1202+
"type": "string"
1203+
}
1204+
},
1205+
"required": [
1206+
"id",
1207+
"name",
1208+
"email_notifications"
1209+
],
1210+
"type": "object"
1211+
},
1212+
"type": "array"
1213+
},
1214+
"user": {
1215+
"$ref": "#/components/schemas/AuthenticatedUser",
1216+
"description": "The authenticated user."
1217+
}
1218+
},
1219+
"required": [
1220+
"user",
1221+
"owned_crates"
1222+
],
1223+
"type": "object"
1224+
}
1225+
}
1226+
},
11811227
"description": "Successful Response"
11821228
}
11831229
},
@@ -1189,10 +1235,31 @@ expression: response.json()
11891235
},
11901236
"/api/private/session/begin": {
11911237
"get": {
1192-
"description": "This route will return an authorization URL for the GitHub OAuth flow including the crates.io\n`client_id` and a randomly generated `state` secret.\n\nsee <https://developer.github.com/v3/oauth/#redirect-users-to-request-github-access>\n\n## Response Body Example\n\n```json\n{\n \"state\": \"b84a63c4ea3fcb4ac84\",\n \"url\": \"https://github.com/login/oauth/authorize?client_id=...&state=...&scope=read%3Aorg\"\n}\n```",
1238+
"description": "This route will return an authorization URL for the GitHub OAuth flow including the crates.io\n`client_id` and a randomly generated `state` secret.\n\nsee <https://developer.github.com/v3/oauth/#redirect-users-to-request-github-access>",
11931239
"operationId": "begin_session",
11941240
"responses": {
11951241
"200": {
1242+
"content": {
1243+
"application/json": {
1244+
"schema": {
1245+
"properties": {
1246+
"state": {
1247+
"example": "b84a63c4ea3fcb4ac84",
1248+
"type": "string"
1249+
},
1250+
"url": {
1251+
"example": "https://github.com/login/oauth/authorize?client_id=...&state=...&scope=read%3Aorg",
1252+
"type": "string"
1253+
}
1254+
},
1255+
"required": [
1256+
"url",
1257+
"state"
1258+
],
1259+
"type": "object"
1260+
}
1261+
}
1262+
},
11961263
"description": "Successful Response"
11971264
}
11981265
},

0 commit comments

Comments
 (0)