Skip to content

Commit

Permalink
Merge pull request #249 from gregsdennis/schema/configurable-error-me…
Browse files Browse the repository at this point in the history
…ssages

Schema/configurable error messages
  • Loading branch information
gregsdennis committed Apr 4, 2022
2 parents 569658c + d8ed505 commit 6dfdcdb
Show file tree
Hide file tree
Showing 60 changed files with 1,570 additions and 114 deletions.
5 changes: 2 additions & 3 deletions .github/label-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@
- JsonPatch/**/*

"documentation":
- docs_source/**/*
- json-everything.net/wwwroot/md/**/*

"build pipeline":
- .github/workflows/**/*

"try-me-site":
- json-everything.net/**/*
- TryJsonEverything/**/*
- any: ['json-everything.net/**/*', '!json-everything.net/wwwroot/md/*']
2 changes: 1 addition & 1 deletion .github/workflows/label.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/labeler@v2
- uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/label-config.yml
Expand Down
56 changes: 53 additions & 3 deletions .github/workflows/publish-schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ on:
workflow_dispatch:

jobs:
publish:
name: build, pack & publish
publish-core:
name: build, pack & publish core package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand All @@ -21,7 +21,7 @@ jobs:
# Publish
- name: publish schema
id: publish_schema
uses: alirezanet/publish-nuget@v3.0.0
uses: brandedoutcast/publish-nuget@v2
with:
PROJECT_FILE_PATH: JsonSchema/JsonSchema.csproj
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
Expand All @@ -34,3 +34,53 @@ jobs:
path: |
**/*.nupkg
**/*.snupkg
build-localizations:
name: build localizations
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- id: get-localized-nuspecs
run: |
NUSPEC_FILES=$(echo -n '[' ; ls nuspec/ --format=commas|sed -e 's/^/\"/'|sed -e 's/,$/\",/'|sed -e 's/\([^,]\)$/\1\"\]/'|sed -e 's/, /\", \"/g')
echo "::set-output name=NUSPEC_FILES::${NUSPEC_FILES}"
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
# Publish
- name: Install dependencies
run: dotnet restore
- name: Build
# Localization build automatically moves localization DLLs into nuspec/ folder
run: dotnet build --configuration Localization --no-restore
- name: archive packages
uses: actions/upload-artifact@v2
with:
name: localization-artifacts
path: nuspec/**/*
outputs:
matrix: ${{ steps.get-localized-nuspecs.outputs.matrix }}
publish-localizations:
needs: build-localizations
name: build, pack & publish localizations
runs-on: ubuntu-latest
strategy:
matrix:
nuspec: ${{ fromJson(needs.build-localizations.outputs.matrix) }}
steps:
- name: Download Artifacts
uses: actions/download-artifact@v2
with:
path: artifacts
- uses: nuget/setup-nuget@v1
with:
nuget-api-key: ${{ secrets.NUGET_API_KEY }}
nuget-version: '6.x'
- name: publish localization
run: |
nuget pack ${{ matrix.nuspec }} -OutputDirectory nupkg/
- name: archive packages
uses: actions/upload-artifact@v2
with:
name: archive-packages
path: nupkg/*.nupkg
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
*.userosscache
*.sln.docstates
Json*.xml
/JsonSchema/nuspec/*/*.dll


# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
Expand Down
12 changes: 6 additions & 6 deletions JsonSchema.Data/DataKeyword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public void Validate(ValidationContext context)
JsonSchema subschema;
try
{
subschema = JsonSerializer.Deserialize<JsonSchema>(json);
subschema = JsonSerializer.Deserialize<JsonSchema>(json)!;
}
catch (JsonException e)
{
Expand Down Expand Up @@ -108,7 +108,7 @@ public void Validate(ValidationContext context)

if (Equals(data, default(JsonElement)))
{
context.LocalResult.Fail($"Could not resolve base URI `{baseUri}`");
context.LocalResult.Fail(ErrorMessages.BaseUriResolution, ("uri", baseUri));
return null;
}

Expand All @@ -117,22 +117,22 @@ public void Validate(ValidationContext context)
fragment = $"#{fragment}";
if (!JsonPointer.TryParse(fragment, out var pointer))
{
context.LocalResult.Fail($"Could not parse pointer `{fragment}`");
context.LocalResult.Fail(ErrorMessages.PointerParse, ("fragment", fragment));
return null;
}

var resolved = pointer!.Evaluate(data);
if (resolved == null)
{
context.LocalResult.Fail($"Could not resolve pointer `{fragment}`");
context.LocalResult.Fail(ErrorMessages.RefResolution, ("uri", fragment));
return null;
}
data = resolved.Value;
}

