diff --git a/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs b/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs index 426020ce..8fdb50e5 100644 --- a/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs +++ b/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs @@ -2,6 +2,7 @@ namespace LEGO.AsyncAPI.Extensions { + using System.Collections.Generic; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; @@ -38,5 +39,25 @@ public static void AddExtension(this T element, string name, IAsyncApiExtensi element.Extensions[name] = any ?? throw Error.ArgumentNull(nameof(any)); } + + /// + /// Tries the get value or default. + /// + /// + /// The dictionary. + /// The key. + /// The value. + /// + public static bool TryGetValueOrDefault(this IDictionary dictionary, string key, out T value) + { + if (dictionary.TryGetValue(key, out var extension)) + { + value = AsyncApiAny.FromExtensionOrDefault(extension); + return true; + } + + value = default(T); + return false; + } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs index 042b1f68..01970b19 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs @@ -2,11 +2,13 @@ namespace LEGO.AsyncAPI.Models { + using System; using System.Collections.ObjectModel; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; + [Obsolete("Please use AsyncApiAny instead")] public class AsyncApiArray : Collection, IAsyncApiExtension, IAsyncApiElement { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs index 90e8e124..e93f595e 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs @@ -2,6 +2,7 @@ namespace LEGO.AsyncAPI.Models { + using System; using System.Collections.Generic; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models.Interfaces; @@ -10,6 +11,7 @@ namespace LEGO.AsyncAPI.Models /// /// AsyncApi object. /// + [Obsolete("Please use AsyncApiAny instead")] public class AsyncApiObject : Dictionary, IAsyncApiExtension, IAsyncApiElement { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs index 098c88d4..5c5f2b31 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs @@ -2,6 +2,8 @@ namespace LEGO.AsyncAPI.Models { + using System.Collections.Generic; + using System.Text.Json; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; @@ -13,10 +15,15 @@ namespace LEGO.AsyncAPI.Models /// public class AsyncApiAny : IAsyncApiElement, IAsyncApiExtension { + private JsonSerializerOptions options = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + private JsonNode node; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The node. public AsyncApiAny(JsonNode node) @@ -24,17 +31,115 @@ public AsyncApiAny(JsonNode node) this.node = node; } + /// + /// Initializes a new instance of the class. + /// + /// The object. + public AsyncApiAny(object obj) + { + this.node = JsonNode.Parse(JsonSerializer.Serialize(obj, this.options)); + } + + /// + /// Initializes a new instance of the class. + /// + /// The node. + public AsyncApiAny(JsonArray node) + { + this.node = node; + } + + /// + /// Initializes a new instance of the class. + /// + /// The node. + public AsyncApiAny(JsonObject node) + { + this.node = node; + } + + /// + /// Converts to from an Extension. + /// + /// T. + /// The extension. + /// . + public static T FromExtensionOrDefault(IAsyncApiExtension extension) + { + if (extension is AsyncApiAny any) + { + return any.GetValueOrDefault(); + } + else + { + return default(T); + } + } + /// /// Gets the node. /// + /// . /// /// The node. /// public JsonNode GetNode() => this.node; + /// + /// Gets the value. + /// + /// . + /// . public T GetValue() { - return this.node.GetValue(); + if (this.node == null) + { + return default(T); + } + + if (this.node is JsonValue) + { + return this.node.GetValue(); + } + + return JsonSerializer.Deserialize(this.node.ToJsonString()); + } + + /// + /// Gets the value or default. + /// + /// . + /// or default. + public T GetValueOrDefault() + { + try + { + return this.GetValue(); + } + catch (System.Exception) + { + return default(T); + } + } + + /// + /// Tries the get value. + /// + /// . + /// The value. + /// true if the value could be converted, otherwise false. + public bool TryGetValue(out T value) + { + try + { + value = this.GetValue(); + return true; + } + catch (System.Exception) + { + value = default(T); + return false; + } } /// diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index b48b1b48..a598ac18 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -7,16 +7,21 @@ namespace LEGO.AsyncAPI.Tests using System.Globalization; using System.IO; using System.Linq; - using LEGO.AsyncAPI.Bindings.Pulsar; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Bindings.Pulsar; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Writers; using NUnit.Framework; + public class ExtensionClass + { + public string Key { get; set; } + public long OtherKey { get; set; } + } public class AsyncApiDocumentV2Tests { [Test] @@ -838,8 +843,6 @@ public void SerializeV2_WithFullSpec_Serializes() string traitTitle = "traitTitle"; string schemaTitle = "schemaTitle"; string schemaDescription = "schemaDescription"; - string anyKey = "key"; - string anyOtherKey = "otherKey"; string anyStringValue = "value"; long anyLongValue = long.MaxValue; string exampleSummary = "exampleSummary"; @@ -864,6 +867,8 @@ public void SerializeV2_WithFullSpec_Serializes() string refreshUrl = "https://example.com/refresh"; string authorizationUrl = "https://example.com/authorization"; string requirementString = "requirementItem"; + + var document = new AsyncApiDocument() { Id = documentId, @@ -1016,11 +1021,11 @@ public void SerializeV2_WithFullSpec_Serializes() Description = schemaDescription, Examples = new List { - new AsyncApiObject + new AsyncApiAny(new ExtensionClass { - { anyKey, new AsyncApiAny(anyStringValue) }, - { anyOtherKey, new AsyncApiAny(anyLongValue) }, - }, + Key = anyStringValue, + OtherKey = anyLongValue, + }), }, }, Examples = new List @@ -1029,11 +1034,11 @@ public void SerializeV2_WithFullSpec_Serializes() { Summary = exampleSummary, Name = exampleName, - Payload = new AsyncApiObject + Payload =new AsyncApiAny(new ExtensionClass { - { anyKey, new AsyncApiAny(anyStringValue) }, - { anyOtherKey, new AsyncApiAny(anyLongValue) }, - }, + Key = anyStringValue, + OtherKey = anyLongValue, + }), Extensions = new Dictionary { { extensionKey, new AsyncApiAny(extensionString) }, diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index d4d5cf05..dbca5966 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -381,6 +381,9 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Sns; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var binding2 = new AsyncApiStringReader(settings).ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); + binding2.Bindings.First().Value.Extensions.TryGetValue("x-bindingExtension", out IAsyncApiExtension any); + var val = AsyncApiAny.FromExtensionOrDefault(any); // Assert Assert.AreEqual(actual, expected); @@ -388,5 +391,9 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() var expectedSnsBinding = (SnsOperationBinding)operation.Bindings.Values.First(); expectedSnsBinding.Should().BeEquivalentTo((SnsOperationBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); } + class ExtensionClass + { + public string bindingXPropertyName { get; set; } + } } } \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index 692c8e06..1be4a264 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -24,7 +24,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs new file mode 100644 index 00000000..598ef31f --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs @@ -0,0 +1,54 @@ +using LEGO.AsyncAPI.Models; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace LEGO.AsyncAPI.Tests +{ + + public class AsyncApiAnyTests + { + [Test] + public void GetValue_ReturnsCorrectConversions() + { + // Arrange + // Act + var a = new AsyncApiAny("string"); + var b = new AsyncApiAny(1); + var c = new AsyncApiAny(1.1); + var d = new AsyncApiAny(true); + var e = new AsyncApiAny(new MyType("test")); + var f = new AsyncApiAny(new List() { "test", "test2"}); + var g = new AsyncApiAny(new List() { "test", "test2"}.AsEnumerable()); + var h = new AsyncApiAny(new List() { new MyType("test") }); + var i = new AsyncApiAny(new Dictionary() { { "t", 2 } }); + var j = new AsyncApiAny(new Dictionary() { { "t", new MyType("test") } }); + + // Assert + Assert.AreEqual("string", a.GetValue()); + Assert.AreEqual(1, b.GetValue()); + Assert.AreEqual(1.1, c.GetValue()); + Assert.AreEqual(true, d.GetValue()); + Assert.NotNull(e.GetValue()); + Assert.IsNotEmpty(f.GetValue>()); + Assert.IsNotEmpty(f.GetValue>()); + Assert.IsNotEmpty(g.GetValue>()); + Assert.IsNotEmpty(g.GetValue>()); + Assert.IsNotEmpty(h.GetValue>()); + Assert.IsNotEmpty(h.GetValue>()); + Assert.IsNotEmpty(i.GetValue>()); + Assert.IsNotEmpty(j.GetValue>()); + } + + class MyType + { + public MyType(string value) + { + this.Value = value; + } + + public string Value { get; set; } + } + } +}