diff --git a/.sqlx/query-a796587302ae98d1af5f41696e401174fbeb9e8399bdcc78ffe8f80181b217a4.json b/.sqlx/query-a796587302ae98d1af5f41696e401174fbeb9e8399bdcc78ffe8f80181b217a4.json deleted file mode 100644 index 14c648d9..00000000 --- a/.sqlx/query-a796587302ae98d1af5f41696e401174fbeb9e8399bdcc78ffe8f80181b217a4.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n SELECT l.id id, l.loader loader, l.icon icon,\n ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,\n ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games\n FROM loaders l \n LEFT OUTER JOIN loaders_project_types lpt ON joining_loader_id = l.id\n LEFT OUTER JOIN project_types pt ON lpt.joining_project_type_id = pt.id\n LEFT OUTER JOIN loaders_project_types_games lptg ON lptg.loader_id = lpt.joining_loader_id AND lptg.project_type_id = lpt.joining_project_type_id\n LEFT OUTER JOIN games g ON lptg.game_id = g.id\n GROUP BY l.id;\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int4" - }, - { - "ordinal": 1, - "name": "loader", - "type_info": "Varchar" - }, - { - "ordinal": 2, - "name": "icon", - "type_info": "Varchar" - }, - { - "ordinal": 3, - "name": "project_types", - "type_info": "VarcharArray" - }, - { - "ordinal": 4, - "name": "games", - "type_info": "VarcharArray" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - null, - null - ] - }, - "hash": "a796587302ae98d1af5f41696e401174fbeb9e8399bdcc78ffe8f80181b217a4" -} diff --git a/.sqlx/query-cdb2f18f826097f0f17a1f7295d7c45eb1987b63c1a21666c6ca60c52217ba4d.json b/.sqlx/query-cdb2f18f826097f0f17a1f7295d7c45eb1987b63c1a21666c6ca60c52217ba4d.json new file mode 100644 index 00000000..e7f4eb17 --- /dev/null +++ b/.sqlx/query-cdb2f18f826097f0f17a1f7295d7c45eb1987b63c1a21666c6ca60c52217ba4d.json @@ -0,0 +1,50 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT l.id id, l.loader loader, l.icon icon, l.metadata metadata,\n ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types,\n ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games\n FROM loaders l \n LEFT OUTER JOIN loaders_project_types lpt ON joining_loader_id = l.id\n LEFT OUTER JOIN project_types pt ON lpt.joining_project_type_id = pt.id\n LEFT OUTER JOIN loaders_project_types_games lptg ON lptg.loader_id = lpt.joining_loader_id AND lptg.project_type_id = lpt.joining_project_type_id\n LEFT OUTER JOIN games g ON lptg.game_id = g.id\n GROUP BY l.id;\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + }, + { + "ordinal": 1, + "name": "loader", + "type_info": "Varchar" + }, + { + "ordinal": 2, + "name": "icon", + "type_info": "Varchar" + }, + { + "ordinal": 3, + "name": "metadata", + "type_info": "Jsonb" + }, + { + "ordinal": 4, + "name": "project_types", + "type_info": "VarcharArray" + }, + { + "ordinal": 5, + "name": "games", + "type_info": "VarcharArray" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + null, + null + ] + }, + "hash": "cdb2f18f826097f0f17a1f7295d7c45eb1987b63c1a21666c6ca60c52217ba4d" +} diff --git a/migrations/20231115105022_plugins_datapacks_v3.sql b/migrations/20231115105022_plugins_datapacks_v3.sql new file mode 100644 index 00000000..27dadc46 --- /dev/null +++ b/migrations/20231115105022_plugins_datapacks_v3.sql @@ -0,0 +1,46 @@ +ALTER TABLE loaders ADD COLUMN metadata jsonb NOT NULL DEFAULT '{}'::jsonb; + +-- Set 'platform' to 'true' for all plugin loaders +-- From knossos v2 + -- pluginLoaders: ['bukkit', 'spigot', 'paper', 'purpur', 'sponge', 'folia'], + -- pluginPlatformLoaders: ['bungeecord', 'waterfall', 'velocity'], + -- allPluginLoaders: [ + -- 'bukkit', + -- 'spigot', + -- 'paper', + -- 'purpur', + -- 'sponge', + -- 'bungeecord', + -- 'waterfall', + -- 'velocity', + -- 'folia', + -- ], + -- dataPackLoaders: ['datapack'], + -- modLoaders: ['forge', 'fabric', 'quilt', 'liteloader', 'modloader', 'rift', 'neoforge'], +UPDATE loaders SET metadata = jsonb_set(metadata, '{platform}', 'false'::jsonb) WHERE loader in ('bukkit', 'spigot', 'paper', 'purpur', 'sponge', 'folia'); +UPDATE loaders SET metadata = jsonb_set(metadata, '{platform}', 'true'::jsonb) WHERE loader in ('bungeecord', 'waterfall', 'velocity'); + +INSERT INTO project_types (name) VALUES ('plugin'); +INSERT INTO project_types (name) VALUES ('datapack'); + +INSERT INTO loaders_project_types (joining_loader_id, joining_project_type_id) +SELECT l.id, pt.id +FROM loaders l +CROSS JOIN project_types pt +WHERE l.loader in ('datapack') +AND pt.name = 'datapack'; + +INSERT INTO loaders_project_types (joining_loader_id, joining_project_type_id) +SELECT l.id, pt.id +FROM loaders l +CROSS JOIN project_types pt +WHERE l.loader in ('bukkit', 'spigot', 'paper', 'purpur', 'sponge', 'bungeecord', 'waterfall', 'velocity', 'folia') +AND pt.name = 'plugin'; + +INSERT INTO loaders_project_types_games (loader_id, project_type_id, game_id) +SELECT joining_loader_id, joining_project_type_id, g.id +FROM loaders_project_types lpt +INNER JOIN project_types pt ON pt.id = lpt.joining_project_type_id +CROSS JOIN games g +WHERE g.name = 'minecraft' +AND pt.name in ('plugin', 'datapack'); diff --git a/src/database/models/loader_fields.rs b/src/database/models/loader_fields.rs index 63e1d6fd..87edf8cc 100644 --- a/src/database/models/loader_fields.rs +++ b/src/database/models/loader_fields.rs @@ -85,6 +85,7 @@ pub struct Loader { pub icon: String, pub supported_project_types: Vec, pub supported_games: Vec, // slugs + pub metadata: serde_json::Value, } impl Loader { @@ -136,7 +137,7 @@ impl Loader { let result = sqlx::query!( " - SELECT l.id id, l.loader loader, l.icon icon, + SELECT l.id id, l.loader loader, l.icon icon, l.metadata metadata, ARRAY_AGG(DISTINCT pt.name) filter (where pt.name is not null) project_types, ARRAY_AGG(DISTINCT g.slug) filter (where g.slug is not null) games FROM loaders l @@ -161,7 +162,9 @@ impl Loader { .collect(), supported_games: x .games - .unwrap_or_default() + .unwrap_or_default(), + metadata: x.metadata + })) }) .try_collect::>() diff --git a/src/routes/v3/tags.rs b/src/routes/v3/tags.rs index 8900f9c4..8fca6b51 100644 --- a/src/routes/v3/tags.rs +++ b/src/routes/v3/tags.rs @@ -84,6 +84,7 @@ pub struct LoaderData { pub name: String, pub supported_project_types: Vec, pub supported_games: Vec, + pub metadata: Value, } pub async fn loader_list( @@ -98,6 +99,7 @@ pub async fn loader_list( name: x.loader, supported_project_types: x.supported_project_types, supported_games: x.supported_games, + metadata: x.metadata, }) .collect::>(); diff --git a/tests/common/api_v3/tags.rs b/tests/common/api_v3/tags.rs index da297a77..a7f75061 100644 --- a/tests/common/api_v3/tags.rs +++ b/tests/common/api_v3/tags.rs @@ -3,8 +3,10 @@ use actix_web::{ test::{self, TestRequest}, }; use async_trait::async_trait; -use labrinth::database::models::loader_fields::LoaderFieldEnumValue; use labrinth::routes::v3::tags::GameData; +use labrinth::{ + database::models::loader_fields::LoaderFieldEnumValue, routes::v3::tags::LoaderData, +}; use crate::common::{ api_common::{ @@ -48,6 +50,12 @@ impl ApiTags for ApiV3 { } impl ApiV3 { + pub async fn get_loaders_deserialized(&self) -> Vec { + let resp = self.get_loaders().await; + assert_eq!(resp.status(), 200); + test::read_body_json(resp).await + } + pub async fn get_loader_field_variants(&self, loader_field: &str) -> ServiceResponse { let req = TestRequest::get() .uri(&format!("/v3/loader_field?loader_field={}", loader_field)) diff --git a/tests/files/dummy_data.sql b/tests/files/dummy_data.sql index 8f2e4707..e332dbfc 100644 --- a/tests/files/dummy_data.sql +++ b/tests/files/dummy_data.sql @@ -25,6 +25,9 @@ INSERT INTO loaders_project_types (joining_loader_id, joining_project_type_id) V INSERT INTO loaders (id, loader) VALUES (6, 'forge'); INSERT INTO loaders_project_types (joining_loader_id, joining_project_type_id) VALUES (6,1); +INSERT INTO loaders (id, loader, metadata) VALUES (7, 'bukkit', '{"platform":false}'::jsonb); +INSERT INTO loaders (id, loader, metadata) VALUES (8, 'waterfall', '{"platform":true}'::jsonb); + -- Adds dummies to mrpack_loaders INSERT INTO loader_field_enum_values (enum_id, value) SELECT id, 'fabric' FROM loader_field_enums WHERE enum_name = 'mrpack_loaders'; INSERT INTO loader_field_enum_values (enum_id, value) SELECT id, 'forge' FROM loader_field_enums WHERE enum_name = 'mrpack_loaders'; diff --git a/tests/tags.rs b/tests/tags.rs index 00659443..21d56b9c 100644 --- a/tests/tags.rs +++ b/tests/tags.rs @@ -1,6 +1,9 @@ -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; -use common::environment::with_test_environment_all; +use common::{ + api_v3::ApiV3, + environment::{with_test_environment, with_test_environment_all, TestEnvironment}, +}; use crate::common::api_common::ApiTags; @@ -10,18 +13,8 @@ mod common; async fn get_tags() { with_test_environment_all(None, |test_env| async move { let api = &test_env.api; - let loaders = api.get_loaders_deserialized_common().await; let categories = api.get_categories_deserialized_common().await; - let loader_names = loaders.into_iter().map(|x| x.name).collect::>(); - assert_eq!( - loader_names, - ["fabric", "forge", "mrpack"] - .iter() - .map(|s| s.to_string()) - .collect() - ); - let category_names = categories .into_iter() .map(|x| x.name) @@ -44,3 +37,28 @@ async fn get_tags() { }) .await; } + +#[actix_rt::test] +async fn get_tags_v3() { + with_test_environment(None, |test_env: TestEnvironment| async move { + let api = &test_env.api; + let loaders = api.get_loaders_deserialized().await; + + let loader_metadata = loaders + .into_iter() + .map(|x| (x.name, x.metadata.get("platform").and_then(|x| x.as_bool()))) + .collect::>(); + let loader_names = loader_metadata.keys().cloned().collect::>(); + assert_eq!( + loader_names, + ["fabric", "forge", "mrpack", "bukkit", "waterfall"] + .iter() + .map(|s| s.to_string()) + .collect() + ); + assert_eq!(loader_metadata["fabric"], None); + assert_eq!(loader_metadata["bukkit"], Some(false)); + assert_eq!(loader_metadata["waterfall"], Some(true)); + }) + .await; +} diff --git a/tests/v2/tags.rs b/tests/v2/tags.rs index 80c1f009..cb072ba0 100644 --- a/tests/v2/tags.rs +++ b/tests/v2/tags.rs @@ -1,3 +1,5 @@ +use itertools::Itertools; + use std::collections::HashSet; use crate::common::{ @@ -12,33 +14,38 @@ async fn get_tags() { let game_versions = api.get_game_versions_deserialized().await; let loaders = api.get_loaders_deserialized().await; let side_types = api.get_side_types_deserialized().await; - let categories = api.get_categories_deserialized().await; - // These tests match dummy data and will need to be updated if the dummy data changes; + // These tests match dummy data and will need to be updated if the dummy data changes + // Versions should be ordered by: + // - ordering + // - ordering ties settled by date added to database + // - We also expect presentation of NEWEST to OLDEST + // - All null orderings are treated as older than any non-null ordering + // (for this test, the 1.20.1, etc, versions are all null ordering) let game_version_versions = game_versions .into_iter() .map(|x| x.version) - .collect::>(); + .collect::>(); assert_eq!( game_version_versions, [ - "1.20.1", - "1.20.2", - "1.20.3", - "1.20.4", - "1.20.5", "Ordering_Negative1", - "Ordering_Positive100" + "Ordering_Positive100", + "1.20.5", + "1.20.4", + "1.20.3", + "1.20.2", + "1.20.1" ] .iter() .map(|s| s.to_string()) - .collect() + .collect_vec() ); let loader_names = loaders.into_iter().map(|x| x.name).collect::>(); assert_eq!( loader_names, - ["fabric", "forge", "mrpack"] + ["fabric", "forge", "mrpack", "bukkit", "waterfall"] .iter() .map(|s| s.to_string()) .collect() @@ -52,26 +59,6 @@ async fn get_tags() { .map(|s| s.to_string()) .collect() ); - - let category_names = categories - .into_iter() - .map(|x| x.name) - .collect::>(); - assert_eq!( - category_names, - [ - "combat", - "economy", - "food", - "optimization", - "decoration", - "mobs", - "magic" - ] - .iter() - .map(|s| s.to_string()) - .collect() - ); }) .await; }