Skip to content

Commit 4a29008

Browse files
authored
controllers/krate/metadata: Move GET /summary endpoint to a dedicated module (#8245)
This module is focussed on the `GET /crates/:crate_id` endpoint and its children. The `GET /summary` endpoint is a distant relative, but doesn't really belong in this module.
1 parent e544ffd commit 4a29008

File tree

4 files changed

+122
-112
lines changed

4 files changed

+122
-112
lines changed

src/controllers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub mod keyword;
7171
pub mod krate;
7272
pub mod metrics;
7373
pub mod site_metadata;
74+
pub mod summary;
7475
pub mod team;
7576
pub mod token;
7677
pub mod user;

src/controllers/krate/metadata.rs

Lines changed: 1 addition & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -12,124 +12,14 @@ use crate::controllers::helpers::pagination::PaginationOptions;
1212

1313
use crate::models::{
1414
Category, Crate, CrateCategory, CrateKeyword, CrateVersions, Keyword, RecentCrateDownloads,
15-
TopVersions, User, Version, VersionOwnerAction,
15+
User, Version, VersionOwnerAction,
1616
};
1717
use crate::schema::*;
1818
use crate::util::errors::crate_not_found;
1919
use crate::views::{
2020
EncodableCategory, EncodableCrate, EncodableDependency, EncodableKeyword, EncodableVersion,
2121
};
2222

23-
/// Handles the `GET /summary` route.
24-
pub async fn summary(state: AppState) -> AppResult<Json<Value>> {
25-
spawn_blocking(move || {
26-
let config = &state.config;
27-
28-
let conn = &mut *state.db_read()?;
29-
let num_crates: i64 = crates::table.count().get_result(conn)?;
30-
let num_downloads: i64 = metadata::table
31-
.select(metadata::total_downloads)
32-
.get_result(conn)?;
33-
34-
fn encode_crates(
35-
conn: &mut PgConnection,
36-
data: Vec<(Crate, Option<i64>)>,
37-
) -> AppResult<Vec<EncodableCrate>> {
38-
let recent_downloads = data.iter().map(|&(_, s)| s).collect::<Vec<_>>();
39-
40-
let krates = data.into_iter().map(|(c, _)| c).collect::<Vec<_>>();
41-
42-
let versions: Vec<Version> = krates.versions().load(conn)?;
43-
versions
44-
.grouped_by(&krates)
45-
.into_iter()
46-
.map(TopVersions::from_versions)
47-
.zip(krates)
48-
.zip(recent_downloads)
49-
.map(|((top_versions, krate), recent_downloads)| {
50-
Ok(EncodableCrate::from_minimal(
51-
krate,
52-
Some(&top_versions),
53-
None,
54-
false,
55-
recent_downloads,
56-
))
57-
})
58-
.collect()
59-
}
60-
61-
let selection = (
62-
Crate::as_select(),
63-
recent_crate_downloads::downloads.nullable(),
64-
);
65-
66-
let new_crates = crates::table
67-
.left_join(recent_crate_downloads::table)
68-
.order(crates::created_at.desc())
69-
.select(selection)
70-
.limit(10)
71-
.load(conn)?;
72-
let just_updated = crates::table
73-
.left_join(recent_crate_downloads::table)
74-
.filter(crates::updated_at.ne(crates::created_at))
75-
.order(crates::updated_at.desc())
76-
.select(selection)
77-
.limit(10)
78-
.load(conn)?;
79-
80-
let mut most_downloaded_query = crates::table
81-
.left_join(recent_crate_downloads::table)
82-
.into_boxed();
83-
if !config.excluded_crate_names.is_empty() {
84-
most_downloaded_query =
85-
most_downloaded_query.filter(crates::name.ne_all(&config.excluded_crate_names));
86-
}
87-
let most_downloaded = most_downloaded_query
88-
.then_order_by(crates::downloads.desc())
89-
.select(selection)
90-
.limit(10)
91-
.load(conn)?;
92-
93-
let mut most_recently_downloaded_query = crates::table
94-
.inner_join(recent_crate_downloads::table)
95-
.into_boxed();
96-
if !config.excluded_crate_names.is_empty() {
97-
most_recently_downloaded_query = most_recently_downloaded_query
98-
.filter(crates::name.ne_all(&config.excluded_crate_names));
99-
}
100-
let most_recently_downloaded = most_recently_downloaded_query
101-
.then_order_by(recent_crate_downloads::downloads.desc())
102-
.select(selection)
103-
.limit(10)
104-
.load(conn)?;
105-
106-
let popular_keywords = keywords::table
107-
.order(keywords::crates_cnt.desc())
108-
.limit(10)
109-
.load(conn)?
110-
.into_iter()
111-
.map(Keyword::into)
112-
.collect::<Vec<EncodableKeyword>>();
113-
114-
let popular_categories = Category::toplevel(conn, "crates", 10, 0)?
115-
.into_iter()
116-
.map(Category::into)
117-
.collect::<Vec<EncodableCategory>>();
118-
119-
Ok(Json(json!({
120-
"num_downloads": num_downloads,
121-
"num_crates": num_crates,
122-
"new_crates": encode_crates(conn, new_crates)?,
123-
"most_downloaded": encode_crates(conn, most_downloaded)?,
124-
"most_recently_downloaded": encode_crates(conn, most_recently_downloaded)?,
125-
"just_updated": encode_crates(conn, just_updated)?,
126-
"popular_keywords": popular_keywords,
127-
"popular_categories": popular_categories,
128-
})))
129-
})
130-
.await
131-
}
132-
13323
/// Handles the `GET /crates/new` special case.
13424
pub async fn show_new(app: AppState, req: Parts) -> AppResult<Json<Value>> {
13525
show(app, Path("new".to_string()), req).await

src/controllers/summary.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
use crate::app::AppState;
2+
use crate::controllers::cargo_prelude::AppResult;
3+
use crate::models::{Category, Crate, CrateVersions, Keyword, TopVersions, Version};
4+
use crate::schema::{crates, keywords, metadata, recent_crate_downloads};
5+
use crate::tasks::spawn_blocking;
6+
use crate::views::{EncodableCategory, EncodableCrate, EncodableKeyword};
7+
use axum::Json;
8+
use diesel::prelude::*;
9+
use serde_json::Value;
10+
11+
/// Handles the `GET /summary` route.
12+
pub async fn summary(state: AppState) -> AppResult<Json<Value>> {
13+
spawn_blocking(move || {
14+
let config = &state.config;
15+
16+
let conn = &mut *state.db_read()?;
17+
let num_crates: i64 = crates::table.count().get_result(conn)?;
18+
let num_downloads: i64 = metadata::table
19+
.select(metadata::total_downloads)
20+
.get_result(conn)?;
21+
22+
fn encode_crates(
23+
conn: &mut PgConnection,
24+
data: Vec<(Crate, Option<i64>)>,
25+
) -> AppResult<Vec<EncodableCrate>> {
26+
let recent_downloads = data.iter().map(|&(_, s)| s).collect::<Vec<_>>();
27+
28+
let krates = data.into_iter().map(|(c, _)| c).collect::<Vec<_>>();
29+
30+
let versions: Vec<Version> = krates.versions().load(conn)?;
31+
versions
32+
.grouped_by(&krates)
33+
.into_iter()
34+
.map(TopVersions::from_versions)
35+
.zip(krates)
36+
.zip(recent_downloads)
37+
.map(|((top_versions, krate), recent_downloads)| {
38+
Ok(EncodableCrate::from_minimal(
39+
krate,
40+
Some(&top_versions),
41+
None,
42+
false,
43+
recent_downloads,
44+
))
45+
})
46+
.collect()
47+
}
48+
49+
let selection = (
50+
Crate::as_select(),
51+
recent_crate_downloads::downloads.nullable(),
52+
);
53+
54+
let new_crates = crates::table
55+
.left_join(recent_crate_downloads::table)
56+
.order(crates::created_at.desc())
57+
.select(selection)
58+
.limit(10)
59+
.load(conn)?;
60+
let just_updated = crates::table
61+
.left_join(recent_crate_downloads::table)
62+
.filter(crates::updated_at.ne(crates::created_at))
63+
.order(crates::updated_at.desc())
64+
.select(selection)
65+
.limit(10)
66+
.load(conn)?;
67+
68+
let mut most_downloaded_query = crates::table
69+
.left_join(recent_crate_downloads::table)
70+
.into_boxed();
71+
if !config.excluded_crate_names.is_empty() {
72+
most_downloaded_query =
73+
most_downloaded_query.filter(crates::name.ne_all(&config.excluded_crate_names));
74+
}
75+
let most_downloaded = most_downloaded_query
76+
.then_order_by(crates::downloads.desc())
77+
.select(selection)
78+
.limit(10)
79+
.load(conn)?;
80+
81+
let mut most_recently_downloaded_query = crates::table
82+
.inner_join(recent_crate_downloads::table)
83+
.into_boxed();
84+
if !config.excluded_crate_names.is_empty() {
85+
most_recently_downloaded_query = most_recently_downloaded_query
86+
.filter(crates::name.ne_all(&config.excluded_crate_names));
87+
}
88+
let most_recently_downloaded = most_recently_downloaded_query
89+
.then_order_by(recent_crate_downloads::downloads.desc())
90+
.select(selection)
91+
.limit(10)
92+
.load(conn)?;
93+
94+
let popular_keywords = keywords::table
95+
.order(keywords::crates_cnt.desc())
96+
.limit(10)
97+
.load(conn)?
98+
.into_iter()
99+
.map(Keyword::into)
100+
.collect::<Vec<EncodableKeyword>>();
101+
102+
let popular_categories = Category::toplevel(conn, "crates", 10, 0)?
103+
.into_iter()
104+
.map(Category::into)
105+
.collect::<Vec<EncodableCategory>>();
106+
107+
Ok(Json(json!({
108+
"num_downloads": num_downloads,
109+
"num_crates": num_crates,
110+
"new_crates": encode_crates(conn, new_crates)?,
111+
"most_downloaded": encode_crates(conn, most_downloaded)?,
112+
"most_recently_downloaded": encode_crates(conn, most_recently_downloaded)?,
113+
"just_updated": encode_crates(conn, just_updated)?,
114+
"popular_keywords": popular_keywords,
115+
"popular_categories": popular_categories,
116+
})))
117+
})
118+
.await
119+
}

src/router.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ pub fn build_axum_router(state: AppState) -> Router<()> {
122122
"/api/v1/me/email_notifications",
123123
put(user::me::update_email_notifications),
124124
)
125-
.route("/api/v1/summary", get(krate::metadata::summary))
125+
.route("/api/v1/summary", get(summary::summary))
126126
.route(
127127
"/api/v1/confirm/:email_token",
128128
put(user::me::confirm_user_email),

0 commit comments

Comments
 (0)