Restructure solution layout by module
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
using System.Text.Json;
|
||||
using Json.Schema;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Common.Json;
|
||||
|
||||
public interface IJsonSchemaValidator
|
||||
{
|
||||
void Validate(JsonDocument document, JsonSchema schema, string documentName);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace StellaOps.Concelier.Connector.Common.Json;
|
||||
|
||||
public sealed record JsonSchemaValidationError(
|
||||
string InstanceLocation,
|
||||
string SchemaLocation,
|
||||
string Message,
|
||||
string Keyword);
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace StellaOps.Concelier.Connector.Common.Json;
|
||||
|
||||
public sealed class JsonSchemaValidationException : Exception
|
||||
{
|
||||
public JsonSchemaValidationException(string documentName, IReadOnlyList<JsonSchemaValidationError> errors)
|
||||
: base($"JSON schema validation failed for '{documentName}'.")
|
||||
{
|
||||
DocumentName = documentName;
|
||||
Errors = errors ?? Array.Empty<JsonSchemaValidationError>();
|
||||
}
|
||||
|
||||
public string DocumentName { get; }
|
||||
|
||||
public IReadOnlyList<JsonSchemaValidationError> Errors { get; }
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Json.Schema;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace StellaOps.Concelier.Connector.Common.Json;
|
||||
public sealed class JsonSchemaValidator : IJsonSchemaValidator
|
||||
{
|
||||
private readonly ILogger<JsonSchemaValidator> _logger;
|
||||
private const int MaxLoggedErrors = 5;
|
||||
|
||||
public JsonSchemaValidator(ILogger<JsonSchemaValidator> logger)
|
||||
{
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
public void Validate(JsonDocument document, JsonSchema schema, string documentName)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
ArgumentNullException.ThrowIfNull(schema);
|
||||
ArgumentException.ThrowIfNullOrEmpty(documentName);
|
||||
|
||||
var result = schema.Evaluate(document.RootElement, new EvaluationOptions
|
||||
{
|
||||
OutputFormat = OutputFormat.List,
|
||||
RequireFormatValidation = true,
|
||||
});
|
||||
|
||||
if (result.IsValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var errors = CollectErrors(result);
|
||||
|
||||
if (errors.Count == 0)
|
||||
{
|
||||
_logger.LogWarning("Schema validation failed for {Document} with unknown errors", documentName);
|
||||
throw new JsonSchemaValidationException(documentName, errors);
|
||||
}
|
||||
|
||||
foreach (var violation in errors.Take(MaxLoggedErrors))
|
||||
{
|
||||
_logger.LogWarning(
|
||||
"Schema violation for {Document} at {InstanceLocation} (keyword: {Keyword}): {Message}",
|
||||
documentName,
|
||||
string.IsNullOrEmpty(violation.InstanceLocation) ? "#" : violation.InstanceLocation,
|
||||
violation.Keyword,
|
||||
violation.Message);
|
||||
}
|
||||
|
||||
if (errors.Count > MaxLoggedErrors)
|
||||
{
|
||||
_logger.LogWarning("{Count} additional schema violations for {Document} suppressed", errors.Count - MaxLoggedErrors, documentName);
|
||||
}
|
||||
|
||||
throw new JsonSchemaValidationException(documentName, errors);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<JsonSchemaValidationError> CollectErrors(EvaluationResults result)
|
||||
{
|
||||
var errors = new List<JsonSchemaValidationError>();
|
||||
Aggregate(result, errors);
|
||||
return errors;
|
||||
}
|
||||
|
||||
private static void Aggregate(EvaluationResults node, List<JsonSchemaValidationError> errors)
|
||||
{
|
||||
if (node.Errors is { Count: > 0 })
|
||||
{
|
||||
foreach (var kvp in node.Errors)
|
||||
{
|
||||
errors.Add(new JsonSchemaValidationError(
|
||||
node.InstanceLocation?.ToString() ?? string.Empty,
|
||||
node.SchemaLocation?.ToString() ?? string.Empty,
|
||||
kvp.Value,
|
||||
kvp.Key));
|
||||
}
|
||||
}
|
||||
|
||||
if (node.Details is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var child in node.Details)
|
||||
{
|
||||
Aggregate(child, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user