Skip to content

Commit 3454267

Browse files
committed
controllers/krate/versions: Add release_tracks meta for non-paginated versions
1 parent fb52bcf commit 3454267

File tree

2 files changed

+78
-30
lines changed

2 files changed

+78
-30
lines changed

src/controllers/krate/versions.rs

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use diesel_async::{AsyncPgConnection, RunQueryDsl};
88
use futures_util::{future, TryStreamExt};
99
use http::request::Parts;
1010
use indexmap::{IndexMap, IndexSet};
11-
use std::cmp::Reverse;
1211
use std::str::FromStr;
1312

1413
use crate::app::AppState;
@@ -51,6 +50,7 @@ pub async fn list_versions(state: AppState, path: CratePath, req: Parts) -> AppR
5150
.map(|mode| ShowIncludeMode::from_str(mode))
5251
.transpose()?
5352
.unwrap_or_default();
53+
let include_release_tracks = include.release_tracks;
5454

5555
// Sort by semver by default
5656
let versions_and_publishers = match params.get("sort").map(|s| s.to_lowercase()).as_deref() {
@@ -75,6 +75,12 @@ pub async fn list_versions(state: AppState, path: CratePath, req: Parts) -> AppR
7575

7676
Ok(match pagination {
7777
Some(_) => json!({ "versions": versions, "meta": versions_and_publishers.meta }),
78+
None if include_release_tracks => {
79+
json!({
80+
"versions": versions,
81+
"meta": ReleaseTracksMeta { release_tracks: versions_and_publishers.meta.release_tracks }
82+
})
83+
}
7884
None => json!({ "versions": versions }),
7985
})
8086
}
@@ -99,7 +105,6 @@ async fn list_by_date(
99105
.select(<(Version, Option<User>)>::as_select())
100106
.into_boxed();
101107

102-
let mut release_tracks = None;
103108
if let Some(options) = options {
104109
assert!(
105110
!matches!(&options.page, Page::Numeric(_)),
@@ -114,9 +119,20 @@ async fn list_by_date(
114119
)
115120
}
116121
query = query.limit(options.per_page);
122+
}
123+
124+
query = query.order((versions::created_at.desc(), versions::id.desc()));
117125

118-
if include.release_tracks {
119-
let mut sorted_versions = IndexSet::new();
126+
let data: Vec<(Version, Option<User>)> = query.load(conn).await?;
127+
let mut next_page = None;
128+
if let Some(options) = options {
129+
next_page = next_seek_params(&data, options, |last| Seek::Date.to_payload(last))?
130+
.map(|p| req.query_with_params(p));
131+
};
132+
133+
let release_tracks = if include.release_tracks {
134+
let mut sorted_versions = IndexSet::new();
135+
if options.is_some() {
120136
versions::table
121137
.filter(versions::crate_id.eq(crate_id))
122138
.filter(not(versions::yanked))
@@ -130,21 +146,23 @@ async fn list_by_date(
130146
future::ready(Ok(()))
131147
})
132148
.await?;
133-
134-
sorted_versions.sort_unstable_by(|a, b| b.cmp(a));
135-
release_tracks = Some(ReleaseTracks::from_sorted_semver_iter(
136-
sorted_versions.iter(),
137-
));
149+
} else {
150+
sorted_versions = data
151+
.iter()
152+
.flat_map(|(version, _)| {
153+
(!version.yanked)
154+
.then_some(version)
155+
.and_then(|v| semver::Version::parse(&v.num).ok())
156+
})
157+
.collect();
138158
}
139-
}
140-
141-
query = query.order((versions::created_at.desc(), versions::id.desc()));
142159

143-
let data: Vec<(Version, Option<User>)> = query.load(conn).await?;
144-
let mut next_page = None;
145-
if let Some(options) = options {
146-
next_page = next_seek_params(&data, options, |last| Seek::Date.to_payload(last))?
147-
.map(|p| req.query_with_params(p));
160+
sorted_versions.sort_unstable_by(|a, b| b.cmp(a));
161+
Some(ReleaseTracks::from_sorted_semver_iter(
162+
sorted_versions.iter(),
163+
))
164+
} else {
165+
None
148166
};
149167

150168
// Since the total count is retrieved through an additional query, to maintain consistency
@@ -269,15 +287,29 @@ async fn list_by_semver(
269287
(vec![], 0, release_tracks)
270288
}
271289
} else {
272-
let mut data: Vec<(Version, Option<User>)> = versions::table
290+
let mut data = IndexMap::new();
291+
versions::table
273292
.filter(versions::crate_id.eq(crate_id))
274293
.left_outer_join(users::table)
275294
.select(<(Version, Option<User>)>::as_select())
276-
.load(conn)
295+
.load_stream::<(Version, Option<User>)>(conn)
296+
.await?
297+
.try_for_each(|row| {
298+
if let Ok(semver) = semver::Version::parse(&row.0.num) {
299+
data.insert(semver, row);
300+
};
301+
future::ready(Ok(()))
302+
})
277303
.await?;
278-
data.sort_by_cached_key(|(version, _)| Reverse(semver::Version::parse(&version.num).ok()));
304+
data.sort_unstable_by(|a, _, b, _| b.cmp(a));
279305
let total = data.len();
280-
(data, total, None)
306+
let release_tracks = include.release_tracks.then(|| {
307+
ReleaseTracks::from_sorted_semver_iter(
308+
data.iter()
309+
.flat_map(|(semver, (version, _))| (!version.yanked).then_some(semver)),
310+
)
311+
});
312+
(data.into_values().collect(), total, release_tracks)
281313
};
282314

