Skip to content

Commit b14e648

Browse files
committed
Merge branch 'rev-dep' of https://github.com/huonw/crates.io into huonw-rev-dep
2 parents 79f3a41 + ea25ea8 commit b14e648

File tree

10 files changed

+136
-12
lines changed

10 files changed

+136
-12
lines changed

app/models/crate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ export default DS.Model.extend({
2020
owners: DS.hasMany('users', {async: true}),
2121
version_downloads: DS.hasMany('version-download', {async: true}),
2222
keywords: DS.hasMany('keywords', {async: true}),
23+
reverse_dependencies: DS.hasMany('reverse-dependency', {async: true}),
2324
});

app/models/reverse-dependency.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Dependency from 'cargo/models/dependency';
2+
3+
export default Dependency;

app/router.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Router.map(function() {
1414
this.resource('crate', { path: '/crates/*crate_id' }, function() {
1515
this.route('download');
1616
this.route('versions');
17+
this.route('reverse_dependencies');
1718
});
1819
this.route('me', function() {
1920
this.route('crates');
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import Ember from 'ember';
2+
import Crate from 'cargo/models/crate';
3+
4+
export default Ember.Route.extend({
5+
afterModel: function(data) {
6+
console.log("afterModel");
7+
if (data instanceof Crate) {
8+
return data.get('reverse_dependencies');
9+
} else {
10+
return data.crate.get('reverse_dependencies');
11+
}
12+
},
13+
14+
setupController: function(controller, data) {
15+
if (data instanceof Crate) {
16+
data = {crate: data, reverse_dependencies: null};
17+
}
18+
this._super(controller, data.crate);
19+
},
20+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<div class='all-versions-back'>
2+
{{#link-to 'crate' this}}&#11013; Back to Main Page{{/link-to}}
3+
</div>
4+
5+
<div class='info'>
6+
<span class='small'>
7+
All <span class='num'>{{ reverse_dependencies.length }}</span>
8+
reverse dependencies of <span class='num'>{{ name }}</span>
9+
</span>
10+
</div>
11+
12+
<div id='crate-all-reverse-dependencies' class='white-rows'>
13+
{{#each model.reverse_dependencies}}
14+
<div class='row'>
15+
<div>
16+
{{#link-to 'crate' crate_id}}{{crate_id}}{{/link-to}} requires {{req}}
17+
{{#link-to 'crate' this}}{{ num }}{{/link-to}}
18+
</div>
19+
{{#link-to 'crate' this class='arrow'}}
20+
<img src="/assets/right-arrow-all-versions.png"/>
21+
{{/link-to}}
22+
</div>
23+
{{/each}}
24+
</div>

src/krate.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use url::{mod, Url};
1717
use {Model, User, Keyword, Version};
1818
use app::{App, RequestApp};
1919
use db::{Connection, RequestTransaction};
20+
use dependency::{Dependency, EncodableDependency};
2021
use download::{VersionDownload, EncodableVersionDownload};
2122
use git;
2223
use keyword::EncodableKeyword;
@@ -68,6 +69,7 @@ pub struct CrateLinks {
6869
pub version_downloads: String,
6970
pub versions: Option<String>,
7071
pub owners: Option<String>,
72+
pub reverse_dependencies: String,
7173
}
7274

7375
impl Crate {
@@ -253,6 +255,7 @@ impl Crate {
253255
version_downloads: format!("/api/v1/crates/{}/downloads", name),
254256
versions: versions_link,
255257
owners: Some(format!("/api/v1/crates/{}/owners", name)),
258+
reverse_dependencies: format!("/api/v1/crates/{}/reverse_dependencies", name)
256259
},
257260
}
258261
}
@@ -333,6 +336,22 @@ impl Crate {
333336
let rows = try!(stmt.query(&[&self.id]));
334337
Ok(rows.map(|r| Model::from_row(&r)).collect())
335338
}
339+
340+
/// Returns (dependency, dependent crate name)
341+
pub fn reverse_dependencies(&self, conn: &Connection) -> CargoResult<Vec<(Dependency, String)>> {
342+
let stmt = try!(conn.prepare("SELECT dependencies.*,
343+
crates.name AS crate_name
344+
FROM dependencies
345+
INNER JOIN versions
346+
ON versions.id = dependencies.version_id
347+
INNER JOIN crates
348+
ON crates.id = versions.crate_id
349+
WHERE dependencies.crate_id = $1
350+
AND versions.num = crates.max_version"));
351+
Ok(try!(stmt.query(&[&self.id])).map(|r| {
352+
(Model::from_row(&r), r.get("crate_name"))
353+
}).collect())
354+
}
336355
}
337356

338357
impl Model for Crate {
@@ -860,3 +879,18 @@ fn modify_owners(req: &mut Request, add: bool) -> CargoResult<Response> {
860879
struct R { ok: bool }
861880
Ok(req.json(&R{ ok: true }))
862881
}
882+
883+
pub fn reverse_dependencies(req: &mut Request) -> CargoResult<Response> {
884+
let name = &req.params()["crate_id"];
885+
let conn = try!(req.tx());
886+
let krate = try!(Crate::find_by_name(conn, name.as_slice()));
887+
let tx = try!(req.tx());
888+
let rev_deps = try!(krate.reverse_dependencies(tx));
889+
let rev_deps = rev_deps.into_iter().map(|(dep, crate_name)| {
890+
dep.encodable(crate_name.as_slice())
891+
}).collect();
892+
893+
#[deriving(Encodable)]
894+
struct R { reverse_dependencies: Vec<EncodableDependency> }
895+
Ok(req.json(&R{ reverse_dependencies: rev_deps }))
896+
}

src/lib.rs

100644100755
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ pub fn middleware(app: Arc<App>) -> MiddlewareBuilder {
8989
api_router.delete("/crates/:crate_id/owners", C(krate::remove_owners));
9090
api_router.delete("/crates/:crate_id/:version/yank", C(version::yank));
9191
api_router.put("/crates/:crate_id/:version/unyank", C(version::unyank));
92+
api_router.get("/crates/:crate_id/reverse_dependencies", C(krate::reverse_dependencies));
9293
api_router.get("/versions", C(version::index));
9394
api_router.get("/versions/:version_id", C(version::show));
9495
api_router.get("/keywords", C(keyword::index));

src/tests/all.rs

100644100755
Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use conduit_test::MockRequest;
2424
use cargo_registry::app::App;
2525
use cargo_registry::db::{mod, RequestTransaction};
2626
use cargo_registry::{User, Crate, Version, Keyword};
27+
use cargo_registry::util::CargoResult;
2728

2829
macro_rules! t( ($e:expr) => (
2930
match $e {
@@ -213,8 +214,14 @@ fn mock_user(req: &mut Request, u: User) -> User {
213214
}
214215

215216
fn mock_crate(req: &mut Request, krate: Crate) -> Crate {
217+
let (c, v) = mock_crate_vers(req, krate, &semver::Version::parse("1.0.0").unwrap());
218+
v.unwrap();
219+
c
220+
}
221+
fn mock_crate_vers(req: &mut Request, krate: Crate, v: &semver::Version)
222+
-> (Crate, CargoResult<Version>) {
216223
let user = req.extensions().find::<User>().unwrap();
217-
let krate = Crate::find_or_insert(req.tx().unwrap(), krate.name.as_slice(),
224+
let mut krate = Crate::find_or_insert(req.tx().unwrap(), krate.name.as_slice(),
218225
user.id, &krate.description,
219226
&krate.homepage,
220227
&krate.documentation,
@@ -224,10 +231,8 @@ fn mock_crate(req: &mut Request, krate: Crate) -> Crate {
224231
&krate.license).unwrap();
225232
Keyword::update_crate(req.tx().unwrap(), &krate,
226233
krate.keywords.as_slice()).unwrap();
227-
Version::insert(req.tx().unwrap(), krate.id,
228-
&semver::Version::parse("1.0.0").unwrap(),
229-
&HashMap::new(), &[]).unwrap();
230-
return krate;
234+
let v = krate.add_version(req.tx().unwrap(), v, &HashMap::new(), &[]);
235+
(krate, v)
231236
}
232237

233238
fn mock_keyword(req: &mut Request, name: &str) -> Keyword {

src/tests/krate.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ struct CrateResponse { krate: EncodableCrate, versions: Vec<EncodableVersion> }
3232
#[deriving(Decodable)]
3333
struct Deps { dependencies: Vec<EncodableDependency> }
3434
#[deriving(Decodable)]
35+
struct RevDeps { reverse_dependencies: Vec<EncodableDependency> }
36+
#[deriving(Decodable)]
3537
struct Downloads { version_downloads: Vec<EncodableVersionDownload> }
3638

3739
#[test]
@@ -504,7 +506,7 @@ fn dependencies() {
504506
let c1 = ::krate("foo");
505507
let c2 = ::krate("bar");
506508
middle.add(::middleware::MockUser(user.clone()));
507-
middle.add(::middleware::MockDependency(c1.clone(), c2.clone()));
509+
middle.add(::middleware::MockDependency(c1.clone(), "1.0.0", c2.clone()));
508510
let rel = format!("/api/v1/crates/{}/1.0.0/dependencies", c1.name);
509511
let mut req = MockRequest::new(conduit::Get, rel.as_slice());
510512
let mut response = ok_resp!(middle.call(&mut req));
@@ -700,3 +702,32 @@ fn bad_keywords() {
700702
::json::<::Bad>(&mut response);
701703
}
702704
}
705+
706+
#[test]
707+
fn reverse_dependencies() {
708+
let (_b, _app, mut middle) = ::app();
709+
let user = ::user("foo");
710+
let c1 = ::krate("foo");
711+
let c2 = ::krate("bar");
712+
middle.add(::middleware::MockUser(user.clone()));
713+
// multiple dependencies of c1 on c2, to detect we handle this
714+
// correctly.
715+
middle.add(::middleware::MockDependency(c1.clone(), "1.0.0", c2.clone()));
716+
middle.add(::middleware::MockDependency(c1.clone(), "1.1.0", c2.clone()));
717+
718+
let rel = format!("/api/v1/crates/{}/reverse_dependencies", c2.name);
719+
let mut req = MockRequest::new(conduit::Get, rel.as_slice());
720+
let mut response = ok_resp!(middle.call(&mut req));
721+
let deps = ::json::<RevDeps>(&mut response);
722+
assert_eq!(deps.reverse_dependencies.len(), 1);
723+
assert_eq!(deps.reverse_dependencies[0].crate_id.as_slice(), &*c1.name);
724+
drop(req);
725+
726+
// c1 has no dependent crates.
727+
let rel = format!("/api/v1/crates/{}/reverse_dependencies", c1.name);
728+
let mut req = MockRequest::new(conduit::Get, rel.as_slice());
729+
let mut response = ok_resp!(middle.call(&mut req));
730+
let deps = ::json::<RevDeps>(&mut response);
731+
assert_eq!(deps.reverse_dependencies.len(), 0);
732+
drop(req);
733+
}

src/tests/middleware.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,19 @@ impl Middleware for MockCrate {
2828
}
2929
}
3030

31-
pub struct MockDependency(pub Crate, pub Crate);
31+
pub struct MockDependency(pub Crate, pub &'static str, pub Crate);
3232

3333
impl Middleware for MockDependency {
3434
fn before(&self, req: &mut Request) -> Result<(), Box<Show + 'static>> {
35-
let MockDependency(ref a, ref b) = *self;
36-
let crate_a = ::mock_crate(req, a.clone());
37-
let crate_b = ::mock_crate(req, b.clone());
38-
let va = crate_a.versions(req.tx().unwrap()).unwrap()[0].id;
39-
Dependency::insert(req.tx().unwrap(), va, crate_b.id,
35+
let MockDependency(ref a, version, ref b) = *self;
36+
let vers = semver::Version::parse(version).unwrap();
37+
let (_crate_a, va) = ::mock_crate_vers(req, a.clone(), &vers);
38+
39+
// don't panic on duplicate uploads
40+
let (crate_b, _) = ::mock_crate_vers(req, b.clone(),
41+
&semver::Version::parse("1.0.0").unwrap());
42+
43+
Dependency::insert(req.tx().unwrap(), va.unwrap().id, crate_b.id,
4044
&semver::VersionReq::parse(">= 0").unwrap(),
4145
Kind::Normal,
4246
false, true, &[], &None).unwrap();

0 commit comments

Comments
 (0)