Skip to content

Commit ce4fc4a

Browse files
committed
Improve OpenAPI documentation for GET /api/v1/crates/{name}/owners endpoints
1 parent a696983 commit ce4fc4a

File tree

3 files changed

+145
-13
lines changed

3 files changed

+145
-13
lines changed

src/controllers/krate/owners.rs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,37 @@ use oauth2::AccessToken;
2626
use secrecy::{ExposeSecret, SecretString};
2727
use thiserror::Error;
2828

29+
#[derive(Debug, Serialize, utoipa::ToSchema)]
30+
pub struct UsersResponse {
31+
pub users: Vec<EncodableOwner>,
32+
}
33+
2934
/// List crate owners.
3035
#[utoipa::path(
3136
get,
3237
path = "/api/v1/crates/{name}/owners",
3338
params(CratePath),
3439
tag = "owners",
35-
responses((status = 200, description = "Successful Response")),
40+
responses((status = 200, description = "Successful Response", body = inline(UsersResponse))),
3641
)]
37-
pub async fn list_owners(state: AppState, path: CratePath) -> AppResult<ErasedJson> {
42+
pub async fn list_owners(state: AppState, path: CratePath) -> AppResult<Json<UsersResponse>> {
3843
let mut conn = state.db_read().await?;
3944

4045
let krate = path.load_crate(&mut conn).await?;
4146

42-
let owners = krate
47+
let users = krate
4348
.owners(&mut conn)
4449
.await?
4550
.into_iter()
4651
.map(Owner::into)
4752
.collect::<Vec<EncodableOwner>>();
4853

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

5262
/// List team owners of a crate.
@@ -55,19 +65,19 @@ pub async fn list_owners(state: AppState, path: CratePath) -> AppResult<ErasedJs
5565
path = "/api/v1/crates/{name}/owner_team",
5666
params(CratePath),
5767
tag = "owners",
58-
responses((status = 200, description = "Successful Response")),
68+
responses((status = 200, description = "Successful Response", body = inline(TeamsResponse))),
5969
)]
60-
pub async fn get_team_owners(state: AppState, path: CratePath) -> AppResult<ErasedJson> {
70+
pub async fn get_team_owners(state: AppState, path: CratePath) -> AppResult<Json<TeamsResponse>> {
6171
let mut conn = state.db_read().await?;
6272
let krate = path.load_crate(&mut conn).await?;
6373

64-
let owners = Team::owning(&krate, &mut conn)
74+
let teams = Team::owning(&krate, &mut conn)
6575
.await?
6676
.into_iter()
6777
.map(Owner::into)
6878
.collect::<Vec<EncodableOwner>>();
6979

70-
Ok(json!({ "teams": owners }))
80+
Ok(Json(TeamsResponse { teams }))
7181
}
7282

7383
/// List user owners of a crate.
@@ -76,20 +86,20 @@ pub async fn get_team_owners(state: AppState, path: CratePath) -> AppResult<Eras
7686
path = "/api/v1/crates/{name}/owner_user",
7787
params(CratePath),
7888
tag = "owners",
79-
responses((status = 200, description = "Successful Response")),
89+
responses((status = 200, description = "Successful Response", body = inline(UsersResponse))),
8090
)]
81-
pub async fn get_user_owners(state: AppState, path: CratePath) -> AppResult<ErasedJson> {
91+
pub async fn get_user_owners(state: AppState, path: CratePath) -> AppResult<Json<UsersResponse>> {
8292
let mut conn = state.db_read().await?;
8393

8494
let krate = path.load_crate(&mut conn).await?;
8595

86-
let owners = User::owning(&krate, &mut conn)
96+
let users = User::owning(&krate, &mut conn)
8797
.await?
8898
.into_iter()
8999
.map(Owner::into)
90100
.collect::<Vec<EncodableOwner>>();
91101

92-
Ok(json!({ "users": owners }))
102+
Ok(Json(UsersResponse { users }))
93103
}
94104

95105
/// Add crate owners.

src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,56 @@ expression: response.json()
584584
],
585585
"type": "object"
586586
},
587+
"Owner": {
588+
"properties": {
589+
"avatar": {
590+
"description": "The avatar URL of the team or user.",
591+
"example": "https://avatars2.githubusercontent.com/u/1234567?v=4",
592+
"type": [
593+
"string",
594+
"null"
595+
]
596+
},
597+
"id": {
598+
"description": "The opaque identifier for the team or user, depending on the `kind` field.",
599+
"example": 42,
600+
"format": "int32",
601+
"type": "integer"
602+
},
603+
"kind": {
604+
"description": "The kind of the owner (`user` or `team`).",
605+
"example": "user",
606+
"type": "string"
607+
},
608+
"login": {
609+
"description": "The login name of the team or user.",
610+
"example": "ghost",
611+
"type": "string"
612+
},
613+
"name": {
614+
"description": "The display name of the team or user.",
615+
"example": "Kate Morgan",
616+
"type": [
617+
"string",
618+
"null"
619+
]
620+
},
621+
"url": {
622+
"description": "The URL to the owner's profile.",
623+
"example": "https://github.com/ghost",
624+
"type": [
625+
"string",
626+
"null"
627+
]
628+
}
629+
},
630+
"required": [
631+
"id",
632+
"login",
633+
"kind"
634+
],
635+
"type": "object"
636+
},
587637
"Slug": {
588638
"properties": {
589639
"description": {
@@ -1952,6 +2002,24 @@ expression: response.json()
19522002
],
19532003
"responses": {
19542004
"200": {
2005+
"content": {
2006+
"application/json": {
2007+
"schema": {
2008+
"properties": {
2009+
"teams": {
2010+
"items": {
2011+
"$ref": "#/components/schemas/Owner"
2012+
},
2013+
"type": "array"
2014+
}
2015+
},
2016+
"required": [
2017+
"teams"
2018+
],
2019+
"type": "object"
2020+
}
2021+
}
2022+
},
19552023
"description": "Successful Response"
19562024
}
19572025
},
@@ -1977,6 +2045,24 @@ expression: response.json()
19772045
],
19782046
"responses": {
19792047
"200": {
2048+
"content": {
2049+
"application/json": {
2050+
"schema": {
2051+
"properties": {
2052+
"users": {
2053+
"items": {
2054+
"$ref": "#/components/schemas/Owner"
2055+
},
2056+
"type": "array"
2057+
}
2058+
},
2059+
"required": [
2060+
"users"
2061+
],
2062+
"type": "object"
2063+
}
2064+
}
2065+
},
19802066
"description": "Successful Response"
19812067
}
19822068
},
@@ -2033,6 +2119,24 @@ expression: response.json()
20332119
],
20342120
"responses": {
20352121
"200": {
2122+
"content": {
2123+
"application/json": {
2124+
"schema": {
2125+
"properties": {
2126+
"users": {
2127+
"items": {
2128+
"$ref": "#/components/schemas/Owner"
2129+
},
2130+
"type": "array"
2131+
}
2132+
},
2133+
"required": [
2134+
"users"
2135+
],
2136+
"type": "object"
2137+
}
2138+
}
2139+
},
20362140
"description": "Successful Response"
20372141
}
20382142
},

src/views.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,13 +509,31 @@ pub struct EncodableCrateLinks {
509509
pub reverse_dependencies: String,
510510
}
511511

512-
#[derive(Serialize, Deserialize, Debug)]
512+
#[derive(Serialize, Deserialize, Debug, utoipa::ToSchema)]
513+
#[schema(as = Owner)]
513514
pub struct EncodableOwner {
515+
/// The opaque identifier for the team or user, depending on the `kind` field.
516+
#[schema(example = 42)]
514517
pub id: i32,
518+
519+
/// The login name of the team or user.
520+
#[schema(example = "ghost")]
515521
pub login: String,
522+
523+
/// The kind of the owner (`user` or `team`).
524+
#[schema(example = "user")]
516525
pub kind: String,
526+
527+
/// The URL to the owner's profile.
528+
#[schema(example = "https://github.com/ghost")]
517529
pub url: Option<String>,
530+
531+
/// The display name of the team or user.
532+
#[schema(example = "Kate Morgan")]
518533
pub name: Option<String>,
534+
535+
/// The avatar URL of the team or user.
536+
#[schema(example = "https://avatars2.githubusercontent.com/u/1234567?v=4")]
519537
pub avatar: Option<String>,
520538
}
521539

0 commit comments

Comments
 (0)