summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web/Framework/ModRepositories
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2019-11-24 13:49:30 -0500
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2019-11-24 13:49:30 -0500
commita3f21685049cabf2d824c8060dc0b1de47e9449e (patch)
treead9add30e9da2a50e0ea0245f1546b7378f0d282 /src/SMAPI.Web/Framework/ModRepositories
parent6521df7b131924835eb797251c1e956fae0d6e13 (diff)
parent277bf082675b98b95bf6184fe3c7a45b969c7ac2 (diff)
downloadSMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.tar.gz
SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.tar.bz2
SMAPI-a3f21685049cabf2d824c8060dc0b1de47e9449e.zip
Merge branch 'develop' into stable
Diffstat (limited to 'src/SMAPI.Web/Framework/ModRepositories')
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs6
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs12
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/CurseForgeRepository.cs63
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs24
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/ModDropRepository.cs12
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/ModInfoModel.cs66
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs16
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/RemoteModStatus.cs18
8 files changed, 175 insertions, 42 deletions
diff --git a/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs
index 94256005..f9f9f47d 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/BaseRepository.cs
@@ -34,9 +34,9 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
this.VendorKey = vendorKey;
}
- /// <summary>Normalise a version string.</summary>
- /// <param name="version">The version to normalise.</param>
- protected string NormaliseVersion(string version)
+ /// <summary>Normalize a version string.</summary>
+ /// <param name="version">The version to normalize.</param>
+ protected string NormalizeVersion(string version)
{
if (string.IsNullOrWhiteSpace(version))
return null;
diff --git a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
index 87e29a2f..0945735a 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
@@ -32,21 +32,19 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
{
// validate ID format
if (!uint.TryParse(id, out uint realID))
- return new ModInfoModel($"The value '{id}' isn't a valid Chucklefish mod ID, must be an integer ID.");
+ return new ModInfoModel().SetError(RemoteModStatus.DoesNotExist, $"The value '{id}' isn't a valid Chucklefish mod ID, must be an integer ID.");
// fetch info
try
{
var mod = await this.Client.GetModAsync(realID);
- if (mod == null)
- return new ModInfoModel("Found no mod with this ID.");
-
- // create model
- return new ModInfoModel(name: mod.Name, version: this.NormaliseVersion(mod.Version), url: mod.Url);
+ return mod != null
+ ? new ModInfoModel(name: mod.Name, version: this.NormalizeVersion(mod.Version), url: mod.Url)
+ : new ModInfoModel().SetError(RemoteModStatus.DoesNotExist, "Found no Chucklefish mod with this ID.");
}
catch (Exception ex)
{
- return new ModInfoModel(ex.ToString());
+ return new ModInfoModel().SetError(RemoteModStatus.TemporaryError, ex.ToString());
}
}
diff --git a/src/SMAPI.Web/Framework/ModRepositories/CurseForgeRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/CurseForgeRepository.cs
new file mode 100644
index 00000000..93ddc1eb
--- /dev/null
+++ b/src/SMAPI.Web/Framework/ModRepositories/CurseForgeRepository.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Threading.Tasks;
+using StardewModdingAPI.Toolkit.Framework.UpdateData;
+using StardewModdingAPI.Web.Framework.Clients.CurseForge;
+
+namespace StardewModdingAPI.Web.Framework.ModRepositories
+{
+ /// <summary>An HTTP client for fetching mod metadata from CurseForge.</summary>
+ internal class CurseForgeRepository : RepositoryBase
+ {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>The underlying CurseForge API client.</summary>
+ private readonly ICurseForgeClient Client;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="client">The underlying CurseForge API client.</param>
+ public CurseForgeRepository(ICurseForgeClient client)
+ : base(ModRepositoryKey.CurseForge)
+ {
+ this.Client = client;
+ }
+
+ /// <summary>Get metadata about a mod in the repository.</summary>
+ /// <param name="id">The mod ID in this repository.</param>
+ public override async Task<ModInfoModel> GetModInfoAsync(string id)
+ {
+ // validate ID format
+ if (!uint.TryParse(id, out uint curseID))
+ return new ModInfoModel().SetError(RemoteModStatus.DoesNotExist, $"The value '{id}' isn't a valid CurseForge mod ID, must be an integer ID.");
+
+ // fetch info
+ try
+ {
+ CurseForgeMod mod = await this.Client.GetModAsync(curseID);
+ if (mod == null)
+ return new ModInfoModel().SetError(RemoteModStatus.DoesNotExist, "Found no CurseForge mod with this ID.");
+ if (mod.Error != null)
+ {
+ RemoteModStatus remoteStatus = RemoteModStatus.InvalidData;
+ return new ModInfoModel().SetError(remoteStatus, mod.Error);
+ }
+
+ return new ModInfoModel(name: mod.Name, version: this.NormalizeVersion(mod.LatestVersion), url: mod.Url);
+ }
+ catch (Exception ex)
+ {
+ return new ModInfoModel().SetError(RemoteModStatus.TemporaryError, ex.ToString());
+ }
+ }
+
+ /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
+ public override void Dispose()
+ {
+ this.Client.Dispose();
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
index 14f44dc0..c62cb73f 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
@@ -30,36 +30,46 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
/// <param name="id">The mod ID in this repository.</param>
public override async Task<ModInfoModel> GetModInfoAsync(string id)
{
+ ModInfoModel result = new ModInfoModel().SetBasicInfo(id, $"https://github.com/{id}/releases");
+
// validate ID format
if (!id.Contains("/") || id.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != id.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase))
- return new ModInfoModel($"The value '{id}' isn't a valid GitHub mod ID, must be a username and project name like 'Pathoschild/LookupAnything'.");
+ return result.SetError(RemoteModStatus.DoesNotExist, $"The value '{id}' isn't a valid GitHub mod ID, must be a username and project name like 'Pathoschild/LookupAnything'.");
// fetch info
try
{
+ // fetch repo info
+ GitRepo repository = await this.Client.GetRepositoryAsync(id);
+ if (repository == null)
+ return result.SetError(RemoteModStatus.DoesNotExist, "Found no GitHub repository for this ID.");
+ result
+ .SetBasicInfo(repository.FullName, $"{repository.WebUrl}/releases")
+ .SetLicense(url: repository.License?.Url, name: repository.License?.SpdxId ?? repository.License?.Name);
+
// get latest release (whether preview or stable)
GitRelease latest = await this.Client.GetLatestReleaseAsync(id, includePrerelease: true);
if (latest == null)
- return new ModInfoModel("Found no mod with this ID.");
+ return result.SetError(RemoteModStatus.DoesNotExist, "Found no GitHub release for this ID.");
// split stable/prerelease if applicable
GitRelease preview = null;
if (latest.IsPrerelease)
{
- GitRelease result = await this.Client.GetLatestReleaseAsync(id, includePrerelease: false);
- if (result != null)
+ GitRelease release = await this.Client.GetLatestReleaseAsync(id, includePrerelease: false);
+ if (release != null)
{
preview = latest;
- latest = result;
+ latest = release;
}
}
// return data
- return new ModInfoModel(name: id, version: this.NormaliseVersion(latest.Tag), previewVersion: this.NormaliseVersion(preview?.Tag), url: $"https://github.com/{id}/releases");
+ return result.SetVersions(version: this.NormalizeVersion(latest.Tag), previewVersion: this.NormalizeVersion(preview?.Tag));
}
catch (Exception ex)
{
- return new ModInfoModel(ex.ToString());
+ return result.SetError(RemoteModStatus.TemporaryError, ex.ToString());
}
}
diff --git a/src/SMAPI.Web/Framework/ModRepositories/ModDropRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ModDropRepository.cs
index 1994f515..62142668 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/ModDropRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/ModDropRepository.cs
@@ -32,21 +32,19 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
{
// validate ID format
if (!long.TryParse(id, out long modDropID))
- return new ModInfoModel($"The value '{id}' isn't a valid ModDrop mod ID, must be an integer ID.");
+ return new ModInfoModel().SetError(RemoteModStatus.DoesNotExist, $"The value '{id}' isn't a valid ModDrop mod ID, must be an integer ID.");
// fetch info
try
{
ModDropMod mod = await this.Client.GetModAsync(modDropID);
- if (mod == null)
- return new ModInfoModel("Found no mod with this ID.");
- if (mod.Error != null)
- return new ModInfoModel(mod.Error);
- return new ModInfoModel(name: mod.Name, version: mod.LatestDefaultVersion?.ToString(), previewVersion: mod.LatestOptionalVersion?.ToString(), url: mod.Url);
+ return mod != null
+ ? new ModInfoModel(name: mod.Name, version: mod.LatestDefaultVersion?.ToString(), previewVersion: mod.LatestOptionalVersion?.ToString(), url: mod.Url)
+ : new ModInfoModel().SetError(RemoteModStatus.DoesNotExist, "Found no ModDrop mod with this ID.");
}
catch (Exception ex)
{
- return new ModInfoModel(ex.ToString());
+ return new ModInfoModel().SetError(RemoteModStatus.TemporaryError, ex.ToString());
}
}
diff --git a/src/SMAPI.Web/Framework/ModRepositories/ModInfoModel.cs b/src/SMAPI.Web/Framework/ModRepositories/ModInfoModel.cs
index 18252298..46b98860 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/ModInfoModel.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/ModInfoModel.cs
@@ -9,15 +9,24 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
/// <summary>The mod name.</summary>
public string Name { get; set; }
- /// <summary>The mod's latest release number.</summary>
+ /// <summary>The mod's latest version.</summary>
public string Version { get; set; }
- /// <summary>The mod's latest optional release, if newer than <see cref="Version"/>.</summary>
+ /// <summary>The mod's latest optional or prerelease version, if newer than <see cref="Version"/>.</summary>
public string PreviewVersion { get; set; }
/// <summary>The mod's web URL.</summary>
public string Url { get; set; }
+ /// <summary>The license URL, if available.</summary>
+ public string LicenseUrl { get; set; }
+
+ /// <summary>The license name, if available.</summary>
+ public string LicenseName { get; set; }
+
+ /// <summary>The mod availability status on the remote site.</summary>
+ public RemoteModStatus Status { get; set; } = RemoteModStatus.Ok;
+
/// <summary>The error message indicating why the mod is invalid (if applicable).</summary>
public string Error { get; set; }
@@ -26,31 +35,62 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
** Public methods
*********/
/// <summary>Construct an empty instance.</summary>
- public ModInfoModel()
- {
- // needed for JSON deserialising
- }
+ public ModInfoModel() { }
/// <summary>Construct an instance.</summary>
/// <param name="name">The mod name.</param>
/// <param name="version">The semantic version for the mod's latest release.</param>
/// <param name="previewVersion">The semantic version for the mod's latest preview release, if available and different from <see cref="Version"/>.</param>
/// <param name="url">The mod's web URL.</param>
- /// <param name="error">The error message indicating why the mod is invalid (if applicable).</param>
- public ModInfoModel(string name, string version, string url, string previewVersion = null, string error = null)
+ public ModInfoModel(string name, string version, string url, string previewVersion = null)
+ {
+ this
+ .SetBasicInfo(name, url)
+ .SetVersions(version, previewVersion);
+ }
+
+ /// <summary>Set the basic mod info.</summary>
+ /// <param name="name">The mod name.</param>
+ /// <param name="url">The mod's web URL.</param>
+ public ModInfoModel SetBasicInfo(string name, string url)
{
this.Name = name;
+ this.Url = url;
+
+ return this;
+ }
+
+ /// <summary>Set the mod version info.</summary>
+ /// <param name="version">The semantic version for the mod's latest release.</param>
+ /// <param name="previewVersion">The semantic version for the mod's latest preview release, if available and different from <see cref="Version"/>.</param>
+ public ModInfoModel SetVersions(string version, string previewVersion = null)
+ {
this.Version = version;
this.PreviewVersion = previewVersion;
- this.Url = url;
- this.Error = error;
+
+ return this;
}
- /// <summary>Construct an instance.</summary>
- /// <param name="error">The error message indicating why the mod is invalid.</param>
- public ModInfoModel(string error)
+ /// <summary>Set the license info, if available.</summary>
+ /// <param name="url">The license URL.</param>
+ /// <param name="name">The license name.</param>
+ public ModInfoModel SetLicense(string url, string name)
{
+ this.LicenseUrl = url;
+ this.LicenseName = name;
+
+ return this;
+ }
+
+ /// <summary>Set a mod error.</summary>
+ /// <param name="status">The mod availability status on the remote site.</param>
+ /// <param name="error">The error message indicating why the mod is invalid (if applicable).</param>
+ public ModInfoModel SetError(RemoteModStatus status, string error)
+ {
+ this.Status = status;
this.Error = error;
+
+ return this;
}
}
}
diff --git a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
index 4c5fe9bf..9551258c 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
@@ -32,21 +32,27 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
{
// validate ID format
if (!uint.TryParse(id, out uint nexusID))
- return new ModInfoModel($"The value '{id}' isn't a valid Nexus mod ID, must be an integer ID.");
+ return new ModInfoModel().SetError(RemoteModStatus.DoesNotExist, $"The value '{id}' isn't a valid Nexus mod ID, must be an integer ID.");
// fetch info
try
{
NexusMod mod = await this.Client.GetModAsync(nexusID);
if (mod == null)
- return new ModInfoModel("Found no mod with this ID.");
+ return new ModInfoModel().SetError(RemoteModStatus.DoesNotExist, "Found no Nexus mod with this ID.");
if (mod.Error != null)
- return new ModInfoModel(mod.Error);
- return new ModInfoModel(name: mod.Name, version: this.NormaliseVersion(mod.Version), previewVersion: mod.LatestFileVersion?.ToString(), url: mod.Url);
+ {
+ RemoteModStatus remoteStatus = mod.Status == NexusModStatus.Hidden || mod.Status == NexusModStatus.NotPublished
+ ? RemoteModStatus.DoesNotExist
+ : RemoteModStatus.TemporaryError;
+ return new ModInfoModel().SetError(remoteStatus, mod.Error);
+ }
+
+ return new ModInfoModel(name: mod.Name, version: this.NormalizeVersion(mod.Version), previewVersion: mod.LatestFileVersion?.ToString(), url: mod.Url);
}
catch (Exception ex)
{
- return new ModInfoModel(ex.ToString());
+ return new ModInfoModel().SetError(RemoteModStatus.TemporaryError, ex.ToString());
}
}
diff --git a/src/SMAPI.Web/Framework/ModRepositories/RemoteModStatus.cs b/src/SMAPI.Web/Framework/ModRepositories/RemoteModStatus.cs
new file mode 100644
index 00000000..02876556
--- /dev/null
+++ b/src/SMAPI.Web/Framework/ModRepositories/RemoteModStatus.cs
@@ -0,0 +1,18 @@
+namespace StardewModdingAPI.Web.Framework.ModRepositories
+{
+ /// <summary>The mod availability status on a remote site.</summary>
+ internal enum RemoteModStatus
+ {
+ /// <summary>The mod is valid.</summary>
+ Ok,
+
+ /// <summary>The mod data was fetched, but the data is not valid (e.g. version isn't semantic).</summary>
+ InvalidData,
+
+ /// <summary>The mod does not exist.</summary>
+ DoesNotExist,
+
+ /// <summary>The mod was temporarily unavailable (e.g. the site could not be reached or an unknown error occurred).</summary>
+ TemporaryError
+ }
+}