diff --git a/migrations/20231124070100_renaming_consistency.sql b/migrations/20231124070100_renaming_consistency.sql new file mode 100644 index 00000000..d1c85390 --- /dev/null +++ b/migrations/20231124070100_renaming_consistency.sql @@ -0,0 +1,12 @@ +-- rename 'title' to 'name' in all tables (collections, organizations, mods, mods_gallery, notifications, notifications_actions) +ALTER TABLE collections RENAME COLUMN title TO name; +ALTER TABLE organizations RENAME COLUMN title TO name; +ALTER TABLE mods RENAME COLUMN title TO name; +ALTER TABLE mods_gallery RENAME COLUMN title TO name; +ALTER TABLE notifications RENAME COLUMN title TO name; +ALTER TABLE notifications_actions RENAME COLUMN title TO name; + +-- rename project 'description' to 'summary' +-- rename project 'body' to 'description' +ALTER TABLE mods RENAME COLUMN description TO summary; +ALTER TABLE mods RENAME COLUMN body TO description; \ No newline at end of file diff --git a/src/database/models/collection_item.rs b/src/database/models/collection_item.rs index 994002c6..035fad6b 100644 --- a/src/database/models/collection_item.rs +++ b/src/database/models/collection_item.rs @@ -62,7 +62,7 @@ impl Collection { sqlx::query!( " INSERT INTO collections ( - id, user_id, title, description, + id, user_id, name, description, created, icon_url, status ) VALUES ( @@ -190,7 +190,7 @@ impl Collection { remaining_collections.iter().map(|x| x.0).collect(); let db_collections: Vec = sqlx::query!( " - SELECT c.id id, c.title title, c.description description, + SELECT c.id id, c.name name, c.description description, c.icon_url icon_url, c.color color, c.created created, c.user_id user_id, c.updated updated, c.status status, ARRAY_AGG(DISTINCT cm.mod_id) filter (where cm.mod_id is not null) mods @@ -209,7 +209,7 @@ impl Collection { Collection { id: CollectionId(id), user_id: UserId(m.user_id), - name: m.title.clone(), + name: m.name.clone(), description: m.description.clone(), icon_url: m.icon_url.clone(), color: m.color.map(|x| x as u32), diff --git a/src/database/models/notification_item.rs b/src/database/models/notification_item.rs index 0af1591f..206c5373 100644 --- a/src/database/models/notification_item.rs +++ b/src/database/models/notification_item.rs @@ -122,8 +122,8 @@ impl Notification { let notification_ids_parsed: Vec = notification_ids.iter().map(|x| x.0).collect(); sqlx::query!( " - SELECT n.id, n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type, n.body, - JSONB_AGG(DISTINCT jsonb_build_object('id', na.id, 'notification_id', na.notification_id, 'title', na.title, 'action_route_method', na.action_route_method, 'action_route', na.action_route)) filter (where na.id is not null) actions + SELECT n.id, n.user_id, n.name, n.text, n.link, n.created, n.read, n.type notification_type, n.body, + JSONB_AGG(DISTINCT jsonb_build_object('id', na.id, 'notification_id', na.notification_id, 'name', na.name, 'action_route_method', na.action_route_method, 'action_route', na.action_route)) filter (where na.id is not null) actions FROM notifications n LEFT OUTER JOIN notifications_actions na on n.id = na.notification_id WHERE n.id = ANY($1) @@ -143,10 +143,10 @@ impl Notification { read: row.read, created: row.created, body: row.body.clone().and_then(|x| serde_json::from_value(x).ok()).unwrap_or_else(|| { - if let Some(title) = row.title { + if let Some(name) = row.name { NotificationBody::LegacyMarkdown { notification_type: row.notification_type, - title, + name, text: row.text.unwrap_or_default(), link: row.link.unwrap_or_default(), actions: serde_json::from_value( @@ -186,8 +186,8 @@ impl Notification { let db_notifications = sqlx::query!( " - SELECT n.id, n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type, n.body, - JSONB_AGG(DISTINCT jsonb_build_object('id', na.id, 'notification_id', na.notification_id, 'title', na.title, 'action_route_method', na.action_route_method, 'action_route', na.action_route)) filter (where na.id is not null) actions + SELECT n.id, n.user_id, n.name, n.text, n.link, n.created, n.read, n.type notification_type, n.body, + JSONB_AGG(DISTINCT jsonb_build_object('id', na.id, 'notification_id', na.notification_id, 'name', na.name, 'action_route_method', na.action_route_method, 'action_route', na.action_route)) filter (where na.id is not null) actions FROM notifications n LEFT OUTER JOIN notifications_actions na on n.id = na.notification_id WHERE n.user_id = $1 @@ -206,10 +206,10 @@ impl Notification { read: row.read, created: row.created, body: row.body.clone().and_then(|x| serde_json::from_value(x).ok()).unwrap_or_else(|| { - if let Some(title) = row.title { + if let Some(name) = row.name { NotificationBody::LegacyMarkdown { notification_type: row.notification_type, - title, + name, text: row.text.unwrap_or_default(), link: row.link.unwrap_or_default(), actions: serde_json::from_value( diff --git a/src/database/models/organization_item.rs b/src/database/models/organization_item.rs index efe94c48..a2d47eac 100644 --- a/src/database/models/organization_item.rs +++ b/src/database/models/organization_item.rs @@ -36,7 +36,7 @@ impl Organization { ) -> Result<(), super::DatabaseError> { sqlx::query!( " - INSERT INTO organizations (id, title, team_id, description, icon_url, color) + INSERT INTO organizations (id, name, team_id, description, icon_url, color) VALUES ($1, $2, $3, $4, $5, $6) ", self.id.0, @@ -166,9 +166,9 @@ impl Organization { let organizations: Vec = sqlx::query!( " - SELECT o.id, o.title, o.team_id, o.description, o.icon_url, o.color + SELECT o.id, o.name, o.team_id, o.description, o.icon_url, o.color FROM organizations o - WHERE o.id = ANY($1) OR LOWER(o.title) = ANY($2) + WHERE o.id = ANY($1) OR LOWER(o.name) = ANY($2) GROUP BY o.id; ", &organization_ids_parsed, @@ -181,7 +181,7 @@ impl Organization { .try_filter_map(|e| async { Ok(e.right().map(|m| Organization { id: OrganizationId(m.id), - name: m.title, + name: m.name, team_id: TeamId(m.team_id), description: m.description, icon_url: m.icon_url, @@ -226,7 +226,7 @@ impl Organization { { let result = sqlx::query!( " - SELECT o.id, o.title, o.team_id, o.description, o.icon_url, o.color + SELECT o.id, o.name, o.team_id, o.description, o.icon_url, o.color FROM organizations o LEFT JOIN mods m ON m.organization_id = o.id WHERE m.id = $1 @@ -240,7 +240,7 @@ impl Organization { if let Some(result) = result { Ok(Some(Organization { id: OrganizationId(result.id), - name: result.title, + name: result.name, team_id: TeamId(result.team_id), description: result.description, icon_url: result.icon_url, diff --git a/src/database/models/project_item.rs b/src/database/models/project_item.rs index 5c0be309..4cdb6571 100644 --- a/src/database/models/project_item.rs +++ b/src/database/models/project_item.rs @@ -88,7 +88,7 @@ impl GalleryItem { sqlx::query!( " INSERT INTO mods_gallery ( - mod_id, image_url, featured, title, description, ordering + mod_id, image_url, featured, name, description, ordering ) SELECT * FROM UNNEST ($1::bigint[], $2::varchar[], $3::bool[], $4::varchar[], $5::varchar[], $6::bigint[]) ", @@ -144,8 +144,8 @@ pub struct ProjectBuilder { pub team_id: TeamId, pub organization_id: Option, pub name: String, + pub summary: String, pub description: String, - pub body: String, pub icon_url: Option, pub issues_url: Option, pub source_url: Option, @@ -175,9 +175,8 @@ impl ProjectBuilder { team_id: self.team_id, organization_id: self.organization_id, name: self.name, + summary: self.summary, description: self.description, - body: self.body, - body_url: None, published: Utc::now(), updated: Utc::now(), approved: None, @@ -246,9 +245,8 @@ pub struct Project { pub team_id: TeamId, pub organization_id: Option, pub name: String, + pub summary: String, pub description: String, - pub body: String, - pub body_url: Option, pub published: DateTime, pub updated: DateTime, pub approved: Option>, @@ -281,7 +279,7 @@ impl Project { sqlx::query!( " INSERT INTO mods ( - id, team_id, title, description, body, + id, team_id, name, summary, description, published, downloads, icon_url, issues_url, source_url, wiki_url, status, requested_status, discord_url, license_url, license, @@ -298,8 +296,8 @@ impl Project { self.id as ProjectId, self.team_id as TeamId, &self.name, + &self.summary, &self.description, - &self.body, self.published, self.downloads, self.icon_url.as_ref(), @@ -567,8 +565,8 @@ impl Project { let db_projects: Vec = sqlx::query!( " - SELECT m.id id, m.title title, m.description description, m.downloads downloads, m.follows follows, - m.icon_url icon_url, m.body body, m.published published, + SELECT m.id id, m.name name, m.summary summary, m.downloads downloads, m.follows follows, + m.icon_url icon_url, m.description description, m.published published, m.updated updated, m.approved approved, m.queued, m.status status, m.requested_status requested_status, m.issues_url issues_url, m.source_url source_url, m.wiki_url wiki_url, m.discord_url discord_url, m.license_url license_url, m.team_id team_id, m.organization_id organization_id, m.license license, m.slug slug, m.moderation_message moderation_message, m.moderation_message_body moderation_message_body, @@ -580,7 +578,7 @@ impl Project { ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is false) categories, ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories, JSONB_AGG(DISTINCT jsonb_build_object('id', v.id, 'date_published', v.date_published)) filter (where v.id is not null) versions, - JSONB_AGG(DISTINCT jsonb_build_object('image_url', mg.image_url, 'featured', mg.featured, 'title', mg.title, 'description', mg.description, 'created', mg.created, 'ordering', mg.ordering)) filter (where mg.image_url is not null) gallery, + JSONB_AGG(DISTINCT jsonb_build_object('image_url', mg.image_url, 'featured', mg.featured, 'name', mg.name, 'description', mg.description, 'created', mg.created, 'ordering', mg.ordering)) filter (where mg.image_url is not null) gallery, JSONB_AGG(DISTINCT jsonb_build_object('platform_id', md.joining_platform_id, 'platform_short', dp.short, 'platform_name', dp.name,'url', md.url)) filter (where md.joining_platform_id is not null) donations FROM mods m INNER JOIN threads t ON t.mod_id = m.id @@ -612,10 +610,9 @@ impl Project { id: ProjectId(id), team_id: TeamId(m.team_id), organization_id: m.organization_id.map(OrganizationId), - name: m.title.clone(), - description: m.description.clone(), + name: m.name.clone(), + summary: m.summary.clone(), downloads: m.downloads, - body_url: None, icon_url: m.icon_url.clone(), published: m.published, updated: m.updated, @@ -632,7 +629,7 @@ impl Project { )), license: m.license.clone(), slug: m.slug.clone(), - body: m.body.clone(), + description: m.description.clone(), follows: m.follows, moderation_message: m.moderation_message, moderation_message_body: m.moderation_message_body, diff --git a/src/database/models/version_item.rs b/src/database/models/version_item.rs index 3e3fc3ec..c4620dfb 100644 --- a/src/database/models/version_item.rs +++ b/src/database/models/version_item.rs @@ -209,7 +209,6 @@ impl VersionBuilder { name: self.name, version_number: self.version_number, changelog: self.changelog, - changelog_url: None, date_published: Utc::now(), downloads: 0, featured: self.featured, @@ -293,7 +292,6 @@ pub struct Version { pub name: String, pub version_number: String, pub changelog: String, - pub changelog_url: Option, pub date_published: DateTime, pub downloads: i32, pub version_type: String, @@ -601,7 +599,6 @@ impl Version { name: v.version_name, version_number: v.version_number, changelog: v.changelog, - changelog_url: None, date_published: v.date_published, downloads: v.downloads, version_type: v.version_type, @@ -972,7 +969,6 @@ mod tests { name: Default::default(), version_number: Default::default(), changelog: Default::default(), - changelog_url: Default::default(), downloads: Default::default(), version_type: Default::default(), featured: Default::default(), diff --git a/src/models/v2/projects.rs b/src/models/v2/projects.rs index b1be2ffc..2986e949 100644 --- a/src/models/v2/projects.rs +++ b/src/models/v2/projects.rs @@ -127,12 +127,12 @@ impl LegacyProject { id: data.id, slug: data.slug, project_type, - team: data.team, + team: data.team_id, organization: data.organization, title: data.name, - description: data.description, - body: data.body, - body_url: data.body_url, + description: data.summary, // V2 description is V3 summary + body: data.description, // V2 body is V3 description + body_url: None, // Always None even in V2 published: data.published, updated: data.updated, approved: data.approved, @@ -293,7 +293,7 @@ impl From for LegacyVersion { name: data.name, version_number: data.version_number, changelog: data.changelog, - changelog_url: data.changelog_url, + changelog_url: None, // Always None even in V2 date_published: data.date_published, downloads: data.downloads, version_type: data.version_type, diff --git a/src/models/v3/notifications.rs b/src/models/v3/notifications.rs index b8372c32..53d70f6a 100644 --- a/src/models/v3/notifications.rs +++ b/src/models/v3/notifications.rs @@ -63,7 +63,7 @@ pub enum NotificationBody { }, LegacyMarkdown { notification_type: Option, - title: String, + name: String, // TODO: Check to make sure this change doesnt break anything text: String, link: String, actions: Vec, @@ -176,13 +176,13 @@ impl From for Notification { ), NotificationBody::LegacyMarkdown { notification_type, - title, + name, text, link, actions, } => ( notification_type.clone(), - title.clone(), + name.clone(), text.clone(), link.clone(), actions.clone().into_iter().map(Into::into).collect(), diff --git a/src/models/v3/projects.rs b/src/models/v3/projects.rs index f48d0201..e0bd36a7 100644 --- a/src/models/v3/projects.rs +++ b/src/models/v3/projects.rs @@ -34,17 +34,15 @@ pub struct Project { /// The aggregated games of the versions of this project pub games: Vec, /// The team of people that has ownership of this project. - pub team: TeamId, + pub team_id: TeamId, /// The optional organization of people that have ownership of this project. pub organization: Option, /// The title or name of the project. pub name: String, /// A short description of the project. - pub description: String, + pub summary: String, /// A long form description of the project. - pub body: String, - /// The link to the long description of the project. Deprecated, always None - pub body_url: Option, + pub description: String, /// The date at which the project was first published. pub published: DateTime, @@ -119,12 +117,11 @@ impl From for Project { slug: m.slug, project_types: data.project_types, games: data.games, - team: m.team_id.into(), + team_id: m.team_id.into(), organization: m.organization_id.map(|i| i.into()), name: m.name, + summary: m.summary, description: m.description, - body: m.body, - body_url: None, published: m.published, updated: m.updated, approved: m.approved, @@ -467,8 +464,6 @@ pub struct Version { pub games: Vec, /// The changelog for this version of the project. pub changelog: String, - /// A link to the changelog for this version of the project. Deprecated, always None - pub changelog_url: Option, /// The date that this version was published. pub date_published: DateTime, @@ -520,7 +515,6 @@ impl From for Version { project_types: data.project_types, games: data.games, changelog: v.changelog, - changelog_url: None, date_published: v.date_published, downloads: v.downloads as u32, version_type: match v.version_type.as_str() { diff --git a/src/routes/v2/project_creation.rs b/src/routes/v2/project_creation.rs index f96601bb..ca644f9d 100644 --- a/src/routes/v2/project_creation.rs +++ b/src/routes/v2/project_creation.rs @@ -197,8 +197,8 @@ pub async fn project_create( Ok(v3::project_creation::ProjectCreateData { name: legacy_create.title, slug: legacy_create.slug, - description: legacy_create.description, - body: legacy_create.body, + summary: legacy_create.description, // Description becomes summary + description: legacy_create.body, // Body becomes description initial_versions, categories: legacy_create.categories, additional_categories: legacy_create.additional_categories, diff --git a/src/routes/v2/projects.rs b/src/routes/v2/projects.rs index a4792179..1f2a9fbf 100644 --- a/src/routes/v2/projects.rs +++ b/src/routes/v2/projects.rs @@ -336,8 +336,8 @@ pub async fn project_edit( let new_project = v3::projects::EditProject { name: v2_new_project.title, - description: v2_new_project.description, - body: v2_new_project.body, + summary: v2_new_project.description, // Description becomes summary + description: v2_new_project.body, // Body becomes description categories: v2_new_project.categories, additional_categories: v2_new_project.additional_categories, issues_url: v2_new_project.issues_url, diff --git a/src/routes/v3/collections.rs b/src/routes/v3/collections.rs index 9934608d..0fb4020a 100644 --- a/src/routes/v3/collections.rs +++ b/src/routes/v3/collections.rs @@ -243,7 +243,7 @@ pub async fn collection_edit( sqlx::query!( " UPDATE collections - SET title = $1 + SET name = $1 WHERE (id = $2) ", name.trim(), diff --git a/src/routes/v3/organizations.rs b/src/routes/v3/organizations.rs index e31c1a2f..f3b60a6b 100644 --- a/src/routes/v3/organizations.rs +++ b/src/routes/v3/organizations.rs @@ -71,7 +71,7 @@ pub async fn organization_projects_get( " SELECT m.id FROM organizations o INNER JOIN mods m ON m.organization_id = o.id - WHERE (o.id = $1 AND $1 IS NOT NULL) OR (o.title = $2 AND $2 IS NOT NULL) + WHERE (o.id = $1 AND $1 IS NOT NULL) OR (o.name = $2 AND $2 IS NOT NULL) ", possible_organization_id.map(|x| x as i64), info @@ -124,11 +124,11 @@ pub async fn organization_create( let mut transaction = pool.begin().await?; // Try title - let title_organization_id_option: Option = + let name_organization_id_option: Option = serde_json::from_str(&format!("\"{}\"", new_organization.name)).ok(); let mut organization_strings = vec![]; - if let Some(title_organization_id) = title_organization_id_option { - organization_strings.push(title_organization_id.to_string()); + if let Some(name_organization_id) = name_organization_id_option { + organization_strings.push(name_organization_id.to_string()); } organization_strings.push(new_organization.name.clone()); let results = Organization::get_many(&organization_strings, &mut *transaction, &redis).await?; @@ -397,47 +397,47 @@ pub async fn organizations_edit( .await?; } - if let Some(title) = &new_organization.name { + if let Some(name) = &new_organization.name { if !perms.contains(OrganizationPermissions::EDIT_DETAILS) { return Err(ApiError::CustomAuthentication( - "You do not have the permissions to edit the title of this organization!" + "You do not have the permissions to edit the name of this organization!" .to_string(), )); } - let title_organization_id_option: Option = parse_base62(title).ok(); - if let Some(title_organization_id) = title_organization_id_option { + let name_organization_id_option: Option = parse_base62(name).ok(); + if let Some(name_organization_id) = name_organization_id_option { let results = sqlx::query!( " SELECT EXISTS(SELECT 1 FROM organizations WHERE id=$1) ", - title_organization_id as i64 + name_organization_id as i64 ) .fetch_one(&mut *transaction) .await?; if results.exists.unwrap_or(true) { return Err(ApiError::InvalidInput( - "Title collides with other organization's id!".to_string(), + "name collides with other organization's id!".to_string(), )); } } - // Make sure the new title is different from the old one - // We are able to unwrap here because the title is always set - if !title.eq(&organization_item.name.clone()) { + // Make sure the new name is different from the old one + // We are able to unwrap here because the name is always set + if !name.eq(&organization_item.name.clone()) { let results = sqlx::query!( " - SELECT EXISTS(SELECT 1 FROM organizations WHERE title = LOWER($1)) + SELECT EXISTS(SELECT 1 FROM organizations WHERE name = LOWER($1)) ", - title + name ) .fetch_one(&mut *transaction) .await?; if results.exists.unwrap_or(true) { return Err(ApiError::InvalidInput( - "Title collides with other organization's id!".to_string(), + "Name collides with other organization's id!".to_string(), )); } } @@ -445,10 +445,10 @@ pub async fn organizations_edit( sqlx::query!( " UPDATE organizations - SET title = LOWER($1) + SET name = LOWER($1) WHERE (id = $2) ", - Some(title), + Some(name), id as database::models::ids::OrganizationId, ) .execute(&mut *transaction) @@ -539,7 +539,7 @@ pub async fn organization_delete( #[derive(Deserialize)] pub struct OrganizationProjectAdd { - pub project_id: String, // Also allow title/slug + pub project_id: String, // Also allow name/slug } pub async fn organization_projects_add( req: HttpRequest, diff --git a/src/routes/v3/project_creation.rs b/src/routes/v3/project_creation.rs index 35c403ca..65e83b78 100644 --- a/src/routes/v3/project_creation.rs +++ b/src/routes/v3/project_creation.rs @@ -168,11 +168,11 @@ pub struct ProjectCreateData { #[validate(length(min = 3, max = 255))] #[serde(alias = "mod_description")] /// A short description of the project. - pub description: String, + pub summary: String, #[validate(length(max = 65536))] #[serde(alias = "mod_body")] /// A long description of the project, in markdown. - pub body: String, + pub description: String, #[validate(length(max = 32))] #[validate] @@ -698,8 +698,8 @@ async fn project_create_inner( team_id, organization_id: project_create_data.organization_id.map(|x| x.into()), name: project_create_data.name, + summary: project_create_data.summary, description: project_create_data.description, - body: project_create_data.body, icon_url: icon_data.clone().map(|x| x.0), issues_url: project_create_data.issues_url, source_url: project_create_data.source_url, @@ -805,12 +805,11 @@ async fn project_create_inner( slug: project_builder.slug.clone(), project_types, games, - team: team_id.into(), + team_id: team_id.into(), organization: project_create_data.organization_id, name: project_builder.name.clone(), + summary: project_builder.summary.clone(), description: project_builder.description.clone(), - body: project_builder.body.clone(), - body_url: None, published: now, updated: now, approved: None, diff --git a/src/routes/v3/projects.rs b/src/routes/v3/projects.rs index 1f5091ed..712407e4 100644 --- a/src/routes/v3/projects.rs +++ b/src/routes/v3/projects.rs @@ -176,9 +176,9 @@ pub struct EditProject { )] pub name: Option, #[validate(length(min = 3, max = 256))] - pub description: Option, + pub summary: Option, #[validate(length(max = 65536))] - pub body: Option, + pub description: Option, #[validate(length(max = 3))] pub categories: Option>, #[validate(length(max = 256))] @@ -313,7 +313,7 @@ pub async fn project_edit( if let Some(name) = &new_project.name { if !perms.contains(ProjectPermissions::EDIT_DETAILS) { return Err(ApiError::CustomAuthentication( - "You do not have the permissions to edit the title of this project!" + "You do not have the permissions to edit the name of this project!" .to_string(), )); } @@ -321,7 +321,7 @@ pub async fn project_edit( sqlx::query!( " UPDATE mods - SET title = $1 + SET name = $1 WHERE (id = $2) ", name.trim(), @@ -331,10 +331,10 @@ pub async fn project_edit( .await?; } - if let Some(description) = &new_project.description { + if let Some(summary) = &new_project.summary { if !perms.contains(ProjectPermissions::EDIT_DETAILS) { return Err(ApiError::CustomAuthentication( - "You do not have the permissions to edit the description of this project!" + "You do not have the permissions to edit the summary of this project!" .to_string(), )); } @@ -342,10 +342,10 @@ pub async fn project_edit( sqlx::query!( " UPDATE mods - SET description = $1 + SET summary = $1 WHERE (id = $2) ", - description, + summary, id as db_ids::ProjectId, ) .execute(&mut *transaction) @@ -877,10 +877,10 @@ pub async fn project_edit( .await?; } - if let Some(body) = &new_project.body { + if let Some(description) = &new_project.description { if !perms.contains(ProjectPermissions::EDIT_BODY) { return Err(ApiError::CustomAuthentication( - "You do not have the permissions to edit the body of this project!" + "You do not have the permissions to edit the description (body) of this project!" .to_string(), )); } @@ -888,10 +888,10 @@ pub async fn project_edit( sqlx::query!( " UPDATE mods - SET body = $1 + SET description = $1 WHERE (id = $2) ", - body, + description, id as db_ids::ProjectId, ) .execute(&mut *transaction) @@ -932,7 +932,7 @@ pub async fn project_edit( // check new description and body for links to associated images // if they no longer exist in the description or body, delete them - let checkable_strings: Vec<&str> = vec![&new_project.description, &new_project.body] + let checkable_strings: Vec<&str> = vec![&new_project.description, &new_project.summary] .into_iter() .filter_map(|x| x.as_ref().map(|y| y.as_str())) .collect(); @@ -2105,7 +2105,7 @@ pub async fn edit_gallery_item( sqlx::query!( " UPDATE mods_gallery - SET title = $2 + SET name = $2 WHERE id = $1 ", id, diff --git a/src/routes/v3/version_creation.rs b/src/routes/v3/version_creation.rs index 9caad08f..62661bf9 100644 --- a/src/routes/v3/version_creation.rs +++ b/src/routes/v3/version_creation.rs @@ -421,7 +421,6 @@ async fn version_create_inner( project_types: all_project_types, games: all_games, changelog: builder.changelog.clone(), - changelog_url: None, date_published: Utc::now(), downloads: 0, version_type: version_data.release_channel, diff --git a/src/search/indexing/local_import.rs b/src/search/indexing/local_import.rs index a0ab6015..e6b287cb 100644 --- a/src/search/indexing/local_import.rs +++ b/src/search/indexing/local_import.rs @@ -19,7 +19,7 @@ pub async fn index_local( let uploads = sqlx::query!( " - SELECT m.id id, v.id version_id, m.title title, m.description description, m.downloads downloads, m.follows follows, + SELECT m.id id, v.id version_id, m.name name, m.description description, m.downloads downloads, m.follows follows, m.icon_url icon_url, m.published published, m.approved approved, m.updated updated, m.team_id team_id, m.license license, m.slug slug, m.status status_name, m.color color, u.username username, @@ -133,7 +133,7 @@ pub async fn index_local( UploadSearchProject { version_id: version_id.to_string(), project_id: project_id.to_string(), - name: m.title, + name: m.name, description: m.description, categories, follows: m.follows, diff --git a/src/util/webhook.rs b/src/util/webhook.rs index 11039949..8bc0ddca 100644 --- a/src/util/webhook.rs +++ b/src/util/webhook.rs @@ -85,7 +85,7 @@ pub async fn send_discord_webhook( let row = sqlx::query!( " - SELECT m.id id, m.title title, m.description description, m.color color, + SELECT m.id id, m.name name, m.description description, m.color color, m.icon_url icon_url, m.slug slug, u.username username, u.avatar_url avatar_url, ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null) categories, @@ -278,7 +278,7 @@ pub async fn send_discord_webhook( project_type, project.slug.unwrap_or_else(|| project_id.to_string()) ), - title: project.title, + title: project.name, // Do not change DiscordEmbed description: project.description, timestamp: Utc::now(), color: project.color.unwrap_or(0x1bd96a) as u32, diff --git a/tests/common/api_common/models.rs b/tests/common/api_common/models.rs index 7b6aec99..b9f2a031 100644 --- a/tests/common/api_common/models.rs +++ b/tests/common/api_common/models.rs @@ -31,11 +31,7 @@ pub struct CommonProject { // For any tests that require those fields, we make a separate test with separate API functions tht do not use Common models. pub id: ProjectId, pub slug: Option, - pub team: TeamId, pub organization: Option, - pub description: String, - pub body: String, - pub body_url: Option, pub published: DateTime, pub updated: DateTime, pub approved: Option>, @@ -71,7 +67,6 @@ pub struct CommonVersion { pub name: String, pub version_number: String, pub changelog: String, - pub changelog_url: Option, pub date_published: DateTime, pub downloads: u32, pub version_type: VersionType, diff --git a/tests/common/api_v3/project.rs b/tests/common/api_v3/project.rs index 08070911..e01520ff 100644 --- a/tests/common/api_v3/project.rs +++ b/tests/common/api_v3/project.rs @@ -24,6 +24,14 @@ use crate::common::{ use super::{request_data::{get_public_project_creation_data, self}, ApiV3}; +impl ApiV3 { + pub async fn get_project_deserialized(&self, id_or_slug: &str, pat: &str) -> Project { + let resp = self.get_project(id_or_slug, pat).await; + assert_eq!(resp.status(), 200); + test::read_body_json(resp).await + } +} + #[async_trait(?Send)] impl ApiProject for ApiV3 { async fn add_public_project( diff --git a/tests/common/api_v3/request_data.rs b/tests/common/api_v3/request_data.rs index 0c7e9c3c..ceec62bc 100644 --- a/tests/common/api_v3/request_data.rs +++ b/tests/common/api_v3/request_data.rs @@ -92,8 +92,8 @@ pub fn get_public_project_creation_data_json( { "name": format!("Test Project {slug}"), "slug": slug, - "description": "A dummy project for testing with.", - "body": "This project is approved, and versions are listed.", + "summary": "A dummy project for testing with.", + "description": "This project is approved, and versions are listed.", "initial_versions": initial_versions, "is_draft": is_draft, "categories": [], diff --git a/tests/common/dummy_data.rs b/tests/common/dummy_data.rs index 5a9d8cd1..5e6f5d30 100644 --- a/tests/common/dummy_data.rs +++ b/tests/common/dummy_data.rs @@ -4,7 +4,7 @@ use std::io::{Cursor, Write}; use actix_http::StatusCode; use actix_web::test::{self, TestRequest}; use labrinth::models::{ - oauth_clients::OAuthClient, organizations::Organization, pats::Scopes, projects::ProjectId, + oauth_clients::OAuthClient, organizations::Organization, pats::Scopes, projects::{ProjectId, Project, Version}, }; use serde_json::json; use sqlx::Executor; @@ -14,10 +14,7 @@ use crate::common::{api_common::Api, database::USER_USER_PAT}; use labrinth::util::actix::{AppendsMultipart, MultipartSegment, MultipartSegmentData}; use super::{ - api_common::{ - models::{CommonProject, CommonVersion}, - ApiProject, - }, + api_common::ApiProject, api_v3::ApiV3, database::TemporaryDatabase, }; @@ -174,16 +171,16 @@ pub struct DummyData { impl DummyData { pub fn new( - project_alpha: CommonProject, - project_alpha_version: CommonVersion, - project_beta: CommonProject, - project_beta_version: CommonVersion, + project_alpha: Project, + project_alpha_version: Version, + project_beta: Project, + project_beta_version: Version, organization_zeta: Organization, oauth_client_alpha: OAuthClient, ) -> Self { DummyData { project_alpha: DummyProjectAlpha { - team_id: project_alpha.team.to_string(), + team_id: project_alpha.team_id.to_string(), project_id: project_alpha.id.to_string(), project_slug: project_alpha.slug.unwrap(), project_id_parsed: project_alpha.id, @@ -193,7 +190,7 @@ impl DummyData { }, project_beta: DummyProjectBeta { - team_id: project_beta.team.to_string(), + team_id: project_beta.team_id.to_string(), project_id: project_beta.id.to_string(), project_slug: project_beta.slug.unwrap(), project_id_parsed: project_beta.id, @@ -311,7 +308,7 @@ pub async fn get_dummy_data(api: &ApiV3) -> DummyData { ) } -pub async fn add_project_alpha(api: &ApiV3) -> (CommonProject, CommonVersion) { +pub async fn add_project_alpha(api: &ApiV3) -> (Project, Version) { let (project, versions) = api .add_public_project( "alpha", @@ -320,10 +317,16 @@ pub async fn add_project_alpha(api: &ApiV3) -> (CommonProject, CommonVersion) { USER_USER_PAT, ) .await; - (project, versions.into_iter().next().unwrap()) + let alpha_project = api + .get_project_deserialized(project.id.to_string().as_str(), USER_USER_PAT) + .await; + let alpha_version = api + .get_version_deserialized(&versions.into_iter().next().unwrap().id.to_string(), USER_USER_PAT) + .await; + (alpha_project, alpha_version) } -pub async fn add_project_beta(api: &ApiV3) -> (CommonProject, CommonVersion) { +pub async fn add_project_beta(api: &ApiV3) -> (Project, Version) { // Adds dummy data to the database with sqlx (projects, versions, threads) // Generate test project data. let jar = TestFile::DummyProjectBeta; @@ -332,8 +335,8 @@ pub async fn add_project_beta(api: &ApiV3) -> (CommonProject, CommonVersion) { { "name": "Test Project Beta", "slug": "beta", - "description": "A dummy project for testing with.", - "body": "This project is not-yet-approved, and versions are draft.", + "summary": "A dummy project for testing with.", + "description": "This project is not-yet-approved, and versions are draft.", "initial_versions": [{ "file_parts": [jar.filename()], "version_number": "1.2.3", @@ -377,6 +380,7 @@ pub async fn add_project_beta(api: &ApiV3) -> (CommonProject, CommonVersion) { .set_multipart(vec![json_segment.clone(), file_segment.clone()]) .to_request(); let resp = api.call(req).await; + println!("{:?}", resp.response().body()); assert_eq!(resp.status(), 200); get_project_beta(api).await @@ -399,14 +403,14 @@ pub async fn add_organization_zeta(api: &ApiV3) -> Organization { get_organization_zeta(api).await } -pub async fn get_project_alpha(api: &ApiV3) -> (CommonProject, CommonVersion) { +pub async fn get_project_alpha(api: &ApiV3) -> (Project, Version) { // Get project let req = TestRequest::get() .uri("/v3/project/alpha") .append_header(("Authorization", USER_USER_PAT)) .to_request(); let resp = api.call(req).await; - let project: CommonProject = test::read_body_json(resp).await; + let project: Project = test::read_body_json(resp).await; // Get project's versions let req = TestRequest::get() @@ -414,13 +418,13 @@ pub async fn get_project_alpha(api: &ApiV3) -> (CommonProject, CommonVersion) { .append_header(("Authorization", USER_USER_PAT)) .to_request(); let resp = api.call(req).await; - let versions: Vec = test::read_body_json(resp).await; + let versions: Vec = test::read_body_json(resp).await; let version = versions.into_iter().next().unwrap(); (project, version) } -pub async fn get_project_beta(api: &ApiV3) -> (CommonProject, CommonVersion) { +pub async fn get_project_beta(api: &ApiV3) -> (Project, Version) { // Get project let req = TestRequest::get() .uri("/v3/project/beta") @@ -429,7 +433,7 @@ pub async fn get_project_beta(api: &ApiV3) -> (CommonProject, CommonVersion) { let resp = api.call(req).await; assert_status(&resp, StatusCode::OK); let project: serde_json::Value = test::read_body_json(resp).await; - let project: CommonProject = serde_json::from_value(project).unwrap(); + let project: Project = serde_json::from_value(project).unwrap(); // Get project's versions let req = TestRequest::get() @@ -438,7 +442,7 @@ pub async fn get_project_beta(api: &ApiV3) -> (CommonProject, CommonVersion) { .to_request(); let resp = api.call(req).await; assert_status(&resp, StatusCode::OK); - let versions: Vec = test::read_body_json(resp).await; + let versions: Vec = test::read_body_json(resp).await; let version = versions.into_iter().next().unwrap(); (project, version) diff --git a/tests/common/permissions.rs b/tests/common/permissions.rs index 7e27def7..01a82f58 100644 --- a/tests/common/permissions.rs +++ b/tests/common/permissions.rs @@ -1018,7 +1018,9 @@ async fn create_dummy_project(setup_api: &ApiV3) -> (String, String) { .add_public_project(&slug, None, None, ADMIN_USER_PAT) .await; let project_id = project.id.to_string(); - let team_id = project.team.to_string(); + + let project = setup_api.get_project_deserialized(&project_id, ADMIN_USER_PAT).await; + let team_id = project.team_id.to_string(); (project_id, team_id) } diff --git a/tests/project.rs b/tests/project.rs index 40863c87..db9f2e97 100644 --- a/tests/project.rs +++ b/tests/project.rs @@ -2,10 +2,11 @@ use actix_http::StatusCode; use actix_web::test; use bytes::Bytes; use chrono::{Duration, Utc}; +use common::api_v3::ApiV3; use common::database::*; use common::dummy_data::DUMMY_CATEGORIES; -use common::environment::with_test_environment_all; +use common::environment::{with_test_environment_all, with_test_environment, TestEnvironment}; use common::permissions::{PermissionsTest, PermissionsTestContext}; use futures::StreamExt; use labrinth::database::models::project_item::{PROJECTS_NAMESPACE, PROJECTS_SLUGS_NAMESPACE}; @@ -368,9 +369,6 @@ pub async fn test_patch_project() { alpha_project_slug, json!({ "slug": "newslug", - "name": "New successful title", - "description": "New successful description", - "body": "New successful body", "categories": [DUMMY_CATEGORIES[0]], "license_id": "MIT", "issues_url": "https://github.com", @@ -397,8 +395,6 @@ pub async fn test_patch_project() { .await; assert_eq!(project.slug.unwrap(), "newslug"); - assert_eq!(project.description, "New successful description"); - assert_eq!(project.body, "New successful body"); assert_eq!(project.categories, vec![DUMMY_CATEGORIES[0]]); assert_eq!(project.license.id, "MIT"); assert_eq!(project.issues_url, Some("https://github.com".to_string())); @@ -412,6 +408,38 @@ pub async fn test_patch_project() { .await; } +#[actix_rt::test] +pub async fn test_patch_v3() { + // Hits V3-specific patchable fields + with_test_environment(None, |test_env : TestEnvironment| async move { + let api = &test_env.api; + + let alpha_project_slug = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug; + + // Sucessful request to patch many fields. + let resp = api + .edit_project( + alpha_project_slug, + json!({ + "name": "New successful title", + "summary": "New successful summary", + "description": "New successful description", + }), + USER_USER_PAT, + ) + .await; + assert_eq!(resp.status(), 204); + + let project = api + .get_project_deserialized(alpha_project_slug, USER_USER_PAT) + .await; + + assert_eq!(project.name, "New successful title"); + assert_eq!(project.summary, "New successful summary"); + assert_eq!(project.description, "New successful description"); + }).await; +} + #[actix_rt::test] pub async fn test_bulk_edit_categories() { with_test_environment_all(None, |test_env| async move { diff --git a/tests/v2/project.rs b/tests/v2/project.rs index 2bb4c757..4a518a02 100644 --- a/tests/v2/project.rs +++ b/tests/v2/project.rs @@ -1,8 +1,8 @@ use crate::common::{ api_common::ApiProject, api_v2::ApiV2, - database::{ENEMY_USER_PAT, FRIEND_USER_ID, FRIEND_USER_PAT, MOD_USER_PAT, USER_USER_PAT}, - dummy_data::{TestFile, DUMMY_CATEGORIES}, + database::{FRIEND_USER_ID, FRIEND_USER_PAT, USER_USER_PAT}, + dummy_data::TestFile, environment::{with_test_environment, TestEnvironment}, permissions::{PermissionsTest, PermissionsTestContext}, }; @@ -367,162 +367,33 @@ async fn permissions_upload_version() { } #[actix_rt::test] -pub async fn test_patch_project() { - with_test_environment(None, |test_env: TestEnvironment| async move { +pub async fn test_patch_v2() { + // Hits V3-specific patchable fields + // Other fields are tested in test_patch_project (the v2 version of that test) + with_test_environment(None, |test_env : TestEnvironment| async move { let api = &test_env.api; let alpha_project_slug = &test_env.dummy.as_ref().unwrap().project_alpha.project_slug; - let beta_project_slug = &test_env.dummy.as_ref().unwrap().project_beta.project_slug; - - // First, we do some patch requests that should fail. - // Failure because the user is not authorized. - let resp = api - .edit_project( - alpha_project_slug, - json!({ - "title": "Test_Add_Project project - test 1", - }), - ENEMY_USER_PAT, - ) - .await; - assert_eq!(resp.status(), 401); - - // Failure because we are setting URL fields to invalid urls. - for url_type in ["issues_url", "source_url", "wiki_url", "discord_url"] { - let resp = api - .edit_project( - alpha_project_slug, - json!({ - url_type: "w.fake.url", - }), - USER_USER_PAT, - ) - .await; - assert_eq!(resp.status(), 400); - } - - // Failure because these are illegal requested statuses for a normal user. - for req in ["unknown", "processing", "withheld", "scheduled"] { - let resp = api - .edit_project( - alpha_project_slug, - json!({ - "requested_status": req, - }), - USER_USER_PAT, - ) - .await; - assert_eq!(resp.status(), 400); - } - - // Failure because these should not be able to be set by a non-mod - for key in ["moderation_message", "moderation_message_body"] { - let resp = api - .edit_project( - alpha_project_slug, - json!({ - key: "test", - }), - USER_USER_PAT, - ) - .await; - assert_eq!(resp.status(), 401); - - // (should work for a mod, though) - let resp = api - .edit_project( - alpha_project_slug, - json!({ - key: "test", - }), - MOD_USER_PAT, - ) - .await; - assert_eq!(resp.status(), 204); - } - - // Failed patch to alpha slug: - // - slug collision with beta - // - too short slug - // - too long slug - // - not url safe slug - // - not url safe slug - for slug in [ - beta_project_slug, - "a", - &"a".repeat(100), - "not url safe%&^!#$##!@#$%^&*()", - ] { - let resp = api - .edit_project( - alpha_project_slug, - json!({ - "slug": slug, // the other dummy project has this slug - }), - USER_USER_PAT, - ) - .await; - assert_eq!(resp.status(), 400); - } - - // Not allowed to directly set status, as 'beta_project_slug' (the other project) is "processing" and cannot have its status changed like this. - let resp = api - .edit_project( - beta_project_slug, - json!({ - "status": "private" - }), - USER_USER_PAT, - ) - .await; - assert_eq!(resp.status(), 401); // Sucessful request to patch many fields. let resp = api .edit_project( alpha_project_slug, json!({ - "slug": "newslug", - "title": "New successful title", - "description": "New successful description", - "body": "New successful body", - "categories": [DUMMY_CATEGORIES[0]], - "license_id": "MIT", - "issues_url": "https://github.com", - "discord_url": "https://discord.gg", - "wiki_url": "https://wiki.com", "client_side": "optional", "server_side": "required", - "donation_urls": [{ - "id": "patreon", - "platform": "Patreon", - "url": "https://patreon.com" - }] }), USER_USER_PAT, ) .await; assert_eq!(resp.status(), 204); - // Old slug no longer works - let resp = api.get_project(alpha_project_slug, USER_USER_PAT).await; - assert_eq!(resp.status(), 404); + let project = api + .get_project_deserialized(alpha_project_slug, USER_USER_PAT) + .await; - // New slug does work - let project = api.get_project_deserialized("newslug", USER_USER_PAT).await; - - assert_eq!(project.slug.unwrap(), "newslug"); - assert_eq!(project.title, "New successful title"); - assert_eq!(project.description, "New successful description"); - assert_eq!(project.body, "New successful body"); - assert_eq!(project.categories, vec![DUMMY_CATEGORIES[0]]); - assert_eq!(project.license.id, "MIT"); - assert_eq!(project.issues_url, Some("https://github.com".to_string())); - assert_eq!(project.discord_url, Some("https://discord.gg".to_string())); - assert_eq!(project.wiki_url, Some("https://wiki.com".to_string())); - assert_eq!(project.client_side.as_str(), "optional"); - assert_eq!(project.server_side.as_str(), "required"); - assert_eq!(project.donation_urls.unwrap()[0].url, "https://patreon.com"); - }) - .await; + assert_eq!(project.client_side.as_str(), "optional"); + assert_eq!(project.server_side.as_str(), "required"); + }).await; } +