diff options
author | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-04-12 19:15:39 -0400 |
---|---|---|
committer | Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com> | 2022-04-12 19:15:39 -0400 |
commit | 0b48c1748b354458059c7607415288de072b01e9 (patch) | |
tree | f63fd950f565d363414eb692be9024895cdea174 /src/SMAPI.Web/Controllers | |
parent | 9fbed0fa1f28a9b0fe0b952a78b10e2d475adb03 (diff) | |
download | SMAPI-0b48c1748b354458059c7607415288de072b01e9.tar.gz SMAPI-0b48c1748b354458059c7607415288de072b01e9.tar.bz2 SMAPI-0b48c1748b354458059c7607415288de072b01e9.zip |
enable nullable annotations in the web project & related code (#837)
Diffstat (limited to 'src/SMAPI.Web/Controllers')
-rw-r--r-- | src/SMAPI.Web/Controllers/IndexController.cs | 12 | ||||
-rw-r--r-- | src/SMAPI.Web/Controllers/JsonValidatorController.cs | 46 | ||||
-rw-r--r-- | src/SMAPI.Web/Controllers/LogParserController.cs | 6 | ||||
-rw-r--r-- | src/SMAPI.Web/Controllers/ModsApiController.cs | 70 | ||||
-rw-r--r-- | src/SMAPI.Web/Controllers/ModsController.cs | 7 |
5 files changed, 69 insertions, 72 deletions
diff --git a/src/SMAPI.Web/Controllers/IndexController.cs b/src/SMAPI.Web/Controllers/IndexController.cs index f7834b9c..522d77cd 100644 --- a/src/SMAPI.Web/Controllers/IndexController.cs +++ b/src/SMAPI.Web/Controllers/IndexController.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -59,8 +57,8 @@ namespace StardewModdingAPI.Web.Controllers { // choose versions ReleaseVersion[] versions = await this.GetReleaseVersionsAsync(); - ReleaseVersion stableVersion = versions.LastOrDefault(version => !version.IsForDevs); - ReleaseVersion stableVersionForDevs = versions.LastOrDefault(version => version.IsForDevs); + ReleaseVersion? stableVersion = versions.LastOrDefault(version => !version.IsForDevs); + ReleaseVersion? stableVersionForDevs = versions.LastOrDefault(version => version.IsForDevs); // render view IndexVersionModel stableVersionModel = stableVersion != null @@ -91,7 +89,7 @@ namespace StardewModdingAPI.Web.Controllers entry.AbsoluteExpiration = DateTimeOffset.UtcNow.Add(this.CacheTime); // get latest stable release - GitRelease release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false); + GitRelease? release = await this.GitHub.GetLatestReleaseAsync(this.RepositoryName, includePrerelease: false); // strip 'noinclude' blocks from release description if (release != null) @@ -113,7 +111,7 @@ namespace StardewModdingAPI.Web.Controllers /// <summary>Get a parsed list of SMAPI downloads for a release.</summary> /// <param name="release">The GitHub release.</param> - private IEnumerable<ReleaseVersion> ParseReleaseVersions(GitRelease release) + private IEnumerable<ReleaseVersion> ParseReleaseVersions(GitRelease? release) { if (release?.Assets == null) yield break; @@ -124,7 +122,7 @@ namespace StardewModdingAPI.Web.Controllers continue; Match match = Regex.Match(asset.FileName, @"SMAPI-(?<version>[\d\.]+(?:-.+)?)-installer(?<forDevs>-for-developers)?.zip"); - if (!match.Success || !SemanticVersion.TryParse(match.Groups["version"].Value, out ISemanticVersion version)) + if (!match.Success || !SemanticVersion.TryParse(match.Groups["version"].Value, out ISemanticVersion? version)) continue; bool isForDevs = match.Groups["forDevs"].Success; diff --git a/src/SMAPI.Web/Controllers/JsonValidatorController.cs b/src/SMAPI.Web/Controllers/JsonValidatorController.cs index 5791d834..e78aeeb6 100644 --- a/src/SMAPI.Web/Controllers/JsonValidatorController.cs +++ b/src/SMAPI.Web/Controllers/JsonValidatorController.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -66,7 +64,7 @@ namespace StardewModdingAPI.Web.Controllers [Route("json/{schemaName}")] [Route("json/{schemaName}/{id}")] [Route("json/{schemaName}/{id}/{operation}")] - public async Task<ViewResult> Index(string schemaName = null, string id = null, string operation = null) + public async Task<ViewResult> Index(string? schemaName = null, string? id = null, string? operation = null) { // parse arguments schemaName = this.NormalizeSchemaName(schemaName); @@ -81,7 +79,7 @@ namespace StardewModdingAPI.Web.Controllers return this.View("Index", result); // fetch raw JSON - StoredFileInfo file = await this.Storage.GetAsync(id, renew); + StoredFileInfo file = await this.Storage.GetAsync(id!, renew); if (string.IsNullOrWhiteSpace(file.Content)) return this.View("Index", result.SetUploadError("The JSON file seems to be empty.")); result.SetContent(file.Content, expiry: file.Expiry, uploadWarning: file.Warning); @@ -105,7 +103,7 @@ namespace StardewModdingAPI.Web.Controllers } catch (JsonReaderException ex) { - return this.View("Index", result.AddErrors(new JsonValidatorErrorModel(ex.LineNumber, ex.Path, ex.Message, ErrorType.None))); + return this.View("Index", result.AddErrors(new JsonValidatorErrorModel(ex.LineNumber, ex.Path!, ex.Message, ErrorType.None))); } // format JSON @@ -121,7 +119,7 @@ namespace StardewModdingAPI.Web.Controllers // load schema JSchema schema; { - FileInfo schemaFile = this.FindSchemaFile(schemaName); + FileInfo? schemaFile = this.FindSchemaFile(schemaName); if (schemaFile == null) return this.View("Index", result.SetParseError($"Invalid schema '{schemaName}'.")); schema = JSchema.Parse(System.IO.File.ReadAllText(schemaFile.FullName)); @@ -144,7 +142,7 @@ namespace StardewModdingAPI.Web.Controllers /// <summary>Save raw JSON data.</summary> [HttpPost, AllowLargePosts] [Route("json")] - public async Task<ActionResult> PostAsync(JsonValidatorRequestModel request) + public async Task<ActionResult> PostAsync(JsonValidatorRequestModel? request) { if (request == null) return this.View("Index", this.GetModel(null, null, isEditView: true).SetUploadError("The request seems to be invalid.")); @@ -163,7 +161,7 @@ namespace StardewModdingAPI.Web.Controllers return this.View("Index", this.GetModel(result.ID, schemaName, isEditView: true).SetContent(input, null).SetUploadError(result.UploadError)); // redirect to view - return this.Redirect(this.Url.PlainAction("Index", "JsonValidator", new { schemaName, id = result.ID })); + return this.Redirect(this.Url.PlainAction("Index", "JsonValidator", new { schemaName, id = result.ID })!); } @@ -174,14 +172,14 @@ namespace StardewModdingAPI.Web.Controllers /// <param name="pasteID">The stored file ID.</param> /// <param name="schemaName">The schema name with which the JSON was validated.</param> /// <param name="isEditView">Whether to show the edit view.</param> - private JsonValidatorModel GetModel(string pasteID, string schemaName, bool isEditView) + private JsonValidatorModel GetModel(string? pasteID, string? schemaName, bool isEditView) { return new JsonValidatorModel(pasteID, schemaName, this.SchemaFormats, isEditView); } /// <summary>Get a normalized schema name, or the <see cref="DefaultSchemaID"/> if blank.</summary> /// <param name="schemaName">The raw schema name to normalize.</param> - private string NormalizeSchemaName(string schemaName) + private string NormalizeSchemaName(string? schemaName) { schemaName = schemaName?.Trim().ToLower(); return !string.IsNullOrWhiteSpace(schemaName) @@ -191,7 +189,7 @@ namespace StardewModdingAPI.Web.Controllers /// <summary>Get the schema file given its unique ID.</summary> /// <param name="id">The schema ID.</param> - private FileInfo FindSchemaFile(string id) + private FileInfo? FindSchemaFile(string? id) { // normalize ID id = id?.Trim().ToLower(); @@ -216,13 +214,13 @@ namespace StardewModdingAPI.Web.Controllers // skip through transparent errors if (this.IsTransparentError(error)) { - foreach (var model in error.ChildErrors.SelectMany(this.GetErrorModels)) + foreach (JsonValidatorErrorModel model in error.ChildErrors.SelectMany(this.GetErrorModels)) yield return model; yield break; } // get message - string message = this.GetOverrideError(error); + string? message = this.GetOverrideError(error); if (message == null || message == this.TransparentToken) message = this.FlattenErrorMessage(error); @@ -236,7 +234,7 @@ namespace StardewModdingAPI.Web.Controllers private string FlattenErrorMessage(ValidationError error, int indent = 0) { // get override - string message = this.GetOverrideError(error); + string? message = this.GetOverrideError(error); if (message != null && message != this.TransparentToken) return message; @@ -257,7 +255,7 @@ namespace StardewModdingAPI.Web.Controllers break; case ErrorType.Required: - message = $"Missing required fields: {string.Join(", ", (List<string>)error.Value)}."; + message = $"Missing required fields: {string.Join(", ", (List<string>)error.Value!)}."; break; } @@ -274,7 +272,7 @@ namespace StardewModdingAPI.Web.Controllers if (!error.ChildErrors.Any()) return false; - string @override = this.GetOverrideError(error); + string? @override = this.GetOverrideError(error); return @override == this.TransparentToken || (error.ErrorType == ErrorType.Then && @override == null); @@ -282,18 +280,18 @@ namespace StardewModdingAPI.Web.Controllers /// <summary>Get an override error from the JSON schema, if any.</summary> /// <param name="error">The schema validation error.</param> - private string GetOverrideError(ValidationError error) + private string? GetOverrideError(ValidationError error) { - string GetRawOverrideError() + string? GetRawOverrideError() { // get override errors - IDictionary<string, string> errors = this.GetExtensionField<Dictionary<string, string>>(error.Schema, "@errorMessages"); + IDictionary<string, string?>? errors = this.GetExtensionField<Dictionary<string, string?>>(error.Schema, "@errorMessages"); if (errors == null) return null; - errors = new Dictionary<string, string>(errors, StringComparer.OrdinalIgnoreCase); + errors = new Dictionary<string, string?>(errors, StringComparer.OrdinalIgnoreCase); // match error by type and message - foreach ((string target, string errorMessage) in errors) + foreach ((string target, string? errorMessage) in errors) { if (!target.Contains(":")) continue; @@ -304,7 +302,7 @@ namespace StardewModdingAPI.Web.Controllers } // match by type - return errors.TryGetValue(error.ErrorType.ToString(), out string message) + return errors.TryGetValue(error.ErrorType.ToString(), out string? message) ? message?.Trim() : null; } @@ -317,7 +315,7 @@ namespace StardewModdingAPI.Web.Controllers /// <typeparam name="T">The field type.</typeparam> /// <param name="schema">The schema whose extension fields to search.</param> /// <param name="key">The case-insensitive field key.</param> - private T GetExtensionField<T>(JSchema schema, string key) + private T? GetExtensionField<T>(JSchema schema, string key) { foreach ((string curKey, JToken value) in schema.ExtensionData) { @@ -330,7 +328,7 @@ namespace StardewModdingAPI.Web.Controllers /// <summary>Format a schema value for display.</summary> /// <param name="value">The value to format.</param> - private string FormatValue(object value) + private string FormatValue(object? value) { return value switch { diff --git a/src/SMAPI.Web/Controllers/LogParserController.cs b/src/SMAPI.Web/Controllers/LogParserController.cs index 93f2613e..33af5a81 100644 --- a/src/SMAPI.Web/Controllers/LogParserController.cs +++ b/src/SMAPI.Web/Controllers/LogParserController.cs @@ -69,7 +69,7 @@ namespace StardewModdingAPI.Web.Controllers case LogViewFormat.RawDownload: { - string content = file.Error ?? file.Content; + string content = file.Error ?? file.Content ?? string.Empty; return this.File(Encoding.UTF8.GetBytes(content), "plain/text", $"SMAPI log ({id}).txt"); } @@ -97,7 +97,7 @@ namespace StardewModdingAPI.Web.Controllers return this.View("Index", this.GetModel(null, uploadError: uploadResult.UploadError)); // redirect to view - return this.Redirect(this.Url.PlainAction("Index", "LogParser", new { id = uploadResult.ID })); + return this.Redirect(this.Url.PlainAction("Index", "LogParser", new { id = uploadResult.ID })!); } @@ -109,7 +109,7 @@ namespace StardewModdingAPI.Web.Controllers /// <param name="expiry">When the uploaded file will no longer be available.</param> /// <param name="uploadWarning">A non-blocking warning while uploading the log.</param> /// <param name="uploadError">An error which occurred while uploading the log.</param> - private LogParserModel GetModel(string? pasteID, DateTime? expiry = null, string? uploadWarning = null, string? uploadError = null) + private LogParserModel GetModel(string? pasteID, DateTimeOffset? expiry = null, string? uploadWarning = null, string? uploadError = null) { Platform? platform = this.DetectClientPlatform(); diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs index 3dc1e366..401bba4f 100644 --- a/src/SMAPI.Web/Controllers/ModsApiController.cs +++ b/src/SMAPI.Web/Controllers/ModsApiController.cs @@ -1,7 +1,6 @@ -#nullable disable - using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -78,7 +77,7 @@ namespace StardewModdingAPI.Web.Controllers /// <param name="model">The mod search criteria.</param> /// <param name="version">The requested API version.</param> [HttpPost] - public async Task<IEnumerable<ModEntryModel>> PostAsync([FromBody] ModSearchModel model, [FromRoute] string version) + public async Task<IEnumerable<ModEntryModel>> PostAsync([FromBody] ModSearchModel? model, [FromRoute] string version) { if (model?.Mods == null) return Array.Empty<ModEntryModel>(); @@ -94,16 +93,16 @@ namespace StardewModdingAPI.Web.Controllers continue; // special case: if this is an update check for the official SMAPI repo, check the Nexus mod page for beta versions - if (mod.ID == config.SmapiInfo.ID && mod.UpdateKeys?.Any(key => key == config.SmapiInfo.DefaultUpdateKey) == true && mod.InstalledVersion?.IsPrerelease() == true) - mod.UpdateKeys = mod.UpdateKeys.Concat(config.SmapiInfo.AddBetaUpdateKeys).ToArray(); + if (mod.ID == config.SmapiInfo.ID && mod.UpdateKeys.Any(key => key == config.SmapiInfo.DefaultUpdateKey) && mod.InstalledVersion?.IsPrerelease() == true) + mod.AddUpdateKeys(config.SmapiInfo.AddBetaUpdateKeys); // fetch result ModEntryModel result = await this.GetModData(mod, wikiData, model.IncludeExtendedMetadata, model.ApiVersion); if (!model.IncludeExtendedMetadata && (model.ApiVersion == null || mod.InstalledVersion == null)) { - var errors = new List<string>(result.Errors); - errors.Add($"This API can't suggest an update because {nameof(model.ApiVersion)} or {nameof(mod.InstalledVersion)} are null, and you didn't specify {nameof(model.IncludeExtendedMetadata)} to get other info. See the SMAPI technical docs for usage."); - result.Errors = errors.ToArray(); + result.Errors = result.Errors + .Concat(new[] { $"This API can't suggest an update because {nameof(model.ApiVersion)} or {nameof(mod.InstalledVersion)} are null, and you didn't specify {nameof(model.IncludeExtendedMetadata)} to get other info. See the SMAPI technical docs for usage." }) + .ToArray(); } mods[mod.ID] = result; @@ -123,26 +122,26 @@ namespace StardewModdingAPI.Web.Controllers /// <param name="includeExtendedMetadata">Whether to include extended metadata for each mod.</param> /// <param name="apiVersion">The SMAPI version installed by the player.</param> /// <returns>Returns the mod data if found, else <c>null</c>.</returns> - private async Task<ModEntryModel> GetModData(ModSearchEntryModel search, WikiModEntry[] wikiData, bool includeExtendedMetadata, ISemanticVersion apiVersion) + private async Task<ModEntryModel> GetModData(ModSearchEntryModel search, WikiModEntry[] wikiData, bool includeExtendedMetadata, ISemanticVersion? apiVersion) { // cross-reference data - ModDataRecord record = this.ModDatabase.Get(search.ID); - WikiModEntry wikiEntry = wikiData.FirstOrDefault(entry => entry.ID.Contains(search.ID.Trim(), StringComparer.OrdinalIgnoreCase)); + ModDataRecord? record = this.ModDatabase.Get(search.ID); + WikiModEntry? wikiEntry = wikiData.FirstOrDefault(entry => entry.ID.Contains(search.ID.Trim(), StringComparer.OrdinalIgnoreCase)); UpdateKey[] updateKeys = this.GetUpdateKeys(search.UpdateKeys, record, wikiEntry).ToArray(); - ModOverrideConfig overrides = this.Config.Value.ModOverrides.FirstOrDefault(p => p.ID.Equals(search.ID?.Trim(), StringComparison.OrdinalIgnoreCase)); + ModOverrideConfig? overrides = this.Config.Value.ModOverrides.FirstOrDefault(p => p.ID.Equals(search.ID.Trim(), StringComparison.OrdinalIgnoreCase)); bool allowNonStandardVersions = overrides?.AllowNonStandardVersions ?? false; // SMAPI versions with a '-beta' tag indicate major changes that may need beta mod versions. // This doesn't apply to normal prerelease versions which have an '-alpha' tag. - bool isSmapiBeta = apiVersion.IsPrerelease() && apiVersion.PrereleaseTag.StartsWith("beta"); + bool isSmapiBeta = apiVersion != null && apiVersion.IsPrerelease() && apiVersion.PrereleaseTag.StartsWith("beta"); // get latest versions - ModEntryModel result = new() { ID = search.ID }; + ModEntryModel result = new(search.ID); IList<string> errors = new List<string>(); - ModEntryVersionModel main = null; - ModEntryVersionModel optional = null; - ModEntryVersionModel unofficial = null; - ModEntryVersionModel unofficialForBeta = null; + ModEntryVersionModel? main = null; + ModEntryVersionModel? optional = null; + ModEntryVersionModel? unofficial = null; + ModEntryVersionModel? unofficialForBeta = null; foreach (UpdateKey updateKey in updateKeys) { // validate update key @@ -162,9 +161,9 @@ namespace StardewModdingAPI.Web.Controllers // handle versions if (this.IsNewer(data.Version, main?.Version)) - main = new ModEntryVersionModel(data.Version, data.Url); + main = new ModEntryVersionModel(data.Version, data.Url!); if (this.IsNewer(data.PreviewVersion, optional?.Version)) - optional = new ModEntryVersionModel(data.PreviewVersion, data.Url); + optional = new ModEntryVersionModel(data.PreviewVersion, data.Url!); } // get unofficial version @@ -172,7 +171,7 @@ namespace StardewModdingAPI.Web.Controllers unofficial = new ModEntryVersionModel(wikiEntry.Compatibility.UnofficialVersion, $"{this.Url.PlainAction("Index", "Mods", absoluteUrl: true)}#{wikiEntry.Anchor}"); // get unofficial version for beta - if (wikiEntry?.HasBetaInfo == true) + if (wikiEntry is { HasBetaInfo: true }) { if (wikiEntry.BetaCompatibility.Status == WikiCompatibilityStatus.Unofficial) { @@ -198,13 +197,13 @@ namespace StardewModdingAPI.Web.Controllers if (overrides?.SetUrl != null) { if (main != null) - main.Url = overrides.SetUrl; + main = new(main.Version, overrides.SetUrl); if (optional != null) - optional.Url = overrides.SetUrl; + optional = new(optional.Version, overrides.SetUrl); } // get recommended update (if any) - ISemanticVersion installedVersion = this.ModSites.GetMappedVersion(search.InstalledVersion?.ToString(), wikiEntry?.Overrides?.ChangeLocalVersions, allowNonStandard: allowNonStandardVersions); + ISemanticVersion? installedVersion = this.ModSites.GetMappedVersion(search.InstalledVersion?.ToString(), wikiEntry?.Overrides?.ChangeLocalVersions, allowNonStandard: allowNonStandardVersions); if (apiVersion != null && installedVersion != null) { // get newer versions @@ -219,7 +218,7 @@ namespace StardewModdingAPI.Web.Controllers updates.Add(unofficialForBeta); // get newest version - ModEntryVersionModel newest = null; + ModEntryVersionModel? newest = null; foreach (ModEntryVersionModel update in updates) { if (newest == null || update.Version.IsNewerThan(newest.Version)) @@ -245,7 +244,7 @@ namespace StardewModdingAPI.Web.Controllers /// <param name="currentVersion">The current semantic version.</param> /// <param name="newVersion">The target semantic version.</param> /// <param name="useBetaChannel">Whether the user enabled the beta channel and should be offered prerelease updates.</param> - private bool IsRecommendedUpdate(ISemanticVersion currentVersion, ISemanticVersion newVersion, bool useBetaChannel) + private bool IsRecommendedUpdate(ISemanticVersion currentVersion, [NotNullWhen(true)] ISemanticVersion? newVersion, bool useBetaChannel) { return newVersion != null @@ -256,7 +255,7 @@ namespace StardewModdingAPI.Web.Controllers /// <summary>Get whether a <paramref name="current"/> version is newer than an <paramref name="other"/> version.</summary> /// <param name="current">The current version.</param> /// <param name="other">The other version.</param> - private bool IsNewer(ISemanticVersion current, ISemanticVersion other) + private bool IsNewer([NotNullWhen(true)] ISemanticVersion? current, ISemanticVersion? other) { return current != null && (other == null || other.IsOlderThan(current)); } @@ -265,17 +264,20 @@ namespace StardewModdingAPI.Web.Controllers /// <param name="updateKey">The namespaced update key.</param> /// <param name="allowNonStandardVersions">Whether to allow non-standard versions.</param> /// <param name="mapRemoteVersions">The changes to apply to remote versions for update checks.</param> - private async Task<ModInfoModel> GetInfoForUpdateKeyAsync(UpdateKey updateKey, bool allowNonStandardVersions, ChangeDescriptor mapRemoteVersions) + private async Task<ModInfoModel> GetInfoForUpdateKeyAsync(UpdateKey updateKey, bool allowNonStandardVersions, ChangeDescriptor? mapRemoteVersions) { + if (!updateKey.LooksValid) + return new ModInfoModel().SetError(RemoteModStatus.DoesNotExist, $"Invalid update key '{updateKey}'."); + // get mod page IModPage page; { bool isCached = - this.ModCache.TryGetMod(updateKey.Site, updateKey.ID, out Cached<IModPage> cachedMod) + this.ModCache.TryGetMod(updateKey.Site, updateKey.ID, out Cached<IModPage>? cachedMod) && !this.ModCache.IsStale(cachedMod.LastUpdated, cachedMod.Data.Status == RemoteModStatus.TemporaryError ? this.Config.Value.ErrorCacheMinutes : this.Config.Value.SuccessCacheMinutes); if (isCached) - page = cachedMod.Data; + page = cachedMod!.Data; else { page = await this.ModSites.GetModPageAsync(updateKey); @@ -291,7 +293,7 @@ namespace StardewModdingAPI.Web.Controllers /// <param name="specifiedKeys">The specified update keys.</param> /// <param name="record">The mod's entry in SMAPI's internal database.</param> /// <param name="entry">The mod's entry in the wiki list.</param> - private IEnumerable<UpdateKey> GetUpdateKeys(string[] specifiedKeys, ModDataRecord record, WikiModEntry entry) + private IEnumerable<UpdateKey> GetUpdateKeys(string[]? specifiedKeys, ModDataRecord? record, WikiModEntry? entry) { // get unique update keys List<UpdateKey> updateKeys = this.GetUnfilteredUpdateKeys(specifiedKeys, record, entry) @@ -310,7 +312,7 @@ namespace StardewModdingAPI.Web.Controllers // if the list has both an update key (like "Nexus:2400") and subkey (like "Nexus:2400@subkey") for the same page, the subkey takes priority { var removeKeys = new HashSet<UpdateKey>(); - foreach (var key in updateKeys) + foreach (UpdateKey key in updateKeys) { if (key.Subkey != null) removeKeys.Add(new UpdateKey(key.Site, key.ID, null)); @@ -326,7 +328,7 @@ namespace StardewModdingAPI.Web.Controllers /// <param name="specifiedKeys">The specified update keys.</param> /// <param name="record">The mod's entry in SMAPI's internal database.</param> /// <param name="entry">The mod's entry in the wiki list.</param> - private IEnumerable<string> GetUnfilteredUpdateKeys(string[] specifiedKeys, ModDataRecord record, WikiModEntry entry) + private IEnumerable<string> GetUnfilteredUpdateKeys(string[]? specifiedKeys, ModDataRecord? record, WikiModEntry? entry) { // specified update keys foreach (string key in specifiedKeys ?? Array.Empty<string>()) @@ -337,7 +339,7 @@ namespace StardewModdingAPI.Web.Controllers // default update key { - string defaultKey = record?.GetDefaultUpdateKey(); + string? defaultKey = record?.GetDefaultUpdateKey(); if (!string.IsNullOrWhiteSpace(defaultKey)) yield return defaultKey; } diff --git a/src/SMAPI.Web/Controllers/ModsController.cs b/src/SMAPI.Web/Controllers/ModsController.cs index 5292e1ce..919afa5b 100644 --- a/src/SMAPI.Web/Controllers/ModsController.cs +++ b/src/SMAPI.Web/Controllers/ModsController.cs @@ -1,5 +1,4 @@ -#nullable disable - +using System; using System.Linq; using System.Text.RegularExpressions; using Microsoft.AspNetCore.Mvc; @@ -54,8 +53,8 @@ namespace StardewModdingAPI.Web.Controllers public ModListModel FetchData() { // fetch cached data - if (!this.Cache.TryGetWikiMetadata(out Cached<WikiMetadata> metadata)) - return new ModListModel(); + if (!this.Cache.TryGetWikiMetadata(out Cached<WikiMetadata>? metadata)) + return new ModListModel(null, null, Array.Empty<ModModel>(), lastUpdated: DateTimeOffset.UtcNow, isStale: true); // build model return new ModListModel( |