diff --git a/.sqlx/query-9b381f34a80942aaec709ee2470e8fab3d403059a4ccbf1110f4eb46f796405e.json b/.sqlx/query-9b381f34a80942aaec709ee2470e8fab3d403059a4ccbf1110f4eb46f796405e.json new file mode 100644 index 0000000..968c7dd --- /dev/null +++ b/.sqlx/query-9b381f34a80942aaec709ee2470e8fab3d403059a4ccbf1110f4eb46f796405e.json @@ -0,0 +1,59 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n m.id, m.featured, m.download_count as mod_download_count,\n mv.name, mv.version, mv.download_count as mod_version_download_count,\n mv.validated\n FROM mods m\n INNER JOIN mod_versions mv ON m.id = mv.mod_id\n INNER JOIN mods_developers md ON md.mod_id = m.id\n WHERE md.developer_id = $1 AND mv.validated = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "featured", + "type_info": "Bool" + }, + { + "ordinal": 2, + "name": "mod_download_count", + "type_info": "Int4" + }, + { + "ordinal": 3, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "version", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "mod_version_download_count", + "type_info": "Int4" + }, + { + "ordinal": 6, + "name": "validated", + "type_info": "Bool" + } + ], + "parameters": { + "Left": [ + "Int4", + "Bool" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "9b381f34a80942aaec709ee2470e8fab3d403059a4ccbf1110f4eb46f796405e" +} diff --git a/src/endpoints/developers.rs b/src/endpoints/developers.rs index 367d5e4..b741a92 100644 --- a/src/endpoints/developers.rs +++ b/src/endpoints/developers.rs @@ -1,17 +1,33 @@ -use actix_web::{delete, post, put, web, HttpResponse, Responder}; -use serde::Deserialize; +use actix_web::{delete, get, post, put, web, HttpResponse, Responder}; +use serde::{Deserialize, Serialize}; use sqlx::Acquire; use crate::{ auth::token, extractors::auth::Auth, types::{ - api::ApiError, + api::{ApiError, ApiResponse}, models::{developer::Developer, mod_entity::Mod}, }, AppData, }; +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct SimpleDevMod { + pub id: String, + pub featured: bool, + pub download_count: i32, + pub versions: Vec, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +pub struct SimpleDevModVersion { + pub name: String, + pub version: String, + pub download_count: i32, + pub validated: bool, +} + #[derive(Deserialize)] struct AddDevPath { id: String, @@ -157,3 +173,24 @@ pub async fn update_profile( .or(Err(ApiError::TransactionError))?; Ok(HttpResponse::NoContent()) } + +#[derive(Deserialize)] +struct GetOwnModsQuery { + validated: Option, +} + +#[get("v1/me/mods")] +pub async fn get_own_mods( + data: web::Data, + query: web::Query, + auth: Auth, +) -> Result { + let dev = auth.into_developer()?; + let mut pool = data.db.acquire().await.or(Err(ApiError::DbAcquireError))?; + let validated = query.validated.unwrap_or(true); + let mods: Vec = Mod::get_all_for_dev(dev.id, validated, &mut pool).await?; + Ok(HttpResponse::Ok().json(ApiResponse { + error: "".to_string(), + payload: mods, + })) +} diff --git a/src/main.rs b/src/main.rs index 761c87a..7faa96c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -100,6 +100,7 @@ async fn main() -> anyhow::Result<()> { .service(endpoints::developers::remove_dev_from_mod) .service(endpoints::developers::delete_tokens) .service(endpoints::developers::update_profile) + .service(endpoints::developers::get_own_mods) .service(endpoints::tags::index) .service(health) }) diff --git a/src/types/models/mod_entity.rs b/src/types/models/mod_entity.rs index d29035a..99ef810 100644 --- a/src/types/models/mod_entity.rs +++ b/src/types/models/mod_entity.rs @@ -1,5 +1,8 @@ use crate::{ - endpoints::mods::{IndexQueryParams, IndexSortType}, + endpoints::{ + developers::{SimpleDevMod, SimpleDevModVersion}, + mods::{IndexQueryParams, IndexSortType}, + }, types::{ api::{ApiError, PaginatedData}, mod_json::ModJson, @@ -370,6 +373,79 @@ impl Mod { }) } + pub async fn get_all_for_dev( + id: i32, + validated: bool, + pool: &mut PgConnection, + ) -> Result, ApiError> { + struct Record { + id: String, + featured: bool, + mod_download_count: i32, + name: String, + version: String, + mod_version_download_count: i32, + validated: bool, + } + let records: Vec = match sqlx::query_as!( + Record, + "SELECT + m.id, m.featured, m.download_count as mod_download_count, + mv.name, mv.version, mv.download_count as mod_version_download_count, + mv.validated + FROM mods m + INNER JOIN mod_versions mv ON m.id = mv.mod_id + INNER JOIN mods_developers md ON md.mod_id = m.id + WHERE md.developer_id = $1 AND mv.validated = $2", + id, + validated + ) + .fetch_all(&mut *pool) + .await + { + Ok(e) => e, + Err(e) => { + log::error!("{}", e); + return Err(ApiError::DbError); + } + }; + + if records.is_empty() { + return Ok(vec![]); + } + + let mut versions: HashMap> = HashMap::new(); + + for record in &records { + let version = SimpleDevModVersion { + name: record.name.clone(), + version: record.version.clone(), + download_count: record.mod_version_download_count, + validated: record.validated, + }; + + versions.entry(record.id.clone()).or_default().push(version); + } + + let mut map: HashMap = HashMap::new(); + + for i in records { + let mod_entity = SimpleDevMod { + id: i.id.clone(), + featured: i.featured, + download_count: i.mod_download_count, + versions: versions.entry(i.id.clone()).or_default().clone(), + }; + if !map.contains_key(&i.id) { + map.insert(i.id.clone(), mod_entity); + } + } + + let mods: Vec = map.into_iter().map(|x| x.1).collect(); + + Ok(mods) + } + pub async fn get_one(id: &str, pool: &mut PgConnection) -> Result, ApiError> { let records: Vec = sqlx::query_as!( ModRecordGetOne,