Skip to content

Commit

Permalink
feat(mod_versions): temp commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Fleeym committed Jan 20, 2025
1 parent 029fc61 commit e0643e6
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/database/repository/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod auth_tokens;
pub mod developers;
pub mod mod_downloads;
pub mod mod_gd_versions;
pub mod mod_tags;
pub mod mod_versions;
pub mod mods;
37 changes: 37 additions & 0 deletions src/database/repository/mod_gd_versions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::types::api::ApiError;
use crate::types::models::mod_gd_version::{GDVersionEnum, ModGDVersionCreate, VerPlatform};
use sqlx::{PgConnection, Postgres, QueryBuilder};

pub async fn create_from_json(
json: &[ModGDVersionCreate],
mod_version_id: i32,
conn: &mut PgConnection,
) -> Result<(), ApiError> {
if json.is_empty() {
return Err(ApiError::BadRequest(
"mod.json has no gd versions added".into(),
));
}

let mut builder: QueryBuilder<Postgres> =
QueryBuilder::new("INSERT INTO mod_gd_versions (gd, platform, mod_id) VALUES ");

for (i, current) in json.iter().enumerate() {
builder.push("(");
let mut separated = builder.separated(", ");
separated.push_bind(current.gd as GDVersionEnum);
separated.push_bind(current.platform as VerPlatform);
separated.push_bind(mod_version_id);
separated.push_unseparated(")");
if i != json.len() - 1 {
separated.push_unseparated(", ");
}
}

builder.build().execute(conn).await.map_err(|e| {
log::error!("Failed to insert mod_gd_versions: {}", e);
ApiError::DbError
})?;

Ok(())
}
118 changes: 117 additions & 1 deletion src/database/repository/mod_tags.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::database::repository::developers::get_all_for_mod;
use crate::types::api::ApiError;
use crate::types::models::tag::Tag;
use sqlx::PgConnection;
use sqlx::{PgConnection, Postgres, QueryBuilder};

