more games information, games route (#756)

* more games information, games route

* adds banner url
This commit is contained in:
Wyatt Verchere
2023-11-14 10:01:31 -08:00
committed by GitHub
parent 375f992a0c
commit f4880d0519
19 changed files with 206 additions and 45 deletions

View File

@@ -214,6 +214,9 @@ pub struct StatusId(pub i32);
pub struct SideTypeId(pub i32);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct GameId(pub i32);
#[derive(Copy, Clone, Debug, Type, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct DonationPlatformId(pub i32);
#[derive(Copy, Clone, Debug, Type, PartialEq, Eq, Hash, Serialize, Deserialize)]

View File

@@ -9,35 +9,71 @@ use futures::TryStreamExt;
use itertools::Itertools;
use serde::{Deserialize, Serialize};
const GAMES_LIST_NAMESPACE: &str = "games";
const LOADER_ID: &str = "loader_id";
const LOADERS_LIST_NAMESPACE: &str = "loaders";
const LOADER_FIELDS_NAMESPACE: &str = "loader_fields";
const LOADER_FIELD_ENUMS_ID_NAMESPACE: &str = "loader_field_enums";
const LOADER_FIELD_ENUM_VALUES_NAMESPACE: &str = "loader_field_enum_values";
#[derive(Clone, Serialize, Deserialize, Debug, Copy)]
pub enum Game {
MinecraftJava,
// MinecraftBedrock
// Future games
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Game {
pub id: GameId,
pub slug: String,
pub name: String,
pub icon_url: Option<String>,
pub banner_url: Option<String>,
}
impl Game {
pub fn name(&self) -> &'static str {
match self {
Game::MinecraftJava => "minecraft-java",
// Game::MinecraftBedrock => "minecraft-bedrock"
// Future games
}
pub async fn get_slug<'a, E>(
slug: &str,
exec: E,
redis: &RedisPool,
) -> Result<Option<Game>, DatabaseError>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
Ok(Self::list(exec, redis)
.await?
.into_iter()
.find(|x| x.slug == slug))
}
pub fn from_name(name: &str) -> Option<Game> {
match name {
"minecraft-java" => Some(Game::MinecraftJava),
// "minecraft-bedrock" => Some(Game::MinecraftBedrock)
// Future games
_ => None,
pub async fn list<'a, E>(exec: E, redis: &RedisPool) -> Result<Vec<Game>, DatabaseError>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres>,
{
let cached_games: Option<Vec<Game>> = redis
.get_deserialized_from_json(GAMES_LIST_NAMESPACE, "games")
.await?;
if let Some(cached_games) = cached_games {
return Ok(cached_games);
}
let result = sqlx::query!(
"
SELECT id, slug, name, icon_url, banner_url FROM games
",
)
.fetch_many(exec)
.try_filter_map(|e| async {
Ok(e.right().map(|x| Game {
id: GameId(x.id),
slug: x.slug,
name: x.name,
icon_url: x.icon_url,
banner_url: x.banner_url,
}))
})
.try_collect::<Vec<Game>>()
.await?;
redis
.set_serialized_to_json(GAMES_LIST_NAMESPACE, "games", &result, None)
.await?;
Ok(result)
}
}
@@ -47,7 +83,7 @@ pub struct Loader {
pub loader: String,
pub icon: String,
pub supported_project_types: Vec<String>,
pub supported_games: Vec<Game>,
pub supported_games: Vec<String>, // slugs
}
impl Loader {
@@ -99,7 +135,7 @@ impl Loader {
"
SELECT l.id id, l.loader loader, l.icon icon,
ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,
ARRAY_AGG(DISTINCT g.name) filter (where g.name is not null) games
ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games
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
@@ -123,9 +159,6 @@ impl Loader {
supported_games: x
.games
.unwrap_or_default()
.iter()
.filter_map(|x| Game::from_name(x))
.collect(),
}))
})
.try_collect::<Vec<_>>()

View File

@@ -571,7 +571,7 @@ impl Project {
t.id thread_id, m.monetization_status monetization_status,
ARRAY_AGG(DISTINCT l.loader) filter (where l.loader is not null) loaders,
ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,
ARRAY_AGG(DISTINCT g.name) filter (where g.name is not null) games,
ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games,
ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is false) categories,
ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories,
JSONB_AGG(DISTINCT jsonb_build_object('id', v.id, 'date_published', v.date_published)) filter (where v.id is not null) versions,

View File

@@ -524,7 +524,7 @@ impl Version {
v.version_type version_type, v.featured featured, v.status status, v.requested_status requested_status, v.ordering ordering,
ARRAY_AGG(DISTINCT l.loader) filter (where l.loader is not null) loaders,
ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,
ARRAY_AGG(DISTINCT g.name) filter (where g.name is not null) games,
ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games,
JSONB_AGG(DISTINCT jsonb_build_object('id', f.id, 'url', f.url, 'filename', f.filename, 'primary', f.is_primary, 'size', f.size, 'file_type', f.file_type)) filter (where f.id is not null) files,
JSONB_AGG(DISTINCT jsonb_build_object('algorithm', h.algorithm, 'hash', encode(h.hash, 'escape'), 'file_id', h.file_id)) filter (where h.hash is not null) hashes,
JSONB_AGG(DISTINCT jsonb_build_object('project_id', d.mod_dependency_id, 'version_id', d.dependency_id, 'dependency_type', d.dependency_type,'file_name', dependency_file_name)) filter (where d.dependency_type is not null) dependencies,

View File

@@ -796,7 +796,7 @@ async fn project_create_inner(
|(mut project_types, mut games), loader| {
if loaders.contains(&loader.id) {
project_types.extend(loader.supported_project_types);
games.extend(loader.supported_games.iter().map(|x| x.name().to_string()));
games.extend(loader.supported_games);
}
(project_types, games)
},

View File

@@ -3,7 +3,7 @@ use std::collections::HashMap;
use super::ApiError;
use crate::database::models::categories::{Category, DonationPlatform, ProjectType, ReportType};
use crate::database::models::loader_fields::{
Loader, LoaderField, LoaderFieldEnumValue, LoaderFieldType,
Game, Loader, LoaderField, LoaderFieldEnumValue, LoaderFieldType,
};
use crate::database::redis::RedisPool;
use actix_web::{web, HttpResponse};
@@ -16,6 +16,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route("category", web::get().to(category_list))
.route("loader", web::get().to(loader_list)),
)
.route("games", web::get().to(games_list))
.route("loader_fields", web::get().to(loader_fields_list))
.route("license", web::get().to(license_list))
.route("license/{id}", web::get().to(license_text))
@@ -24,6 +25,32 @@ pub fn config(cfg: &mut web::ServiceConfig) {
.route("project_type", web::get().to(project_type_list));
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct GameData {
pub slug: String,
pub name: String,
pub icon: Option<String>,
pub banner: Option<String>,
}
pub async fn games_list(
pool: web::Data<PgPool>,
redis: web::Data<RedisPool>,
) -> Result<HttpResponse, ApiError> {
let results = Game::list(&**pool, &redis)
.await?
.into_iter()
.map(|x| GameData {
slug: x.slug,
name: x.name,
icon: x.icon_url,
banner: x.banner_url,
})
.collect::<Vec<_>>();
Ok(HttpResponse::Ok().json(results))
}
#[derive(serde::Serialize, serde::Deserialize)]
pub struct CategoryData {
pub icon: String,
@@ -69,11 +96,7 @@ pub async fn loader_list(
icon: x.icon,
name: x.loader,
supported_project_types: x.supported_project_types,
supported_games: x
.supported_games
.iter()
.map(|x| x.name().to_string())
.collect(),
supported_games: x.supported_games,
})
.collect::<Vec<_>>();

View File

@@ -423,8 +423,7 @@ async fn version_create_inner(
let (all_project_types, all_games): (Vec<String>, Vec<String>) =
loader_structs.iter().fold((vec![], vec![]), |mut acc, x| {
acc.0.extend_from_slice(&x.supported_project_types);
acc.1
.extend(x.supported_games.iter().map(|x| x.name().to_string()));
acc.1.extend(x.supported_games.clone());
acc
});

View File

@@ -27,7 +27,7 @@ pub async fn index_local(
ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null and mc.is_additional is true) additional_categories,
ARRAY_AGG(DISTINCT lo.loader) filter (where lo.loader is not null) loaders,
ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,
ARRAY_AGG(DISTINCT g.name) filter (where g.name is not null) games,
ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games,
ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is false) gallery,
ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is true) featured_gallery,
JSONB_AGG(

View File

@@ -91,7 +91,7 @@ pub async fn send_discord_webhook(
ARRAY_AGG(DISTINCT c.category) filter (where c.category is not null) categories,
ARRAY_AGG(DISTINCT lo.loader) filter (where lo.loader is not null) loaders,
ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,
ARRAY_AGG(DISTINCT g.name) filter (where g.name is not null) games,
ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games,
ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is false) gallery,
ARRAY_AGG(DISTINCT mg.image_url) filter (where mg.image_url is not null and mg.featured is true) featured_gallery,
JSONB_AGG(