rewrite to make update-check logic vendor-agnostic (#336)
This commit is contained in:
parent
edbc3ef3c0
commit
2c02dfe45a
|
@ -1,7 +1,9 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using StardewModdingAPI.Web.Framework;
|
||||
using StardewModdingAPI.Web.Framework.ModRepositories;
|
||||
using StardewModdingAPI.Web.Models;
|
||||
|
||||
namespace StardewModdingAPI.Web.Controllers
|
||||
|
@ -11,28 +13,75 @@ namespace StardewModdingAPI.Web.Controllers
|
|||
[Route("api/check")]
|
||||
public class CheckController : Controller
|
||||
{
|
||||
/*********
|
||||
** Properties
|
||||
*********/
|
||||
/// <summary>The mod repositories which provide mod metadata.</summary>
|
||||
private readonly IDictionary<string, IModRepository> Repositories =
|
||||
new IModRepository[]
|
||||
{
|
||||
new NexusRepository()
|
||||
}
|
||||
.ToDictionary(p => p.VendorKey, StringComparer.CurrentCultureIgnoreCase);
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Fetch version metadata for the given mods.</summary>
|
||||
/// <param name="mods">The mods for which to fetch update metadata.</param>
|
||||
/// <param name="search">The mod update search criteria.</param>
|
||||
[HttpPost]
|
||||
public async Task<ModGenericModel[]> Post([FromBody] ModSearchModel[] mods)
|
||||
public async Task<ModGenericModel[]> Post([FromBody] ModSearchModel search)
|
||||
{
|
||||
using (NexusModsClient client = new NexusModsClient())
|
||||
{
|
||||
List<ModGenericModel> result = new List<ModGenericModel>();
|
||||
IList<ModGenericModel> result = new List<ModGenericModel>();
|
||||
|
||||
foreach (ModSearchModel mod in mods)
|
||||
foreach (string modKey in search.ModKeys)
|
||||
{
|
||||
// parse mod key
|
||||
if (!this.TryParseModKey(modKey, out string vendorKey, out string modID))
|
||||
{
|
||||
if (mod.NexusID.HasValue)
|
||||
result.Add(await client.GetModInfoAsync(mod.NexusID.Value));
|
||||
else
|
||||
result.Add(new ModGenericModel(null, mod.NexusID ?? 0));
|
||||
result.Add(new ModGenericModel(modKey, "The mod key isn't in a valid format. It should contain the mod repository key and mod ID like 'Nexus:541'."));
|
||||
continue;
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
// get matching repository
|
||||
if (!this.Repositories.TryGetValue(vendorKey, out IModRepository repository))
|
||||
{
|
||||
result.Add(new ModGenericModel(modKey, "There's no mod repository matching this namespaced mod ID."));
|
||||
continue;
|
||||
}
|
||||
|
||||
// fetch mod info
|
||||
result.Add(await repository.GetModInfoAsync(modID));
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
|
||||
/*********
|
||||
** Private methods
|
||||
*********/
|
||||
/// <summary>Parse a namespaced mod ID.</summary>
|
||||
/// <param name="raw">The raw mod ID to parse.</param>
|
||||
/// <param name="vendorKey">The parsed vendor key.</param>
|
||||
/// <param name="modID">The parsed mod ID.</param>
|
||||
/// <returns>Returns whether the value could be parsed.</returns>
|
||||
private bool TryParseModKey(string raw, out string vendorKey, out string modID)
|
||||
{
|
||||
// split parts
|
||||
string[] parts = raw?.Split(':');
|
||||
if (parts == null || parts.Length != 2)
|
||||
{
|
||||
vendorKey = null;
|
||||
modID = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse
|
||||
vendorKey = parts[0];
|
||||
modID = parts[1];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,16 +2,23 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using StardewModdingAPI.Web.Models;
|
||||
|
||||
namespace StardewModdingAPI.Web.Framework
|
||||
namespace StardewModdingAPI.Web.Framework.ModRepositories
|
||||
{
|
||||
/// <summary>A repository which provides mod metadata.</summary>
|
||||
internal interface IModRepository : IDisposable
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The unique key for this vendor.</summary>
|
||||
string VendorKey { get; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Get metadata about a mod in the repository.</summary>
|
||||
/// <param name="id">The mod ID in this repository.</param>
|
||||
Task<ModGenericModel> GetModInfoAsync(int id);
|
||||
Task<ModGenericModel> GetModInfoAsync(string id);
|
||||
}
|
||||
}
|
|
@ -4,10 +4,10 @@ using Newtonsoft.Json;
|
|||
using Pathoschild.Http.Client;
|
||||
using StardewModdingAPI.Web.Models;
|
||||
|
||||
namespace StardewModdingAPI.Web.Framework
|
||||
namespace StardewModdingAPI.Web.Framework.ModRepositories
|
||||
{
|
||||
/// <summary>An HTTP client for fetching mod metadata from Nexus Mods.</summary>
|
||||
internal class NexusModsClient : IModRepository
|
||||
internal class NexusRepository : IModRepository
|
||||
{
|
||||
/*********
|
||||
** Properties
|
||||
|
@ -15,11 +15,19 @@ namespace StardewModdingAPI.Web.Framework
|
|||
/// <summary>The underlying HTTP client.</summary>
|
||||
private readonly IClient Client;
|
||||
|
||||
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The unique key for this vendor.</summary>
|
||||
public string VendorKey { get; } = "Nexus";
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
public NexusModsClient()
|
||||
public NexusRepository()
|
||||
{
|
||||
this.Client = new FluentClient("http://www.nexusmods.com/stardewvalley")
|
||||
.SetUserAgent("Nexus Client v0.63.15");
|
||||
|
@ -27,18 +35,18 @@ namespace StardewModdingAPI.Web.Framework
|
|||
|
||||
/// <summary>Get metadata about a mod in the repository.</summary>
|
||||
/// <param name="id">The mod ID in this repository.</param>
|
||||
public async Task<ModGenericModel> GetModInfoAsync(int id)
|
||||
public async Task<ModGenericModel> GetModInfoAsync(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
NexusResponseModel response = await this.Client
|
||||
.GetAsync($"mods/{id}")
|
||||
.As<NexusResponseModel>();
|
||||
return new ModGenericModel("Nexus", id, response.Name, response.Version, response.Url);
|
||||
return new ModGenericModel($"{this.VendorKey}:{id}", response.Name, response.Version, response.Url);
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ModGenericModel("Nexus", id);
|
||||
return new ModGenericModel($"{this.VendorKey}:{id}", ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,9 +66,6 @@ namespace StardewModdingAPI.Web.Framework
|
|||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The unique mod ID.</summary>
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <summary>The mod name.</summary>
|
||||
public string Name { get; set; }
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
using Newtonsoft.Json;
|
||||
|
||||
namespace StardewModdingAPI.Web.Models
|
||||
{
|
||||
/// <summary>Generic metadata about a mod.</summary>
|
||||
|
@ -6,53 +8,48 @@ namespace StardewModdingAPI.Web.Models
|
|||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>The unique mod ID.</summary>
|
||||
public int ID { get; }
|
||||
/// <summary>The namespaced mod key.</summary>
|
||||
public string ModKey { get; }
|
||||
|
||||
/// <summary>The mod name.</summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>The mod's vendor ID.</summary>
|
||||
public string Vendor { get; }
|
||||
|
||||
/// <summary>The mod's semantic version number.</summary>
|
||||
public string Version { get; }
|
||||
|
||||
/// <summary>The mod's web URL.</summary>
|
||||
public string Url { get; }
|
||||
|
||||
/// <summary>Whether the mod is valid.</summary>
|
||||
public bool Valid { get; }
|
||||
/// <summary>The error message indicating why the mod is invalid (if applicable).</summary>
|
||||
public string Error { get; }
|
||||
|
||||
|
||||
/*********
|
||||
** Public methods
|
||||
*********/
|
||||
/// <summary>Construct a valid instance.</summary>
|
||||
/// <param name="vendor">The mod's vendor ID.</param>
|
||||
/// <param name="id">The unique mod ID.</param>
|
||||
/// <param name="modKey">The namespaced mod key.</param>
|
||||
/// <param name="name">The mod name.</param>
|
||||
/// <param name="version">The mod's semantic version number.</param>
|
||||
/// <param name="url">The mod's web URL.</param>
|
||||
/// <param name="valid">Whether the mod is valid.</param>
|
||||
public ModGenericModel(string vendor, int id, string name, string version, string url, bool valid = true)
|
||||
/// <param name="error">The error message indicating why the mod is invalid (if applicable).</param>
|
||||
[JsonConstructor]
|
||||
public ModGenericModel(string modKey, string name, string version, string url, string error = null)
|
||||
{
|
||||
this.Vendor = vendor;
|
||||
this.ID = id;
|
||||
this.ModKey = modKey;
|
||||
this.Name = name;
|
||||
this.Version = version;
|
||||
this.Url = url;
|
||||
this.Valid = valid;
|
||||
this.Error = error; // mainly initialised here for the JSON deserialiser
|
||||
}
|
||||
|
||||
/// <summary>Construct an valid instance.</summary>
|
||||
/// <param name="vendor">The mod's vendor ID.</param>
|
||||
/// <param name="id">The unique mod ID.</param>
|
||||
public ModGenericModel(string vendor, int id)
|
||||
/// <param name="modKey">The namespaced mod key.</param>
|
||||
/// <param name="error">The error message indicating why the mod is invalid.</param>
|
||||
public ModGenericModel(string modKey, string error)
|
||||
{
|
||||
this.Vendor = vendor;
|
||||
this.ID = id;
|
||||
this.Valid = false;
|
||||
this.ModKey = modKey;
|
||||
this.Error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
namespace StardewModdingAPI.Web.Models
|
||||
{
|
||||
/// <summary>The search criteria for a mod.</summary>
|
||||
/// <summary>The mod update search criteria.</summary>
|
||||
public class ModSearchModel
|
||||
{
|
||||
/// <summary>The Nexus Mods ID (if any).</summary>
|
||||
public int? NexusID { get; set; }
|
||||
/// <summary>The namespaced mod keys to search.</summary>
|
||||
public string[] ModKeys { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue