Skip to content

Record who published crate versions #1561

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
09f12ad
Adds in new migration to add the published_by
Andy-Bell Nov 24, 2018
acffea6
Updates the Version and NewVersion models to add
Andy-Bell Nov 24, 2018
dc302ca
Makes the EncodeableVersion struct reference the
Andy-Bell Nov 24, 2018
997f1e1
Ensures the user id is being passed onto the
Andy-Bell Nov 24, 2018
e5ffc60
Fixes rest of thetests broken by the prior changes
Andy-Bell Nov 24, 2018
32bd19b
Merge branch 'master' into record-who-published-crate
Andy-Bell Nov 27, 2018
d039b5d
cargo fmt
carols10cents Nov 27, 2018
d7816ec
Remove NOT NULL from published by column.
Andy-Bell Nov 27, 2018
b92c9f2
Change models to reflect the fact that
Andy-Bell Nov 29, 2018
3654052
Master changed VersionBuilder and CrateBuilder to
Andy-Bell Nov 29, 2018
645724f
Merge remote-tracking branch 'upstream/master' into record-who-publis…
carols10cents Dec 4, 2018
928d457
Merge remote-tracking branch 'upstream/master' into record-who-publis…
carols10cents Dec 20, 2018
f220fe7
Merge remote-tracking branch 'upstream/master' into record-who-publis…
carols10cents Feb 3, 2019
890afcc
Merge remote-tracking branch 'upstream/master' into record-who-publis…
carols10cents Feb 3, 2019
477c73c
Have version encode take an optional User that published the crate
carols10cents Feb 1, 2019
3b4bdef
Make a method to fetch the user the version was published by
carols10cents Feb 2, 2019
b9d15df
Query for published_by with queries for version and crate
carols10cents Feb 2, 2019
62fdb18
Query for published_by for multiple versions where needed
carols10cents Feb 2, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ALTER TABLE versions DROP CONSTRAINT fk_versions_published_by;

ALTER TABLE versions DROP COLUMN published_by;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ALTER TABLE versions
ADD COLUMN published_by integer;

ALTER TABLE versions
ADD CONSTRAINT "fk_versions_published_by"
FOREIGN KEY (published_by)
REFERENCES users(id);
1 change: 1 addition & 0 deletions src/bin/update-downloads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ mod test {
None,
None,
0,
user_id,
)
.unwrap();
let version = version.save(conn, &[]).unwrap();
Expand Down
40 changes: 27 additions & 13 deletions src/controllers/krate/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

use crate::controllers::prelude::*;
use crate::models::{
Category, Crate, CrateCategory, CrateDownload, CrateKeyword, CrateVersions, Keyword, Version,
Category, Crate, CrateCategory, CrateDownload, CrateKeyword, CrateVersions, Keyword, User,
Version,
};
use crate::schema::*;
use crate::views::{
Expand Down Expand Up @@ -106,9 +107,13 @@ pub fn show(req: &mut dyn Request) -> CargoResult<Response> {
let conn = req.db_conn()?;
let krate = Crate::by_name(name).first::<Crate>(&*conn)?;

let mut versions = krate.all_versions().load::<Version>(&*conn)?;
versions.sort_by(|a, b| b.num.cmp(&a.num));
let ids = versions.iter().map(|v| v.id).collect();
let mut versions_and_publishers: Vec<(Version, Option<User>)> = krate
.all_versions()
.left_outer_join(users::table)
.select((versions::all_columns, users::all_columns.nullable()))
.load(&*conn)?;
versions_and_publishers.sort_by(|a, b| b.0.num.cmp(&a.0.num));
let ids = versions_and_publishers.iter().map(|v| v.0.id).collect();

let kws = CrateKeyword::belonging_to(&krate)
.inner_join(keywords::table)
Expand Down Expand Up @@ -146,9 +151,9 @@ pub fn show(req: &mut dyn Request) -> CargoResult<Response> {
false,
recent_downloads,
),
versions: versions
versions: versions_and_publishers
.into_iter()
.map(|v| v.encodable(&krate.name))
.map(|(v, pb)| v.encodable(&krate.name, pb))
.collect(),
keywords: kws.into_iter().map(|k| k.encodable()).collect(),
categories: cats.into_iter().map(|k| k.encodable()).collect(),
Expand Down Expand Up @@ -185,11 +190,15 @@ pub fn versions(req: &mut dyn Request) -> CargoResult<Response> {
let crate_name = &req.params()["crate_id"];
let conn = req.db_conn()?;
let krate = Crate::by_name(crate_name).first::<Crate>(&*conn)?;
let mut versions = krate.all_versions().load::<Version>(&*conn)?;
versions.sort_by(|a, b| b.num.cmp(&a.num));
let versions = versions
let mut versions_and_publishers: Vec<(Version, Option<User>)> = krate
.all_versions()
.left_outer_join(users::table)
.select((versions::all_columns, users::all_columns.nullable()))
.load(&*conn)?;
versions_and_publishers.sort_by(|a, b| b.0.num.cmp(&a.0.num));
let versions = versions_and_publishers
.into_iter()
.map(|v| v.encodable(crate_name))
.map(|(v, pb)| v.encodable(crate_name, pb))
.collect();

#[derive(Serialize)]
Expand Down Expand Up @@ -218,10 +227,15 @@ pub fn reverse_dependencies(req: &mut dyn Request) -> CargoResult<Response> {
let versions = versions::table
.filter(versions::id.eq(any(version_ids)))
.inner_join(crates::table)
.select((versions::all_columns, crates::name))
.load::<(Version, String)>(&*conn)?
.left_outer_join(users::table)
.select((
versions::all_columns,
crates::name,
users::all_columns.nullable(),
))
.load::<(Version, String, Option<User>)>(&*conn)?
.into_iter()
.map(|(version, krate_name)| version.encodable(&krate_name))
.map(|(version, krate_name, published_by)| version.encodable(&krate_name, published_by))
.collect();

#[derive(Serialize)]
Expand Down
1 change: 1 addition & 0 deletions src/controllers/krate/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ pub fn publish(req: &mut dyn Request) -> CargoResult<Response> {
// Downcast is okay because the file length must be less than the max upload size
// to get here, and max upload sizes are way less than i32 max
file_length as i32,
user.id,
)?
.save(&conn, &new_crate.authors)?;

Expand Down
13 changes: 10 additions & 3 deletions src/controllers/user/me.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,16 @@ pub fn updates(req: &mut dyn Request) -> CargoResult<Response> {
let followed_crates = Follow::belonging_to(user).select(follows::crate_id);
let data = versions::table
.inner_join(crates::table)
.left_outer_join(users::table)
.filter(crates::id.eq(any(followed_crates)))
.order(versions::created_at.desc())
.select((versions::all_columns, crates::name))
.select((
versions::all_columns,
crates::name,
users::all_columns.nullable(),
))
.paginate(limit, offset)
.load::<((Version, String), i64)>(&*conn)?;
.load::<((Version, String, Option<User>), i64)>(&*conn)?;

let more = data
.get(0)
Expand All @@ -68,7 +73,9 @@ pub fn updates(req: &mut dyn Request) -> CargoResult<Response> {

let versions = data
.into_iter()
.map(|((version, crate_name), _)| version.encodable(&crate_name))
.map(|((version, crate_name, published_by), _)| {
version.encodable(&crate_name, published_by)
})
.collect();

#[derive(Serialize)]
Expand Down
24 changes: 17 additions & 7 deletions src/controllers/version/deprecated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use crate::controllers::prelude::*;

use crate::models::{Crate, Version};
use crate::models::{Crate, User, Version};
use crate::schema::*;
use crate::views::EncodableVersion;

Expand All @@ -24,11 +24,16 @@ pub fn index(req: &mut dyn Request) -> CargoResult<Response> {

let versions = versions::table
.inner_join(crates::table)
.select((versions::all_columns, crates::name))
.left_outer_join(users::table)
.select((
versions::all_columns,
crates::name,
users::all_columns.nullable(),
))
.filter(versions::id.eq(any(ids)))
.load::<(Version, String)>(&*conn)?
.load::<(Version, String, Option<User>)>(&*conn)?
.into_iter()
.map(|(version, crate_name)| version.encodable(&crate_name))
.map(|(version, crate_name, published_by)| version.encodable(&crate_name, published_by))
.collect();

#[derive(Serialize)]
Expand All @@ -45,17 +50,22 @@ pub fn show_by_id(req: &mut dyn Request) -> CargoResult<Response> {
let id = &req.params()["version_id"];
let id = id.parse().unwrap_or(0);
let conn = req.db_conn()?;
let (version, krate): (Version, Crate) = versions::table
let (version, krate, published_by): (Version, Crate, Option<User>) = versions::table
.find(id)
.inner_join(crates::table)
.select((versions::all_columns, crate::models::krate::ALL_COLUMNS))
.left_outer_join(users::table)
.select((
versions::all_columns,
crate::models::krate::ALL_COLUMNS,
users::all_columns.nullable(),
))
.first(&*conn)?;

#[derive(Serialize)]
struct R {
version: EncodableVersion,
}
Ok(req.json(&R {
version: version.encodable(&krate.name),
version: version.encodable(&krate.name, published_by),
}))
}
4 changes: 3 additions & 1 deletion src/controllers/version/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,14 @@ pub fn authors(req: &mut dyn Request) -> CargoResult<Response> {
/// API route to have.
pub fn show(req: &mut dyn Request) -> CargoResult<Response> {
let (version, krate) = version_and_crate(req)?;
let conn = req.db_conn()?;
let published_by = version.published_by(&conn);

#[derive(Serialize)]
struct R {
version: EncodableVersion,
}
Ok(req.json(&R {
version: version.encodable(&krate.name),
version: version.encodable(&krate.name, published_by),
}))
}
18 changes: 16 additions & 2 deletions src/models/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use diesel::prelude::*;

use crate::util::{human, CargoResult};

use crate::models::{Crate, Dependency};
use crate::models::{Crate, Dependency, User};
use crate::schema::*;
use crate::views::{EncodableVersion, EncodableVersionLinks};

Expand All @@ -23,6 +23,7 @@ pub struct Version {
pub yanked: bool,
pub license: Option<String>,
pub crate_size: Option<i32>,
pub published_by: Option<i32>,
}

#[derive(Insertable, Debug)]
Expand All @@ -33,10 +34,11 @@ pub struct NewVersion {
features: serde_json::Value,
license: Option<String>,
crate_size: Option<i32>,
published_by: i32,
}

impl Version {
pub fn encodable(self, crate_name: &str) -> EncodableVersion {
pub fn encodable(self, crate_name: &str, published_by: Option<User>) -> EncodableVersion {
let Version {
id,
num,
Expand Down Expand Up @@ -68,6 +70,7 @@ impl Version {
authors: format!("/api/v1/crates/{}/{}/authors", crate_name, num),
},
crate_size,
published_by: published_by.map(|pb| pb.encodable_public()),
}
}

Expand Down Expand Up @@ -107,6 +110,15 @@ impl Version {
.set(rendered_at.eq(now))
.execute(conn)
}

/// Gets the User who ran `cargo publish` for this version, if recorded.
/// Not for use when you have a group of versions you need the publishers for.
pub fn published_by(&self, conn: &PgConnection) -> Option<User> {
match self.published_by {
Some(pb) => users::table.find(pb).first(conn).ok(),
None => None,
}
}
}

impl NewVersion {
Expand All @@ -118,6 +130,7 @@ impl NewVersion {
license: Option<String>,
license_file: Option<&str>,
crate_size: i32,
published_by: i32,
) -> CargoResult<Self> {
let features = serde_json::to_value(features)?;

Expand All @@ -127,6 +140,7 @@ impl NewVersion {
features,
license,
crate_size: Some(crate_size),
published_by,
};

new_version.validate_license(license_file)?;
Expand Down
7 changes: 7 additions & 0 deletions src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,12 @@ table! {
///
/// (Automatically generated by Diesel.)
crate_size -> Nullable<Int4>,
/// The `published_by` column of the `versions` table.
///
/// Its SQL type is `Nullable<Int4>`.
///
/// (Automatically generated by Diesel.)
published_by -> Nullable<Int4>,
}
}

Expand All @@ -927,6 +933,7 @@ joinable!(version_authors -> users (user_id));
joinable!(version_authors -> versions (version_id));
joinable!(version_downloads -> versions (version_id));
joinable!(versions -> crates (crate_id));
joinable!(versions -> users (published_by));

allow_tables_to_appear_in_same_query!(
api_tokens,
Expand Down
24 changes: 18 additions & 6 deletions src/tests/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ impl<'a> VersionBuilder<'a> {
self
}

fn build(self, crate_id: i32, connection: &PgConnection) -> CargoResult<Version> {
fn build(
self,
crate_id: i32,
published_by: i32,
connection: &PgConnection,
) -> CargoResult<Version> {
use diesel::{insert_into, update};

let license = match self.license {
Expand All @@ -83,6 +88,7 @@ impl<'a> VersionBuilder<'a> {
license,
self.license_file,
self.size,
published_by,
)?
.save(connection, &[])?;

Expand Down Expand Up @@ -119,10 +125,16 @@ impl<'a> VersionBuilder<'a> {
/// # Panics
///
/// Panics (and fails the test) if any part of inserting the version record fails.
pub fn expect_build(self, crate_id: i32, connection: &PgConnection) -> Version {
self.build(crate_id, connection).unwrap_or_else(|e| {
panic!("Unable to create version: {:?}", e);
})
pub fn expect_build(
self,
crate_id: i32,
published_by: i32,
connection: &PgConnection,
) -> Version {
self.build(crate_id, published_by, connection)
.unwrap_or_else(|e| {
panic!("Unable to create version: {:?}", e);
})
}
}

Expand Down Expand Up @@ -266,7 +278,7 @@ impl<'a> CrateBuilder<'a> {
}

for version_builder in self.versions {
version_builder.build(krate.id, connection)?;
version_builder.build(krate.id, self.owner_id, connection)?;
}

if !self.keywords.is_empty() {
Expand Down
Loading