summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/SMAPI.Common/Models/ModInfoModel.cs12
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs50
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs4
-rw-r--r--src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs5
-rw-r--r--src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs7
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs2
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs19
-rw-r--r--src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs2
-rw-r--r--src/SMAPI.Web/Startup.cs3
-rw-r--r--src/SMAPI.Web/appsettings.json3
10 files changed, 78 insertions, 29 deletions
diff --git a/src/SMAPI.Common/Models/ModInfoModel.cs b/src/SMAPI.Common/Models/ModInfoModel.cs
index 48305cb8..48df235a 100644
--- a/src/SMAPI.Common/Models/ModInfoModel.cs
+++ b/src/SMAPI.Common/Models/ModInfoModel.cs
@@ -9,9 +9,12 @@ namespace StardewModdingAPI.Common.Models
/// <summary>The mod name.</summary>
public string Name { get; set; }
- /// <summary>The mod's semantic version number.</summary>
+ /// <summary>The semantic version for the mod's latest release.</summary>
public string Version { get; set; }
+ /// <summary>The semantic version for the mod's latest preview release, if available and different from <see cref="Version"/>.</summary>
+ public string PreviewVersion { get; set; }
+
/// <summary>The mod's web URL.</summary>
public string Url { get; set; }
@@ -28,16 +31,17 @@ namespace StardewModdingAPI.Common.Models
// needed for JSON deserialising
}
-
/// <summary>Construct an instance.</summary>
/// <param name="name">The mod name.</param>
- /// <param name="version">The mod's semantic version number.</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 error = null)
+ public ModInfoModel(string name, string version, string url, string previewVersion = null, string error = null)
{
this.Name = name;
this.Version = version;
+ this.PreviewVersion = previewVersion;
this.Url = url;
this.Error = error; // mainly initialised here for the JSON deserialiser
}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
index 0b205660..4abe0737 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitHubClient.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Pathoschild.Http.Client;
@@ -11,8 +12,11 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
/*********
** Properties
*********/
- /// <summary>The URL for a GitHub releases API query excluding the base URL, where {0} is the repository owner and name.</summary>
- private readonly string ReleaseUrlFormat;
+ /// <summary>The URL for a GitHub API query for the latest stable release, excluding the base URL, where {0} is the organisation and project name.</summary>
+ private readonly string StableReleaseUrlFormat;
+
+ /// <summary>The URL for a GitHub API query for the latest release (including prerelease), excluding the base URL, where {0} is the organisation and project name.</summary>
+ private readonly string AnyReleaseUrlFormat;
/// <summary>The underlying HTTP client.</summary>
private readonly IClient Client;
@@ -23,14 +27,16 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
*********/
/// <summary>Construct an instance.</summary>
/// <param name="baseUrl">The base URL for the GitHub API.</param>
- /// <param name="releaseUrlFormat">The URL for a GitHub releases API query excluding the <paramref name="baseUrl"/>, where {0} is the repository owner and name.</param>
+ /// <param name="stableReleaseUrlFormat">The URL for a GitHub API query for the latest stable release, excluding the <paramref name="baseUrl"/>, where {0} is the organisation and project name.</param>
+ /// <param name="anyReleaseUrlFormat">The URL for a GitHub API query for the latest release (including prerelease), excluding the <paramref name="baseUrl"/>, where {0} is the organisation and project name.</param>
/// <param name="userAgent">The user agent for the API client.</param>
/// <param name="acceptHeader">The Accept header value expected by the GitHub API.</param>
/// <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 GitHubClient(string baseUrl, string releaseUrlFormat, string userAgent, string acceptHeader, string username, string password)
+ public GitHubClient(string baseUrl, string stableReleaseUrlFormat, string anyReleaseUrlFormat, string userAgent, string acceptHeader, string username, string password)
{
- this.ReleaseUrlFormat = releaseUrlFormat;
+ this.StableReleaseUrlFormat = stableReleaseUrlFormat;
+ this.AnyReleaseUrlFormat = anyReleaseUrlFormat;
this.Client = new FluentClient(baseUrl)
.SetUserAgent(userAgent)
@@ -41,18 +47,23 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
/// <summary>Get the latest release for a GitHub repository.</summary>
/// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param>
- /// <returns>Returns the latest release if found, else <c>null</c>.</returns>
- public async Task<GitRelease> GetLatestReleaseAsync(string repo)
+ /// <param name="includePrerelease">Whether to return a prerelease version if it's latest.</param>
+ /// <returns>Returns the release if found, else <c>null</c>.</returns>
+ public async Task<GitRelease> GetLatestReleaseAsync(string repo, bool includePrerelease = false)
{
- // validate key format
- if (!repo.Contains("/") || repo.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != repo.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase))
- throw new ArgumentException($"The value '{repo}' isn't a valid GitHub repository key, must be a username and project name like 'Pathoschild/SMAPI'.", nameof(repo));
-
- // fetch info
+ this.AssetKeyFormat(repo);
try
{
+ if (includePrerelease)
+ {
+ GitRelease[] results = await this.Client
+ .GetAsync(string.Format(this.AnyReleaseUrlFormat, repo))
+ .AsArray<GitRelease>();
+ return results.FirstOrDefault();
+ }
+
return await this.Client
- .GetAsync(string.Format(this.ReleaseUrlFormat, repo))
+ .GetAsync(string.Format(this.StableReleaseUrlFormat, repo))
.As<GitRelease>();
}
catch (ApiException ex) when (ex.Status == HttpStatusCode.NotFound)
@@ -66,5 +77,18 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
{
this.Client?.Dispose();
}
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Assert that a repository key is formatted correctly.</summary>
+ /// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param>
+ /// <exception cref="ArgumentException">The repository key is invalid.</exception>
+ private void AssetKeyFormat(string repo)
+ {
+ if (repo == null || !repo.Contains("/") || repo.IndexOf("/", StringComparison.InvariantCultureIgnoreCase) != repo.LastIndexOf("/", StringComparison.InvariantCultureIgnoreCase))
+ throw new ArgumentException($"The value '{repo}' isn't a valid GitHub repository key, must be a username and project name like 'Pathoschild/SMAPI'.", nameof(repo));
+ }
}
}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
index b944088d..827374fb 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/GitRelease.cs
@@ -19,6 +19,10 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
/// <summary>The Markdown description for the release.</summary>
public string Body { get; set; }
+ /// <summary>Whether this is a prerelease version.</summary>
+ [JsonProperty("prerelease")]
+ public bool IsPrerelease { get; set; }
+
/// <summary>The attached files.</summary>
public GitAsset[] Assets { get; set; }
}
diff --git a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
index 6e8eadff..9519c26f 100644
--- a/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
+++ b/src/SMAPI.Web/Framework/Clients/GitHub/IGitHubClient.cs
@@ -11,7 +11,8 @@ namespace StardewModdingAPI.Web.Framework.Clients.GitHub
*********/
/// <summary>Get the latest release for a GitHub repository.</summary>
/// <param name="repo">The repository key (like <c>Pathoschild/SMAPI</c>).</param>
- /// <returns>Returns the latest release if found, else <c>null</c>.</returns>
- Task<GitRelease> GetLatestReleaseAsync(string repo);
+ /// <param name="includePrerelease">Whether to return a prerelease version if it's latest.</param>
+ /// <returns>Returns the release if found, else <c>null</c>.</returns>
+ Task<GitRelease> GetLatestReleaseAsync(string repo, bool includePrerelease = false);
}
}
diff --git a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
index 61219414..de6c024a 100644
--- a/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
+++ b/src/SMAPI.Web/Framework/ConfigModels/ApiClientsConfig.cs
@@ -29,8 +29,11 @@ namespace StardewModdingAPI.Web.Framework.ConfigModels
/// <summary>The base URL for the GitHub API.</summary>
public string GitHubBaseUrl { get; set; }
- /// <summary>The URL for a GitHub API latest-release query excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary>
- public string GitHubReleaseUrlFormat { get; set; }
+ /// <summary>The URL for a GitHub API query for the latest stable release, excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary>
+ public string GitHubStableReleaseUrlFormat { get; set; }
+
+ /// <summary>The URL for a GitHub API query for the latest release (including prerelease), excluding the <see cref="GitHubBaseUrl"/>, where {0} is the organisation and project name.</summary>
+ public string GitHubAnyReleaseUrlFormat { get; set; }
/// <summary>The Accept header value expected by the GitHub API.</summary>
public string GitHubAcceptHeader { get; set; }
diff --git a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
index 266055a6..3e5a4272 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/ChucklefishRepository.cs
@@ -43,7 +43,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
return new ModInfoModel("Found no mod with this ID.");
// create model
- return new ModInfoModel(mod.Name, this.NormaliseVersion(mod.Version), mod.Url);
+ return new ModInfoModel(name: mod.Name, version: this.NormaliseVersion(mod.Version), url: mod.Url);
}
catch (Exception ex)
{
diff --git a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
index 7bad6127..59eb8cd1 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/GitHubRepository.cs
@@ -38,10 +38,21 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
// fetch info
try
{
- GitRelease release = await this.Client.GetLatestReleaseAsync(id);
- return release != null
- ? new ModInfoModel(id, this.NormaliseVersion(release.Tag), $"https://github.com/{id}/releases")
- : new ModInfoModel("Found no mod with this ID.");
+ // get latest release
+ GitRelease latest = await this.Client.GetLatestReleaseAsync(id, includePrerelease: true);
+ GitRelease preview = null;
+ if (latest == null)
+ return new ModInfoModel("Found no mod with this ID.");
+
+ // get latest stable release (if not latest)
+ if (latest.IsPrerelease)
+ {
+ preview = latest;
+ latest = await this.Client.GetLatestReleaseAsync(id, includePrerelease: false);
+ }
+
+ // return data
+ return new ModInfoModel(name: id, version: this.NormaliseVersion(latest?.Tag), previewVersion: this.NormaliseVersion(preview?.Tag), url: $"https://github.com/{id}/releases");
}
catch (Exception ex)
{
diff --git a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
index e1dc0fcc..6411ad4c 100644
--- a/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
+++ b/src/SMAPI.Web/Framework/ModRepositories/NexusRepository.cs
@@ -43,7 +43,7 @@ namespace StardewModdingAPI.Web.Framework.ModRepositories
return new ModInfoModel("Found no mod with this ID.");
if (mod.Error != null)
return new ModInfoModel(mod.Error);
- return new ModInfoModel(mod.Name, this.NormaliseVersion(mod.Version), mod.Url);
+ return new ModInfoModel(name: mod.Name, version: this.NormaliseVersion(mod.Version), url: mod.Url);
}
catch (Exception ex)
{
diff --git a/src/SMAPI.Web/Startup.cs b/src/SMAPI.Web/Startup.cs
index d7d4d074..47102e5c 100644
--- a/src/SMAPI.Web/Startup.cs
+++ b/src/SMAPI.Web/Startup.cs
@@ -74,7 +74,8 @@ namespace StardewModdingAPI.Web
services.AddSingleton<IGitHubClient>(new GitHubClient(
baseUrl: api.GitHubBaseUrl,
- releaseUrlFormat: api.GitHubReleaseUrlFormat,
+ stableReleaseUrlFormat: api.GitHubStableReleaseUrlFormat,
+ anyReleaseUrlFormat: api.GitHubAnyReleaseUrlFormat,
userAgent: userAgent,
acceptHeader: api.GitHubAcceptHeader,
username: api.GitHubUsername,
diff --git a/src/SMAPI.Web/appsettings.json b/src/SMAPI.Web/appsettings.json
index 3cf72ddb..bfe827fa 100644
--- a/src/SMAPI.Web/appsettings.json
+++ b/src/SMAPI.Web/appsettings.json
@@ -24,7 +24,8 @@
"ChucklefishModPageUrlFormat": "resources/{0}",
"GitHubBaseUrl": "https://api.github.com",
- "GitHubReleaseUrlFormat": "repos/{0}/releases/latest",
+ "GitHubStableReleaseUrlFormat": "repos/{0}/releases/latest",
+ "GitHubAnyReleaseUrlFormat": "repos/{0}/releases?per_page=1",
"GitHubAcceptHeader": "application/vnd.github.v3+json",
"GitHubUsername": null, // see top note
"GitHubPassword": null, // see top note