if (Equals(data, default(JsonElement)))
{
context.LocalResult.Fail($"Could not resolve URI `{baseUri}`");
context.LocalResult.Fail(ErrorMessages.RefResolution, ("uri", baseUri));
return null;
}

Expand Down Expand Up @@ -201,7 +201,7 @@ public override DataKeyword Read(ref Utf8JsonReader reader, Type typeToConvert,
if (reader.TokenType != JsonTokenType.StartObject)
throw new JsonException("Expected object");

var references = JsonSerializer.Deserialize<Dictionary<string, string>>(ref reader, options)
var references = JsonSerializer.Deserialize<Dictionary<string, string>>(ref reader, options)!
.ToDictionary(kvp => kvp.Key, kvp => new Uri(kvp.Value, UriKind.RelativeOrAbsolute));
return new DataKeyword(references);
}
Expand Down
4 changes: 2 additions & 2 deletions JsonSchema.Data/JsonSchema.Data.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<DocumentationFile>JsonSchema.Net.Data.xml</DocumentationFile>
<LangVersion>latest</LangVersion>
<Version>1.0.5</Version>
<FileVersion>1.0.5.0</FileVersion>
<Version>1.1.0</Version>
<FileVersion>1.1.0.0</FileVersion>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
Expand Down
74 changes: 74 additions & 0 deletions JsonSchema.Tests/LocalizationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System.Globalization;
using System.Text.Json;
using NUnit.Framework;

namespace Json.Schema.Tests;

public class LocalizationTests
{
[Test]
public void MinimumReturnsDefaultErrorMessage()
{
JsonSchema schema = new JsonSchemaBuilder()
.Type(SchemaValueType.Number)
.Minimum(10);

var instance = JsonDocument.Parse("5");

var results = schema.Validate(instance.RootElement, new ValidationOptions { OutputFormat = OutputFormat.Basic });

var message = results.Message;

Assert.AreEqual("5 is less than or equal to 10", message);
}

[Test]
public void MinimumReturnsDefaultErrorMessageButInSpanish()
{
try
{
ErrorMessages.Culture = CultureInfo.GetCultureInfo("es");

JsonSchema schema = new JsonSchemaBuilder()
.Type(SchemaValueType.Number)
.Minimum(10);

var instance = JsonDocument.Parse("5");

var results = schema.Validate(instance.RootElement, new ValidationOptions { OutputFormat = OutputFormat.Basic });

var message = results.Message;

Assert.AreEqual("5 es menor o igual que 10", message);
}
finally
{
ErrorMessages.Culture = null;
}
}

[Test]
public void MinimumReturnsCustomErrorMessage()
{
try
{
ErrorMessages.Minimum = "This is a custom error message with [[received]] and [[limit]]";

JsonSchema schema = new JsonSchemaBuilder()
.Type(SchemaValueType.Number)
.Minimum(10);

var instance = JsonDocument.Parse("5");

var results = schema.Validate(instance.RootElement, new ValidationOptions { OutputFormat = OutputFormat.Basic });

var message = results.Message;

Assert.AreEqual("This is a custom error message with 5 and 10", message);
}
finally
{
ErrorMessages.Minimum = null!;
}
}
}
4 changes: 2 additions & 2 deletions JsonSchema.Tests/OutputTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public void Detailed_Multi_Failure_Both()
""keywordLocation"": ""#/properties/multi/allOf/0/$ref/type"",
""absoluteKeywordLocation"": ""https://test.com/schema#/$defs/integer/type"",
""instanceLocation"": ""#/multi"",
""error"": ""Value is number but should be integer""
""error"": ""Value is \""number\"" but should be \""integer\""""
},
{
""valid"": false,
Expand Down Expand Up @@ -196,7 +196,7 @@ public void RelativeAndAbsoluteLocations()
""keywordLocation"": ""#/properties/refs/$ref/type"",
""absoluteKeywordLocation"": ""https://test.com/schema#/$defs/integer/type"",
""instanceLocation"": ""#/refs"",
""error"": ""Value is number but should be integer""
""error"": ""Value is \""number\"" but should be \""integer\""""
}";

result.AssertInvalid(expected);
Expand Down
2 changes: 1 addition & 1 deletion JsonSchema.Tests/ResultsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ private static void AssertEquivalent(ValidationResults results, string expected
if (expected == null) return;

var expectedJson = JsonDocument.Parse(expected).RootElement;
var actualJson = JsonDocument.Parse(JsonSerializer.Serialize(results)).RootElement;
var actualJson = JsonDocument.Parse(JsonSerializer.Serialize(results, new JsonSerializerOptions{Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping})).RootElement;

Assert.IsTrue(expectedJson.IsEquivalentTo(actualJson));
}
Expand Down
8 changes: 6 additions & 2 deletions JsonSchema.Tests/VocabularyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ public void Validate(ValidationContext context)
if (date >= Date)
context.LocalResult.Pass();
else
context.LocalResult.Fail($"{date:O} must be on or after {Date:O}");
context.LocalResult.Fail("[[provided:O]] must be on or after [[value:O]]",
("provided", date),
("value", Date));
}

