add support for transparent schema errors with multiple child errors (#654)

This commit is contained in:
Jesse Plamondon-Willard 2019-08-06 03:52:32 -04:00
parent 674ceea74e
commit 807868f440
No known key found for this signature in database
GPG Key ID: CF8B1456B3E29F49
2 changed files with 39 additions and 13 deletions

View File

@ -61,10 +61,29 @@ against the error message (more fragile):
``` ```
Error messages may contain special tokens: Error messages may contain special tokens:
* `@value` is replaced with the error's value field (which is usually the original field value, but
not always). * The `@value` token is replaced with the error's value field. This is usually (but not always) the
* If the validation error has exactly one sub-error and the message is set to `$transparent`, the original field value.
sub-error will be displayed instead. (The sub-error itself may be set to `$transparent`, etc.) * When an error has child errors, by default they're flattened into one message:
```
line | field | error
---- | ---------- | -------------------------------------------------------------------------
4 | Changes[0] | JSON does not match schema from 'then'.
| | ==> Changes[0].ToArea.Y: Invalid type. Expected Integer but got String.
| | ==> Changes[0].ToArea: Missing required fields: Height.
```
If you set the message for an error to `$transparent`, the parent error is omitted entirely and
the child errors are shown instead:
```
line | field | error
---- | ------------------- | ----------------------------------------------
8 | Changes[0].ToArea.Y | Invalid type. Expected Integer but got String.
8 | Changes[0].ToArea | Missing required fields: Height.
```
The child errors themselves may be marked `$transparent`, etc. If an error has no child errors,
this override is ignored.
Caveats: Caveats:
* To override an error from a `then` block, the `@errorMessages` must be inside the `then` block * To override an error from a `then` block, the `@errorMessages` must be inside the `then` block

View File

@ -46,6 +46,9 @@ namespace StardewModdingAPI.Web.Controllers
/// <summary>The schema ID to use if none was specified.</summary> /// <summary>The schema ID to use if none was specified.</summary>
private string DefaultSchemaID = "manifest"; private string DefaultSchemaID = "manifest";
/// <summary>A token in an error message which indicates that the child errors should be displayed instead.</summary>
private readonly string TransparentToken = "$transparent";
/********* /*********
** Public methods ** Public methods
@ -124,7 +127,7 @@ namespace StardewModdingAPI.Web.Controllers
// validate JSON // validate JSON
parsed.IsValid(schema, out IList<ValidationError> rawErrors); parsed.IsValid(schema, out IList<ValidationError> rawErrors);
var errors = rawErrors var errors = rawErrors
.Select(this.GetErrorModel) .SelectMany(this.GetErrorModels)
.ToArray(); .ToArray();
return this.View("Index", result.AddErrors(errors)); return this.View("Index", result.AddErrors(errors));
} }
@ -205,21 +208,25 @@ namespace StardewModdingAPI.Web.Controllers
return null; return null;
} }
/// <summary>Get a flattened representation representation of a schema validation error and any child errors.</summary> /// <summary>Get view models representing a schema validation error and any child errors.</summary>
/// <param name="error">The error to represent.</param> /// <param name="error">The error to represent.</param>
private JsonValidatorErrorModel GetErrorModel(ValidationError error) private IEnumerable<JsonValidatorErrorModel> GetErrorModels(ValidationError error)
{ {
// skip through transparent errors // skip through transparent errors
while (this.GetOverrideError(error) == "$transparent" && error.ChildErrors.Count == 1) if (this.GetOverrideError(error) == this.TransparentToken && error.ChildErrors.Any())
error = error.ChildErrors[0]; {
foreach (var model in error.ChildErrors.SelectMany(this.GetErrorModels))
yield return model;
yield break;
}
// get message // get message
string message = this.GetOverrideError(error); string message = this.GetOverrideError(error);
if (message == null) if (message == null || message == this.TransparentToken)
message = this.FlattenErrorMessage(error); message = this.FlattenErrorMessage(error);
// build model // build model
return new JsonValidatorErrorModel(error.LineNumber, error.Path, message, error.ErrorType); yield return new JsonValidatorErrorModel(error.LineNumber, error.Path, message, error.ErrorType);
} }
/// <summary>Get a flattened, human-readable message for a schema validation error and any child errors.</summary> /// <summary>Get a flattened, human-readable message for a schema validation error and any child errors.</summary>
@ -229,11 +236,11 @@ namespace StardewModdingAPI.Web.Controllers
{ {
// get override // get override
string message = this.GetOverrideError(error); string message = this.GetOverrideError(error);
if (message != null) if (message != null && message != this.TransparentToken)
return message; return message;
// skip through transparent errors // skip through transparent errors
while (this.GetOverrideError(error) == "$transparent" && error.ChildErrors.Count == 1) while (this.GetOverrideError(error) == this.TransparentToken && error.ChildErrors.Count == 1)
error = error.ChildErrors[0]; error = error.ChildErrors[0];
// get friendly representation of main error // get friendly representation of main error