You've already forked AstralRinth
forked from didirus/AstralRinth
Search test + v3 (#731)
* search patch for accurate loader/gv filtering * backup * basic search test * finished test * incomplete commit; backing up * Working multipat reroute backup * working rough draft v3 * most tests passing * works * search v2 conversion * added some tags.rs v2 conversions * Worked through warnings, unwraps, prints * refactors * new search test * version files changes fixes * redesign to revs * removed old caches * removed games * fmt clippy * merge conflicts * fmt, prepare * moved v2 routes over to v3 * fixes; tests passing * project type changes * moved files over * fmt, clippy, prepare, etc * loaders to loader_fields, added tests * fmt, clippy, prepare * fixed sorting bug * reversed back- wrong order for consistency * fmt; clippy; prepare --------- Co-authored-by: Jai A <jaiagr+gpg@pm.me>
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::database::redis::RedisPool;
|
||||
|
||||
use super::ids::*;
|
||||
use super::DatabaseError;
|
||||
use chrono::DateTime;
|
||||
use chrono::Utc;
|
||||
use futures::TryStreamExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -14,29 +14,6 @@ pub struct ProjectType {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
pub struct SideType {
|
||||
pub id: SideTypeId,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Loader {
|
||||
pub id: LoaderId,
|
||||
pub loader: String,
|
||||
pub icon: String,
|
||||
pub supported_project_types: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||
pub struct GameVersion {
|
||||
pub id: GameVersionId,
|
||||
pub version: String,
|
||||
#[serde(rename = "type")]
|
||||
pub type_: String,
|
||||
pub created: DateTime<Utc>,
|
||||
pub major: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Category {
|
||||
pub id: CategoryId,
|
||||
@@ -59,21 +36,32 @@ pub struct DonationPlatform {
|
||||
}
|
||||
|
||||
impl Category {
|
||||
pub async fn get_id<'a, E>(name: &str, exec: E) -> Result<Option<CategoryId>, DatabaseError>
|
||||
// Gets hashmap of category ids matching a name
|
||||
// Multiple categories can have the same name, but different project types, so we need to return a hashmap
|
||||
// ProjectTypeId -> CategoryId
|
||||
pub async fn get_ids<'a, E>(
|
||||
name: &str,
|
||||
exec: E,
|
||||
) -> Result<HashMap<ProjectTypeId, CategoryId>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT id FROM categories
|
||||
SELECT id, project_type FROM categories
|
||||
WHERE category = $1
|
||||
",
|
||||
name,
|
||||
)
|
||||
.fetch_optional(exec)
|
||||
.fetch_all(exec)
|
||||
.await?;
|
||||
|
||||
Ok(result.map(|r| CategoryId(r.id)))
|
||||
let mut map = HashMap::new();
|
||||
for r in result {
|
||||
map.insert(ProjectTypeId(r.project_type), CategoryId(r.id));
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
pub async fn get_id_project<'a, E>(
|
||||
@@ -139,221 +127,6 @@ impl Category {
|
||||
}
|
||||
}
|
||||
|
||||
impl Loader {
|
||||
pub async fn get_id<'a, E>(name: &str, exec: E) -> Result<Option<LoaderId>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT id FROM loaders
|
||||
WHERE loader = $1
|
||||
",
|
||||
name
|
||||
)
|
||||
.fetch_optional(exec)
|
||||
.await?;
|
||||
|
||||
Ok(result.map(|r| LoaderId(r.id)))
|
||||
}
|
||||
|
||||
pub async fn list<'a, E>(exec: E, redis: &RedisPool) -> Result<Vec<Loader>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let res: Option<Vec<Loader>> = redis
|
||||
.get_deserialized_from_json(TAGS_NAMESPACE, "loader")
|
||||
.await?;
|
||||
|
||||
if let Some(res) = res {
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT l.id id, l.loader loader, l.icon icon,
|
||||
ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types
|
||||
FROM loaders l
|
||||
LEFT OUTER JOIN loaders_project_types lpt ON joining_loader_id = l.id
|
||||
LEFT OUTER JOIN project_types pt ON lpt.joining_project_type_id = pt.id
|
||||
GROUP BY l.id;
|
||||
"
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async {
|
||||
Ok(e.right().map(|x| Loader {
|
||||
id: LoaderId(x.id),
|
||||
loader: x.loader,
|
||||
icon: x.icon,
|
||||
supported_project_types: x
|
||||
.project_types
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect(),
|
||||
}))
|
||||
})
|
||||
.try_collect::<Vec<_>>()
|
||||
.await?;
|
||||
|
||||
redis
|
||||
.set_serialized_to_json(TAGS_NAMESPACE, "loader", &result, None)
|
||||
.await?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GameVersionBuilder<'a> {
|
||||
pub version: Option<&'a str>,
|
||||
pub version_type: Option<&'a str>,
|
||||
pub date: Option<&'a DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl GameVersion {
|
||||
pub fn builder() -> GameVersionBuilder<'static> {
|
||||
GameVersionBuilder::default()
|
||||
}
|
||||
|
||||
pub async fn get_id<'a, E>(
|
||||
version: &str,
|
||||
exec: E,
|
||||
) -> Result<Option<GameVersionId>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT id FROM game_versions
|
||||
WHERE version = $1
|
||||
",
|
||||
version
|
||||
)
|
||||
.fetch_optional(exec)
|
||||
.await?;
|
||||
|
||||
Ok(result.map(|r| GameVersionId(r.id)))
|
||||
}
|
||||
|
||||
pub async fn list<'a, E>(exec: E, redis: &RedisPool) -> Result<Vec<GameVersion>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let res: Option<Vec<GameVersion>> = redis
|
||||
.get_deserialized_from_json(TAGS_NAMESPACE, "game_version")
|
||||
.await?;
|
||||
|
||||
if let Some(res) = res {
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT gv.id id, gv.version version_, gv.type type_, gv.created created, gv.major FROM game_versions gv
|
||||
ORDER BY created DESC
|
||||
"
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| GameVersion {
|
||||
id: GameVersionId(c.id),
|
||||
version: c.version_,
|
||||
type_: c.type_,
|
||||
created: c.created,
|
||||
major: c.major
|
||||
})) })
|
||||
.try_collect::<Vec<GameVersion>>()
|
||||
.await?;
|
||||
|
||||
redis
|
||||
.set_serialized_to_json(TAGS_NAMESPACE, "game_version", &result, None)
|
||||
.await?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn list_filter<'a, E>(
|
||||
version_type_option: Option<&str>,
|
||||
major_option: Option<bool>,
|
||||
exec: E,
|
||||
redis: &RedisPool,
|
||||
) -> Result<Vec<GameVersion>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let result = Self::list(exec, redis)
|
||||
.await?
|
||||
.into_iter()
|
||||
.filter(|x| {
|
||||
let mut bool = true;
|
||||
|
||||
if let Some(version_type) = version_type_option {
|
||||
bool &= &*x.type_ == version_type;
|
||||
}
|
||||
if let Some(major) = major_option {
|
||||
bool &= x.major == major;
|
||||
}
|
||||
|
||||
bool
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GameVersionBuilder<'a> {
|
||||
/// The game version. Spaces must be replaced with '_' for it to be valid
|
||||
pub fn version(self, version: &'a str) -> Result<GameVersionBuilder<'a>, DatabaseError> {
|
||||
Ok(Self {
|
||||
version: Some(version),
|
||||
..self
|
||||
})
|
||||
}
|
||||
|
||||
pub fn version_type(
|
||||
self,
|
||||
version_type: &'a str,
|
||||
) -> Result<GameVersionBuilder<'a>, DatabaseError> {
|
||||
Ok(Self {
|
||||
version_type: Some(version_type),
|
||||
..self
|
||||
})
|
||||
}
|
||||
|
||||
pub fn created(self, created: &'a DateTime<Utc>) -> GameVersionBuilder<'a> {
|
||||
Self {
|
||||
date: Some(created),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn insert<'b, E>(self, exec: E) -> Result<GameVersionId, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'b, Database = sqlx::Postgres>,
|
||||
{
|
||||
// This looks like a mess, but it *should* work
|
||||
// This allows game versions to be partially updated without
|
||||
// replacing the unspecified fields with defaults.
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
INSERT INTO game_versions (version, type, created)
|
||||
VALUES ($1, COALESCE($2, 'other'), COALESCE($3, timezone('utc', now())))
|
||||
ON CONFLICT (version) DO UPDATE
|
||||
SET type = COALESCE($2, game_versions.type),
|
||||
created = COALESCE($3, game_versions.created)
|
||||
RETURNING id
|
||||
",
|
||||
self.version,
|
||||
self.version_type,
|
||||
self.date.map(chrono::DateTime::naive_utc),
|
||||
)
|
||||
.fetch_one(exec)
|
||||
.await?;
|
||||
|
||||
Ok(GameVersionId(result.id))
|
||||
}
|
||||
}
|
||||
|
||||
impl DonationPlatform {
|
||||
pub async fn get_id<'a, E>(
|
||||
id: &str,
|
||||
@@ -509,51 +282,3 @@ impl ProjectType {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl SideType {
|
||||
pub async fn get_id<'a, E>(name: &str, exec: E) -> Result<Option<SideTypeId>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT id FROM side_types
|
||||
WHERE name = $1
|
||||
",
|
||||
name
|
||||
)
|
||||
.fetch_optional(exec)
|
||||
.await?;
|
||||
|
||||
Ok(result.map(|r| SideTypeId(r.id)))
|
||||
}
|
||||
|
||||
pub async fn list<'a, E>(exec: E, redis: &RedisPool) -> Result<Vec<String>, DatabaseError>
|
||||
where
|
||||
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
|
||||
{
|
||||
let res: Option<Vec<String>> = redis
|
||||
.get_deserialized_from_json(TAGS_NAMESPACE, "side_type")
|
||||
.await?;
|
||||
|
||||
if let Some(res) = res {
|
||||
return Ok(res);
|
||||
}
|
||||
|
||||
let result = sqlx::query!(
|
||||
"
|
||||
SELECT name FROM side_types
|
||||
"
|
||||
)
|
||||
.fetch_many(exec)
|
||||
.try_filter_map(|e| async { Ok(e.right().map(|c| c.name)) })
|
||||
.try_collect::<Vec<String>>()
|
||||
.await?;
|
||||
|
||||
redis
|
||||
.set_serialized_to_json(TAGS_NAMESPACE, "side_type", &result, None)
|
||||
.await?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user