Skip to content

Commit

Permalink
feat(mods): rewrite mod updates
Browse files Browse the repository at this point in the history
  • Loading branch information
Fleeym committed Feb 4, 2025
1 parent b5c6975 commit d0d3927
Show file tree
Hide file tree
Showing 17 changed files with 1,226 additions and 642 deletions.
74 changes: 74 additions & 0 deletions src/database/repository/dependencies.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use sqlx::PgConnection;

use crate::types::{
api::ApiError,
mod_json::ModJson,
models::dependency::{DependencyImportance, FetchedDependency, ModVersionCompare},
};

pub async fn create(
mod_version_id: i32,
json: &ModJson,
conn: &mut PgConnection,
) -> Result<Vec<FetchedDependency>, ApiError> {
let dependencies = json.prepare_dependencies_for_create()?;
if dependencies.is_empty() {
return Ok(vec![]);
}

let len = dependencies.len();
let dependent_id = vec![mod_version_id; len];
let mut dependency_id: Vec<String> = Vec::with_capacity(len);
let mut version: Vec<String> = Vec::with_capacity(len);
let mut compare: Vec<ModVersionCompare> = Vec::with_capacity(len);
let mut importance: Vec<DependencyImportance> = Vec::with_capacity(len);

for i in dependencies {
dependency_id.push(i.dependency_id);
version.push(i.version);
compare.push(i.compare);
importance.push(i.importance);
}

sqlx::query_as!(
FetchedDependency,
r#"INSERT INTO dependencies
(dependent_id, dependency_id, version, compare, importance)
SELECT * FROM UNNEST(
$1::int4[],
$2::text[],
$3::text[],
$4::version_compare[],
$5::dependency_importance[]
)
RETURNING
dependent_id as mod_version_id,
dependency_id,
version,
compare as "compare: _",
importance as "importance: _""#,
&dependent_id,
&dependency_id,
&version,
&compare as &[ModVersionCompare],
&importance as &[DependencyImportance]
)
.fetch_all(conn)
.await
.inspect_err(|e| log::error!("Failed to insert dependencies: {}", e))
.or(Err(ApiError::DbError))
}

pub async fn clear(id: i32, conn: &mut PgConnection) -> Result<(), ApiError> {
sqlx::query!(
"DELETE FROM dependencies
WHERE dependent_id = $1",
id
)
.execute(conn)
.await
.inspect_err(|e| log::error!("Failed to clear deps: {}", e))
.or(Err(ApiError::DbError))?;

Ok(())
}
77 changes: 77 additions & 0 deletions src/database/repository/incompatibilities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use sqlx::PgConnection;

use crate::types::{
api::ApiError,
mod_json::ModJson,
models::{
dependency::ModVersionCompare,
incompatibility::{FetchedIncompatibility, IncompatibilityImportance},
},
};

pub async fn create(
mod_version_id: i32,
json: &ModJson,
conn: &mut PgConnection,
) -> Result<Vec<FetchedIncompatibility>, ApiError> {
let incompats = json.prepare_incompatibilities_for_create()?;
if incompats.is_empty() {
return Ok(vec![]);
}

let len = incompats.len();
let mod_id = vec![mod_version_id; len];
let mut incompatibility_id: Vec<String> = Vec::with_capacity(len);
let mut version: Vec<String> = Vec::with_capacity(len);
let mut compare: Vec<ModVersionCompare> = Vec::with_capacity(len);
let mut importance: Vec<IncompatibilityImportance> = Vec::with_capacity(len);

for i in incompats {
incompatibility_id.push(i.incompatibility_id);
version.push(i.version);
compare.push(i.compare);
importance.push(i.importance);
}

sqlx::query_as!(
FetchedIncompatibility,
r#"INSERT INTO incompatibilities
(mod_id, incompatibility_id, version, compare, importance)
SELECT * FROM UNNEST(
$1::int4[],
$2::text[],
$3::text[],
$4::version_compare[],
$5::incompatibility_importance[]
)
RETURNING
mod_id,
incompatibility_id,
version,
compare as "compare: _",
importance as "importance: _""#,
&mod_id,
&incompatibility_id,
&version,
&compare as &[ModVersionCompare],
&importance as &[IncompatibilityImportance]
)
.fetch_all(conn)
.await
.inspect_err(|e| log::error!("Failed to insert dependencies: {}", e))
.or(Err(ApiError::DbError))
}

pub async fn clear(id: i32, conn: &mut PgConnection) -> Result<(), ApiError> {
sqlx::query!(
"DELETE FROM incompatibilities
WHERE mod_id = $1",
id
)
.execute(conn)
.await
.inspect_err(|e| log::error!("Failed to clear incompats: {}", e))
.or(Err(ApiError::DbError))?;

Ok(())
}
4 changes: 4 additions & 0 deletions src/database/repository/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
pub mod auth_tokens;
pub mod dependencies;
pub mod developers;
pub mod github_login_attempts;
pub mod github_web_logins;
pub mod incompatibilities;
pub mod mod_downloads;
pub mod mod_gd_versions;
pub mod mod_tags;
pub mod mod_version_statuses;
pub mod mod_versions;
pub mod mods;
pub mod refresh_tokens;
52 changes: 52 additions & 0 deletions src/database/repository/mod_gd_versions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use sqlx::PgConnection;

use crate::types::{
api::ApiError,
mod_json::ModJson,
models::mod_gd_version::{DetailedGDVersion, GDVersionEnum, VerPlatform},
};

