Skip to content

Commit 57ae2d7

Browse files
committed
Merge remote-tracking branch 'upstream/master' into sg-bump-diesel
2 parents 82485ac + 4628bcf commit 57ae2d7

File tree

13 files changed

+1352
-1167
lines changed

13 files changed

+1352
-1167
lines changed

src/krate/downloads.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//! Endpoint for exposing crate download counts
2+
//!
3+
//! The enpoints for download a crate and exposing version specific
4+
//! download counts are located in `krate::downloads`.
5+
6+
use std::cmp;
7+
8+
use conduit::{Request, Response};
9+
use conduit_router::RequestParams;
10+
use diesel::prelude::*;
11+
12+
use db::RequestTransaction;
13+
use download::{EncodableVersionDownload, VersionDownload};
14+
use schema::*;
15+
use util::{CargoResult, RequestUtils};
16+
use Version;
17+
18+
use super::{to_char, Crate};
19+
20+
/// Handles the `GET /crates/:crate_id/downloads` route.
21+
pub fn downloads(req: &mut Request) -> CargoResult<Response> {
22+
use diesel::dsl::*;
23+
use diesel::types::BigInt;
24+
25+
let crate_name = &req.params()["crate_id"];
26+
let conn = req.db_conn()?;
27+
let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?;
28+
29+
let mut versions = Version::belonging_to(&krate).load::<Version>(&*conn)?;
30+
versions.sort_by(|a, b| b.num.cmp(&a.num));
31+
let (latest_five, rest) = versions.split_at(cmp::min(5, versions.len()));
32+
33+
let downloads = VersionDownload::belonging_to(latest_five)
34+
.filter(version_downloads::date.gt(date(now - 90.days())))
35+
.order(version_downloads::date.asc())
36+
.load(&*conn)?
37+
.into_iter()
38+
.map(VersionDownload::encodable)
39+
.collect::<Vec<_>>();
40+
41+
let sum_downloads = sql::<BigInt>("SUM(version_downloads.downloads)");
42+
let extra = VersionDownload::belonging_to(rest)
43+
.select((
44+
to_char(version_downloads::date, "YYYY-MM-DD"),
45+
sum_downloads,
46+
))
47+
.filter(version_downloads::date.gt(date(now - 90.days())))
48+
.group_by(version_downloads::date)
49+
.order(version_downloads::date.asc())
50+
.load::<ExtraDownload>(&*conn)?;
51+
52+
#[derive(Serialize, Queryable)]
53+
struct ExtraDownload {
54+
date: String,
55+
downloads: i64,
56+
}
57+
#[derive(Serialize)]
58+
struct R {
59+
version_downloads: Vec<EncodableVersionDownload>,
60+
meta: Meta,
61+
}
62+
#[derive(Serialize)]
63+
struct Meta {
64+
extra_downloads: Vec<ExtraDownload>,
65+
}
66+
let meta = Meta {
67+
extra_downloads: extra,
68+
};
69+
Ok(req.json(&R {
70+
version_downloads: downloads,
71+
meta: meta,
72+
}))
73+
}

src/krate/follow.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//! Endpoints for managing a per user list of followed crates
2+
3+
use conduit::{Request, Response};
4+
use conduit_router::RequestParams;
5+
use diesel::associations::Identifiable;
6+
use diesel::prelude::*;
7+
use diesel;
8+
9+
use db::RequestTransaction;
10+
use schema::*;
11+
use user::RequestUser;
12+
use util::{CargoResult, RequestUtils};
13+
14+
use super::{Crate, Follow};
15+
16+
fn follow_target(req: &mut Request) -> CargoResult<Follow> {
17+
let user = req.user()?;
18+
let conn = req.db_conn()?;
19+
let crate_name = &req.params()["crate_id"];
20+
let crate_id = Crate::by_name(crate_name).select(crates::id).first(&*conn)?;
21+
Ok(Follow {
22+
user_id: user.id,
23+
crate_id: crate_id,
24+
})
25+
}
26+
27+
/// Handles the `PUT /crates/:crate_id/follow` route.
28+
pub fn follow(req: &mut Request) -> CargoResult<Response> {
29+
let follow = follow_target(req)?;
30+
let conn = req.db_conn()?;
31+
diesel::insert_into(follows::table)
32+
.values(&follow)
33+
.on_conflict_do_nothing()
34+
.execute(&*conn)?;
35+
#[derive(Serialize)]
36+
struct R {
37+
ok: bool,
38+
}
39+
Ok(req.json(&R { ok: true }))
40+
}
41+
42+
/// Handles the `DELETE /crates/:crate_id/follow` route.
43+
pub fn unfollow(req: &mut Request) -> CargoResult<Response> {
44+
let follow = follow_target(req)?;
45+
let conn = req.db_conn()?;
46+
diesel::delete(&follow).execute(&*conn)?;
47+
#[derive(Serialize)]
48+
struct R {
49+
ok: bool,
50+
}
51+
Ok(req.json(&R { ok: true }))
52+
}
53+
54+
/// Handles the `GET /crates/:crate_id/following` route.
55+
pub fn following(req: &mut Request) -> CargoResult<Response> {
56+
use diesel::dsl::exists;
57+
58+
let follow = follow_target(req)?;
59+
let conn = req.db_conn()?;
60+
let following = diesel::select(exists(follows::table.find(follow.id()))).get_result(&*conn)?;
61+
#[derive(Serialize)]
62+
struct R {
63+
following: bool,
64+
}
65+
Ok(req.json(&R {
66+
following: following,
67+
}))
68+
}

0 commit comments

Comments
 (0)