summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web/Framework/Caching/Wiki
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Web/Framework/Caching/Wiki')
-rw-r--r--src/SMAPI.Web/Framework/Caching/Wiki/CachedWikiMetadata.cs43
-rw-r--r--src/SMAPI.Web/Framework/Caching/Wiki/CachedWikiMod.cs230
-rw-r--r--src/SMAPI.Web/Framework/Caching/Wiki/IWikiCacheRepository.cs30
-rw-r--r--src/SMAPI.Web/Framework/Caching/Wiki/WikiCacheRepository.cs73
4 files changed, 376 insertions, 0 deletions
diff --git a/src/SMAPI.Web/Framework/Caching/Wiki/CachedWikiMetadata.cs b/src/SMAPI.Web/Framework/Caching/Wiki/CachedWikiMetadata.cs
new file mode 100644
index 00000000..6a560eb4
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Caching/Wiki/CachedWikiMetadata.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using MongoDB.Bson;
+
+namespace StardewModdingAPI.Web.Framework.Caching.Wiki
+{
+ /// <summary>The model for cached wiki metadata.</summary>
+ internal class CachedWikiMetadata
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The internal MongoDB ID.</summary>
+ [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Named per MongoDB conventions.")]
+ public ObjectId _id { get; set; }
+
+ /// <summary>When the data was last updated.</summary>
+ public DateTimeOffset LastUpdated { get; set; }
+
+ /// <summary>The current stable Stardew Valley version.</summary>
+ public string StableVersion { get; set; }
+
+ /// <summary>The current beta Stardew Valley version.</summary>
+ public string BetaVersion { get; set; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ public CachedWikiMetadata() { }
+
+ /// <summary>Construct an instance.</summary>
+ /// <param name="stableVersion">The current stable Stardew Valley version.</param>
+ /// <param name="betaVersion">The current beta Stardew Valley version.</param>
+ public CachedWikiMetadata(string stableVersion, string betaVersion)
+ {
+ this.StableVersion = stableVersion;
+ this.BetaVersion = betaVersion;
+ this.LastUpdated = DateTimeOffset.UtcNow;
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Caching/Wiki/CachedWikiMod.cs b/src/SMAPI.Web/Framework/Caching/Wiki/CachedWikiMod.cs
new file mode 100644
index 00000000..8569984a
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Caching/Wiki/CachedWikiMod.cs
@@ -0,0 +1,230 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using MongoDB.Bson;
+using MongoDB.Bson.Serialization.Attributes;
+using MongoDB.Bson.Serialization.Options;
+using StardewModdingAPI.Toolkit;
+using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
+
+namespace StardewModdingAPI.Web.Framework.Caching.Wiki
+{
+ /// <summary>The model for cached wiki mods.</summary>
+ internal class CachedWikiMod
+ {
+ /*********
+ ** Accessors
+ *********/
+ /****
+ ** Tracking
+ ****/
+ /// <summary>The internal MongoDB ID.</summary>
+ [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Named per MongoDB conventions.")]
+ public ObjectId _id { get; set; }
+
+ /// <summary>When the data was last updated.</summary>
+ public DateTimeOffset LastUpdated { get; set; }
+
+ /****
+ ** Mod info
+ ****/
+ /// <summary>The mod's unique ID. If the mod has alternate/old IDs, they're listed in latest to newest order.</summary>
+ public string[] ID { get; set; }
+
+ /// <summary>The mod's display name. If the mod has multiple names, the first one is the most canonical name.</summary>
+ public string[] Name { get; set; }
+
+ /// <summary>The mod's author name. If the author has multiple names, the first one is the most canonical name.</summary>
+ public string[] Author { get; set; }
+
+ /// <summary>The mod ID on Nexus.</summary>
+ public int? NexusID { get; set; }
+
+ /// <summary>The mod ID in the Chucklefish mod repo.</summary>
+ public int? ChucklefishID { get; set; }
+
+ /// <summary>The mod ID in the CurseForge mod repo.</summary>
+ public int? CurseForgeID { get; set; }
+
+ /// <summary>The mod key in the CurseForge mod repo (used in mod page URLs).</summary>
+ public string CurseForgeKey { get; set; }
+
+ /// <summary>The mod ID in the ModDrop mod repo.</summary>
+ public int? ModDropID { get; set; }
+
+ /// <summary>The GitHub repository in the form 'owner/repo'.</summary>
+ public string GitHubRepo { get; set; }
+
+ /// <summary>The URL to a non-GitHub source repo.</summary>
+ public string CustomSourceUrl { get; set; }
+
+ /// <summary>The custom mod page URL (if applicable).</summary>
+ public string CustomUrl { get; set; }
+
+ /// <summary>The name of the mod which loads this content pack, if applicable.</summary>
+ public string ContentPackFor { get; set; }
+
+ /// <summary>The human-readable warnings for players about this mod.</summary>
+ public string[] Warnings { get; set; }
+
+ /// <summary>Extra metadata links (usually for open pull requests).</summary>
+ public Tuple<Uri, string>[] MetadataLinks { get; set; }
+
+ /// <summary>Special notes intended for developers who maintain unofficial updates or submit pull requests. </summary>
+ public string DevNote { get; set; }
+
+ /// <summary>The link anchor for the mod entry in the wiki compatibility list.</summary>
+ public string Anchor { get; set; }
+
+ /****
+ ** Stable compatibility
+ ****/
+ /// <summary>The compatibility status.</summary>
+ public WikiCompatibilityStatus MainStatus { get; set; }
+
+ /// <summary>The human-readable summary of the compatibility status or workaround, without HTML formatting.</summary>
+ public string MainSummary { get; set; }
+
+ /// <summary>The game or SMAPI version which broke this mod (if applicable).</summary>
+ public string MainBrokeIn { get; set; }
+
+ /// <summary>The version of the latest unofficial update, if applicable.</summary>
+ public string MainUnofficialVersion { get; set; }
+
+ /// <summary>The URL to the latest unofficial update, if applicable.</summary>
+ public string MainUnofficialUrl { get; set; }
+
+ /****
+ ** Beta compatibility
+ ****/
+ /// <summary>The compatibility status.</summary>
+ public WikiCompatibilityStatus? BetaStatus { get; set; }
+
+ /// <summary>The human-readable summary of the compatibility status or workaround, without HTML formatting.</summary>
+ public string BetaSummary { get; set; }
+
+ /// <summary>The game or SMAPI version which broke this mod (if applicable).</summary>
+ public string BetaBrokeIn { get; set; }
+
+ /// <summary>The version of the latest unofficial update, if applicable.</summary>
+ public string BetaUnofficialVersion { get; set; }
+
+ /// <summary>The URL to the latest unofficial update, if applicable.</summary>
+ public string BetaUnofficialUrl { get; set; }
+
+ /****
+ ** Version maps
+ ****/
+ /// <summary>Maps local versions to a semantic version for update checks.</summary>
+ [BsonDictionaryOptions(Representation = DictionaryRepresentation.ArrayOfArrays)]
+ public IDictionary<string, string> MapLocalVersions { get; set; }
+
+ /// <summary>Maps remote versions to a semantic version for update checks.</summary>
+ [BsonDictionaryOptions(Representation = DictionaryRepresentation.ArrayOfArrays)]
+ public IDictionary<string, string> MapRemoteVersions { get; set; }
+
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>Construct an instance.</summary>
+ public CachedWikiMod() { }
+
+ /// <summary>Construct an instance.</summary>
+ /// <param name="mod">The mod data.</param>
+ public CachedWikiMod(WikiModEntry mod)
+ {
+ // tracking
+ this.LastUpdated = DateTimeOffset.UtcNow;
+
+ // mod info
+ this.ID = mod.ID;
+ this.Name = mod.Name;
+ this.Author = mod.Author;
+ this.NexusID = mod.NexusID;
+ this.ChucklefishID = mod.ChucklefishID;
+ this.CurseForgeID = mod.CurseForgeID;
+ this.CurseForgeKey = mod.CurseForgeKey;
+ this.ModDropID = mod.ModDropID;
+ this.GitHubRepo = mod.GitHubRepo;
+ this.CustomSourceUrl = mod.CustomSourceUrl;
+ this.CustomUrl = mod.CustomUrl;
+ this.ContentPackFor = mod.ContentPackFor;
+ this.MetadataLinks = mod.MetadataLinks;
+ this.Warnings = mod.Warnings;
+ this.DevNote = mod.DevNote;
+ this.Anchor = mod.Anchor;
+
+ // stable compatibility
+ this.MainStatus = mod.Compatibility.Status;
+ this.MainSummary = mod.Compatibility.Summary;
+ this.MainBrokeIn = mod.Compatibility.BrokeIn;
+ this.MainUnofficialVersion = mod.Compatibility.UnofficialVersion?.ToString();
+ this.MainUnofficialUrl = mod.Compatibility.UnofficialUrl;
+
+ // beta compatibility
+ this.BetaStatus = mod.BetaCompatibility?.Status;
+ this.BetaSummary = mod.BetaCompatibility?.Summary;
+ this.BetaBrokeIn = mod.BetaCompatibility?.BrokeIn;
+ this.BetaUnofficialVersion = mod.BetaCompatibility?.UnofficialVersion?.ToString();
+ this.BetaUnofficialUrl = mod.BetaCompatibility?.UnofficialUrl;
+
+ // version maps
+ this.MapLocalVersions = mod.MapLocalVersions;
+ this.MapRemoteVersions = mod.MapRemoteVersions;
+ }
+
+ /// <summary>Reconstruct the original model.</summary>
+ public WikiModEntry GetModel()
+ {
+ var mod = new WikiModEntry
+ {
+ ID = this.ID,
+ Name = this.Name,
+ Author = this.Author,
+ NexusID = this.NexusID,
+ ChucklefishID = this.ChucklefishID,
+ CurseForgeID = this.CurseForgeID,
+ CurseForgeKey = this.CurseForgeKey,
+ ModDropID = this.ModDropID,
+ GitHubRepo = this.GitHubRepo,
+ CustomSourceUrl = this.CustomSourceUrl,
+ CustomUrl = this.CustomUrl,
+ ContentPackFor = this.ContentPackFor,
+ Warnings = this.Warnings,
+ MetadataLinks = this.MetadataLinks,
+ DevNote = this.DevNote,
+ Anchor = this.Anchor,
+
+ // stable compatibility
+ Compatibility = new WikiCompatibilityInfo
+ {
+ Status = this.MainStatus,
+ Summary = this.MainSummary,
+ BrokeIn = this.MainBrokeIn,
+ UnofficialVersion = this.MainUnofficialVersion != null ? new SemanticVersion(this.MainUnofficialVersion) : null,
+ UnofficialUrl = this.MainUnofficialUrl
+ },
+
+ // version maps
+ MapLocalVersions = this.MapLocalVersions,
+ MapRemoteVersions = this.MapRemoteVersions
+ };
+
+ // beta compatibility
+ if (this.BetaStatus != null)
+ {
+ mod.BetaCompatibility = new WikiCompatibilityInfo
+ {
+ Status = this.BetaStatus.Value,
+ Summary = this.BetaSummary,
+ BrokeIn = this.BetaBrokeIn,
+ UnofficialVersion = this.BetaUnofficialVersion != null ? new SemanticVersion(this.BetaUnofficialVersion) : null,
+ UnofficialUrl = this.BetaUnofficialUrl
+ };
+ }
+
+ return mod;
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Caching/Wiki/IWikiCacheRepository.cs b/src/SMAPI.Web/Framework/Caching/Wiki/IWikiCacheRepository.cs
new file mode 100644
index 00000000..b54c8a2f
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Caching/Wiki/IWikiCacheRepository.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq.Expressions;
+using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
+
+namespace StardewModdingAPI.Web.Framework.Caching.Wiki
+{
+ /// <summary>Encapsulates logic for accessing the wiki data cache.</summary>
+ internal interface IWikiCacheRepository : ICacheRepository
+ {
+ /*********
+ ** Methods
+ *********/
+ /// <summary>Get the cached wiki metadata.</summary>
+ /// <param name="metadata">The fetched metadata.</param>
+ bool TryGetWikiMetadata(out CachedWikiMetadata metadata);
+
+ /// <summary>Get the cached wiki mods.</summary>
+ /// <param name="filter">A filter to apply, if any.</param>
+ IEnumerable<CachedWikiMod> GetWikiMods(Expression<Func<CachedWikiMod, bool>> filter = null);
+
+ /// <summary>Save data fetched from the wiki compatibility list.</summary>
+ /// <param name="stableVersion">The current stable Stardew Valley version.</param>
+ /// <param name="betaVersion">The current beta Stardew Valley version.</param>
+ /// <param name="mods">The mod data.</param>
+ /// <param name="cachedMetadata">The stored metadata record.</param>
+ /// <param name="cachedMods">The stored mod records.</param>
+ void SaveWikiData(string stableVersion, string betaVersion, IEnumerable<WikiModEntry> mods, out CachedWikiMetadata cachedMetadata, out CachedWikiMod[] cachedMods);
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Caching/Wiki/WikiCacheRepository.cs b/src/SMAPI.Web/Framework/Caching/Wiki/WikiCacheRepository.cs
new file mode 100644
index 00000000..1ae9d38f
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Caching/Wiki/WikiCacheRepository.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using MongoDB.Driver;
+using StardewModdingAPI.Toolkit.Framework.Clients.Wiki;
+
+namespace StardewModdingAPI.Web.Framework.Caching.Wiki
+{
+ /// <summary>Encapsulates logic for accessing the wiki data cache.</summary>
+ internal class WikiCacheRepository : BaseCacheRepository, IWikiCacheRepository
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>The collection for wiki metadata.</summary>
+ private readonly IMongoCollection<CachedWikiMetadata> WikiMetadata;
+
+ /// <summary>The collection for wiki mod data.</summary>
+ private readonly IMongoCollection<CachedWikiMod> WikiMods;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="database">The authenticated MongoDB database.</param>
+ public WikiCacheRepository(IMongoDatabase database)
+ {
+ // get collections
+ this.WikiMetadata = database.GetCollection<CachedWikiMetadata>("wiki-metadata");
+ this.WikiMods = database.GetCollection<CachedWikiMod>("wiki-mods");
+
+ // add indexes if needed
+ this.WikiMods.Indexes.CreateOne(new CreateIndexModel<CachedWikiMod>(Builders<CachedWikiMod>.IndexKeys.Ascending(p => p.ID)));
+ }
+
+ /// <summary>Get the cached wiki metadata.</summary>
+ /// <param name="metadata">The fetched metadata.</param>
+ public bool TryGetWikiMetadata(out CachedWikiMetadata metadata)
+ {
+ metadata = this.WikiMetadata.Find("{}").FirstOrDefault();
+ return metadata != null;
+ }
+
+ /// <summary>Get the cached wiki mods.</summary>
+ /// <param name="filter">A filter to apply, if any.</param>
+ public IEnumerable<CachedWikiMod> GetWikiMods(Expression<Func<CachedWikiMod, bool>> filter = null)
+ {
+ return filter != null
+ ? this.WikiMods.Find(filter).ToList()
+ : this.WikiMods.Find("{}").ToList();
+ }
+
+ /// <summary>Save data fetched from the wiki compatibility list.</summary>
+ /// <param name="stableVersion">The current stable Stardew Valley version.</param>
+ /// <param name="betaVersion">The current beta Stardew Valley version.</param>
+ /// <param name="mods">The mod data.</param>
+ /// <param name="cachedMetadata">The stored metadata record.</param>
+ /// <param name="cachedMods">The stored mod records.</param>
+ public void SaveWikiData(string stableVersion, string betaVersion, IEnumerable<WikiModEntry> mods, out CachedWikiMetadata cachedMetadata, out CachedWikiMod[] cachedMods)
+ {
+ cachedMetadata = new CachedWikiMetadata(stableVersion, betaVersion);
+ cachedMods = mods.Select(mod => new CachedWikiMod(mod)).ToArray();
+
+ this.WikiMods.DeleteMany("{}");
+ this.WikiMods.InsertMany(cachedMods);
+
+ this.WikiMetadata.DeleteMany("{}");
+ this.WikiMetadata.InsertOne(cachedMetadata);
+ }
+ }
+}