diff --git a/crates/crates_io_api_types/src/lib.rs b/crates/crates_io_api_types/src/lib.rs index e9fadfd75c9..7204c688435 100644 --- a/crates/crates_io_api_types/src/lib.rs +++ b/crates/crates_io_api_types/src/lib.rs @@ -314,6 +314,14 @@ pub struct EncodableCrate { #[schema(example = 456_789)] pub recent_downloads: Option, + /// The total number of downloads for this crate in the last month. + #[schema(example = 123_456)] + pub monthly_downloads: Option, + + /// The total number of downloads for this crate in the last week. + #[schema(example = 56_789)] + pub weekly_downloads: Option, + /// The "default" version of this crate. /// /// This version will be displayed by default on the crate's page. @@ -380,6 +388,8 @@ impl EncodableCrate { exact_match: bool, downloads: i64, recent_downloads: Option, + monthly_downloads: Option, + weekly_downloads: Option, ) -> Self { let Crate { name, @@ -440,6 +450,8 @@ impl EncodableCrate { created_at, downloads, recent_downloads, + monthly_downloads, + weekly_downloads, versions, keywords: keyword_ids, categories: category_ids, @@ -477,6 +489,8 @@ impl EncodableCrate { exact_match: bool, downloads: i64, recent_downloads: Option, + monthly_downloads: Option, + weekly_downloads: Option, ) -> Self { Self::from( krate, @@ -490,6 +504,8 @@ impl EncodableCrate { exact_match, downloads, recent_downloads, + monthly_downloads, + weekly_downloads, ) } } @@ -1187,6 +1203,8 @@ mod tests { .and_utc(), downloads: 0, recent_downloads: None, + monthly_downloads: None, + weekly_downloads: None, default_version: None, num_versions: 0, yanked: false, diff --git a/crates/crates_io_database/src/schema.patch b/crates/crates_io_database/src/schema.patch index dca5aa2bb06..567d204b55e 100644 --- a/crates/crates_io_database/src/schema.patch +++ b/crates/crates_io_database/src/schema.patch @@ -40,7 +40,7 @@ /// The `id` column of the `dependencies` table. /// /// Its SQL type is `Int4`. -@@ -773,6 +767,24 @@ +@@ -773,6 +767,32 @@ } diesel::table! { @@ -58,6 +58,14 @@ + /// + /// Its SQL type is `BigInt`. + downloads -> BigInt, ++ /// The `monthly` column of the `recent_crate_downloads` table. ++ /// ++ /// Its SQL type is `BigInt`. ++ monthly -> BigInt, ++ /// The `weekly` column of the `recent_crate_downloads` table. ++ /// ++ /// Its SQL type is `BigInt`. ++ weekly -> BigInt, + } +} + @@ -65,7 +73,7 @@ use diesel::sql_types::*; use diesel_full_text_search::Tsvector; -@@ -1214,7 +1226,8 @@ +@@ -1214,7 +1234,8 @@ diesel::joinable!(crate_downloads -> crates (crate_id)); diesel::joinable!(crate_owner_invitations -> crates (crate_id)); diesel::joinable!(crate_owners -> crates (crate_id)); @@ -75,7 +83,7 @@ diesel::joinable!(crates_categories -> categories (category_id)); diesel::joinable!(crates_categories -> crates (crate_id)); diesel::joinable!(crates_keywords -> crates (crate_id)); -@@ -1230,6 +1243,7 @@ +@@ -1230,6 +1251,7 @@ diesel::joinable!(publish_limit_buckets -> users (user_id)); diesel::joinable!(publish_rate_overrides -> users (user_id)); diesel::joinable!(readme_renderings -> versions (version_id)); @@ -83,7 +91,7 @@ diesel::joinable!(trustpub_configs_github -> crates (crate_id)); diesel::joinable!(trustpub_configs_gitlab -> crates (crate_id)); diesel::joinable!(version_downloads -> versions (version_id)); -@@ -1262,6 +1276,7 @@ +@@ -1262,6 +1284,7 @@ publish_limit_buckets, publish_rate_overrides, readme_renderings, diff --git a/crates/crates_io_database/src/schema.rs b/crates/crates_io_database/src/schema.rs index 9f75a52b579..bb7c999b58b 100644 --- a/crates/crates_io_database/src/schema.rs +++ b/crates/crates_io_database/src/schema.rs @@ -783,6 +783,14 @@ diesel::table! { /// /// Its SQL type is `BigInt`. downloads -> BigInt, + /// The `monthly` column of the `recent_crate_downloads` table. + /// + /// Its SQL type is `BigInt`. + monthly -> BigInt, + /// The `weekly` column of the `recent_crate_downloads` table. + /// + /// Its SQL type is `BigInt`. + weekly -> BigInt, } } diff --git a/migrations/2025-11-30-121230-add_monthly_downloads/down.sql b/migrations/2025-11-30-121230-add_monthly_downloads/down.sql new file mode 100644 index 00000000000..ba704a2ad05 --- /dev/null +++ b/migrations/2025-11-30-121230-add_monthly_downloads/down.sql @@ -0,0 +1,8 @@ +DROP MATERIALIZED VIEW recent_crate_downloads; +CREATE MATERIALIZED VIEW recent_crate_downloads (crate_id, downloads) AS + SELECT crate_id, SUM(version_downloads.downloads) FROM version_downloads + INNER JOIN versions + ON version_downloads.version_id = versions.id + WHERE version_downloads.date > date(CURRENT_TIMESTAMP - INTERVAL '90 days') + GROUP BY crate_id; +CREATE UNIQUE INDEX recent_crate_downloads_crate_id ON recent_crate_downloads (crate_id); diff --git a/migrations/2025-11-30-121230-add_monthly_downloads/up.sql b/migrations/2025-11-30-121230-add_monthly_downloads/up.sql new file mode 100644 index 00000000000..6420638547e --- /dev/null +++ b/migrations/2025-11-30-121230-add_monthly_downloads/up.sql @@ -0,0 +1,13 @@ +DROP MATERIALIZED VIEW recent_crate_downloads; +CREATE MATERIALIZED VIEW recent_crate_downloads (crate_id, downloads, monthly, weekly) AS + SELECT + crate_id, + SUM(version_downloads.downloads), + SUM(version_downloads.downloads) FILTER (WHERE version_downloads.date > CURRENT_DATE - 30), + SUM(version_downloads.downloads) FILTER (WHERE version_downloads.date > CURRENT_DATE - 7) + FROM version_downloads + INNER JOIN versions + ON version_downloads.version_id = versions.id + WHERE version_downloads.date > CURRENT_DATE - 90 + GROUP BY crate_id; +CREATE UNIQUE INDEX recent_crate_downloads_crate_id ON recent_crate_downloads (crate_id); diff --git a/src/controllers/krate/metadata.rs b/src/controllers/krate/metadata.rs index 8132279bce8..8ac8c719285 100644 --- a/src/controllers/krate/metadata.rs +++ b/src/controllers/krate/metadata.rs @@ -187,7 +187,9 @@ pub async fn find_crate( cats.as_deref(), false, downloads, - recent_downloads, + recent_downloads.map(|(d, _, _)| d), + recent_downloads.map(|(_, m, _)| m), + recent_downloads.map(|(_, _, w)| w), ); let encodable_versions = versions_publishers_and_audit_actions.map(|vpa| { @@ -289,14 +291,18 @@ fn load_recent_downloads( conn: &mut AsyncPgConnection, crate_id: i32, includes: bool, -) -> BoxFuture<'_, AppResult>> { +) -> BoxFuture<'_, AppResult>> { if !includes { return always_ready(|| Ok(None)).boxed(); } let fut = recent_crate_downloads::table .filter(recent_crate_downloads::crate_id.eq(crate_id)) - .select(recent_crate_downloads::downloads) + .select(( + recent_crate_downloads::downloads, + recent_crate_downloads::monthly, + recent_crate_downloads::weekly, + )) .get_result(conn); async move { Ok(fut.await.optional()?) }.boxed() } diff --git a/src/controllers/krate/publish.rs b/src/controllers/krate/publish.rs index fe2201c7c7b..ffb3bbd2d82 100644 --- a/src/controllers/krate/publish.rs +++ b/src/controllers/krate/publish.rs @@ -701,6 +701,8 @@ pub async fn publish(app: AppState, req: Parts, body: Body) -> AppResult>(); diff --git a/src/controllers/krate/update.rs b/src/controllers/krate/update.rs index 2550ef7dc7c..960cb741d72 100644 --- a/src/controllers/krate/update.rs +++ b/src/controllers/krate/update.rs @@ -189,6 +189,8 @@ async fn update_inner( false, downloads, recent_downloads, + None, + None, ); Ok(Json(PatchResponse { diff --git a/src/controllers/summary.rs b/src/controllers/summary.rs index fb14a898c1a..c913d29436c 100644 --- a/src/controllers/summary.rs +++ b/src/controllers/summary.rs @@ -181,6 +181,8 @@ fn encode_crates( false, record.total_downloads, record.recent_downloads, + None, + None, )) }) .collect() diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__auth__new_krate_with_bearer_token-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__auth__new_krate_with_bearer_token-2.snap index c9ecba99892..a2967d9029a 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__auth__new_krate_with_bearer_token-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__auth__new_krate_with_bearer_token-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo_new", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate-2.snap index d49e3abbb84..55cbdab1e26 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo_new", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_twice-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_twice-2.snap index 2d4ac8d32ec..c5f443b00a1 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_twice-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_twice-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "2.0.0", "max_version": "2.0.0", + "monthly_downloads": null, "name": "foo_twice", "newest_version": "2.0.0", "num_versions": 2, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_twice_alt-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_twice_alt-2.snap index 86310a1443c..27e22fa6d6d 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_twice_alt-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_twice_alt-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "2.0.0", "max_version": "2.0.0", + "monthly_downloads": null, "name": "foo_twice", "newest_version": "0.99.0", "num_versions": 2, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_weird_version-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_weird_version-2.snap index 4ac962b4dd9..18558f42b06 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_weird_version-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_weird_version-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": null, "max_version": "0.0.0-pre", + "monthly_downloads": null, "name": "foo_weird", "newest_version": "0.0.0-pre", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_with_token-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_with_token-2.snap index d49e3abbb84..55cbdab1e26 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_with_token-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__basics__new_krate_with_token-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo_new", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_1.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_1.snap index fcdf38c64b8..8763fcd2e3d 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_1.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_1.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0+foo", "max_version": "1.0.0+foo", + "monthly_downloads": null, "name": "foo", "newest_version": "1.0.0+foo", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_2.snap index 44ba9b58393..6ecf25ebbbc 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": null, "max_version": "1.0.0-beta.1", + "monthly_downloads": null, "name": "foo", "newest_version": "1.0.0-beta.1", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_3.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_3.snap index fcdf38c64b8..8763fcd2e3d 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_3.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__build_metadata__version_with_build_metadata@build_metadata_3.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0+foo", "max_version": "1.0.0+foo", + "monthly_downloads": null, "name": "foo", "newest_version": "1.0.0+foo", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__categories__good_categories-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__categories__good_categories-2.snap index a6c75cc9155..c016b4a529d 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__categories__good_categories-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__categories__good_categories-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo_good_cat", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__dependencies__dep_limit-4.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__dependencies__dep_limit-4.snap index fb761155819..96eff7ab5ff 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__dependencies__dep_limit-4.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__dependencies__dep_limit-4.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__edition__edition_is_saved-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__edition__edition_is_saved-2.snap index e49caf40320..2d74ff060c1 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__edition__edition_is_saved-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__edition__edition_is_saved-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__keywords__good_keywords-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__keywords__good_keywords-2.snap index 33ee243f3eb..11336f9d93a 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__keywords__good_keywords-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__keywords__good_keywords-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo_good_key", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__links__crate_with_links_field.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__links__crate_with_links_field.snap index c56f5430cf3..35a7de48273 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__links__crate_with_links_field.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__links__crate_with_links_field.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__manifest__boolean_readme-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__manifest__boolean_readme-2.snap index 42a2e6c15af..b0a93269a16 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__manifest__boolean_readme-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__manifest__boolean_readme-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__manifest__lib_and_bin_crate-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__manifest__lib_and_bin_crate-2.snap index 42a2e6c15af..b0a93269a16 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__manifest__lib_and_bin_crate-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__manifest__lib_and_bin_crate-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__max_size__tarball_between_default_axum_limit_and_max_upload_size-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__max_size__tarball_between_default_axum_limit_and_max_upload_size-2.snap index 85327b9f9ae..e685f6e9e44 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__max_size__tarball_between_default_axum_limit_and_max_upload_size-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__max_size__tarball_between_default_axum_limit_and_max_upload_size-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.1.0", "max_version": "1.1.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.1.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_empty_readme-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_empty_readme-2.snap index 73dad84c62f..03e01fdd3cd 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_empty_readme-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_empty_readme-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo_readme", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_readme-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_readme-2.snap index 73dad84c62f..03e01fdd3cd 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_readme-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_readme-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": null, "name": "foo_readme", "newest_version": "1.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_readme_and_plus_version-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_readme_and_plus_version-2.snap index e93f8621437..ead40fb4c62 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_readme_and_plus_version-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__readme__new_krate_with_readme_and_plus_version-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.0.0+foo", "max_version": "1.0.0+foo", + "monthly_downloads": null, "name": "foo_readme", "newest_version": "1.0.0+foo", "num_versions": 1, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__full_flow-7.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__full_flow-7.snap index 367b9c4420d..eea6d0b1355 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__full_flow-7.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__full_flow-7.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.1.0", "max_version": "1.1.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.1.0", "num_versions": 2, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__happy_path-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__happy_path-2.snap index 367b9c4420d..eea6d0b1355 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__happy_path-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__happy_path-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.1.0", "max_version": "1.1.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.1.0", "num_versions": 2, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__happy_path_with_fancy_auth_header-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__happy_path_with_fancy_auth_header-2.snap index 367b9c4420d..eea6d0b1355 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__happy_path_with_fancy_auth_header-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_github__happy_path_with_fancy_auth_header-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.1.0", "max_version": "1.1.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.1.0", "num_versions": 2, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__full_flow-7.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__full_flow-7.snap index 3b43123ce16..9e242bd6303 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__full_flow-7.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__full_flow-7.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.1.0", "max_version": "1.1.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.1.0", "num_versions": 2, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__happy_path-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__happy_path-2.snap index 3b43123ce16..9e242bd6303 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__happy_path-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__happy_path-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.1.0", "max_version": "1.1.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.1.0", "num_versions": 2, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__happy_path_with_fancy_auth_header-2.snap b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__happy_path_with_fancy_auth_header-2.snap index 3b43123ce16..9e242bd6303 100644 --- a/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__happy_path_with_fancy_auth_header-2.snap +++ b/src/tests/krate/publish/snapshots/integration__krate__publish__trustpub_gitlab__happy_path_with_fancy_auth_header-2.snap @@ -25,6 +25,7 @@ expression: response.json() }, "max_stable_version": "1.1.0", "max_version": "1.1.0", + "monthly_downloads": null, "name": "foo", "newest_version": "1.1.0", "num_versions": 2, @@ -33,6 +34,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "warnings": { diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__read__include_default_version-2.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__read__include_default_version-2.snap index b2dcf2edbd4..69444ad9b11 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__read__include_default_version-2.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__read__include_default_version-2.snap @@ -26,6 +26,7 @@ expression: response.json() }, "max_stable_version": null, "max_version": "0.0.0", + "monthly_downloads": null, "name": "foo_default_version", "newest_version": "0.0.0", "num_versions": 3, @@ -34,6 +35,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "keywords": null, diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__read__new_name-2.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__read__new_name-2.snap index 039673bf580..1e33e5b35bb 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__read__new_name-2.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__read__new_name-2.snap @@ -26,6 +26,7 @@ expression: response.json() }, "max_stable_version": null, "max_version": "0.0.0", + "monthly_downloads": null, "name": "new", "newest_version": "0.0.0", "num_versions": 1, @@ -34,6 +35,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "keywords": null, diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__read__show-2.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__read__show-2.snap index e57bcb96ca1..c8b87429821 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__read__show-2.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__read__show-2.snap @@ -28,6 +28,7 @@ expression: response.json() }, "max_stable_version": "1.0.0", "max_version": "1.0.0", + "monthly_downloads": 10, "name": "foo_show", "newest_version": "0.5.1", "num_versions": 3, @@ -40,6 +41,7 @@ expression: response.json() 2, 1 ], + "weekly_downloads": 10, "yanked": false }, "keywords": [ diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__read__show_all_yanked-2.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__read__show_all_yanked-2.snap index 3efa201fdef..8bd5b3654f9 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__read__show_all_yanked-2.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__read__show_all_yanked-2.snap @@ -28,6 +28,7 @@ expression: response.json() }, "max_stable_version": null, "max_version": "0.0.0", + "monthly_downloads": 10, "name": "foo_show", "newest_version": "0.0.0", "num_versions": 2, @@ -39,6 +40,7 @@ expression: response.json() 2, 1 ], + "weekly_downloads": 10, "yanked": true }, "keywords": [ diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__read__show_minimal-2.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__read__show_minimal-2.snap index 9eba2e9ef0e..61e4dfb21b6 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__read__show_minimal-2.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__read__show_minimal-2.snap @@ -26,6 +26,7 @@ expression: response.json() }, "max_stable_version": null, "max_version": "0.0.0", + "monthly_downloads": null, "name": "foo_show_minimal", "newest_version": "0.0.0", "num_versions": 3, @@ -34,6 +35,7 @@ expression: response.json() "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false }, "keywords": null, diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-3.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-3.snap index 4171996d570..ac6fba54222 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-3.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-3.snap @@ -25,6 +25,7 @@ expression: json }, "max_stable_version": null, "max_version": "0.0.0", + "monthly_downloads": null, "name": "foo", "newest_version": "0.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: json "trustpub_only": true, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false } } diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-6.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-6.snap index 8aaf1c25c3b..ec6de185963 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-6.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-6.snap @@ -25,6 +25,7 @@ expression: json }, "max_stable_version": null, "max_version": "0.0.0", + "monthly_downloads": null, "name": "foo", "newest_version": "0.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: json "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false } } diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-9.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-9.snap index 753aaab2edb..1f2e4f0d0f2 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-9.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__update__disable_trustpub_only-9.snap @@ -26,6 +26,7 @@ expression: json }, "max_stable_version": "0.99.0", "max_version": "0.99.0", + "monthly_downloads": null, "name": "foo", "newest_version": "0.99.0", "num_versions": 1, @@ -36,6 +37,7 @@ expression: json "versions": [ 1 ], + "weekly_downloads": null, "yanked": false }, "keywords": [], diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-3.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-3.snap index 8aaf1c25c3b..ec6de185963 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-3.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-3.snap @@ -25,6 +25,7 @@ expression: json }, "max_stable_version": null, "max_version": "0.0.0", + "monthly_downloads": null, "name": "foo", "newest_version": "0.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: json "trustpub_only": false, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false } } diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-6.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-6.snap index 4171996d570..ac6fba54222 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-6.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-6.snap @@ -25,6 +25,7 @@ expression: json }, "max_stable_version": null, "max_version": "0.0.0", + "monthly_downloads": null, "name": "foo", "newest_version": "0.0.0", "num_versions": 1, @@ -33,6 +34,7 @@ expression: json "trustpub_only": true, "updated_at": "[datetime]", "versions": null, + "weekly_downloads": null, "yanked": false } } diff --git a/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-9.snap b/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-9.snap index 7aed958afbd..2c756f23e59 100644 --- a/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-9.snap +++ b/src/tests/routes/crates/snapshots/integration__routes__crates__update__enable_trustpub_only-9.snap @@ -26,6 +26,7 @@ expression: json }, "max_stable_version": "0.99.0", "max_version": "0.99.0", + "monthly_downloads": null, "name": "foo", "newest_version": "0.99.0", "num_versions": 1, @@ -36,6 +37,7 @@ expression: json "versions": [ 1 ], + "weekly_downloads": null, "yanked": false }, "keywords": [], diff --git a/src/tests/snapshots/integration__openapi__openapi_snapshot-2.snap b/src/tests/snapshots/integration__openapi__openapi_snapshot-2.snap index 519d256df4f..159f4eed827 100644 --- a/src/tests/snapshots/integration__openapi__openapi_snapshot-2.snap +++ b/src/tests/snapshots/integration__openapi__openapi_snapshot-2.snap @@ -325,6 +325,15 @@ expression: response.json() "example": "2.0.0-beta.1", "type": "string" }, + "monthly_downloads": { + "description": "The total number of downloads for this crate in the last month.", + "example": 123456, + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "name": { "description": "The name of the crate.", "example": "serde", @@ -381,6 +390,15 @@ expression: response.json() "null" ] }, + "weekly_downloads": { + "description": "The total number of downloads for this crate in the last week.", + "example": 56789, + "format": "int64", + "type": [ + "integer", + "null" + ] + }, "yanked": { "description": "Whether all versions of this crate have been yanked.", "type": "boolean"