public bool Equals(MinDateKeyword other)
Expand Down Expand Up @@ -93,7 +95,9 @@ public void Validate(ValidationContext context)
if (date <= Date)
context.LocalResult.Pass();
else
context.LocalResult.Fail($"{date:O} must be on or before {Date:O}");
context.LocalResult.Fail("[[provided:O]] must be on or before [[value:O]]",
("provided", date),
("value", Date));
}

public bool Equals(MaxDateKeyword other)
Expand Down
4 changes: 2 additions & 2 deletions JsonSchema.UniqueKeys/JsonSchema.UniqueKeys.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<DocumentationFile>JsonSchema.Net.UniqueKeys.xml</DocumentationFile>
<LangVersion>latest</LangVersion>
<Version>1.0.2</Version>
<FileVersion>1.0.2.0</FileVersion>
<Version>1.1.0</Version>
<FileVersion>1.1.0.0</FileVersion>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
Expand Down
7 changes: 5 additions & 2 deletions JsonSchema.UniqueKeys/UniqueKeysKeyword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ public void Validate(ValidationContext context)
if (!matchedIndexPairs.Any())
context.LocalResult.Pass();
else
context.LocalResult.Fail($"Found duplicate items at index pairs {string.Join(", ", matchedIndexPairs.Select(x => $"({x.Item1}, {x.Item2})"))}");
{
var pairs = string.Join(", ", matchedIndexPairs.Select(d => $"({d.Item1}, {d.Item2})"));
context.LocalResult.Fail(ErrorMessages.UniqueItems, ("pairs", pairs));
}
context.ExitKeyword(Name);
}

Expand Down Expand Up @@ -136,7 +139,7 @@ public override UniqueKeysKeyword Read(ref Utf8JsonReader reader, Type typeToCon
if (reader.TokenType != JsonTokenType.StartArray)
throw new JsonException("Expected array");

var references = JsonSerializer.Deserialize<List<JsonPointer>>(ref reader, options);
var references = JsonSerializer.Deserialize<List<JsonPointer>>(ref reader, options)!;
return new UniqueKeysKeyword(references);
}

Expand Down
20 changes: 19 additions & 1 deletion JsonSchema/ConstKeyword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void Validate(ValidationContext context)
if (Value.IsEquivalentTo(context.LocalInstance))
context.LocalResult.Pass();
else
context.LocalResult.Fail("Expected value to match given value");
context.LocalResult.Fail(ErrorMessages.Const, ("value", Value.ToJsonString()));
context.ExitKeyword(Name, context.LocalResult.IsValid);
}

Expand Down Expand Up @@ -88,4 +88,22 @@ public override void Write(Utf8JsonWriter writer, ConstKeyword value, JsonSerial
writer.WritePropertyName(ConstKeyword.Name);
value.Value.WriteTo(writer);
}
}

public static partial class ErrorMessages
{
private static string? _const;

/// <summary>
/// Gets or sets the error message for <see cref="ConstKeyword"/>.
/// </summary>
/// <remarks>
/// Available tokens are:
/// - [[value]] - the value in the schema
/// </remarks>
public static string Const
{
get => _const ?? Get();
set => _const = value;
}
}
17 changes: 16 additions & 1 deletion JsonSchema/ContainsKeyword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public void Validate(ValidationContext context)
context.LocalResult.Pass();
}
else
context.LocalResult.Fail("Expected array to contain at least one item that matched the schema, but it did not");
context.LocalResult.Fail(ErrorMessages.Contains);
context.ExitKeyword(Name, context.LocalResult.IsValid);
}

Expand Down Expand Up @@ -148,4 +148,19 @@ public override void Write(Utf8JsonWriter writer, ContainsKeyword value, JsonSer
writer.WritePropertyName(ContainsKeyword.Name);
JsonSerializer.Serialize(writer, value.Schema, options);
}
}

public static partial class ErrorMessages
{
private static string? _contains;

/// <summary>
/// Gets or sets the error message for <see cref="ContainsKeyword"/>.
/// </summary>
/// <remarks>No tokens are supported.</remarks>
public static string Contains
{
get => _contains ?? Get();
set => _contains = value;
}
}

0 comments on commit 6dfdcdb

Please sign in to comment.