pub async fn get_all(conn: &mut PgConnection) -> Result<Vec<Tag>, ApiError> {
let tags = sqlx::query!(
Expand Down Expand Up @@ -28,3 +29,118 @@ pub async fn get_all(conn: &mut PgConnection) -> Result<Vec<Tag>, ApiError> {

Ok(tags)
}

pub async fn get_all_from_names(
names: &[String],
conn: &mut PgConnection,
) -> Result<Vec<Tag>, ApiError> {
let tags = sqlx::query!(
"SELECT
id,
name,
display_name,
is_readonly
FROM mod_tags
WHERE name = ANY($1)",
names
)
.fetch_all(&mut *conn)
.await
.map_err(|e| {
log::error!("mod_tags::get_all_from_names failed: {}", e);
ApiError::DbError
})?
.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::<Vec<Tag>>();

Ok(tags)
}

pub async fn get_for_mod(mod_id: &str, conn: &mut PgConnection) -> Result<Vec<Tag>, ApiError> {
Ok(sqlx::query!(
"SELECT
mt.id,
mt.name,
mt.display_name,
mt.is_readonly
FROM mod_tags mt
INNER JOIN mods_mod_tags mmt ON mmt.tag_id = mt.id
WHERE mmt.mod_id = $1",
mod_id
)
.fetch_all(conn)
.await
.map_err(|e| {
log::error!("Failed to fetch mod_tags for mod: {}", e);
ApiError::DbError
})?
.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::<Vec<Tag>>())
}

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

let insertable = new_tags
.iter()
.filter(|t| !existing.iter().any(|e| e.id == **t))
.collect::<Vec<i32>>();

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

sqlx::query!(
"DELETE FROM mods_mod_tags
WHERE mod_id = $1
AND tag_id = ANY($2)",
mod_id,
&deletable
)
.execute(conn)
.await
.map_err(|e| {
log::error!("Failed to delete mod tags: {}", e);
ApiError::DbError
})?;

let mut builder: QueryBuilder<Postgres> =
QueryBuilder::new("INSERT INTO mods_mod_tags (mod_id, tag_id) VALUES ");

for (i, tag) in insertable.into_iter().enumerate() {
builder.push("(");
let mut separated = builder.separated(", ");
separated.push_bind(mod_id);
separated.push_bind(tag);
builder.push(")");

if i != insertable.len() - 1 {
builder.push(", ");
}
}

builder.build().execute(conn).await.map_err(|e| {
log::error!("Failed to insert tags for mod: {}", e);
ApiError::DbError
})?;

Ok(())
}
52 changes: 52 additions & 0 deletions src/database/repository/mod_versions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,58 @@
use crate::database::repository::{mod_gd_versions, mod_tags};
use crate::endpoints::tags;
use crate::types::api::ApiError;
use crate::types::mod_json::ModJson;
use crate::types::models::mod_gd_version::ModGDVersion;
use crate::types::models::mod_version::ModVersion;
use crate::types::models::tag::Tag;
use sqlx::PgConnection;

pub async fn create_from_json(
json: &ModJson,
conn: &mut PgConnection,
) -> Result<ModVersion, ApiError> {
sqlx::query!("SET CONSTRAINTS public.mod_versions.mod_versions_status_id_fkey DEFERRED")
.execute(conn)
.await
.map_err(|e| {
log::error!(
"Failed to update constraints for mod_version creation: {}",
e
);
ApiError::DbError
})?;

// status_id = 0 will be modified later
let id = sqlx::query!(
"INSERT INTO mod_versions
(name, description, version, download_link, hash, geode,
early_load, api, mod_id, status_id) VALUES
($1, $2, $3, $4, $5, $6,
$7, $8, $9, 0)
RETURNING id",
json.name,
json.description,
json.version,
json.download_url,
json.hash,
json.geode,
json.early_load,
json.api.is_some(),
json.id
)
.fetch_one(conn)
.await
.map_err(|e| {
log::error!("Failed to create mod_version: {}", e);
ApiError::DbError
})?
.id;

let tags = mod_tags::get_all_from_names(json.tags.as_ref().unwrap_or_default(), conn).await?;
mod_tags::update_for_mod(&json.id, tags.into_iter().map(|x| x.id).collect(), conn).await?;
mod_gd_versions::create_from_json(&json.gd.to_create_payload(json), id, conn).await?;
}

pub async fn increment_downloads(id: i32, conn: &mut PgConnection) -> Result<(), ApiError> {
sqlx::query!(
"UPDATE mod_versions
Expand Down
4 changes: 2 additions & 2 deletions src/types/mod_json.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use std::io::{Cursor, Read, Seek};

use actix_web::web::Bytes;
Expand All @@ -12,7 +12,7 @@ use semver::Version;
use serde::Deserialize;
use std::io::BufReader;
use zip::read::ZipFile;

use crate::types::models::mod_gd_version::{GDVersionEnum, ModGDVersionCreate, VerPlatform};
use super::{
api::ApiError,
models::{
Expand Down
4 changes: 2 additions & 2 deletions src/types/models/mod_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,7 @@ impl ModVersion {
make_accepted: bool,
pool: &mut PgConnection,
) -> Result<(), ApiError> {
if let Err(e) = sqlx::query!("SET CONSTRAINTS mod_versions_status_id_fkey DEFERRED")
if let Err(e) = sqlx::query!("SET CONSTRAINTS public.mod_versions.mod_versions_status_id_fkey DEFERRED")
.execute(&mut *pool)
.await
{
Expand Down Expand Up @@ -615,7 +615,7 @@ impl ModVersion {
}

// Revert deferred constraints
if let Err(e) = sqlx::query!("SET CONSTRAINTS mod_versions_status_id_fkey IMMEDIATE")
if let Err(e) = sqlx::query!("SET CONSTRAINTS public.mod_versions.mod_versions_status_id_fkey IMMEDIATE")
.execute(&mut *pool)
.await
{
Expand Down

0 comments on commit e0643e6

Please sign in to comment.