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:
Geometrically
2021-06-16 09:05:35 -07:00
committed by GitHub
parent 2a4caa856e
commit d2c2503cfa
39 changed files with 2365 additions and 1303 deletions

View File

@@ -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
);

View File

@@ -1,4 +1,4 @@
mod cache;
pub mod cache;
pub mod models;
mod postgres_database;
pub use models::Project;

View File

@@ -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)]

View File

@@ -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[]))
",
&notification_ids_parsed
)
.execute(&mut *transaction)
.await?;
sqlx::query!(
"
DELETE FROM notifications
WHERE id IN (SELECT * FROM UNNEST($1::bigint[]))
",
&notification_ids_parsed
)
.execute(&mut *transaction)
.await?;
Ok(Some(()))

View File

@@ -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(),

View File

@@ -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 {
",
&notifications
)
.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 {
",
&notifications
)
.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(()))

View File

@@ -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)]

View File

@@ -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?;