Skip to content

Commit dcb32bd

Browse files
committed
controllers/krate/versions: Extract ListQueryParams struct
1 parent 17d7476 commit dcb32bd

File tree

3 files changed

+112
-18
lines changed

3 files changed

+112
-18
lines changed

src/controllers/helpers/pagination.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::util::errors::{bad_request, AppResult};
77
use crate::util::HeaderMapExt;
88
use std::num::NonZeroU32;
99

10+
use axum::extract::FromRequestParts;
1011
use base64::{engine::general_purpose, Engine};
1112
use diesel::pg::Pg;
1213
use diesel::prelude::*;
@@ -55,19 +56,20 @@ impl PaginationOptions {
5556
}
5657
}
5758

58-
#[derive(Debug, Deserialize, utoipa::IntoParams)]
59+
#[derive(Debug, Deserialize, FromRequestParts, utoipa::IntoParams)]
60+
#[from_request(via(axum::extract::Query))]
5961
#[into_params(parameter_in = Query)]
6062
pub struct PaginationQueryParams {
6163
/// The page number to request.
6264
///
6365
/// This parameter is mutually exclusive with `seek` and not supported for
6466
/// all requests.
6567
#[param(value_type = Option<u32>, minimum = 1)]
66-
page: Option<NonZeroU32>,
68+
pub page: Option<NonZeroU32>,
6769

6870
/// The number of items to request per page.
6971
#[param(value_type = Option<u32>, minimum = 1)]
70-
per_page: Option<NonZeroU32>,
72+
pub per_page: Option<NonZeroU32>,
7173

7274
/// The seek key to request.
7375
///
@@ -76,7 +78,7 @@ pub struct PaginationQueryParams {
7678
///
7779
/// The seek key can usually be found in the `meta.next_page` field of
7880
/// paginated responses.
79-
seek: Option<String>,
81+
pub seek: Option<String>,
8082
}
8183

8284
pub(crate) struct PaginationOptionsBuilder {

src/controllers/krate/versions.rs

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Endpoint for versions of a crate
22
3+
use axum::extract::{FromRequestParts, Query};
34
use axum_extra::json;
45
use axum_extra::response::ErasedJson;
56
use diesel::dsl::not;
@@ -11,48 +12,75 @@ use indexmap::{IndexMap, IndexSet};
1112
use std::str::FromStr;
1213

1314
use crate::app::AppState;
14-
use crate::controllers::helpers::pagination::{encode_seek, Page, PaginationOptions};
15+
use crate::controllers::helpers::pagination::{
16+
encode_seek, Page, PaginationOptions, PaginationQueryParams,
17+
};
1518
use crate::controllers::krate::CratePath;
1619
use crate::models::{User, Version, VersionOwnerAction};
1720
use crate::schema::{users, versions};
1821
use crate::util::errors::{bad_request, AppResult, BoxedAppError};
1922
use crate::util::RequestUtils;
2023
use crate::views::EncodableVersion;
2124

25+
#[derive(Debug, Deserialize, FromRequestParts, utoipa::IntoParams)]
26+
#[from_request(via(Query))]
27+
#[into_params(parameter_in = Query)]
28+
pub struct ListQueryParams {
29+
/// Additional data to include in the response.
30+
///
31+
/// Valid values: `release_tracks`.
32+
///
33+
/// Defaults to no additional data.
34+
///
35+
/// This parameter expects a comma-separated list of values.
36+
include: Option<String>,
37+
38+
/// The sort order of the versions.
39+
///
40+
/// Valid values: `date`, and `semver`.
41+
///
42+
/// Defaults to `semver`.
43+
sort: Option<String>,
44+
}
45+
2246
/// List all versions of a crate.
2347
#[utoipa::path(
2448
get,
2549
path = "/api/v1/crates/{name}/versions",
26-
params(CratePath),
50+
params(CratePath, ListQueryParams, PaginationQueryParams),
2751
tag = "versions",
2852
responses((status = 200, description = "Successful Response")),
2953
)]
30-
pub async fn list_versions(state: AppState, path: CratePath, req: Parts) -> AppResult<ErasedJson> {
54+
pub async fn list_versions(
55+
state: AppState,
56+
path: CratePath,
57+
params: ListQueryParams,
58+
pagination: PaginationQueryParams,
59+
req: Parts,
60+
) -> AppResult<ErasedJson> {
3161
let mut conn = state.db_read().await?;
3262

3363
let crate_id = path.load_crate_id(&mut conn).await?;
3464

35-
let mut pagination = None;
36-
let params = req.query();
3765
// To keep backward compatibility, we paginate only if per_page is provided
38-
if params.get("per_page").is_some() {
39-
pagination = Some(
66+
let pagination = match pagination.per_page {
67+
Some(_) => Some(
4068
PaginationOptions::builder()
4169
.enable_seek(true)
4270
.enable_pages(false)
4371
.gather(&req)?,
44-
);
45-
}
72+
),
73+
None => None,
74+
};
4675

47-
let include = req
48-
.query()
49-
.get("include")
50-
.map(|mode| ShowIncludeMode::from_str(mode))
76+
let include = params
77+
.include
78+
.map(|mode| ShowIncludeMode::from_str(&mode))
5179
.transpose()?
5280
.unwrap_or_default();
5381

5482
// Sort by semver by default
55-
let versions_and_publishers = match params.get("sort").map(|s| s.to_lowercase()).as_deref() {
83+
let versions_and_publishers = match params.sort.map(|s| s.to_lowercase()).as_deref() {
5684
Some("date") => {
5785
list_by_date(crate_id, pagination.as_ref(), include, &req, &mut conn).await?
5886
}

src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,70 @@ snapshot_kind: text
830830
"schema": {
831831
"type": "string"
832832
}
833+
},
834+
{
835+
"description": "Additional data to include in the response.\n\nValid values: `release_tracks`.\n\nDefaults to no additional data.\n\nThis parameter expects a comma-separated list of values.",
836+
"in": "query",
837+
"name": "include",
838+
"required": false,
839+
"schema": {
840+
"type": [
841+
"string",
842+
"null"
843+
]
844+
}
845+
},
846+
{
847+
"description": "The sort order of the versions.\n\nValid values: `date`, and `semver`.\n\nDefaults to `semver`.",
848+
"in": "query",
849+
"name": "sort",
850+
"required": false,
851+
"schema": {
852+
"type": [
853+
"string",
854+
"null"
855+
]
856+
}
857+
},
858+
{
859+
"description": "The page number to request.\n\nThis parameter is mutually exclusive with `seek` and not supported for\nall requests.",
860+
"in": "query",
861+
"name": "page",
862+
"required": false,
863+
"schema": {
864+
"format": "int32",
865+
"minimum": 1,
866+
"type": [
867+
"integer",
868+
"null"
869+
]
870+
}
871+
},
872+
{
873+
"description": "The number of items to request per page.",
874+
"in": "query",
875+
"name": "per_page",
876+
"required": false,
877+
"schema": {
878+
"format": "int32",
879+
"minimum": 1,
880+
"type": [
881+
"integer",
882+
"null"
883+
]
884+
}
885+
},
886+
{
887+
"description": "The seek key to request.\n\nThis parameter is mutually exclusive with `page` and not supported for\nall requests.\n\nThe seek key can usually be found in the `meta.next_page` field of\npaginated responses.",
888+
"in": "query",
889+
"name": "seek",
890+
"required": false,
891+
"schema": {
892+
"type": [
893+
"string",
894+
"null"
895+
]
896+
}
833897
}
834898
],
835899
"responses": {

0 commit comments

Comments
 (0)