From da79386cc3006f8be3e7317a72d246863f1406c0 Mon Sep 17 00:00:00 2001 From: Aeledfyr <45501007+Aeledfyr@users.noreply.github.com> Date: Sat, 31 Oct 2020 23:06:47 -0500 Subject: [PATCH] Track and sort by release date of game_versions tags (#95) --- ...201029190804_add-game-version-datetime.sql | 3 + sqlx-data.json | 89 ++++++++++--------- src/database/models/categories.rs | 21 ++++- src/routes/tags.rs | 13 ++- src/scheduler.rs | 37 +++++++- 5 files changed, 108 insertions(+), 55 deletions(-) create mode 100644 migrations/20201029190804_add-game-version-datetime.sql diff --git a/migrations/20201029190804_add-game-version-datetime.sql b/migrations/20201029190804_add-game-version-datetime.sql new file mode 100644 index 00000000..d9f08943 --- /dev/null +++ b/migrations/20201029190804_add-game-version-datetime.sql @@ -0,0 +1,3 @@ + +ALTER TABLE game_versions +ADD COLUMN created timestamptz NOT NULL DEFAULT timezone('utc', now()); diff --git a/sqlx-data.json b/sqlx-data.json index 00a6ee0c..d208bdfb 100644 --- a/sqlx-data.json +++ b/sqlx-data.json @@ -479,27 +479,6 @@ "nullable": [] } }, - "3d18702f07161c0cdbc31d70b89ffeb3678617ccc44dfc6fb03dd63f47226c7b": { - "query": "\n INSERT INTO game_versions (version, type)\n VALUES ($1, $2)\n ON CONFLICT (version) DO UPDATE\n SET type = excluded.type\n RETURNING id\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int4" - } - ], - "parameters": { - "Left": [ - "Varchar", - "Varchar" - ] - }, - "nullable": [ - false - ] - } - }, "42e072309779598d0c213280dd8052d1b4889cb24ef5204ca13b74f693b94328": { "query": "\n SELECT user_id FROM team_members tm\n INNER JOIN mods ON mods.team_id = tm.team_id\n WHERE mods.id = $1\n ", "describe": { @@ -791,6 +770,28 @@ ] } }, + "72c75313688dfd88a659c5250c71b9899abd6186ab32a067a7d4b8a0846ebd18": { + "query": "\n INSERT INTO game_versions (version, type, created)\n VALUES ($1, COALESCE($2, 'other'), COALESCE($3, timezone('utc', now())))\n ON CONFLICT (version) DO UPDATE\n SET type = COALESCE($2, game_versions.type),\n created = COALESCE($3, game_versions.created)\n RETURNING id\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int4" + } + ], + "parameters": { + "Left": [ + "Varchar", + "Text", + "Timestamp" + ] + }, + "nullable": [ + false + ] + } + }, "72d6b5f2f11d88981db82c7247c9e7e5ebfd8d34985a1a8209d6628e66490f37": { "query": "\n SELECT id FROM categories\n WHERE category = $1\n ", "describe": { @@ -843,24 +844,6 @@ "nullable": [] } }, - "89fbff6249b248d3e150879aaea1662140bcb10d5104992c784285322c8b3b94": { - "query": "\n SELECT version FROM game_versions\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "version", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false - ] - } - }, "8f706d78ac4235ea04c59e2c220a4791e1d08fdf287b783b4aaef36fd2445467": { "query": "\n DELETE FROM loaders\n WHERE loader = $1\n ", "describe": { @@ -1193,6 +1176,26 @@ "nullable": [] } }, + "ba2d5d676aca425a61243a9d8d3b5745c5550aa934df087aac1c9c2b5e49a243": { + "query": "\n SELECT version FROM game_versions\n WHERE type = $1\n ORDER BY created DESC\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "version", + "type_info": "Varchar" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false + ] + } + }, "bec1612d4929d143bc5d6860a57cc036c5ab23e69d750ca5791c620297953c50": { "query": "\n SELECT team_id FROM mods WHERE id = $1\n ", "describe": { @@ -1716,8 +1719,8 @@ ] } }, - "ec4a3ef12a35bb78002fdafccbdb198b15f9a0fdb2b3e4108f9081b7e56e8769": { - "query": "\n SELECT version FROM game_versions\n WHERE type = $1\n ", + "ed4c0b620d01cdcdd0c2b3b5727ae3485d51114ca76e17331cec0d244d7f972d": { + "query": "\n SELECT version FROM game_versions\n ORDER BY created DESC\n ", "describe": { "columns": [ { @@ -1727,9 +1730,7 @@ } ], "parameters": { - "Left": [ - "Text" - ] + "Left": [] }, "nullable": [ false diff --git a/src/database/models/categories.rs b/src/database/models/categories.rs index ea8f9776..3c8529bc 100644 --- a/src/database/models/categories.rs +++ b/src/database/models/categories.rs @@ -272,6 +272,7 @@ impl<'a> LoaderBuilder<'a> { pub struct GameVersionBuilder<'a> { pub version: Option<&'a str>, pub version_type: Option<&'a str>, + pub date: Option<&'a chrono::DateTime>, } impl GameVersion { @@ -330,6 +331,7 @@ impl GameVersion { let result = sqlx::query!( " SELECT version FROM game_versions + ORDER BY created DESC " ) .fetch_many(exec) @@ -348,6 +350,7 @@ impl GameVersion { " SELECT version FROM game_versions WHERE type = $1 + ORDER BY created DESC ", version_type ) @@ -417,20 +420,32 @@ impl<'a> GameVersionBuilder<'a> { } } + pub fn created(self, created: &'a chrono::DateTime) -> GameVersionBuilder<'a> { + Self { + date: Some(created), + ..self + } + } + pub async fn insert<'b, E>(self, exec: E) -> Result where E: sqlx::Executor<'b, Database = sqlx::Postgres>, { + // This looks like a mess, but it *should* work + // This allows game versions to be partially updated without + // replacing the unspecified fields with defaults. let result = sqlx::query!( " - INSERT INTO game_versions (version, type) - VALUES ($1, $2) + INSERT INTO game_versions (version, type, created) + VALUES ($1, COALESCE($2, 'other'), COALESCE($3, timezone('utc', now()))) ON CONFLICT (version) DO UPDATE - SET type = excluded.type + SET type = COALESCE($2, game_versions.type), + created = COALESCE($3, game_versions.created) RETURNING id ", self.version, self.version_type, + self.date.map(chrono::DateTime::naive_utc), ) .fetch_one(exec) .await?; diff --git a/src/routes/tags.rs b/src/routes/tags.rs index a79c16f9..3b7ef8a7 100644 --- a/src/routes/tags.rs +++ b/src/routes/tags.rs @@ -170,6 +170,7 @@ pub async fn game_version_list( pub struct GameVersionData { #[serde(rename = "type")] type_: String, + date: Option>, } #[put("game_version/{name}")] @@ -194,11 +195,15 @@ pub async fn game_version_create( // The version type currently isn't limited, but it should be one of: // "release", "snapshot", "alpha", "beta", "other" - let _id = GameVersion::builder() + let mut builder = GameVersion::builder() .version(&name)? - .version_type(&version_data.type_)? - .insert(&**pool) - .await?; + .version_type(&version_data.type_)?; + + if let Some(date) = &version_data.date { + builder = builder.created(date); + } + + let _id = builder.insert(&**pool).await?; Ok(HttpResponse::Ok().body("")) } diff --git a/src/scheduler.rs b/src/scheduler.rs index 01999b5b..6f235f7d 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -82,6 +82,8 @@ struct VersionFormat<'a> { id: String, #[serde(rename = "type")] type_: std::borrow::Cow<'a, str>, + #[serde(rename = "releaseTime")] + release_time: chrono::DateTime, } async fn update_versions(pool: &sqlx::Pool) -> Result<(), VersionIndexingError> { @@ -92,15 +94,41 @@ async fn update_versions(pool: &sqlx::Pool) -> Result<(), Versio let mut skipped_versions_count = 0u32; + // A list of version names that contains spaces. + // Generated using the command + // ```sh + // curl https://launchermeta.mojang.com/mc/game/version_manifest.json \ + // | jq '[.versions[].id | select(contains(" "))]' + // ``` + const HALL_OF_SHAME: [(&str, &str); 12] = [ + ("1.14.2 Pre-Release 4", "1.14.2-pre4"), + ("1.14.2 Pre-Release 3", "1.14.2-pre3"), + ("1.14.2 Pre-Release 2", "1.14.2-pre2"), + ("1.14.2 Pre-Release 1", "1.14.2-pre1"), + ("1.14.1 Pre-Release 2", "1.14.1-pre2"), + ("1.14.1 Pre-Release 1", "1.14.1-pre1"), + ("1.14 Pre-Release 5", "1.14-pre5"), + ("1.14 Pre-Release 4", "1.14-pre4"), + ("1.14 Pre-Release 3", "1.14-pre3"), + ("1.14 Pre-Release 2", "1.14-pre2"), + ("1.14 Pre-Release 1", "1.14-pre1"), + ("3D Shareware v1.34", "3D-Shareware-v1.34"), + ]; + for version in input.versions.into_iter() { - let name = version.id; + let mut name = version.id; if !name .chars() .all(|c| c.is_ascii_alphanumeric() || "-_.".contains(c)) { - // We'll deal with these manually - skipped_versions_count += 1; - continue; + if let Some((_, alternate)) = HALL_OF_SHAME.iter().find(|(version, _)| name == *version) + { + name = String::from(*alternate); + } else { + // We'll deal with these manually + skipped_versions_count += 1; + continue; + } } let type_ = match &*version.type_ { @@ -114,6 +142,7 @@ async fn update_versions(pool: &sqlx::Pool) -> Result<(), Versio crate::database::models::categories::GameVersion::builder() .version(&name)? .version_type(type_)? + .created(&version.release_time) .insert(pool) .await?; }