Skip to content

Commit cb04e06

Browse files
authored
Merge pull request #10725 from Turbo87/openapi
Improve OpenAPI documentation
2 parents a696983 + f4305ab commit cb04e06

File tree

8 files changed

+582
-72
lines changed

8 files changed

+582
-72
lines changed

src/controllers/crate_owner_invitation.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ use crate::views::{
1414
};
1515
use axum::Json;
1616
use axum::extract::{FromRequestParts, Path, Query};
17-
use axum_extra::json;
18-
use axum_extra::response::ErasedJson;
1917
use chrono::Utc;
2018
use diesel::pg::Pg;
2119
use diesel::prelude::*;
@@ -329,6 +327,12 @@ pub struct OwnerInvitation {
329327
crate_owner_invite: InvitationResponse,
330328
}
331329

330+
#[derive(Debug, Serialize, utoipa::ToSchema)]
331+
pub struct HandleResponse {
332+
#[schema(inline)]
333+
crate_owner_invitation: InvitationResponse,
334+
}
335+
332336
/// Accept or decline a crate owner invitation.
333337
#[utoipa::path(
334338
put,
@@ -341,13 +345,13 @@ pub struct OwnerInvitation {
341345
("cookie" = []),
342346
),
343347
tag = "owners",
344-
responses((status = 200, description = "Successful Response")),
348+
responses((status = 200, description = "Successful Response", body = inline(HandleResponse))),
345349
)]
346350
pub async fn handle_crate_owner_invitation(
347351
state: AppState,
348352
parts: Parts,
349353
Json(crate_invite): Json<OwnerInvitation>,
350-
) -> AppResult<ErasedJson> {
354+
) -> AppResult<Json<HandleResponse>> {
351355
let crate_invite = crate_invite.crate_owner_invite;
352356

353357
let mut conn = state.db_write().await?;
@@ -364,7 +368,9 @@ pub async fn handle_crate_owner_invitation(
364368
invitation.decline(&mut conn).await?;
365369
}
366370

367-
Ok(json!({ "crate_owner_invitation": crate_invite }))
371+
Ok(Json(HandleResponse {
372+
crate_owner_invitation: crate_invite,
373+
}))
368374
}
369375

