Move to SPDX licenses (#449)

* Move to SPDX licenses

Found a way to do this without breaking API compat, so here it is, instead of waiting for v3

Resolves MOD-129
Resolves #396

* License URL updates

* what was I thinking

* Do a thing

* Add open source filter

* Remove dead imports

* Borrow

* Update 20220910132835_spdx-licenses.sql

* Add license text route

* Update migration

* Address comments
This commit is contained in:
triphora
2022-11-29 23:53:24 -05:00
committed by GitHub
parent 34688852a4
commit 820519b4f7
15 changed files with 748 additions and 1007 deletions

View File

@@ -38,12 +38,6 @@ pub struct ReportType {
pub report_type: String,
}
pub struct License {
pub id: LicenseId,
pub short: String,
pub name: String,
}
pub struct DonationPlatform {
pub id: DonationPlatformId,
pub short: String,
@@ -685,157 +679,6 @@ impl<'a> GameVersionBuilder<'a> {
}
}
#[derive(Default)]
pub struct LicenseBuilder<'a> {
pub short: Option<&'a str>,
pub name: Option<&'a str>,
}
impl License {
pub fn builder() -> LicenseBuilder<'static> {
LicenseBuilder::default()
}
pub async fn get_id<'a, E>(
id: &str,
exec: E,
) -> Result<Option<LicenseId>, DatabaseError>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
let result = sqlx::query!(
"
SELECT id FROM licenses
WHERE short = $1
",
id
)
.fetch_optional(exec)
.await?;
Ok(result.map(|r| LicenseId(r.id)))
}
pub async fn get<'a, E>(
id: LicenseId,
exec: E,
) -> Result<License, DatabaseError>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
let result = sqlx::query!(
"
SELECT short, name FROM licenses
WHERE id = $1
",
id as LicenseId
)
.fetch_one(exec)
.await?;
Ok(License {
id,
short: result.short,
name: result.name,
})
}
pub async fn list<'a, E>(exec: E) -> Result<Vec<License>, DatabaseError>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
let result = sqlx::query!(
"
SELECT id, short, name FROM licenses
"
)
.fetch_many(exec)
.try_filter_map(|e| async {
Ok(e.right().map(|c| License {
id: LicenseId(c.id),
short: c.short,
name: c.name,
}))
})
.try_collect::<Vec<License>>()
.await?;
Ok(result)
}
pub async fn remove<'a, E>(
short: &str,
exec: E,
) -> Result<Option<()>, DatabaseError>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
let result = sqlx::query!(
"
DELETE FROM licenses
WHERE short = $1
",
short
)
.execute(exec)
.await?;
if result.rows_affected() == 0 {
// Nothing was deleted
Ok(None)
} else {
Ok(Some(()))
}
}
}
impl<'a> LicenseBuilder<'a> {
/// The license's short name/abbreviation. Spaces must be replaced with '_' for it to be valid
pub fn short(
self,
short: &'a str,
) -> Result<LicenseBuilder<'a>, DatabaseError> {
Ok(Self {
short: Some(short),
..self
})
}
/// The license's long name
pub fn name(
self,
name: &'a str,
) -> Result<LicenseBuilder<'a>, DatabaseError> {
Ok(Self {
name: Some(name),
..self
})
}
pub async fn insert<'b, E>(
self,
exec: E,
) -> Result<LicenseId, DatabaseError>
where
E: sqlx::Executor<'b, Database = sqlx::Postgres>,
{
let result = sqlx::query!(
"
INSERT INTO licenses (short, name)
VALUES ($1, $2)
ON CONFLICT (short) DO NOTHING
RETURNING id
",
self.short,
self.name,
)
.fetch_one(exec)
.await?;
Ok(LicenseId(result.id))
}
}
#[derive(Default)]
pub struct DonationPlatformBuilder<'a> {
pub short: Option<&'a str>,

View File