283315
let mut next_page = None;
@@ -365,6 +397,12 @@ struct ResponseMeta {
365397
release_tracks: Option<ReleaseTracks>,
366398
}
367399

400+
#[derive(Serialize)]
401+
struct ReleaseTracksMeta {
402+
#[serde(skip_serializing_if = "Option::is_none")]
403+
release_tracks: Option<ReleaseTracks>,
404+
}
405+
368406
#[derive(Debug, Eq, PartialEq, Serialize)]
369407
struct ReleaseTracks(IndexMap<ReleaseTrackName, ReleaseTrackDetails>);
370408

src/tests/routes/crates/versions/list.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,19 @@ async fn test_sorting() -> anyhow::Result<()> {
109109

110110
// Sort by semver
111111
let url = "/api/v1/crates/foo_versions/versions?sort=semver";
112-
let json: AllVersions = anon.get(url).await.good();
113-
for (num, expect) in nums(&json.versions).iter().zip(expects) {
114-
assert_eq!(num, expect);
115-
}
116112
for (url, release_tracks) in [
117113
(url, None),
118114
(
119115
&format!("{url}&include=release_tracks"),
120116
release_tracks.as_ref(),
121117
),
122118
] {
119+
let json: AllVersions = anon.get(url).await.good();
120+
assert_eq!(nums(&json.versions), expects);
121+
assert_eq!(
122+
json.meta.and_then(|m| m.release_tracks).as_ref(),
123+
release_tracks
124+
);
123125
let (resp, calls) = page_with_seek(&anon, url).await;
124126
for (json, expect) in resp.iter().zip(expects) {
125127
assert_eq!(json.versions[0].num, expect);
@@ -131,18 +133,20 @@ async fn test_sorting() -> anyhow::Result<()> {
131133

132134
// Sort by date
133135
let url = "/api/v1/crates/foo_versions/versions?sort=date";
134-
let json: AllVersions = anon.get(url).await.good();
135-
let expects = versions.iter().cloned().rev().collect::<Vec<_>>();
136-
for (num, expect) in nums(&json.versions).iter().zip(&expects) {
137-
assert_eq!(num, *expect);
138-
}
139136
for (url, release_tracks) in [
140137
(url, None),
141138
(
142139
&format!("{url}&include=release_tracks"),
143140
release_tracks.as_ref(),
144141
),
145142
] {
143+
let json: AllVersions = anon.get(url).await.good();
144+
let expects = versions.iter().cloned().rev().collect::<Vec<_>>();
145+
assert_eq!(nums(&json.versions), expects);
146+
assert_eq!(
147+
json.meta.and_then(|m| m.release_tracks).as_ref(),
148+
release_tracks
149+
);
146150
let (resp, calls) = page_with_seek(&anon, url).await;
147151
for (json, expect) in resp.iter().zip(&expects) {
148152
assert_eq!(json.versions[0].num, *expect);
@@ -364,6 +368,12 @@ async fn invalid_seek_parameter() {
364368
#[derive(Debug, Deserialize)]
365369
pub struct AllVersions {
366370
pub versions: Vec<EncodableVersion>,
371+
pub meta: Option<ReleaseTracksMeta>,
372+
}
373+
374+
#[derive(Debug, Deserialize)]
375+
pub struct ReleaseTracksMeta {
376+
pub release_tracks: Option<serde_json::Value>,
367377
}
368378

369379
#[derive(Debug, Deserialize)]

0 commit comments

Comments
 (0)