370376
/// Accept a crate owner invitation with a token.
@@ -375,23 +381,25 @@ pub async fn handle_crate_owner_invitation(
375381
("token" = String, Path, description = "Secret token sent to the user's email address"),
376382
),
377383
tag = "owners",
378-
responses((status = 200, description = "Successful Response")),
384+
responses((status = 200, description = "Successful Response", body = inline(HandleResponse))),
379385
)]
380386
pub async fn accept_crate_owner_invitation_with_token(
381387
state: AppState,
382388
Path(token): Path<String>,
383-
) -> AppResult<ErasedJson> {
389+
) -> AppResult<Json<HandleResponse>> {
384390
let mut conn = state.db_write().await?;
385391
let invitation = CrateOwnerInvitation::find_by_token(&token, &mut conn).await?;
386392

387393
let crate_id = invitation.crate_id;
388394
invitation.accept(&mut conn).await?;
389395

390-
Ok(json!({
391-
"crate_owner_invitation": {
392-
"crate_id": crate_id,
393-
"accepted": true,
394-
},
396+
let crate_owner_invitation = InvitationResponse {
397+
crate_id,
398+
accepted: true,
399+
};
400+
401+
Ok(Json(HandleResponse {
402+
crate_owner_invitation,
395403
}))
396404
}
397405

src/controllers/krate/owners.rs

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ use crate::views::EncodableOwner;
1313
use crate::{App, app::AppState};
1414
use crate::{auth::AuthCheck, email::Email};
1515
use axum::Json;
16-
use axum_extra::json;
17-
use axum_extra::response::ErasedJson;
1816
use chrono::Utc;
1917
use crates_io_github::{GitHubClient, GitHubError};
2018
use diesel::prelude::*;
@@ -26,27 +24,37 @@ use oauth2::AccessToken;
2624
use secrecy::{ExposeSecret, SecretString};
2725
use thiserror::Error;
2826

27+
#[derive(Debug, Serialize, utoipa::ToSchema)]
28+
pub struct UsersResponse {
29+
pub users: Vec<EncodableOwner>,
30+
}
31+
2932
/// List crate owners.
3033
#[utoipa::path(
3134
get,
3235
path = "/api/v1/crates/{name}/owners",
3336
params(CratePath),
3437
tag = "owners",
35-
responses((status = 200, description = "Successful Response")),
38+
responses((status = 200, description = "Successful Response", body = inline(UsersResponse))),
3639
)]
37-
pub async fn list_owners(state: AppState, path: CratePath) -> AppResult<ErasedJson> {
40+
pub async fn list_owners(state: AppState, path: CratePath) -> AppResult<Json<UsersResponse>> {
3841
let mut conn = state.db_read().await?;
3942

4043
let krate = path.load_crate(&mut conn).await?;
4144

42-
let owners = krate
45+
let users = krate
4346
.owners(&mut conn)
4447
.await?
4548
.into_iter()
4649
.map(Owner::into)
4750
.collect::<Vec<EncodableOwner>>();
4851

49-
Ok(json!({ "users": owners }))
52+
Ok(Json(UsersResponse { users }))
53+
}
54+
55+
#[derive(Debug, Serialize, utoipa::ToSchema)]
56+
pub struct TeamsResponse {
57+
pub teams: Vec<EncodableOwner>,
5058
}
5159

5260
/// List team owners of a crate.
@@ -55,19 +63,19 @@ pub async fn list_owners(state: AppState, path: CratePath) -> AppResult<ErasedJs
5563
path = "/api/v1/crates/{name}/owner_team",
5664
params(CratePath),
5765
tag = "owners",
58-
responses((status = 200, description = "Successful Response")),
66+
responses((status = 200, description = "Successful Response", body = inline(TeamsResponse))),
5967
)]
60-
pub async fn get_team_owners(state: AppState, path: CratePath) -> AppResult<ErasedJson> {
68+
pub async fn get_team_owners(state: AppState, path: CratePath) -> AppResult<Json<TeamsResponse>> {
6169
let mut conn = state.db_read().await?;
6270
let krate = path.load_crate(&mut conn).await?;
6371

64-
let owners = Team::owning(&krate, &mut conn)
72+
let teams = Team::owning(&krate, &mut conn)
6573
.await?
6674
.into_iter()
6775
.map(Owner::into)
6876
.collect::<Vec<EncodableOwner>>();
6977

70-
Ok(json!({ "teams": owners }))
78+
Ok(Json(TeamsResponse { teams }))
7179
}
7280

7381
/// List user owners of a crate.
@@ -76,20 +84,30 @@ pub async fn get_team_owners(state: AppState, path: CratePath) -> AppResult<Eras
7684
path = "/api/v1/crates/{name}/owner_user",
7785
params(CratePath),
7886
tag = "owners",
79-
responses((status = 200, description = "Successful Response")),
87+
responses((status = 200, description = "Successful Response", body = inline(UsersResponse))),
8088
)]
81-
pub async fn get_user_owners(state: AppState, path: CratePath) -> AppResult<ErasedJson> {
89+
pub async fn get_user_owners(state: AppState, path: CratePath) -> AppResult<Json<UsersResponse>> {
8290
let mut conn = state.db_read().await?;
8391

8492
let krate = path.load_crate(&mut conn).await?;
8593

86-
let owners = User::owning(&krate, &mut conn)
94+
let users = User::owning(&krate, &mut conn)
8795
.await?
8896
.into_iter()
8997
.map(Owner::into)
9098
.collect::<Vec<EncodableOwner>>();
9199

92-
Ok(json!({ "users": owners }))
100+
Ok(Json(UsersResponse { users }))
101+
}
102+
103+
#[derive(Debug, Serialize, utoipa::ToSchema)]
104+
pub struct ModifyResponse {
105+
/// A message describing the result of the operation.
106+
#[schema(example = "user ghost has been invited to be an owner of crate serde")]
107+
pub msg: String,
108+
109+
#[schema(example = true)]
110+
pub ok: bool,
93111
}
94112

95113
/// Add crate owners.
@@ -102,14 +120,14 @@ pub async fn get_user_owners(state: AppState, path: CratePath) -> AppResult<Eras
102120
("cookie" = []),
103121
),
104122
tag = "owners",
105-
responses((status = 200, description = "Successful Response")),
123+
responses((status = 200, description = "Successful Response", body = inline(ModifyResponse))),
106124
)]
107125
pub async fn add_owners(
108126
app: AppState,
109127
path: CratePath,
110128
parts: Parts,
111129
Json(body): Json<ChangeOwnersRequest>,
112-
) -> AppResult<ErasedJson> {
130+
) -> AppResult<Json<ModifyResponse>> {
113131
modify_owners(app, path.name, parts, body, true).await
114132
}
115133

