Skip to content

Commit 726906f

Browse files
Merge #1101
1101: Fixes #962 Backend: add "most_recently_downloaded" to 'summary' endpoint r=sgrif ## Description - Adds "most_recently_downloaded" property to JSON response returned from `api/v1/summary` - Adds tests for `api/v1/summary` endpoint
2 parents d3a4822 + 1f0c928 commit 726906f

File tree

2 files changed

+110
-5
lines changed

2 files changed

+110
-5
lines changed

src/krate/mod.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,8 @@ pub fn index(req: &mut Request) -> CargoResult<Response> {
777777

778778
/// Handles the `GET /summary` route.
779779
pub fn summary(req: &mut Request) -> CargoResult<Response> {
780+
use diesel::expression::{date, now, sql, DayAndMonthIntervalDsl};
781+
use diesel::types::{BigInt, Nullable};
780782
use schema::crates::dsl::*;
781783

782784
let conn = req.db_conn()?;
@@ -816,6 +818,20 @@ pub fn summary(req: &mut Request) -> CargoResult<Response> {
816818
.limit(10)
817819
.load(&*conn)?;
818820

821+
let recent_downloads = sql::<Nullable<BigInt>>("SUM(crate_downloads.downloads)");
822+
let most_recently_downloaded = crates
823+
.left_join(
824+
crate_downloads::table.on(
825+
id.eq(crate_downloads::crate_id)
826+
.and(crate_downloads::date.gt(date(now - 90.days()))),
827+
),
828+
)
829+
.group_by(id)
830+
.order(recent_downloads.desc().nulls_last())
831+
.limit(10)
832+
.select(ALL_COLUMNS)
833+
.load::<Crate>(&*conn)?;
834+
819835
let popular_keywords = keywords::table
820836
.order(keywords::crates_cnt.desc())
821837
.limit(10)
@@ -835,6 +851,7 @@ pub fn summary(req: &mut Request) -> CargoResult<Response> {
835851
num_crates: i64,
836852
new_crates: Vec<EncodableCrate>,
837853
most_downloaded: Vec<EncodableCrate>,
854+
most_recently_downloaded: Vec<EncodableCrate>,
838855
just_updated: Vec<EncodableCrate>,
839856
popular_keywords: Vec<EncodableKeyword>,
840857
popular_categories: Vec<EncodableCategory>,
@@ -844,6 +861,7 @@ pub fn summary(req: &mut Request) -> CargoResult<Response> {
844861
num_crates: num_crates,
845862
new_crates: encode_crates(new_crates)?,
846863
most_downloaded: encode_crates(most_downloaded)?,
864+
most_recently_downloaded: encode_crates(most_recently_downloaded)?,
847865
just_updated: encode_crates(just_updated)?,
848866
popular_keywords: popular_keywords,
849867
popular_categories: popular_categories,

src/tests/krate.rs

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ use std::collections::HashMap;
44
use std::io::prelude::*;
55
use std::fs::{self, File};
66

7+
use chrono::Utc;
78
use conduit::{Handler, Method};
8-
9-
use git2;
9+
use diesel::update;
1010
use self::diesel::prelude::*;
11-
use serde_json;
11+
use git2;
1212
use semver;
13+
use serde_json;
1314

1415
use cargo_registry::dependency::EncodableDependency;
1516
use cargo_registry::download::EncodableVersionDownload;
@@ -18,11 +19,11 @@ use cargo_registry::keyword::EncodableKeyword;
1819
use cargo_registry::krate::{Crate, EncodableCrate, MAX_NAME_LENGTH};
1920

2021
use cargo_registry::token::ApiToken;
21-
use cargo_registry::schema::{crates, versions};
22+
use cargo_registry::schema::{crates, metadata, versions};
2223

2324
use cargo_registry::upload as u;
2425
use cargo_registry::version::EncodableVersion;
25-
use cargo_registry::category::Category;
26+
use cargo_registry::category::{Category, EncodableCategory};
2627

2728
use {CrateList, CrateMeta, GoodCrate};
2829

@@ -51,6 +52,18 @@ struct Downloads {
5152
version_downloads: Vec<EncodableVersionDownload>,
5253
}
5354

55+
#[derive(Deserialize)]
56+
struct SummaryResponse {
57+
num_downloads: i64,
58+
num_crates: i64,
59+
new_crates: Vec<EncodableCrate>,
60+
most_downloaded: Vec<EncodableCrate>,
61+
most_recently_downloaded: Vec<EncodableCrate>,
62+
just_updated: Vec<EncodableCrate>,
63+
popular_keywords: Vec<EncodableKeyword>,
64+
popular_categories: Vec<EncodableCategory>,
65+
}
66+
5467
fn new_crate(name: &str) -> u::NewCrate {
5568
u::NewCrate {
5669
name: u::CrateName(name.to_string()),
@@ -984,6 +997,80 @@ fn summary_doesnt_die() {
984997
ok_resp!(middle.call(&mut req));
985998
}
986999

1000+
#[test]
1001+
fn summary_new_crates() {
1002+
let (_b, app, middle) = ::app();
1003+
let u;
1004+
let krate;
1005+
let krate2;
1006+
let krate3;
1007+
{
1008+
let conn = app.diesel_database.get().unwrap();
1009+
u = ::new_user("foo").create_or_update(&conn).unwrap();
1010+
1011+
krate = ::CrateBuilder::new("some_downloads", u.id)
1012+
.version(::VersionBuilder::new("0.1.0"))
1013+
.description("description")
1014+
.keyword("popular")
1015+
.downloads(20)
1016+
.recent_downloads(10)
1017+
.expect_build(&conn);
1018+
1019+
krate2 = ::CrateBuilder::new("most_recent_downloads", u.id)
1020+
.version(::VersionBuilder::new("0.2.0"))
1021+
.keyword("popular")
1022+
.downloads(5000)
1023+
.recent_downloads(50)
1024+
.expect_build(&conn);
1025+
1026+
krate3 = ::CrateBuilder::new("just_updated", u.id)
1027+
.version(::VersionBuilder::new("0.1.0"))
1028+
.expect_build(&conn);
1029+
1030+
::CrateBuilder::new("with_downloads", u.id)
1031+
.version(::VersionBuilder::new("0.3.0"))
1032+
.keyword("popular")
1033+
.downloads(1000)
1034+
.expect_build(&conn);
1035+
1036+
::new_category("Category 1", "cat1")
1037+
.create_or_update(&conn)
1038+
.unwrap();
1039+
Category::update_crate(&conn, &krate, &["cat1"]).unwrap();
1040+
Category::update_crate(&conn, &krate2, &["cat1"]).unwrap();
1041+
1042+
// set total_downloads global value for `num_downloads` prop
1043+
update(metadata::table)
1044+
.set(metadata::total_downloads.eq(6000))
1045+
.execute(&*conn)
1046+
.unwrap();
1047+
1048+
// update 'just_updated' krate. Others won't appear because updated_at == created_at.
1049+
let updated = Utc::now().naive_utc();
1050+
update(&krate3)
1051+
.set(crates::updated_at.eq(updated))
1052+
.execute(&*conn)
1053+
.unwrap();
1054+
}
1055+
1056+
let mut req = ::req(app.clone(), Method::Get, "/api/v1/summary");
1057+
let mut response = ok_resp!(middle.call(&mut req));
1058+
let json: SummaryResponse = ::json(&mut response);
1059+
1060+
assert_eq!(json.num_crates, 4);
1061+
assert_eq!(json.num_downloads, 6000);
1062+
assert_eq!(json.most_downloaded[0].name, "most_recent_downloads");
1063+
assert_eq!(
1064+
json.most_recently_downloaded[0].name,
1065+
"most_recent_downloads"
1066+
);
1067+
assert_eq!(json.popular_keywords[0].keyword, "popular");
1068+
assert_eq!(json.popular_categories[0].category, "Category 1");
1069+
assert_eq!(json.just_updated.len(), 1);
1070+
assert_eq!(json.just_updated[0].name, "just_updated");
1071+
assert_eq!(json.new_crates.len(), 4);
1072+
}
1073+
9871074
#[test]
9881075
fn download() {
9891076
use chrono::{Duration, Utc};

0 commit comments

Comments
 (0)