@@ -131,9 +131,6 @@ pub struct StatusId(pub i32);
pub struct SideTypeId(pub i32);
#[derive(Copy, Clone, Debug, Type)]
#[sqlx(transparent)]
pub struct LicenseId(pub i32);
#[derive(Copy, Clone, Debug, Type)]
#[sqlx(transparent)]
pub struct DonationPlatformId(pub i32);
#[derive(Copy, Clone, Debug, Type, PartialEq, Eq, Hash)]

View File

@@ -92,7 +92,7 @@ pub struct ProjectBuilder {
pub status: StatusId,
pub client_side: SideTypeId,
pub server_side: SideTypeId,
pub license: LicenseId,
pub license: String,
pub slug: Option<String>,
pub donation_urls: Vec<DonationUrl>,
pub gallery_items: Vec<GalleryItem>,
@@ -201,7 +201,7 @@ pub struct Project {
pub discord_url: Option<String>,
pub client_side: SideTypeId,
pub server_side: SideTypeId,
pub license: LicenseId,
pub license: String,
pub slug: Option<String>,
pub moderation_message: Option<String>,
pub moderation_message_body: Option<String>,
@@ -247,7 +247,7 @@ impl Project {
self.client_side as SideTypeId,
self.server_side as SideTypeId,
self.license_url.as_ref(),
self.license as LicenseId,
&self.license,
self.slug.as_ref(),
self.project_type as ProjectTypeId
)
@@ -301,7 +301,7 @@ impl Project {
client_side: SideTypeId(row.client_side),
status: StatusId(row.status),
server_side: SideTypeId(row.server_side),
license: LicenseId(row.license),
license: row.license,
slug: row.slug,
body: row.body,
follows: row.follows,
@@ -362,7 +362,7 @@ impl Project {
client_side: SideTypeId(m.client_side),
status: StatusId(m.status),
server_side: SideTypeId(m.server_side),
license: LicenseId(m.license),
license: m.license,
slug: m.slug,
body: m.body,
follows: m.follows,
@@ -649,7 +649,7 @@ impl Project {
m.updated updated, m.approved approved, m.status 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.client_side client_side, m.server_side server_side, m.license license, m.slug slug, m.moderation_message moderation_message, m.moderation_message_body moderation_message_body,
s.status status_name, cs.name client_side_type, ss.name server_side_type, l.short short, l.name license_name, pt.name project_type_name, m.flame_anvil_project flame_anvil_project, m.flame_anvil_user flame_anvil_user,
s.status status_name, cs.name client_side_type, ss.name server_side_type, pt.name project_type_name, m.flame_anvil_project flame_anvil_project, m.flame_anvil_user flame_anvil_user,
ARRAY_AGG(DISTINCT c.category || ' |||| ' || mc.is_additional) filter (where c.category is not null) categories,
ARRAY_AGG(DISTINCT v.id || ' |||| ' || v.date_published) filter (where v.id is not null) versions,
ARRAY_AGG(DISTINCT mg.image_url || ' |||| ' || mg.featured || ' |||| ' || mg.created || ' |||| ' || COALESCE(mg.title, ' ') || ' |||| ' || COALESCE(mg.description, ' ')) filter (where mg.image_url is not null) gallery,
@@ -659,7 +659,6 @@ impl Project {
INNER JOIN statuses s ON s.id = m.status
INNER JOIN side_types cs ON m.client_side = cs.id
INNER JOIN side_types ss ON m.server_side = ss.id
INNER JOIN licenses l ON m.license = l.id
LEFT JOIN mods_donations md ON md.joining_mod_id = m.id
LEFT JOIN donation_platforms dp ON md.joining_platform_id = dp.id
LEFT JOIN mods_categories mc ON mc.joining_mod_id = m.id
@@ -667,7 +666,7 @@ impl Project {
LEFT JOIN versions v ON v.mod_id = m.id
LEFT JOIN mods_gallery mg ON mg.mod_id = m.id
WHERE m.id = $1
GROUP BY pt.id, s.id, cs.id, ss.id, l.id, m.id;
GROUP BY pt.id, s.id, cs.id, ss.id, m.id;
",
id as ProjectId,
)
@@ -712,7 +711,7 @@ impl Project {
client_side: SideTypeId(m.client_side),
status: StatusId(m.status),
server_side: SideTypeId(m.server_side),
license: LicenseId(m.license),
license: m.license.clone(),
slug: m.slug.clone(),
body: m.body.clone(),
follows: m.follows,
@@ -806,8 +805,6 @@ impl Project {
status: crate::models::projects::ProjectStatus::from_str(
&m.status_name,
),
license_id: m.short,
license_name: m.license_name,
client_side: crate::models::projects::SideType::from_str(
&m.client_side_type,
),
@@ -838,7 +835,7 @@ impl Project {
m.updated updated, m.approved approved, m.status 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.client_side client_side, m.server_side server_side, m.license license, m.slug slug, m.moderation_message moderation_message, m.moderation_message_body moderation_message_body,
s.status status_name, cs.name client_side_type, ss.name server_side_type, l.short short, l.name license_name, pt.name project_type_name, m.flame_anvil_project flame_anvil_project, m.flame_anvil_user flame_anvil_user,
s.status status_name, cs.name client_side_type, ss.name server_side_type, pt.name project_type_name, m.flame_anvil_project flame_anvil_project, m.flame_anvil_user flame_anvil_user,
ARRAY_AGG(DISTINCT c.category || ' |||| ' || mc.is_additional) filter (where c.category is not null) categories,
ARRAY_AGG(DISTINCT v.id || ' |||| ' || v.date_published) filter (where v.id is not null) versions,
ARRAY_AGG(DISTINCT mg.image_url || ' |||| ' || mg.featured || ' |||| ' || mg.created || ' |||| ' || COALESCE(mg.title, ' ') || ' |||| ' || COALESCE(mg.description, ' ')) filter (where mg.image_url is not null) gallery,
@@ -848,7 +845,6 @@ impl Project {
INNER JOIN statuses s ON s.id = m.status
INNER JOIN side_types cs ON m.client_side = cs.id
INNER JOIN side_types ss ON m.server_side = ss.id
INNER JOIN licenses l ON m.license = l.id
LEFT JOIN mods_donations md ON md.joining_mod_id = m.id
LEFT JOIN donation_platforms dp ON md.joining_platform_id = dp.id
LEFT JOIN mods_categories mc ON mc.joining_mod_id = m.id
@@ -856,7 +852,7 @@ impl Project {
LEFT JOIN versions v ON v.mod_id = m.id
LEFT JOIN mods_gallery mg ON mg.mod_id = m.id
WHERE m.id = ANY($1)
GROUP BY pt.id, s.id, cs.id, ss.id, l.id, m.id;
GROUP BY pt.id, s.id, cs.id, ss.id, m.id;
",
&project_ids_parsed
)
@@ -903,7 +899,7 @@ impl Project {
client_side: SideTypeId(m.client_side),
status: StatusId(m.status),
server_side: SideTypeId(m.server_side),
license: LicenseId(m.license),
license: m.license.clone(),
slug: m.slug.clone(),
body: m.body.clone(),
follows: m.follows,
@@ -983,8 +979,6 @@ impl Project {
})
.collect(),
status: crate::models::projects::ProjectStatus::from_str(&m.status_name),
license_id: m.short,
license_name: m.license_name,
client_side: crate::models::projects::SideType::from_str(&m.client_side_type),
server_side: crate::models::projects::SideType::from_str(&m.server_side_type),
}}))
@@ -1004,8 +998,6 @@ pub struct QueryProject {
pub donation_urls: Vec<DonationUrl>,
pub gallery_items: Vec<GalleryItem>,
pub status: crate::models::projects::ProjectStatus,
pub license_id: String,
pub license_name: String,
pub client_side: crate::models::projects::SideType,
pub server_side: crate::models::projects::SideType,
}