@@ -123,14 +141,14 @@ pub async fn add_owners(
123141
("cookie" = []),
124142
),
125143
tag = "owners",
126-
responses((status = 200, description = "Successful Response")),
144+
responses((status = 200, description = "Successful Response", body = inline(ModifyResponse))),
127145
)]
128146
pub async fn remove_owners(
129147
app: AppState,
130148
path: CratePath,
131149
parts: Parts,
132150
Json(body): Json<ChangeOwnersRequest>,
133-
) -> AppResult<ErasedJson> {
151+
) -> AppResult<Json<ModifyResponse>> {
134152
modify_owners(app, path.name, parts, body, false).await
135153
}
136154

@@ -146,7 +164,7 @@ async fn modify_owners(
146164
parts: Parts,
147165
body: ChangeOwnersRequest,
148166
add: bool,
149-
) -> AppResult<ErasedJson> {
167+
) -> AppResult<Json<ModifyResponse>> {
150168
let logins = body.owners;
151169

152170
// Bound the number of invites processed per request to limit the cost of
@@ -166,7 +184,7 @@ async fn modify_owners(
166184

167185
let user = auth.user();
168186

169-
let (comma_sep_msg, emails) = conn
187+
let (msg, emails) = conn
170188
.transaction(|conn| {
171189
let app = app.clone();
172190
async move {
@@ -281,7 +299,7 @@ async fn modify_owners(
281299
}
282300
}
283301

284-
Ok(json!({ "msg": comma_sep_msg, "ok": true }))
302+
Ok(Json(ModifyResponse { msg, ok: true }))
285303
}
286304

287305
/// Invite `login` as an owner of this crate, returning the created

src/controllers/krate/publish.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const MAX_DESCRIPTION_LENGTH: usize = 1000;
6363
("cookie" = []),
6464
),
6565
tag = "publish",
66-
responses((status = 200, description = "Successful Response")),
66+
responses((status = 200, description = "Successful Response", body = inline(GoodCrate))),
6767
)]
6868
pub async fn publish(app: AppState, req: Parts, body: Body) -> AppResult<Json<GoodCrate>> {
6969
let stream = body.into_data_stream();

src/controllers/krate/rev_deps.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,43 @@ use crate::controllers::krate::CratePath;
44
use crate::models::{CrateName, User, Version, VersionOwnerAction};
55
use crate::util::errors::AppResult;
66
use crate::views::{EncodableDependency, EncodableVersion};
7-
use axum_extra::json;
8-
use axum_extra::response::ErasedJson;
7+
use axum::Json;
98
use crates_io_database::schema::{crates, users, versions};
109
use diesel::prelude::*;
1110
use diesel_async::RunQueryDsl;
1211
use http::request::Parts;
1312

13+
#[derive(Debug, Serialize, utoipa::ToSchema)]
14+
pub struct RevDepsResponse {
15+
/// The list of reverse dependencies of the crate.
16+
dependencies: Vec<EncodableDependency>,
17+
18+
/// The versions referenced in the `dependencies` field.
19+
versions: Vec<EncodableVersion>,
20+
21+
#[schema(inline)]
22+
meta: RevDepsMeta,
23+
}
24+
25+
#[derive(Debug, Serialize, utoipa::ToSchema)]
26+
pub struct RevDepsMeta {
27+
#[schema(example = 32)]
28+
total: i64,
29+
}
30+
1431
/// List reverse dependencies of a crate.
1532
#[utoipa::path(
1633
get,
1734
path = "/api/v1/crates/{name}/reverse_dependencies",
1835
params(CratePath),
1936
tag = "crates",
20-
responses((status = 200, description = "Successful Response")),
37+
responses((status = 200, description = "Successful Response", body = inline(RevDepsResponse))),
2138
)]
2239
pub async fn list_reverse_dependencies(
2340
app: AppState,
2441
path: CratePath,
2542
req: Parts,
26-
) -> AppResult<ErasedJson> {
43+
) -> AppResult<Json<RevDepsResponse>> {
2744
let mut conn = app.db_read().await?;
2845

2946
let pagination_options = PaginationOptions::builder().gather(&req)?;
@@ -64,9 +81,9 @@ pub async fn list_reverse_dependencies(
6481
})
6582
.collect::<Vec<_>>();
6683

67-
Ok(json!({
68-
"dependencies": rev_deps,
69-
"versions": versions,
70-
"meta": { "total": total },
84+
Ok(Json(RevDepsResponse {
85+
dependencies: rev_deps,
86+
versions,
87+
meta: RevDepsMeta { total },
7188
}))
7289
}

0 commit comments

Comments
 (0)