summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web/Framework/Clients
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Web/Framework/Clients')
-rw-r--r--src/SMAPI.Web/Framework/Clients/GenericModDownload.cs15
-rw-r--r--src/SMAPI.Web/Framework/Clients/GenericModPage.cs16
-rw-r--r--src/SMAPI.Web/Framework/Clients/UpdateManifest/IUpdateManifestClient.cs9
-rw-r--r--src/SMAPI.Web/Framework/Clients/UpdateManifest/TextAsJsonMediaTypeFormatter.cs15
-rw-r--r--src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestClient.cs55
-rw-r--r--src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModDownload.cs27
-rw-r--r--src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModModel.cs26
-rw-r--r--src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModPage.cs58
-rw-r--r--src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModel.cs23
-rw-r--r--src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestVersionModel.cs26
10 files changed, 265 insertions, 5 deletions
diff --git a/src/SMAPI.Web/Framework/Clients/GenericModDownload.cs b/src/SMAPI.Web/Framework/Clients/GenericModDownload.cs
index b37e5cda..5cc03aba 100644
--- a/src/SMAPI.Web/Framework/Clients/GenericModDownload.cs
+++ b/src/SMAPI.Web/Framework/Clients/GenericModDownload.cs
@@ -17,6 +17,10 @@ namespace StardewModdingAPI.Web.Framework.Clients
/// <summary>The download's file version.</summary>
public string? Version { get; }
+ /// <summary>
+ /// The URL for this download, if it has one distinct from the mod page's URL.
+ /// </summary>
+ public string? Url { get; }
/*********
** Public methods
@@ -25,21 +29,22 @@ namespace StardewModdingAPI.Web.Framework.Clients
/// <param name="name">The download's display name.</param>
/// <param name="description">The download's description.</param>
/// <param name="version">The download's file version.</param>
- public GenericModDownload(string name, string? description, string? version)
+ /// <param name="url">The download's URL (if different from the mod page's URL).</param>
+ public GenericModDownload(string name, string? description, string? version, string? url = null)
{
this.Name = name;
this.Description = description;
this.Version = version;
+ this.Url = url;
}
/// <summary>
- /// Return true if the subkey matches this download. A subkey matches if it appears as
+ /// Return <see langword="true"/> if the subkey matches this download. A subkey matches if it appears as
/// a substring in the name or description.
/// </summary>
/// <param name="subkey">the subkey</param>
- /// <returns><c>true</c> if <paramref name="subkey"/> matches this download, otherwise <c>false</c></returns>
- /// <exception cref="System.NotImplementedException"></exception>
- public bool MatchesSubkey(string subkey) {
+ /// <returns><see langword="true"/> if <paramref name="subkey"/> matches this download, otherwise <see langword="false"/></returns>
+ public virtual bool MatchesSubkey(string subkey) {
return this.Name.Contains(subkey, StringComparison.OrdinalIgnoreCase) == true
|| this.Description?.Contains(subkey, StringComparison.OrdinalIgnoreCase) == true;
}
diff --git a/src/SMAPI.Web/Framework/Clients/GenericModPage.cs b/src/SMAPI.Web/Framework/Clients/GenericModPage.cs
index 5353c7e1..4c66e1a0 100644
--- a/src/SMAPI.Web/Framework/Clients/GenericModPage.cs
+++ b/src/SMAPI.Web/Framework/Clients/GenericModPage.cs
@@ -40,6 +40,8 @@ namespace StardewModdingAPI.Web.Framework.Clients
[MemberNotNullWhen(true, nameof(IModPage.Name), nameof(IModPage.Url))]
public bool IsValid => this.Status == RemoteModStatus.Ok;
+ /// <summary>Whether to use strict subkey matching or not.</summary>
+ public bool IsSubkeyStrict { get; set; } = false;
/*********
** Public methods
@@ -79,5 +81,19 @@ namespace StardewModdingAPI.Web.Framework.Clients
return this;
}
+
+ /// <summary>Returns the mod page name.</summary>
+ /// <param name="subkey">ignored</param>
+ /// <returns>The mod page name.</returns>
+ public virtual string? GetName(string? subkey) {
+ return this.Name;
+ }
+
+ /// <summary>Returns the mod page URL.</summary>
+ /// <param name="subkey">ignored</param>
+ /// <returns>The mod page URL.</returns>
+ public virtual string? GetUrl(string? subkey) {
+ return this.Url;
+ }
}
}
diff --git a/src/SMAPI.Web/Framework/Clients/UpdateManifest/IUpdateManifestClient.cs b/src/SMAPI.Web/Framework/Clients/UpdateManifest/IUpdateManifestClient.cs
new file mode 100644
index 00000000..36d018c7
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/UpdateManifest/IUpdateManifestClient.cs
@@ -0,0 +1,9 @@
+// Copyright 2022 Jamie Taylor
+using System;
+
+namespace StardewModdingAPI.Web.Framework.Clients.UpdateManifest
+{
+ /// <summary>An HTTP client for fetching an update manifest from an arbitrary URL.</summary>
+ internal interface IUpdateManifestClient : IModSiteClient, IDisposable { }
+}
+
diff --git a/src/SMAPI.Web/Framework/Clients/UpdateManifest/TextAsJsonMediaTypeFormatter.cs b/src/SMAPI.Web/Framework/Clients/UpdateManifest/TextAsJsonMediaTypeFormatter.cs
new file mode 100644
index 00000000..48e3c294
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/UpdateManifest/TextAsJsonMediaTypeFormatter.cs
@@ -0,0 +1,15 @@
+// Copyright 2022 Jamie Taylor
+using System.Net.Http.Formatting;
+using System.Net.Http.Headers;
+
+namespace StardewModdingAPI.Web.Framework.Clients.UpdateManifest {
+ /// <summary>
+ /// A <see cref="JsonMediaTypeFormatter"/> that can parse from content of type <c>text/plain</c>.
+ /// </summary>
+ internal class TextAsJsonMediaTypeFormatter : JsonMediaTypeFormatter {
+ /// <summary>Construct a new <see cref="JsonMediaTypeFormatter"/></summary>
+ public TextAsJsonMediaTypeFormatter() {
+ this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestClient.cs b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestClient.cs
new file mode 100644
index 00000000..d7cf4945
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestClient.cs
@@ -0,0 +1,55 @@
+// Copyright 2022 Jamie Taylor
+using System;
+using Pathoschild.Http.Client;
+using StardewModdingAPI.Toolkit.Framework.UpdateData;
+using System.Threading.Tasks;
+using System.Net;
+
+namespace StardewModdingAPI.Web.Framework.Clients.UpdateManifest {
+ /// <summary>An HTTP client for fetching an update manifest from an arbitrary URL.</summary>
+ internal class UpdateManifestClient : IUpdateManifestClient {
+ /*********
+ ** Fields
+ *********/
+ /// <summary>The underlying HTTP client.</summary>
+ private readonly IClient Client;
+
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The unique key for the mod site.</summary>
+ public ModSiteKey SiteKey => ModSiteKey.UpdateManifest;
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="userAgent">The user agent for the API client.</param>
+ public UpdateManifestClient(string userAgent) {
+ this.Client = new FluentClient()
+ .SetUserAgent(userAgent);
+ this.Client.Formatters.Add(new TextAsJsonMediaTypeFormatter());
+ }
+
+ /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
+ public void Dispose() {
+ this.Client.Dispose();
+ }
+
+ /// <inheritdoc/>
+ public async Task<IModPage?> GetModData(string id) {
+ UpdateManifestModel? manifest;
+ try {
+ manifest = await this.Client.GetAsync(id).As<UpdateManifestModel?>();
+ } catch (ApiException ex) when (ex.Status == HttpStatusCode.NotFound) {
+ return new GenericModPage(this.SiteKey, id).SetError(RemoteModStatus.DoesNotExist, $"No update manifest found at {id}");
+ }
+ if (manifest is null) {
+ return new GenericModPage(this.SiteKey, id).SetError(RemoteModStatus.DoesNotExist, $"Error parsing manifest at {id}");
+ }
+
+ return new UpdateManifestModPage(id, manifest);
+ }
+ }
+}
diff --git a/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModDownload.cs b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModDownload.cs
new file mode 100644
index 00000000..117ae15c
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModDownload.cs
@@ -0,0 +1,27 @@
+// Copyright 2022 Jamie Taylor
+using System;
+namespace StardewModdingAPI.Web.Framework.Clients.UpdateManifest {
+ /// <summary>Metadata about a mod download in an update manifest file.</summary>
+ internal class UpdateManifestModDownload : GenericModDownload {
+ /// <summary>The subkey for this mod download</summary>
+ private readonly string subkey;
+ /// <summary>Construct an instance.</summary>
+ /// <param name="subkey">The subkey for this download.</param>
+ /// <param name="name">The mod name for this download.</param>
+ /// <param name="version">The download's version.</param>
+ /// <param name="url">The download's URL.</param>
+ public UpdateManifestModDownload(string subkey, string name, string? version, string? url) : base(name, null, version, url) {
+ this.subkey = subkey;
+ }
+
+ /// <summary>
+ /// Returns <see langword="true"/> iff the given subkey is the same as the subkey for this download.
+ /// </summary>
+ /// <param name="subkey">The subkey to match</param>
+ /// <returns><see langword="true"/> if <paramref name="subkey"/> is the same as the subkey for this download, <see langword="false"/> otherwise.</returns>
+ public override bool MatchesSubkey(string subkey) {
+ return this.subkey == subkey;
+ }
+ }
+}
+
diff --git a/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModModel.cs b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModModel.cs
new file mode 100644
index 00000000..4ec9c03d
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModModel.cs
@@ -0,0 +1,26 @@
+// Copyright 2022 Jamie Taylor
+using System;
+namespace StardewModdingAPI.Web.Framework.Clients.UpdateManifest {
+ /// <summary>Data model for a mod in an update manifest.</summary>
+ internal class UpdateManifestModModel {
+ /// <summary>The mod's name.</summary>
+ public string Name { get; }
+
+ /// <summary>The mod's URL.</summary>
+ public string? Url { get; }
+
+ /// <summary>The versions for this mod.</summary>
+ public UpdateManifestVersionModel[] Versions { get; }
+
+ /// <summary>Construct an instance.</summary>
+ /// <param name="name">The mod's name.</param>
+ /// <param name="url">The mod's URL.</param>
+ /// <param name="versions">The versions for this mod.</param>
+ public UpdateManifestModModel(string name, string? url, UpdateManifestVersionModel[] versions) {
+ this.Name = name;
+ this.Url = url;
+ this.Versions = versions;
+ }
+ }
+}
+
diff --git a/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModPage.cs b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModPage.cs
new file mode 100644
index 00000000..109175b5
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModPage.cs
@@ -0,0 +1,58 @@
+// Copyright 2022 Jamie Taylor
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using StardewModdingAPI.Toolkit.Framework.UpdateData;
+
+namespace StardewModdingAPI.Web.Framework.Clients.UpdateManifest {
+ /// <summary>Metadata about an update manifest "page".</summary>
+ internal class UpdateManifestModPage : GenericModPage {
+ /// <summary>The update manifest model.</summary>
+ private UpdateManifestModel manifest;
+
+ /// <summary>Constuct an instance.</summary>
+ /// <param name="id">The "id" (i.e., URL) of this update manifest.</param>
+ /// <param name="manifest">The manifest object model.</param>
+ public UpdateManifestModPage(string id, UpdateManifestModel manifest) : base(ModSiteKey.UpdateManifest, id) {
+ this.IsSubkeyStrict = true;
+ this.manifest = manifest;
+ this.SetInfo(name: id, url: id, version: null, downloads: TranslateDownloads(manifest).ToArray());
+ }
+
+ /// <summary>Return the mod name for the given subkey, if it exists in this update manifest.</summary>
+ /// <param name="subkey">The subkey.</param>
+ /// <returns>The mod name for the given subkey, or <see langword="null"/> if this manifest does not contain the given subkey.</returns>
+ public override string? GetName(string? subkey) {
+ if (subkey is null)
+ return null;
+ this.manifest.Subkeys.TryGetValue(subkey, out UpdateManifestModModel? modModel);
+ return modModel?.Name;
+ }
+
+ /// <summary>Return the mod URL for the given subkey, if it exists in this update manifest.</summary>
+ /// <param name="subkey">The subkey.</param>
+ /// <returns>The mod URL for the given subkey, or <see langword="null"/> if this manifest does not contain the given subkey.</returns>
+ public override string? GetUrl(string? subkey) {
+ if (subkey is null)
+ return null;
+ this.manifest.Subkeys.TryGetValue(subkey, out UpdateManifestModModel? modModel);
+ return modModel?.Url;
+ }
+
+
+ /*********
+ ** Private methods
+ *********/
+ /// <summary>Translate the downloads from the manifest's object model into <see cref="IModDownload"/> objects.</summary>
+ /// <param name="manifest">The manifest object model.</param>
+ /// <returns>An <see cref="IModDownload"/> for each <see cref="UpdateManifestVersionModel"/> in the manifest.</returns>
+ private static IEnumerable<IModDownload> TranslateDownloads(UpdateManifestModel manifest) {
+ foreach (var entry in manifest.Subkeys) {
+ foreach (var version in entry.Value.Versions) {
+ yield return new UpdateManifestModDownload(entry.Key, entry.Value.Name, version.Version, version.DownloadFileUrl ?? version.DownloadPageUrl);
+ }
+ }
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModel.cs b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModel.cs
new file mode 100644
index 00000000..03f89726
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestModel.cs
@@ -0,0 +1,23 @@
+// Copyright 2022 Jamie Taylor
+using System;
+using System.Collections.Generic;
+
+namespace StardewModdingAPI.Web.Framework.Clients.UpdateManifest {
+ /// <summary>Data model for an update manifest.</summary>
+ internal class UpdateManifestModel {
+ /// <summary>The manifest format version.</summary>
+ public string ManifestVersion { get; }
+
+ /// <summary>The subkeys in this update manifest.</summary>
+ public IDictionary<string, UpdateManifestModModel> Subkeys { get; }
+
+ /// <summary>Construct an instance.</summary>
+ /// <param name="manifestVersion">The manifest format version.</param>
+ /// <param name="subkeys">The subkeys in this update manifest.</param>
+ public UpdateManifestModel(string manifestVersion, IDictionary<string, UpdateManifestModModel> subkeys) {
+ this.ManifestVersion = manifestVersion;
+ this.Subkeys = subkeys;
+ }
+ }
+}
+
diff --git a/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestVersionModel.cs b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestVersionModel.cs
new file mode 100644
index 00000000..55b6db61
--- /dev/null
+++ b/src/SMAPI.Web/Framework/Clients/UpdateManifest/UpdateManifestVersionModel.cs
@@ -0,0 +1,26 @@
+// Copyright 2022 Jamie Taylor
+using System;
+namespace StardewModdingAPI.Web.Framework.Clients.UpdateManifest {
+ /// <summary>Data model for a Version in an update manifest.</summary>
+ internal class UpdateManifestVersionModel {
+ /// <summary>The semantic version string.</summary>
+ public string Version { get; }
+
+ /// <summary>The URL for this version's download page (if any).</summary>
+ public string? DownloadPageUrl { get; }
+
+ /// <summary>The URL for this version's direct file download (if any).</summary>
+ public string? DownloadFileUrl { get; }
+
+ /// <summary>Construct an instance.</summary>
+ /// <param name="version">The semantic version string.</param>
+ /// <param name="downloadPageUrl">This version's download page URL (if any).</param>
+ /// <param name="downloadFileUrl">This version's direct file download URL (if any).</param>
+ public UpdateManifestVersionModel(string version, string? downloadPageUrl, string? downloadFileUrl) {
+ this.Version = version;
+ this.DownloadPageUrl = downloadPageUrl;
+ this.DownloadFileUrl = downloadFileUrl;
+ }
+ }
+}
+