summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web/Framework/Clients/UpdateManifest
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Web/Framework/Clients/UpdateManifest')
-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
8 files changed, 239 insertions, 0 deletions
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;
+ }
+ }
+}
+