pub async fn create(
mod_version_id: i32,
json: &ModJson,
conn: &mut PgConnection,
) -> Result<DetailedGDVersion, ApiError> {
let create = json.gd.to_create_payload(json);

let gd: Vec<GDVersionEnum> = create.iter().map(|x| x.gd).collect();
let platform: Vec<VerPlatform> = create.iter().map(|x| x.platform).collect();
let mod_id = vec![mod_version_id; create.len()];

sqlx::query!(
"INSERT INTO mod_gd_versions
(gd, platform, mod_id)
SELECT * FROM UNNEST(
$1::gd_version[],
$2::gd_ver_platform[],
$3::int4[]
)",
&gd as &[GDVersionEnum],
&platform as &[VerPlatform],
&mod_id
)
.execute(conn)
.await
.inspect_err(|e| log::error!("Failed to insert mod_gd_versions: {}", e))
.or(Err(ApiError::DbError))?;

Ok(json.gd.clone())
}

pub async fn clear(mod_version_id: i32, conn: &mut PgConnection) -> Result<(), ApiError> {
sqlx::query!(
"DELETE FROM mod_gd_versions mgv
WHERE mgv.mod_id = $1",
mod_version_id
)
.execute(&mut *conn)
.await
.inspect_err(|e| log::error!("Failed to remove GD versions: {}", e))
.or(Err(ApiError::DbError))?;

Ok(())
}
118 changes: 118 additions & 0 deletions src/database/repository/mod_tags.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::types::api::ApiError;
use crate::types::mod_json::ModJson;
use crate::types::models::tag::Tag;
use sqlx::PgConnection;

Expand Down Expand Up @@ -28,3 +29,120 @@ pub async fn get_all(conn: &mut PgConnection) -> Result<Vec<Tag>, ApiError> {

Ok(tags)
}

pub async fn get_for_mod(id: &str, conn: &mut PgConnection) -> Result<Vec<Tag>, ApiError> {
sqlx::query!(
"SELECT
id,
name,
display_name,
is_readonly
FROM mod_tags mt
INNER JOIN mods_mod_tags mmt ON mmt.tag_id = mt.id
WHERE mmt.mod_id = $1",
id
)
.fetch_all(&mut *conn)
.await
.map_err(|e| {
log::error!("mod_tags::get_tags failed: {}", e);
ApiError::DbError
})
.map(|vec| {
vec.into_iter()
.map(|i| Tag {
id: i.id,
display_name: i.display_name.unwrap_or(i.name.clone()),
name: i.name,
is_readonly: i.is_readonly,
})
.collect()
})
}

pub async fn parse_tag_list(
tags: &[String],
conn: &mut PgConnection,
) -> Result<Vec<Tag>, ApiError> {
if tags.is_empty() {
return Ok(vec![]);
}

let db_tags = get_all(conn).await?;

let mut ret = Vec::new();
for tag in tags {
if let Some(t) = db_tags.iter().find(|t| t.name == *tag) {
ret.push(t.clone());
} else {
return Err(ApiError::BadRequest(format!(
"Tag '{}' isn't allowed. Only the following are allowed: '{}'",
tag,
db_tags
.into_iter()
.map(|t| t.name)
.collect::<Vec<String>>()
.join(", ")
)));
}
}

Ok(ret)
}

pub async fn update_for_mod(
id: &str,
tags: &[Tag],
conn: &mut PgConnection,
) -> Result<(), ApiError> {
let existing = get_for_mod(id, &mut *conn).await?;

let insertable = tags
.iter()
.filter(|t| !existing.iter().any(|e| e.id == t.id))
.map(|x| x.id)
.collect::<Vec<_>>();

let deletable = existing
.iter()
.filter(|e| !tags.iter().any(|t| e.id == t.id))
.map(|x| x.id)
.collect::<Vec<_>>();

if !deletable.is_empty() {
sqlx::query!(
"DELETE FROM mods_mod_tags
WHERE mod_id = $1
AND tag_id = ANY($2)",
id,
&deletable
)
.execute(&mut *conn)
.await
.inspect_err(|e| log::error!("Failed to remove tags: {}", e))
.or(Err(ApiError::DbError))?;
}

if insertable.is_empty() {
return Ok(());
}

let mod_id = vec![id.into(); insertable.len()];

sqlx::query!(
"INSERT INTO mods_mod_tags
(mod_id, tag_id)
SELECT * FROM UNNEST(
$1::text[],
$2::int4[]
)",
&mod_id,
&insertable
)
.execute(&mut *conn)
.await
.inspect_err(|e| log::error!("Failed to insert tags: {}", e))
.or(Err(ApiError::DbError))?;

Ok(())
}
25 changes: 25 additions & 0 deletions src/database/repository/mod_version_statuses.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use sqlx::PgConnection;

use crate::types::{api::ApiError, models::mod_version_status::ModVersionStatusEnum};

pub async fn create(
mod_version_id: i32,
status: ModVersionStatusEnum,
info: Option<String>,
conn: &mut PgConnection,
) -> Result<i32, ApiError> {
sqlx::query!(
"INSERT INTO mod_version_statuses
(mod_version_id, status, info, admin_id)
VALUES ($1, $2, $3, NULL)
RETURNING id",
mod_version_id,
status as ModVersionStatusEnum,
info
)
.fetch_one(conn)
.await
.inspect_err(|e| log::error!("Failed to create status: {}", e))
.or(Err(ApiError::DbError))
.map(|i| i.id)
}
Loading

0 comments on commit d0d3927

Please sign in to comment.