From 6294b2731786d9110cd998d6ce503f11e0cfd167 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 13 Feb 2020 21:03:04 -0500 Subject: fix update-check error for SMAPI on Android with four-part versions --- src/SMAPI.Web/Controllers/ModsApiController.cs | 44 ++++++++++++++++++-------- 1 file changed, 30 insertions(+), 14 deletions(-) (limited to 'src/SMAPI.Web/Controllers') diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index f194b4d0..8e65b6b9 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -133,6 +133,7 @@ namespace StardewModdingAPI.Web.Controllers ModDataRecord record = this.ModDatabase.Get(search.ID); WikiModEntry wikiEntry = wikiData.FirstOrDefault(entry => entry.ID.Contains(search.ID.Trim(), StringComparer.InvariantCultureIgnoreCase)); UpdateKey[] updateKeys = this.GetUpdateKeys(search.UpdateKeys, record, wikiEntry).ToArray(); + bool isSmapi = search.ID == "Pathoschild.SMAPI"; // get latest versions ModEntryModel result = new ModEntryModel { ID = search.ID }; @@ -151,7 +152,7 @@ namespace StardewModdingAPI.Web.Controllers } // fetch data - ModInfoModel data = await this.GetInfoForUpdateKeyAsync(updateKey); + ModInfoModel data = await this.GetInfoForUpdateKeyAsync(updateKey, allowNonStandardVersions: isSmapi); if (data.Error != null) { errors.Add(data.Error); @@ -161,7 +162,7 @@ namespace StardewModdingAPI.Web.Controllers // handle main version if (data.Version != null) { - ISemanticVersion version = this.GetMappedVersion(data.Version, wikiEntry?.MapRemoteVersions); + ISemanticVersion version = this.GetMappedVersion(data.Version, wikiEntry?.MapRemoteVersions, allowNonStandard: isSmapi); if (version == null) { errors.Add($"The update key '{updateKey}' matches a mod with invalid semantic version '{data.Version}'."); @@ -175,7 +176,7 @@ namespace StardewModdingAPI.Web.Controllers // handle optional version if (data.PreviewVersion != null) { - ISemanticVersion version = this.GetMappedVersion(data.PreviewVersion, wikiEntry?.MapRemoteVersions); + ISemanticVersion version = this.GetMappedVersion(data.PreviewVersion, wikiEntry?.MapRemoteVersions, allowNonStandard: isSmapi); if (version == null) { errors.Add($"The update key '{updateKey}' matches a mod with invalid optional semantic version '{data.PreviewVersion}'."); @@ -215,7 +216,7 @@ namespace StardewModdingAPI.Web.Controllers } // special cases - if (result.ID == "Pathoschild.SMAPI") + if (isSmapi) { if (main != null) main.Url = "https://smapi.io/"; @@ -224,7 +225,7 @@ namespace StardewModdingAPI.Web.Controllers } // get recommended update (if any) - ISemanticVersion installedVersion = this.GetMappedVersion(search.InstalledVersion?.ToString(), wikiEntry?.MapLocalVersions); + ISemanticVersion installedVersion = this.GetMappedVersion(search.InstalledVersion?.ToString(), wikiEntry?.MapLocalVersions, allowNonStandard: isSmapi); if (apiVersion != null && installedVersion != null) { // get newer versions @@ -283,7 +284,8 @@ namespace StardewModdingAPI.Web.Controllers /// Get the mod info for an update key. /// The namespaced update key. - private async Task GetInfoForUpdateKeyAsync(UpdateKey updateKey) + /// Whether to allow non-standard versions. + private async Task GetInfoForUpdateKeyAsync(UpdateKey updateKey, bool allowNonStandardVersions) { // get mod if (!this.ModCache.TryGetMod(updateKey.Repository, updateKey.ID, out CachedMod mod) || this.ModCache.IsStale(mod.LastUpdated, mod.FetchStatus == RemoteModStatus.TemporaryError ? this.ErrorCacheMinutes : this.SuccessCacheMinutes)) @@ -298,7 +300,7 @@ namespace StardewModdingAPI.Web.Controllers { if (result.Version == null) result.SetError(RemoteModStatus.InvalidData, $"The update key '{updateKey}' matches a mod with no version number."); - else if (!SemanticVersion.TryParse(result.Version, out _)) + else if (!this.TryParseVersion(result.Version, allowNonStandardVersions, out _)) result.SetError(RemoteModStatus.InvalidData, $"The update key '{updateKey}' matches a mod with invalid semantic version '{result.Version}'."); } @@ -357,15 +359,16 @@ namespace StardewModdingAPI.Web.Controllers /// Get a semantic local version for update checks. /// The version to parse. /// A map of version replacements. - private ISemanticVersion GetMappedVersion(string version, IDictionary map) + /// Whether to allow non-standard versions. + private ISemanticVersion GetMappedVersion(string version, IDictionary map, bool allowNonStandard) { // try mapped version - string rawNewVersion = this.GetRawMappedVersion(version, map); - if (SemanticVersion.TryParse(rawNewVersion, out ISemanticVersion parsedNew)) + string rawNewVersion = this.GetRawMappedVersion(version, map, allowNonStandard); + if (this.TryParseVersion(rawNewVersion, allowNonStandard, out ISemanticVersion parsedNew)) return parsedNew; // return original version - return SemanticVersion.TryParse(version, out ISemanticVersion parsedOld) + return this.TryParseVersion(version, allowNonStandard, out ISemanticVersion parsedOld) ? parsedOld : null; } @@ -373,7 +376,8 @@ namespace StardewModdingAPI.Web.Controllers /// Get a semantic local version for update checks. /// The version to map. /// A map of version replacements. - private string GetRawMappedVersion(string version, IDictionary map) + /// Whether to allow non-standard versions. + private string GetRawMappedVersion(string version, IDictionary map, bool allowNonStandard) { if (version == null || map == null || !map.Any()) return version; @@ -383,19 +387,31 @@ namespace StardewModdingAPI.Web.Controllers return map[version]; // match parsed version - if (SemanticVersion.TryParse(version, out ISemanticVersion parsed)) + if (this.TryParseVersion(version, allowNonStandard, out ISemanticVersion parsed)) { if (map.ContainsKey(parsed.ToString())) return map[parsed.ToString()]; foreach (var pair in map) { - if (SemanticVersion.TryParse(pair.Key, out ISemanticVersion target) && parsed.Equals(target) && SemanticVersion.TryParse(pair.Value, out ISemanticVersion newVersion)) + if (this.TryParseVersion(pair.Key, allowNonStandard, out ISemanticVersion target) && parsed.Equals(target) && this.TryParseVersion(pair.Value, allowNonStandard, out ISemanticVersion newVersion)) return newVersion.ToString(); } } return version; } + + /// Try to parse a version string. + /// The version string. + /// Whether to allow non-standard versions. + /// The parsed representation. + /// Returns whether parsing the version succeeded. + public bool TryParseVersion(string version, bool allowNonStandard, out ISemanticVersion parsed) + { + return allowNonStandard + ? SemanticVersion.TryParseNonStandard(version, out parsed) + : SemanticVersion.TryParse(version, out parsed); + } } } -- cgit From da49c7c13bbb0e47aa07f9c793640559c123ee3c Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Mon, 17 Feb 2020 23:48:57 -0500 Subject: move hardcoded SMAPI update check logic into config --- src/SMAPI.Web/Controllers/ModsApiController.cs | 34 ++++++++++++-------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'src/SMAPI.Web/Controllers') diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index 8e65b6b9..8370fe09 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -41,11 +41,8 @@ namespace StardewModdingAPI.Web.Controllers /// The cache in which to store mod data. private readonly IModCacheRepository ModCache; - /// The number of minutes successful update checks should be cached before refetching them. - private readonly int SuccessCacheMinutes; - - /// The number of minutes failed update checks should be cached before refetching them. - private readonly int ErrorCacheMinutes; + /// The config settings for mod update checks. + private readonly IOptions Config; /// The internal mod metadata list. private readonly ModDatabase ModDatabase; @@ -58,21 +55,19 @@ namespace StardewModdingAPI.Web.Controllers /// The web hosting environment. /// The cache in which to store wiki data. /// The cache in which to store mod metadata. - /// The config settings for mod update checks. + /// The config settings for mod update checks. /// The Chucklefish API client. /// The CurseForge API client. /// The GitHub API client. /// The ModDrop API client. /// The Nexus API client. - public ModsApiController(IHostingEnvironment environment, IWikiCacheRepository wikiCache, IModCacheRepository modCache, IOptions configProvider, IChucklefishClient chucklefish, ICurseForgeClient curseForge, IGitHubClient github, IModDropClient modDrop, INexusClient nexus) + public ModsApiController(IHostingEnvironment environment, IWikiCacheRepository wikiCache, IModCacheRepository modCache, IOptions config, IChucklefishClient chucklefish, ICurseForgeClient curseForge, IGitHubClient github, IModDropClient modDrop, INexusClient nexus) { this.ModDatabase = new ModToolkit().GetModDatabase(Path.Combine(environment.WebRootPath, "SMAPI.metadata.json")); - ModUpdateCheckConfig config = configProvider.Value; this.WikiCache = wikiCache; this.ModCache = modCache; - this.SuccessCacheMinutes = config.SuccessCacheMinutes; - this.ErrorCacheMinutes = config.ErrorCacheMinutes; + this.Config = config; this.Repositories = new IModRepository[] { @@ -133,7 +128,8 @@ namespace StardewModdingAPI.Web.Controllers ModDataRecord record = this.ModDatabase.Get(search.ID); WikiModEntry wikiEntry = wikiData.FirstOrDefault(entry => entry.ID.Contains(search.ID.Trim(), StringComparer.InvariantCultureIgnoreCase)); UpdateKey[] updateKeys = this.GetUpdateKeys(search.UpdateKeys, record, wikiEntry).ToArray(); - bool isSmapi = search.ID == "Pathoschild.SMAPI"; + ModOverrideConfig overrides = this.Config.Value.ModOverrides.FirstOrDefault(p => p.ID.Equals(search.ID?.Trim(), StringComparison.InvariantCultureIgnoreCase)); + bool allowNonStandardVersions = overrides?.AllowNonStandardVersions ?? false; // get latest versions ModEntryModel result = new ModEntryModel { ID = search.ID }; @@ -152,7 +148,7 @@ namespace StardewModdingAPI.Web.Controllers } // fetch data - ModInfoModel data = await this.GetInfoForUpdateKeyAsync(updateKey, allowNonStandardVersions: isSmapi); + ModInfoModel data = await this.GetInfoForUpdateKeyAsync(updateKey, allowNonStandardVersions); if (data.Error != null) { errors.Add(data.Error); @@ -162,7 +158,7 @@ namespace StardewModdingAPI.Web.Controllers // handle main version if (data.Version != null) { - ISemanticVersion version = this.GetMappedVersion(data.Version, wikiEntry?.MapRemoteVersions, allowNonStandard: isSmapi); + ISemanticVersion version = this.GetMappedVersion(data.Version, wikiEntry?.MapRemoteVersions, allowNonStandardVersions); if (version == null) { errors.Add($"The update key '{updateKey}' matches a mod with invalid semantic version '{data.Version}'."); @@ -176,7 +172,7 @@ namespace StardewModdingAPI.Web.Controllers // handle optional version if (data.PreviewVersion != null) { - ISemanticVersion version = this.GetMappedVersion(data.PreviewVersion, wikiEntry?.MapRemoteVersions, allowNonStandard: isSmapi); + ISemanticVersion version = this.GetMappedVersion(data.PreviewVersion, wikiEntry?.MapRemoteVersions, allowNonStandardVersions); if (version == null) { errors.Add($"The update key '{updateKey}' matches a mod with invalid optional semantic version '{data.PreviewVersion}'."); @@ -216,16 +212,16 @@ namespace StardewModdingAPI.Web.Controllers } // special cases - if (isSmapi) + if (overrides?.SetUrl != null) { if (main != null) - main.Url = "https://smapi.io/"; + main.Url = overrides.SetUrl; if (optional != null) - optional.Url = "https://smapi.io/"; + optional.Url = overrides.SetUrl; } // get recommended update (if any) - ISemanticVersion installedVersion = this.GetMappedVersion(search.InstalledVersion?.ToString(), wikiEntry?.MapLocalVersions, allowNonStandard: isSmapi); + ISemanticVersion installedVersion = this.GetMappedVersion(search.InstalledVersion?.ToString(), wikiEntry?.MapLocalVersions, allowNonStandard: allowNonStandardVersions); if (apiVersion != null && installedVersion != null) { // get newer versions @@ -288,7 +284,7 @@ namespace StardewModdingAPI.Web.Controllers private async Task GetInfoForUpdateKeyAsync(UpdateKey updateKey, bool allowNonStandardVersions) { // get mod - if (!this.ModCache.TryGetMod(updateKey.Repository, updateKey.ID, out CachedMod mod) || this.ModCache.IsStale(mod.LastUpdated, mod.FetchStatus == RemoteModStatus.TemporaryError ? this.ErrorCacheMinutes : this.SuccessCacheMinutes)) + if (!this.ModCache.TryGetMod(updateKey.Repository, updateKey.ID, out CachedMod mod) || this.ModCache.IsStale(mod.LastUpdated, mod.FetchStatus == RemoteModStatus.TemporaryError ? this.Config.Value.ErrorCacheMinutes : this.Config.Value.SuccessCacheMinutes)) { // get site if (!this.Repositories.TryGetValue(updateKey.Repository, out IModRepository repository)) -- cgit From dd27b3bf35d01e4fd0703e47d538bd8cec03652b Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Thu, 20 Feb 2020 21:32:55 -0500 Subject: fix parsing four-part versions from the update-check API --- src/SMAPI.Web/Controllers/ModsApiController.cs | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'src/SMAPI.Web/Controllers') diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index 8370fe09..06768f03 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -296,7 +296,7 @@ namespace StardewModdingAPI.Web.Controllers { if (result.Version == null) result.SetError(RemoteModStatus.InvalidData, $"The update key '{updateKey}' matches a mod with no version number."); - else if (!this.TryParseVersion(result.Version, allowNonStandardVersions, out _)) + else if (!SemanticVersion.TryParse(result.Version, allowNonStandardVersions, out _)) result.SetError(RemoteModStatus.InvalidData, $"The update key '{updateKey}' matches a mod with invalid semantic version '{result.Version}'."); } @@ -360,11 +360,11 @@ namespace StardewModdingAPI.Web.Controllers { // try mapped version string rawNewVersion = this.GetRawMappedVersion(version, map, allowNonStandard); - if (this.TryParseVersion(rawNewVersion, allowNonStandard, out ISemanticVersion parsedNew)) + if (SemanticVersion.TryParse(rawNewVersion, allowNonStandard, out ISemanticVersion parsedNew)) return parsedNew; // return original version - return this.TryParseVersion(version, allowNonStandard, out ISemanticVersion parsedOld) + return SemanticVersion.TryParse(version, allowNonStandard, out ISemanticVersion parsedOld) ? parsedOld : null; } @@ -383,31 +383,19 @@ namespace StardewModdingAPI.Web.Controllers return map[version]; // match parsed version - if (this.TryParseVersion(version, allowNonStandard, out ISemanticVersion parsed)) + if (SemanticVersion.TryParse(version, allowNonStandard, out ISemanticVersion parsed)) { if (map.ContainsKey(parsed.ToString())) return map[parsed.ToString()]; foreach (var pair in map) { - if (this.TryParseVersion(pair.Key, allowNonStandard, out ISemanticVersion target) && parsed.Equals(target) && this.TryParseVersion(pair.Value, allowNonStandard, out ISemanticVersion newVersion)) + if (SemanticVersion.TryParse(pair.Key, allowNonStandard, out ISemanticVersion target) && parsed.Equals(target) && SemanticVersion.TryParse(pair.Value, allowNonStandard, out ISemanticVersion newVersion)) return newVersion.ToString(); } } return version; } - - /// Try to parse a version string. - /// The version string. - /// Whether to allow non-standard versions. - /// The parsed representation. - /// Returns whether parsing the version succeeded. - public bool TryParseVersion(string version, bool allowNonStandard, out ISemanticVersion parsed) - { - return allowNonStandard - ? SemanticVersion.TryParseNonStandard(version, out parsed) - : SemanticVersion.TryParse(version, out parsed); - } } } -- cgit