summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web/Controllers
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2019-07-24 18:33:26 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2019-09-14 18:58:54 -0400
commit17c6ae7ed995344111513ca91b18ec6598ec2399 (patch)
tree11eecb3fc870ab67c8644fc7014b7b9c87c59f91 /src/SMAPI.Web/Controllers
parent03a082297a750dace586dc2d15d17c840d96d95f (diff)
downloadSMAPI-17c6ae7ed995344111513ca91b18ec6598ec2399.tar.gz
SMAPI-17c6ae7ed995344111513ca91b18ec6598ec2399.tar.bz2
SMAPI-17c6ae7ed995344111513ca91b18ec6598ec2399.zip
migrate update check caching to MongoDB (#651)
Diffstat (limited to 'src/SMAPI.Web/Controllers')
-rw-r--r--src/SMAPI.Web/Controllers/ModsApiController.cs68
1 files changed, 26 insertions, 42 deletions
diff --git a/src/SMAPI.Web/Controllers/ModsApiController.cs b/src/SMAPI.Web/Controllers/ModsApiController.cs
index a74d0d8a..195ee5bf 100644
--- a/src/SMAPI.Web/Controllers/ModsApiController.cs
+++ b/src/SMAPI.Web/Controllers/ModsApiController.cs
@@ -2,17 +2,17 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using StardewModdingAPI.Toolkit;
using StardewModdingAPI.Toolkit.Framework.Clients.WebApi;
using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
using StardewModdingAPI.Toolkit.Framework.ModData;
using StardewModdingAPI.Toolkit.Framework.UpdateData;
+using StardewModdingAPI.Web.Framework.Caching.Mods;
+using StardewModdingAPI.Web.Framework.Caching.Wiki;
using StardewModdingAPI.Web.Framework.Clients.Chucklefish;
using StardewModdingAPI.Web.Framework.Clients.GitHub;
using StardewModdingAPI.Web.Framework.Clients.ModDrop;
@@ -33,8 +33,11 @@ namespace StardewModdingAPI.Web.Controllers
/// <summary>The mod repositories which provide mod metadata.</summary>
private readonly IDictionary<ModRepositoryKey, IModRepository> Repositories;
- /// <summary>The cache in which to store mod metadata.</summary>
- private readonly IMemoryCache Cache;
+ /// <summary>The cache in which to store wiki data.</summary>
+ private readonly IWikiCacheRepository WikiCache;
+
+ /// <summary>The cache in which to store mod data.</summary>
+ private readonly IModCacheRepository ModCache;
/// <summary>The number of minutes successful update checks should be cached before refetching them.</summary>
private readonly int SuccessCacheMinutes;
@@ -42,9 +45,6 @@ namespace StardewModdingAPI.Web.Controllers
/// <summary>The number of minutes failed update checks should be cached before refetching them.</summary>
private readonly int ErrorCacheMinutes;
- /// <summary>A regex which matches SMAPI-style semantic version.</summary>
- private readonly string VersionRegex;
-
/// <summary>The internal mod metadata list.</summary>
private readonly ModDatabase ModDatabase;
@@ -57,22 +57,23 @@ namespace StardewModdingAPI.Web.Controllers
*********/
/// <summary>Construct an instance.</summary>
/// <param name="environment">The web hosting environment.</param>
- /// <param name="cache">The cache in which to store mod metadata.</param>
+ /// <param name="wikiCache">The cache in which to store wiki data.</param>
+ /// <param name="modCache">The cache in which to store mod metadata.</param>
/// <param name="configProvider">The config settings for mod update checks.</param>
/// <param name="chucklefish">The Chucklefish API client.</param>
/// <param name="github">The GitHub API client.</param>
/// <param name="modDrop">The ModDrop API client.</param>
/// <param name="nexus">The Nexus API client.</param>
- public ModsApiController(IHostingEnvironment environment, IMemoryCache cache, IOptions<ModUpdateCheckConfig> configProvider, IChucklefishClient chucklefish, IGitHubClient github, IModDropClient modDrop, INexusClient nexus)
+ public ModsApiController(IHostingEnvironment environment, IWikiCacheRepository wikiCache, IModCacheRepository modCache, IOptions<ModUpdateCheckConfig> configProvider, IChucklefishClient chucklefish, IGitHubClient github, IModDropClient modDrop, INexusClient nexus)
{
this.ModDatabase = new ModToolkit().GetModDatabase(Path.Combine(environment.WebRootPath, "SMAPI.metadata.json"));
ModUpdateCheckConfig config = configProvider.Value;
this.CompatibilityPageUrl = config.CompatibilityPageUrl;
- this.Cache = cache;
+ this.WikiCache = wikiCache;
+ this.ModCache = modCache;
this.SuccessCacheMinutes = config.SuccessCacheMinutes;
this.ErrorCacheMinutes = config.ErrorCacheMinutes;
- this.VersionRegex = config.SemanticVersionRegex;
this.Repositories =
new IModRepository[]
{
@@ -93,7 +94,7 @@ namespace StardewModdingAPI.Web.Controllers
return new ModEntryModel[0];
// fetch wiki data
- WikiModEntry[] wikiData = await this.GetWikiDataAsync();
+ WikiModEntry[] wikiData = this.WikiCache.GetWikiMods().Select(p => p.GetModel()).ToArray();
IDictionary<string, ModEntryModel> mods = new Dictionary<string, ModEntryModel>(StringComparer.CurrentCultureIgnoreCase);
foreach (ModSearchEntryModel mod in model.Mods)
{
@@ -218,26 +219,6 @@ namespace StardewModdingAPI.Web.Controllers
return current != null && (other == null || other.IsOlderThan(current));
}
- /// <summary>Get mod data from the wiki compatibility list.</summary>
- private async Task<WikiModEntry[]> GetWikiDataAsync()
- {
- ModToolkit toolkit = new ModToolkit();
- return await this.Cache.GetOrCreateAsync("_wiki", async entry =>
- {
- try
- {
- WikiModEntry[] entries = (await toolkit.GetWikiCompatibilityListAsync()).Mods;
- entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.SuccessCacheMinutes);
- return entries;
- }
- catch
- {
- entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.ErrorCacheMinutes);
- return new WikiModEntry[0];
- }
- });
- }
-
/// <summary>Get the mod info for an update key.</summary>
/// <param name="updateKey">The namespaced update key.</param>
private async Task<ModInfoModel> GetInfoForUpdateKeyAsync(string updateKey)
@@ -247,24 +228,27 @@ namespace StardewModdingAPI.Web.Controllers
if (!parsed.LooksValid)
return new ModInfoModel().WithError(RemoteModStatus.DoesNotExist, $"The update key '{updateKey}' isn't in a valid format. It should contain the site key and mod ID like 'Nexus:541'.");
- // get matching repository
- if (!this.Repositories.TryGetValue(parsed.Repository, out IModRepository repository))
- return new ModInfoModel().WithError(RemoteModStatus.DoesNotExist, $"There's no mod site with key '{parsed.Repository}'. Expected one of [{string.Join(", ", this.Repositories.Keys)}].");
-
- // fetch mod info
- return await this.Cache.GetOrCreateAsync($"{repository.VendorKey}:{parsed.ID}".ToLower(), async entry =>
+ // get mod
+ if (!this.ModCache.TryGetMod(parsed.Repository, parsed.ID, out CachedMod mod) || this.ModCache.IsStale(mod.LastUpdated, mod.FetchStatus == RemoteModStatus.TemporaryError ? this.ErrorCacheMinutes : this.SuccessCacheMinutes))
{
+ // get site
+ if (!this.Repositories.TryGetValue(parsed.Repository, out IModRepository repository))
+ return new ModInfoModel().WithError(RemoteModStatus.DoesNotExist, $"There's no mod site with key '{parsed.Repository}'. Expected one of [{string.Join(", ", this.Repositories.Keys)}].");
+
+ // fetch mod
ModInfoModel result = await repository.GetModInfoAsync(parsed.ID);
if (result.Error == null)
{
if (result.Version == null)
result.WithError(RemoteModStatus.InvalidData, $"The update key '{updateKey}' matches a mod with no version number.");
- else if (!Regex.IsMatch(result.Version, this.VersionRegex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase))
+ else if (!SemanticVersion.TryParse(result.Version, out _))
result.WithError(RemoteModStatus.InvalidData, $"The update key '{updateKey}' matches a mod with invalid semantic version '{result.Version}'.");
}
- entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(result.Status == RemoteModStatus.TemporaryError ? this.ErrorCacheMinutes : this.SuccessCacheMinutes);
- return result;
- });
+
+ // cache mod
+ this.ModCache.SaveMod(repository.VendorKey, parsed.ID, result, out mod);
+ }
+ return mod.GetModel();
}
/// <summary>Get update keys based on the available mod metadata, while maintaining the precedence order.</summary>