Skip to content

Commit

Permalink
finish up /v1/mods/{id}/versions/latest
Browse files Browse the repository at this point in the history
  • Loading branch information
Fleeym committed Feb 20, 2024
1 parent ccdb520 commit 3d339ff
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 62 deletions.
27 changes: 25 additions & 2 deletions openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ tags:
description: User management

paths:
/:
get:
summary: Health check
description: Returns a 200 OK if the index is up and running
responses:
"200":
description: OK

/v1/login/github:
post:
tags:
Expand Down Expand Up @@ -211,14 +219,21 @@ paths:
"500":
$ref: "#/components/responses/InternalServerError"

/v1/mods/{id}/latest:
/v1/mods/{id}/versions/latest:
get:
tags:
- mods
summary: Get info for the latest version of a mod (NOT YET IMPLEMENTED)
summary: Get info for the latest version of a mod
description: Returns info for the current latest version of the mod
parameters:
- $ref: "#/components/parameters/ModID"
- $ref: "#/components/parameters/Platforms"
- name: gd
in: query
description: Geometry Dash version
required: true
schema:
$ref: "#/components/schemas/GDVersionString"

responses:
"200":
Expand Down Expand Up @@ -625,6 +640,14 @@ components:
example: 10
schema:
type: integer

Platforms:
name: platforms
in: query
description: Platforms that mods have to support, comma separated [win,android32,android64,mac,ios]
example: "win,android32,android64"
schema:
type: string
responses:
Unauthorized:
description: Unauthorized
Expand Down
70 changes: 47 additions & 23 deletions src/endpoints/mod_versions.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::str::FromStr;

use actix_web::{dev::ConnectionInfo, get, post, put, web, HttpResponse, Responder};
use serde::Deserialize;
use sqlx::{types::ipnetwork::IpNetwork, Acquire};
Expand All @@ -11,6 +13,7 @@ use crate::{
developer::Developer,
download,
mod_entity::{download_geode_file, Mod},
mod_gd_version::{GDVersionEnum, VerPlatform},
mod_version::ModVersion,
},
},
Expand Down Expand Up @@ -45,42 +48,63 @@ struct UpdateVersionPath {
version: String,
}

#[derive(Deserialize)]
struct GetOneQuery {
platforms: Option<String>,
gd: Option<String>,
}

#[get("v1/mods/{id}/versions/{version}")]
pub async fn get_one(
path: web::Path<GetOnePath>,
data: web::Data<AppData>,
query: web::Query<GetOneQuery>,
) -> Result<impl Responder, ApiError> {
let mut pool = data.db.acquire().await.or(Err(ApiError::DbAcquireError))?;
let mut version = ModVersion::get_one(&path.id, &path.version, &mut pool).await?;

let mut version = {
if path.version == "latest" {
let gd: Option<GDVersionEnum> = match query.gd {
Some(ref gd) => Some(
GDVersionEnum::from_str(gd)
.or(Err(ApiError::BadRequest("Invalid gd".to_string())))?,
),
None => None,
};

let mut platforms: Vec<VerPlatform> = vec![];

if let Some(p) = &query.platforms {
for x in p.split(',') {
match VerPlatform::from_str(x) {
Ok(v) => {
if v == VerPlatform::Android {
platforms.push(VerPlatform::Android32);
platforms.push(VerPlatform::Android64);
} else {
platforms.push(v);
}
}
Err(_) => {
return Err(ApiError::BadRequest("Invalid platform".to_string()));
}
}
}
};

ModVersion::get_latest_for_mod(&path.id, gd, platforms, &mut pool).await?
} else {
ModVersion::get_one(&path.id, &path.version, &mut pool).await?
}
};

version.modify_download_link(&data.app_url);
Ok(web::Json(ApiResponse {
error: "".to_string(),
payload: version,
}))
}

