diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index c9534d20..f949164d 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -39,6 +39,12 @@ public class AsyncApiReaderSettings : AsyncApiSettings public ReferenceResolutionSetting ReferenceResolution { get; set; } = ReferenceResolutionSetting.ResolveInternalReferences; + /// + /// Indicates what should happen when unmapped members are encountered during deserialization. + /// Error and Warning will add an error or warning to the diagnostics object. + /// + public UnmappedMemberHandling UnmappedMemberHandling { get; set; } = UnmappedMemberHandling.Error; + /// /// Dictionary of parsers for converting extensions into strongly typed classes. /// diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs index 11120606..2313f30d 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs @@ -29,8 +29,7 @@ public void ParseField( IDictionary> fixedFields, IDictionary, Action> patternFields) { - var found = fixedFields.TryGetValue(this.Name, out var fixedFieldMap); - + var _ = fixedFields.TryGetValue(this.Name, out var fixedFieldMap); if (fixedFieldMap != null) { try @@ -78,8 +77,12 @@ public void ParseField( } else { - this.Context.Diagnostic.Errors.Add( - new AsyncApiError(string.Empty, $"{this.Name} is not a valid property at {this.Context.GetLocation()}")); + switch (this.Context.Settings.UnmappedMemberHandling) + { + case UnmappedMemberHandling.Error: + this.Context.Diagnostic.Errors.Add(new AsyncApiError(string.Empty, $"{this.Name} is not a valid property at {this.Context.GetLocation()}")); + break; + } } } } diff --git a/src/LEGO.AsyncAPI.Readers/UnmappedMemberHandling.cs b/src/LEGO.AsyncAPI.Readers/UnmappedMemberHandling.cs new file mode 100644 index 00000000..b58c34c4 --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/UnmappedMemberHandling.cs @@ -0,0 +1,20 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers +{ + /// + /// Unmapped member handling. + /// + public enum UnmappedMemberHandling + { + /// + /// Add error to diagnostics for unmapped members. + /// + Error, + + /// + /// Ignore unmapped members. + /// + Ignore, + } +} diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs index 013ec856..5015cef2 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs @@ -3,6 +3,7 @@ namespace LEGO.AsyncAPI.Readers { using System; + using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.Exceptions; using LEGO.AsyncAPI.Readers.ParseNodes; @@ -169,7 +170,7 @@ public static AvroSchema LoadSchema(ParseNode node) mapNode.ParseFields(ref union, UnionFixedFields, UnionMetadataPatternFields); return union; default: - throw new InvalidOperationException($"Unsupported type: {type}"); + throw new AsyncApiException($"Unsupported type: {type}"); } } diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index 0a4cd5e7..20716506 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -5,6 +5,7 @@ namespace LEGO.AsyncAPI.Tests using System; using System.Collections.Generic; using System.Linq; + using FluentAssertions; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; @@ -30,6 +31,7 @@ public void Read_WithExtensionParser_Parses() info: title: test version: 1.0.0 + test: 1234 contact: name: API Support url: https://www.example.com/support @@ -57,6 +59,7 @@ public void Read_WithExtensionParser_Parses() { { extensionName, valueExtensionParser }, }, + UnmappedMemberHandling = UnmappedMemberHandling.Ignore, }; var reader = new AsyncApiStringReader(settings); @@ -64,6 +67,96 @@ public void Read_WithExtensionParser_Parses() Assert.AreEqual((doc.Channels["workspace"].Extensions[extensionName] as AsyncApiAny).GetValue(), 1234); } + [Test] + public void Read_WithUnmappedMemberHandlingError_AddsError() + { + var extensionName = "x-someValue"; + var yaml = $""" + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + test: 1234 + contact: + name: API Support + url: https://www.example.com/support + email: support@example.com + channels: + workspace: + {extensionName}: onetwothreefour + """; + Func valueExtensionParser = (any) => + { + if (any.TryGetValue(out var value)) + { + if (value == "onetwothreefour") + { + return new AsyncApiAny(1234); + } + } + + return new AsyncApiAny("No value provided"); + }; + + var settings = new AsyncApiReaderSettings + { + ExtensionParsers = new Dictionary> + { + { extensionName, valueExtensionParser }, + }, + UnmappedMemberHandling = UnmappedMemberHandling.Error, + }; + + var reader = new AsyncApiStringReader(settings); + var doc = reader.Read(yaml, out var diagnostic); + diagnostic.Errors.Should().HaveCount(1); + } + + [Test] + public void Read_WithUnmappedMemberHandlingIgnore_NoErrors() + { + var extensionName = "x-someValue"; + var yaml = $""" + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + test: 1234 + contact: + name: API Support + url: https://www.example.com/support + email: support@example.com + channels: + workspace: + {extensionName}: onetwothreefour + """; + Func valueExtensionParser = (any) => + { + if (any.TryGetValue(out var value)) + { + if (value == "onetwothreefour") + { + return new AsyncApiAny(1234); + } + } + + return new AsyncApiAny("No value provided"); + }; + + var settings = new AsyncApiReaderSettings + { + ExtensionParsers = new Dictionary> + { + { extensionName, valueExtensionParser }, + }, + UnmappedMemberHandling = UnmappedMemberHandling.Ignore, + }; + + var reader = new AsyncApiStringReader(settings); + var doc = reader.Read(yaml, out var diagnostic); + diagnostic.Errors.Should().HaveCount(0); + } + [Test] public void Read_WithThrowingExtensionParser_AddsToDiagnostics() {