Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
Cleans up and removes TODOs, adds tests (#844)
Browse files Browse the repository at this point in the history
* removes version ordering from v2; simplifies now-unecessary three-level faceting

* resolved some todos

* test for game version updating

* merge fixes; display_categories fix
  • Loading branch information
thesuzerain authored Jan 12, 2024
1 parent 76c885f commit ef31c0c
Show file tree
Hide file tree
Showing 24 changed files with 244 additions and 242 deletions.
25 changes: 4 additions & 21 deletions src/database/models/legacy_loader_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,16 @@ impl MinecraftGameVersion {
redis: &RedisPool,
) -> Result<Vec<MinecraftGameVersion>, DatabaseError>
where
E: sqlx::Executor<'a, Database = sqlx::Postgres> + Copy,
E: sqlx::Acquire<'a, Database = sqlx::Postgres>,
{
let game_version_enum = LoaderFieldEnum::get(Self::FIELD_NAME, exec, redis)
let mut exec = exec.acquire().await?;
let game_version_enum = LoaderFieldEnum::get(Self::FIELD_NAME, &mut *exec, redis)
.await?
.ok_or_else(|| {
DatabaseError::SchemaError("Could not find game version enum.".to_string())
})?;
let game_version_enum_values =
LoaderFieldEnumValue::list(game_version_enum.id, exec, redis).await?;
LoaderFieldEnumValue::list(game_version_enum.id, &mut *exec, redis).await?;

let game_versions = game_version_enum_values
.into_iter()
Expand All @@ -71,24 +72,6 @@ impl MinecraftGameVersion {
Ok(game_versions)
}

// TODO: remove this
pub async fn list_transaction(
transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
redis: &RedisPool,
) -> Result<Vec<MinecraftGameVersion>, DatabaseError> {
let game_version_enum = LoaderFieldEnum::get(Self::FIELD_NAME, &mut **transaction, redis)
.await?
.ok_or_else(|| {
DatabaseError::SchemaError("Could not find game version enum.".to_string())
})?;
let game_version_enum_values =
LoaderFieldEnumValue::list(game_version_enum.id, &mut **transaction, redis).await?;
Ok(game_version_enum_values
.into_iter()
.map(MinecraftGameVersion::from_enum_value)
.collect())
}

// Tries to create a MinecraftGameVersion from a VersionField
// Clones on success
pub fn try_from_version_field(
Expand Down
6 changes: 0 additions & 6 deletions src/models/v2/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ impl LegacyProject {
// Requires any queried versions to be passed in, to get access to certain version fields contained within.
// - This can be any version, because the fields are ones that used to be on the project itself.
// - Its conceivable that certain V3 projects that have many different ones may not have the same fields on all of them.
// TODO: Should this return an error instead for v2 users?
// It's safe to use a db version_item for this as the only info is side types, game versions, and loader fields (for loaders), which used to be public on project anyway.
pub fn from(data: Project, versions_item: Option<version_item::QueryVersion>) -> Self {
let mut client_side = LegacySideType::Unknown;
Expand Down Expand Up @@ -289,10 +288,6 @@ pub struct LegacyVersion {
/// A list of loaders this project supports (has a newtype struct)
pub loaders: Vec<Loader>,

// TODO: should we remove this? as this is a v3 field and tests for it should be isolated to v3
// it allows us to keep tests that use this struct in common
pub ordering: Option<i32>,

pub id: VersionId,
pub project_id: ProjectId,
pub author_id: UserId,
Expand Down Expand Up @@ -354,7 +349,6 @@ impl From<Version> for LegacyVersion {
files: data.files,
dependencies: data.dependencies,
game_versions,
ordering: data.ordering,
loaders,
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/models/v2/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub struct LegacyResultSearchProject {
impl LegacyResultSearchProject {
pub fn from(result_search_project: ResultSearchProject) -> Self {
let mut categories = result_search_project.categories;
categories.extend(result_search_project.loaders);
categories.extend(result_search_project.loaders.clone());
if categories.contains(&"mrpack".to_string()) {
if let Some(mrpack_loaders) = result_search_project
.project_loader_fields
Expand All @@ -58,6 +58,7 @@ impl LegacyResultSearchProject {
}
}
let mut display_categories = result_search_project.display_categories;
display_categories.extend(result_search_project.loaders);
if display_categories.contains(&"mrpack".to_string()) {
if let Some(mrpack_loaders) = result_search_project
.project_loader_fields
Expand Down
63 changes: 19 additions & 44 deletions src/routes/v2/projects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use crate::routes::v3::projects::ProjectIds;
use crate::routes::{v2_reroute, v3, ApiError};
use crate::search::{search_for_project, SearchConfig, SearchError};
use actix_web::{delete, get, patch, post, web, HttpRequest, HttpResponse};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use std::collections::HashMap;
Expand Down Expand Up @@ -53,63 +52,39 @@ pub async fn project_search(
web::Query(info): web::Query<SearchRequest>,
config: web::Data<SearchConfig>,
) -> Result<HttpResponse, SearchError> {
// TODO: make this nicer
// Search now uses loader_fields instead of explicit 'client_side' and 'server_side' fields
// While the backend for this has changed, it doesnt affect much
// in the API calls except that 'versions:x' is now 'game_versions:x'
let facets: Option<Vec<Vec<Vec<String>>>> = if let Some(facets) = info.facets {
let facets = serde_json::from_str::<Vec<Vec<serde_json::Value>>>(&facets)?;
// Search can now *optionally* have a third inner array: So Vec(AND)<Vec(OR)<Vec(AND)< _ >>>
// For every inner facet, we will check if it can be deserialized into a Vec<&str>, and do so.
// If not, we will assume it is a single facet and wrap it in a Vec.
let facets: Vec<Vec<Vec<String>>> = facets
.into_iter()
.map(|facets| {
facets
.into_iter()
.map(|facet| {
if facet.is_array() {
serde_json::from_value::<Vec<String>>(facet).unwrap_or_default()
} else {
vec![serde_json::from_value::<String>(facet).unwrap_or_default()]
}
})
.collect_vec()
})
.collect_vec();
let facets: Option<Vec<Vec<String>>> = if let Some(facets) = info.facets {
let facets = serde_json::from_str::<Vec<Vec<String>>>(&facets)?;

// These loaders specifically used to be combined with 'mod' to be a plugin, but now
// they are their own loader type. We will convert 'mod' to 'mod' OR 'plugin'
// as it essentially was before.
let facets = v2_reroute::convert_plugin_loaders_v3(facets);
let facets = v2_reroute::convert_plugin_loader_facets_v3(facets);

Some(
facets
.into_iter()
.map(|facet| {
facet
.into_iter()
.map(|facets| {
facets
.into_iter()
.map(|facet| {
if let Some((key, operator, val)) = parse_facet(&facet) {
format!(
"{}{}{}",
match key.as_str() {
"versions" => "game_versions",
"project_type" => "project_types",
"title" => "name",
x => x,
},
operator,
val
)
} else {
facet.to_string()
}
})
.collect::<Vec<_>>()
.map(|facet| {
if let Some((key, operator, val)) = parse_facet(&facet) {
format!(
"{}{}{}",
match key.as_str() {
"versions" => "game_versions",
"project_type" => "project_types",
"title" => "name",
x => x,
},
operator,
val
)
} else {
facet.to_string()
}
})
.collect::<Vec<_>>()
})
Expand Down
3 changes: 1 addition & 2 deletions src/routes/v2/versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,6 @@ pub struct EditVersion {
pub downloads: Option<u32>,
pub status: Option<VersionStatus>,
pub file_types: Option<Vec<EditVersionFileType>>,
pub ordering: Option<Option<i32>>, //TODO: How do you actually pass this in json?
}

#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -291,7 +290,7 @@ pub async fn version_edit(
})
.collect::<Vec<_>>()
}),
ordering: new_version.ordering,
ordering: None,
fields,
};

Expand Down
12 changes: 6 additions & 6 deletions src/routes/v2_reroute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,18 +178,18 @@ pub fn convert_side_types_v3(
fields
}

// Converts plugin loaders from v2 to v3
// Converts plugin loaders from v2 to v3, for search facets
// Within every 1st and 2nd level (the ones allowed in v2), we convert every instance of:
// "project_type:mod" to "project_type:plugin" OR "project_type:mod"
pub fn convert_plugin_loaders_v3(facets: Vec<Vec<Vec<String>>>) -> Vec<Vec<Vec<String>>> {
pub fn convert_plugin_loader_facets_v3(facets: Vec<Vec<String>>) -> Vec<Vec<String>> {
facets
.into_iter()
.map(|inner_facets| {
if inner_facets == [["project_type:mod"]] {
if inner_facets == ["project_type:mod"] {
vec![
vec!["project_type:plugin".to_string()],
vec!["project_type:datapack".to_string()],
vec!["project_type:mod".to_string()],
"project_type:plugin".to_string(),
"project_type:datapack".to_string(),
"project_type:mod".to_string(),
]
} else {
inner_facets
Expand Down
2 changes: 0 additions & 2 deletions src/routes/v3/version_creation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,8 +583,6 @@ async fn upload_file_to_version_inner(
};

let all_loaders = models::loader_fields::Loader::list(&mut **transaction, &redis).await?;

// TODO: this coded is reused a lot, it should be refactored into a function
let selected_loaders = version
.loaders
.iter()
Expand Down
2 changes: 0 additions & 2 deletions src/routes/v3/version_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ pub async fn get_update_from_hash(
.map(|x| x.1)
.ok();
let hash = info.into_inner().0.to_lowercase();

if let Some(file) = database::models::Version::get_file_from_hash(
hash_query
.algorithm
Expand Down Expand Up @@ -185,7 +184,6 @@ pub async fn get_update_from_hash(
}
}
}

Err(ApiError::NotFound)
}

Expand Down
10 changes: 6 additions & 4 deletions src/routes/v3/versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,12 @@ pub struct EditVersion {
pub downloads: Option<u32>,
pub status: Option<VersionStatus>,
pub file_types: Option<Vec<EditVersionFileType>>,

pub ordering: Option<Option<i32>>, //TODO: How do you actually pass this in json?
#[serde(
default,
skip_serializing_if = "Option::is_none",
with = "::serde_with::rust::double_option"
)]
pub ordering: Option<Option<i32>>,

// Flattened loader fields
// All other fields are loader-specific VersionFields
Expand All @@ -220,8 +224,6 @@ pub struct EditVersionFileType {
pub file_type: Option<FileType>,
}

// TODO: Avoid this 'helper' pattern here and similar fnunctoins- a macro might be the best bet here to ensure it's callable from both v2 and v3
// (web::Path can't be recreated naturally)
pub async fn version_edit(
req: HttpRequest,
info: web::Path<(VersionId,)>,
Expand Down
8 changes: 4 additions & 4 deletions src/search/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ pub struct UploadSearchProject {
pub loaders: Vec<String>, // Search uses loaders as categories- this is purely for the Project model.
pub links: Vec<LinkUrl>,
pub gallery_items: Vec<GalleryItem>, // Gallery *only* urls are stored in gallery, but the gallery items are stored here- required for the Project model.
pub games: Vec<String>, // Todo: in future, could be a searchable field.
pub organization_id: Option<String>, // Todo: in future, could be a searchable field.
pub games: Vec<String>,
pub organization_id: Option<String>,
pub project_loader_fields: HashMap<String, Vec<serde_json::Value>>, // Aggregation of loader_fields from all versions of the project, allowing for reconstruction of the Project model.

#[serde(flatten)]
Expand Down Expand Up @@ -183,8 +183,8 @@ pub struct ResultSearchProject {
pub loaders: Vec<String>, // Search uses loaders as categories- this is purely for the Project model.
pub links: Vec<LinkUrl>,
pub gallery_items: Vec<GalleryItem>, // Gallery *only* urls are stored in gallery, but the gallery items are stored here- required for the Project model.
pub games: Vec<String>, // Todo: in future, could be a searchable field.
pub organization_id: Option<String>, // Todo: in future, could be a searchable field.
pub games: Vec<String>,
pub organization_id: Option<String>,
pub project_loader_fields: HashMap<String, Vec<serde_json::Value>>, // Aggregation of loader_fields from all versions of the project, allowing for reconstruction of the Project model.

#[serde(flatten)]
Expand Down
2 changes: 1 addition & 1 deletion src/validate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ pub async fn validate_file(
.find_map(|v| MinecraftGameVersion::try_from_version_field(&v).ok())
.unwrap_or_default();
let all_game_versions =
MinecraftGameVersion::list_transaction(&mut *transaction, redis).await?;
MinecraftGameVersion::list(None, None, &mut *transaction, redis).await?;
validate_minecraft_file(
data,
file_extension,
Expand Down
3 changes: 0 additions & 3 deletions tests/common/api_common/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,6 @@ pub struct CommonVersion {
pub requested_status: Option<VersionStatus>,
pub files: Vec<VersionFile>,
pub dependencies: Vec<Dependency>,

// TODO: should ordering be in v2?
pub ordering: Option<i32>,
}

#[derive(Deserialize)]
Expand Down
2 changes: 0 additions & 2 deletions tests/common/api_v2/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ use crate::{

use super::ApiV2;

// TODO: Tag gets do not include PAT, as they are public.

impl ApiV2 {
async fn get_side_types(&self) -> ServiceResponse {
let req = TestRequest::get()
Expand Down
27 changes: 15 additions & 12 deletions tests/common/api_v2/team.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ impl ApiTeams for ApiV2 {
) -> Vec<CommonTeamMember> {
let resp = self.get_team_members(id_or_title, pat).await;
assert_status!(&resp, StatusCode::OK);
// TODO: Note, this does NOT deserialize to any other struct first, as currently TeamMember is the same in v2 and v3.
// CommonTeamMember = TeamMember (v3)
// This may yet change, so we should keep common struct.
test::read_body_json(resp).await
// First, deserialize to the non-common format (to test the response is valid for this api version)
let v: Vec<LegacyTeamMember> = test::read_body_json(resp).await;
// Then, deserialize to the common format
let value = serde_json::to_value(v).unwrap();
serde_json::from_value(value).unwrap()
}

async fn get_teams_members(
Expand Down Expand Up @@ -103,10 +104,11 @@ impl ApiTeams for ApiV2 {
) -> Vec<CommonTeamMember> {
let resp = self.get_project_members(id_or_title, pat).await;
assert_status!(&resp, StatusCode::OK);
// TODO: Note, this does NOT deserialize to any other struct first, as currently TeamMember is the same in v2 and v3.
// CommonTeamMember = TeamMember (v3)
// This may yet change, so we should keep common struct.
test::read_body_json(resp).await
// First, deserialize to the non-common format (to test the response is valid for this api version)
let v: Vec<LegacyTeamMember> = test::read_body_json(resp).await;
// Then, deserialize to the common format
let value = serde_json::to_value(v).unwrap();
serde_json::from_value(value).unwrap()
}

async fn get_organization_members(
Expand All @@ -128,10 +130,11 @@ impl ApiTeams for ApiV2 {
) -> Vec<CommonTeamMember> {
let resp = self.get_organization_members(id_or_title, pat).await;
assert_status!(&resp, StatusCode::OK);
// TODO: Note, this does NOT deserialize to any other struct first, as currently TeamMember is the same in v2 and v3.
// CommonTeamMember = TeamMember (v3)
// This may yet change, so we should keep common struct.
test::read_body_json(resp).await
// First, deserialize to the non-common format (to test the response is valid for this api version)
let v: Vec<LegacyTeamMember> = test::read_body_json(resp).await;
// Then, deserialize to the common format
let value = serde_json::to_value(v).unwrap();
serde_json::from_value(value).unwrap()
}

async fn join_team(&self, team_id: &str, pat: Option<&str>) -> ServiceResponse {
Expand Down
1 change: 0 additions & 1 deletion tests/common/api_v3/tags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ impl ApiV3 {
test::read_body_json(resp).await
}

// TODO: fold this into v3 API of other v3 testing PR
async fn get_games(&self) -> ServiceResponse {
let req = TestRequest::get()
.uri("/v3/games")
Expand Down
Loading

0 comments on commit ef31c0c

Please sign in to comment.