From 80d9d95214f30459ae7f7fbb2baf30f207cf0ecc Mon Sep 17 00:00:00 2001 From: Bryan Burgers Date: Wed, 27 Feb 2019 15:42:01 -0600 Subject: [PATCH 1/2] Add a `include_yanked` parameter to /api/v1/crates Add a URL parameter to the `/api/v1/crates` endpoint that specifies whether or not the endpoint should return crates that have been fully yanked - i.e., where every version has been yanked. If not specified, this defaults to `true` because we have typically always returned all matching crates, whether or not their versions have been yanked. This makes it possible to display only non-yanked crates at various places in the UI, and is especially intended for the user and team pages to display only non-yanked crates. --- src/controllers/krate/search.rs | 15 +++++++++++- src/tests/krate.rs | 43 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/src/controllers/krate/search.rs b/src/controllers/krate/search.rs index 8120b3e860..6086c8df31 100644 --- a/src/controllers/krate/search.rs +++ b/src/controllers/krate/search.rs @@ -1,5 +1,6 @@ //! Endpoint for searching and discovery functionality +use diesel::dsl::*; use diesel::sql_types::{NotNull, Nullable}; use diesel_full_text_search::*; @@ -33,7 +34,6 @@ use crate::models::krate::{canon_crate_name, ALL_COLUMNS}; /// function out to cover the different use cases, and create unit tests /// for them. pub fn search(req: &mut dyn Request) -> CargoResult { - use diesel::dsl::sql; use diesel::sql_types::{Bool, Text}; let conn = req.db_conn()?; @@ -44,6 +44,10 @@ pub fn search(req: &mut dyn Request) -> CargoResult { .map(|s| &**s) .unwrap_or("recent-downloads"); let mut has_filter = false; + let include_yanked = params + .get("include_yanked") + .map(|s| s == "yes") + .unwrap_or(true); let selection = ( ALL_COLUMNS, @@ -154,6 +158,15 @@ pub fn search(req: &mut dyn Request) -> CargoResult { ); } + if !include_yanked { + has_filter = true; + query = query.filter(exists( + versions::table + .filter(versions::crate_id.eq(crates::id)) + .filter(versions::yanked.eq(false)), + )); + } + if sort == "downloads" { query = query.then_order_by(crates::downloads.desc()) } else if sort == "recent-downloads" { diff --git a/src/tests/krate.rs b/src/tests/krate.rs index 0ad371f540..9489c78eca 100644 --- a/src/tests/krate.rs +++ b/src/tests/krate.rs @@ -461,6 +461,49 @@ fn loose_search_order() { assert_eq!(search_temp.crates.len(), 3); } +#[test] +fn index_include_yanked() { + let (app, anon, user) = TestApp::init().with_user(); + let user = user.as_model(); + + app.db(|conn| { + CrateBuilder::new("unyanked", user.id) + .version(VersionBuilder::new("1.0.0")) + .version(VersionBuilder::new("2.0.0")) + .expect_build(conn); + + CrateBuilder::new("newest_yanked", user.id) + .version(VersionBuilder::new("1.0.0")) + .version(VersionBuilder::new("2.0.0").yanked(true)) + .expect_build(conn); + + CrateBuilder::new("oldest_yanked", user.id) + .version(VersionBuilder::new("1.0.0").yanked(true)) + .version(VersionBuilder::new("2.0.0")) + .expect_build(conn); + + CrateBuilder::new("all_yanked", user.id) + .version(VersionBuilder::new("1.0.0").yanked(true)) + .version(VersionBuilder::new("2.0.0").yanked(true)) + .expect_build(conn); + }); + + // Include fully yanked (all versions were yanked) crates + let json = anon.search("include_yanked=yes&sort=alphabetical"); + assert_eq!(json.meta.total, 4); + assert_eq!(json.crates[0].name, "all_yanked"); + assert_eq!(json.crates[1].name, "newest_yanked"); + assert_eq!(json.crates[2].name, "oldest_yanked"); + assert_eq!(json.crates[3].name, "unyanked"); + + // Do not include fully yanked (all versions were yanked) crates + let json = anon.search("include_yanked=no&sort=alphabetical"); + assert_eq!(json.meta.total, 3); + assert_eq!(json.crates[0].name, "newest_yanked"); + assert_eq!(json.crates[1].name, "oldest_yanked"); + assert_eq!(json.crates[2].name, "unyanked"); +} + #[test] fn show() { let (app, anon, user) = TestApp::init().with_user(); From 7b4e10e19e2330368a960be4c484270d3ed18abb Mon Sep 17 00:00:00 2001 From: Bryan Burgers Date: Thu, 28 Feb 2019 15:40:24 -0600 Subject: [PATCH 2/2] Exclude yanked crates on user/team pages Don't display crates that have been fully yanked - i.e., crates that where every version has been yanked - on user and team pages. --- app/routes/team.js | 1 + app/routes/user.js | 1 + 2 files changed, 2 insertions(+) diff --git a/app/routes/team.js b/app/routes/team.js index b311521f66..a0c539e215 100644 --- a/app/routes/team.js +++ b/app/routes/team.js @@ -16,6 +16,7 @@ export default Route.extend({ return this.store.queryRecord('team', { team_id }).then( team => { params.team_id = team.get('id'); + params.include_yanked = 'n'; return RSVP.hash({ crates: this.store.query('crate', params), team, diff --git a/app/routes/user.js b/app/routes/user.js index 265e31cc7f..a47e3b258c 100644 --- a/app/routes/user.js +++ b/app/routes/user.js @@ -15,6 +15,7 @@ export default Route.extend({ return this.store.queryRecord('user', { user_id }).then( user => { params.user_id = user.get('id'); + params.include_yanked = 'n'; return RSVP.hash({ crates: this.store.query('crate', params), user,