summaryrefslogtreecommitdiff
path: root/src/SMAPI.Web
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2019-08-04 18:01:05 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2019-09-14 19:00:54 -0400
commitee0ff5687d4002aab20cd91fd28d007d916af36c (patch)
tree113b44f267630804d0b0c3408800733290b5331f /src/SMAPI.Web
parentf24e7428df15ee8bcc9a2a45de98363670e72231 (diff)
downloadSMAPI-ee0ff5687d4002aab20cd91fd28d007d916af36c.tar.gz
SMAPI-ee0ff5687d4002aab20cd91fd28d007d916af36c.tar.bz2
SMAPI-ee0ff5687d4002aab20cd91fd28d007d916af36c.zip
add user-friendly doc link & error messages, document validator, improve manifest schema (#654)
Diffstat (limited to 'src/SMAPI.Web')
-rw-r--r--src/SMAPI.Web/Controllers/JsonValidatorController.cs49
-rw-r--r--src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorModel.cs3
-rw-r--r--src/SMAPI.Web/Views/JsonValidator/Index.cshtml5
-rw-r--r--src/SMAPI.Web/wwwroot/schemas/manifest.json48
4 files changed, 89 insertions, 16 deletions
diff --git a/src/SMAPI.Web/Controllers/JsonValidatorController.cs b/src/SMAPI.Web/Controllers/JsonValidatorController.cs
index 37393a98..7b755d3b 100644
--- a/src/SMAPI.Web/Controllers/JsonValidatorController.cs
+++ b/src/SMAPI.Web/Controllers/JsonValidatorController.cs
@@ -113,6 +113,9 @@ namespace StardewModdingAPI.Web.Controllers
schema = JSchema.Parse(System.IO.File.ReadAllText(schemaFile.FullName));
}
+ // get format doc URL
+ result.FormatUrl = this.GetExtensionField<string>(schema, "@documentationUrl");
+
// validate JSON
parsed.IsValid(schema, out IList<ValidationError> rawErrors);
var errors = rawErrors
@@ -172,13 +175,22 @@ namespace StardewModdingAPI.Web.Controllers
/// <param name="indent">The indentation level to apply for inner errors.</param>
private string GetFlattenedError(ValidationError error, int indent = 0)
{
+ // get override error
+ string message = this.GetOverrideError(error.Schema, error.ErrorType);
+ if (message != null)
+ return message;
+
// get friendly representation of main error
- string message = error.Message;
+ message = error.Message;
switch (error.ErrorType)
{
case ErrorType.Enum:
message = $"Invalid value. Found '{error.Value}', but expected one of '{string.Join("', '", error.Schema.Enum)}'.";
break;
+
+ case ErrorType.Required:
+ message = $"Missing required fields: {string.Join(", ", (List<string>)error.Value)}.";
+ break;
}
// add inner errors
@@ -216,5 +228,40 @@ namespace StardewModdingAPI.Web.Controllers
return null;
}
+
+ /// <summary>Get an override error from the JSON schema, if any.</summary>
+ /// <param name="schema">The schema or subschema that raised the error.</param>
+ /// <param name="errorType">The error type.</param>
+ private string GetOverrideError(JSchema schema, ErrorType errorType)
+ {
+ // get override errors
+ IDictionary<string, string> errors = this.GetExtensionField<Dictionary<string, string>>(schema, "@errorMessages");
+ if (errors == null)
+ return null;
+ errors = new Dictionary<string, string>(errors, StringComparer.InvariantCultureIgnoreCase);
+
+ // get matching error
+ return errors.TryGetValue(errorType.ToString(), out string errorPhrase)
+ ? errorPhrase
+ : null;
+ }
+
+ /// <summary>Get an extension field from a JSON schema.</summary>
+ /// <typeparam name="T">The field type.</typeparam>
+ /// <param name="schema">The schema whose extension fields to search.</param>
+ /// <param name="key">The case-insensitive field key.</param>
+ private T GetExtensionField<T>(JSchema schema, string key)
+ {
+ if (schema.ExtensionData != null)
+ {
+ foreach (var pair in schema.ExtensionData)
+ {
+ if (pair.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase))
+ return pair.Value.ToObject<T>();
+ }
+ }
+
+ return default;
+ }
}
}
diff --git a/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorModel.cs b/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorModel.cs
index 4c122d4f..2d13bf23 100644
--- a/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorModel.cs
+++ b/src/SMAPI.Web/ViewModels/JsonValidator/JsonValidatorModel.cs
@@ -33,6 +33,9 @@ namespace StardewModdingAPI.Web.ViewModels.JsonValidator
/// <summary>An error which occurred while parsing the JSON.</summary>
public string ParseError { get; set; }
+ /// <summary>A web URL to the user-facing format documentation.</summary>
+ public string FormatUrl { get; set; }
+
/*********
** Public methods
diff --git a/src/SMAPI.Web/Views/JsonValidator/Index.cshtml b/src/SMAPI.Web/Views/JsonValidator/Index.cshtml
index 6658e7b9..5c3168e5 100644
--- a/src/SMAPI.Web/Views/JsonValidator/Index.cshtml
+++ b/src/SMAPI.Web/Views/JsonValidator/Index.cshtml
@@ -95,6 +95,11 @@ else if (Model.PasteID != null)
</div>
<h2>Validation errors</h2>
+ @if (Model.FormatUrl != null)
+ {
+ <p>See <a href="@Model.FormatUrl">format documentation</a>.</p>
+ }
+
@if (Model.Errors.Any())
{
<table id="metadata" class="table">
diff --git a/src/SMAPI.Web/wwwroot/schemas/manifest.json b/src/SMAPI.Web/wwwroot/schemas/manifest.json
index 06173333..804eb53d 100644
--- a/src/SMAPI.Web/wwwroot/schemas/manifest.json
+++ b/src/SMAPI.Web/wwwroot/schemas/manifest.json
@@ -3,6 +3,7 @@
"$id": "https://smapi.io/schemas/manifest.json",
"title": "SMAPI manifest",
"description": "Manifest file for a SMAPI mod or content pack",
+ "@documentationUrl": "https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Manifest",
"type": "object",
"properties": {
"Name": {
@@ -38,7 +39,10 @@
"description": "The DLL filename SMAPI should load for this mod. Mutually exclusive with ContentPackFor.",
"type": "string",
"pattern": "^[a-zA-Z0-9_.-]+\\.dll$",
- "examples": "LookupAnything.dll"
+ "examples": "LookupAnything.dll",
+ "@errorMessages": {
+ "pattern": "Invalid value; must be a filename ending with .dll."
+ }
},
"ContentPackFor": {
"title": "Content pack for",
@@ -59,12 +63,17 @@
"required": [ "UniqueID" ]
},
+ "MinimumApiVersion": {
+ "title": "Minimum API version",
+ "description": "The minimum SMAPI version needed to use this mod. If a player tries to use the mod with an older SMAPI version, they'll see a friendly message saying they need to update SMAPI. This also serves as a proxy for the minimum game version, since SMAPI itself enforces a minimum game version.",
+ "$ref": "#/definitions/SemanticVersion"
+ },
"Dependencies": {
"title": "Mod dependencies",
"description": "Specifies other mods to load before this mod. If a dependency is required and a player tries to use the mod without the dependency installed, the mod won't be loaded and they'll see a friendly message saying they need to install those.",
"type": "array",
"items": {
- "type": "object",
+ "type": "object",
"properties": {
"UniqueID": {
"title": "Dependency unique ID",
@@ -90,8 +99,26 @@
"type": "array",
"items": {
"type": "string",
- "pattern": "^(Chucklefish:\\d+|Nexus:\\d+|GitHub:[A-Za-z0-9_]+/[A-Za-z0-9_]+|ModDrop:\\d+)$"
+ "pattern": "^(Chucklefish:\\d+|Nexus:\\d+|GitHub:[A-Za-z0-9_]+/[A-Za-z0-9_]+|ModDrop:\\d+)$",
+ "@errorMessages": {
+ "pattern": "Invalid update key; see https://stardewvalleywiki.com/Modding:Modder_Guide/APIs/Manifest#Update_checks for more info."
+ }
+ }
+ }
+ },
+ "definitions": {
+ "SemanticVersion": {
+ "type": "string",
+ "pattern": "^(?>(?<major>0|[1-9]\\d*))\\.(?>(?<minor>0|[1-9]\\d*))(?>(?:\\.(?<patch>0|[1-9]\\d*))?)(?:-(?<prerelease>(?>[a-zA-Z0-9]+[\\-\\.]?)+))?$", // derived from SMAPI.Toolkit.SemanticVersion
+ "examples": [ "1.0.0", "1.0.1-beta.2" ],
+ "@errorMessages": {
+ "pattern": "Invalid semantic version; must be formatted like 1.2.0 or 1.2.0-prerelease.tags. See https://semver.org/ for more info."
}
+ },
+ "ModID": {
+ "type": "string",
+ "pattern": "^[a-zA-Z0-9_.-]+$", // derived from SMAPI.Toolkit.Utilities.PathUtilities.IsSlug
+ "examples": [ "Pathoschild.LookupAnything" ]
}
},
@@ -104,17 +131,8 @@
"required": [ "ContentPackFor" ]
}
],
-
- "definitions": {
- "SemanticVersion": {
- "type": "string",
- "pattern": "(?>(?<major>0|[1-9]\\d*))\\.(?>(?<minor>0|[1-9]\\d*))(?>(?:\\.(?<patch>0|[1-9]\\d*))?)(?:-(?<prerelease>(?>[a-zA-Z0-9]+[\\-\\.]?)+))?", // derived from SMAPI.Toolkit.SemanticVersion
- "examples": [ "1.0.0", "1.0.1-beta.2" ]
- },
- "ModID": {
- "type": "string",
- "pattern": "^[a-zA-Z0-9_.-]+$", // derived from SMAPI.Toolkit.Utilities.PathUtilities.IsSlug
- "examples": [ "Pathoschild.LookupAnything" ]
- }
+ "additionalProperties": false,
+ "@errorMessages": {
+ "oneOf": "Can't specify both EntryDll or ContentPackFor, they're mutually exclusive."
}
}