Skip to content

Commit efd8cc7

Browse files
committed
Improve OpenAPI documentation for GET /api/v1/crates/{name}/downloads endpoint
1 parent 57a418f commit efd8cc7

File tree

2 files changed

+106
-27
lines changed

2 files changed

+106
-27
lines changed

src/controllers/krate/downloads.rs

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ use crate::models::{User, Version as FullVersion, VersionDownload, VersionOwnerA
1010
use crate::schema::{version_downloads, version_owner_actions, versions};
1111
use crate::util::errors::{AppResult, BoxedAppError, bad_request};
1212
use crate::views::{EncodableVersion, EncodableVersionDownload};
13+
use axum::Json;
1314
use axum::extract::FromRequestParts;
1415
use axum_extra::extract::Query;
15-
use axum_extra::json;
16-
use axum_extra::response::ErasedJson;
1716
use crates_io_database::schema::users;
1817
use crates_io_diesel_helpers::to_char;
1918
use diesel::prelude::*;
@@ -37,6 +36,37 @@ pub struct DownloadsQueryParams {
3736
include: Option<String>,
3837
}
3938

39+
#[derive(Debug, Serialize, utoipa::ToSchema)]
40+
pub struct DownloadsResponse {
41+
/// The per-day download counts for the last 90 days.
42+
pub version_downloads: Vec<EncodableVersionDownload>,
43+
44+
/// The versions referenced in the download counts, if `?include=versions`
45+
/// was requested.
46+
#[serde(skip_serializing_if = "Option::is_none")]
47+
pub versions: Option<Vec<EncodableVersion>>,
48+
49+
#[schema(inline)]
50+
pub meta: DownloadsMeta,
51+
}
52+
53+
#[derive(Debug, Serialize, utoipa::ToSchema)]
54+
pub struct DownloadsMeta {
55+
#[schema(inline)]
56+
pub extra_downloads: Vec<ExtraDownload>,
57+
}
58+
59+
#[derive(Debug, Serialize, Queryable, utoipa::ToSchema)]
60+
pub struct ExtraDownload {
61+
/// The date this download count is for.
62+
#[schema(example = "2019-12-13")]
63+
date: String,
64+
65+
/// The number of downloads on the given date.
66+
#[schema(example = 123)]
67+
downloads: i64,
68+
}
69+
4070
/// Get the download counts for a crate.
4171
///
4272
/// This includes the per-day downloads for the last 90 days and for the
@@ -46,14 +76,13 @@ pub struct DownloadsQueryParams {
4676
path = "/api/v1/crates/{name}/downloads",
4777
params(CratePath, DownloadsQueryParams),
4878
tag = "crates",
49-
responses((status = 200, description = "Successful Response")),
79+
responses((status = 200, description = "Successful Response", body = inline(DownloadsResponse))),
5080
)]
51-
5281
pub async fn get_crate_downloads(
5382
state: AppState,
5483
path: CratePath,
5584
params: DownloadsQueryParams,
56-
) -> AppResult<ErasedJson> {
85+
) -> AppResult<Json<DownloadsResponse>> {
5786
let mut conn = state.db_read().await?;
5887

5988
use diesel::dsl::*;
@@ -78,7 +107,7 @@ pub async fn get_crate_downloads(
78107
.unwrap_or_default();
79108

80109
let sum_downloads = sql::<BigInt>("SUM(version_downloads.downloads)");
81-
let (downloads, extra, versions_and_publishers, actions) = tokio::try_join!(
110+
let (downloads, extra_downloads, versions_and_publishers, actions) = tokio::try_join!(
82111
VersionDownload::belonging_to(latest_five)
83112
.filter(version_downloads::date.gt(date(now - 90.days())))
84113
.order((
@@ -101,18 +130,12 @@ pub async fn get_crate_downloads(
101130
load_actions(&mut conn, latest_five, include.versions),
102131
)?;
103132

104-
let downloads = downloads
133+
let version_downloads = downloads
105134
.into_iter()
106135
.map(VersionDownload::into)
107136
.collect::<Vec<EncodableVersionDownload>>();
108137

109-
#[derive(Serialize, Queryable)]
110-
struct ExtraDownload {
111-
date: String,
112-
downloads: i64,
113-
}
114-
115-
if include.versions {
138+
let versions = if include.versions {
116139
let versions_and_publishers = versions_and_publishers.grouped_by(latest_five);
117140
let actions = actions.grouped_by(latest_five);
118141
let versions = versions_and_publishers
@@ -125,20 +148,15 @@ pub async fn get_crate_downloads(
125148
})
126149
.collect::<Vec<_>>();
127150

128-
return Ok(json!({
129-
"version_downloads": downloads,
130-
"versions": versions,
131-
"meta": {
132-
"extra_downloads": extra,
133-
},
134-
}));
135-
}
151+
Some(versions)
152+
} else {
153+
None
154+
};
136155

137-
Ok(json!({
138-
"version_downloads": downloads,
139-
"meta": {
140-
"extra_downloads": extra,
141-
},
156+
Ok(Json(DownloadsResponse {
157+
version_downloads,
158+
versions,
159+
meta: DownloadsMeta { extra_downloads },
142160
}))
143161
}
144162

src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1397,6 +1397,67 @@ expression: response.json()
13971397
],
13981398
"responses": {
13991399
"200": {
1400+
"content": {
1401+
"application/json": {
1402+
"schema": {
1403+
"properties": {
1404+
"meta": {
1405+
"properties": {
1406+
"extra_downloads": {
1407+
"items": {
1408+
"properties": {
1409+
"date": {
1410+
"description": "The date this download count is for.",
1411+
"example": "2019-12-13",
1412+
"type": "string"
1413+
},
1414+
"downloads": {
1415+
"description": "The number of downloads on the given date.",
1416+
"example": 123,
1417+
"format": "int64",
1418+
"type": "integer"
1419+
}
1420+
},
1421+
"required": [
1422+
"date",
1423+
"downloads"
1424+
],
1425+
"type": "object"
1426+
},
1427+
"type": "array"
1428+
}
1429+
},
1430+
"required": [
1431+
"extra_downloads"
1432+
],
1433+
"type": "object"
1434+
},
1435+
"version_downloads": {
1436+
"description": "The per-day download counts for the last 90 days.",
1437+
"items": {
1438+
"$ref": "#/components/schemas/VersionDownload"
1439+
},
1440+
"type": "array"
1441+
},
1442+
"versions": {
1443+
"description": "The versions referenced in the download counts, if `?include=versions`\nwas requested.",
1444+
"items": {
1445+
"$ref": "#/components/schemas/Version"
1446+
},
1447+
"type": [
1448+
"array",
1449+
"null"
1450+
]
1451+
}
1452+
},
1453+
"required": [
1454+
"version_downloads",
1455+
"meta"
1456+
],
1457+
"type": "object"
1458+
}
1459+
}
1460+
},
14001461
"description": "Successful Response"
14011462
}
14021463
},

0 commit comments

Comments
 (0)