Skip to content

Commit b2ee89c

Browse files
committed
Merge remote-tracking branch 'upstream/pr/758' into merge-candidate
2 parents 60fe8ed + 215195f commit b2ee89c

File tree

2 files changed

+64
-161
lines changed

2 files changed

+64
-161
lines changed

src/category.rs

+20-102
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::collections::HashSet;
21
use time::Timespec;
32

43
use conduit::{Request, Response};
@@ -68,17 +67,6 @@ impl Category {
6867
})
6968
}
7069

71-
pub fn find_by_slug(conn: &GenericConnection, slug: &str) -> CargoResult<Category> {
72-
let stmt = conn.prepare(
73-
"SELECT * FROM categories \
74-
WHERE slug = LOWER($1)",
75-
)?;
76-
let rows = stmt.query(&[&slug])?;
77-
rows.iter().next().chain_error(|| NotFound).map(|row| {
78-
Model::from_row(&row)
79-
})
80-
}
81-
8270
pub fn encodable(self) -> EncodableCategory {
8371
let Category {
8472
crates_cnt,
@@ -132,79 +120,13 @@ impl Category {
132120
})
133121
}
134122

135-
pub fn update_crate_old(
136-
conn: &GenericConnection,
137-
krate: &Crate,
138-
categories: &[String],
139-
) -> CargoResult<Vec<String>> {
140-
let old_categories = krate.categories(conn)?;
141-
let old_categories_ids: HashSet<_> = old_categories.iter().map(|cat| cat.id).collect();
142-
143-
// If a new category specified is not in the database, filter
144-
// it out and don't add it. Return it to be able to warn about it.
145-
let mut invalid_categories = vec![];
146-
let new_categories: Vec<Category> = categories
147-
.iter()
148-
.flat_map(|c| match Category::find_by_slug(conn, c) {
149-
Ok(cat) => Some(cat),
150-
Err(_) => {
151-
invalid_categories.push(c.to_string());
152-
None
153-
}
154-
})
155-
.collect();
156-
157-
let new_categories_ids: HashSet<_> = new_categories.iter().map(|cat| cat.id).collect();
123+
pub fn count_toplevel(conn: &PgConnection) -> QueryResult<i64> {
124+
use self::categories::dsl::*;
158125

159-
let to_rm: Vec<_> = old_categories_ids
160-
.difference(&new_categories_ids)
161-
.cloned()
162-
.collect();
163-
let to_add: Vec<_> = new_categories_ids
164-
.difference(&old_categories_ids)
165-
.cloned()
166-
.collect();
167-
168-
if !to_rm.is_empty() {
169-
conn.execute(
170-
"DELETE FROM crates_categories \
171-
WHERE category_id = ANY($1) \
172-
AND crate_id = $2",
173-
&[&to_rm, &krate.id],
174-
)?;
175-
}
176-
177-
if !to_add.is_empty() {
178-
let insert: Vec<_> = to_add
179-
.into_iter()
180-
.map(|id| format!("({}, {})", krate.id, id))
181-
.collect();
182-
let insert = insert.join(", ");
183-
conn.execute(
184-
&format!(
185-
"INSERT INTO crates_categories \
186-
(crate_id, category_id) VALUES {}",
187-
insert
188-
),
189-
&[],
190-
)?;
191-
}
192-
193-
Ok(invalid_categories)
194-
}
195-
196-
pub fn count_toplevel(conn: &PgConnection) -> CargoResult<i64> {
197-
use diesel::expression::*;
198-
use diesel::types::*;
199-
200-
let query = sql::<BigInt>(
201-
"\
202-
SELECT COUNT(*) \
203-
FROM categories \
204-
WHERE category NOT LIKE '%::%'",
205-
);
206-
let count = query.get_result(&*conn)?;
207-
Ok(count)
126+
categories
127+
.filter(category.not_like("%::%"))
128+
.count()
129+
.get_result(conn)
208130
}
209131

210132
pub fn toplevel(
@@ -270,27 +192,23 @@ impl Category {
270192
Ok(categories)
271193
}
272194

273-
pub fn subcategories(&self, conn: &PgConnection) -> CargoResult<Vec<Category>> {
274-
use diesel::expression::*;
275-
use diesel::types::*;
195+
pub fn subcategories(&self, conn: &PgConnection) -> QueryResult<Vec<Category>> {
196+
use diesel::expression::dsl::*;
197+
use diesel::types::Text;
276198

277-
let query = sql::<(Integer, Text, Text, Text, Integer, Timestamp)>(
278-
"\
279-
SELECT c.id, c.category, c.slug, c.description, \
199+
sql(
200+
"SELECT c.id, c.category, c.slug, c.description, \
280201
COALESCE (( \
281202
SELECT sum(c2.crates_cnt)::int \
282203
FROM categories as c2 \
283204
WHERE c2.slug = c.slug \
284205
OR c2.slug LIKE c.slug || '::%' \
285-
), 0) as crates_cnt, \
286-
c.created_at \
206+
), 0) as crates_cnt, c.created_at \
287207
FROM categories as c \
288208
WHERE c.category ILIKE $1 || '::%' \
289209
AND c.category NOT ILIKE $1 || '::%::%'",
290-
).bind::<Text, _>(&self.category);
291-
292-
let rows = query.get_results(conn)?;
293-
Ok(rows)
210+
).bind::<Text, _>(&self.category)
211+
.load(conn)
294212
}
295213
}
296214