#[get("v1/mods/{id}/versions/latest")]
pub async fn get_latest(
path: web::Path<String>,
data: web::Data<AppData>,
) -> Result<impl Responder, ApiError> {
let mut pool = data.db.acquire().await.or(Err(ApiError::DbAcquireError))?;
let ids = vec![path.into_inner()];
let version = ModVersion::get_latest_for_mods(&mut pool, &ids, None, vec![]).await?;

match version.get(&ids[0]) {
None => Err(ApiError::NotFound(format!("Mod {} not found", ids[0]))),
Some(v) => {
let mut v = v.clone();
v.modify_download_link(&data.app_url);
Ok(web::Json(ApiResponse {
error: "".to_string(),
payload: v,
}))
}
}
}

#[get("v1/mods/{id}/versions/{version}/download")]
pub async fn download_version(
path: web::Path<GetOnePath>,
Expand Down
1 change: 0 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ async fn main() -> anyhow::Result<()> {
.service(endpoints::mods::create)
.service(endpoints::mods::get_logo)
.service(endpoints::mod_versions::get_one)
.service(endpoints::mod_versions::get_latest)
.service(endpoints::mod_versions::download_version)
.service(endpoints::mod_versions::create_version)
.service(endpoints::mod_versions::update_version)
Expand Down
22 changes: 18 additions & 4 deletions src/types/models/dependency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ use sqlx::{PgConnection, Postgres, QueryBuilder};

use crate::types::api::ApiError;

use super::mod_version::ModVersion;

#[derive(sqlx::FromRow, Clone)]
pub struct Dependency {
pub dependent_id: i32,
Expand Down Expand Up @@ -38,6 +36,22 @@ pub struct FetchedDependency {
pub importance: DependencyImportance,
}

impl FetchedDependency {
pub fn to_response(&self) -> ResponseDependency {
ResponseDependency {
mod_id: self.dependency_id.clone(),
version: {
if self.version == "*" {
"*".to_string()
} else {
format!("{}{}", self.compare, self.version)
}
},
importance: self.importance,
}
}
}

#[derive(sqlx::Type, Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
#[sqlx(type_name = "version_compare")]
pub enum ModVersionCompare {
Expand Down Expand Up @@ -112,13 +126,13 @@ impl Dependency {
}

pub async fn get_for_mod_version(
ver: &ModVersion,
id: i32,
pool: &mut PgConnection,
) -> Result<Vec<FetchedDependency>, ApiError> {
match sqlx::query_as!(FetchedDependency,
r#"SELECT dp.dependent_id as mod_version_id, dp.dependency_id, dp.version, dp.compare AS "compare: _", dp.importance AS "importance: _" FROM dependencies dp
WHERE dp.dependent_id = $1"#,
ver.id
id
)
.fetch_all(&mut *pool)
.await
Expand Down
16 changes: 16 additions & 0 deletions src/types/models/incompatibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@ pub struct ResponseIncompatibility {
pub importance: IncompatibilityImportance,
}

impl FetchedIncompatibility {
pub fn to_response(&self) -> ResponseIncompatibility {
ResponseIncompatibility {
mod_id: self.incompatibility_id.clone(),
version: {
if self.version == "*" {
"*".to_string()
} else {
format!("{}{}", self.compare, self.version)
}
},
importance: self.importance,
}
}
}

impl Incompatibility {
pub async fn create_for_mod_version(
id: i32,
Expand Down
14 changes: 14 additions & 0 deletions src/types/models/mod_gd_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ pub enum GDVersionEnum {
GD2205,
}

impl FromStr for GDVersionEnum {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
match s {
"*" => Ok(GDVersionEnum::All),
"2.113" => Ok(GDVersionEnum::GD2113),
"2.200" => Ok(GDVersionEnum::GD2200),
"2.204" => Ok(GDVersionEnum::GD2204),
"2.205" => Ok(GDVersionEnum::GD2205),
_ => Err(()),
}
}
}

#[derive(sqlx::Type, Debug, Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash)]
#[sqlx(type_name = "gd_ver_platform", rename_all = "lowercase")]
#[serde(rename_all = "lowercase")]
Expand Down
101 changes: 69 additions & 32 deletions src/types/models/mod_version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,72 @@ impl ModVersion {
Ok(ret)
}

pub async fn get_latest_for_mod(
id: &str,
gd: Option<GDVersionEnum>,
platforms: Vec<VerPlatform>,
pool: &mut PgConnection,
) -> Result<ModVersion, ApiError> {
let mut query_builder: QueryBuilder<Postgres> = QueryBuilder::new(
r#"SELECT DISTINCT
mv.name, mv.id, mv.description, mv.version, mv.download_link, mv.hash, mv.geode, mv.download_count,
mv.early_load, mv.api, mv.mod_id FROM mod_versions mv
INNER JOIN mod_gd_versions mgv ON mgv.mod_id = mv.id
WHERE mv.version = (SELECT latest_version FROM mods WHERE id = "#,
);
query_builder.push_bind(id);
query_builder.push(") AND mv.validated = true");
if let Some(g) = gd {
query_builder.push(" AND mgv.gd = ");
query_builder.push_bind(g);
}
for (i, platform) in platforms.iter().enumerate() {
if i == 0 {
query_builder.push(" AND mgv.platform IN (");
}
query_builder.push_bind(*platform);
if i == platforms.len() - 1 {
query_builder.push(")");
} else {
query_builder.push(", ");
}
}
query_builder.push(" AND mv.mod_id = $1");
let records = match query_builder
.build_query_as::<ModVersionGetOne>()
.fetch_optional(&mut *pool)
.await
{
Ok(r) => r,
Err(e) => {
log::info!("{:?}", e);
return Err(ApiError::DbError);
}
};
let mut version = match records {
None => return Err(ApiError::NotFound("".to_string())),
Some(x) => x.into_mod_version(),
};

version.gd = ModGDVersion::get_for_mod_version(version.id, pool).await?;
version.dependencies = Some(
Dependency::get_for_mod_version(version.id, pool)
.await?
.into_iter()
.map(|x| x.to_response())
.collect(),
);
version.incompatibilities = Some(
Incompatibility::get_for_mod_version(version.id, pool)
.await?
.into_iter()
.map(|x| x.to_response())
.collect(),
);

Ok(version)
}

pub async fn get_download_url(
id: &str,
version: &str,
Expand Down Expand Up @@ -260,39 +326,10 @@ impl ModVersion {

let mut version = result.unwrap().into_mod_version();
version.gd = ModGDVersion::get_for_mod_version(version.id, pool).await?;
let deps = Dependency::get_for_mod_version(&version, pool).await?;
version.dependencies = Some(
deps.into_iter()
.map(|x| ResponseDependency {
mod_id: x.dependency_id.clone(),
version: {
if x.version == "*" {
"*".to_string()
} else {
format!("{}{}", x.compare, x.version.trim_start_matches('v'))
}
},
importance: x.importance,
})
.collect(),
);
let deps = Dependency::get_for_mod_version(version.id, pool).await?;
version.dependencies = Some(deps.into_iter().map(|x| x.to_response()).collect());
let incompat = Incompatibility::get_for_mod_version(version.id, pool).await?;
version.incompatibilities = Some(
incompat
.into_iter()
.map(|x| ResponseIncompatibility {
mod_id: x.incompatibility_id.clone(),
version: {
if x.version == "*" {
"*".to_string()
} else {
format!("{}{}", x.compare, x.version.trim_start_matches('v'))
}
},
importance: x.importance,
})
.collect(),
);
version.incompatibilities = Some(incompat.into_iter().map(|x| x.to_response()).collect());

Ok(version)
}
Expand Down

0 comments on commit 3d339ff

Please sign in to comment.