summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/StardewModdingAPI.Web/Controllers/ModsController.cs6
-rw-r--r--src/StardewModdingAPI.Web/Framework/ModRepositories/BaseRepository.cs51
-rw-r--r--src/StardewModdingAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs27
-rw-r--r--src/StardewModdingAPI.Web/Framework/ModRepositories/GitHubRepository.cs32
-rw-r--r--src/StardewModdingAPI.Web/Framework/ModRepositories/NexusRepository.cs23
5 files changed, 81 insertions, 58 deletions
diff --git a/src/StardewModdingAPI.Web/Controllers/ModsController.cs b/src/StardewModdingAPI.Web/Controllers/ModsController.cs
index 4eaa66d2..566577e4 100644
--- a/src/StardewModdingAPI.Web/Controllers/ModsController.cs
+++ b/src/StardewModdingAPI.Web/Controllers/ModsController.cs
@@ -108,9 +108,11 @@ namespace StardewModdingAPI.Web.Controllers
result[modKey] = await this.Cache.GetOrCreateAsync($"{repository.VendorKey}:{modID}".ToLower(), async entry =>
{
entry.AbsoluteExpiration = DateTimeOffset.UtcNow.AddMinutes(this.CacheMinutes);
+
ModInfoModel info = await repository.GetModInfoAsync(modID);
- if (info.Error == null && !Regex.IsMatch(info.Version, this.VersionRegex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase))
- info = new ModInfoModel(info.Name, info.Version, info.Url, $"Mod has invalid semantic version '{info.Version}'.");
+ if (info.Error == null && (info.Version == null || !Regex.IsMatch(info.Version, this.VersionRegex, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)))
+ info = new ModInfoModel(info.Name, info.Version, info.Url, info.Version == null ? "Mod has no version number." : $"Mod has invalid semantic version '{info.Version}'.");
+
return info;
});
}
diff --git a/src/StardewModdingAPI.Web/Framework/ModRepositories/BaseRepository.cs b/src/StardewModdingAPI.Web/Framework/ModRepositories/BaseRepository.cs
new file mode 100644
index 00000000..d98acd89
--- /dev/null
+++ b/src/StardewModdingAPI.Web/Framework/ModRepositories/BaseRepository.cs
@@ -0,0 +1,51 @@
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using StardewModdingAPI.Models;
+
+namespace StardewModdingAPI.Web.Framework.ModRepositories
+{
+ internal abstract class RepositoryBase : IModRepository
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The unique key for this vendor.</summary>
+ public string VendorKey { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
+ public abstract void Dispose();
+
+ /// <summary>Get metadata about a mod in the repository.</summary>
+ /// <param name="id">The mod ID in this repository.</param>
+ public abstract Task<ModInfoModel> GetModInfoAsync(string id);
+
+
+ /*********
+ ** Protected methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="vendorKey">The unique key for this vendor.</param>
+ protected RepositoryBase(string vendorKey)
+ {
+ this.VendorKey = vendorKey;
+ }
+
+ /// <summary>Normalise a version string.</summary>
+ /// <param name="version">The version to normalise.</param>
+ protected string NormaliseVersion(string version)
+ {
+ if (string.IsNullOrWhiteSpace(version))
+ return null;
+
+ version = version.Trim();
+ if (Regex.IsMatch(version, @"^v\d", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)) // common version prefix
+ version = version.Substring(1);
+
+ return version;
+ }
+ }
+}
diff --git a/src/StardewModdingAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs b/src/StardewModdingAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
index 59d7f3ba..4822c414 100644
--- a/src/StardewModdingAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
+++ b/src/StardewModdingAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
@@ -8,26 +8,19 @@ using StardewModdingAPI.Models;
namespace StardewModdingAPI.Web.Framework.ModRepositories
{
/// <summary>An HTTP client for fetching mod metadata from the Chucklefish mod site.</summary>
- internal class ChucklefishRepository : IModRepository
+ internal class ChucklefishRepository : RepositoryBase
{
/*********
** Properties
*********/
- /// <summary>The underlying HTTP client.</summary>
- private readonly IClient Client;
-
-
- /*********
- ** Accessors
- *********/
- /// <summary>The unique key for this vendor.</summary>
- public string VendorKey { get; }
-
/// <summary>The base URL for the Chucklefish mod site.</summary>
- public string BaseUrl { get; }
+ private readonly string BaseUrl;
/// <summary>The URL for a mod page excluding the base URL, where {0} is the mod ID.</summary>
- public string ModPageUrlFormat { get; }
+ private readonly string ModPageUrlFormat;
+
+ /// <summary>The underlying HTTP client.</summary>
+ private readonly IClient Client;
/*********
@@ -39,8 +32,8 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
/// <param name="baseUrl">The base URL for the Chucklefish mod site.</param>
/// <param name="modPageUrlFormat">The URL for a mod page excluding the <paramref name="baseUrl"/>, where {0} is the mod ID.</param>
public ChucklefishRepository(string vendorKey, string userAgent, string baseUrl, string modPageUrlFormat)
+ : base(vendorKey)
{
- this.VendorKey = vendorKey;
this.BaseUrl = baseUrl;
this.ModPageUrlFormat = modPageUrlFormat;
this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent);
@@ -48,7 +41,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
/// <summary>Get metadata about a mod in the repository.</summary>
/// <param name="id">The mod ID in this repository.</param>
- public async Task<ModInfoModel> GetModInfoAsync(string id)
+ public override async Task<ModInfoModel> GetModInfoAsync(string id)
{
try
{
@@ -77,7 +70,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
string version = doc.DocumentNode.SelectSingleNode("//h1/span").InnerText;
// create model
- return new ModInfoModel(name, version, url);
+ return new ModInfoModel(name, this.NormaliseVersion(version), url);
}
catch (Exception ex)
{
@@ -86,7 +79,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
}
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
- public void Dispose()
+ public override void Dispose()
{
this.Client.Dispose();
}
diff --git a/src/StardewModdingAPI.Web/Framework/ModRepositories/GitHubRepository.cs b/src/StardewModdingAPI.Web/Framework/ModRepositories/GitHubRepository.cs
index f794c605..7dfe9f62 100644
--- a/src/StardewModdingAPI.Web/Framework/ModRepositories/GitHubRepository.cs
+++ b/src/StardewModdingAPI.Web/Framework/ModRepositories/GitHubRepository.cs
@@ -7,26 +7,19 @@ using StardewModdingAPI.Models;
namespace StardewModdingAPI.Web.Framework.ModRepositories
{
/// <summary>An HTTP client for fetching mod metadata from GitHub project releases.</summary>
- internal class GitHubRepository : IModRepository
+ internal class GitHubRepository : RepositoryBase
{
/*********
** Properties
*********/
+ /// <summary>The URL for a Nexus Mods API query excluding the base URL, where {0} is the mod ID.</summary>
+ private readonly string ReleaseUrlFormat;
+
/// <summary>The underlying HTTP client.</summary>
private readonly IClient Client;
/*********
- ** Accessors
- *********/
- /// <summary>The unique key for this vendor.</summary>
- public string VendorKey { get; }
-
- /// <summary>The URL for a Nexus Mods API query excluding the base URL, where {0} is the mod ID.</summary>
- public string ReleaseUrlFormat { get; }
-
-
- /*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
@@ -38,8 +31,8 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
/// <param name="username">The username with which to authenticate to the GitHub API.</param>
/// <param name="password">The password with which to authenticate to the GitHub API.</param>
public GitHubRepository(string vendorKey, string baseUrl, string releaseUrlFormat, string userAgent, string acceptHeader, string username, string password)
+ : base(vendorKey)
{
- this.VendorKey = vendorKey;
this.ReleaseUrlFormat = releaseUrlFormat;
this.Client = new FluentClient(baseUrl)
@@ -51,23 +44,14 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
/// <summary>Get metadata about a mod in the repository.</summary>
/// <param name="id">The mod ID in this repository.</param>
- public async Task<ModInfoModel> GetModInfoAsync(string id)
+ public override async Task<ModInfoModel> GetModInfoAsync(string id)
{
try
{
- // fetch data
GitRelease release = await this.Client
.GetAsync(string.Format(this.ReleaseUrlFormat, id))
.As<GitRelease>();
-
- // extract fields
- string name = id;
- string version = release.Tag;
- if (version.StartsWith("v")) // common format on GitHub
- version = version.Substring(1);
- string url = $"https://github.com/{id}/releases";
-
- return new ModInfoModel(name, version, url);
+ return new ModInfoModel(id, this.NormaliseVersion(release.Tag), $"https://github.com/{id}/releases");
}
catch (Exception ex)
{
@@ -76,7 +60,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
}
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
- public void Dispose()
+ public override void Dispose()
{
this.Client.Dispose();
}
diff --git a/src/StardewModdingAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/StardewModdingAPI.Web/Framework/ModRepositories/NexusRepository.cs
index 6cf5b04a..e679b977 100644
--- a/src/StardewModdingAPI.Web/Framework/ModRepositories/NexusRepository.cs
+++ b/src/StardewModdingAPI.Web/Framework/ModRepositories/NexusRepository.cs
@@ -7,26 +7,19 @@ using StardewModdingAPI.Models;
namespace StardewModdingAPI.Web.Framework.ModRepositories
{
/// <summary>An HTTP client for fetching mod metadata from Nexus Mods.</summary>
- internal class NexusRepository : IModRepository
+ internal class NexusRepository : RepositoryBase
{
/*********
** Properties
*********/
+ /// <summary>The URL for a Nexus Mods API query excluding the base URL, where {0} is the mod ID.</summary>
+ private readonly string ModUrlFormat;
+
/// <summary>The underlying HTTP client.</summary>
private readonly IClient Client;
/*********
- ** Accessors
- *********/
- /// <summary>The unique key for this vendor.</summary>
- public string VendorKey { get; }
-
- /// <summary>The URL for a Nexus Mods API query excluding the base URL, where {0} is the mod ID.</summary>
- public string ModUrlFormat { get; }
-
-
- /*********
** Public methods
*********/
/// <summary>Construct an instance.</summary>
@@ -35,15 +28,15 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
/// <param name="baseUrl">The base URL for the Nexus Mods API.</param>
/// <param name="modUrlFormat">The URL for a Nexus Mods API query excluding the <paramref name="baseUrl"/>, where {0} is the mod ID.</param>
public NexusRepository(string vendorKey, string userAgent, string baseUrl, string modUrlFormat)
+ : base(vendorKey)
{
- this.VendorKey = vendorKey;
this.ModUrlFormat = modUrlFormat;
this.Client = new FluentClient(baseUrl).SetUserAgent(userAgent);
}
/// <summary>Get metadata about a mod in the repository.</summary>
/// <param name="id">The mod ID in this repository.</param>
- public async Task<ModInfoModel> GetModInfoAsync(string id)
+ public override async Task<ModInfoModel> GetModInfoAsync(string id)
{
try
{
@@ -52,7 +45,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
.As<NexusResponseModel>();
return response != null
- ? new ModInfoModel(response.Name, response.Version, response.Url)
+ ? new ModInfoModel(response.Name, this.NormaliseVersion(response.Version), response.Url)
: new ModInfoModel("Found no mod with this ID.");
}
catch (Exception ex)
@@ -62,7 +55,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
}
/// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
- public void Dispose()
+ public override void Dispose()
{
this.Client.Dispose();
}