You've already forked AstralRinth
forked from didirus/AstralRinth
Final V2 Changes (#212)
* Redo dependencies, add rejection reasons, make notifications more readable * Fix errors, add dependency route, finish PR * Fix clippy errors
This commit is contained in:
12
src/database/cache/mod.rs
vendored
12
src/database/cache/mod.rs
vendored
@@ -2,7 +2,7 @@
|
||||
//pub mod project_query_cache;
|
||||
#[macro_export]
|
||||
macro_rules! generate_cache {
|
||||
($name:ident,$id:ty, $val:ty, $cache_name:ident, $mod_name:ident, $getter_name:ident, $setter_name:ident) => {
|
||||
($name:ident,$id:ty, $val:ty, $cache_name:ident, $mod_name:ident, $getter_name:ident, $setter_name:ident, $remover_name:ident) => {
|
||||
pub mod $mod_name {
|
||||
use cached::async_mutex::Mutex;
|
||||
use cached::{Cached, SizedCache};
|
||||
@@ -20,6 +20,10 @@ macro_rules! generate_cache {
|
||||
let mut cache = $cache_name.lock().await;
|
||||
Cached::cache_set(&mut *cache, id, val.clone());
|
||||
}
|
||||
pub async fn $remover_name<'a>(id: $id) {
|
||||
let mut cache = $cache_name.lock().await;
|
||||
Cached::cache_remove(&mut *cache, &id);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -31,7 +35,8 @@ generate_cache!(
|
||||
PROJECT_CACHE,
|
||||
project_cache,
|
||||
get_cache_project,
|
||||
set_cache_project
|
||||
set_cache_project,
|
||||
remove_cache_project
|
||||
);
|
||||
generate_cache!(
|
||||
query_project,
|
||||
@@ -40,5 +45,6 @@ generate_cache!(
|
||||
QUERY_PROJECT_CACHE,
|
||||
query_project_cache,
|
||||
get_cache_query_project,
|
||||
set_cache_query_project
|
||||
set_cache_query_project,
|
||||
remove_cache_query_project
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mod cache;
|
||||
pub mod cache;
|
||||
pub mod models;
|
||||
mod postgres_database;
|
||||
pub use models::Project;
|
||||
|
||||
@@ -113,7 +113,7 @@ pub struct TeamId(pub i64);
|
||||
#[sqlx(transparent)]
|
||||
pub struct TeamMemberId(pub i64);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Type)]
|
||||
#[derive(Copy, Clone, Debug, Type, PartialEq)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct ProjectId(pub i64);
|
||||
#[derive(Copy, Clone, Debug, Type)]
|
||||
@@ -133,7 +133,7 @@ pub struct LicenseId(pub i32);
|
||||
#[sqlx(transparent)]
|
||||
pub struct DonationPlatformId(pub i32);
|
||||
|
||||
#[derive(Copy, Clone, Debug, Type)]
|
||||
#[derive(Copy, Clone, Debug, Type, PartialEq)]
|
||||
#[sqlx(transparent)]
|
||||
pub struct VersionId(pub i64);
|
||||
#[derive(Copy, Clone, Debug, Type)]
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::ids::*;
|
||||
use crate::database::models::DatabaseError;
|
||||
|
||||
pub struct NotificationBuilder {
|
||||
pub notification_type: Option<String>,
|
||||
pub title: String,
|
||||
pub text: String,
|
||||
pub link: String,
|
||||
@@ -16,6 +17,7 @@ pub struct NotificationActionBuilder {
|
||||
pub struct Notification {
|
||||
pub id: NotificationId,
|
||||
pub user_id: UserId,
|
||||
pub notification_type: Option<String>,
|
||||
pub title: String,
|
||||
pub text: String,
|
||||
pub link: String,
|
||||
@@ -64,6 +66,7 @@ impl NotificationBuilder {
|
||||
Notification {
|
||||
id,
|
||||
user_id: user,
|
||||
notification_type: self.notification_type.clone(),
|
||||
title: self.title.clone(),
|
||||
text: self.text.clone(),
|
||||
link: self.link.clone(),
|
||||
@@ -87,17 +90,18 @@ impl Notification {
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO notifications (
|
||||
id, user_id, title, text, link
|
||||
id, user_id, title, text, link, type
|
||||
)
|
||||
VALUES (
|
||||
$1, $2, $3, $4, $5
|
||||
$1, $2, $3, $4, $5, $6
|
||||
)
|
||||
",
|
||||
self.id as NotificationId,
|
||||
self.user_id as UserId,
|
||||
&self.title,
|
||||
&self.text,
|
||||
&self.link
|
||||
&self.link,
|
||||
self.notification_type
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
@@ -118,7 +122,7 @@ impl Notification {
|
||||
{
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT n.user_id, n.title, n.text, n.link, n.created, n.read,
|
||||
SELECT n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type,
|
||||
STRING_AGG(DISTINCT na.id || ', ' || na.title || ', ' || na.action_route || ', ' || na.action_route_method, ' ,') actions
|
||||
FROM notifications n
|
||||
LEFT OUTER JOIN notifications_actions na on n.id = na.notification_id
|
||||
@@ -150,6 +154,7 @@ impl Notification {
|
||||
Ok(Some(Notification {
|
||||
id,
|
||||
user_id: UserId(row.user_id),
|
||||
notification_type: row.notification_type,
|
||||
title: row.title,
|
||||
text: row.text,
|
||||
link: row.link,
|
||||
@@ -174,7 +179,7 @@ impl Notification {
|
||||
let notification_ids_parsed: Vec<i64> = notification_ids.into_iter().map(|x| x.0).collect();
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT n.id, n.user_id, n.title, n.text, n.link, n.created, n.read,
|
||||
SELECT n.id, n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type,
|
||||
STRING_AGG(DISTINCT na.id || ', ' || na.title || ', ' || na.action_route || ', ' || na.action_route_method, ' ,') actions
|
||||
FROM notifications n
|
||||
LEFT OUTER JOIN notifications_actions na on n.id = na.notification_id
|
||||
@@ -207,6 +212,7 @@ impl Notification {
|
||||
Notification {
|
||||
id,
|
||||
user_id: UserId(row.user_id),
|
||||
notification_type: row.notification_type,
|
||||
title: row.title,
|
||||
text: row.text,
|
||||
link: row.link,
|
||||
@@ -231,7 +237,7 @@ impl Notification {
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT n.id, n.user_id, n.title, n.text, n.link, n.created, n.read,
|
||||
SELECT n.id, n.user_id, n.title, n.text, n.link, n.created, n.read, n.type notification_type,
|
||||
STRING_AGG(DISTINCT na.id || ', ' || na.title || ', ' || na.action_route || ', ' || na.action_route_method, ' ,') actions
|
||||
FROM notifications n
|
||||
LEFT OUTER JOIN notifications_actions na on n.id = na.notification_id
|
||||
@@ -263,6 +269,7 @@ impl Notification {
|
||||
Notification {
|
||||
id,
|
||||
user_id: UserId(row.user_id),
|
||||
notification_type: row.notification_type,
|
||||
title: row.title,
|
||||
text: row.text,
|
||||
link: row.link,
|
||||
@@ -276,13 +283,10 @@ impl Notification {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn remove<'a, 'b, E>(
|
||||
pub async fn remove(
|
||||
id: NotificationId,
|
||||
exec: E,
|
||||
) -> Result<Option<()>, sqlx::error::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<Option<()>, sqlx::error::Error> {
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM notifications_actions
|
||||
@@ -290,7 +294,7 @@ impl Notification {
|
||||
",
|
||||
id as NotificationId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -300,7 +304,36 @@ impl Notification {
|
||||
",
|
||||
id as NotificationId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
Ok(Some(()))
|
||||
}
|
||||
|
||||
pub async fn remove_many(
|
||||
notification_ids: Vec<NotificationId>,
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<Option<()>, sqlx::error::Error> {
|
||||
let notification_ids_parsed: Vec<i64> = notification_ids.into_iter().map(|x| x.0).collect();
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM notifications_actions
|
||||
WHERE notification_id IN (SELECT * FROM UNNEST($1::bigint[]))
|
||||
",
|
||||
¬ification_ids_parsed
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM notifications
|
||||
WHERE id IN (SELECT * FROM UNNEST($1::bigint[]))
|
||||
",
|
||||
¬ification_ids_parsed
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
Ok(Some(()))
|
||||
|
||||
@@ -88,6 +88,8 @@ impl ProjectBuilder {
|
||||
server_side: self.server_side,
|
||||
license: self.license,
|
||||
slug: self.slug,
|
||||
rejection_reason: None,
|
||||
rejection_body: None,
|
||||
};
|
||||
project_struct.insert(&mut *transaction).await?;
|
||||
|
||||
@@ -141,6 +143,8 @@ pub struct Project {
|
||||
pub server_side: SideTypeId,
|
||||
pub license: LicenseId,
|
||||
pub slug: Option<String>,
|
||||
pub rejection_reason: Option<String>,
|
||||
pub rejection_body: Option<String>,
|
||||
}
|
||||
|
||||
impl Project {
|
||||
@@ -204,7 +208,8 @@ impl Project {
|
||||
icon_url, body, body_url, published,
|
||||
updated, status,
|
||||
issues_url, source_url, wiki_url, discord_url, license_url,
|
||||
team_id, client_side, server_side, license, slug
|
||||
team_id, client_side, server_side, license, slug,
|
||||
rejection_reason, rejection_body
|
||||
FROM mods
|
||||
WHERE id = $1
|
||||
",
|
||||
@@ -237,6 +242,8 @@ impl Project {
|
||||
slug: row.slug,
|
||||
body: row.body,
|
||||
follows: row.follows,
|
||||
rejection_reason: row.rejection_reason,
|
||||
rejection_body: row.rejection_body,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
@@ -259,7 +266,8 @@ impl Project {
|
||||
icon_url, body, body_url, published,
|
||||
updated, status,
|
||||
issues_url, source_url, wiki_url, discord_url, license_url,
|
||||
team_id, client_side, server_side, license, slug
|
||||
team_id, client_side, server_side, license, slug,
|
||||
rejection_reason, rejection_body
|
||||
FROM mods
|
||||
WHERE id IN (SELECT * FROM UNNEST($1::bigint[]))
|
||||
",
|
||||
@@ -290,6 +298,8 @@ impl Project {
|
||||
slug: m.slug,
|
||||
body: m.body,
|
||||
follows: m.follows,
|
||||
rejection_reason: m.rejection_reason,
|
||||
rejection_body: m.rejection_body,
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<Project>>()
|
||||
@@ -298,20 +308,17 @@ impl Project {
|
||||
Ok(projects)
|
||||
}
|
||||
|
||||
pub async fn remove_full<'a, 'b, E>(
|
||||
pub async fn remove_full(
|
||||
id: ProjectId,
|
||||
exec: E,
|
||||
) -> Result<Option<()>, sqlx::error::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<Option<()>, sqlx::error::Error> {
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT team_id FROM mods WHERE id = $1
|
||||
",
|
||||
id as ProjectId,
|
||||
)
|
||||
.fetch_optional(exec)
|
||||
.fetch_optional(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
let team_id: TeamId = if let Some(id) = result {
|
||||
@@ -327,7 +334,7 @@ impl Project {
|
||||
",
|
||||
id as ProjectId
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -337,7 +344,7 @@ impl Project {
|
||||
",
|
||||
id as ProjectId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -347,7 +354,7 @@ impl Project {
|
||||
",
|
||||
id as ProjectId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -357,7 +364,7 @@ impl Project {
|
||||
",
|
||||
id as ProjectId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -367,7 +374,7 @@ impl Project {
|
||||
",
|
||||
id as ProjectId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
use futures::TryStreamExt;
|
||||
@@ -378,15 +385,24 @@ impl Project {
|
||||
",
|
||||
id as ProjectId,
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.fetch_many(&mut *transaction)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| VersionId(c.id))) })
|
||||
.try_collect::<Vec<VersionId>>()
|
||||
.await?;
|
||||
|
||||
for version in versions {
|
||||
super::Version::remove_full(version, exec).await?;
|
||||
super::Version::remove_full(version, transaction).await?;
|
||||
}
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM dependencies WHERE mod_dependency_id = $1
|
||||
",
|
||||
id as ProjectId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM mods
|
||||
@@ -394,7 +410,7 @@ impl Project {
|
||||
",
|
||||
id as ProjectId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -404,7 +420,7 @@ impl Project {
|
||||
",
|
||||
team_id as TeamId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -414,7 +430,7 @@ impl Project {
|
||||
",
|
||||
team_id as TeamId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
Ok(Some(()))
|
||||
@@ -552,7 +568,7 @@ impl Project {
|
||||
executor: E,
|
||||
) -> Result<Option<QueryProject>, sqlx::error::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
@@ -560,7 +576,7 @@ impl Project {
|
||||
m.icon_url icon_url, m.body body, m.body_url body_url, m.published published,
|
||||
m.updated updated, 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.team_id team_id, m.client_side client_side, m.server_side server_side, m.license license, m.slug slug, m.rejection_reason rejection_reason, m.rejection_body rejection_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,
|
||||
STRING_AGG(DISTINCT c.category, ',') categories, STRING_AGG(DISTINCT v.id::text, ',') versions
|
||||
FROM mods m
|
||||
@@ -605,6 +621,8 @@ impl Project {
|
||||
slug: m.slug.clone(),
|
||||
body: m.body.clone(),
|
||||
follows: m.follows,
|
||||
rejection_reason: m.rejection_reason,
|
||||
rejection_body: m.rejection_body,
|
||||
},
|
||||
project_type: m.project_type_name,
|
||||
categories: m
|
||||
@@ -647,7 +665,7 @@ impl Project {
|
||||
m.icon_url icon_url, m.body body, m.body_url body_url, m.published published,
|
||||
m.updated updated, 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.team_id team_id, m.client_side client_side, m.server_side server_side, m.license license, m.slug slug, m.rejection_reason rejection_reason, m.rejection_body rejection_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,
|
||||
STRING_AGG(DISTINCT c.category, ',') categories, STRING_AGG(DISTINCT v.id::text, ',') versions
|
||||
FROM mods m
|
||||
@@ -689,7 +707,9 @@ impl Project {
|
||||
license: LicenseId(m.license),
|
||||
slug: m.slug.clone(),
|
||||
body: m.body.clone(),
|
||||
follows: m.follows
|
||||
follows: m.follows,
|
||||
rejection_reason: m.rejection_reason,
|
||||
rejection_body: m.rejection_body,
|
||||
},
|
||||
project_type: m.project_type_name,
|
||||
categories: m.categories.unwrap_or_default().split(',').map(|x| x.to_string()).collect(),
|
||||
|
||||
@@ -238,10 +238,10 @@ impl User {
|
||||
Ok(projects)
|
||||
}
|
||||
|
||||
pub async fn remove<'a, 'b, E>(id: UserId, exec: E) -> Result<Option<()>, sqlx::error::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
pub async fn remove(
|
||||
id: UserId,
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<Option<()>, sqlx::error::Error> {
|
||||
let deleted_user: UserId = crate::models::users::DELETED_USER.into();
|
||||
|
||||
sqlx::query!(
|
||||
@@ -254,7 +254,7 @@ impl User {
|
||||
id as UserId,
|
||||
crate::models::teams::OWNER_ROLE
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -266,7 +266,7 @@ impl User {
|
||||
deleted_user as UserId,
|
||||
id as UserId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
use futures::TryStreamExt;
|
||||
@@ -277,7 +277,7 @@ impl User {
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.fetch_many(&mut *transaction)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|m| m.id as i64)) })
|
||||
.try_collect::<Vec<i64>>()
|
||||
.await?;
|
||||
@@ -289,7 +289,7 @@ impl User {
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -299,7 +299,7 @@ impl User {
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -309,7 +309,7 @@ impl User {
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -319,7 +319,7 @@ impl User {
|
||||
",
|
||||
¬ifications
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -329,7 +329,7 @@ impl User {
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -339,19 +339,16 @@ impl User {
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
Ok(Some(()))
|
||||
}
|
||||
|
||||
pub async fn remove_full<'a, 'b, E>(
|
||||
pub async fn remove_full(
|
||||
id: UserId,
|
||||
exec: E,
|
||||
) -> Result<Option<()>, sqlx::error::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<Option<()>, sqlx::error::Error> {
|
||||
use futures::TryStreamExt;
|
||||
let projects: Vec<ProjectId> = sqlx::query!(
|
||||
"
|
||||
@@ -362,13 +359,14 @@ impl User {
|
||||
id as UserId,
|
||||
crate::models::teams::OWNER_ROLE
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.fetch_many(&mut *transaction)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|m| ProjectId(m.id))) })
|
||||
.try_collect::<Vec<ProjectId>>()
|
||||
.await?;
|
||||
|
||||
for project_id in projects {
|
||||
let _result = super::project_item::Project::remove_full(project_id, exec).await?;
|
||||
let _result =
|
||||
super::project_item::Project::remove_full(project_id, transaction).await?;
|
||||
}
|
||||
|
||||
let notifications: Vec<i64> = sqlx::query!(
|
||||
@@ -378,7 +376,7 @@ impl User {
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.fetch_many(&mut *transaction)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|m| m.id as i64)) })
|
||||
.try_collect::<Vec<i64>>()
|
||||
.await?;
|
||||
@@ -390,7 +388,7 @@ impl User {
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -400,7 +398,7 @@ impl User {
|
||||
",
|
||||
¬ifications
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
let deleted_user: UserId = crate::models::users::DELETED_USER.into();
|
||||
@@ -414,7 +412,7 @@ impl User {
|
||||
deleted_user as UserId,
|
||||
id as UserId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -424,7 +422,7 @@ impl User {
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -434,7 +432,7 @@ impl User {
|
||||
",
|
||||
id as UserId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
Ok(Some(()))
|
||||
|
||||
@@ -10,13 +10,62 @@ pub struct VersionBuilder {
|
||||
pub version_number: String,
|
||||
pub changelog: String,
|
||||
pub files: Vec<VersionFileBuilder>,
|
||||
pub dependencies: Vec<(VersionId, String)>,
|
||||
pub dependencies: Vec<DependencyBuilder>,
|
||||
pub game_versions: Vec<GameVersionId>,
|
||||
pub loaders: Vec<LoaderId>,
|
||||
pub release_channel: ChannelId,
|
||||
pub featured: bool,
|
||||
}
|
||||
|
||||
pub struct DependencyBuilder {
|
||||
pub project_id: Option<ProjectId>,
|
||||
pub version_id: Option<VersionId>,
|
||||
pub dependency_type: String,
|
||||
}
|
||||
|
||||
impl DependencyBuilder {
|
||||
pub async fn insert(
|
||||
self,
|
||||
version_id: VersionId,
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<(), DatabaseError> {
|
||||
let version_dependency_id = if let Some(project_id) = self.project_id {
|
||||
sqlx::query!(
|
||||
"
|
||||
SELECT version.id id FROM (
|
||||
SELECT DISTINCT ON(v.id) v.id, v.date_published FROM versions v
|
||||
INNER JOIN game_versions_versions gvv ON gvv.joining_version_id = v.id AND gvv.game_version_id IN (SELECT game_version_id FROM game_versions_versions WHERE joining_version_id = $2)
|
||||
INNER JOIN loaders_versions lv ON lv.version_id = v.id AND lv.loader_id IN (SELECT loader_id FROM loaders_versions WHERE version_id = $2)
|
||||
WHERE v.mod_id = $1
|
||||
) AS version
|
||||
ORDER BY version.date_published DESC
|
||||
LIMIT 1
|
||||
",
|
||||
project_id as ProjectId,
|
||||
version_id as VersionId,
|
||||
)
|
||||
.fetch_optional(&mut *transaction).await?.map(|x| VersionId(x.id))
|
||||
} else {
|
||||
self.version_id
|
||||
};
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO dependencies (dependent_id, dependency_type, dependency_id, mod_dependency_id)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
",
|
||||
version_id as VersionId,
|
||||
self.dependency_type,
|
||||
version_dependency_id.map(|x| x.0),
|
||||
self.project_id.map(|x| x.0),
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VersionFileBuilder {
|
||||
pub url: String,
|
||||
pub filename: String,
|
||||
@@ -105,20 +154,10 @@ impl VersionBuilder {
|
||||
}
|
||||
|
||||
for dependency in self.dependencies {
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO dependencies (dependent_id, dependency_id, dependency_type)
|
||||
VALUES ($1, $2, $3)
|
||||
",
|
||||
self.version_id as VersionId,
|
||||
dependency.0 as VersionId,
|
||||
dependency.1,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
dependency.insert(self.version_id, transaction).await?;
|
||||
}
|
||||
|
||||
for loader in self.loaders {
|
||||
for loader in self.loaders.clone() {
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO loaders_versions (loader_id, version_id)
|
||||
@@ -131,7 +170,7 @@ impl VersionBuilder {
|
||||
.await?;
|
||||
}
|
||||
|
||||
for game_version in self.game_versions {
|
||||
for game_version in self.game_versions.clone() {
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT INTO game_versions_versions (game_version_id, joining_version_id)
|
||||
@@ -144,6 +183,42 @@ impl VersionBuilder {
|
||||
.await?;
|
||||
}
|
||||
|
||||
// Sync dependencies
|
||||
|
||||
use futures::stream::TryStreamExt;
|
||||
|
||||
let dependencies = sqlx::query!(
|
||||
"
|
||||
SELECT d.id id
|
||||
FROM versions v
|
||||
INNER JOIN dependencies d ON d.dependent_id = v.id
|
||||
INNER JOIN game_versions_versions gvv ON gvv.joining_version_id = v.id AND gvv.game_version_id IN (SELECT * FROM UNNEST($2::integer[]))
|
||||
INNER JOIN loaders_versions lv ON lv.version_id = v.id AND lv.loader_id IN (SELECT * FROM UNNEST($3::integer[]))
|
||||
WHERE v.mod_id = $1
|
||||
",
|
||||
self.project_id as ProjectId,
|
||||
&self.game_versions.iter().map(|x| x.0).collect::<Vec<i32>>(),
|
||||
&self.loaders.iter().map(|x| x.0).collect::<Vec<i32>>(),
|
||||
)
|
||||
.fetch_many(&mut *transaction)
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|d| d.id as i64))
|
||||
})
|
||||
.try_collect::<Vec<i64>>()
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE dependencies
|
||||
SET dependency_id = $2
|
||||
WHERE id IN (SELECT * FROM UNNEST($1::bigint[]))
|
||||
",
|
||||
&dependencies,
|
||||
self.version_id as VersionId,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
Ok(self.version_id)
|
||||
}
|
||||
}
|
||||
@@ -200,17 +275,17 @@ impl Version {
|
||||
}
|
||||
|
||||
// TODO: someone verify this
|
||||
pub async fn remove_full<'a, E>(id: VersionId, exec: E) -> Result<Option<()>, sqlx::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
|
||||
{
|
||||
pub async fn remove_full(
|
||||
id: VersionId,
|
||||
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
) -> Result<Option<()>, sqlx::Error> {
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT EXISTS(SELECT 1 FROM versions WHERE id = $1)
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.fetch_one(exec)
|
||||
.fetch_one(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
if !result.exists.unwrap_or(false) {
|
||||
@@ -224,7 +299,33 @@ impl Version {
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
use futures::TryStreamExt;
|
||||
|
||||
let game_versions: Vec<i32> = sqlx::query!(
|
||||
"
|
||||
SELECT game_version_id id FROM game_versions_versions
|
||||
WHERE joining_version_id = $1
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.fetch_many(&mut *transaction)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| c.id)) })
|
||||
.try_collect::<Vec<i32>>()
|
||||
.await?;
|
||||
|
||||
let loaders: Vec<i32> = sqlx::query!(
|
||||
"
|
||||
SELECT loader_id id FROM loaders_versions
|
||||
WHERE version_id = $1
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.fetch_many(&mut *transaction)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| c.id)) })
|
||||
.try_collect::<Vec<i32>>()
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -234,7 +335,7 @@ impl Version {
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -244,7 +345,7 @@ impl Version {
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -254,11 +355,9 @@ impl Version {
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
use futures::TryStreamExt;
|
||||
|
||||
let files = sqlx::query!(
|
||||
"
|
||||
SELECT files.id, files.url, files.filename, files.is_primary FROM files
|
||||
@@ -266,7 +365,7 @@ impl Version {
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.fetch_many(&mut *transaction)
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|c| VersionFile {
|
||||
id: FileId(c.id),
|
||||
@@ -301,7 +400,7 @@ impl Version {
|
||||
",
|
||||
id as VersionId
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
@@ -311,54 +410,71 @@ impl Version {
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
// Sync dependencies
|
||||
|
||||
let project_id = sqlx::query!(
|
||||
"
|
||||
SELECT mod_id FROM versions WHERE id = $1
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.fetch_one(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
let new_version_id = sqlx::query!(
|
||||
"
|
||||
SELECT v.id id
|
||||
FROM versions v
|
||||
INNER JOIN game_versions_versions gvv ON gvv.joining_version_id = v.id AND gvv.game_version_id IN (SELECT * FROM UNNEST($2::integer[]))
|
||||
INNER JOIN loaders_versions lv ON lv.version_id = v.id AND lv.loader_id IN (SELECT * FROM UNNEST($3::integer[]))
|
||||
WHERE v.mod_id = $1
|
||||
ORDER BY v.date_published DESC
|
||||
LIMIT 1
|
||||
",
|
||||
project_id.mod_id,
|
||||
&game_versions,
|
||||
&loaders,
|
||||
)
|
||||
.fetch_optional(&mut *transaction)
|
||||
.await?
|
||||
.map(|x| x.id);
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE dependencies
|
||||
SET dependency_id = $2
|
||||
WHERE dependency_id = $1
|
||||
",
|
||||
id as VersionId,
|
||||
new_version_id,
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM dependencies WHERE mod_dependency_id = NULL AND dependency_id = NULL
|
||||
",
|
||||
)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
// delete version
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM versions WHERE id = $1
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.execute(exec)
|
||||
.execute(&mut *transaction)
|
||||
.await?;
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
DELETE FROM dependencies WHERE dependent_id = $1
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.execute(exec)
|
||||
.await?;
|
||||
|
||||
Ok(Some(()))
|
||||
}
|
||||
|
||||
pub async fn get_dependencies<'a, E>(
|
||||
id: VersionId,
|
||||
exec: E,
|
||||
) -> Result<Vec<VersionId>, sqlx::Error>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
use futures::stream::TryStreamExt;
|
||||
|
||||
let vec = sqlx::query!(
|
||||
"
|
||||
SELECT dependency_id id FROM dependencies
|
||||
WHERE dependent_id = $1
|
||||
",
|
||||
id as VersionId,
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|v| VersionId(v.id))) })
|
||||
.try_collect::<Vec<VersionId>>()
|
||||
.await?;
|
||||
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
pub async fn get_project_versions<'a, E>(
|
||||
project_id: ProjectId,
|
||||
game_versions: Option<Vec<String>>,
|
||||
@@ -491,7 +607,7 @@ impl Version {
|
||||
STRING_AGG(DISTINCT gv.version, ',') game_versions, STRING_AGG(DISTINCT l.loader, ',') loaders,
|
||||
STRING_AGG(DISTINCT f.id || ', ' || f.filename || ', ' || f.is_primary || ', ' || f.url, ' ,') files,
|
||||
STRING_AGG(DISTINCT h.algorithm || ', ' || encode(h.hash, 'escape') || ', ' || h.file_id, ' ,') hashes,
|
||||
STRING_AGG(DISTINCT d.dependency_id || ', ' || d.dependency_type, ' ,') dependencies
|
||||
STRING_AGG(DISTINCT COALESCE(d.dependency_id, 0) || ', ' || COALESCE(d.mod_dependency_id, 0) || ', ' || d.dependency_type, ' ,') dependencies
|
||||
FROM versions v
|
||||
INNER JOIN release_channels rc on v.release_channel = rc.id
|
||||
LEFT OUTER JOIN game_versions_versions gvv on v.id = gvv.joining_version_id
|
||||
@@ -557,11 +673,24 @@ impl Version {
|
||||
.for_each(|f| {
|
||||
let dependency: Vec<&str> = f.split(", ").collect();
|
||||
|
||||
if dependency.len() >= 2 {
|
||||
dependencies.push((
|
||||
VersionId(dependency[0].parse().unwrap_or(0)),
|
||||
dependency[1].to_string(),
|
||||
))
|
||||
if dependency.len() >= 3 {
|
||||
dependencies.push(QueryDependency {
|
||||
project_id: match &*dependency[2] {
|
||||
"0" => None,
|
||||
_ => match dependency[2].parse() {
|
||||
Ok(x) => Some(ProjectId(x)),
|
||||
Err(_) => None,
|
||||
},
|
||||
},
|
||||
version_id: match &*dependency[0] {
|
||||
"0" => None,
|
||||
_ => match dependency[0].parse() {
|
||||
Ok(x) => Some(VersionId(x)),
|
||||
Err(_) => None,
|
||||
},
|
||||
},
|
||||
dependency_type: dependency[1].to_string(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -615,7 +744,7 @@ impl Version {
|
||||
STRING_AGG(DISTINCT gv.version, ',') game_versions, STRING_AGG(DISTINCT l.loader, ',') loaders,
|
||||
STRING_AGG(DISTINCT f.id || ', ' || f.filename || ', ' || f.is_primary || ', ' || f.url, ' ,') files,
|
||||
STRING_AGG(DISTINCT h.algorithm || ', ' || encode(h.hash, 'escape') || ', ' || h.file_id, ' ,') hashes,
|
||||
STRING_AGG(DISTINCT d.dependency_id || ', ' || d.dependency_type, ' ,') dependencies
|
||||
STRING_AGG(DISTINCT COALESCE(d.dependency_id, 0) || ', ' || COALESCE(d.mod_dependency_id, 0) || ', ' || d.dependency_type, ' ,') dependencies
|
||||
FROM versions v
|
||||
INNER JOIN release_channels rc on v.release_channel = rc.id
|
||||
LEFT OUTER JOIN game_versions_versions gvv on v.id = gvv.joining_version_id
|
||||
@@ -678,8 +807,28 @@ impl Version {
|
||||
v.dependencies.unwrap_or_default().split(" ,").for_each(|f| {
|
||||
let dependency: Vec<&str> = f.split(", ").collect();
|
||||
|
||||
if dependency.len() >= 2 {
|
||||
dependencies.push((VersionId(dependency[0].parse().unwrap_or(0)), dependency[1].to_string()))
|
||||
if dependency.len() >= 3 {
|
||||
dependencies.push(QueryDependency {
|
||||
project_id: match &*dependency[2] {
|
||||
"0" => None,
|
||||
_ => {
|
||||
match dependency[2].parse() {
|
||||
Ok(x) => Some(ProjectId(x)),
|
||||
Err(_) => None,
|
||||
}
|
||||
},
|
||||
},
|
||||
version_id: match &*dependency[0] {
|
||||
"0" => None,
|
||||
_ => {
|
||||
match dependency[0].parse() {
|
||||
Ok(x) => Some(VersionId(x)),
|
||||
Err(_) => None,
|
||||
}
|
||||
},
|
||||
},
|
||||
dependency_type: dependency[1].to_string()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -743,7 +892,14 @@ pub struct QueryVersion {
|
||||
pub game_versions: Vec<String>,
|
||||
pub loaders: Vec<String>,
|
||||
pub featured: bool,
|
||||
pub dependencies: Vec<(VersionId, String)>,
|
||||
pub dependencies: Vec<QueryDependency>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct QueryDependency {
|
||||
pub project_id: Option<ProjectId>,
|
||||
pub version_id: Option<VersionId>,
|
||||
pub dependency_type: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -11,7 +11,20 @@ pub async fn connect() -> Result<PgPool, sqlx::Error> {
|
||||
|
||||
let database_url = dotenv::var("DATABASE_URL").expect("`DATABASE_URL` not in .env");
|
||||
let pool = PgPoolOptions::new()
|
||||
.max_connections(20)
|
||||
.min_connections(
|
||||
dotenv::var("DATABASE_MIN_CONNECTIONS")
|
||||
.ok()
|
||||
.map(|x| x.parse::<u32>().ok())
|
||||
.flatten()
|
||||
.unwrap_or(16),
|
||||
)
|
||||
.max_connections(
|
||||
dotenv::var("DATABASE_MAX_CONNECTIONS")
|
||||
.ok()
|
||||
.map(|x| x.parse::<u32>().ok())
|
||||
.flatten()
|
||||
.unwrap_or(16),
|
||||
)
|
||||
.connect(&database_url)
|
||||
.await?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user