Skip to content

Commit 250c5f2

Browse files
committed
Touch a crate whenever its versions are changed
Prior to this commit, if uploading a new version of a crate didn't otherwise change the metadata (description, etc), the crate's updated_at record wouldn't be changed. The migration specifically checks if `updated_at` changed on the versions to make sure the touch doesn't happen if `downloads` was bumped. I wanted to avoid duplicating that here, so instead this will only happen if the row is new, or as a result of the trigger bumping updated_at on the version. I have specifically not included a query to correct existing data, as it's an irreversable operation. We should manually run a query like `UPDATE crates SET updated_at = MAX(crates.updated_at, SELECT(MAX(versions.updated_at) FROM versions WHERE versions.crate_id = crates.id))` if we want to correct existing data. Fixes #908.
1 parent 120f800 commit 250c5f2

File tree

6 files changed

+102
-2
lines changed

6 files changed

+102
-2
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DROP TRIGGER touch_crate ON versions;
2+
DROP FUNCTION touch_crate_on_version_modified();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CREATE OR REPLACE FUNCTION touch_crate_on_version_modified() RETURNS trigger AS $$
2+
BEGIN
3+
IF (
4+
TG_OP = 'INSERT' OR
5+
NEW.updated_at IS DISTINCT FROM OLD.updated_at
6+
) THEN
7+
UPDATE crates SET updated_at = CURRENT_TIMESTAMP WHERE
8+
crates.id = NEW.crate_id;
9+
END IF;
10+
RETURN NEW;
11+
END;
12+
$$ LANGUAGE plpgsql;
13+
14+
CREATE TRIGGER touch_crate BEFORE INSERT OR UPDATE ON versions
15+
FOR EACH ROW EXECUTE PROCEDURE touch_crate_on_version_modified();

src/schema.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,5 @@ table! {
183183
license -> Nullable<Varchar>,
184184
}
185185
}
186+
187+
operator_allowed!(crates::updated_at, Sub, sub);

src/tests/all.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,10 +478,11 @@ fn sign_in_as(req: &mut Request, user: &User) {
478478
);
479479
}
480480

481-
fn sign_in(req: &mut Request, app: &App) {
481+
fn sign_in(req: &mut Request, app: &App) -> User {
482482
let conn = app.diesel_database.get().unwrap();
483483
let user = ::new_user("foo").create_or_update(&conn).unwrap();
484484
sign_in_as(req, &user);
485+
user
485486
}
486487

487488
fn new_dependency(conn: &PgConnection, version: &Version, krate: &Crate) -> Dependency {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
===REQUEST 371
2+
PUT http://alexcrichton-test.s3.amazonaws.com/crates/foo_versions_updated_at/foo_versions_updated_at-1.0.0.crate HTTP/1.1
3+
Accept: */*
4+
Proxy-Connection: Keep-Alive
5+
Date: Sun, 28 Jun 2015 14:07:18 -0700
6+
Content-Type: application/x-tar
7+
Content-Length: 0
8+
Host: alexcrichton-test.s3.amazonaws.com
9+
Authorization: AWS AKIAJF3GEK7N44BACDZA:B6cKWtg9t24ej4DljrlsMnkgtdg=
10+
11+
12+
===RESPONSE 258
13+
HTTP/1.1 200
14+
x-amz-request-id: D72B3AB3755D34E3
15+
etag: "d41d8cd98f00b204e9800998ecf8427e"
16+
date: Sun, 28 Jun 2015 21:07:51 GMT
17+
x-amz-id-2: 4MEDTSaUXJ7J70JfbXTcfI2m1qoriY+OEOxVAmj2oARpllZTI+vCT52o9FZnVUzJzkchRL407nI=
18+
content-length: 0
19+
server: AmazonS3
20+
21+
22+
===REQUEST 371
23+
PUT http://alexcrichton-test.s3.amazonaws.com/crates/foo_versions_updated_at/foo_versions_updated_at-2.0.0.crate HTTP/1.1
24+
Accept: */*
25+
Proxy-Connection: Keep-Alive
26+
Date: Sun, 28 Jun 2015 14:07:18 -0700
27+
Content-Type: application/x-tar
28+
Content-Length: 0
29+
Host: alexcrichton-test.s3.amazonaws.com
30+
Authorization: AWS AKIAJF3GEK7N44BACDZA:B6cKWtg9t24ej4DljrlsMnkgtdg=
31+
32+
33+
===RESPONSE 258
34+
HTTP/1.1 200
35+
x-amz-request-id: D72B3AB3755D34E3
36+
etag: "d41d8cd98f00b204e9800998ecf8427e"
37+
date: Sun, 28 Jun 2015 21:07:51 GMT
38+
x-amz-id-2: 4MEDTSaUXJ7J70JfbXTcfI2m1qoriY+OEOxVAmj2oARpllZTI+vCT52o9FZnVUzJzkchRL407nI=
39+
content-length: 0
40+
server: AmazonS3
41+
42+

src/tests/krate.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use cargo_registry::krate::{Crate, EncodableCrate, MAX_NAME_LENGTH};
1919

2020
use cargo_registry::token::ApiToken;
2121
use cargo_registry::owner::EncodableOwner;
22-
use cargo_registry::schema::versions;
22+
use cargo_registry::schema::{versions, crates};
2323

2424
use cargo_registry::upload as u;
2525
use cargo_registry::user::EncodableUser;
@@ -463,6 +463,44 @@ fn versions() {
463463
assert_eq!(json.versions[2].num, "0.5.0");
464464
}
465465

466+
#[test]
467+
fn uploading_new_version_touches_crate() {
468+
use diesel::expression::dsl::*;
469+
470+
let (_b, app, middle) = ::app();
471+
472+
let mut upload_req = ::new_req(app.clone(), "foo_versions_updated_at", "1.0.0");
473+
let u = ::sign_in(&mut upload_req, &app);
474+
ok_resp!(middle.call(&mut upload_req));
475+
476+
{
477+
let conn = app.diesel_database.get().unwrap();
478+
diesel::update(crates::table)
479+
.set(crates::updated_at.eq(crates::updated_at - 1.hour()))
480+
.execute(&*conn)
481+
.unwrap();
482+
}
483+
484+
let mut show_req = ::req(
485+
app.clone(),
486+
Method::Get,
487+
"/api/v1/crates/foo_versions_updated_at",
488+
);
489+
let mut response = ok_resp!(middle.call(&mut show_req));
490+
let json: CrateResponse = ::json(&mut response);
491+
let updated_at_before = json.krate.updated_at;
492+
493+
let mut upload_req = ::new_req(app.clone(), "foo_versions_updated_at", "2.0.0");
494+
::sign_in_as(&mut upload_req, &u);
495+
ok_resp!(middle.call(&mut upload_req));
496+
497+
let mut response = ok_resp!(middle.call(&mut show_req));
498+
let json: CrateResponse = ::json(&mut response);
499+
let updated_at_after = json.krate.updated_at;
500+
501+
assert_ne!(updated_at_before, updated_at_after);
502+
}
503+
466504
#[test]
467505
fn new_wrong_token() {
468506
let (_b, app, middle) = ::app();

0 commit comments

Comments
 (0)