@@ -361,14 +279,14 @@ pub fn index(req: &mut Request) -> CargoResult<Response> {
361279

362280
/// Handles the `GET /categories/:category_id` route.
363281
pub fn show(req: &mut Request) -> CargoResult<Response> {
364-
use self::categories::dsl::{categories, slug};
365-
366-
let id = &req.params()["category_id"];
282+
let slug = &req.params()["category_id"];
367283
let conn = req.db_conn()?;
368-
let cat = categories.filter(slug.eq(id)).first::<Category>(&*conn)?;
369-
let subcats = cat.subcategories(&*conn)?
284+
let cat = categories::table
285+
.filter(categories::slug.eq(::lower(slug)))
286+
.first::<Category>(&*conn)?;
287+
let subcats = cat.subcategories(&conn)?
370288
.into_iter()
371-
.map(|s| s.encodable())
289+
.map(Category::encodable)
372290
.collect();
373291

374292
let cat = cat.encodable();

src/tests/category.rs

+44-59
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use conduit::{Handler, Method};
2-
use conduit_test::MockRequest;
32

43
use cargo_registry::category::{Category, EncodableCategory, EncodableCategoryWithSubcategories};
54

@@ -42,6 +41,7 @@ fn index() {
4241
.create_or_update(&conn)
4342
.unwrap();
4443
}
44+
4545
let mut response = ok_resp!(middle.call(&mut req));
4646
let json: CategoryList = ::json(&mut response);
4747

@@ -64,7 +64,6 @@ fn show() {
6464
{
6565
let conn = t!(app.diesel_database.get());
6666

67-
// Create a category and a subcategory
6867
t!(::new_category("Foo Bar", "foo-bar").create_or_update(&conn));
6968
t!(::new_category("Foo Bar::Baz", "foo-bar::baz").create_or_update(&conn));
7069
}
@@ -82,11 +81,13 @@ fn show() {
8281
fn update_crate() {
8382
let (_b, app, middle) = ::app();
8483
let mut req = ::req(app.clone(), Method::Get, "/api/v1/categories/foo");
85-
let cnt = |req: &mut MockRequest, cat: &str| {
86-
req.with_path(&format!("/api/v1/categories/{}", cat));
87-
let mut response = ok_resp!(middle.call(req));
88-
::json::<GoodCategory>(&mut response).category.crates_cnt as usize
89-
};
84+
macro_rules! cnt {
85+
($req: expr, $cat: expr) => {{
86+
$req.with_path(&format!("/api/v1/categories/{}", $cat));
87+
let mut response = ok_resp!(middle.call($req));
88+
::json::<GoodCategory>(&mut response).category.crates_cnt as usize
89+
}}
90+
}
9091

9192
let krate = {
9293
let conn = t!(app.diesel_database.get());
@@ -97,61 +98,48 @@ fn update_crate() {
9798
};
9899

99100
// Updating with no categories has no effect
100-
{
101-
let conn = t!(app.diesel_database.get());
102-
Category::update_crate(&conn, &krate, &[]).unwrap();
103-
}
104-
assert_eq!(cnt(&mut req, "cat1"), 0);
105-
assert_eq!(cnt(&mut req, "category-2"), 0);
101+
Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &[]).unwrap();
102+
assert_eq!(cnt!(&mut req, "cat1"), 0);
103+
assert_eq!(cnt!(&mut req, "category-2"), 0);
106104

107105
// Happy path adding one category
108-
{
109-
let conn = t!(app.diesel_database.get());
110-
Category::update_crate(&conn, &krate, &["cat1"]).unwrap();
111-
}
112-
assert_eq!(cnt(&mut req, "cat1"), 1);
113-
assert_eq!(cnt(&mut req, "category-2"), 0);
106+
Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &["cat1"]).unwrap();
107+
assert_eq!(cnt!(&mut req, "cat1"), 1);
108+
assert_eq!(cnt!(&mut req, "category-2"), 0);
114109

115110
// Replacing one category with another
116-
{
117-
let conn = t!(app.diesel_database.get());
118-
Category::update_crate(&conn, &krate, &["category-2"]).unwrap();
119-
}
120-
assert_eq!(cnt(&mut req, "cat1"), 0);
121-
assert_eq!(cnt(&mut req, "category-2"), 1);
111+
Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &["category-2"]).unwrap();
112+
assert_eq!(cnt!(&mut req, "cat1"), 0);
113+
assert_eq!(cnt!(&mut req, "category-2"), 1);
122114

123115
// Removing one category
124-
{
125-
let conn = t!(app.diesel_database.get());
126-
Category::update_crate(&conn, &krate, &[]).unwrap();
127-
}
128-
assert_eq!(cnt(&mut req, "cat1"), 0);
129-
assert_eq!(cnt(&mut req, "category-2"), 0);
116+
Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &[]).unwrap();
117+
assert_eq!(cnt!(&mut req, "cat1"), 0);
118+
assert_eq!(cnt!(&mut req, "category-2"), 0);
130119

131120
// Adding 2 categories
132-
{
133-
let conn = t!(app.diesel_database.get());
134-
Category::update_crate(&conn, &krate, &["cat1", "category-2"]).unwrap();
135-
}
136-
assert_eq!(cnt(&mut req, "cat1"), 1);
137-
assert_eq!(cnt(&mut req, "category-2"), 1);
121+
Category::update_crate(
122+
&app.diesel_database.get().unwrap(),
123+
&krate,
124+
&["cat1", "category-2"],
125+
).unwrap();
126+
assert_eq!(cnt!(&mut req, "cat1"), 1);
127+
assert_eq!(cnt!(&mut req, "category-2"), 1);
138128

139129
// Removing all categories
140-
{
141-
let conn = t!(app.diesel_database.get());
142-
Category::update_crate(&conn, &krate, &[]).unwrap();
143-
}
144-
assert_eq!(cnt(&mut req, "cat1"), 0);
145-
assert_eq!(cnt(&mut req, "category-2"), 0);
130+
Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &[]).unwrap();
131+
assert_eq!(cnt!(&mut req, "cat1"), 0);
132+
assert_eq!(cnt!(&mut req, "category-2"), 0);
146133

147134
// Attempting to add one valid category and one invalid category
148-
let invalid_categories = {
149-
let conn = t!(app.diesel_database.get());
150-
Category::update_crate(&conn, &krate, &["cat1", "catnope"]).unwrap()
151-
};
152-
assert_eq!(invalid_categories, vec!["catnope".to_string()]);
153-
assert_eq!(cnt(&mut req, "cat1"), 1);
154-
assert_eq!(cnt(&mut req, "category-2"), 0);
135+
let invalid_categories = Category::update_crate(
136+
&app.diesel_database.get().unwrap(),
137+
&krate,
138+
&["cat1", "catnope"],
139+
).unwrap();
140+
assert_eq!(invalid_categories, vec!["catnope"]);
141+
assert_eq!(cnt!(&mut req, "cat1"), 1);
142+
assert_eq!(cnt!(&mut req, "category-2"), 0);
155143

156144
// Does not add the invalid category to the category list
157145
// (unlike the behavior of keywords)
@@ -162,12 +150,9 @@ fn update_crate() {
162150
assert_eq!(json.meta.total, 2);
163151

164152
// Attempting to add a category by display text; must use slug
165-
{
166-
let conn = t!(app.diesel_database.get());
167-
Category::update_crate(&conn, &krate, &["Category 2"]).unwrap();
168-
}
169-
assert_eq!(cnt(&mut req, "cat1"), 0);
170-
assert_eq!(cnt(&mut req, "category-2"), 0);
153+
Category::update_crate(&app.diesel_database.get().unwrap(), &krate, &["Category 2"]).unwrap();
154+
assert_eq!(cnt!(&mut req, "cat1"), 0);
155+
assert_eq!(cnt!(&mut req, "category-2"), 0);
171156

172157
// Add a category and its subcategory
173158
{
@@ -177,7 +162,7 @@ fn update_crate() {
177162
));
178163
Category::update_crate(&conn, &krate, &["cat1", "cat1::bar"]).unwrap();
179164
}
180-
assert_eq!(cnt(&mut req, "cat1"), 1);
181-
assert_eq!(cnt(&mut req, "cat1::bar"), 1);
182-
assert_eq!(cnt(&mut req, "category-2"), 0);
165+
assert_eq!(cnt!(&mut req, "cat1"), 1);
166+
assert_eq!(cnt!(&mut req, "cat1::bar"), 1);
167+
assert_eq!(cnt!(&mut req, "category-2"), 0);
183168
}

0 commit comments

Comments
 (0)