Skip to content

Commit 3c27993

Browse files
committed
Poll for updates for a user
1 parent a16318d commit 3c27993

File tree

3 files changed

+88
-1
lines changed

3 files changed

+88
-1
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub fn middleware(app: Arc<App>) -> MiddlewareBuilder {
7373
router.get("/logout", C(user::logout));
7474
router.get("/me", C(user::me));
7575
router.put("/me/reset_token", C(user::reset_token));
76+
router.get("/me/updates", C(user::updates));
7677
router.get("/summary", C(krate::summary));
7778
router.get("/crates", C(krate::index));
7879
router.get("/crates/:crate_id", C(krate::show));

src/tests/user.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use conduit_test::MockRequest;
77
use cargo_registry::krate::EncodableCrate;
88
use cargo_registry::user::{User, EncodableUser};
99
use cargo_registry::db::RequestTransaction;
10+
use cargo_registry::version::EncodableVersion;
1011

1112
#[deriving(Decodable)]
1213
struct AuthResponse { url: String, state: String }
@@ -106,3 +107,52 @@ fn my_packages() {
106107
let response: Response = ::json(&mut response);
107108
assert_eq!(response.crates.len(), 1);
108109
}
110+
111+
#[test]
112+
fn following() {
113+
#[deriving(Decodable)]
114+
struct R {
115+
versions: Vec<EncodableVersion>,
116+
meta: Meta,
117+
}
118+
#[deriving(Decodable)] struct Meta { more: bool }
119+
120+
let (_b, app, middle) = ::app();
121+
let mut req = ::req(app, conduit::Get, "/");
122+
::mock_user(&mut req, ::user());
123+
::mock_crate(&mut req, "foo");
124+
::mock_crate(&mut req, "bar");
125+
126+
let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
127+
.with_method(conduit::Get)));
128+
let r = ::json::<R>(&mut response);
129+
assert_eq!(r.versions.len(), 0);
130+
assert_eq!(r.meta.more, false);
131+
132+
ok_resp!(middle.call(req.with_path("/crates/foo/follow")
133+
.with_method(conduit::Put)));
134+
ok_resp!(middle.call(req.with_path("/crates/bar/follow")
135+
.with_method(conduit::Put)));
136+
137+
let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
138+
.with_method(conduit::Get)));
139+
let r = ::json::<R>(&mut response);
140+
assert_eq!(r.versions.len(), 2);
141+
assert_eq!(r.meta.more, false);
142+
143+
let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
144+
.with_method(conduit::Get)
145+
.with_query("per_page=1")));
146+
let r = ::json::<R>(&mut response);
147+
assert_eq!(r.versions.len(), 1);
148+
assert_eq!(r.meta.more, true);
149+
150+
ok_resp!(middle.call(req.with_path("/crates/bar/unfollow")
151+
.with_method(conduit::Put)));
152+
let mut response = ok_resp!(middle.call(req.with_path("/me/updates")
153+
.with_method(conduit::Get)
154+
.with_query("page=2&per_page=1")));
155+
let r = ::json::<R>(&mut response);
156+
assert_eq!(r.versions.len(), 0);
157+
assert_eq!(r.meta.more, false);
158+
}

src/user/mod.rs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ use oauth2::Authorization;
1010
use pg::PostgresRow;
1111
use pg::types::ToSql;
1212

13+
use {Model, Version};
1314
use app::RequestApp;
1415
use db::{Connection, RequestTransaction};
15-
use util::{RequestUtils, CargoResult, internal, Require, ChainError, human};
1616
use util::errors::NotFound;
17+
use util::{RequestUtils, CargoResult, internal, Require, ChainError, human};
18+
use version::EncodableVersion;
1719

1820
pub use self::middleware::{Middleware, RequestUser};
1921

@@ -225,3 +227,37 @@ pub fn me(req: &mut Request) -> CargoResult<Response> {
225227
let token = user.api_token.clone();
226228
Ok(req.json(&R{ user: user.clone().encodable(), api_token: token }))
227229
}
230+
231+
pub fn updates(req: &mut Request) -> CargoResult<Response> {
232+
let user = try!(req.user());
233+
let (offset, limit) = try!(req.pagination(10, 100));
234+
let tx = try!(req.tx());
235+
let sql = "SELECT versions.*, crates.name AS crate_name
236+
FROM versions
237+
INNER JOIN follows
238+
ON follows.user_id = $1 AND
239+
follows.crate_id = versions.crate_id
240+
INNER JOIN crates
241+
ON crates.id = versions.crate_id
242+
ORDER BY versions.created_at DESC
243+
OFFSET $2 LIMIT $3";
244+
245+
let stmt = try!(tx.prepare(sql));
246+
let mut versions = Vec::new();
247+
for row in try!(stmt.query(&[&user.id, &offset, &limit])) {
248+
let version: Version = Model::from_row(&row);
249+
let name: String = row.get("crate_name");
250+
versions.push(version.encodable(name.as_slice()));
251+
}
252+
253+
let sql = format!("SELECT 1 WHERE EXISTS({})", sql);
254+
let stmt = try!(tx.prepare(sql.as_slice()));
255+
let more = try!(stmt.query(&[&user.id, &(offset + limit), &limit]))
256+
.next().is_some();
257+
258+
#[deriving(Encodable)]
259+
struct R { versions: Vec<EncodableVersion>, meta: Meta }
260+
#[deriving(Encodable)]
261+
struct Meta { more: bool }
262+
Ok(req.json(&R{ versions: versions, meta: Meta { more: more } }))
263+
}

0 commit comments

Comments
 (0)