From 578e2378aa65251ff29d841605ef5a9c53641541 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 28 Apr 2023 15:16:51 +0100 Subject: [PATCH 01/39] feat: WIP Add sns channel bindings --- .../AsyncApiSnsBindingsDeserializer.cs | 41 ++++++++++ src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs | 3 + .../Models/Bindings/BindingType.cs | 3 + .../Bindings/Sns/OrderingConfiguration.cs | 20 +++++ .../Models/Bindings/Sns/Policy.cs | 27 +++++++ .../Models/Bindings/Sns/SnsChannelBinding.cs | 77 +++++++++++++++++++ .../Models/Bindings/Sns/Statement.cs | 48 ++++++++++++ .../Writers/AsyncApiWriterExtensions.cs | 2 + .../Bindings/Sns/SnsBindings_Should.cs | 57 ++++++++++++++ 9 files changed, 278 insertions(+) create mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiSnsBindingsDeserializer.cs create mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Sns/OrderingConfiguration.cs create mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Sns/Policy.cs create mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Sns/SnsChannelBinding.cs create mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Sns/Statement.cs create mode 100644 test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiSnsBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiSnsBindingsDeserializer.cs new file mode 100644 index 00000000..d6a13cb5 --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiSnsBindingsDeserializer.cs @@ -0,0 +1,41 @@ +namespace LEGO.AsyncAPI.Readers +{ + + using LEGO.AsyncAPI.Models.Bindings.Sns; + using LEGO.AsyncAPI.Readers.ParseNodes; + + internal static partial class AsyncApiV2Deserializer + { + private static FixedFieldMap snsChannelBindingFixedFields = new() + { + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + { "policy", (a, n) => { a.Policy = LoadPolicy(n); } }, + }; + + private static FixedFieldMap policyFixedFields = new() + { + { "statements", (a, n) => { a.Statements = n.CreateSimpleList(s => LoadStatement(s)); } }, + }; + + private static FixedFieldMap statementFixedFields = new() + { + { "principal", (a, n) => { a.Principal = n.GetScalarValue(); } }, + }; + + private static Policy LoadPolicy(ParseNode node) + { + var mapNode = node.CheckMapNode("policy"); + var policy = new Policy(); + ParseMap(mapNode, policy, policyFixedFields, null); + return policy; + } + + private static Statement LoadStatement(ParseNode node) + { + var mapNode = node.CheckMapNode("statement"); + var statement = new Statement(); + ParseMap(mapNode, statement, statementFixedFields, null); + return statement; + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs index 2d3ae13c..3ed31c5d 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs @@ -138,5 +138,8 @@ public static class AsyncApiConstants public const string MaxMessageBytes = "max.message.bytes"; public const string TopicConfiguration = "topicConfiguration"; public const string GeoReplication = "geo-replication"; + public const string Policy = "policy"; + public const string Statements = "statements"; + public const string Principal = "principal"; } } diff --git a/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs b/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs index b88d3d5d..98b173dd 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs +++ b/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs @@ -17,5 +17,8 @@ public enum BindingType [Display("pulsar")] Pulsar, + + [Display("sns")] + Sns, } } diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Sns/OrderingConfiguration.cs b/src/LEGO.AsyncAPI/Models/Bindings/Sns/OrderingConfiguration.cs new file mode 100644 index 00000000..fb8ddc07 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/Bindings/Sns/OrderingConfiguration.cs @@ -0,0 +1,20 @@ +namespace LEGO.AsyncAPI.Models.Bindings.Sns; + +public class OrderingConfiguration +{ + /// + /// What type of SNS Topic is this? + /// + public Ordering Type { get; set; } + + /// + /// True to turn on de-duplication of messages for a channel. + /// + public bool ContentBasedDeduplication { get; set; } +} + +public enum Ordering +{ + Standard, + Fifo +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Sns/Policy.cs b/src/LEGO.AsyncAPI/Models/Bindings/Sns/Policy.cs new file mode 100644 index 00000000..81283fa5 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/Bindings/Sns/Policy.cs @@ -0,0 +1,27 @@ +using System; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Writers; + +namespace LEGO.AsyncAPI.Models.Bindings.Sns; + +using System.Collections.Generic; + +public class Policy : IAsyncApiElement +{ + /// + /// An array of statement objects, each of which controls a permission for this topic. + /// + public List Statements { get; set; } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalCollection(AsyncApiConstants.Statements, this.Statements, (w, t) => t.Serialize(w)); + writer.WriteEndObject(); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Sns/SnsChannelBinding.cs new file mode 100644 index 00000000..b7a5dc25 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/Bindings/Sns/SnsChannelBinding.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using LEGO.AsyncAPI.Writers; + +namespace LEGO.AsyncAPI.Models.Bindings.Sns; + +using LEGO.AsyncAPI.Models.Interfaces; + +/// +/// Binding class for SNS channel settings. +/// +public class SnsChannelBinding : IChannelBinding +{ + /// + /// The name of the topic. Can be different from the channel name to allow flexibility around AWS resource naming limitations. + /// + public string Name { get; set; } + + /// + /// By default, we assume an unordered SNS topic. This field allows configuration of a FIFO SNS Topic. + /// + public OrderingConfiguration Ordering { get; set; } + + /// + /// The security policy for the SNS Topic. + /// + public Policy Policy { get; set; } + + /// + /// Key-value pairs that represent AWS tags on the topic. + /// + public Dictionary Tags { get; set; } + + /// + /// The version of this binding. + /// + public string BindingVersion { get; set; } + + public BindingType Type => BindingType.Sns; + + public bool UnresolvedReference { get; set; } + + public AsyncApiReference Reference { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void SerializeV2WithoutReference(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty(AsyncApiConstants.Name, this.Name); + writer.WriteOptionalObject(AsyncApiConstants.Policy, this.Policy, (w, t) => t.Serialize(w)); + + writer.WriteEndObject(); + } + + public void SerializeV2(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + + this.SerializeV2WithoutReference(writer); + + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI/Models/Bindings/Sns/Statement.cs new file mode 100644 index 00000000..08d573d6 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/Bindings/Sns/Statement.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Writers; + +namespace LEGO.AsyncAPI.Models.Bindings.Sns; + +public class Statement : IAsyncApiElement +{ + public Effect Effect { get; set; } + + /// + /// The AWS account or resource ARN that this statement applies to. + /// + // public StringOrStringList Principal { get; set; } + public string Principal { get; set; } + + /// + /// The SNS permission being allowed or denied e.g. sns:Publish + /// + // public StringOrStringList Action { get; set; } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + // writer.WriteOptionalObject(AsyncApiConstants.Principal, this.Principal, (w, t) => t.Serialize(w)); + writer.WriteOptionalProperty(AsyncApiConstants.Principal, this.Principal); + writer.WriteEndObject(); + } +} + +public enum Effect +{ + Allow, + Deny +} + +public class StringOrStringList : IAsyncApiElement +{ + public string StringValue { get; set; } + + public List StringList { get; set; } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs index 5953e9d1..194812db 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs @@ -234,6 +234,8 @@ public static void WriteRequiredCollection( { writer.WriteCollectionInternal(name, elements, action); } + + // public static void WriteOptionalObjectCollection(this ) /// /// Write the optional AsyncApi element map (string to string mapping). diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs new file mode 100644 index 00000000..a822cc5e --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -0,0 +1,57 @@ +using System; + +namespace LEGO.AsyncAPI.Tests.Bindings.Sns +{ + using NUnit.Framework; + using System.Collections.Generic; + using FluentAssertions; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Bindings.Sns; + using LEGO.AsyncAPI.Readers; + + internal class SnsBindings_Should + { + [Test] + public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = + @"bindings: + sns: + name: myTopic + policy: + statements: + - principal: hello"; + + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new SnsChannelBinding() + { + Name = "myTopic", + Policy = new Policy() + { + Statements = new List() + { + new Statement() + { + Principal = "hello", + }, + }, + }, + }); + + // Act + var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + + var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(actual, expected); + binding.Should().BeEquivalentTo(channel); + + } + } +} \ No newline at end of file From 861ee6f153f4302c831929e8bbb47a0b736c9cbc Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Wed, 3 May 2023 08:44:17 +0200 Subject: [PATCH 02/39] feat!: separate bindings and allow for custom bindings. (#107) Co-authored-by: UlrikSandberg --- .github/workflows/release-internal.yml | 4 +- .github/workflows/release-package.yml | 2 +- AsyncAPI.sln | 6 + src/LEGO.AsyncAPI.Bindings/Binding.cs | 30 +++++ .../BindingsCollection.cs | 71 +++++++++++ .../ChannelBinding{T}.cs | 16 +++ .../Http/HttpMessageBinding.cs | 51 ++++++++ .../Http/HttpOperationBinding.cs | 61 +++++----- .../Kafka/KafkaChannelBinding.cs | 85 ++++++++++++++ .../Kafka/KafkaMessageBinding.cs | 57 +++------ .../Kafka/KafkaOperationBinding.cs | 54 +++++++++ .../Kafka/KafkaServerBinding.cs | 45 +++---- .../Kafka/TopicConfigurationObject.cs | 10 +- .../LEGO.AsyncAPI.Bindings.csproj | 38 ++++++ .../MessageBinding{T}.cs | 16 +++ .../OperationBinding{T}.cs | 16 +++ .../Pulsar/Persistence.cs | 0 .../Pulsar/PulsarChannelBinding.cs | 66 +++++------ .../Pulsar/PulsarServerBinding.cs | 42 +++++++ .../Pulsar/RetentionDefinition.cs | 0 .../ServerBinding{T}.cs | 16 +++ .../WebSockets/WebSocketsChannelBinding.cs | 48 +++----- src/LEGO.AsyncAPI.Bindings/stylecop.json | 15 +++ .../AsyncApiReaderSettings.cs | 6 + .../AsyncApiYamlDocumentReader.cs | 8 ++ .../BindingDeserializer.cs | 31 +++++ .../AsyncApiHttpBindingsDeserializer.cs | 25 ---- .../AsyncApiKafkaBindingsDeserializer.cs | 59 ---------- .../AsyncApiPulsarBindingsDeserializer.cs | 43 ------- .../AsyncApiWebSocketsBindingsDeserializer.cs | 18 --- .../Interface/IBindingParser{T}.cs | 12 ++ .../LEGO.AsyncAPI.Readers.csproj | 4 + .../ParseNodes/FixedFieldMap.cs | 4 +- .../ParseNodes/MapNode.cs | 50 +------- .../ParseNodes/ParseNode.cs | 2 +- .../ParseNodes/PropertyNode.cs | 2 +- .../ParseNodes/ValueNode.cs | 2 +- src/LEGO.AsyncAPI.Readers/ParsingContext.cs | 8 ++ .../V2/AsyncApiBindingDeserializer.cs | 72 ------------ .../V2/AsyncApiChannelBindingDeserializer.cs | 34 +++--- .../V2/AsyncApiComponentsDeserializer.cs | 10 +- .../V2/AsyncApiDeserializer.cs | 8 +- .../V2/AsyncApiMessageBindingDeserializer.cs | 24 ++-- .../V2/AsyncApiMessageDeserializer.cs | 4 +- .../V2/AsyncApiMessageTraitDeserializer.cs | 2 +- .../AsyncApiOperationBindingDeserializer.cs | 24 ++-- .../V2/AsyncApiParameterDeserializer.cs | 2 +- .../V2/AsyncApiSchemaDeserializer.cs | 10 +- .../V2/AsyncApiServerBindingDeserializer.cs | 30 +++-- .../V2/AsyncApiV2VersionService.cs | 2 +- src/LEGO.AsyncAPI.Readers/YamlHelper.cs | 1 - .../Expressions/MethodExpression.cs | 7 -- src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs | 41 +++++++ src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs | 7 +- .../Models/AsyncApiComponents.cs | 16 +-- src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs | 10 +- .../Models/AsyncApiWriterExtensions.cs | 2 +- .../Models/Bindings/BindingType.cs | 21 ---- .../Bindings/Http/HttpMessageBinding.cs | 75 ------------ .../Bindings/Kafka/KafkaChannelBinding.cs | 84 ------------- .../Bindings/Kafka/KafkaOperationBinding.cs | 73 ------------ .../Bindings/Pulsar/PulsarServerBinding.cs | 66 ----------- .../Models/Interfaces/IBinding.cs | 8 +- src/LEGO.AsyncAPI/Models/ReferenceType.cs | 16 ++- .../Services/AsyncApiReferenceResolver.cs | 25 ++-- .../Services/AsyncApiVisitorBase.cs | 10 +- src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs | 32 ++--- src/LEGO.AsyncAPI/Services/CurrentKeys.cs | 2 + .../Validation/Rules/AsyncApiContactRules.cs | 1 - .../Rules/AsyncApiCorrelationIdRules.cs | 1 - .../Validation/Rules/AsyncApiDocumentRules.cs | 1 - .../Rules/AsyncApiExtensionRules.cs | 1 - .../Validation/Rules/AsyncApiLicenseRules.cs | 1 - .../Validation/Rules/AsyncApiTagRules.cs | 1 - .../Writers/AsyncApiWriterExtensions.cs | 6 + .../Writers/AsyncApiWriterSettings.cs | 2 +- src/LEGO.AsyncAPI/Writers/StringExtensions.cs | 2 +- .../AsyncApiDocumentBuilder.cs | 20 ++-- .../AsyncApiDocumentV2Tests.cs | 110 ++++++++++++++++-- .../AsyncApiLicenseTests.cs | 4 +- .../AsyncApiReaderTests.cs | 2 + .../Bindings/CustomBinding_Should.cs | 78 +++++++++++++ .../Bindings/Http/HttpBindings_Should.cs | 23 ++-- .../Bindings/Kafka/KafkaBindings_Should.cs | 33 ++++-- .../Bindings/Pulsar/PulsarBindings_Should.cs | 53 +++++---- .../WebSockets/WebSocketBindings_Should.cs | 13 ++- .../LEGO.AsyncAPI.Tests.csproj | 5 + .../Models/AsyncApiChannel_Should.cs | 12 +- .../Models/AsyncApiContact_Should.cs | 2 + .../AsyncApiExternalDocumentation_Should.cs | 2 + .../Models/AsyncApiInfo_Should.cs | 2 + .../Models/AsyncApiLicense_Should.cs | 2 + .../Models/AsyncApiMessageExample_Should.cs | 2 + .../Models/AsyncApiMessage_Should.cs | 20 ++-- .../Models/AsyncApiOAuthFlow_Should.cs | 2 + .../Models/AsyncApiOperation_Should.cs | 16 +-- .../Models/AsyncApiSchema_Should.cs | 6 +- .../AsyncApiSecurityRequirement_Should.cs | 12 ++ .../Models/AsyncApiServer_Should.cs | 10 +- test/LEGO.AsyncAPI.Tests/StringExtensions.cs | 4 +- .../Validation/ValidationRulesetTests.cs | 4 +- test/LEGO.AsyncAPI.Tests/stylecop.json | 15 +++ 102 files changed, 1190 insertions(+), 1019 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/Binding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Http/HttpOperationBinding.cs (55%) create mode 100644 src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/KafkaMessageBinding.cs (57%) create mode 100644 src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/KafkaServerBinding.cs (53%) rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/TopicConfigurationObject.cs (93%) create mode 100644 src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj create mode 100644 src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Pulsar/Persistence.cs (100%) rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Pulsar/PulsarChannelBinding.cs (55%) create mode 100644 src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Pulsar/RetentionDefinition.cs (100%) create mode 100644 src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/WebSockets/WebSocketsChannelBinding.cs (57%) create mode 100644 src/LEGO.AsyncAPI.Bindings/stylecop.json create mode 100644 src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs create mode 100644 src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs create mode 100644 src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaChannelBinding.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs create mode 100644 test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs create mode 100644 test/LEGO.AsyncAPI.Tests/stylecop.json diff --git a/.github/workflows/release-internal.yml b/.github/workflows/release-internal.yml index 8b54315b..df4c5e58 100644 --- a/.github/workflows/release-internal.yml +++ b/.github/workflows/release-internal.yml @@ -5,7 +5,7 @@ on: paths: - 'src/LEGO.AsyncAPI/**' - 'src/LEGO.AsyncAPI.Readers/**' - - 'src/LEGO.AsyncAPI.Writers/**' + - 'src/LEGO.AsyncAPI.Bindings/**' - ".github/workflows/release-package.yml" - '!**/*.md' workflow_dispatch: @@ -16,7 +16,7 @@ jobs: name: Publish NuGet packages strategy: matrix: - package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers"] + package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers", "LEGO.AsyncAPI.Bindings"] steps: - name: Checkout repository uses: actions/checkout@v1 diff --git a/.github/workflows/release-package.yml b/.github/workflows/release-package.yml index 0f064176..9e52a9fe 100644 --- a/.github/workflows/release-package.yml +++ b/.github/workflows/release-package.yml @@ -56,7 +56,7 @@ jobs: environment: AsyncAPI strategy: matrix: - package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers" ] + package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers", "LEGO.AsyncAPI.Bindings" ] steps: - name: Checkout repository uses: actions/checkout@v1 diff --git a/AsyncAPI.sln b/AsyncAPI.sln index bf944501..f972fa3d 100644 --- a/AsyncAPI.sln +++ b/AsyncAPI.sln @@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LEGO.AsyncAPI.Bindings", "src\LEGO.AsyncAPI.Bindings\LEGO.AsyncAPI.Bindings.csproj", "{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,6 +34,10 @@ Global {7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Release|Any CPU.Build.0 = Release|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/LEGO.AsyncAPI.Bindings/Binding.cs b/src/LEGO.AsyncAPI.Bindings/Binding.cs new file mode 100644 index 00000000..86f2cfde --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Binding.cs @@ -0,0 +1,30 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.Interface; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class Binding : AsyncApiBinding, IBindingParser + where T : IBinding, new() + { + protected static void ParseMap( + MapNode mapNode, + T domainObject, + FixedFieldMap fixedFieldMap) + { + if (mapNode == null) + { + return; + } + + foreach (var propertyNode in mapNode) + { + propertyNode.ParseField(domainObject, fixedFieldMap, null); + } + } + + public abstract T LoadBinding(PropertyNode node); + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs new file mode 100644 index 00000000..0a34ec39 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -0,0 +1,71 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Bindings.Http; + using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Bindings.Pulsar; + using LEGO.AsyncAPI.Bindings.WebSockets; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.Interface; + + public static class BindingsCollection + { + public static TCollection Add( + this TCollection destination, + IEnumerable source) + where TCollection : ICollection + { + ArgumentNullException.ThrowIfNull(destination); + ArgumentNullException.ThrowIfNull(source); + + if (destination is List list) + { + list.AddRange(source); + return destination; + } + + foreach (var item in source) + { + destination.Add(item); + } + + return destination; + } + + public static IEnumerable> All => new List> + { + Pulsar, + Kafka, + Http, + }; + + public static IEnumerable> Http => new List> + { + new HttpOperationBinding(), + new HttpMessageBinding() + }; + + public static IEnumerable> Websockets => new List> + { + new WebSocketsChannelBinding(), + }; + + public static IEnumerable> Kafka => new List> + { + new KafkaServerBinding(), + new KafkaChannelBinding(), + new KafkaOperationBinding(), + new KafkaMessageBinding(), + }; + + public static IEnumerable> Pulsar => new List> + { + // Pulsar + new PulsarServerBinding(), + new PulsarChannelBinding(), + }; + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs new file mode 100644 index 00000000..d792809c --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class ChannelBinding : Binding, IChannelBinding + where T : IChannelBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("ChannelBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs new file mode 100644 index 00000000..b4985ca3 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs @@ -0,0 +1,51 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +using LEGO.AsyncAPI.Models; + +namespace LEGO.AsyncAPI.Bindings.Http +{ + using System; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for http messaging channels. + /// + public class HttpMessageBinding : MessageBinding + { + + /// + /// A Schema object containing the definitions for HTTP-specific headers. This schema MUST be of type object and have a properties key. + /// + public AsyncApiSchema Headers { get; set; } + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + + writer.WriteOptionalObject(AsyncApiConstants.Headers, this.Headers, (w, h) => h.SerializeV2(w)); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + + writer.WriteEndObject(); + } + + public override string BindingKey => "http"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, + }; + + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs similarity index 55% rename from src/LEGO.AsyncAPI/Models/Bindings/Http/HttpOperationBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs index 27a91798..0921eabf 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs @@ -1,21 +1,32 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Http +using LEGO.AsyncAPI.Models; + +namespace LEGO.AsyncAPI.Bindings.Http { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for http operations. /// - public class HttpOperationBinding : IOperationBinding + public class HttpOperationBinding : OperationBinding { + public enum HttpOperationType + { + [Display("request")] + Request, + + [Display("response")] + Response, + } /// /// REQUIRED. Type of operation. Its value MUST be either request or response. /// - public string Type { get; set; } + public HttpOperationType? Type { get; set; } /// /// When type is request, this is the HTTP method, otherwise it MUST be ignored. Its value MUST be one of GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, CONNECT, and TRACE. @@ -27,15 +38,10 @@ public class HttpOperationBinding : IOperationBinding /// public AsyncApiSchema Query { get; set; } - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - /// /// Serialize to AsyncAPI V2 document without using reference. /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -44,37 +50,22 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteStartObject(); - writer.WriteRequiredProperty(AsyncApiConstants.Type, this.Type); + writer.WriteRequiredProperty(AsyncApiConstants.Type, this.Type.GetDisplayName()); writer.WriteOptionalProperty(AsyncApiConstants.Method, this.Method); writer.WriteOptionalObject(AsyncApiConstants.Query, this.Query, (w, h) => h.SerializeV2(w)); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - public void SerializeV2(IAsyncApiWriter writer) + protected override FixedFieldMap FixedFieldMap => new() { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, + { "query", (a, n) => { a.Query = JsonSchemaDeserializer.LoadSchema(n); } }, + }; - BindingType IBinding.Type => BindingType.Http; + public override string BindingKey => "http"; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs new file mode 100644 index 00000000..921903c1 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs @@ -0,0 +1,85 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Kafka +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Bindings.Kafka; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for Kafka channel settings. + /// + public class KafkaChannelBinding : ChannelBinding + { + /// + /// Kafka topic name if different from channel name. + /// + public string Topic { get; set; } + + /// + /// Number of partitions configured on this topic (useful to know how many parallel consumers you may run). + /// + public int? Partitions { get; set; } + + /// + /// Number of replicas configured on this topic. + /// + public int? Replicas { get; set; } + + /// + /// Topic configuration properties that are relevant for the API. + /// + public TopicConfigurationObject TopicConfiguration { get; set; } + + public override string BindingKey => "kafka"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, + { "partitions", (a, n) => { a.Partitions = n.GetIntegerValue(); } }, + { "topicConfiguration", (a, n) => { a.TopicConfiguration = this.LoadTopicConfiguration(n); } }, + { "replicas", (a, n) => { a.Replicas = n.GetIntegerValue(); } }, + }; + + private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new() + { + { "cleanup.policy", (a, n) => { a.CleanupPolicy = n.CreateSimpleList(s => s.GetScalarValue()); } }, + { "retention.ms", (a, n) => { a.RetentionMiliseconds = n.GetIntegerValue(); } }, + { "retention.bytes", (a, n) => { a.RetentionBytes = n.GetIntegerValue(); } }, + { "delete.retention.ms", (a, n) => { a.DeleteRetentionMiliseconds = n.GetIntegerValue(); } }, + { "max.message.bytes", (a, n) => { a.MaxMessageBytes = n.GetIntegerValue(); } }, + }; + + private TopicConfigurationObject LoadTopicConfiguration(ParseNode node) + { + var mapNode = node.CheckMapNode("topicConfiguration"); + var retention = new TopicConfigurationObject(); + ParseMap(mapNode, retention, kafkaChannelTopicConfigurationObjectFixedFields); + return retention; + } + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty(AsyncApiConstants.Topic, this.Topic); + writer.WriteOptionalProperty(AsyncApiConstants.Partitions, this.Partitions); + writer.WriteOptionalProperty(AsyncApiConstants.Replicas, this.Replicas); + writer.WriteOptionalObject(AsyncApiConstants.TopicConfiguration, this.TopicConfiguration, (w, t) => t.Serialize(w)); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs similarity index 57% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaMessageBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs index 1fd4bb90..31123de1 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaMessageBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs @@ -1,16 +1,17 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Kafka +namespace LEGO.AsyncAPI.Bindings.Kafka { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for kafka messages. /// - public class KafkaMessageBinding : IMessageBinding + public class KafkaMessageBinding : MessageBinding { /// /// The message key. NOTE: You can also use the reference object way. @@ -35,22 +36,8 @@ public class KafkaMessageBinding : IMessageBinding /// /// The version of this binding. If omitted, "latest" MUST be assumed. /// - public string BindingVersion { get; set; } - - /// - /// Indicates if object is populated with data or is just a reference to the data. - /// - public bool UnresolvedReference { get; set; } - - /// - /// Reference object. - /// - public AsyncApiReference Reference { get; set; } - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -64,6 +51,7 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.SchemaIdPayloadEncoding, this.SchemaIdPayloadEncoding); writer.WriteOptionalProperty(AsyncApiConstants.SchemaLookupStrategy, this.SchemaLookupStrategy); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } @@ -73,28 +61,17 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) /// /// The writer. /// writer - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - this.SerializeV2WithoutReference(writer); - } - /// - /// Gets or sets this object MAY be extended with Specification Extensions. - /// To protect the API from leaking the underlying JSON library, the extension data extraction is handled by a customer resolver. - /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public override string BindingKey => "kafka"; - public BindingType Type => BindingType.Kafka; + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "key", (a, n) => { a.Key = JsonSchemaDeserializer.LoadSchema(n); } }, + { "schemaIdLocation", (a, n) => { a.SchemaIdLocation = n.GetScalarValue(); } }, + { "schemaIdPayloadEncoding", (a, n) => { a.SchemaIdPayloadEncoding = n.GetScalarValue(); } }, + { "schemaLookupStrategy", (a, n) => { a.SchemaLookupStrategy = n.GetScalarValue(); } }, + }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs new file mode 100644 index 00000000..db80e0ce --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs @@ -0,0 +1,54 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Kafka +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for Kafka operations. + /// + public class KafkaOperationBinding : OperationBinding + { + /// + /// Id of the consumer group. + /// + public AsyncApiSchema GroupId { get; set; } + + /// + /// Id of the consumer inside a consumer group. + /// + public AsyncApiSchema ClientId { get; set; } + + public override string BindingKey => "kafka"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "groupId", (a, n) => { a.GroupId = JsonSchemaDeserializer.LoadSchema(n); } }, + { "clientId", (a, n) => { a.ClientId = JsonSchemaDeserializer.LoadSchema(n); } }, + }; + + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalObject(AsyncApiConstants.GroupId, this.GroupId, (w, h) => h.SerializeV2(w)); + writer.WriteOptionalObject(AsyncApiConstants.ClientId, this.ClientId, (w, h) => h.SerializeV2(w)); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs similarity index 53% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaServerBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs index bd9d6b19..1d40a798 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaServerBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs @@ -1,16 +1,16 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Kafka +namespace LEGO.AsyncAPI.Bindings.Kafka { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for Kafka server settings. /// - public class KafkaServerBinding : IServerBinding + public class KafkaServerBinding : ServerBinding { /// /// API URL for the Schema Registry used when producing Kafka messages (if a Schema Registry was used) @@ -22,23 +22,20 @@ public class KafkaServerBinding : IServerBinding /// public string SchemaRegistryVendor { get; set; } - /// - /// The version of this binding. - /// - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Kafka; - public bool UnresolvedReference { get; set; } + public override string BindingKey => "kafka"; - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "schemaRegistryUrl", (a, n) => { a.SchemaRegistryUrl = n.GetScalarValue(); } }, + { "schemaRegistryVendor", (a, n) => { a.SchemaRegistryVendor = n.GetScalarValue(); } }, + }; /// /// Serialize to AsyncAPI V2 document without using reference. /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -49,24 +46,8 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.SchemaRegistryUrl, this.SchemaRegistryUrl); writer.WriteOptionalProperty(AsyncApiConstants.SchemaRegistryVendor, this.SchemaRegistryVendor); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } } } diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/TopicConfigurationObject.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs similarity index 93% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/TopicConfigurationObject.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs index 5a1ccffe..9edc25af 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/TopicConfigurationObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs @@ -1,12 +1,12 @@ // Copyright (c) The LEGO Group. All rights reserved. -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; -using System; -using System.Collections.Generic; - namespace LEGO.AsyncAPI.Models.Bindings.Kafka { + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + using System; + using System.Collections.Generic; + public class TopicConfigurationObject : IAsyncApiElement { /// diff --git a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj new file mode 100644 index 00000000..c7f7b9a0 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj @@ -0,0 +1,38 @@ + + + + net6.0 + disable + The LEGO Group + https://github.com/LEGO/AsyncAPI.NET + README.md + AsyncAPI.NET Bindings + asyncapi .net openapi documentation + AsyncAPI.NET.Bindings + LEGO.AsyncAPI.Bindings + LEGO.AsyncAPI.Bindings + https://github.com/LEGO/AsyncAPI.NET + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs new file mode 100644 index 00000000..6cf7c457 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers; +using LEGO.AsyncAPI.Readers.ParseNodes; + +namespace LEGO.AsyncAPI.Bindings +{ + public abstract class MessageBinding : Binding, IMessageBinding + where T : IMessageBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("MessageBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs new file mode 100644 index 00000000..87432ab0 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers; +using LEGO.AsyncAPI.Readers.ParseNodes; + +namespace LEGO.AsyncAPI.Bindings +{ + public abstract class OperationBinding : Binding , IOperationBinding + where T : IOperationBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("OperationBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/Persistence.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/Persistence.cs similarity index 100% rename from src/LEGO.AsyncAPI/Models/Bindings/Pulsar/Persistence.cs rename to src/LEGO.AsyncAPI.Bindings/Pulsar/Persistence.cs diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs similarity index 55% rename from src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarChannelBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs index 7a4f504f..481043c4 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs @@ -1,16 +1,15 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Pulsar +namespace LEGO.AsyncAPI.Bindings.Pulsar { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Bindings.Pulsar; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; - /// - /// Binding class for Pulsar server settings. - /// - public class PulsarChannelBinding : IChannelBinding + public class PulsarChannelBinding : ChannelBinding { /// /// The namespace associated with the topic. @@ -38,7 +37,7 @@ public class PulsarChannelBinding : IChannelBinding public RetentionDefinition Retention { get; set; } /// - /// Message Time-to-live in seconds. + /// Message Time-to-live in seconds. /// public int? TTL { get; set; } @@ -47,22 +46,9 @@ public class PulsarChannelBinding : IChannelBinding /// public bool? Deduplication { get; set; } - /// - /// The version of this binding. - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Pulsar; - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); + public override string BindingKey => "pulsar"; - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -78,24 +64,34 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.TTL, this.TTL); writer.WriteOptionalProperty(AsyncApiConstants.Deduplication, this.Deduplication); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - public void SerializeV2(IAsyncApiWriter writer) + protected override FixedFieldMap FixedFieldMap => new() { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "namespace", (a, n) => { a.Namespace = n.GetScalarValue(); } }, + { "persistence", (a, n) => { a.Persistence = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "compaction", (a, n) => { a.Compaction = n.GetIntegerValue(); } }, + { "retention", (a, n) => { a.Retention = this.LoadRetention(n); } }, + { "geo-replication", (a, n) => { a.GeoReplication = n.CreateSimpleList(s => s.GetScalarValue()); } }, + { "ttl", (a, n) => { a.TTL = n.GetIntegerValue(); } }, + { "deduplication", (a, n) => { a.Deduplication = n.GetBooleanValue(); } }, + }; + + private FixedFieldMap pulsarServerBindingRetentionFixedFields = new() + { + { "time", (a, n) => { a.Time = n.GetIntegerValue(); } }, + { "size", (a, n) => { a.Size = n.GetIntegerValue(); } }, + }; - this.SerializeV2WithoutReference(writer); + private RetentionDefinition LoadRetention(ParseNode node) + { + var mapNode = node.CheckMapNode("retention"); + var retention = new RetentionDefinition(); + ParseMap(mapNode, retention, this.pulsarServerBindingRetentionFixedFields); + return retention; } } } diff --git a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs new file mode 100644 index 00000000..e767443d --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs @@ -0,0 +1,42 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Pulsar +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for Pulsar server settings. + /// + public class PulsarServerBinding : ServerBinding + { + /// + /// The pulsar tenant. If omitted, "public" must be assumed. + /// + public string Tenant { get; set; } + + public override string BindingKey => "pulsar"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "tenant", (a, n) => { a.Tenant = n.GetScalarValue(); } }, + }; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty(AsyncApiConstants.Tenant, this.Tenant); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/RetentionDefinition.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/RetentionDefinition.cs similarity index 100% rename from src/LEGO.AsyncAPI/Models/Bindings/Pulsar/RetentionDefinition.cs rename to src/LEGO.AsyncAPI.Bindings/Pulsar/RetentionDefinition.cs diff --git a/src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs new file mode 100644 index 00000000..ef945fde --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers; +using LEGO.AsyncAPI.Readers.ParseNodes; + +namespace LEGO.AsyncAPI.Bindings +{ + public abstract class ServerBinding : Binding, IServerBinding + where T : IServerBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("ServerBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/WebSockets/WebSocketsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs similarity index 57% rename from src/LEGO.AsyncAPI/Models/Bindings/WebSockets/WebSocketsChannelBinding.cs rename to src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs index 7aa2e793..94afcb55 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/WebSockets/WebSocketsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs @@ -1,13 +1,14 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.WebSockets +namespace LEGO.AsyncAPI.Bindings.WebSockets { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; - public class WebSocketsChannelBinding : IChannelBinding + public class WebSocketsChannelBinding : ChannelBinding { /// /// The HTTP method t use when establishing the connection. Its value MUST be either 'GET' or 'POST'. @@ -24,18 +25,17 @@ public class WebSocketsChannelBinding : IChannelBinding /// public AsyncApiSchema Headers { get; set; } - public string BindingVersion { get; set; } + public override string BindingKey => "websockets"; - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = - new Dictionary(); - - public BindingType Type => BindingType.Websockets; + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, + { "query", (a, n) => { a.Query = JsonSchemaDeserializer.LoadSchema(n); } }, + { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, + }; - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -48,24 +48,8 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalObject(AsyncApiConstants.Query, this.Query, (w, h) => h.SerializeV2(w)); writer.WriteOptionalObject(AsyncApiConstants.Headers, this.Headers, (w, h) => h.SerializeV2(w)); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } } -} \ No newline at end of file +} diff --git a/src/LEGO.AsyncAPI.Bindings/stylecop.json b/src/LEGO.AsyncAPI.Bindings/stylecop.json new file mode 100644 index 00000000..0a8f4661 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/stylecop.json @@ -0,0 +1,15 @@ +{ + // ACTION REQUIRED: This file was automatically added to your project, but it + // will not take effect until additional steps are taken to enable it. See the + // following page for additional information: + // + // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md + + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "The LEGO Group", + "xmlHeader": false + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index 21fe1c73..01576991 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -6,6 +6,7 @@ namespace LEGO.AsyncAPI.Readers using System.Collections.Generic; using System.IO; using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.Interface; using LEGO.AsyncAPI.Validations; public enum ReferenceResolutionSetting @@ -40,6 +41,11 @@ public Dictionary> { get; set; } = new Dictionary>(); + public List> + Bindings + { get; } = + new List>(); + /// /// Rules to use for validating AsyncApi specification. If none are provided a default set of rules are applied. /// diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs index 9dae2141..cc03a892 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs @@ -41,6 +41,10 @@ public AsyncApiDocument Read(YamlDocument input, out AsyncApiDiagnostic diagnost var context = new ParsingContext(diagnostic) { ExtensionParsers = this.settings.ExtensionParsers, + ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + ChannelBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + OperationBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + MessageBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), }; AsyncApiDocument document = null; @@ -144,6 +148,10 @@ public T ReadFragment(YamlDocument input, AsyncApiVersion version, out AsyncA var context = new ParsingContext(diagnostic) { ExtensionParsers = this.settings.ExtensionParsers, + ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + ChannelBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + OperationBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + MessageBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), }; IAsyncApiElement element = null; diff --git a/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs new file mode 100644 index 00000000..5744985d --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs @@ -0,0 +1,31 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers +{ + using LEGO.AsyncAPI.Extensions; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public class BindingDeserializer + { + public static T LoadBinding(string nodeName, ParseNode node, FixedFieldMap fieldMap) + where T : IBinding, new() + { + var mapNode = node.CheckMapNode(nodeName); + var binding = new T(); + + AsyncApiV2Deserializer.ParseMap(mapNode, binding, fieldMap, BindingPatternExtensionFields()); + + return binding; + } + + private static PatternFieldMap BindingPatternExtensionFields() + where T : IBinding, new() + { + return new() + { + { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) }, + }; + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs deleted file mode 100644 index 72c2579b..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Models.Bindings.Http; - using LEGO.AsyncAPI.Readers.ParseNodes; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap httpMessageBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "headers", (a, n) => { a.Headers = LoadSchema(n); } }, - }; - - private static FixedFieldMap httpOperationBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "type", (a, n) => { a.Type = n.GetScalarValue(); } }, - { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, - { "query", (a, n) => { a.Query = LoadSchema(n); } }, - }; - - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs deleted file mode 100644 index 0d80eb8f..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Models.Bindings.Kafka; - using LEGO.AsyncAPI.Readers.ParseNodes; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap kafkaServerBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "schemaRegistryUrl", (a, n) => { a.SchemaRegistryUrl = n.GetScalarValue(); } }, - { "schemaRegistryVendor", (a, n) => { a.SchemaRegistryVendor = n.GetScalarValue(); } }, - }; - - private static FixedFieldMap kafkaChannelBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, - { "partitions", (a, n) => { a.Partitions = n.GetIntegerValue(); } }, - { "topicConfiguration", (a, n) => { a.TopicConfiguration = LoadTopicConfiguration(n); } }, - { "replicas", (a, n) => { a.Replicas = n.GetIntegerValue(); } }, - }; - - private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new() - { - { "cleanup.policy", (a, n) => { a.CleanupPolicy = n.CreateSimpleList(s => s.GetScalarValue()); } }, - { "retention.ms", (a, n) => { a.RetentionMiliseconds = n.GetIntegerValue(); } }, - { "retention.bytes", (a, n) => { a.RetentionBytes = n.GetIntegerValue(); } }, - { "delete.retention.ms", (a, n) => { a.DeleteRetentionMiliseconds = n.GetIntegerValue(); } }, - { "max.message.bytes", (a, n) => { a.MaxMessageBytes = n.GetIntegerValue(); } }, - }; - - private static FixedFieldMap kafkaOperationBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "groupId", (a, n) => { a.GroupId = LoadSchema(n); } }, - { "clientId", (a, n) => { a.ClientId = LoadSchema(n); } }, - }; - - private static FixedFieldMap kafkaMessageBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "key", (a, n) => { a.Key = LoadSchema(n); } }, - { "schemaIdLocation", (a, n) => { a.SchemaIdLocation = n.GetScalarValue(); } }, - { "schemaIdPayloadEncoding", (a, n) => { a.SchemaIdPayloadEncoding = n.GetScalarValue(); } }, - { "schemaLookupStrategy", (a, n) => { a.SchemaLookupStrategy = n.GetScalarValue(); } }, - }; - - private static TopicConfigurationObject LoadTopicConfiguration(ParseNode node) - { - var mapNode = node.CheckMapNode("topicConfiguration"); - var retention = new TopicConfigurationObject(); - ParseMap(mapNode, retention, kafkaChannelTopicConfigurationObjectFixedFields, null); - return retention; - } - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs deleted file mode 100644 index 6250a064..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Models.Bindings.Pulsar; - using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap pulsarServerBindingFixedFields = new () - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "tenant", (a, n) => { a.Tenant = n.GetScalarValue(); } }, - }; - - private static FixedFieldMap pulsarChannelBindingFixedFields = new () - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "namespace", (a, n) => { a.Namespace = n.GetScalarValue(); } }, - { "persistence", (a, n) => { a.Persistence = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "compaction", (a, n) => { a.Compaction = n.GetIntegerValue(); } }, - { "retention", (a, n) => { a.Retention = LoadRetention(n); } }, - { "geo-replication", (a, n) => { a.GeoReplication = n.CreateSimpleList(s => s.GetScalarValue()); } }, - { "ttl", (a, n) => { a.TTL = n.GetIntegerValue(); } }, - { "deduplication", (a, n) => { a.Deduplication = n.GetBooleanValue(); } }, - }; - - private static FixedFieldMap pulsarServerBindingRetentionFixedFields = new () - { - { "time", (a, n) => { a.Time = n.GetIntegerValue(); } }, - { "size", (a, n) => { a.Size = n.GetIntegerValue(); } }, - }; - - private static RetentionDefinition LoadRetention(ParseNode node) - { - var mapNode = node.CheckMapNode("retention"); - var retention = new RetentionDefinition(); - ParseMap(mapNode, retention, pulsarServerBindingRetentionFixedFields, null); - return retention; - } - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs deleted file mode 100644 index 4d4af870..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Readers.ParseNodes; - using Models.Bindings.WebSockets; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap webSocketsChannelBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, - { "query", (a, n) => { a.Query = LoadSchema(n); } }, - { "headers", (a, n) => { a.Headers = LoadSchema(n); } }, - }; - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs b/src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs new file mode 100644 index 00000000..9c83fe4d --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs @@ -0,0 +1,12 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers.Interface +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public interface IBindingParser : IBinding + { + T LoadBinding(PropertyNode node); + } +} diff --git a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj index d1e8bd74..6fd9d2e7 100644 --- a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj +++ b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj @@ -44,4 +44,8 @@ + + + + diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs index efb046dd..316a927a 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System; using System.Collections.Generic; - internal class FixedFieldMap : Dictionary> + public class FixedFieldMap : Dictionary> { } -} \ No newline at end of file +} diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs index 41da8d03..1b944de6 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; - internal class MapNode : ParseNode, IEnumerable + public class MapNode : ParseNode, IEnumerable { private readonly YamlMappingNode node; private readonly List nodes; @@ -89,54 +89,6 @@ public override Dictionary CreateMap(Func map) return nodes.ToDictionary(k => k.key, v => v.value); } - public override Dictionary CreateBindingMapWithReference( - ReferenceType referenceType, - Func map) - { - var yamlMap = this.node; - if (yamlMap == null) - { - throw new AsyncApiReaderException($"Expected map while parsing {typeof(T).Name}", this.Context); - } - - var nodes = yamlMap.Select( - n => - { - var key = n.Key.GetScalarValue(); - (string key, T value) entry; - try - { - this.Context.StartObject(key); - entry = ( - key: key, - value: map(new PropertyNode(this.Context, key, n.Value)) - ); - - if (entry.value == null) - { - return default; - } - - if (entry.value.Reference == null) - { - entry.value.Reference = new AsyncApiReference() - { - Type = referenceType, - Id = entry.key, - }; - } - } - finally - { - this.Context.EndObject(); - } - - return entry; - } - ); - return nodes.Where(n => n != default).ToDictionary(k => k.key, v => v.value); - } - public override Dictionary CreateMapWithReference( ReferenceType referenceType, Func map) diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs index aa106cd2..94fc4268 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using LEGO.AsyncAPI.Readers.Exceptions; using YamlDotNet.RepresentationModel; - internal abstract class ParseNode + public abstract class ParseNode { protected ParseNode(ParsingContext parsingContext) { diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs index 99e1a33e..42793fa4 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs @@ -11,7 +11,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using LEGO.AsyncAPI.Readers.Exceptions; using YamlDotNet.RepresentationModel; - internal class PropertyNode : ParseNode + public class PropertyNode : ParseNode { public PropertyNode(ParsingContext context, string name, YamlNode node) : base( diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs index 63e0238e..385cfca0 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using YamlDotNet.Core; using YamlDotNet.RepresentationModel; - internal class ValueNode : ParseNode + public class ValueNode : ParseNode { private readonly YamlScalarNode node; diff --git a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs index 63e8d1a6..58b0b04b 100644 --- a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs +++ b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs @@ -25,6 +25,14 @@ internal Dictionary> ExtensionPar = new (); + internal Dictionary> ServerBindingParsers { get; set; } = new(); + + internal Dictionary> ChannelBindingParsers { get; set; } + + internal Dictionary> OperationBindingParsers { get; set; } = new(); + + internal Dictionary> MessageBindingParsers { get; set; } = new(); + internal RootNode RootNode { get; set; } internal List Tags { get; private set; } = new (); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs deleted file mode 100644 index 7c997dc0..00000000 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Extensions; - using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Readers.ParseNodes; - using System; - - internal static partial class AsyncApiV2Deserializer - { - private static Type messageBindingType = typeof(IMessageBinding); - private static Type operationBindingType = typeof(IOperationBinding); - private static Type channelBindingType = typeof(IChannelBinding); - private static Type serverBindingType = typeof(IServerBinding); - - private static PatternFieldMap BindingPatternExtensionFields() - where T : IBinding, new() - { - return new() - { - { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, - }; - } - - internal static T LoadBinding(string nodeName, ParseNode node, FixedFieldMap fieldMap) - where T : IBinding, new() - { - var mapNode = node.CheckMapNode(nodeName); - var pointer = mapNode.GetReferencePointer(); - if (pointer != null) - { - ReferenceType referenceType = ReferenceType.None; - var bindingType = typeof(T); - - if (bindingType.IsAssignableTo(messageBindingType)) - { - referenceType = ReferenceType.MessageBinding; - } - - if (bindingType.IsAssignableTo(operationBindingType)) - { - referenceType = ReferenceType.OperationBinding; - } - - if (bindingType.IsAssignableTo(channelBindingType)) - { - referenceType = ReferenceType.ChannelBinding; - } - - if (bindingType.IsAssignableTo(serverBindingType)) - { - referenceType = ReferenceType.ServerBinding; - } - - if (referenceType == ReferenceType.None) - { - throw new ArgumentException($"ReferenceType not found {typeof(T).Name}"); - } - - return mapNode.GetReferencedObject(referenceType, pointer); - } - - var binding = new T(); - - ParseMap(mapNode, binding, fieldMap, BindingPatternExtensionFields()); - - return binding; - } - } -} diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs index c6e56700..1aab9e85 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs @@ -4,19 +4,21 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { internal static AsyncApiBindings LoadChannelBindings(ParseNode node) { - var mapNode = node.CheckMapNode("channelBinding"); + var mapNode = node.CheckMapNode("channelBindings"); + var pointer = mapNode.GetReferencePointer(); + if (pointer != null) + { + return mapNode.GetReferencedObject>(ReferenceType.ChannelBindings, pointer); + } var channelBindings = new AsyncApiBindings(); - foreach (var property in mapNode) { var channelBinding = LoadChannelBinding(property); @@ -35,21 +37,23 @@ internal static AsyncApiBindings LoadChannelBindings(ParseNode return channelBindings; } - internal static IChannelBinding LoadChannelBinding(ParseNode node) + private static IChannelBinding LoadChannelBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try + { + if (node.Context.ChannelBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } + } + catch (AsyncApiException ex) { - case BindingType.Kafka: - return LoadBinding("ChannelBinding", property.Value, kafkaChannelBindingFixedFields); - case BindingType.Pulsar: - return LoadBinding("ChannelBinding", property.Value, pulsarChannelBindingFixedFields); - case BindingType.Websockets: - return LoadBinding("ChannelBinding", property.Value, webSocketsChannelBindingFixedFields); - default: - throw new AsyncApiException($"ChannelBinding {property.Name} is not supported"); + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs index fa475105..3b63db28 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs @@ -10,7 +10,7 @@ internal static partial class AsyncApiV2Deserializer { private static FixedFieldMap componentsFixedFields = new() { - { "schemas", (a, n) => a.Schemas = n.CreateMapWithReference(ReferenceType.Schema, LoadSchema) }, + { "schemas", (a, n) => a.Schemas = n.CreateMapWithReference(ReferenceType.Schema, JsonSchemaDeserializer.LoadSchema) }, { "servers", (a, n) => a.Servers = n.CreateMapWithReference(ReferenceType.Server, LoadServer) }, { "channels", (a, n) => a.Channels = n.CreateMapWithReference(ReferenceType.Channel, LoadChannel) }, { "messages", (a, n) => a.Messages = n.CreateMapWithReference(ReferenceType.Message, LoadMessage) }, @@ -19,10 +19,10 @@ internal static partial class AsyncApiV2Deserializer { "correlationIds", (a, n) => a.CorrelationIds = n.CreateMapWithReference(ReferenceType.CorrelationId, LoadCorrelationId) }, { "operationTraits", (a, n) => a.OperationTraits = n.CreateMapWithReference(ReferenceType.OperationTrait, LoadOperationTrait) }, { "messageTraits", (a, n) => a.MessageTraits = n.CreateMapWithReference(ReferenceType.MessageTrait, LoadMessageTrait) }, - { "serverBindings", (a, n) => a.ServerBindings = n.CreateMapWithReference(ReferenceType.ServerBinding, LoadServerBinding) }, - { "channelBindings", (a, n) => a.ChannelBindings = n.CreateMapWithReference(ReferenceType.ChannelBinding, LoadChannelBinding) }, - { "operationBindings", (a, n) => a.OperationBindings = n.CreateBindingMapWithReference(ReferenceType.OperationBinding, LoadOperationBinding) }, - { "messageBindings", (a, n) => a.MessageBindings = n.CreateMapWithReference(ReferenceType.MessageBinding, LoadMessageBinding) }, + { "serverBindings", (a, n) => a.ServerBindings = n.CreateMapWithReference(ReferenceType.ServerBindings, LoadServerBindings) }, + { "channelBindings", (a, n) => a.ChannelBindings = n.CreateMapWithReference(ReferenceType.ChannelBindings, LoadChannelBindings) }, + { "operationBindings", (a, n) => a.OperationBindings = n.CreateMapWithReference(ReferenceType.OperationBindings, LoadOperationBindings) }, + { "messageBindings", (a, n) => a.MessageBindings = n.CreateMapWithReference(ReferenceType.MessageBindings, LoadMessageBindings) }, }; private static PatternFieldMap componentsPatternFields = diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs index 0328c4ee..7966f34b 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs @@ -12,7 +12,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static void ParseMap( + internal static void ParseMap( MapNode mapNode, T domainObject, FixedFieldMap fixedFieldMap, @@ -29,7 +29,7 @@ private static void ParseMap( } } - private static void ProcessAnyFields( + internal static void ProcessAnyFields( MapNode mapNode, T domainObject, AnyFieldMap anyFieldMap) @@ -58,7 +58,7 @@ private static void ProcessAnyFields( } } - private static void ProcessAnyListFields( + internal static void ProcessAnyListFields( MapNode mapNode, T domainObject, AnyListFieldMap anyListFieldMap) @@ -163,7 +163,7 @@ public static IAsyncApiAny LoadAny(ParseNode node) return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); } - private static IAsyncApiExtension LoadExtension(string name, ParseNode node) + internal static IAsyncApiExtension LoadExtension(string name, ParseNode node) { try { diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs index c3198737..9a180e84 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs @@ -4,12 +4,8 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; - using LEGO.AsyncAPI.Models.Bindings.Http; - using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { @@ -40,16 +36,20 @@ internal static AsyncApiBindings LoadMessageBindings(ParseNode internal static IMessageBinding LoadMessageBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try { - case BindingType.Kafka: - return LoadBinding("MessageBinding", property.Value, kafkaMessageBindingFixedFields); - case BindingType.Http: - return LoadBinding("MessageBinding", property.Value, httpMessageBindingFixedFields); - default: - throw new AsyncApiException($"MessageBinding {property.Name} is not supported"); + if (node.Context.MessageBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } } + catch (AsyncApiException ex) + { + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); + } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs index 97e40ba7..4c16bf22 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs @@ -21,10 +21,10 @@ internal static partial class AsyncApiV2Deserializer "messageId", (a, n) => { a.MessageId = n.GetScalarValue(); } }, { - "headers", (a, n) => { a.Headers = LoadSchema(n); } + "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, { - "payload", (a, n) => { a.Payload = LoadSchema(n); } + "payload", (a, n) => { a.Payload = JsonSchemaDeserializer.LoadSchema(n); } }, { "correlationId", (a, n) => { a.CorrelationId = LoadCorrelationId(n); } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs index 7e142ee2..eca8af64 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs @@ -11,7 +11,7 @@ internal static partial class AsyncApiV2Deserializer private static FixedFieldMap messageTraitFixedFields = new() { { "messageId", (a, n) => { a.MessageId = n.GetScalarValue(); } }, - { "headers", (a, n) => { a.Headers = LoadSchema(n); } }, + { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, { "correlationId", (a, n) => { a.CorrelationId = LoadCorrelationId(n); } }, { "schemaFormat", (a, n) => { a.SchemaFormat = n.GetScalarValue(); } }, { "contentType", (a, n) => { a.ContentType = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs index 03631600..4d4eb85f 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs @@ -4,16 +4,14 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { internal static AsyncApiBindings LoadOperationBindings(ParseNode node) { - var mapNode = node.CheckMapNode("operationBinding"); + var mapNode = node.CheckMapNode("operationBindings"); var operationBindings = new AsyncApiBindings(); @@ -38,16 +36,20 @@ internal static AsyncApiBindings LoadOperationBindings(ParseN internal static IOperationBinding LoadOperationBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try { - case BindingType.Kafka: - return LoadBinding("OperationBinding", property.Value, kafkaOperationBindingFixedFields); - case BindingType.Http: - return LoadBinding("OperationBinding", property.Value, httpOperationBindingFixedFields); - default: - throw new AsyncApiException($"OperationBinding {property.Name} is not supported"); + if (node.Context.OperationBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } } + catch (AsyncApiException ex) + { + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); + } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs index 3841fbf0..bff810f1 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs @@ -11,7 +11,7 @@ internal static partial class AsyncApiV2Deserializer private static FixedFieldMap parameterFixedFields = new() { { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, - { "schema", (a, n) => { a.Schema = LoadSchema(n); } }, + { "schema", (a, n) => { a.Schema = JsonSchemaDeserializer.LoadSchema(n); } }, { "location", (a, n) => { a.Location = n.GetScalarValue(); } }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index e63d3311..88dc8efb 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Readers using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; - internal static partial class AsyncApiV2Deserializer + public class JsonSchemaDeserializer { private static readonly FixedFieldMap schemaFixedFields = new () { @@ -144,7 +144,7 @@ internal static partial class AsyncApiV2Deserializer "discriminator", (a, n) => { a.Discriminator = n.GetScalarValue(); } }, { - "externalDocs", (a, n) => { a.ExternalDocs = LoadExternalDocs(n); } + "externalDocs", (a, n) => { a.ExternalDocs = AsyncApiV2Deserializer.LoadExternalDocs(n); } }, { "deprecated", (a, n) => { a.Deprecated = bool.Parse(n.GetScalarValue()); } @@ -154,7 +154,7 @@ internal static partial class AsyncApiV2Deserializer private static readonly PatternFieldMap schemaPatternFields = new() { - { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) }, + { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) }, }; private static readonly AnyFieldMap schemaAnyFields = new() @@ -201,8 +201,8 @@ public static AsyncApiSchema LoadSchema(ParseNode node) propertyNode.ParseField(schema, schemaFixedFields, schemaPatternFields); } - ProcessAnyFields(mapNode, schema, schemaAnyFields); - ProcessAnyListFields(mapNode, schema, schemaAnyListFields); + AsyncApiV2Deserializer.ProcessAnyFields(mapNode, schema, schemaAnyFields); + AsyncApiV2Deserializer.ProcessAnyListFields(mapNode, schema, schemaAnyListFields); return schema; } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs index 632d85ab..44c821d3 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs @@ -4,19 +4,21 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { internal static AsyncApiBindings LoadServerBindings(ParseNode node) { - var mapNode = node.CheckMapNode("serverBinding"); + var mapNode = node.CheckMapNode("serverBindings"); + var pointer = mapNode.GetReferencePointer(); + if (pointer != null) + { + return mapNode.GetReferencedObject>(ReferenceType.ServerBindings, pointer); + } var serverBindings = new AsyncApiBindings(); - foreach (var property in mapNode) { var serverBinding = LoadServerBinding(property); @@ -38,16 +40,20 @@ internal static AsyncApiBindings LoadServerBindings(ParseNode no internal static IServerBinding LoadServerBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try + { + if (node.Context.ServerBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } + } + catch (AsyncApiException ex) { - case BindingType.Kafka: - return LoadBinding("ServerBinding", property.Value, kafkaServerBindingFixedFields); - case BindingType.Pulsar: - return LoadBinding("ServerBinding", property.Value, pulsarServerBindingFixedFields); - default: - throw new AsyncApiException($"ServerBinding {property.Name} is not supported"); + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs index b4f34131..c3878072 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs @@ -35,7 +35,7 @@ public AsyncApiV2VersionService(AsyncApiDiagnostic diagnostic) [typeof(AsyncApiOAuthFlows)] = AsyncApiV2Deserializer.LoadOAuthFlows, [typeof(AsyncApiOperation)] = AsyncApiV2Deserializer.LoadOperation, [typeof(AsyncApiParameter)] = AsyncApiV2Deserializer.LoadParameter, - [typeof(AsyncApiSchema)] = AsyncApiV2Deserializer.LoadSchema, + [typeof(AsyncApiSchema)] = JsonSchemaDeserializer.LoadSchema, [typeof(AsyncApiSecurityRequirement)] = AsyncApiV2Deserializer.LoadSecurityRequirement, [typeof(AsyncApiSecurityScheme)] = AsyncApiV2Deserializer.LoadSecurityScheme, [typeof(AsyncApiServer)] = AsyncApiV2Deserializer.LoadServer, diff --git a/src/LEGO.AsyncAPI.Readers/YamlHelper.cs b/src/LEGO.AsyncAPI.Readers/YamlHelper.cs index f29522f0..0f159c73 100644 --- a/src/LEGO.AsyncAPI.Readers/YamlHelper.cs +++ b/src/LEGO.AsyncAPI.Readers/YamlHelper.cs @@ -6,7 +6,6 @@ namespace LEGO.AsyncAPI.Readers using System.Linq; using LEGO.AsyncAPI.Exceptions; using YamlDotNet.RepresentationModel; - internal static class YamlHelper { public static string GetScalarValue(this YamlNode node) diff --git a/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs b/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs index 150e12a0..b9403b07 100644 --- a/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs @@ -16,12 +16,5 @@ public sealed class MethodExpression : RuntimeExpression /// Gets the expression string. /// public override string Expression { get; } = Method; - - /// - /// Private constructor. - /// - public MethodExpression() - { - } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs new file mode 100644 index 00000000..4034b4e9 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs @@ -0,0 +1,41 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public abstract class AsyncApiBinding : IBinding + { + public abstract string BindingKey { get; } + + public bool UnresolvedReference { get; set; } + + public AsyncApiReference Reference { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public string BindingVersion { get; set; } + + public void SerializeV2(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + + this.SerializeProperties(writer); + } + + public abstract void SerializeProperties(IAsyncApiWriter writer); + } +} diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs index b2784755..d039cddf 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs @@ -4,11 +4,10 @@ namespace LEGO.AsyncAPI.Models { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - public class AsyncApiBindings : Dictionary, IAsyncApiSerializable, IAsyncApiReferenceable + public class AsyncApiBindings : Dictionary, IAsyncApiReferenceable where TBinding : IBinding { public bool UnresolvedReference { get; set; } @@ -17,7 +16,7 @@ public class AsyncApiBindings : Dictionary, IAs public void Add(TBinding binding) { - this[binding.Type] = binding; + this[binding.BindingKey] = binding; } public void SerializeV2(IAsyncApiWriter writer) @@ -51,7 +50,7 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) var bindingType = binding.Key; var bindingValue = binding.Value; - writer.WritePropertyName(bindingType.GetDisplayName()); + writer.WritePropertyName(bindingType); bindingValue.SerializeV2(writer); } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs b/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs index 9e8e6e02..6c8a2a4c 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs @@ -69,22 +69,22 @@ public class AsyncApiComponents : IAsyncApiExtensible, IAsyncApiSerializable /// /// An object to hold reusable Server Bindings Objects. /// - public IDictionary ServerBindings { get; set; } = new Dictionary(); + public IDictionary> ServerBindings { get; set; } = new Dictionary>(); /// /// An object to hold reusable Channel Bindings Objects. /// - public IDictionary ChannelBindings { get; set; } = new Dictionary(); + public IDictionary> ChannelBindings { get; set; } = new Dictionary>(); /// /// An object to hold reusable Operation Bindings Objects. /// - public IDictionary OperationBindings { get; set; } = new Dictionary(); + public IDictionary> OperationBindings { get; set; } = new Dictionary>(); /// /// An object to hold reusable Message Bindings Objects. /// - public IDictionary MessageBindings { get; set; } = new Dictionary(); + public IDictionary> MessageBindings { get; set; } = new Dictionary>(); public IDictionary Extensions { get; set; } = new Dictionary(); @@ -311,7 +311,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.ServerBinding && + component.Reference.Type == ReferenceType.ServerBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); @@ -329,7 +329,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.ChannelBinding && + component.Reference.Type == ReferenceType.ChannelBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); @@ -347,7 +347,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.OperationBinding && + component.Reference.Type == ReferenceType.OperationBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); @@ -365,7 +365,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.MessageBinding && + component.Reference.Type == ReferenceType.MessageBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs index 1558dcc2..b28168e2 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs @@ -7,7 +7,7 @@ namespace LEGO.AsyncAPI.Models using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using Services; + using LEGO.AsyncAPI.Services; /// /// This is the root document object for the API specification. It combines resource listing and API declaration together into one document. @@ -189,13 +189,13 @@ public IAsyncApiReferenceable ResolveReference(AsyncApiReference reference) return this.Components.OperationTraits[reference.Id]; case ReferenceType.MessageTrait: return this.Components.MessageTraits[reference.Id]; - case ReferenceType.ServerBinding: + case ReferenceType.ServerBindings: return this.Components.ServerBindings[reference.Id]; - case ReferenceType.ChannelBinding: + case ReferenceType.ChannelBindings: return this.Components.ChannelBindings[reference.Id]; - case ReferenceType.OperationBinding: + case ReferenceType.OperationBindings: return this.Components.OperationBindings[reference.Id]; - case ReferenceType.MessageBinding: + case ReferenceType.MessageBindings: return this.Components.MessageBindings[reference.Id]; default: throw new AsyncApiException("Invalid reference type."); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs index 45e61cbf..c9a5c309 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs @@ -4,7 +4,7 @@ namespace LEGO.AsyncAPI.Models { using LEGO.AsyncAPI.Writers; - internal static class AsyncApiWriterExtensions + public static class AsyncApiWriterExtensions { internal static AsyncApiWriterSettings GetSettings(this IAsyncApiWriter asyncApiWriter) { diff --git a/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs b/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs deleted file mode 100644 index b88d3d5d..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings -{ - using LEGO.AsyncAPI.Attributes; - - public enum BindingType - { - [Display("kafka")] - Kafka, - - [Display("http")] - Http, - - [Display("websockets")] - Websockets, - - [Display("pulsar")] - Pulsar, - } -} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs deleted file mode 100644 index eeb5d05f..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings.Http -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// Binding class for http messaging channels. - /// - public class HttpMessageBinding : IMessageBinding - { - - /// - /// A Schema object containing the definitions for HTTP-specific headers. This schema MUST be of type object and have a properties key. - /// - public AsyncApiSchema Headers { get; set; } - - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - - /// - /// Indicates if object is populated with data or is just a reference to the data - /// - public bool UnresolvedReference { get; set; } - - /// - /// Reference object. - /// - public AsyncApiReference Reference { get; set; } - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - - writer.WriteOptionalObject(AsyncApiConstants.Headers, this.Headers, (w, h) => h.SerializeV2(w)); - writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - - writer.WriteEndObject(); - } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - public BindingType Type => BindingType.Http; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaChannelBinding.cs deleted file mode 100644 index 998c47b3..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaChannelBinding.cs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings.Kafka -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// Binding class for Kafka channel settings. - /// - public class KafkaChannelBinding : IChannelBinding - { - /// - /// Kafka topic name if different from channel name. - /// - public string Topic { get; set; } - - /// - /// Number of partitions configured on this topic (useful to know how many parallel consumers you may run). - /// - public int? Partitions { get; set; } - - /// - /// Number of replicas configured on this topic. - /// - public int? Replicas { get; set; } - - /// - /// Topic configuration properties that are relevant for the API. - /// - public TopicConfigurationObject TopicConfiguration { get; set; } - - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Kafka; - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - writer.WriteOptionalProperty(AsyncApiConstants.Topic, this.Topic); - writer.WriteOptionalProperty(AsyncApiConstants.Partitions, this.Partitions); - writer.WriteOptionalProperty(AsyncApiConstants.Replicas, this.Replicas); - writer.WriteOptionalObject(AsyncApiConstants.TopicConfiguration, this.TopicConfiguration, (w, t) => t.Serialize(w)); - writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - - writer.WriteEndObject(); - } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs deleted file mode 100644 index 85ec18d6..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings.Kafka -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// Binding class for Kafka operations. - /// - public class KafkaOperationBinding : IOperationBinding - { - /// - /// Id of the consumer group. - /// - public AsyncApiSchema GroupId { get; set; } - - /// - /// Id of the consumer inside a consumer group. - /// - public AsyncApiSchema ClientId { get; set; } - - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public BindingType Type => BindingType.Kafka; - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - writer.WriteOptionalObject(AsyncApiConstants.GroupId, this.GroupId, (w, h) => h.SerializeV2(w)); - writer.WriteOptionalObject(AsyncApiConstants.ClientId, this.ClientId, (w, h) => h.SerializeV2(w)); - writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - - writer.WriteEndObject(); - } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs deleted file mode 100644 index 2fc0e15c..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings.Pulsar -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// Binding class for Pulsar server settings. - /// - public class PulsarServerBinding : IServerBinding - { - /// - /// The pulsar tenant. If omitted, "public" must be assumed. - /// - public string Tenant { get; set; } - - /// - /// The version of this binding. - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Pulsar; - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - - writer.WriteOptionalProperty(AsyncApiConstants.Tenant, this.Tenant); - writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - - writer.WriteEndObject(); - } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs b/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs index c427db45..44e4573d 100644 --- a/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs +++ b/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs @@ -1,15 +1,13 @@ // Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Models.Interfaces { - using LEGO.AsyncAPI.Models.Bindings; - /// /// Describes a protocol-specific binding. /// - public interface IBinding : IAsyncApiReferenceable, IAsyncApiExtensible + public interface IBinding : IAsyncApiSerializable, IAsyncApiExtensible { - public BindingType Type { get; } + public string BindingKey { get; } - public string BindingVersion { get; set; } + public string BindingVersion { get; set; } } } diff --git a/src/LEGO.AsyncAPI/Models/ReferenceType.cs b/src/LEGO.AsyncAPI/Models/ReferenceType.cs index 8195e197..8903dd73 100644 --- a/src/LEGO.AsyncAPI/Models/ReferenceType.cs +++ b/src/LEGO.AsyncAPI/Models/ReferenceType.cs @@ -56,22 +56,22 @@ public enum ReferenceType /// /// ServerBindings item. /// - [Display("serverBindings")] ServerBinding, + [Display("serverBindings")] ServerBindings, /// /// ChannelBindings item. /// - [Display("channelBindings")] ChannelBinding, + [Display("channelBindings")] ChannelBindings, /// /// OperationBindings item. /// - [Display("operationBindings")] OperationBinding, + [Display("operationBindings")] OperationBindings, /// /// MessageBindings item. /// - [Display("messageBindings")] MessageBinding, + [Display("messageBindings")] MessageBindings, /// /// Examples item. @@ -82,6 +82,10 @@ public enum ReferenceType /// Headers item. /// [Display("headers")] Header, - ServerVariable, + + /// + /// The server variable + /// + [Display("serverVariable")] ServerVariable, } -} \ No newline at end of file +} diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs index 85f2d4da..c1e45d6a 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs @@ -5,9 +5,9 @@ namespace LEGO.AsyncAPI.Services using System; using System.Collections.Generic; using System.Linq; - using Exceptions; - using Models; - using Models.Interfaces; + using LEGO.AsyncAPI.Exceptions; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; /// /// This class is used to walk an AsyncApiDocument and convert unresolved references to references to populated objects @@ -64,8 +64,7 @@ public override void Visit(AsyncApiDocument doc) public override void Visit(AsyncApiChannel channel) { this.ResolveMap(channel.Parameters); - var bindingDictionary = channel.Bindings.Select(binding => binding.Value).ToDictionary(x => x.Type.GetDisplayName()); - this.ResolveMap(bindingDictionary); + this.ResolveObject(channel.Bindings, r => channel.Bindings = r); } public override void Visit(AsyncApiMessageTrait trait) @@ -81,8 +80,7 @@ public override void Visit(AsyncApiOperation operation) { this.ResolveList(operation.Message); this.ResolveList(operation.Traits); - var bindingDictionary = operation.Bindings.Select(binding => binding.Value).ToDictionary(x => x.Type.GetDisplayName()); - this.ResolveMap(bindingDictionary); + this.ResolveObject(operation.Bindings, r => operation.Bindings = r); } public override void Visit(AsyncApiMessage message) @@ -91,19 +89,12 @@ public override void Visit(AsyncApiMessage message) this.ResolveObject(message.Payload, r => message.Payload = r); this.ResolveList(message.Traits); this.ResolveObject(message.CorrelationId, r => message.CorrelationId = r); - var bindingDictionary = message.Bindings.Select(binding => binding.Value).ToDictionary(x => x.Type.GetDisplayName()); - this.ResolveMap(bindingDictionary); + this.ResolveObject(message.Bindings, r => message.Bindings = r); } - /// - /// Resolve all references to bindings. - /// - public override void Visit(AsyncApiBindings bindings) + public override void Visit(AsyncApiServer server) { - foreach (var binding in bindings.Values.ToList()) - { - this.ResolveObject(binding, resolvedBinding => bindings[binding.Type] = resolvedBinding); - } + this.ResolveObject(server.Bindings, r => server.Bindings = r); } /// diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs index 9519f3ba..bac5482f 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs @@ -15,7 +15,7 @@ public abstract class AsyncApiVisitorBase private readonly Stack path = new Stack(); /// - /// Properties available to identify context of where an object is within AsyncApi Document + /// Properties available to identify context of where an object is within AsyncApi Document. /// public CurrentKeys CurrentKeys { get; } = new CurrentKeys(); @@ -171,14 +171,6 @@ public virtual void Visit(AsyncApiOAuthFlow asyncApiOAuthFlow) { } - /// - /// Visits - /// - public virtual void Visit(AsyncApiBindings bindings) - where TBinding : class, IBinding - { - } - /// /// Visits /// diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs index c3ea6af8..1e0f7330 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs @@ -73,6 +73,17 @@ internal void Walk(AsyncApiComponents components) } }); + this.Walk(AsyncApiConstants.ServerBindings, () => + { + if (components.ServerBindings != null) + { + foreach (var item in components.ServerBindings) + { + this.Walk(item.Key, () => this.Walk(item.Value, isComponent: true)); + } + } + }); + this.Walk(AsyncApiConstants.Parameters, () => { if (components.Parameters != null) @@ -532,48 +543,44 @@ internal void Walk(AsyncApiMessageTrait trait, bool isComponent = false) this.Walk(trait as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings serverBindings) + internal void Walk(AsyncApiBindings serverBindings, bool isComponent = false) { - if (serverBindings is null) + if (serverBindings == null || this.ProcessAsReference(serverBindings, isComponent)) { return; } this.visitor.Visit(serverBindings); - this.Walk(serverBindings as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings channelBindings) + internal void Walk(AsyncApiBindings channelBindings, bool isComponent = false) { - if (channelBindings is null) + if (channelBindings == null || this.ProcessAsReference(channelBindings, isComponent)) { return; } this.visitor.Visit(channelBindings); - this.Walk(channelBindings as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings operationBindings) + internal void Walk(AsyncApiBindings operationBindings, bool isComponent = false) { - if (operationBindings is null) + if (operationBindings == null || this.ProcessAsReference(operationBindings, isComponent)) { return; } this.visitor.Visit(operationBindings); - this.Walk(operationBindings as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings messageBindings) + internal void Walk(AsyncApiBindings messageBindings, bool isComponent = false) { - if (messageBindings is null) + if (messageBindings == null || this.ProcessAsReference(messageBindings, isComponent)) { return; } this.visitor.Visit(messageBindings); - this.Walk(messageBindings as IAsyncApiExtensible); } internal void Walk(IList examples) @@ -703,7 +710,6 @@ internal void Walk(AsyncApiServer server, bool isComponent = false) this.visitor.Visit(server); this.Walk(AsyncApiConstants.Variables, () => this.Walk(server.Variables)); this.Walk(AsyncApiConstants.Security, () => this.Walk(server.Security)); - this.Walk(AsyncApiConstants.Bindings, () => this.Walk(server.Bindings)); this.visitor.Visit(server as IAsyncApiExtensible); } diff --git a/src/LEGO.AsyncAPI/Services/CurrentKeys.cs b/src/LEGO.AsyncAPI/Services/CurrentKeys.cs index f070a15c..3544eb21 100644 --- a/src/LEGO.AsyncAPI/Services/CurrentKeys.cs +++ b/src/LEGO.AsyncAPI/Services/CurrentKeys.cs @@ -4,6 +4,8 @@ namespace LEGO.AsyncAPI.Services { public class CurrentKeys { + public string ServerBindings { get; internal set; } + public string Channel { get; internal set; } public string Extension { get; internal set; } diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs index 019c2ef4..79576005 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs @@ -3,7 +3,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Validations; [AsyncApiRule] diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs index 5422eea8..3e6a21c8 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Validations; - using System.Linq; [AsyncApiRule] public static class AsyncApiCorrelationIdRules diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs index 3cba8059..27076369 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { - using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using LEGO.AsyncAPI.Models; diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs index 8ce2a099..710b30e5 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { - using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Validations; diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs index ce735c93..69b8c5ee 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs @@ -3,7 +3,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Validations; [AsyncApiRule] diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs index bb421471..244686c5 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Validations; - using System.Linq; [AsyncApiRule] public static class AsyncApiTagRules diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs index 5953e9d1..ec9c96b6 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs @@ -138,6 +138,12 @@ public static void WriteOptionalObject( { if (value != null) { + if (value is IAsyncApiReferenceable refer && refer.Reference != null) + { + writer.WriteRequiredObject(name, value, action); + return; + } + var values = value as IEnumerable; if (values != null && !values.GetEnumerator().MoveNext()) { diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs index b6edd619..b3f061e3 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs @@ -40,7 +40,7 @@ public ReferenceInlineSetting ReferenceInline /// public bool InlineReferences { get; set; } = false; - internal bool ShouldInlineReference(AsyncApiReference reference) + public bool ShouldInlineReference(AsyncApiReference reference) { return this.InlineReferences; } diff --git a/src/LEGO.AsyncAPI/Writers/StringExtensions.cs b/src/LEGO.AsyncAPI/Writers/StringExtensions.cs index 15697bd3..e4efe5f7 100644 --- a/src/LEGO.AsyncAPI/Writers/StringExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/StringExtensions.cs @@ -4,7 +4,7 @@ namespace LEGO.AsyncAPI.Writers { using System; using System.Reflection; - using Attributes; + using LEGO.AsyncAPI.Attributes; public static class StringExtensions { diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs index 49b32d80..3f2ee429 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs @@ -1,4 +1,6 @@ -namespace LEGO.AsyncAPI.Tests +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests { using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; @@ -154,47 +156,47 @@ public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiMessageTrait me return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IServerBinding serverBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings serverBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.ServerBindings.Add(key, serverBinding); + this.document.Components.ServerBindings.Add(key, serverBindings); return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IChannelBinding channelBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings channelBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.ChannelBindings.Add(key, channelBinding); + this.document.Components.ChannelBindings.Add(key, channelBindings); return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IOperationBinding operationBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings operationBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.OperationBindings.Add(key, operationBinding); + this.document.Components.OperationBindings.Add(key, operationBindings); return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IMessageBinding messageBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings messageBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.MessageBindings.Add(key, messageBinding); + this.document.Components.MessageBindings.Add(key, messageBindings); return this; } diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 75db39a9..fea62870 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -1,15 +1,18 @@ -namespace LEGO.AsyncAPI.Tests +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests { using System; using System.Collections.Generic; 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.Models; using LEGO.AsyncAPI.Models.Any; - using LEGO.AsyncAPI.Models.Bindings; - using LEGO.AsyncAPI.Models.Bindings.Http; - using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Writers; @@ -748,7 +751,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Bindings = new AsyncApiBindings() { { - BindingType.Kafka, new KafkaOperationBinding() + "kafka", new KafkaOperationBinding() { ClientId = new AsyncApiSchema() { @@ -770,7 +773,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -1201,7 +1204,7 @@ public void SerializeV2_WithFullSpec_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -2132,6 +2135,91 @@ This includes both parental consents and cookie consents. var deserialized = reader.Read(spec, out var diagnostic); } + [Test] + public void Serialize_WithBindingReferences_SerializesDeserializes() + { + var doc = new AsyncApiDocument(); + doc.Info = new AsyncApiInfo() + { + Description = "test description" + }; + doc.Servers.Add("production", new AsyncApiServer + { + Description = "test description", + Protocol = "pulsar+ssl", + Url = "example.com", + Bindings = new AsyncApiBindings() + { + Reference = new AsyncApiReference() + { + Type = ReferenceType.ServerBindings, + Id = "bindings", + }, + }, + }); + doc.Components = new AsyncApiComponents() + { + Channels = new Dictionary() + { + { "otherchannel", new AsyncApiChannel() + { + Publish = new AsyncApiOperation() + { + Description = "test", + }, + Bindings = new AsyncApiBindings() + { + Reference = new AsyncApiReference() + { + Type = ReferenceType.ChannelBindings, + Id = "bindings", + }, + }, + } + } + }, + ServerBindings = new Dictionary>() + { + { + "bindings", new AsyncApiBindings() + { + new PulsarServerBinding() + { + Tenant = "staging" + }, + } + } + }, + ChannelBindings = new Dictionary>() + { + { + "bindings", new AsyncApiBindings() + { + new PulsarChannelBinding() + { + Namespace = "users", + Persistence = AsyncAPI.Models.Bindings.Pulsar.Persistence.Persistent, + } + } + } + }, + }; + doc.Channels.Add("testChannel", + new AsyncApiChannel + { + Reference = new AsyncApiReference() + { + Type = ReferenceType.Channel, + Id = "otherchannel" + } + }); + var actual = doc.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); + + var settings = new AsyncApiReaderSettings(); + settings.Bindings.AddRange(BindingsCollection.Pulsar); + var reader = new AsyncApiStringReader(settings); + var deserialized = reader.Read(actual, out var diagnostic); + } [Test] public void Serializev2_WithBindings_Serializes() { @@ -2219,18 +2307,20 @@ public void Serializev2_WithBindings_Serializes() }); var actual = doc.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); - var reader = new AsyncApiStringReader(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.AddRange(BindingsCollection.All); + var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); Assert.AreEqual(2, deserialized.Channels.First().Value.Publish.Message.First().Bindings.Count); var binding = deserialized.Channels.First().Value.Publish.Message.First().Bindings.First(); - Assert.AreEqual(BindingType.Http, binding.Key); + Assert.AreEqual("http", binding.Key); var httpBinding = binding.Value as HttpMessageBinding; Assert.AreEqual("this mah binding", httpBinding.Headers.Description); diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs index 7059b6fe..8012aa99 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs @@ -1,4 +1,6 @@ -using FluentAssertions; +// Copyright (c) The LEGO Group. All rights reserved. + +using FluentAssertions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index e2aeac00..a5d6a7a6 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Tests { using System; diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs new file mode 100644 index 00000000..94081564 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs @@ -0,0 +1,78 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Any; + using LEGO.AsyncAPI.Models.Interfaces; + using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + using NUnit.Framework; + + public class MyBinding : ChannelBinding + { + public string Custom { get; set; } + + public override string BindingKey => "my"; + + protected override FixedFieldMap FixedFieldMap => new FixedFieldMap() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "custom", (a, n) => { a.Custom = n.GetScalarValue(); } }, + }; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + writer.WriteStartObject(); + writer.WriteRequiredProperty("custom", this.Custom); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } + + public class CustomBinding_Should + { + [Test] + public void CustomBinding_SerializesDeserializes() + { + // Arrange + var expected = +@"bindings: + my: + custom: someValue + bindingVersion: 0.1.0 + x-myextension: someValue"; + + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new MyBinding + { + Custom = "someValue", + BindingVersion = "0.1.0", + Extensions = new Dictionary() + { + { "x-myextension", new AsyncApiString("someValue") }, + }, + }); + + // Act + var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(new MyBinding()); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(channel); + } + } +} diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs index 01ce4885..0b00bbf7 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs @@ -1,8 +1,11 @@ -namespace LEGO.AsyncAPI.Tests.Bindings.Http +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.Http { using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.Http; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -32,11 +35,12 @@ public void HttpMessageBinding_FilledObject_SerializesAndDeserializes() var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Http); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(message); } @@ -56,7 +60,7 @@ public void HttpOperationBinding_FilledObject_SerializesAndDeserializes() operation.Bindings.Add(new HttpOperationBinding { - Type = "request", + Type = HttpOperationBinding.HttpOperationType.Request, Method = "POST", Query = new AsyncApiSchema { @@ -68,11 +72,12 @@ public void HttpOperationBinding_FilledObject_SerializesAndDeserializes() var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Http); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(operation); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index 49e1e515..537e904c 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -1,6 +1,10 @@ -namespace LEGO.AsyncAPI.Tests.Bindings.Kafka +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.Kafka { using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Readers; @@ -50,11 +54,12 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() // Assert actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(channel); } @@ -86,11 +91,12 @@ public void KafkaServerBinding_WithFilledObject_SerializesAndDeserializes() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(server); } @@ -124,11 +130,12 @@ public void KafkaMessageBinding_WithFilledObject_SerializesAndDeserializes() var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(message); } @@ -163,10 +170,12 @@ public void KafkaOperationBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(operation); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs index 05fd4f98..15577763 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs @@ -1,13 +1,14 @@ -using LEGO.AsyncAPI.Models.Bindings; - +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Bindings.Pulsar { + using System.Collections.Generic; using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Pulsar; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Bindings.Pulsar; using LEGO.AsyncAPI.Readers; using NUnit.Framework; - using System.Collections.Generic; internal class PulsarBindings_Should { @@ -59,10 +60,12 @@ public void PulsarChannelBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(channel); } @@ -76,10 +79,12 @@ public void PulsarChannelBindingNamespaceDefaultToNull() persistence: persistent"; // Act - // Assert - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - Assert.AreEqual(null, ((PulsarChannelBinding)binding.Bindings[BindingType.Pulsar]).Namespace); + // Assert + Assert.AreEqual(null, ((PulsarChannelBinding)binding.Bindings["pulsar"]).Namespace); } [Test] @@ -93,9 +98,12 @@ public void PulsarChannelBindingPropertiesExceptNamespaceDefaultToNull() // Act // Assert - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - var pulsarBinding = ((PulsarChannelBinding) binding.Bindings[BindingType.Pulsar]); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var pulsarBinding = ((PulsarChannelBinding)binding.Bindings["pulsar"]); + Assert.AreEqual("staging", pulsarBinding.Namespace); Assert.AreEqual(null, pulsarBinding.Persistence); Assert.AreEqual(null, pulsarBinding.Compaction); Assert.AreEqual(null, pulsarBinding.GeoReplication); @@ -130,11 +138,12 @@ public void PulsarServerBinding_WithFilledObject_SerializesAndDeserializes() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(server); } @@ -165,12 +174,13 @@ public void ServerBindingVersionDefaultsToNull() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); - Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings[BindingType.Pulsar]).BindingVersion); + Assert.AreEqual(expected, actual); + Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings["pulsar"]).BindingVersion); binding.Should().BeEquivalentTo(server); } @@ -201,12 +211,13 @@ public void ServerTenantDefaultsToNull() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); - Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings[BindingType.Pulsar]).Tenant); + Assert.AreEqual(expected, actual); + Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings["pulsar"]).Tenant); binding.Should().BeEquivalentTo(server); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs index 0f934b91..2a57b997 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs @@ -1,8 +1,11 @@ -namespace LEGO.AsyncAPI.Tests.Bindings.WebSockets +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.WebSockets { using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.WebSockets; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.WebSockets; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -40,10 +43,12 @@ public void WebSocketChannelBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Websockets); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(channel); } } diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index c55609e5..692c8e06 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -28,6 +28,7 @@ + @@ -41,6 +42,10 @@ + + + + diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs index dfaa6764..05f91943 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs @@ -1,9 +1,11 @@ -namespace LEGO.AsyncAPI.Tests.Models +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Models { using System.Collections.Generic; + using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Bindings.WebSockets; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.Kafka; - using LEGO.AsyncAPI.Models.Bindings.WebSockets; using LEGO.AsyncAPI.Models.Interfaces; using NUnit.Framework; @@ -69,7 +71,7 @@ public void AsyncApiChannel_WithWebSocketsBinding_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -104,7 +106,7 @@ public void AsyncApiChannel_WithKafkaBinding_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs index 817e2bc3..efef6408 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + using System; using LEGO.AsyncAPI.Models; using NUnit.Framework; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs index 0721426c..7d91e1db 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + using System; using LEGO.AsyncAPI.Models; using NUnit.Framework; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs index 31fdb8d0..2529ab80 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + using System; using LEGO.AsyncAPI.Models; using NUnit.Framework; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs index 5cd8f59a..562e527a 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + using System; using LEGO.AsyncAPI.Models; using NUnit.Framework; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs index 3df3266e..34c46687 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + using System; using LEGO.AsyncAPI.Models; using NUnit.Framework; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index d2dabcd3..f86a9ad4 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -1,13 +1,15 @@ -namespace LEGO.AsyncAPI.Tests.Models +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Models { using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Any; - using LEGO.AsyncAPI.Models.Bindings; - using LEGO.AsyncAPI.Models.Bindings.Http; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -90,7 +92,7 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); message.Should().BeEquivalentTo(deserializedMessage); } @@ -131,7 +133,7 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); message.Should().BeEquivalentTo(deserializedMessage); } @@ -275,7 +277,7 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Bindings = new AsyncApiBindings() { { - BindingType.Http, new HttpMessageBinding + "http", new HttpMessageBinding { Headers = new AsyncApiSchema { @@ -370,10 +372,12 @@ public void AsyncApiMessage_WithFilledObject_Serializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.All); + var deserializedMessage = new AsyncApiStringReader(settings).ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); message.Should().BeEquivalentTo(deserializedMessage); } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs index 207fe140..9a7217f2 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + using System; using LEGO.AsyncAPI.Models; using NUnit.Framework; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs index f0778cec..12c97eab 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs @@ -1,9 +1,11 @@ +// Copyright (c) The LEGO Group. All rights reserved. + using System; using System.Globalization; using System.IO; +using LEGO.AsyncAPI.Bindings.Http; +using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Bindings.Http; -using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; using NUnit.Framework; @@ -46,7 +48,7 @@ public void SerializeV2_WithMultipleMessages_SerializesWithOneOf() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -69,7 +71,7 @@ public void SerializeV2_WithSingleMessage_Serializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -78,7 +80,7 @@ public void AsyncApiOperation_WithBindings_Serializes() var expected = @"bindings: http: - type: type + type: request method: PUT query: description: some query @@ -95,7 +97,7 @@ public void AsyncApiOperation_WithBindings_Serializes() { new HttpOperationBinding { - Type = "type", + Type = HttpOperationBinding.HttpOperationType.Request, Method = "PUT", Query = new AsyncApiSchema { @@ -125,7 +127,7 @@ public void AsyncApiOperation_WithBindings_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 57101ed9..3f319982 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -1,4 +1,6 @@ -using LEGO.AsyncAPI.Models; +// Copyright (c) The LEGO Group. All rights reserved. + +using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Writers; using NUnit.Framework; using System; @@ -150,7 +152,7 @@ public void Serialize_WithInliningOptions_ShouldInlineAccordingly(bool shouldInl expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs index f8872233..73498417 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs @@ -1,4 +1,7 @@ +// Copyright (c) The LEGO Group. All rights reserved. + using System; +using System.Collections.Generic; using LEGO.AsyncAPI.Models; using NUnit.Framework; @@ -16,5 +19,14 @@ public void SerializeV2_WithNullWriter_Throws() // Assert Assert.Throws(() => { asyncApiSecurityRequirement.SerializeV2(null); }); } + + [Test] + public void SerializeV2_Serializes() + { + var asyncApiSecurityRequirement = new AsyncApiSecurityRequirement(); + asyncApiSecurityRequirement.Add(new AsyncApiSecurityScheme { Type = SecuritySchemeType.ApiKey }, new List { "string" }); + + var output = asyncApiSecurityRequirement.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs index 52010434..17e43a0d 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs @@ -1,5 +1,7 @@ -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Bindings.Kafka; +// Copyright (c) The LEGO Group. All rights reserved. + +using LEGO.AsyncAPI.Bindings.Kafka; +using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using NUnit.Framework; using System.Collections.Generic; @@ -71,7 +73,7 @@ public void AsyncApiServer_Serializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -104,7 +106,7 @@ public void AsyncApiServer_WithKafkaBinding_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } } } diff --git a/test/LEGO.AsyncAPI.Tests/StringExtensions.cs b/test/LEGO.AsyncAPI.Tests/StringExtensions.cs index 6a3c89fd..2f0fddb8 100644 --- a/test/LEGO.AsyncAPI.Tests/StringExtensions.cs +++ b/test/LEGO.AsyncAPI.Tests/StringExtensions.cs @@ -1,4 +1,6 @@ -namespace LEGO.AsyncAPI.Tests +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests { using System; diff --git a/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs b/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs index 70929de1..1c8ca9d2 100644 --- a/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs +++ b/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs @@ -1,4 +1,6 @@ -using LEGO.AsyncAPI.Validations; +// Copyright (c) The LEGO Group. All rights reserved. + +using LEGO.AsyncAPI.Validations; using NUnit.Framework; namespace LEGO.AsyncAPI.Tests.Validation diff --git a/test/LEGO.AsyncAPI.Tests/stylecop.json b/test/LEGO.AsyncAPI.Tests/stylecop.json new file mode 100644 index 00000000..0a8f4661 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/stylecop.json @@ -0,0 +1,15 @@ +{ + // ACTION REQUIRED: This file was automatically added to your project, but it + // will not take effect until additional steps are taken to enable it. See the + // following page for additional information: + // + // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md + + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "The LEGO Group", + "xmlHeader": false + } + } +} From 222ad97656f1df06468109cf76ce6e5c7dc7b8ac Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Wed, 3 May 2023 11:13:09 +0200 Subject: [PATCH 03/39] chore: add readme.md to bindings pack --- src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj index c7f7b9a0..e05aacab 100644 --- a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj +++ b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj @@ -29,7 +29,12 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + + + True + \ + + From d856ccc1c8863008fcbfa63bef046e7157404c8b Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Thu, 4 May 2023 11:30:40 +0100 Subject: [PATCH 04/39] feat: WIP fix file overwrite --- .../Sns/SnsChannelBinding.cs | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index b7a5dc25..c690a045 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using LEGO.AsyncAPI.Bindings; +using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; namespace LEGO.AsyncAPI.Models.Bindings.Sns; @@ -9,7 +11,7 @@ namespace LEGO.AsyncAPI.Models.Bindings.Sns; /// /// Binding class for SNS channel settings. /// -public class SnsChannelBinding : IChannelBinding +public class SnsChannelBinding : ChannelBinding { /// /// The name of the topic. Can be different from the channel name to allow flexibility around AWS resource naming limitations. @@ -31,47 +33,54 @@ public class SnsChannelBinding : IChannelBinding /// public Dictionary Tags { get; set; } + public override string BindingKey => "sns"; + /// /// The version of this binding. /// public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Sns; - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); - - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + + protected override FixedFieldMap FixedFieldMap => new() { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - writer.WriteOptionalProperty(AsyncApiConstants.Name, this.Name); - writer.WriteOptionalObject(AsyncApiConstants.Policy, this.Policy, (w, t) => t.Serialize(w)); - - writer.WriteEndObject(); + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + { "policy", (a, n) => { a.Policy = LoadPolicy(n); } }, + }; + + private static FixedFieldMap policyFixedFields = new() + { + { "statements", (a, n) => { a.Statements = n.CreateSimpleList(s => LoadStatement(s)); } }, + }; + + private static FixedFieldMap statementFixedFields = new() + { + { "principal", (a, n) => { a.Principal = n.GetScalarValue(); } }, + }; + + private static Policy LoadPolicy(ParseNode node) + { + var mapNode = node.CheckMapNode("policy"); + var policy = new Policy(); + ParseMap(mapNode, policy, policyFixedFields); + return policy; } - - public void SerializeV2(IAsyncApiWriter writer) + + private static Statement LoadStatement(ParseNode node) + { + var mapNode = node.CheckMapNode("statement"); + var statement = new Statement(); + ParseMap(mapNode, statement, statementFixedFields); + return statement; + } + + /// + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { throw new ArgumentNullException(nameof(writer)); } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - this.SerializeV2WithoutReference(writer); - + writer.WriteStartObject(); + writer.WriteEndObject(); } } \ No newline at end of file From 1fac6698de0bef93f846f68312fb99adf05736f6 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Thu, 4 May 2023 13:02:22 +0100 Subject: [PATCH 05/39] feat: WIP further attempts at serde --- .../Sns/SnsChannelBinding.cs | 46 ++++++++++++++++--- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 35 ++++++++++++-- src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs | 2 + .../Bindings/Sns/SnsBindings_Should.cs | 22 +++++++-- 4 files changed, 91 insertions(+), 14 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index c690a045..63f691e8 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -39,23 +39,25 @@ public class SnsChannelBinding : ChannelBinding /// The version of this binding. /// public string BindingVersion { get; set; } - + protected override FixedFieldMap FixedFieldMap => new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, { "policy", (a, n) => { a.Policy = LoadPolicy(n); } }, }; - + private static FixedFieldMap policyFixedFields = new() { { "statements", (a, n) => { a.Statements = n.CreateSimpleList(s => LoadStatement(s)); } }, }; - + private static FixedFieldMap statementFixedFields = new() { - { "principal", (a, n) => { a.Principal = n.GetScalarValue(); } }, + { "effect", (a, n) => { a.Effect = LoadEffectEnum(n); } }, + { "principal", (a, n) => { a.Principal = LoadStringOrStringList(n, "principal"); } }, + { "action", (a, n) => { a.Action = LoadStringOrStringList(n, "action"); } }, }; - + private static Policy LoadPolicy(ParseNode node) { var mapNode = node.CheckMapNode("policy"); @@ -71,7 +73,37 @@ private static Statement LoadStatement(ParseNode node) ParseMap(mapNode, statement, statementFixedFields); return statement; } - + + public static Effect LoadEffectEnum(ParseNode node) + { + Enum.TryParse(node.GetScalarValue(), out Effect effect); + return effect; + } + + public static StringOrStringList LoadStringOrStringList(ParseNode node, string nodeName) + { + var mapNode = node.CheckMapNode(nodeName); + var stringValue = mapNode.GetScalarValue(); + var stringOrStringList = new StringOrStringList(); + if (stringValue != null) + { + stringOrStringList.StringValue = stringValue; + } + else + { + var stringListValue = mapNode.GetEnumerator(); + while (stringListValue.Current != null) + { + stringOrStringList.StringList.Add(stringListValue.Current.GetScalarValue()); + stringListValue.MoveNext(); + } + + stringListValue.Dispose(); + } + + return stringOrStringList; + } + /// public override void SerializeProperties(IAsyncApiWriter writer) { @@ -81,6 +113,8 @@ public override void SerializeProperties(IAsyncApiWriter writer) } writer.WriteStartObject(); + writer.WriteOptionalProperty(AsyncApiConstants.Name, this.Name); + writer.WriteOptionalObject(AsyncApiConstants.Policy, this.Policy, (w, t) => t.Serialize(w)); writer.WriteEndObject(); } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 08d573d6..2d356fa4 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -13,12 +13,12 @@ public class Statement : IAsyncApiElement /// The AWS account or resource ARN that this statement applies to. /// // public StringOrStringList Principal { get; set; } - public string Principal { get; set; } + public StringOrStringList Principal { get; set; } /// /// The SNS permission being allowed or denied e.g. sns:Publish /// - // public StringOrStringList Action { get; set; } + public StringOrStringList Action { get; set; } public void Serialize(IAsyncApiWriter writer) { @@ -28,8 +28,9 @@ public void Serialize(IAsyncApiWriter writer) } writer.WriteStartObject(); - // writer.WriteOptionalObject(AsyncApiConstants.Principal, this.Principal, (w, t) => t.Serialize(w)); - writer.WriteOptionalProperty(AsyncApiConstants.Principal, this.Principal); + writer.WriteOptionalProperty(AsyncApiConstants.Effect, this.Effect.ToString()); + writer.WriteOptionalObject(AsyncApiConstants.Principal, this.Principal, (w, t) => t.Serialize(w)); + writer.WriteOptionalObject(AsyncApiConstants.Action, this.Action, (w, t) => t.Serialize(w)); writer.WriteEndObject(); } } @@ -45,4 +46,30 @@ public class StringOrStringList : IAsyncApiElement public string StringValue { get; set; } public List StringList { get; set; } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + if (this.StringValue != null) + { + writer.WriteValue(this.StringValue); + } + else + { + writer.WriteStartArray(); + foreach (var v in this.StringList) + { + writer.WriteValue(v); + } + + writer.WriteEndObject(); + } + + writer.WriteEndObject(); + } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs index 3ed31c5d..559b6f93 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs @@ -140,6 +140,8 @@ public static class AsyncApiConstants public const string GeoReplication = "geo-replication"; public const string Policy = "policy"; public const string Statements = "statements"; + public const string Effect = "effect"; public const string Principal = "principal"; + public const string Action = "action"; } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index a822cc5e..18fe0938 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -33,21 +33,35 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { new Statement() { - Principal = "hello", + Effect = Effect.Allow, + Principal = new StringOrStringList() + { + StringValue = "someARN", + }, + Action = new StringOrStringList() + { + StringList = new List() + { + "sns:Publish", + "sns:Delete", + }, + }, }, }, }, }); - + // Act var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + Console.WriteLine(actual); + // Assert actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - + var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - + // Assert Assert.AreEqual(actual, expected); binding.Should().BeEquivalentTo(channel); From 0edd5c94ca3d544979ccec266ed707efe1d1e83f Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Thu, 4 May 2023 14:20:42 +0100 Subject: [PATCH 06/39] feat: WIP fix typo --- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 2d356fa4..3c5ef3f5 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -67,7 +67,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteValue(v); } - writer.WriteEndObject(); + writer.WriteEndArray(); } writer.WriteEndObject(); From e7bc0df635ddd597339e409d3d8416bb1ef007d0 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Thu, 4 May 2023 16:20:04 +0100 Subject: [PATCH 07/39] feat: WIP more node work --- .../BindingsCollection.cs | 9 ++++++- .../Sns/OrderingConfiguration.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs | 3 ++- .../Sns/SnsChannelBinding.cs | 25 ++++++++----------- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 3 ++- .../ParseNodes/ListNode.cs | 2 +- .../Bindings/Sns/SnsBindings_Should.cs | 12 +++++---- 7 files changed, 31 insertions(+), 25 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs index 0a34ec39..112da0c8 100644 --- a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -7,6 +7,7 @@ namespace LEGO.AsyncAPI.Bindings using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Bindings.Pulsar; + using LEGO.AsyncAPI.Bindings.Sns; using LEGO.AsyncAPI.Bindings.WebSockets; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Interface; @@ -40,12 +41,13 @@ public static TCollection Add( Pulsar, Kafka, Http, + Sns, }; public static IEnumerable> Http => new List> { new HttpOperationBinding(), - new HttpMessageBinding() + new HttpMessageBinding(), }; public static IEnumerable> Websockets => new List> @@ -67,5 +69,10 @@ public static TCollection Add( new PulsarServerBinding(), new PulsarChannelBinding(), }; + + public static IEnumerable> Sns => new List> + { + new SnsChannelBinding(), + }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs b/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs index fb8ddc07..36fb49a1 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs @@ -1,4 +1,4 @@ -namespace LEGO.AsyncAPI.Models.Bindings.Sns; +namespace LEGO.AsyncAPI.Bindings.Sns; public class OrderingConfiguration { diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs index 81283fa5..ec68f849 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs @@ -1,8 +1,9 @@ using System; +using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; -namespace LEGO.AsyncAPI.Models.Bindings.Sns; +namespace LEGO.AsyncAPI.Bindings.Sns; using System.Collections.Generic; diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 63f691e8..8acf330f 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; +using System.Linq; using LEGO.AsyncAPI.Bindings; +using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; +using YamlDotNet.Core.Tokens; -namespace LEGO.AsyncAPI.Models.Bindings.Sns; +namespace LEGO.AsyncAPI.Bindings.Sns; using LEGO.AsyncAPI.Models.Interfaces; @@ -48,7 +51,7 @@ public class SnsChannelBinding : ChannelBinding private static FixedFieldMap policyFixedFields = new() { - { "statements", (a, n) => { a.Statements = n.CreateSimpleList(s => LoadStatement(s)); } }, + { "statements", (a, n) => { a.Statements = n.CreateList(s => LoadStatement(s)); } }, }; private static FixedFieldMap statementFixedFields = new() @@ -82,23 +85,15 @@ public static Effect LoadEffectEnum(ParseNode node) public static StringOrStringList LoadStringOrStringList(ParseNode node, string nodeName) { - var mapNode = node.CheckMapNode(nodeName); - var stringValue = mapNode.GetScalarValue(); var stringOrStringList = new StringOrStringList(); - if (stringValue != null) + if (node is ValueNode v) { - stringOrStringList.StringValue = stringValue; + stringOrStringList.StringValue = v.GetScalarValue(); } - else + + if (node is ListNode m) { - var stringListValue = mapNode.GetEnumerator(); - while (stringListValue.Current != null) - { - stringOrStringList.StringList.Add(stringListValue.Current.GetScalarValue()); - stringListValue.MoveNext(); - } - - stringListValue.Dispose(); + stringOrStringList.StringList = m.CreateList(s => s.GetScalarValue()); } return stringOrStringList; diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 3c5ef3f5..25b54607 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -1,9 +1,10 @@ using System; using System.Collections.Generic; +using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; -namespace LEGO.AsyncAPI.Models.Bindings.Sns; +namespace LEGO.AsyncAPI.Bindings.Sns; public class Statement : IAsyncApiElement { diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs index c402936e..17c7a592 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs @@ -11,7 +11,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using LEGO.AsyncAPI.Readers.Exceptions; using YamlDotNet.RepresentationModel; - internal class ListNode : ParseNode, IEnumerable + public class ListNode : ParseNode, IEnumerable { private readonly YamlSequenceNode nodeList; diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 18fe0938..977889f3 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -1,12 +1,14 @@ using System; +using BindingsCollection = LEGO.AsyncAPI.Bindings.BindingsCollection; namespace LEGO.AsyncAPI.Tests.Bindings.Sns { using NUnit.Framework; using System.Collections.Generic; using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Sns; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.Sns; using LEGO.AsyncAPI.Readers; internal class SnsBindings_Should @@ -33,7 +35,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { new Statement() { - Effect = Effect.Allow, + Effect = Effect.Deny, Principal = new StringOrStringList() { StringValue = "someARN", @@ -53,14 +55,14 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - - Console.WriteLine(actual); // Assert actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Sns); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert Assert.AreEqual(actual, expected); From 980bf81b983b22d4645069737f6eec35576a50ae Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 5 May 2023 09:52:33 +0100 Subject: [PATCH 08/39] feat: WIP switch to display enum --- src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 8acf330f..ecf43f65 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -56,7 +56,7 @@ public class SnsChannelBinding : ChannelBinding private static FixedFieldMap statementFixedFields = new() { - { "effect", (a, n) => { a.Effect = LoadEffectEnum(n); } }, + { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, { "principal", (a, n) => { a.Principal = LoadStringOrStringList(n, "principal"); } }, { "action", (a, n) => { a.Action = LoadStringOrStringList(n, "action"); } }, }; diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 25b54607..3c12c1ee 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using LEGO.AsyncAPI.Attributes; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; @@ -29,7 +30,7 @@ public void Serialize(IAsyncApiWriter writer) } writer.WriteStartObject(); - writer.WriteOptionalProperty(AsyncApiConstants.Effect, this.Effect.ToString()); + writer.WriteOptionalProperty(AsyncApiConstants.Effect, this.Effect.GetDisplayName()); writer.WriteOptionalObject(AsyncApiConstants.Principal, this.Principal, (w, t) => t.Serialize(w)); writer.WriteOptionalObject(AsyncApiConstants.Action, this.Action, (w, t) => t.Serialize(w)); writer.WriteEndObject(); @@ -38,8 +39,10 @@ public void Serialize(IAsyncApiWriter writer) public enum Effect { + [Display("allow")] Allow, - Deny + [Display("deny")] + Deny, } public class StringOrStringList : IAsyncApiElement From 822147250ed63de6045206ad45f01bd82a69fd32 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 5 May 2023 10:14:13 +0100 Subject: [PATCH 09/39] feat: WIP fixed lists --- .../Sns/SnsChannelBinding.cs | 16 +++++++--------- src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index ecf43f65..1ebdba1b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -77,12 +77,6 @@ private static Statement LoadStatement(ParseNode node) return statement; } - public static Effect LoadEffectEnum(ParseNode node) - { - Enum.TryParse(node.GetScalarValue(), out Effect effect); - return effect; - } - public static StringOrStringList LoadStringOrStringList(ParseNode node, string nodeName) { var stringOrStringList = new StringOrStringList(); @@ -90,12 +84,16 @@ public static StringOrStringList LoadStringOrStringList(ParseNode node, string n { stringOrStringList.StringValue = v.GetScalarValue(); } - - if (node is ListNode m) + else { - stringOrStringList.StringList = m.CreateList(s => s.GetScalarValue()); + stringOrStringList.StringList = node.CreateSimpleList(s => s.GetScalarValue()); } + // if (node is ListNode m) + // { + // stringOrStringList.StringList = m.CreateList(s => s.GetScalarValue()); + // } + return stringOrStringList; } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs index 17c7a592..c402936e 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs @@ -11,7 +11,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using LEGO.AsyncAPI.Readers.Exceptions; using YamlDotNet.RepresentationModel; - public class ListNode : ParseNode, IEnumerable + internal class ListNode : ParseNode, IEnumerable { private readonly YamlSequenceNode nodeList; From ab5550f0943c6affbda89829c450a776064c71f7 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 5 May 2023 10:18:52 +0100 Subject: [PATCH 10/39] feat: WIP fix write issues --- src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs | 9 ++------- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 3 --- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 1ebdba1b..8bc1e59a 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -80,20 +80,15 @@ private static Statement LoadStatement(ParseNode node) public static StringOrStringList LoadStringOrStringList(ParseNode node, string nodeName) { var stringOrStringList = new StringOrStringList(); - if (node is ValueNode v) + if (node is ValueNode) { - stringOrStringList.StringValue = v.GetScalarValue(); + stringOrStringList.StringValue = node.GetScalarValue(); } else { stringOrStringList.StringList = node.CreateSimpleList(s => s.GetScalarValue()); } - // if (node is ListNode m) - // { - // stringOrStringList.StringList = m.CreateList(s => s.GetScalarValue()); - // } - return stringOrStringList; } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 3c12c1ee..4ff0d446 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -58,7 +58,6 @@ public void Serialize(IAsyncApiWriter writer) throw new ArgumentNullException(nameof(writer)); } - writer.WriteStartObject(); if (this.StringValue != null) { writer.WriteValue(this.StringValue); @@ -73,7 +72,5 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteEndArray(); } - - writer.WriteEndObject(); } } \ No newline at end of file From 286e64db5c6cda2a7bb7e61e9bf0cd776ffe9f05 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 5 May 2023 10:23:21 +0100 Subject: [PATCH 11/39] feat: WIP remove commented function --- src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs index 1d0b862a..ec9c96b6 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs @@ -240,8 +240,6 @@ public static void WriteRequiredCollection( { writer.WriteCollectionInternal(name, elements, action); } - - // public static void WriteOptionalObjectCollection(this ) /// /// Write the optional AsyncApi element map (string to string mapping). From b8aceead767648869095b26226100a7416736688 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 5 May 2023 12:18:03 +0100 Subject: [PATCH 12/39] feat: WIP Complete SNS body --- .../Sns/OrderingConfiguration.cs | 25 +++++++++++++++++-- .../Sns/SnsChannelBinding.cs | 18 ++++++++++++- .../Bindings/Sns/SnsBindings_Should.cs | 14 ++++++++++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs b/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs index 36fb49a1..58727e4a 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs @@ -1,6 +1,12 @@ + +using System; +using LEGO.AsyncAPI.Attributes; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Writers; + namespace LEGO.AsyncAPI.Bindings.Sns; -public class OrderingConfiguration +public class OrderingConfiguration : IAsyncApiElement { /// /// What type of SNS Topic is this? @@ -11,10 +17,25 @@ public class OrderingConfiguration /// True to turn on de-duplication of messages for a channel. /// public bool ContentBasedDeduplication { get; set; } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty("type", this.Type.GetDisplayName()); + writer.WriteOptionalProperty("contentBasedDeduplication", this.ContentBasedDeduplication); + writer.WriteEndObject(); + } } public enum Ordering { + [Display("standard")] Standard, - Fifo + [Display("FIFO")] + Fifo, } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 8bc1e59a..5854379b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -24,7 +24,7 @@ public class SnsChannelBinding : ChannelBinding /// /// By default, we assume an unordered SNS topic. This field allows configuration of a FIFO SNS Topic. /// - public OrderingConfiguration Ordering { get; set; } + public OrderingConfiguration Type { get; set; } /// /// The security policy for the SNS Topic. @@ -46,9 +46,16 @@ public class SnsChannelBinding : ChannelBinding protected override FixedFieldMap FixedFieldMap => new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + { "type", (a, n) => { a.Type = LoadType(n); } }, { "policy", (a, n) => { a.Policy = LoadPolicy(n); } }, }; + private static FixedFieldMap orderingFixedFields = new() + { + { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "contentBasedDeduplication", (a, n) => { a.ContentBasedDeduplication = n.GetBooleanValue(); } }, + }; + private static FixedFieldMap policyFixedFields = new() { { "statements", (a, n) => { a.Statements = n.CreateList(s => LoadStatement(s)); } }, @@ -61,6 +68,14 @@ public class SnsChannelBinding : ChannelBinding { "action", (a, n) => { a.Action = LoadStringOrStringList(n, "action"); } }, }; + private static OrderingConfiguration LoadType(ParseNode node) + { + var mapNode = node.CheckMapNode("type"); + var ordering = new OrderingConfiguration(); + ParseMap(mapNode, ordering, orderingFixedFields); + return ordering; + } + private static Policy LoadPolicy(ParseNode node) { var mapNode = node.CheckMapNode("policy"); @@ -102,6 +117,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteOptionalProperty(AsyncApiConstants.Name, this.Name); + writer.WriteOptionalObject("type", this.Type, (w, t) => t.Serialize(w)); writer.WriteOptionalObject(AsyncApiConstants.Policy, this.Policy, (w, t) => t.Serialize(w)); writer.WriteEndObject(); } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 977889f3..15f11b6a 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -21,14 +21,26 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() @"bindings: sns: name: myTopic + type: + type: FIFO + contentBasedDeduplication: true policy: statements: - - principal: hello"; + - effect: deny + principal: someARN + action: + - sns:Publish + - sns:Delete"; var channel = new AsyncApiChannel(); channel.Bindings.Add(new SnsChannelBinding() { Name = "myTopic", + Type = new OrderingConfiguration() + { + Type = Ordering.Fifo, + ContentBasedDeduplication = true, + }, Policy = new Policy() { Statements = new List() From d0f0cebf6ee86b0b9425fa3997d852c9bdac86ad Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 5 May 2023 14:09:27 +0100 Subject: [PATCH 13/39] feat: WIP finish SNS channel binding --- src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs | 7 ++----- .../Bindings/Sns/SnsBindings_Should.cs | 10 +++++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 5854379b..a36b543e 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -38,16 +38,12 @@ public class SnsChannelBinding : ChannelBinding public override string BindingKey => "sns"; - /// - /// The version of this binding. - /// - public string BindingVersion { get; set; } - protected override FixedFieldMap FixedFieldMap => new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, { "type", (a, n) => { a.Type = LoadType(n); } }, { "policy", (a, n) => { a.Policy = LoadPolicy(n); } }, + { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, }; private static FixedFieldMap orderingFixedFields = new() @@ -119,6 +115,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.Name, this.Name); writer.WriteOptionalObject("type", this.Type, (w, t) => t.Serialize(w)); writer.WriteOptionalObject(AsyncApiConstants.Policy, this.Policy, (w, t) => t.Serialize(w)); + writer.WriteOptionalMap("tags", this.Tags, (w, t) => w.WriteValue(t)); writer.WriteEndObject(); } } \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 15f11b6a..be1d33aa 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -30,7 +30,10 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() principal: someARN action: - sns:Publish - - sns:Delete"; + - sns:Delete + tags: + owner: AsyncAPI.NET + platform: AsyncAPIOrg"; var channel = new AsyncApiChannel(); channel.Bindings.Add(new SnsChannelBinding() @@ -63,6 +66,11 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() }, }, }, + Tags = new Dictionary() + { + { "owner", "AsyncAPI.NET" }, + { "platform", "AsyncAPIOrg" }, + }, }); // Act From 41ea55db54fa79024eef7735c845cc61196a4764 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Wed, 10 May 2023 14:46:25 +0100 Subject: [PATCH 14/39] feat: WIP Add basic SNS operation serialization --- src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs | 84 +++++++++++ .../Sns/DeliveryPolicy.cs | 80 ++++++++++ .../Sns/FilterPolicy.cs | 25 ++++ src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs | 34 +++++ .../Sns/RedrivePolicy.cs | 31 ++++ .../Sns/SnsOperationBinding.cs | 42 ++++++ src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 2 +- .../Bindings/Sns/SnsBindings_Should.cs | 141 ++++++++++++++++++ 8 files changed, 438 insertions(+), 1 deletion(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs new file mode 100644 index 00000000..d532b8b2 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs @@ -0,0 +1,84 @@ +using System; +using LEGO.AsyncAPI.Attributes; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Writers; + +namespace LEGO.AsyncAPI.Bindings.Sns; + +public class Consumer : IAsyncApiElement +{ + /// + /// What protocol will this endpoint receive messages by? + /// + public Protocol Protocol { get; set; } + + /// + /// Where are messages being delivered to? + /// + public Identifier Endpoint { get; set; } + + /// + /// Only receive a subset of messages from the channel, determined by this policy. + /// + public FilterPolicy FilterPolicy { get; set; } + + /// + /// If true AWS SNS attributes are removed from the body, and for SQS, SNS message attributes are copied to SQS message attributes. If false the SNS attributes are included in the body. + /// + public bool RawMessageDelivery { get; set; } + + /// + /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. + /// + public RedrivePolicy RedrivePolicy { get; set; } + + /// + /// Policy for retries to HTTP. The parameter is for that SNS Subscription and overrides any policy on the SNS Topic. + /// + public DeliveryPolicy DeliveryPolicy { get; set; } + + /// + /// The display name to use with an SNS subscription + /// + public string DisplayName { get; set; } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("protocol", this.Protocol.GetDisplayName()); + writer.WriteRequiredObject("endpoint", this.Endpoint, (w, e) => e.Serialize(w)); + writer.WriteOptionalObject("filterPolicy", this.FilterPolicy, (w, f) => f.Serialize(w)); + writer.WriteRequiredProperty("rawMessageDelivery", this.RawMessageDelivery); + writer.WriteOptionalObject("redrivePolicy", this.RedrivePolicy, (w, p) => p.Serialize(w)); + writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); + writer.WriteOptionalProperty("displayName", this.DisplayName); + writer.WriteEndObject(); + } +} + +public enum Protocol +{ + [Display("http")] + Http, + [Display("https")] + Https, + [Display("email")] + Email, + [Display("email-json")] + EmailJson, + [Display("sms")] + Sms, + [Display("sqs")] + Sqs, + [Display("application")] + Application, + [Display("lambda")] + Lambda, + [Display("firehose")] + Firehose, +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs new file mode 100644 index 00000000..26871e81 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs @@ -0,0 +1,80 @@ +using System; +using LEGO.AsyncAPI.Attributes; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Writers; + +namespace LEGO.AsyncAPI.Bindings.Sns; + +public class DeliveryPolicy : IAsyncApiElement +{ + /// + /// The minimum delay for a retry in seconds. + /// + public int? MinDelayTarget { get; set; } + + /// + /// The maximum delay for a retry in seconds. + /// + public int? MaxDelayTarget { get; set; } + + /// + /// The total number of retries, including immediate, pre-backoff, backoff, and post-backoff retries. + /// + public int? NumRetries { get; set; } + + /// + /// The number of immediate retries (with no delay). + /// + public int? NumNoDelayRetries { get; set; } + + /// + /// The number of immediate retries (with delay). + /// + public int? NumMinDelayRetries { get; set; } + + /// + /// The number of post-backoff phase retries, with the maximum delay between retries. + /// + public int? NumMaxDelayRetries { get; set; } + + /// + /// The algorithm for backoff between retries. + /// + public BackOffFunction BackoffFunction { get; set; } + + /// + /// The maximum number of deliveries per second, per subscription. + /// + public int? MaxReceivesPerSecond { get; set; } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty("minDelayTarget", this.MinDelayTarget); + writer.WriteOptionalProperty("maxDelayTarget", this.MaxDelayTarget); + writer.WriteOptionalProperty("numRetries", this.NumRetries); + writer.WriteOptionalProperty("numNoDelayRetries", this.NumNoDelayRetries); + writer.WriteOptionalProperty("numMinDelayRetries", this.NumMinDelayRetries); + writer.WriteOptionalProperty("numMaxDelayRetries", this.NumMaxDelayRetries); + writer.WriteOptionalProperty("backOffFunction", this.BackoffFunction.GetDisplayName()); + writer.WriteOptionalProperty("maxReceivesPerSecond", this.MaxReceivesPerSecond); + writer.WriteEndObject(); + } +} + +public enum BackOffFunction +{ + [Display("arithmetic")] + Arithmetic, + [Display("exponential")] + Exponential, + [Display("geometric")] + Geometric, + [Display("linear")] + Linear, +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs new file mode 100644 index 00000000..b6189ce0 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs @@ -0,0 +1,25 @@ +using System; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Writers; + +namespace LEGO.AsyncAPI.Bindings.Sns; + +public class FilterPolicy : IAsyncApiElement +{ + /// + /// A map of a message attribute to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint. + /// + public IAsyncApiAny Attributes { get; set; } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredObject("attributes", this.Attributes, (w, a) => a.Write(w)); + writer.WriteEndObject(); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs new file mode 100644 index 00000000..d22acc6b --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs @@ -0,0 +1,34 @@ +using System; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Writers; + +namespace LEGO.AsyncAPI.Bindings.Sns; + +public class Identifier : IAsyncApiElement +{ + public string Url { get; set; } + + public string Email { get; set; } + + public string Phone { get; set; } + + public string Arn { get; set; } + + public string Name { get; set; } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty("url", this.Url); + writer.WriteOptionalProperty("email", this.Email); + writer.WriteOptionalProperty("phone", this.Phone); + writer.WriteOptionalProperty("arn", this.Arn); + writer.WriteOptionalProperty("name", this.Name); + writer.WriteEndObject(); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs new file mode 100644 index 00000000..c55549f6 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs @@ -0,0 +1,31 @@ +using System; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Writers; + +namespace LEGO.AsyncAPI.Bindings.Sns; + +public class RedrivePolicy : IAsyncApiElement +{ + /// + /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. + /// + public Identifier DeadLetterQueue { get; set; } + + /// + /// The number of times a message is delivered to the source queue before being moved to the dead-letter queue. + /// + public int? MaxReceiveCount { get; set; } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredObject("deadLetterQueue", this.DeadLetterQueue, (w, q) => q.Serialize(w)); + writer.WriteOptionalProperty("maxReceiveCount", this.MaxReceiveCount); + writer.WriteEndObject(); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs new file mode 100644 index 00000000..d5e7976e --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using LEGO.AsyncAPI.Readers.ParseNodes; +using LEGO.AsyncAPI.Writers; + +namespace LEGO.AsyncAPI.Bindings.Sns; + +public class SnsOperationBinding : OperationBinding +{ + /// + /// Often we can assume that the SNS Topic is the channel name-we provide this field in case the you need to supply the ARN, or the Topic name is not the channel name in the AsyncAPI document. + /// + public Identifier Topic { get; set; } + + /// + /// The protocols that listen to this topic and their endpoints. + /// + public List Consumers { get; set; } + + /// + /// Policy for retries to HTTP. The field is the default for HTTP receivers of the SNS Topic which may be overridden by a specific consumer. + /// + public DeliveryPolicy DeliveryPolicy { get; set; } + + public override string BindingKey => "sns"; + + protected override FixedFieldMap FixedFieldMap { get; } + + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalObject("topic", this.Topic, (w, t) => t.Serialize(w)); + writer.WriteOptionalCollection("consumers", this.Consumers, (w, c) => c.Serialize(w)); + writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); + writer.WriteEndObject(); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 4ff0d446..c319cd4f 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -16,7 +16,7 @@ public class Statement : IAsyncApiElement /// // public StringOrStringList Principal { get; set; } public StringOrStringList Principal { get; set; } - + /// /// The SNS permission being allowed or denied e.g. sns:Publish /// diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index be1d33aa..eafe3eb6 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -1,4 +1,5 @@ using System; +using LEGO.AsyncAPI.Models.Any; using BindingsCollection = LEGO.AsyncAPI.Bindings.BindingsCollection; namespace LEGO.AsyncAPI.Tests.Bindings.Sns @@ -89,5 +90,145 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() binding.Should().BeEquivalentTo(channel); } + + [Test] + public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = + @"bindings: + sns: + topic: + name: someTopic + consumers: + - protocol: sqs + endpoint: + name: someQueue + filterPolicy: + attributes: + store: + - asyncapi_corp + contact: dec.kolakowski + event: + - anything-but: order_cancelled + order_key: + transient: by_area + customer_interests: + - rugby + - football + - baseball + rawMessageDelivery: false + redrivePolicy: + deadLetterQueue: + arn: arn:aws:SQS:eu-west-1:0000000:123456789 + maxReceiveCount: 25 + deliveryPolicy: + minDelayTarget: 10 + maxDelayTarget: 100 + numRetries: 5 + numNoDelayRetries: 2 + numMinDelayRetries: 3 + numMaxDelayRetries: 5 + backoffFunction: linear + maxReceivesPerSecond: 2"; + + var operation = new AsyncApiOperation(); + operation.Bindings.Add(new SnsOperationBinding() + { + Topic = new Identifier() + { + Name = "someTopic", + }, + Consumers = new List() + { + new Consumer() + { + Protocol = Protocol.Sqs, + Endpoint = new Identifier() + { + Name = "someQueue", + }, + FilterPolicy = new FilterPolicy() + { + Attributes = new AsyncApiObject() + { + { "store", new AsyncApiArray() { new AsyncApiString("asyncapi_corp") } }, + { "contact", new AsyncApiString("dec.kolakowski") }, + { + "event", new AsyncApiArray() + { + new AsyncApiObject() + { + { "anything-but", new AsyncApiString("order_cancelled") }, + }, + } + }, + { + "order_key", new AsyncApiObject() + { + { "transient", new AsyncApiString("by_area") }, + } + }, + { + "customer_interests", new AsyncApiArray() + { + new AsyncApiString("rugby"), + new AsyncApiString("football"), + new AsyncApiString("baseball"), + } + }, + }, + }, + RawMessageDelivery = true, + RedrivePolicy = new RedrivePolicy() + { + DeadLetterQueue = new Identifier() + { + Arn = "arn:aws:SQS:eu-west-1:0000000:123456789" + }, + MaxReceiveCount = 25, + }, + DeliveryPolicy = new DeliveryPolicy() + { + MinDelayTarget = 10, + MaxDelayTarget = 100, + NumRetries = 5, + NumNoDelayRetries = 2, + NumMinDelayRetries = 3, + NumMaxDelayRetries = 5, + BackoffFunction = BackOffFunction.Linear, + MaxReceivesPerSecond = 2, + }, + }, + }, + DeliveryPolicy = new DeliveryPolicy() + { + MinDelayTarget = 10, + MaxDelayTarget = 100, + NumRetries = 5, + NumNoDelayRetries = 2, + NumMinDelayRetries = 3, + NumMaxDelayRetries = 5, + BackoffFunction = BackOffFunction.Linear, + MaxReceivesPerSecond = 2, + }, + }); + + // Act + var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Sns); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + + // Assert + Assert.AreEqual(actual, expected); + binding.Should().BeEquivalentTo(operation); + + } } } \ No newline at end of file From 11755d562cad43364935d4e2783be174e4f4bc56 Mon Sep 17 00:00:00 2001 From: "Alex W. Carlsen" Date: Thu, 11 May 2023 08:42:23 +0200 Subject: [PATCH 15/39] refactor: binding improvement --- src/LEGO.AsyncAPI.Bindings/Binding.cs | 16 ----------- src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs | 28 +++++++++++++++++++ .../Kafka/KafkaChannelBinding.cs | 10 +------ .../Pulsar/PulsarChannelBinding.cs | 10 +------ 4 files changed, 30 insertions(+), 34 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs diff --git a/src/LEGO.AsyncAPI.Bindings/Binding.cs b/src/LEGO.AsyncAPI.Bindings/Binding.cs index 86f2cfde..e7c57400 100644 --- a/src/LEGO.AsyncAPI.Bindings/Binding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Binding.cs @@ -9,22 +9,6 @@ namespace LEGO.AsyncAPI.Bindings public abstract class Binding : AsyncApiBinding, IBindingParser where T : IBinding, new() { - protected static void ParseMap( - MapNode mapNode, - T domainObject, - FixedFieldMap fixedFieldMap) - { - if (mapNode == null) - { - return; - } - - foreach (var propertyNode in mapNode) - { - propertyNode.ParseField(domainObject, fixedFieldMap, null); - } - } - public abstract T LoadBinding(PropertyNode node); } } diff --git a/src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs b/src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs new file mode 100644 index 00000000..9ce8b9ad --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs @@ -0,0 +1,28 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Readers.ParseNodes; + + public static class BindingHelpers + { + public static T ParseMap(this ParseNode node, FixedFieldMap fixedFieldMap) + where T : new() + { + var mapNode = node.CheckMapNode(node.Context.GetLocation()); + if (mapNode == null) + { + return default(T); + } + + var instance = new T(); + + foreach (var propertyNode in mapNode) + { + propertyNode.ParseField(instance, fixedFieldMap, null); + } + + return instance; + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs index 921903c1..78b745e0 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs @@ -40,7 +40,7 @@ public class KafkaChannelBinding : ChannelBinding { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, { "partitions", (a, n) => { a.Partitions = n.GetIntegerValue(); } }, - { "topicConfiguration", (a, n) => { a.TopicConfiguration = this.LoadTopicConfiguration(n); } }, + { "topicConfiguration", (a, n) => { a.TopicConfiguration = n.ParseMap(kafkaChannelTopicConfigurationObjectFixedFields); } }, { "replicas", (a, n) => { a.Replicas = n.GetIntegerValue(); } }, }; @@ -53,14 +53,6 @@ public class KafkaChannelBinding : ChannelBinding { "max.message.bytes", (a, n) => { a.MaxMessageBytes = n.GetIntegerValue(); } }, }; - private TopicConfigurationObject LoadTopicConfiguration(ParseNode node) - { - var mapNode = node.CheckMapNode("topicConfiguration"); - var retention = new TopicConfigurationObject(); - ParseMap(mapNode, retention, kafkaChannelTopicConfigurationObjectFixedFields); - return retention; - } - /// /// Serialize to AsyncAPI V2 document without using reference. /// diff --git a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs index 481043c4..c673a8e8 100644 --- a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs @@ -74,7 +74,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) { "namespace", (a, n) => { a.Namespace = n.GetScalarValue(); } }, { "persistence", (a, n) => { a.Persistence = n.GetScalarValue().GetEnumFromDisplayName(); } }, { "compaction", (a, n) => { a.Compaction = n.GetIntegerValue(); } }, - { "retention", (a, n) => { a.Retention = this.LoadRetention(n); } }, + { "retention", (a, n) => { a.Retention = n.ParseMap(this.pulsarServerBindingRetentionFixedFields); } }, { "geo-replication", (a, n) => { a.GeoReplication = n.CreateSimpleList(s => s.GetScalarValue()); } }, { "ttl", (a, n) => { a.TTL = n.GetIntegerValue(); } }, { "deduplication", (a, n) => { a.Deduplication = n.GetBooleanValue(); } }, @@ -85,13 +85,5 @@ public override void SerializeProperties(IAsyncApiWriter writer) { "time", (a, n) => { a.Time = n.GetIntegerValue(); } }, { "size", (a, n) => { a.Size = n.GetIntegerValue(); } }, }; - - private RetentionDefinition LoadRetention(ParseNode node) - { - var mapNode = node.CheckMapNode("retention"); - var retention = new RetentionDefinition(); - ParseMap(mapNode, retention, this.pulsarServerBindingRetentionFixedFields); - return retention; - } } } From 33769c306f635a58e25506a6eac39c191b991178 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Thu, 11 May 2023 15:10:31 +0100 Subject: [PATCH 16/39] feat: WIP Add sns op fix fields --- .../Sns/SnsChannelBinding.cs | 34 ++---------- .../Sns/SnsOperationBinding.cs | 53 ++++++++++++++++++- .../Bindings/Sns/SnsBindings_Should.cs | 25 ++++++++- 3 files changed, 79 insertions(+), 33 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index a36b543e..6b7799ed 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -41,20 +41,20 @@ public class SnsChannelBinding : ChannelBinding protected override FixedFieldMap FixedFieldMap => new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, - { "type", (a, n) => { a.Type = LoadType(n); } }, - { "policy", (a, n) => { a.Policy = LoadPolicy(n); } }, + { "type", (a, n) => { a.Type = n.ParseMap(this.orderingFixedFields); } }, + { "policy", (a, n) => { a.Policy = n.ParseMap(this.policyFixedFields); } }, { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, }; - private static FixedFieldMap orderingFixedFields = new() + private FixedFieldMap orderingFixedFields = new() { { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, { "contentBasedDeduplication", (a, n) => { a.ContentBasedDeduplication = n.GetBooleanValue(); } }, }; - private static FixedFieldMap policyFixedFields = new() + private FixedFieldMap policyFixedFields = new() { - { "statements", (a, n) => { a.Statements = n.CreateList(s => LoadStatement(s)); } }, + { "statements", (a, n) => { a.Statements = n.CreateList(s => s.ParseMap(statementFixedFields)); } }, }; private static FixedFieldMap statementFixedFields = new() @@ -63,31 +63,7 @@ public class SnsChannelBinding : ChannelBinding { "principal", (a, n) => { a.Principal = LoadStringOrStringList(n, "principal"); } }, { "action", (a, n) => { a.Action = LoadStringOrStringList(n, "action"); } }, }; - - private static OrderingConfiguration LoadType(ParseNode node) - { - var mapNode = node.CheckMapNode("type"); - var ordering = new OrderingConfiguration(); - ParseMap(mapNode, ordering, orderingFixedFields); - return ordering; - } - - private static Policy LoadPolicy(ParseNode node) - { - var mapNode = node.CheckMapNode("policy"); - var policy = new Policy(); - ParseMap(mapNode, policy, policyFixedFields); - return policy; - } - private static Statement LoadStatement(ParseNode node) - { - var mapNode = node.CheckMapNode("statement"); - var statement = new Statement(); - ParseMap(mapNode, statement, statementFixedFields); - return statement; - } - public static StringOrStringList LoadStringOrStringList(ParseNode node, string nodeName) { var stringOrStringList = new StringOrStringList(); diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs index d5e7976e..466aa7a7 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; @@ -24,8 +25,56 @@ public class SnsOperationBinding : OperationBinding public override string BindingKey => "sns"; - protected override FixedFieldMap FixedFieldMap { get; } - + protected override FixedFieldMap FixedFieldMap => new() + { + { "topic", (a, n) => { a.Topic = n.ParseMap(this.identifierFixFields); } }, + { "consumers", (a, n) => { a.Consumers = n.CreateList(s => s.ParseMap(this.consumerFixedFields)); } }, + { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMap(this.deliveryPolicyFixedFields); } }, + }; + + private FixedFieldMap identifierFixFields => new() + { + { "url", (a, n) => { a.Url = n.GetScalarValue(); } }, + { "email", (a, n) => { a.Url = n.GetScalarValue(); } }, + { "phone", (a, n) => { a.Url = n.GetScalarValue(); } }, + { "arn", (a, n) => { a.Url = n.GetScalarValue(); } }, + { "name", (a, n) => { a.Url = n.GetScalarValue(); } }, + }; + + private FixedFieldMap consumerFixedFields => new () + { + { "protocol", (a, n) => { a.Protocol = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "endpoint", (a, n) => { a.Endpoint = n.ParseMap(this.identifierFixFields); } }, + { "filterPolicy", (a, n) => { a.FilterPolicy = n.ParseMap(this.filterPolicyFixedFields); } }, + { "rawMessageDelivery", (a, n) => { a.RawMessageDelivery = n.GetBooleanValue(); } }, + { "redrivePolicy", (a, n) => { a.RedrivePolicy = n.ParseMap(this.redrivePolicyFixedFields); } }, + { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMap(this.deliveryPolicyFixedFields); } }, + { "displayName", (a, n) => { a.DisplayName = n.GetScalarValue(); } }, + }; + + private FixedFieldMap filterPolicyFixedFields => new() + { + { "attributes", (a, n) => { a.Attributes = new AsyncApiObject(); }} + }; + + private FixedFieldMap redrivePolicyFixedFields => new() + { + { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMap(identifierFixFields); } }, + { "maxReceiveCount", (a, n) => { a.MaxReceiveCount = n.GetIntegerValue(); } }, + }; + + private FixedFieldMap deliveryPolicyFixedFields => new() + { + { "minDelayTarget", (a, n) => { a.MinDelayTarget = n.GetIntegerValue(); } }, + { "maxDelayTarget", (a, n) => { a.MaxDelayTarget = n.GetIntegerValue(); } }, + { "numRetries", (a, n) => { a.NumRetries = n.GetIntegerValue(); } }, + { "numNoDelayRetries", (a, n) => { a.NumNoDelayRetries = n.GetIntegerValue(); } }, + { "numMinDelayRetries", (a, n) => { a.NumMinDelayRetries = n.GetIntegerValue(); } }, + { "numMaxDelayRetries", (a, n) => { a.NumMaxDelayRetries = n.GetIntegerValue(); } }, + { "backOffFunction", (a, n) => { a.BackoffFunction = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "maxReceivesPerSecond", (a, n) => { a.MaxReceivesPerSecond = n.GetIntegerValue(); } }, + }; + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index eafe3eb6..6693732f 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -28,10 +28,15 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: deny - principal: someARN + principal: arn:aws:iam::123456789012:user/alex.wichmann action: - sns:Publish - sns:Delete + - effect: allow + principal: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski + action: sns:Create tags: owner: AsyncAPI.NET platform: AsyncAPIOrg"; @@ -54,7 +59,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() Effect = Effect.Deny, Principal = new StringOrStringList() { - StringValue = "someARN", + StringValue = "arn:aws:iam::123456789012:user/alex.wichmann", }, Action = new StringOrStringList() { @@ -65,6 +70,22 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() }, }, }, + new Statement() + { + Effect = Effect.Allow, + Principal = new StringOrStringList() + { + StringList = new List() + { + "arn:aws:iam::123456789012:user/alex.wichmann", + "arn:aws:iam::123456789012:user/dec.kolakowski", + }, + }, + Action = new StringOrStringList() + { + StringValue = "sns:Create", + }, + }, }, }, Tags = new Dictionary() From 6a9266fe3a24951fcf4ab347dd185a8a5083e538 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Thu, 11 May 2023 15:27:24 +0100 Subject: [PATCH 17/39] feat: WIP Add correct required properties --- src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 6b7799ed..b4085d44 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -88,7 +88,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) } writer.WriteStartObject(); - writer.WriteOptionalProperty(AsyncApiConstants.Name, this.Name); + writer.WriteRequiredProperty(AsyncApiConstants.Name, this.Name); writer.WriteOptionalObject("type", this.Type, (w, t) => t.Serialize(w)); writer.WriteOptionalObject(AsyncApiConstants.Policy, this.Policy, (w, t) => t.Serialize(w)); writer.WriteOptionalMap("tags", this.Tags, (w, t) => w.WriteValue(t)); diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index c319cd4f..e91b2195 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -30,9 +30,9 @@ public void Serialize(IAsyncApiWriter writer) } writer.WriteStartObject(); - writer.WriteOptionalProperty(AsyncApiConstants.Effect, this.Effect.GetDisplayName()); - writer.WriteOptionalObject(AsyncApiConstants.Principal, this.Principal, (w, t) => t.Serialize(w)); - writer.WriteOptionalObject(AsyncApiConstants.Action, this.Action, (w, t) => t.Serialize(w)); + writer.WriteRequiredProperty(AsyncApiConstants.Effect, this.Effect.GetDisplayName()); + writer.WriteRequiredObject(AsyncApiConstants.Principal, this.Principal, (w, t) => t.Serialize(w)); + writer.WriteRequiredObject(AsyncApiConstants.Action, this.Action, (w, t) => t.Serialize(w)); writer.WriteEndObject(); } } From 6bcf4e244bf6186be9f4b14d6e9cd83687440627 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Thu, 11 May 2023 15:29:22 +0100 Subject: [PATCH 18/39] feat: WIP remove constants --- src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs | 4 ++-- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 6 +++--- src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs | 5 ----- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs index ec68f849..e7254d57 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs @@ -22,7 +22,7 @@ public void Serialize(IAsyncApiWriter writer) } writer.WriteStartObject(); - writer.WriteOptionalCollection(AsyncApiConstants.Statements, this.Statements, (w, t) => t.Serialize(w)); + writer.WriteOptionalCollection("statements", this.Statements, (w, t) => t.Serialize(w)); writer.WriteEndObject(); } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index b4085d44..d8e7cbd1 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -88,9 +88,9 @@ public override void SerializeProperties(IAsyncApiWriter writer) } writer.WriteStartObject(); - writer.WriteRequiredProperty(AsyncApiConstants.Name, this.Name); + writer.WriteRequiredProperty("name", this.Name); writer.WriteOptionalObject("type", this.Type, (w, t) => t.Serialize(w)); - writer.WriteOptionalObject(AsyncApiConstants.Policy, this.Policy, (w, t) => t.Serialize(w)); + writer.WriteOptionalObject("policy", this.Policy, (w, t) => t.Serialize(w)); writer.WriteOptionalMap("tags", this.Tags, (w, t) => w.WriteValue(t)); writer.WriteEndObject(); } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index e91b2195..7259e062 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -30,9 +30,9 @@ public void Serialize(IAsyncApiWriter writer) } writer.WriteStartObject(); - writer.WriteRequiredProperty(AsyncApiConstants.Effect, this.Effect.GetDisplayName()); - writer.WriteRequiredObject(AsyncApiConstants.Principal, this.Principal, (w, t) => t.Serialize(w)); - writer.WriteRequiredObject(AsyncApiConstants.Action, this.Action, (w, t) => t.Serialize(w)); + writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); + writer.WriteRequiredObject("action", this.Action, (w, t) => t.Serialize(w)); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs index 559b6f93..2d3ae13c 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs @@ -138,10 +138,5 @@ public static class AsyncApiConstants public const string MaxMessageBytes = "max.message.bytes"; public const string TopicConfiguration = "topicConfiguration"; public const string GeoReplication = "geo-replication"; - public const string Policy = "policy"; - public const string Statements = "statements"; - public const string Effect = "effect"; - public const string Principal = "principal"; - public const string Action = "action"; } } From b4d3a56d9d8849c153389dd96e0d35e7db3462df Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Thu, 11 May 2023 15:35:55 +0100 Subject: [PATCH 19/39] feat: WIP move to block scoped namespaces --- src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs | 134 +++++++-------- .../Sns/DeliveryPolicy.cs | 131 +++++++-------- .../Sns/FilterPolicy.cs | 37 ++-- src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs | 49 +++--- .../Sns/OrderingConfiguration.cs | 66 ++++---- src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs | 44 ++--- .../Sns/RedrivePolicy.cs | 49 +++--- .../Sns/SnsChannelBinding.cs | 156 ++++++++--------- .../Sns/SnsOperationBinding.cs | 158 +++++++++--------- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 116 ++++++------- 10 files changed, 469 insertions(+), 471 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs index d532b8b2..7b7fab89 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs @@ -1,84 +1,76 @@ -using System; -using LEGO.AsyncAPI.Attributes; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; - -namespace LEGO.AsyncAPI.Bindings.Sns; - -public class Consumer : IAsyncApiElement +namespace LEGO.AsyncAPI.Bindings.Sns { - /// - /// What protocol will this endpoint receive messages by? - /// - public Protocol Protocol { get; set; } + using System; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; - /// - /// Where are messages being delivered to? - /// - public Identifier Endpoint { get; set; } + public class Consumer : IAsyncApiElement + { + /// + /// What protocol will this endpoint receive messages by? + /// + public Protocol Protocol { get; set; } - /// - /// Only receive a subset of messages from the channel, determined by this policy. - /// - public FilterPolicy FilterPolicy { get; set; } + /// + /// Where are messages being delivered to? + /// + public Identifier Endpoint { get; set; } - /// - /// If true AWS SNS attributes are removed from the body, and for SQS, SNS message attributes are copied to SQS message attributes. If false the SNS attributes are included in the body. - /// - public bool RawMessageDelivery { get; set; } + /// + /// Only receive a subset of messages from the channel, determined by this policy. + /// + public FilterPolicy FilterPolicy { get; set; } - /// - /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. - /// - public RedrivePolicy RedrivePolicy { get; set; } + /// + /// If true AWS SNS attributes are removed from the body, and for SQS, SNS message attributes are copied to SQS message attributes. If false the SNS attributes are included in the body. + /// + public bool RawMessageDelivery { get; set; } - /// - /// Policy for retries to HTTP. The parameter is for that SNS Subscription and overrides any policy on the SNS Topic. - /// - public DeliveryPolicy DeliveryPolicy { get; set; } + /// + /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. + /// + public RedrivePolicy RedrivePolicy { get; set; } - /// - /// The display name to use with an SNS subscription - /// - public string DisplayName { get; set; } + /// + /// Policy for retries to HTTP. The parameter is for that SNS Subscription and overrides any policy on the SNS Topic. + /// + public DeliveryPolicy DeliveryPolicy { get; set; } - public void Serialize(IAsyncApiWriter writer) - { - if (writer is null) + /// + /// The display name to use with an SNS subscription + /// + public string DisplayName { get; set; } + + public void Serialize(IAsyncApiWriter writer) { - throw new ArgumentNullException(nameof(writer)); - } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - writer.WriteStartObject(); - writer.WriteRequiredProperty("protocol", this.Protocol.GetDisplayName()); - writer.WriteRequiredObject("endpoint", this.Endpoint, (w, e) => e.Serialize(w)); - writer.WriteOptionalObject("filterPolicy", this.FilterPolicy, (w, f) => f.Serialize(w)); - writer.WriteRequiredProperty("rawMessageDelivery", this.RawMessageDelivery); - writer.WriteOptionalObject("redrivePolicy", this.RedrivePolicy, (w, p) => p.Serialize(w)); - writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); - writer.WriteOptionalProperty("displayName", this.DisplayName); - writer.WriteEndObject(); + writer.WriteStartObject(); + writer.WriteRequiredProperty("protocol", this.Protocol.GetDisplayName()); + writer.WriteRequiredObject("endpoint", this.Endpoint, (w, e) => e.Serialize(w)); + writer.WriteOptionalObject("filterPolicy", this.FilterPolicy, (w, f) => f.Serialize(w)); + writer.WriteRequiredProperty("rawMessageDelivery", this.RawMessageDelivery); + writer.WriteOptionalObject("redrivePolicy", this.RedrivePolicy, (w, p) => p.Serialize(w)); + writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); + writer.WriteOptionalProperty("displayName", this.DisplayName); + writer.WriteEndObject(); + } } -} -public enum Protocol -{ - [Display("http")] - Http, - [Display("https")] - Https, - [Display("email")] - Email, - [Display("email-json")] - EmailJson, - [Display("sms")] - Sms, - [Display("sqs")] - Sqs, - [Display("application")] - Application, - [Display("lambda")] - Lambda, - [Display("firehose")] - Firehose, + public enum Protocol + { + [Display("http")] Http, + [Display("https")] Https, + [Display("email")] Email, + [Display("email-json")] EmailJson, + [Display("sms")] Sms, + [Display("sqs")] Sqs, + [Display("application")] Application, + [Display("lambda")] Lambda, + [Display("firehose")] Firehose, + } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs index 26871e81..f9e4e08b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs @@ -1,80 +1,77 @@ -using System; -using LEGO.AsyncAPI.Attributes; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; - -namespace LEGO.AsyncAPI.Bindings.Sns; - -public class DeliveryPolicy : IAsyncApiElement +namespace LEGO.AsyncAPI.Bindings.Sns { - /// - /// The minimum delay for a retry in seconds. - /// - public int? MinDelayTarget { get; set; } + using System; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class DeliveryPolicy : IAsyncApiElement + { + /// + /// The minimum delay for a retry in seconds. + /// + public int? MinDelayTarget { get; set; } - /// - /// The maximum delay for a retry in seconds. - /// - public int? MaxDelayTarget { get; set; } + /// + /// The maximum delay for a retry in seconds. + /// + public int? MaxDelayTarget { get; set; } - /// - /// The total number of retries, including immediate, pre-backoff, backoff, and post-backoff retries. - /// - public int? NumRetries { get; set; } + /// + /// The total number of retries, including immediate, pre-backoff, backoff, and post-backoff retries. + /// + public int? NumRetries { get; set; } - /// - /// The number of immediate retries (with no delay). - /// - public int? NumNoDelayRetries { get; set; } + /// + /// The number of immediate retries (with no delay). + /// + public int? NumNoDelayRetries { get; set; } - /// - /// The number of immediate retries (with delay). - /// - public int? NumMinDelayRetries { get; set; } + /// + /// The number of immediate retries (with delay). + /// + public int? NumMinDelayRetries { get; set; } - /// - /// The number of post-backoff phase retries, with the maximum delay between retries. - /// - public int? NumMaxDelayRetries { get; set; } + /// + /// The number of post-backoff phase retries, with the maximum delay between retries. + /// + public int? NumMaxDelayRetries { get; set; } - /// - /// The algorithm for backoff between retries. - /// - public BackOffFunction BackoffFunction { get; set; } + /// + /// The algorithm for backoff between retries. + /// + public BackOffFunction BackoffFunction { get; set; } - /// - /// The maximum number of deliveries per second, per subscription. - /// - public int? MaxReceivesPerSecond { get; set; } - - public void Serialize(IAsyncApiWriter writer) - { - if (writer is null) + /// + /// The maximum number of deliveries per second, per subscription. + /// + public int? MaxReceivesPerSecond { get; set; } + + public void Serialize(IAsyncApiWriter writer) { - throw new ArgumentNullException(nameof(writer)); - } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - writer.WriteStartObject(); - writer.WriteOptionalProperty("minDelayTarget", this.MinDelayTarget); - writer.WriteOptionalProperty("maxDelayTarget", this.MaxDelayTarget); - writer.WriteOptionalProperty("numRetries", this.NumRetries); - writer.WriteOptionalProperty("numNoDelayRetries", this.NumNoDelayRetries); - writer.WriteOptionalProperty("numMinDelayRetries", this.NumMinDelayRetries); - writer.WriteOptionalProperty("numMaxDelayRetries", this.NumMaxDelayRetries); - writer.WriteOptionalProperty("backOffFunction", this.BackoffFunction.GetDisplayName()); - writer.WriteOptionalProperty("maxReceivesPerSecond", this.MaxReceivesPerSecond); - writer.WriteEndObject(); + writer.WriteStartObject(); + writer.WriteOptionalProperty("minDelayTarget", this.MinDelayTarget); + writer.WriteOptionalProperty("maxDelayTarget", this.MaxDelayTarget); + writer.WriteOptionalProperty("numRetries", this.NumRetries); + writer.WriteOptionalProperty("numNoDelayRetries", this.NumNoDelayRetries); + writer.WriteOptionalProperty("numMinDelayRetries", this.NumMinDelayRetries); + writer.WriteOptionalProperty("numMaxDelayRetries", this.NumMaxDelayRetries); + writer.WriteOptionalProperty("backOffFunction", this.BackoffFunction.GetDisplayName()); + writer.WriteOptionalProperty("maxReceivesPerSecond", this.MaxReceivesPerSecond); + writer.WriteEndObject(); + } } -} -public enum BackOffFunction -{ - [Display("arithmetic")] - Arithmetic, - [Display("exponential")] - Exponential, - [Display("geometric")] - Geometric, - [Display("linear")] - Linear, + public enum BackOffFunction + { + [Display("arithmetic")] Arithmetic, + [Display("exponential")] Exponential, + [Display("geometric")] Geometric, + [Display("linear")] Linear, + } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs index b6189ce0..0c6aa0fe 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs @@ -1,25 +1,26 @@ -using System; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; - -namespace LEGO.AsyncAPI.Bindings.Sns; - -public class FilterPolicy : IAsyncApiElement +namespace LEGO.AsyncAPI.Bindings.Sns { - /// - /// A map of a message attribute to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint. - /// - public IAsyncApiAny Attributes { get; set; } + using System; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; - public void Serialize(IAsyncApiWriter writer) + public class FilterPolicy : IAsyncApiElement { - if (writer is null) + /// + /// A map of a message attribute to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint. + /// + public IAsyncApiAny Attributes { get; set; } + + public void Serialize(IAsyncApiWriter writer) { - throw new ArgumentNullException(nameof(writer)); - } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - writer.WriteStartObject(); - writer.WriteRequiredObject("attributes", this.Attributes, (w, a) => a.Write(w)); - writer.WriteEndObject(); + writer.WriteStartObject(); + writer.WriteRequiredObject("attributes", this.Attributes, (w, a) => a.Write(w)); + writer.WriteEndObject(); + } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs index d22acc6b..86b09e45 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs @@ -1,34 +1,35 @@ -using System; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; - -namespace LEGO.AsyncAPI.Bindings.Sns; - -public class Identifier : IAsyncApiElement +namespace LEGO.AsyncAPI.Bindings.Sns { - public string Url { get; set; } + using System; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class Identifier : IAsyncApiElement + { + public string Url { get; set; } - public string Email { get; set; } + public string Email { get; set; } - public string Phone { get; set; } + public string Phone { get; set; } - public string Arn { get; set; } + public string Arn { get; set; } - public string Name { get; set; } + public string Name { get; set; } - public void Serialize(IAsyncApiWriter writer) - { - if (writer is null) + public void Serialize(IAsyncApiWriter writer) { - throw new ArgumentNullException(nameof(writer)); + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty("url", this.Url); + writer.WriteOptionalProperty("email", this.Email); + writer.WriteOptionalProperty("phone", this.Phone); + writer.WriteOptionalProperty("arn", this.Arn); + writer.WriteOptionalProperty("name", this.Name); + writer.WriteEndObject(); } - - writer.WriteStartObject(); - writer.WriteOptionalProperty("url", this.Url); - writer.WriteOptionalProperty("email", this.Email); - writer.WriteOptionalProperty("phone", this.Phone); - writer.WriteOptionalProperty("arn", this.Arn); - writer.WriteOptionalProperty("name", this.Name); - writer.WriteEndObject(); } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs b/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs index 58727e4a..b8323d45 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs @@ -1,41 +1,41 @@ - -using System; -using LEGO.AsyncAPI.Attributes; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; - -namespace LEGO.AsyncAPI.Bindings.Sns; - -public class OrderingConfiguration : IAsyncApiElement +namespace LEGO.AsyncAPI.Bindings.Sns { - /// - /// What type of SNS Topic is this? - /// - public Ordering Type { get; set; } + using System; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class OrderingConfiguration : IAsyncApiElement + { + /// + /// What type of SNS Topic is this? + /// + public Ordering Type { get; set; } - /// - /// True to turn on de-duplication of messages for a channel. - /// - public bool ContentBasedDeduplication { get; set; } + /// + /// True to turn on de-duplication of messages for a channel. + /// + public bool ContentBasedDeduplication { get; set; } - public void Serialize(IAsyncApiWriter writer) - { - if (writer is null) + public void Serialize(IAsyncApiWriter writer) { - throw new ArgumentNullException(nameof(writer)); - } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - writer.WriteStartObject(); - writer.WriteOptionalProperty("type", this.Type.GetDisplayName()); - writer.WriteOptionalProperty("contentBasedDeduplication", this.ContentBasedDeduplication); - writer.WriteEndObject(); + writer.WriteStartObject(); + writer.WriteOptionalProperty("type", this.Type.GetDisplayName()); + writer.WriteOptionalProperty("contentBasedDeduplication", this.ContentBasedDeduplication); + writer.WriteEndObject(); + } } -} -public enum Ordering -{ - [Display("standard")] - Standard, - [Display("FIFO")] - Fifo, + public enum Ordering + { + [Display("standard")] + Standard, + [Display("FIFO")] + Fifo, + } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs index e7254d57..a51bdf46 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs @@ -1,28 +1,28 @@ -using System; -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; - -namespace LEGO.AsyncAPI.Bindings.Sns; - -using System.Collections.Generic; - -public class Policy : IAsyncApiElement +namespace LEGO.AsyncAPI.Bindings.Sns { - /// - /// An array of statement objects, each of which controls a permission for this topic. - /// - public List Statements { get; set; } - - public void Serialize(IAsyncApiWriter writer) + using System.Collections.Generic; + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class Policy : IAsyncApiElement { - if (writer is null) + /// + /// An array of statement objects, each of which controls a permission for this topic. + /// + public List Statements { get; set; } + + public void Serialize(IAsyncApiWriter writer) { - throw new ArgumentNullException(nameof(writer)); - } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - writer.WriteStartObject(); - writer.WriteOptionalCollection("statements", this.Statements, (w, t) => t.Serialize(w)); - writer.WriteEndObject(); + writer.WriteStartObject(); + writer.WriteOptionalCollection("statements", this.Statements, (w, t) => t.Serialize(w)); + writer.WriteEndObject(); + } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs index c55549f6..82ea2810 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs @@ -1,31 +1,32 @@ -using System; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; - -namespace LEGO.AsyncAPI.Bindings.Sns; - -public class RedrivePolicy : IAsyncApiElement +namespace LEGO.AsyncAPI.Bindings.Sns { - /// - /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. - /// - public Identifier DeadLetterQueue { get; set; } + using System; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; - /// - /// The number of times a message is delivered to the source queue before being moved to the dead-letter queue. - /// - public int? MaxReceiveCount { get; set; } - - public void Serialize(IAsyncApiWriter writer) + public class RedrivePolicy : IAsyncApiElement { - if (writer is null) + /// + /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. + /// + public Identifier DeadLetterQueue { get; set; } + + /// + /// The number of times a message is delivered to the source queue before being moved to the dead-letter queue. + /// + public int? MaxReceiveCount { get; set; } + + public void Serialize(IAsyncApiWriter writer) { - throw new ArgumentNullException(nameof(writer)); - } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - writer.WriteStartObject(); - writer.WriteRequiredObject("deadLetterQueue", this.DeadLetterQueue, (w, q) => q.Serialize(w)); - writer.WriteOptionalProperty("maxReceiveCount", this.MaxReceiveCount); - writer.WriteEndObject(); + writer.WriteStartObject(); + writer.WriteRequiredObject("deadLetterQueue", this.DeadLetterQueue, (w, q) => q.Serialize(w)); + writer.WriteOptionalProperty("maxReceiveCount", this.MaxReceiveCount); + writer.WriteEndObject(); + } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index d8e7cbd1..579a001a 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -1,97 +1,97 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using LEGO.AsyncAPI.Bindings; -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Readers.ParseNodes; -using LEGO.AsyncAPI.Writers; -using YamlDotNet.Core.Tokens; - -namespace LEGO.AsyncAPI.Bindings.Sns; - -using LEGO.AsyncAPI.Models.Interfaces; - -/// -/// Binding class for SNS channel settings. -/// -public class SnsChannelBinding : ChannelBinding +namespace LEGO.AsyncAPI.Bindings.Sns { - /// - /// The name of the topic. Can be different from the channel name to allow flexibility around AWS resource naming limitations. - /// - public string Name { get; set; } + using LEGO.AsyncAPI.Models.Interfaces; + using System; + using System.Collections.Generic; + using System.Linq; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + using YamlDotNet.Core.Tokens; /// - /// By default, we assume an unordered SNS topic. This field allows configuration of a FIFO SNS Topic. + /// Binding class for SNS channel settings. /// - public OrderingConfiguration Type { get; set; } + public class SnsChannelBinding : ChannelBinding + { + /// + /// The name of the topic. Can be different from the channel name to allow flexibility around AWS resource naming limitations. + /// + public string Name { get; set; } - /// - /// The security policy for the SNS Topic. - /// - public Policy Policy { get; set; } + /// + /// By default, we assume an unordered SNS topic. This field allows configuration of a FIFO SNS Topic. + /// + public OrderingConfiguration Type { get; set; } - /// - /// Key-value pairs that represent AWS tags on the topic. - /// - public Dictionary Tags { get; set; } + /// + /// The security policy for the SNS Topic. + /// + public Policy Policy { get; set; } - public override string BindingKey => "sns"; + /// + /// Key-value pairs that represent AWS tags on the topic. + /// + public Dictionary Tags { get; set; } - protected override FixedFieldMap FixedFieldMap => new() - { - { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, - { "type", (a, n) => { a.Type = n.ParseMap(this.orderingFixedFields); } }, - { "policy", (a, n) => { a.Policy = n.ParseMap(this.policyFixedFields); } }, - { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, - }; + public override string BindingKey => "sns"; - private FixedFieldMap orderingFixedFields = new() - { - { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "contentBasedDeduplication", (a, n) => { a.ContentBasedDeduplication = n.GetBooleanValue(); } }, - }; + protected override FixedFieldMap FixedFieldMap => new() + { + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + { "type", (a, n) => { a.Type = n.ParseMap(this.orderingFixedFields); } }, + { "policy", (a, n) => { a.Policy = n.ParseMap(this.policyFixedFields); } }, + { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, + }; - private FixedFieldMap policyFixedFields = new() - { - { "statements", (a, n) => { a.Statements = n.CreateList(s => s.ParseMap(statementFixedFields)); } }, - }; + private FixedFieldMap orderingFixedFields = new() + { + { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "contentBasedDeduplication", (a, n) => { a.ContentBasedDeduplication = n.GetBooleanValue(); } }, + }; - private static FixedFieldMap statementFixedFields = new() - { - { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = LoadStringOrStringList(n, "principal"); } }, - { "action", (a, n) => { a.Action = LoadStringOrStringList(n, "action"); } }, - }; - - public static StringOrStringList LoadStringOrStringList(ParseNode node, string nodeName) - { - var stringOrStringList = new StringOrStringList(); - if (node is ValueNode) + private FixedFieldMap policyFixedFields = new() { - stringOrStringList.StringValue = node.GetScalarValue(); - } - else + { "statements", (a, n) => { a.Statements = n.CreateList(s => s.ParseMap(statementFixedFields)); } }, + }; + + private static FixedFieldMap statementFixedFields = new() { - stringOrStringList.StringList = node.CreateSimpleList(s => s.GetScalarValue()); - } + { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "principal", (a, n) => { a.Principal = LoadStringOrStringList(n, "principal"); } }, + { "action", (a, n) => { a.Action = LoadStringOrStringList(n, "action"); } }, + }; + + public static StringOrStringList LoadStringOrStringList(ParseNode node, string nodeName) + { + var stringOrStringList = new StringOrStringList(); + if (node is ValueNode) + { + stringOrStringList.StringValue = node.GetScalarValue(); + } + else + { + stringOrStringList.StringList = node.CreateSimpleList(s => s.GetScalarValue()); + } - return stringOrStringList; - } + return stringOrStringList; + } - /// - public override void SerializeProperties(IAsyncApiWriter writer) - { - if (writer is null) + /// + public override void SerializeProperties(IAsyncApiWriter writer) { - throw new ArgumentNullException(nameof(writer)); - } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - writer.WriteStartObject(); - writer.WriteRequiredProperty("name", this.Name); - writer.WriteOptionalObject("type", this.Type, (w, t) => t.Serialize(w)); - writer.WriteOptionalObject("policy", this.Policy, (w, t) => t.Serialize(w)); - writer.WriteOptionalMap("tags", this.Tags, (w, t) => w.WriteValue(t)); - writer.WriteEndObject(); + writer.WriteStartObject(); + writer.WriteRequiredProperty("name", this.Name); + writer.WriteOptionalObject("type", this.Type, (w, t) => t.Serialize(w)); + writer.WriteOptionalObject("policy", this.Policy, (w, t) => t.Serialize(w)); + writer.WriteOptionalMap("tags", this.Tags, (w, t) => w.WriteValue(t)); + writer.WriteEndObject(); + } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs index 466aa7a7..bc04f195 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -1,91 +1,95 @@ -using System; -using System.Collections.Generic; -using LEGO.AsyncAPI.Models.Any; -using LEGO.AsyncAPI.Readers.ParseNodes; -using LEGO.AsyncAPI.Writers; - -namespace LEGO.AsyncAPI.Bindings.Sns; - -public class SnsOperationBinding : OperationBinding +namespace LEGO.AsyncAPI.Bindings.Sns { + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Any; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + /// - /// Often we can assume that the SNS Topic is the channel name-we provide this field in case the you need to supply the ARN, or the Topic name is not the channel name in the AsyncAPI document. - /// - public Identifier Topic { get; set; } - - /// - /// The protocols that listen to this topic and their endpoints. - /// - public List Consumers { get; set; } - - /// - /// Policy for retries to HTTP. The field is the default for HTTP receivers of the SNS Topic which may be overridden by a specific consumer. + /// This object contains information about the operation representation in SNS. /// - public DeliveryPolicy DeliveryPolicy { get; set; } - - public override string BindingKey => "sns"; - - protected override FixedFieldMap FixedFieldMap => new() + public class SnsOperationBinding : OperationBinding { - { "topic", (a, n) => { a.Topic = n.ParseMap(this.identifierFixFields); } }, - { "consumers", (a, n) => { a.Consumers = n.CreateList(s => s.ParseMap(this.consumerFixedFields)); } }, - { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMap(this.deliveryPolicyFixedFields); } }, - }; + /// + /// Often we can assume that the SNS Topic is the channel name-we provide this field in case the you need to supply the ARN, or the Topic name is not the channel name in the AsyncAPI document. + /// + public Identifier Topic { get; set; } - private FixedFieldMap identifierFixFields => new() - { - { "url", (a, n) => { a.Url = n.GetScalarValue(); } }, - { "email", (a, n) => { a.Url = n.GetScalarValue(); } }, - { "phone", (a, n) => { a.Url = n.GetScalarValue(); } }, - { "arn", (a, n) => { a.Url = n.GetScalarValue(); } }, - { "name", (a, n) => { a.Url = n.GetScalarValue(); } }, - }; + /// + /// The protocols that listen to this topic and their endpoints. + /// + public List Consumers { get; set; } - private FixedFieldMap consumerFixedFields => new () - { - { "protocol", (a, n) => { a.Protocol = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "endpoint", (a, n) => { a.Endpoint = n.ParseMap(this.identifierFixFields); } }, - { "filterPolicy", (a, n) => { a.FilterPolicy = n.ParseMap(this.filterPolicyFixedFields); } }, - { "rawMessageDelivery", (a, n) => { a.RawMessageDelivery = n.GetBooleanValue(); } }, - { "redrivePolicy", (a, n) => { a.RedrivePolicy = n.ParseMap(this.redrivePolicyFixedFields); } }, - { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMap(this.deliveryPolicyFixedFields); } }, - { "displayName", (a, n) => { a.DisplayName = n.GetScalarValue(); } }, - }; + /// + /// Policy for retries to HTTP. The field is the default for HTTP receivers of the SNS Topic which may be overridden by a specific consumer. + /// + public DeliveryPolicy DeliveryPolicy { get; set; } - private FixedFieldMap filterPolicyFixedFields => new() - { - { "attributes", (a, n) => { a.Attributes = new AsyncApiObject(); }} - }; + public override string BindingKey => "sns"; - private FixedFieldMap redrivePolicyFixedFields => new() - { - { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMap(identifierFixFields); } }, - { "maxReceiveCount", (a, n) => { a.MaxReceiveCount = n.GetIntegerValue(); } }, - }; + protected override FixedFieldMap FixedFieldMap => new() + { + { "topic", (a, n) => { a.Topic = n.ParseMap(this.identifierFixFields); } }, + { "consumers", (a, n) => { a.Consumers = n.CreateList(s => s.ParseMap(this.consumerFixedFields)); } }, + { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMap(this.deliveryPolicyFixedFields); } }, + }; - private FixedFieldMap deliveryPolicyFixedFields => new() - { - { "minDelayTarget", (a, n) => { a.MinDelayTarget = n.GetIntegerValue(); } }, - { "maxDelayTarget", (a, n) => { a.MaxDelayTarget = n.GetIntegerValue(); } }, - { "numRetries", (a, n) => { a.NumRetries = n.GetIntegerValue(); } }, - { "numNoDelayRetries", (a, n) => { a.NumNoDelayRetries = n.GetIntegerValue(); } }, - { "numMinDelayRetries", (a, n) => { a.NumMinDelayRetries = n.GetIntegerValue(); } }, - { "numMaxDelayRetries", (a, n) => { a.NumMaxDelayRetries = n.GetIntegerValue(); } }, - { "backOffFunction", (a, n) => { a.BackoffFunction = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "maxReceivesPerSecond", (a, n) => { a.MaxReceivesPerSecond = n.GetIntegerValue(); } }, - }; + private FixedFieldMap identifierFixFields => new() + { + { "url", (a, n) => { a.Url = n.GetScalarValue(); } }, + { "email", (a, n) => { a.Url = n.GetScalarValue(); } }, + { "phone", (a, n) => { a.Url = n.GetScalarValue(); } }, + { "arn", (a, n) => { a.Url = n.GetScalarValue(); } }, + { "name", (a, n) => { a.Url = n.GetScalarValue(); } }, + }; - public override void SerializeProperties(IAsyncApiWriter writer) - { - if (writer is null) + private FixedFieldMap consumerFixedFields => new () { - throw new ArgumentNullException(nameof(writer)); - } + { "protocol", (a, n) => { a.Protocol = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "endpoint", (a, n) => { a.Endpoint = n.ParseMap(this.identifierFixFields); } }, + { "filterPolicy", (a, n) => { a.FilterPolicy = n.ParseMap(this.filterPolicyFixedFields); } }, + { "rawMessageDelivery", (a, n) => { a.RawMessageDelivery = n.GetBooleanValue(); } }, + { "redrivePolicy", (a, n) => { a.RedrivePolicy = n.ParseMap(this.redrivePolicyFixedFields); } }, + { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMap(this.deliveryPolicyFixedFields); } }, + { "displayName", (a, n) => { a.DisplayName = n.GetScalarValue(); } }, + }; - writer.WriteStartObject(); - writer.WriteOptionalObject("topic", this.Topic, (w, t) => t.Serialize(w)); - writer.WriteOptionalCollection("consumers", this.Consumers, (w, c) => c.Serialize(w)); - writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); - writer.WriteEndObject(); + private FixedFieldMap filterPolicyFixedFields => new() + { + { "attributes", (a, n) => { a.Attributes = new AsyncApiObject(); }} + }; + + private FixedFieldMap redrivePolicyFixedFields => new() + { + { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMap(identifierFixFields); } }, + { "maxReceiveCount", (a, n) => { a.MaxReceiveCount = n.GetIntegerValue(); } }, + }; + + private FixedFieldMap deliveryPolicyFixedFields => new() + { + { "minDelayTarget", (a, n) => { a.MinDelayTarget = n.GetIntegerValue(); } }, + { "maxDelayTarget", (a, n) => { a.MaxDelayTarget = n.GetIntegerValue(); } }, + { "numRetries", (a, n) => { a.NumRetries = n.GetIntegerValue(); } }, + { "numNoDelayRetries", (a, n) => { a.NumNoDelayRetries = n.GetIntegerValue(); } }, + { "numMinDelayRetries", (a, n) => { a.NumMinDelayRetries = n.GetIntegerValue(); } }, + { "numMaxDelayRetries", (a, n) => { a.NumMaxDelayRetries = n.GetIntegerValue(); } }, + { "backOffFunction", (a, n) => { a.BackoffFunction = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "maxReceivesPerSecond", (a, n) => { a.MaxReceivesPerSecond = n.GetIntegerValue(); } }, + }; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalObject("topic", this.Topic, (w, t) => t.Serialize(w)); + writer.WriteOptionalCollection("consumers", this.Consumers, (w, c) => c.Serialize(w)); + writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); + writer.WriteEndObject(); + } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 7259e062..fbc19f6f 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -1,76 +1,78 @@ -using System; -using System.Collections.Generic; -using LEGO.AsyncAPI.Attributes; -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; +namespace LEGO.AsyncAPI.Bindings.Sns +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; -namespace LEGO.AsyncAPI.Bindings.Sns; + public class Statement : IAsyncApiElement + { -public class Statement : IAsyncApiElement -{ - public Effect Effect { get; set; } + public Effect Effect { get; set; } - /// - /// The AWS account or resource ARN that this statement applies to. - /// - // public StringOrStringList Principal { get; set; } - public StringOrStringList Principal { get; set; } - - /// - /// The SNS permission being allowed or denied e.g. sns:Publish - /// - public StringOrStringList Action { get; set; } + /// + /// The AWS account or resource ARN that this statement applies to. + /// + // public StringOrStringList Principal { get; set; } + public StringOrStringList Principal { get; set; } - public void Serialize(IAsyncApiWriter writer) - { - if (writer is null) + /// + /// The SNS permission being allowed or denied e.g. sns:Publish + /// + public StringOrStringList Action { get; set; } + + public void Serialize(IAsyncApiWriter writer) { - throw new ArgumentNullException(nameof(writer)); - } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - writer.WriteStartObject(); - writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); - writer.WriteRequiredObject("action", this.Action, (w, t) => t.Serialize(w)); - writer.WriteEndObject(); + writer.WriteStartObject(); + writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); + writer.WriteRequiredObject("action", this.Action, (w, t) => t.Serialize(w)); + writer.WriteEndObject(); + } } -} -public enum Effect -{ - [Display("allow")] - Allow, - [Display("deny")] - Deny, -} + public enum Effect + { + [Display("allow")] + Allow, + [Display("deny")] + Deny, + } -public class StringOrStringList : IAsyncApiElement -{ - public string StringValue { get; set; } + public class StringOrStringList : IAsyncApiElement + { + public string StringValue { get; set; } - public List StringList { get; set; } + public List StringList { get; set; } - public void Serialize(IAsyncApiWriter writer) - { - if (writer is null) + public void Serialize(IAsyncApiWriter writer) { - throw new ArgumentNullException(nameof(writer)); - } + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } - if (this.StringValue != null) - { - writer.WriteValue(this.StringValue); - } - else - { - writer.WriteStartArray(); - foreach (var v in this.StringList) + if (this.StringValue != null) { - writer.WriteValue(v); + writer.WriteValue(this.StringValue); } + else + { + writer.WriteStartArray(); + foreach (var v in this.StringList) + { + writer.WriteValue(v); + } - writer.WriteEndArray(); + writer.WriteEndArray(); + } } } } \ No newline at end of file From 0e1a44587dae51417940f283313de9bd52716d04 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 12 May 2023 09:00:30 +0200 Subject: [PATCH 20/39] ci: pull_request_target --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 447ff2b5..270dbede 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,11 @@ on: paths: - 'src/**' - '!**/*.md' + pull_request_target: + branches: [ main ] + paths: + - 'src/**' + - '!**/*.md' workflow_dispatch: jobs: build: From 2690f4cf950787be01fe9377c81e58ca6b4d937a Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 12 May 2023 09:12:10 +0200 Subject: [PATCH 21/39] ci: add build and test run for forks (#110) --- .github/workflows/ci-fork.yml | 29 +++++++++++++++++++++++++++++ .github/workflows/ci.yml | 5 ----- 2 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/ci-fork.yml diff --git a/.github/workflows/ci-fork.yml b/.github/workflows/ci-fork.yml new file mode 100644 index 00000000..274f2bd0 --- /dev/null +++ b/.github/workflows/ci-fork.yml @@ -0,0 +1,29 @@ +name: Build & Test +on: + pull_request_target: + branches: [ main ] + paths: + - 'src/**' + - '!**/*.md' +jobs: + build: + runs-on: ${{ matrix.os }} + if: ${{ github.event.pull_request.head.repo.fork }} + environment: + name: Fork + strategy: + matrix: + os: [ windows-latest, macos-latest ] + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '6.0.x' + include-prerelease: true + - name: Restore dependencies + run: dotnet restore + - name: Build + run: dotnet build --no-restore + - name: Test + run: dotnet test --no-build --verbosity normal diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 270dbede..447ff2b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,11 +10,6 @@ on: paths: - 'src/**' - '!**/*.md' - pull_request_target: - branches: [ main ] - paths: - - 'src/**' - - '!**/*.md' workflow_dispatch: jobs: build: From b244161201c2f325b4f0105096db6409a9e46ca7 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 12 May 2023 09:32:05 +0200 Subject: [PATCH 22/39] ci: delete new workflow --- .github/workflows/ci-fork.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 .github/workflows/ci-fork.yml diff --git a/.github/workflows/ci-fork.yml b/.github/workflows/ci-fork.yml deleted file mode 100644 index 274f2bd0..00000000 --- a/.github/workflows/ci-fork.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Build & Test -on: - pull_request_target: - branches: [ main ] - paths: - - 'src/**' - - '!**/*.md' -jobs: - build: - runs-on: ${{ matrix.os }} - if: ${{ github.event.pull_request.head.repo.fork }} - environment: - name: Fork - strategy: - matrix: - os: [ windows-latest, macos-latest ] - steps: - - uses: actions/checkout@v2 - - name: Setup .NET - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '6.0.x' - include-prerelease: true - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore - - name: Test - run: dotnet test --no-build --verbosity normal From eedb2fadbbae172a368df404c980563bd042fc6d Mon Sep 17 00:00:00 2001 From: Goker Akce Date: Fri, 12 May 2023 08:43:36 +0100 Subject: [PATCH 23/39] fix: update AsyncApiTextReader class name (#109) Co-authored-by: Goker Akce Co-authored-by: Alex Wichmann --- src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs | 6 +++--- src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs | 4 ++-- src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs index da04716d..ed6c3942 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs @@ -33,7 +33,7 @@ public AsyncApiStreamReader(AsyncApiReaderSettings settings = null) public AsyncApiDocument Read(Stream input, out AsyncApiDiagnostic diagnostic) { var reader = new StreamReader(input); - var result = new AsyncApiTextReaderReader(this.settings).Read(reader, out diagnostic); + var result = new AsyncApiTextReader(this.settings).Read(reader, out diagnostic); if (!this.settings.LeaveStreamOpen) { reader.Dispose(); @@ -65,7 +65,7 @@ public async Task ReadAsync(Stream input) var reader = new StreamReader(bufferedStream); - return await new AsyncApiTextReaderReader(this.settings).ReadAsync(reader); + return await new AsyncApiTextReader(this.settings).ReadAsync(reader); } /// @@ -80,7 +80,7 @@ public T ReadFragment(Stream input, AsyncApiVersion version, out AsyncApiDiag { using (var reader = new StreamReader(input)) { - return new AsyncApiTextReaderReader(this.settings).ReadFragment(reader, version, out diagnostic); + return new AsyncApiTextReader(this.settings).ReadFragment(reader, version, out diagnostic); } } } diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs index c6680620..8d4bd870 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs @@ -30,7 +30,7 @@ public AsyncApiDocument Read(string input, out AsyncApiDiagnostic diagnostic) { using (var reader = new StringReader(input)) { - return new AsyncApiTextReaderReader(this.settings).Read(reader, out diagnostic); + return new AsyncApiTextReader(this.settings).Read(reader, out diagnostic); } } @@ -42,7 +42,7 @@ public T ReadFragment(string input, AsyncApiVersion version, out AsyncApiDiag { using (var reader = new StringReader(input)) { - return new AsyncApiTextReaderReader(this.settings).ReadFragment(reader, version, out diagnostic); + return new AsyncApiTextReader(this.settings).ReadFragment(reader, version, out diagnostic); } } } diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs index 51817948..0eaef44d 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs @@ -14,7 +14,7 @@ namespace LEGO.AsyncAPI.Readers /// /// Service class for converting contents of TextReader into AsyncApiDocument instances /// - public class AsyncApiTextReaderReader : IAsyncApiReader + public class AsyncApiTextReader : IAsyncApiReader { private readonly AsyncApiReaderSettings settings; @@ -22,7 +22,7 @@ public class AsyncApiTextReaderReader : IAsyncApiReader /// - public AsyncApiTextReaderReader(AsyncApiReaderSettings settings = null) + public AsyncApiTextReader(AsyncApiReaderSettings settings = null) { this.settings = settings ?? new AsyncApiReaderSettings(); } From 21caee9a49ea932b4f5640dfb0465dcb7837ae69 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Mon, 15 May 2023 12:26:24 +0100 Subject: [PATCH 24/39] feat: fix identifier fixed fields bug --- src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs index bc04f195..8bb2ccd9 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -38,10 +38,10 @@ public class SnsOperationBinding : OperationBinding private FixedFieldMap identifierFixFields => new() { { "url", (a, n) => { a.Url = n.GetScalarValue(); } }, - { "email", (a, n) => { a.Url = n.GetScalarValue(); } }, - { "phone", (a, n) => { a.Url = n.GetScalarValue(); } }, - { "arn", (a, n) => { a.Url = n.GetScalarValue(); } }, - { "name", (a, n) => { a.Url = n.GetScalarValue(); } }, + { "email", (a, n) => { a.Email = n.GetScalarValue(); } }, + { "phone", (a, n) => { a.Phone = n.GetScalarValue(); } }, + { "arn", (a, n) => { a.Arn = n.GetScalarValue(); } }, + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, }; private FixedFieldMap consumerFixedFields => new () From e120788b7f0f7e20f75b2a8c91febbf51a985428 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Tue, 16 May 2023 09:12:08 +0100 Subject: [PATCH 25/39] feat: correct type property to be required --- src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs b/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs index b8323d45..9b37365b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs @@ -25,7 +25,7 @@ public void Serialize(IAsyncApiWriter writer) } writer.WriteStartObject(); - writer.WriteOptionalProperty("type", this.Type.GetDisplayName()); + writer.WriteRequiredProperty("type", this.Type.GetDisplayName()); writer.WriteOptionalProperty("contentBasedDeduplication", this.ContentBasedDeduplication); writer.WriteEndObject(); } From 783a1d220a7f3d97ff24481612d6ebef9ff8e577 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Tue, 16 May 2023 09:14:58 +0100 Subject: [PATCH 26/39] feat: rename ordering to match spec --- .../Sns/{OrderingConfiguration.cs => Ordering.cs} | 6 +++--- src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs | 10 +++++----- .../Bindings/Sns/SnsBindings_Should.cs | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/LEGO.AsyncAPI.Bindings/Sns/{OrderingConfiguration.cs => Ordering.cs} (88%) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs similarity index 88% rename from src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs rename to src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs index 9b37365b..b0352f01 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/OrderingConfiguration.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs @@ -5,12 +5,12 @@ namespace LEGO.AsyncAPI.Bindings.Sns using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - public class OrderingConfiguration : IAsyncApiElement + public class Ordering : IAsyncApiElement { /// /// What type of SNS Topic is this? /// - public Ordering Type { get; set; } + public OrderingType Type { get; set; } /// /// True to turn on de-duplication of messages for a channel. @@ -31,7 +31,7 @@ public void Serialize(IAsyncApiWriter writer) } } - public enum Ordering + public enum OrderingType { [Display("standard")] Standard, diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 579a001a..36c61022 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -23,7 +23,7 @@ public class SnsChannelBinding : ChannelBinding /// /// By default, we assume an unordered SNS topic. This field allows configuration of a FIFO SNS Topic. /// - public OrderingConfiguration Type { get; set; } + public Ordering Ordering { get; set; } /// /// The security policy for the SNS Topic. @@ -40,14 +40,14 @@ public class SnsChannelBinding : ChannelBinding protected override FixedFieldMap FixedFieldMap => new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, - { "type", (a, n) => { a.Type = n.ParseMap(this.orderingFixedFields); } }, + { "type", (a, n) => { a.Ordering = n.ParseMap(this.orderingFixedFields); } }, { "policy", (a, n) => { a.Policy = n.ParseMap(this.policyFixedFields); } }, { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, }; - private FixedFieldMap orderingFixedFields = new() + private FixedFieldMap orderingFixedFields = new() { - { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, { "contentBasedDeduplication", (a, n) => { a.ContentBasedDeduplication = n.GetBooleanValue(); } }, }; @@ -88,7 +88,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("name", this.Name); - writer.WriteOptionalObject("type", this.Type, (w, t) => t.Serialize(w)); + writer.WriteOptionalObject("ordering", this.Ordering, (w, t) => t.Serialize(w)); writer.WriteOptionalObject("policy", this.Policy, (w, t) => t.Serialize(w)); writer.WriteOptionalMap("tags", this.Tags, (w, t) => w.WriteValue(t)); writer.WriteEndObject(); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 6693732f..eaabc738 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -22,7 +22,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() @"bindings: sns: name: myTopic - type: + ordering: type: FIFO contentBasedDeduplication: true policy: @@ -45,9 +45,9 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() channel.Bindings.Add(new SnsChannelBinding() { Name = "myTopic", - Type = new OrderingConfiguration() + Ordering = new Ordering() { - Type = Ordering.Fifo, + Type = OrderingType.Fifo, ContentBasedDeduplication = true, }, Policy = new Policy() From e01e26cd8583049db5478c2dc6cdd9e78a182bdd Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Thu, 18 May 2023 08:08:39 +0200 Subject: [PATCH 27/39] chore: add bindings to labeler --- .github/labeler.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index 4561e7f9..13e2f191 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -4,6 +4,8 @@ ci/cd: - .github/**/* asyncapi.readers: - src/LEGO.AsyncAPI.Readers/**/* +asyncapi.bindings: +- src/LEGO.AsyncAPI.Bindings/**/* asyncapi.models: - src/LEGO.AsyncAPI/**/* asyncapi.tests: From 30b87fb10cdb55332672bfc0dcadc2f5a3dbad79 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Thu, 18 May 2023 08:56:20 +0200 Subject: [PATCH 28/39] test: add 'any' example --- test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs | 3 ++- .../Bindings/CustomBinding_Should.cs | 12 +++++++++++- .../Bindings/Pulsar/PulsarBindings_Should.cs | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index fea62870..c24bd9c5 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -108,7 +108,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() @"asyncapi: '2.6.0' info: title: Streetlights Kafka API - version: 1.0.0 + version: '1.0.0' description: The Smartylighting Streetlights API allows you to remotely manage the city lights. license: name: Apache 2.0 @@ -2220,6 +2220,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); } + [Test] public void Serializev2_WithBindings_Serializes() { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs index 94081564..c0d36c10 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs @@ -19,10 +19,13 @@ public class MyBinding : ChannelBinding public override string BindingKey => "my"; + public IAsyncApiAny Any { get; set; } + protected override FixedFieldMap FixedFieldMap => new FixedFieldMap() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "custom", (a, n) => { a.Custom = n.GetScalarValue(); } }, + { "any", (a, n) => { a.Any = n.CreateAny(); } }, }; public override void SerializeProperties(IAsyncApiWriter writer) @@ -30,6 +33,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("custom", this.Custom); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteOptionalObject("any", this.Any, (w, p) => w.WriteAny(p)); writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } @@ -45,13 +49,19 @@ public void CustomBinding_SerializesDeserializes() @"bindings: my: custom: someValue - bindingVersion: 0.1.0 + bindingVersion: '0.1.0' + any: + anyKeyName: anyValue x-myextension: someValue"; var channel = new AsyncApiChannel(); channel.Bindings.Add(new MyBinding { Custom = "someValue", + Any = new AsyncApiObject() + { + { "anyKeyName", new AsyncApiString("anyValue") }, + }, BindingVersion = "0.1.0", Extensions = new Dictionary() { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs index 15577763..899d55ca 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs @@ -30,7 +30,7 @@ public void PulsarChannelBinding_WithFilledObject_SerializesAndDeserializes() size: 1000 ttl: 360 deduplication: true - bindingVersion: 0.1.0"; + bindingVersion: '0.1.0'"; var channel = new AsyncApiChannel(); channel.Bindings.Add(new PulsarChannelBinding From 227751854e8e27e8a7b10fb6250de77a003354ba Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Thu, 18 May 2023 11:34:05 +0100 Subject: [PATCH 29/39] feat: add any filter policy handling --- .../BindingsCollection.cs | 1 + .../Sns/DeliveryPolicy.cs | 6 ++--- .../Sns/SnsOperationBinding.cs | 4 ++-- .../Bindings/Sns/SnsBindings_Should.cs | 23 +++++++++++++------ 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs index 112da0c8..cccf53fc 100644 --- a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -73,6 +73,7 @@ public static TCollection Add( public static IEnumerable> Sns => new List> { new SnsChannelBinding(), + new SnsOperationBinding(), }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs index f9e4e08b..5fff723b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs @@ -40,7 +40,7 @@ public class DeliveryPolicy : IAsyncApiElement /// /// The algorithm for backoff between retries. /// - public BackOffFunction BackoffFunction { get; set; } + public BackoffFunction BackoffFunction { get; set; } /// /// The maximum number of deliveries per second, per subscription. @@ -61,13 +61,13 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteOptionalProperty("numNoDelayRetries", this.NumNoDelayRetries); writer.WriteOptionalProperty("numMinDelayRetries", this.NumMinDelayRetries); writer.WriteOptionalProperty("numMaxDelayRetries", this.NumMaxDelayRetries); - writer.WriteOptionalProperty("backOffFunction", this.BackoffFunction.GetDisplayName()); + writer.WriteOptionalProperty("backoffFunction", this.BackoffFunction.GetDisplayName()); writer.WriteOptionalProperty("maxReceivesPerSecond", this.MaxReceivesPerSecond); writer.WriteEndObject(); } } - public enum BackOffFunction + public enum BackoffFunction { [Display("arithmetic")] Arithmetic, [Display("exponential")] Exponential, diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs index 8bb2ccd9..78917ee4 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -57,7 +57,7 @@ public class SnsOperationBinding : OperationBinding private FixedFieldMap filterPolicyFixedFields => new() { - { "attributes", (a, n) => { a.Attributes = new AsyncApiObject(); }} + { "attributes", (a, n) => { a.Attributes = n.CreateAny(); } }, }; private FixedFieldMap redrivePolicyFixedFields => new() @@ -74,7 +74,7 @@ public class SnsOperationBinding : OperationBinding { "numNoDelayRetries", (a, n) => { a.NumNoDelayRetries = n.GetIntegerValue(); } }, { "numMinDelayRetries", (a, n) => { a.NumMinDelayRetries = n.GetIntegerValue(); } }, { "numMaxDelayRetries", (a, n) => { a.NumMaxDelayRetries = n.GetIntegerValue(); } }, - { "backOffFunction", (a, n) => { a.BackoffFunction = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "backoffFunction", (a, n) => { a.BackoffFunction = n.GetScalarValue().GetEnumFromDisplayName(); } }, { "maxReceivesPerSecond", (a, n) => { a.MaxReceivesPerSecond = n.GetIntegerValue(); } }, }; diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index eaabc738..33e17b7e 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -127,7 +127,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() name: someQueue filterPolicy: attributes: - store: + store: - asyncapi_corp contact: dec.kolakowski event: @@ -151,7 +151,16 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() numMinDelayRetries: 3 numMaxDelayRetries: 5 backoffFunction: linear - maxReceivesPerSecond: 2"; + maxReceivesPerSecond: 2 + deliveryPolicy: + minDelayTarget: 10 + maxDelayTarget: 100 + numRetries: 5 + numNoDelayRetries: 2 + numMinDelayRetries: 3 + numMaxDelayRetries: 5 + backoffFunction: geometric + maxReceivesPerSecond: 10"; var operation = new AsyncApiOperation(); operation.Bindings.Add(new SnsOperationBinding() @@ -200,7 +209,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() }, }, }, - RawMessageDelivery = true, + RawMessageDelivery = false, RedrivePolicy = new RedrivePolicy() { DeadLetterQueue = new Identifier() @@ -217,7 +226,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() NumNoDelayRetries = 2, NumMinDelayRetries = 3, NumMaxDelayRetries = 5, - BackoffFunction = BackOffFunction.Linear, + BackoffFunction = BackoffFunction.Linear, MaxReceivesPerSecond = 2, }, }, @@ -230,8 +239,8 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() NumNoDelayRetries = 2, NumMinDelayRetries = 3, NumMaxDelayRetries = 5, - BackoffFunction = BackOffFunction.Linear, - MaxReceivesPerSecond = 2, + BackoffFunction = BackoffFunction.Geometric, + MaxReceivesPerSecond = 10, }, }); @@ -243,7 +252,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings.Add(BindingsCollection.Sns); - var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert From ce6d084994074e72acdd897f84c28bcba2c419bd Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 19 May 2023 09:54:27 +0100 Subject: [PATCH 30/39] feat: correct policy effect casing --- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 4 ++-- test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index fbc19f6f..201c663b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -40,9 +40,9 @@ public void Serialize(IAsyncApiWriter writer) public enum Effect { - [Display("allow")] + [Display("Allow")] Allow, - [Display("deny")] + [Display("Deny")] Deny, } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 33e17b7e..cf34ba59 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -27,12 +27,12 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() contentBasedDeduplication: true policy: statements: - - effect: deny + - effect: Deny principal: arn:aws:iam::123456789012:user/alex.wichmann action: - sns:Publish - sns:Delete - - effect: allow + - effect: Allow principal: - arn:aws:iam::123456789012:user/alex.wichmann - arn:aws:iam::123456789012:user/dec.kolakowski From 55f22bdef8ab33931551c4e54256284c41108b7c Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 19 May 2023 10:42:01 +0100 Subject: [PATCH 31/39] feat: use write any --- src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs index 0c6aa0fe..db4e0415 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs @@ -19,7 +19,7 @@ public void Serialize(IAsyncApiWriter writer) } writer.WriteStartObject(); - writer.WriteRequiredObject("attributes", this.Attributes, (w, a) => a.Write(w)); + writer.WriteRequiredObject("attributes", this.Attributes, (w, a) => w.WriteAny(a)); writer.WriteEndObject(); } } From b589e4fd57b601191ddac7c089e6fe09297aa800 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Wed, 3 May 2023 08:44:17 +0200 Subject: [PATCH 32/39] feat!: separate bindings and allow for custom bindings. (#107) BREAKING CHANGE: Bindings have been moved to a separate project --- .github/labeler.yml | 2 + .github/workflows/release-internal.yml | 40 +- .github/workflows/release-package.yml | 2 +- AsyncAPI.sln | 6 + src/LEGO.AsyncAPI.Bindings/Binding.cs | 14 + src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs | 49 + .../BindingsCollection.cs | 71 ++ .../ChannelBinding{T}.cs | 16 + .../Http/HttpMessageBinding.cs | 50 + .../Http/HttpOperationBinding.cs | 60 +- .../Kafka/KafkaChannelBinding.cs | 57 +- .../Kafka/KafkaMessageBinding.cs | 57 +- .../Kafka/KafkaOperationBinding.cs | 54 + .../Kafka/KafkaServerBinding.cs | 45 +- .../Kafka/TopicConfigurationObject.cs | 10 +- .../LEGO.AsyncAPI.Bindings.csproj | 43 + .../MessageBinding{T}.cs | 16 + .../OperationBinding{T}.cs | 16 + .../Pulsar/Persistence.cs | 0 .../Pulsar/PulsarChannelBinding.cs | 62 +- .../Pulsar/PulsarServerBinding.cs | 42 + .../Pulsar/RetentionDefinition.cs | 0 .../ServerBinding{T}.cs | 16 + .../WebSockets/WebSocketsChannelBinding.cs | 48 +- src/LEGO.AsyncAPI.Bindings/stylecop.json | 15 + .../AsyncApiReaderSettings.cs | 6 + .../AsyncApiStreamReader.cs | 6 +- .../AsyncApiStringReader.cs | 4 +- .../AsyncApiTextReader.cs | 4 +- .../AsyncApiYamlDocumentReader.cs | 8 + .../BindingDeserializer.cs | 31 + .../AsyncApiHttpBindingsDeserializer.cs | 25 - .../AsyncApiKafkaBindingsDeserializer.cs | 59 - .../AsyncApiPulsarBindingsDeserializer.cs | 43 - .../AsyncApiWebSocketsBindingsDeserializer.cs | 18 - .../Interface/IBindingParser{T}.cs | 12 + .../LEGO.AsyncAPI.Readers.csproj | 4 + .../ParseNodes/FixedFieldMap.cs | 4 +- .../ParseNodes/MapNode.cs | 50 +- .../ParseNodes/ParseNode.cs | 2 +- .../ParseNodes/PatternFieldMap.cs | 2 +- .../ParseNodes/PropertyNode.cs | 7 +- .../ParseNodes/ValueNode.cs | 2 +- src/LEGO.AsyncAPI.Readers/ParsingContext.cs | 8 + .../V2/AsyncApiBindingDeserializer.cs | 72 -- .../V2/AsyncApiChannelBindingDeserializer.cs | 34 +- .../V2/AsyncApiComponentsDeserializer.cs | 10 +- .../V2/AsyncApiDeserializer.cs | 8 +- .../V2/AsyncApiMessageBindingDeserializer.cs | 24 +- .../V2/AsyncApiMessageDeserializer.cs | 8 +- .../V2/AsyncApiMessageTraitDeserializer.cs | 2 +- .../AsyncApiOperationBindingDeserializer.cs | 24 +- .../V2/AsyncApiParameterDeserializer.cs | 2 +- .../V2/AsyncApiSchemaDeserializer.cs | 10 +- .../V2/AsyncApiServerBindingDeserializer.cs | 30 +- .../V2/AsyncApiV2VersionService.cs | 2 +- .../V2/ExtensionHelpers.cs | 43 + src/LEGO.AsyncAPI.Readers/YamlHelper.cs | 1 - .../Expressions/MethodExpression.cs | 7 - .../AsyncApiExtensibleExtensions.cs | 4 +- src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs | 41 + src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs | 7 +- .../Models/AsyncApiComponents.cs | 16 +- src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs | 10 +- .../Models/AsyncApiWriterExtensions.cs | 2 +- .../Models/Bindings/BindingType.cs | 21 - .../Bindings/Http/HttpMessageBinding.cs | 75 -- .../Bindings/Kafka/KafkaOperationBinding.cs | 73 -- .../Bindings/Pulsar/PulsarServerBinding.cs | 66 - .../Models/Interfaces/IBinding.cs | 8 +- src/LEGO.AsyncAPI/Models/ReferenceType.cs | 16 +- .../Services/AsyncApiReferenceResolver.cs | 25 +- .../Services/AsyncApiVisitorBase.cs | 10 +- src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs | 32 +- src/LEGO.AsyncAPI/Services/CurrentKeys.cs | 2 + .../Validation/Rules/AsyncApiContactRules.cs | 1 - .../Rules/AsyncApiCorrelationIdRules.cs | 1 - .../Validation/Rules/AsyncApiDocumentRules.cs | 1 - .../Rules/AsyncApiExtensionRules.cs | 1 - .../Validation/Rules/AsyncApiLicenseRules.cs | 1 - .../Rules/AsyncApiOAuthFlowRules.cs | 2 +- .../Validation/Rules/AsyncApiTagRules.cs | 1 - .../Writers/AsyncApiWriterException.cs | 2 +- .../Writers/AsyncApiWriterExtensions.cs | 6 + .../Writers/AsyncApiWriterSettings.cs | 2 +- src/LEGO.AsyncAPI/Writers/StringExtensions.cs | 2 +- .../AsyncApiDocumentBuilder.cs | 22 +- .../AsyncApiDocumentV2Tests.cs | 1111 ++--------------- .../AsyncApiLicenseTests.cs | 26 +- .../AsyncApiReaderTests.cs | 2 + .../Bindings/CustomBinding_Should.cs | 124 ++ .../Bindings/Http/HttpBindings_Should.cs | 23 +- .../Bindings/Kafka/KafkaBindings_Should.cs | 35 +- .../Bindings/Pulsar/PulsarBindings_Should.cs | 53 +- .../WebSockets/WebSocketBindings_Should.cs | 13 +- .../LEGO.AsyncAPI.Tests.csproj | 5 + .../Models/AsyncApiChannel_Should.cs | 12 +- .../Models/AsyncApiContact_Should.cs | 8 +- .../AsyncApiExternalDocumentation_Should.cs | 8 +- .../Models/AsyncApiInfo_Should.cs | 8 +- .../Models/AsyncApiLicense_Should.cs | 8 +- .../Models/AsyncApiMessageExample_Should.cs | 8 +- .../Models/AsyncApiMessage_Should.cs | 20 +- .../Models/AsyncApiOAuthFlow_Should.cs | 8 +- .../Models/AsyncApiOperation_Should.cs | 30 +- .../Models/AsyncApiSchema_Should.cs | 18 +- .../AsyncApiSecurityRequirement_Should.cs | 18 +- .../Models/AsyncApiServer_Should.cs | 16 +- test/LEGO.AsyncAPI.Tests/StringExtensions.cs | 4 +- .../Validation/ValidationRulesetTests.cs | 6 +- test/LEGO.AsyncAPI.Tests/stylecop.json | 15 + 111 files changed, 1341 insertions(+), 2051 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/Binding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Http/HttpOperationBinding.cs (55%) rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/KafkaChannelBinding.cs (52%) rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/KafkaMessageBinding.cs (57%) create mode 100644 src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/KafkaServerBinding.cs (53%) rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/TopicConfigurationObject.cs (93%) create mode 100644 src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj create mode 100644 src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Pulsar/Persistence.cs (100%) rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Pulsar/PulsarChannelBinding.cs (59%) create mode 100644 src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Pulsar/RetentionDefinition.cs (100%) create mode 100644 src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/WebSockets/WebSocketsChannelBinding.cs (57%) create mode 100644 src/LEGO.AsyncAPI.Bindings/stylecop.json create mode 100644 src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs create mode 100644 src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs create mode 100644 src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs create mode 100644 src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs create mode 100644 test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs create mode 100644 test/LEGO.AsyncAPI.Tests/stylecop.json diff --git a/.github/labeler.yml b/.github/labeler.yml index 4561e7f9..13e2f191 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -4,6 +4,8 @@ ci/cd: - .github/**/* asyncapi.readers: - src/LEGO.AsyncAPI.Readers/**/* +asyncapi.bindings: +- src/LEGO.AsyncAPI.Bindings/**/* asyncapi.models: - src/LEGO.AsyncAPI/**/* asyncapi.tests: diff --git a/.github/workflows/release-internal.yml b/.github/workflows/release-internal.yml index 8b54315b..66df465a 100644 --- a/.github/workflows/release-internal.yml +++ b/.github/workflows/release-internal.yml @@ -5,31 +5,51 @@ on: paths: - 'src/LEGO.AsyncAPI/**' - 'src/LEGO.AsyncAPI.Readers/**' - - 'src/LEGO.AsyncAPI.Writers/**' - - ".github/workflows/release-package.yml" + - 'src/LEGO.AsyncAPI.Bindings/**' + - ".github/workflows/release-internal.yml" - '!**/*.md' workflow_dispatch: jobs: - release: + check: + runs-on: ubuntu-latest + name: Check release + environment: AsyncAPI + steps: + - name: Checkout repository + uses: actions/checkout@v1 + + - name: Semantic Release + uses: cycjimmy/semantic-release-action@v3 + with: + dry_run: true + ci: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + outputs: + trigger_release: ${{ steps.semantic.outputs.new_release_published }} + version: ${{ steps.semantic.outputs.new_release_published == 'true' && steps.semantic.outputs.new_release_version }} + + pre-release: runs-on: ubuntu-latest name: Publish NuGet packages + needs: check + environment: AsyncAPI strategy: matrix: - package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers"] + package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers", "LEGO.AsyncAPI.Bindings" ] steps: - name: Checkout repository uses: actions/checkout@v1 - name: Setup .NET Core @ Latest + if: needs.check.outputs.trigger_release == 'true' uses: actions/setup-dotnet@v1 - with: - source-url: https://nuget.pkg.github.com/LEGO/index.json - env: - NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Build ${{ matrix.package-name }} project and pack NuGet package - run: dotnet pack src/${{ matrix.package-name }}/${{ matrix.package-name }}.csproj -c Release -o out-${{ matrix.package-name }} -p:PackageVersion=0.2.$GITHUB_RUN_NUMBER.0-prerelease + if: needs.check.outputs.trigger_release == 'true' + run: dotnet pack src/${{ matrix.package-name }}/${{ matrix.package-name }}.csproj -c Release -o out-${{ matrix.package-name }} -p:PackageVersion=${{ needs.check.outputs.version }}-beta - name: Push generated package to GitHub Packages registry - run: dotnet nuget push out-${{ matrix.package-name }}/*.nupkg --skip-duplicate -n --api-key ${{secrets.GITHUB_TOKEN}} + if: needs.check.outputs.trigger_release == 'true' + run: dotnet nuget push out-${{ matrix.package-name }}/*.nupkg -s https://api.nuget.org/v3/index.json --skip-duplicate -n --api-key ${{secrets.NUGET}} diff --git a/.github/workflows/release-package.yml b/.github/workflows/release-package.yml index 0f064176..9e52a9fe 100644 --- a/.github/workflows/release-package.yml +++ b/.github/workflows/release-package.yml @@ -56,7 +56,7 @@ jobs: environment: AsyncAPI strategy: matrix: - package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers" ] + package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers", "LEGO.AsyncAPI.Bindings" ] steps: - name: Checkout repository uses: actions/checkout@v1 diff --git a/AsyncAPI.sln b/AsyncAPI.sln index bf944501..f972fa3d 100644 --- a/AsyncAPI.sln +++ b/AsyncAPI.sln @@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LEGO.AsyncAPI.Bindings", "src\LEGO.AsyncAPI.Bindings\LEGO.AsyncAPI.Bindings.csproj", "{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,6 +34,10 @@ Global {7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Release|Any CPU.Build.0 = Release|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/LEGO.AsyncAPI.Bindings/Binding.cs b/src/LEGO.AsyncAPI.Bindings/Binding.cs new file mode 100644 index 00000000..e7c57400 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Binding.cs @@ -0,0 +1,14 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.Interface; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class Binding : AsyncApiBinding, IBindingParser + where T : IBinding, new() + { + public abstract T LoadBinding(PropertyNode node); + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs b/src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs new file mode 100644 index 00000000..424afc9e --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs @@ -0,0 +1,49 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public static class BindingHelpers + { + public static T ParseMap(this ParseNode node, FixedFieldMap fixedFieldMap) + where T : new() + { + var mapNode = node.CheckMapNode(node.Context.GetLocation()); + if (mapNode == null) + { + return default(T); + } + + var instance = new T(); + + foreach (var propertyNode in mapNode) + { + propertyNode.ParseField(instance, fixedFieldMap, null); + } + + return instance; + } + + public static T ParseMapWithExtensions(this ParseNode node, FixedFieldMap fixedFieldMap) + where T : IAsyncApiExtensible, new() + { + var mapNode = node.CheckMapNode(node.Context.GetLocation()); + if (mapNode == null) + { + return default(T); + } + + var instance = new T(); + + foreach (var propertyNode in mapNode) + { + propertyNode.ParseField(instance, fixedFieldMap, ExtensionHelpers.GetExtensionsFieldMap()); + } + + return instance; + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs new file mode 100644 index 00000000..0a34ec39 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -0,0 +1,71 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Bindings.Http; + using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Bindings.Pulsar; + using LEGO.AsyncAPI.Bindings.WebSockets; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.Interface; + + public static class BindingsCollection + { + public static TCollection Add( + this TCollection destination, + IEnumerable source) + where TCollection : ICollection + { + ArgumentNullException.ThrowIfNull(destination); + ArgumentNullException.ThrowIfNull(source); + + if (destination is List list) + { + list.AddRange(source); + return destination; + } + + foreach (var item in source) + { + destination.Add(item); + } + + return destination; + } + + public static IEnumerable> All => new List> + { + Pulsar, + Kafka, + Http, + }; + + public static IEnumerable> Http => new List> + { + new HttpOperationBinding(), + new HttpMessageBinding() + }; + + public static IEnumerable> Websockets => new List> + { + new WebSocketsChannelBinding(), + }; + + public static IEnumerable> Kafka => new List> + { + new KafkaServerBinding(), + new KafkaChannelBinding(), + new KafkaOperationBinding(), + new KafkaMessageBinding(), + }; + + public static IEnumerable> Pulsar => new List> + { + // Pulsar + new PulsarServerBinding(), + new PulsarChannelBinding(), + }; + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs new file mode 100644 index 00000000..d792809c --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class ChannelBinding : Binding, IChannelBinding + where T : IChannelBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("ChannelBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs new file mode 100644 index 00000000..15a654dd --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs @@ -0,0 +1,50 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Http +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for http messaging channels. + /// + public class HttpMessageBinding : MessageBinding + { + + /// + /// A Schema object containing the definitions for HTTP-specific headers. This schema MUST be of type object and have a properties key. + /// + public AsyncApiSchema Headers { get; set; } + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + + writer.WriteOptionalObject(AsyncApiConstants.Headers, this.Headers, (w, h) => h.SerializeV2(w)); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + + writer.WriteEndObject(); + } + + public override string BindingKey => "http"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, + }; + + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs similarity index 55% rename from src/LEGO.AsyncAPI/Models/Bindings/Http/HttpOperationBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs index 27a91798..b5c92c38 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs @@ -1,21 +1,31 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Http +namespace LEGO.AsyncAPI.Bindings.Http { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for http operations. /// - public class HttpOperationBinding : IOperationBinding + public class HttpOperationBinding : OperationBinding { + public enum HttpOperationType + { + [Display("request")] + Request, + + [Display("response")] + Response, + } /// /// REQUIRED. Type of operation. Its value MUST be either request or response. /// - public string Type { get; set; } + public HttpOperationType? Type { get; set; } /// /// When type is request, this is the HTTP method, otherwise it MUST be ignored. Its value MUST be one of GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, CONNECT, and TRACE. @@ -27,15 +37,10 @@ public class HttpOperationBinding : IOperationBinding /// public AsyncApiSchema Query { get; set; } - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - /// /// Serialize to AsyncAPI V2 document without using reference. /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -44,37 +49,22 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteStartObject(); - writer.WriteRequiredProperty(AsyncApiConstants.Type, this.Type); + writer.WriteRequiredProperty(AsyncApiConstants.Type, this.Type.GetDisplayName()); writer.WriteOptionalProperty(AsyncApiConstants.Method, this.Method); writer.WriteOptionalObject(AsyncApiConstants.Query, this.Query, (w, h) => h.SerializeV2(w)); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - public void SerializeV2(IAsyncApiWriter writer) + protected override FixedFieldMap FixedFieldMap => new() { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, + { "query", (a, n) => { a.Query = JsonSchemaDeserializer.LoadSchema(n); } }, + }; - BindingType IBinding.Type => BindingType.Http; + public override string BindingKey => "http"; } } diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs similarity index 52% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaChannelBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs index 998c47b3..78b745e0 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs @@ -1,16 +1,17 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Kafka +namespace LEGO.AsyncAPI.Bindings.Kafka { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Bindings.Kafka; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for Kafka channel settings. /// - public class KafkaChannelBinding : IChannelBinding + public class KafkaChannelBinding : ChannelBinding { /// /// Kafka topic name if different from channel name. @@ -32,23 +33,30 @@ public class KafkaChannelBinding : IChannelBinding /// public TopicConfigurationObject TopicConfiguration { get; set; } - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Kafka; - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } + public override string BindingKey => "kafka"; - public IDictionary Extensions { get; set; } = new Dictionary(); + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, + { "partitions", (a, n) => { a.Partitions = n.GetIntegerValue(); } }, + { "topicConfiguration", (a, n) => { a.TopicConfiguration = n.ParseMap(kafkaChannelTopicConfigurationObjectFixedFields); } }, + { "replicas", (a, n) => { a.Replicas = n.GetIntegerValue(); } }, + }; + + private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new() + { + { "cleanup.policy", (a, n) => { a.CleanupPolicy = n.CreateSimpleList(s => s.GetScalarValue()); } }, + { "retention.ms", (a, n) => { a.RetentionMiliseconds = n.GetIntegerValue(); } }, + { "retention.bytes", (a, n) => { a.RetentionBytes = n.GetIntegerValue(); } }, + { "delete.retention.ms", (a, n) => { a.DeleteRetentionMiliseconds = n.GetIntegerValue(); } }, + { "max.message.bytes", (a, n) => { a.MaxMessageBytes = n.GetIntegerValue(); } }, + }; /// /// Serialize to AsyncAPI V2 document without using reference. /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -61,24 +69,9 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.Replicas, this.Replicas); writer.WriteOptionalObject(AsyncApiConstants.TopicConfiguration, this.TopicConfiguration, (w, t) => t.Serialize(w)); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } } } diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs similarity index 57% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaMessageBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs index 1fd4bb90..31123de1 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaMessageBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs @@ -1,16 +1,17 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Kafka +namespace LEGO.AsyncAPI.Bindings.Kafka { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for kafka messages. /// - public class KafkaMessageBinding : IMessageBinding + public class KafkaMessageBinding : MessageBinding { /// /// The message key. NOTE: You can also use the reference object way. @@ -35,22 +36,8 @@ public class KafkaMessageBinding : IMessageBinding /// /// The version of this binding. If omitted, "latest" MUST be assumed. /// - public string BindingVersion { get; set; } - - /// - /// Indicates if object is populated with data or is just a reference to the data. - /// - public bool UnresolvedReference { get; set; } - - /// - /// Reference object. - /// - public AsyncApiReference Reference { get; set; } - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -64,6 +51,7 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.SchemaIdPayloadEncoding, this.SchemaIdPayloadEncoding); writer.WriteOptionalProperty(AsyncApiConstants.SchemaLookupStrategy, this.SchemaLookupStrategy); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } @@ -73,28 +61,17 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) /// /// The writer. /// writer - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - this.SerializeV2WithoutReference(writer); - } - /// - /// Gets or sets this object MAY be extended with Specification Extensions. - /// To protect the API from leaking the underlying JSON library, the extension data extraction is handled by a customer resolver. - /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public override string BindingKey => "kafka"; - public BindingType Type => BindingType.Kafka; + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "key", (a, n) => { a.Key = JsonSchemaDeserializer.LoadSchema(n); } }, + { "schemaIdLocation", (a, n) => { a.SchemaIdLocation = n.GetScalarValue(); } }, + { "schemaIdPayloadEncoding", (a, n) => { a.SchemaIdPayloadEncoding = n.GetScalarValue(); } }, + { "schemaLookupStrategy", (a, n) => { a.SchemaLookupStrategy = n.GetScalarValue(); } }, + }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs new file mode 100644 index 00000000..db80e0ce --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs @@ -0,0 +1,54 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Kafka +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for Kafka operations. + /// + public class KafkaOperationBinding : OperationBinding + { + /// + /// Id of the consumer group. + /// + public AsyncApiSchema GroupId { get; set; } + + /// + /// Id of the consumer inside a consumer group. + /// + public AsyncApiSchema ClientId { get; set; } + + public override string BindingKey => "kafka"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "groupId", (a, n) => { a.GroupId = JsonSchemaDeserializer.LoadSchema(n); } }, + { "clientId", (a, n) => { a.ClientId = JsonSchemaDeserializer.LoadSchema(n); } }, + }; + + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalObject(AsyncApiConstants.GroupId, this.GroupId, (w, h) => h.SerializeV2(w)); + writer.WriteOptionalObject(AsyncApiConstants.ClientId, this.ClientId, (w, h) => h.SerializeV2(w)); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs similarity index 53% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaServerBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs index bd9d6b19..1d40a798 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaServerBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs @@ -1,16 +1,16 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Kafka +namespace LEGO.AsyncAPI.Bindings.Kafka { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for Kafka server settings. /// - public class KafkaServerBinding : IServerBinding + public class KafkaServerBinding : ServerBinding { /// /// API URL for the Schema Registry used when producing Kafka messages (if a Schema Registry was used) @@ -22,23 +22,20 @@ public class KafkaServerBinding : IServerBinding /// public string SchemaRegistryVendor { get; set; } - /// - /// The version of this binding. - /// - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Kafka; - public bool UnresolvedReference { get; set; } + public override string BindingKey => "kafka"; - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "schemaRegistryUrl", (a, n) => { a.SchemaRegistryUrl = n.GetScalarValue(); } }, + { "schemaRegistryVendor", (a, n) => { a.SchemaRegistryVendor = n.GetScalarValue(); } }, + }; /// /// Serialize to AsyncAPI V2 document without using reference. /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -49,24 +46,8 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.SchemaRegistryUrl, this.SchemaRegistryUrl); writer.WriteOptionalProperty(AsyncApiConstants.SchemaRegistryVendor, this.SchemaRegistryVendor); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } } } diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/TopicConfigurationObject.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs similarity index 93% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/TopicConfigurationObject.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs index 5a1ccffe..da6233c3 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/TopicConfigurationObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs @@ -1,12 +1,12 @@ // Copyright (c) The LEGO Group. All rights reserved. -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; -using System; -using System.Collections.Generic; - namespace LEGO.AsyncAPI.Models.Bindings.Kafka { + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + public class TopicConfigurationObject : IAsyncApiElement { /// diff --git a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj new file mode 100644 index 00000000..e05aacab --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj @@ -0,0 +1,43 @@ + + + + net6.0 + disable + The LEGO Group + https://github.com/LEGO/AsyncAPI.NET + README.md + AsyncAPI.NET Bindings + asyncapi .net openapi documentation + AsyncAPI.NET.Bindings + LEGO.AsyncAPI.Bindings + LEGO.AsyncAPI.Bindings + https://github.com/LEGO/AsyncAPI.NET + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + True + \ + + + + + + + + diff --git a/src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs new file mode 100644 index 00000000..3e3956f9 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class MessageBinding : Binding, IMessageBinding + where T : IMessageBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("MessageBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs new file mode 100644 index 00000000..0e4216ed --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class OperationBinding : Binding , IOperationBinding + where T : IOperationBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("OperationBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/Persistence.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/Persistence.cs similarity index 100% rename from src/LEGO.AsyncAPI/Models/Bindings/Pulsar/Persistence.cs rename to src/LEGO.AsyncAPI.Bindings/Pulsar/Persistence.cs diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs similarity index 59% rename from src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarChannelBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs index 7a4f504f..c673a8e8 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs @@ -1,16 +1,15 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Pulsar +namespace LEGO.AsyncAPI.Bindings.Pulsar { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Bindings.Pulsar; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; - /// - /// Binding class for Pulsar server settings. - /// - public class PulsarChannelBinding : IChannelBinding + public class PulsarChannelBinding : ChannelBinding { /// /// The namespace associated with the topic. @@ -38,7 +37,7 @@ public class PulsarChannelBinding : IChannelBinding public RetentionDefinition Retention { get; set; } /// - /// Message Time-to-live in seconds. + /// Message Time-to-live in seconds. /// public int? TTL { get; set; } @@ -47,22 +46,9 @@ public class PulsarChannelBinding : IChannelBinding /// public bool? Deduplication { get; set; } - /// - /// The version of this binding. - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Pulsar; - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); + public override string BindingKey => "pulsar"; - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -78,24 +64,26 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.TTL, this.TTL); writer.WriteOptionalProperty(AsyncApiConstants.Deduplication, this.Deduplication); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - public void SerializeV2(IAsyncApiWriter writer) + protected override FixedFieldMap FixedFieldMap => new() { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "namespace", (a, n) => { a.Namespace = n.GetScalarValue(); } }, + { "persistence", (a, n) => { a.Persistence = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "compaction", (a, n) => { a.Compaction = n.GetIntegerValue(); } }, + { "retention", (a, n) => { a.Retention = n.ParseMap(this.pulsarServerBindingRetentionFixedFields); } }, + { "geo-replication", (a, n) => { a.GeoReplication = n.CreateSimpleList(s => s.GetScalarValue()); } }, + { "ttl", (a, n) => { a.TTL = n.GetIntegerValue(); } }, + { "deduplication", (a, n) => { a.Deduplication = n.GetBooleanValue(); } }, + }; + + private FixedFieldMap pulsarServerBindingRetentionFixedFields = new() + { + { "time", (a, n) => { a.Time = n.GetIntegerValue(); } }, + { "size", (a, n) => { a.Size = n.GetIntegerValue(); } }, + }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs new file mode 100644 index 00000000..e767443d --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs @@ -0,0 +1,42 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Pulsar +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for Pulsar server settings. + /// + public class PulsarServerBinding : ServerBinding + { + /// + /// The pulsar tenant. If omitted, "public" must be assumed. + /// + public string Tenant { get; set; } + + public override string BindingKey => "pulsar"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "tenant", (a, n) => { a.Tenant = n.GetScalarValue(); } }, + }; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty(AsyncApiConstants.Tenant, this.Tenant); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/RetentionDefinition.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/RetentionDefinition.cs similarity index 100% rename from src/LEGO.AsyncAPI/Models/Bindings/Pulsar/RetentionDefinition.cs rename to src/LEGO.AsyncAPI.Bindings/Pulsar/RetentionDefinition.cs diff --git a/src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs new file mode 100644 index 00000000..6c1c58cf --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class ServerBinding : Binding, IServerBinding + where T : IServerBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("ServerBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/WebSockets/WebSocketsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs similarity index 57% rename from src/LEGO.AsyncAPI/Models/Bindings/WebSockets/WebSocketsChannelBinding.cs rename to src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs index 7aa2e793..94afcb55 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/WebSockets/WebSocketsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs @@ -1,13 +1,14 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.WebSockets +namespace LEGO.AsyncAPI.Bindings.WebSockets { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; - public class WebSocketsChannelBinding : IChannelBinding + public class WebSocketsChannelBinding : ChannelBinding { /// /// The HTTP method t use when establishing the connection. Its value MUST be either 'GET' or 'POST'. @@ -24,18 +25,17 @@ public class WebSocketsChannelBinding : IChannelBinding /// public AsyncApiSchema Headers { get; set; } - public string BindingVersion { get; set; } + public override string BindingKey => "websockets"; - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = - new Dictionary(); - - public BindingType Type => BindingType.Websockets; + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, + { "query", (a, n) => { a.Query = JsonSchemaDeserializer.LoadSchema(n); } }, + { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, + }; - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -48,24 +48,8 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalObject(AsyncApiConstants.Query, this.Query, (w, h) => h.SerializeV2(w)); writer.WriteOptionalObject(AsyncApiConstants.Headers, this.Headers, (w, h) => h.SerializeV2(w)); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } } -} \ No newline at end of file +} diff --git a/src/LEGO.AsyncAPI.Bindings/stylecop.json b/src/LEGO.AsyncAPI.Bindings/stylecop.json new file mode 100644 index 00000000..0a8f4661 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/stylecop.json @@ -0,0 +1,15 @@ +{ + // ACTION REQUIRED: This file was automatically added to your project, but it + // will not take effect until additional steps are taken to enable it. See the + // following page for additional information: + // + // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md + + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "The LEGO Group", + "xmlHeader": false + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index 21fe1c73..01576991 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -6,6 +6,7 @@ namespace LEGO.AsyncAPI.Readers using System.Collections.Generic; using System.IO; using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.Interface; using LEGO.AsyncAPI.Validations; public enum ReferenceResolutionSetting @@ -40,6 +41,11 @@ public Dictionary> { get; set; } = new Dictionary>(); + public List> + Bindings + { get; } = + new List>(); + /// /// Rules to use for validating AsyncApi specification. If none are provided a default set of rules are applied. /// diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs index da04716d..ed6c3942 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs @@ -33,7 +33,7 @@ public AsyncApiStreamReader(AsyncApiReaderSettings settings = null) public AsyncApiDocument Read(Stream input, out AsyncApiDiagnostic diagnostic) { var reader = new StreamReader(input); - var result = new AsyncApiTextReaderReader(this.settings).Read(reader, out diagnostic); + var result = new AsyncApiTextReader(this.settings).Read(reader, out diagnostic); if (!this.settings.LeaveStreamOpen) { reader.Dispose(); @@ -65,7 +65,7 @@ public async Task ReadAsync(Stream input) var reader = new StreamReader(bufferedStream); - return await new AsyncApiTextReaderReader(this.settings).ReadAsync(reader); + return await new AsyncApiTextReader(this.settings).ReadAsync(reader); } /// @@ -80,7 +80,7 @@ public T ReadFragment(Stream input, AsyncApiVersion version, out AsyncApiDiag { using (var reader = new StreamReader(input)) { - return new AsyncApiTextReaderReader(this.settings).ReadFragment(reader, version, out diagnostic); + return new AsyncApiTextReader(this.settings).ReadFragment(reader, version, out diagnostic); } } } diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs index c6680620..8d4bd870 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs @@ -30,7 +30,7 @@ public AsyncApiDocument Read(string input, out AsyncApiDiagnostic diagnostic) { using (var reader = new StringReader(input)) { - return new AsyncApiTextReaderReader(this.settings).Read(reader, out diagnostic); + return new AsyncApiTextReader(this.settings).Read(reader, out diagnostic); } } @@ -42,7 +42,7 @@ public T ReadFragment(string input, AsyncApiVersion version, out AsyncApiDiag { using (var reader = new StringReader(input)) { - return new AsyncApiTextReaderReader(this.settings).ReadFragment(reader, version, out diagnostic); + return new AsyncApiTextReader(this.settings).ReadFragment(reader, version, out diagnostic); } } } diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs index 51817948..0eaef44d 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs @@ -14,7 +14,7 @@ namespace LEGO.AsyncAPI.Readers /// /// Service class for converting contents of TextReader into AsyncApiDocument instances /// - public class AsyncApiTextReaderReader : IAsyncApiReader + public class AsyncApiTextReader : IAsyncApiReader { private readonly AsyncApiReaderSettings settings; @@ -22,7 +22,7 @@ public class AsyncApiTextReaderReader : IAsyncApiReader /// - public AsyncApiTextReaderReader(AsyncApiReaderSettings settings = null) + public AsyncApiTextReader(AsyncApiReaderSettings settings = null) { this.settings = settings ?? new AsyncApiReaderSettings(); } diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs index 9dae2141..cc03a892 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs @@ -41,6 +41,10 @@ public AsyncApiDocument Read(YamlDocument input, out AsyncApiDiagnostic diagnost var context = new ParsingContext(diagnostic) { ExtensionParsers = this.settings.ExtensionParsers, + ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + ChannelBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + OperationBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + MessageBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), }; AsyncApiDocument document = null; @@ -144,6 +148,10 @@ public T ReadFragment(YamlDocument input, AsyncApiVersion version, out AsyncA var context = new ParsingContext(diagnostic) { ExtensionParsers = this.settings.ExtensionParsers, + ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + ChannelBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + OperationBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + MessageBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), }; IAsyncApiElement element = null; diff --git a/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs new file mode 100644 index 00000000..5744985d --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs @@ -0,0 +1,31 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers +{ + using LEGO.AsyncAPI.Extensions; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public class BindingDeserializer + { + public static T LoadBinding(string nodeName, ParseNode node, FixedFieldMap fieldMap) + where T : IBinding, new() + { + var mapNode = node.CheckMapNode(nodeName); + var binding = new T(); + + AsyncApiV2Deserializer.ParseMap(mapNode, binding, fieldMap, BindingPatternExtensionFields()); + + return binding; + } + + private static PatternFieldMap BindingPatternExtensionFields() + where T : IBinding, new() + { + return new() + { + { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) }, + }; + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs deleted file mode 100644 index 72c2579b..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Models.Bindings.Http; - using LEGO.AsyncAPI.Readers.ParseNodes; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap httpMessageBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "headers", (a, n) => { a.Headers = LoadSchema(n); } }, - }; - - private static FixedFieldMap httpOperationBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "type", (a, n) => { a.Type = n.GetScalarValue(); } }, - { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, - { "query", (a, n) => { a.Query = LoadSchema(n); } }, - }; - - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs deleted file mode 100644 index 0d80eb8f..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Models.Bindings.Kafka; - using LEGO.AsyncAPI.Readers.ParseNodes; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap kafkaServerBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "schemaRegistryUrl", (a, n) => { a.SchemaRegistryUrl = n.GetScalarValue(); } }, - { "schemaRegistryVendor", (a, n) => { a.SchemaRegistryVendor = n.GetScalarValue(); } }, - }; - - private static FixedFieldMap kafkaChannelBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, - { "partitions", (a, n) => { a.Partitions = n.GetIntegerValue(); } }, - { "topicConfiguration", (a, n) => { a.TopicConfiguration = LoadTopicConfiguration(n); } }, - { "replicas", (a, n) => { a.Replicas = n.GetIntegerValue(); } }, - }; - - private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new() - { - { "cleanup.policy", (a, n) => { a.CleanupPolicy = n.CreateSimpleList(s => s.GetScalarValue()); } }, - { "retention.ms", (a, n) => { a.RetentionMiliseconds = n.GetIntegerValue(); } }, - { "retention.bytes", (a, n) => { a.RetentionBytes = n.GetIntegerValue(); } }, - { "delete.retention.ms", (a, n) => { a.DeleteRetentionMiliseconds = n.GetIntegerValue(); } }, - { "max.message.bytes", (a, n) => { a.MaxMessageBytes = n.GetIntegerValue(); } }, - }; - - private static FixedFieldMap kafkaOperationBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "groupId", (a, n) => { a.GroupId = LoadSchema(n); } }, - { "clientId", (a, n) => { a.ClientId = LoadSchema(n); } }, - }; - - private static FixedFieldMap kafkaMessageBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "key", (a, n) => { a.Key = LoadSchema(n); } }, - { "schemaIdLocation", (a, n) => { a.SchemaIdLocation = n.GetScalarValue(); } }, - { "schemaIdPayloadEncoding", (a, n) => { a.SchemaIdPayloadEncoding = n.GetScalarValue(); } }, - { "schemaLookupStrategy", (a, n) => { a.SchemaLookupStrategy = n.GetScalarValue(); } }, - }; - - private static TopicConfigurationObject LoadTopicConfiguration(ParseNode node) - { - var mapNode = node.CheckMapNode("topicConfiguration"); - var retention = new TopicConfigurationObject(); - ParseMap(mapNode, retention, kafkaChannelTopicConfigurationObjectFixedFields, null); - return retention; - } - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs deleted file mode 100644 index 6250a064..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Models.Bindings.Pulsar; - using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap pulsarServerBindingFixedFields = new () - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "tenant", (a, n) => { a.Tenant = n.GetScalarValue(); } }, - }; - - private static FixedFieldMap pulsarChannelBindingFixedFields = new () - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "namespace", (a, n) => { a.Namespace = n.GetScalarValue(); } }, - { "persistence", (a, n) => { a.Persistence = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "compaction", (a, n) => { a.Compaction = n.GetIntegerValue(); } }, - { "retention", (a, n) => { a.Retention = LoadRetention(n); } }, - { "geo-replication", (a, n) => { a.GeoReplication = n.CreateSimpleList(s => s.GetScalarValue()); } }, - { "ttl", (a, n) => { a.TTL = n.GetIntegerValue(); } }, - { "deduplication", (a, n) => { a.Deduplication = n.GetBooleanValue(); } }, - }; - - private static FixedFieldMap pulsarServerBindingRetentionFixedFields = new () - { - { "time", (a, n) => { a.Time = n.GetIntegerValue(); } }, - { "size", (a, n) => { a.Size = n.GetIntegerValue(); } }, - }; - - private static RetentionDefinition LoadRetention(ParseNode node) - { - var mapNode = node.CheckMapNode("retention"); - var retention = new RetentionDefinition(); - ParseMap(mapNode, retention, pulsarServerBindingRetentionFixedFields, null); - return retention; - } - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs deleted file mode 100644 index 4d4af870..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Readers.ParseNodes; - using Models.Bindings.WebSockets; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap webSocketsChannelBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, - { "query", (a, n) => { a.Query = LoadSchema(n); } }, - { "headers", (a, n) => { a.Headers = LoadSchema(n); } }, - }; - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs b/src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs new file mode 100644 index 00000000..9c83fe4d --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs @@ -0,0 +1,12 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers.Interface +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public interface IBindingParser : IBinding + { + T LoadBinding(PropertyNode node); + } +} diff --git a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj index d1e8bd74..6fd9d2e7 100644 --- a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj +++ b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj @@ -44,4 +44,8 @@ + + + + diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs index efb046dd..316a927a 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System; using System.Collections.Generic; - internal class FixedFieldMap : Dictionary> + public class FixedFieldMap : Dictionary> { } -} \ No newline at end of file +} diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs index 41da8d03..1b944de6 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; - internal class MapNode : ParseNode, IEnumerable + public class MapNode : ParseNode, IEnumerable { private readonly YamlMappingNode node; private readonly List nodes; @@ -89,54 +89,6 @@ public override Dictionary CreateMap(Func map) return nodes.ToDictionary(k => k.key, v => v.value); } - public override Dictionary CreateBindingMapWithReference( - ReferenceType referenceType, - Func map) - { - var yamlMap = this.node; - if (yamlMap == null) - { - throw new AsyncApiReaderException($"Expected map while parsing {typeof(T).Name}", this.Context); - } - - var nodes = yamlMap.Select( - n => - { - var key = n.Key.GetScalarValue(); - (string key, T value) entry; - try - { - this.Context.StartObject(key); - entry = ( - key: key, - value: map(new PropertyNode(this.Context, key, n.Value)) - ); - - if (entry.value == null) - { - return default; - } - - if (entry.value.Reference == null) - { - entry.value.Reference = new AsyncApiReference() - { - Type = referenceType, - Id = entry.key, - }; - } - } - finally - { - this.Context.EndObject(); - } - - return entry; - } - ); - return nodes.Where(n => n != default).ToDictionary(k => k.key, v => v.value); - } - public override Dictionary CreateMapWithReference( ReferenceType referenceType, Func map) diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs index aa106cd2..94fc4268 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using LEGO.AsyncAPI.Readers.Exceptions; using YamlDotNet.RepresentationModel; - internal abstract class ParseNode + public abstract class ParseNode { protected ParseNode(ParsingContext parsingContext) { diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/PatternFieldMap.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/PatternFieldMap.cs index 0c1112ad..040a79e7 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/PatternFieldMap.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/PatternFieldMap.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System; using System.Collections.Generic; - internal class PatternFieldMap : Dictionary, Action> + public class PatternFieldMap : Dictionary, Action> { } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs index 99e1a33e..221f0d9a 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs @@ -11,7 +11,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using LEGO.AsyncAPI.Readers.Exceptions; using YamlDotNet.RepresentationModel; - internal class PropertyNode : ParseNode + public class PropertyNode : ParseNode { public PropertyNode(ParsingContext context, string name, YamlNode node) : base( @@ -84,10 +84,5 @@ public void ParseField( } } } - - public override IAsyncApiAny CreateAny() - { - throw new NotImplementedException(); - } } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs index 63e0238e..385cfca0 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using YamlDotNet.Core; using YamlDotNet.RepresentationModel; - internal class ValueNode : ParseNode + public class ValueNode : ParseNode { private readonly YamlScalarNode node; diff --git a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs index 63e8d1a6..58b0b04b 100644 --- a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs +++ b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs @@ -25,6 +25,14 @@ internal Dictionary> ExtensionPar = new (); + internal Dictionary> ServerBindingParsers { get; set; } = new(); + + internal Dictionary> ChannelBindingParsers { get; set; } + + internal Dictionary> OperationBindingParsers { get; set; } = new(); + + internal Dictionary> MessageBindingParsers { get; set; } = new(); + internal RootNode RootNode { get; set; } internal List Tags { get; private set; } = new (); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs deleted file mode 100644 index 7c997dc0..00000000 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Extensions; - using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Readers.ParseNodes; - using System; - - internal static partial class AsyncApiV2Deserializer - { - private static Type messageBindingType = typeof(IMessageBinding); - private static Type operationBindingType = typeof(IOperationBinding); - private static Type channelBindingType = typeof(IChannelBinding); - private static Type serverBindingType = typeof(IServerBinding); - - private static PatternFieldMap BindingPatternExtensionFields() - where T : IBinding, new() - { - return new() - { - { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, - }; - } - - internal static T LoadBinding(string nodeName, ParseNode node, FixedFieldMap fieldMap) - where T : IBinding, new() - { - var mapNode = node.CheckMapNode(nodeName); - var pointer = mapNode.GetReferencePointer(); - if (pointer != null) - { - ReferenceType referenceType = ReferenceType.None; - var bindingType = typeof(T); - - if (bindingType.IsAssignableTo(messageBindingType)) - { - referenceType = ReferenceType.MessageBinding; - } - - if (bindingType.IsAssignableTo(operationBindingType)) - { - referenceType = ReferenceType.OperationBinding; - } - - if (bindingType.IsAssignableTo(channelBindingType)) - { - referenceType = ReferenceType.ChannelBinding; - } - - if (bindingType.IsAssignableTo(serverBindingType)) - { - referenceType = ReferenceType.ServerBinding; - } - - if (referenceType == ReferenceType.None) - { - throw new ArgumentException($"ReferenceType not found {typeof(T).Name}"); - } - - return mapNode.GetReferencedObject(referenceType, pointer); - } - - var binding = new T(); - - ParseMap(mapNode, binding, fieldMap, BindingPatternExtensionFields()); - - return binding; - } - } -} diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs index c6e56700..1aab9e85 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs @@ -4,19 +4,21 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { internal static AsyncApiBindings LoadChannelBindings(ParseNode node) { - var mapNode = node.CheckMapNode("channelBinding"); + var mapNode = node.CheckMapNode("channelBindings"); + var pointer = mapNode.GetReferencePointer(); + if (pointer != null) + { + return mapNode.GetReferencedObject>(ReferenceType.ChannelBindings, pointer); + } var channelBindings = new AsyncApiBindings(); - foreach (var property in mapNode) { var channelBinding = LoadChannelBinding(property); @@ -35,21 +37,23 @@ internal static AsyncApiBindings LoadChannelBindings(ParseNode return channelBindings; } - internal static IChannelBinding LoadChannelBinding(ParseNode node) + private static IChannelBinding LoadChannelBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try + { + if (node.Context.ChannelBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } + } + catch (AsyncApiException ex) { - case BindingType.Kafka: - return LoadBinding("ChannelBinding", property.Value, kafkaChannelBindingFixedFields); - case BindingType.Pulsar: - return LoadBinding("ChannelBinding", property.Value, pulsarChannelBindingFixedFields); - case BindingType.Websockets: - return LoadBinding("ChannelBinding", property.Value, webSocketsChannelBindingFixedFields); - default: - throw new AsyncApiException($"ChannelBinding {property.Name} is not supported"); + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs index fa475105..3b63db28 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs @@ -10,7 +10,7 @@ internal static partial class AsyncApiV2Deserializer { private static FixedFieldMap componentsFixedFields = new() { - { "schemas", (a, n) => a.Schemas = n.CreateMapWithReference(ReferenceType.Schema, LoadSchema) }, + { "schemas", (a, n) => a.Schemas = n.CreateMapWithReference(ReferenceType.Schema, JsonSchemaDeserializer.LoadSchema) }, { "servers", (a, n) => a.Servers = n.CreateMapWithReference(ReferenceType.Server, LoadServer) }, { "channels", (a, n) => a.Channels = n.CreateMapWithReference(ReferenceType.Channel, LoadChannel) }, { "messages", (a, n) => a.Messages = n.CreateMapWithReference(ReferenceType.Message, LoadMessage) }, @@ -19,10 +19,10 @@ internal static partial class AsyncApiV2Deserializer { "correlationIds", (a, n) => a.CorrelationIds = n.CreateMapWithReference(ReferenceType.CorrelationId, LoadCorrelationId) }, { "operationTraits", (a, n) => a.OperationTraits = n.CreateMapWithReference(ReferenceType.OperationTrait, LoadOperationTrait) }, { "messageTraits", (a, n) => a.MessageTraits = n.CreateMapWithReference(ReferenceType.MessageTrait, LoadMessageTrait) }, - { "serverBindings", (a, n) => a.ServerBindings = n.CreateMapWithReference(ReferenceType.ServerBinding, LoadServerBinding) }, - { "channelBindings", (a, n) => a.ChannelBindings = n.CreateMapWithReference(ReferenceType.ChannelBinding, LoadChannelBinding) }, - { "operationBindings", (a, n) => a.OperationBindings = n.CreateBindingMapWithReference(ReferenceType.OperationBinding, LoadOperationBinding) }, - { "messageBindings", (a, n) => a.MessageBindings = n.CreateMapWithReference(ReferenceType.MessageBinding, LoadMessageBinding) }, + { "serverBindings", (a, n) => a.ServerBindings = n.CreateMapWithReference(ReferenceType.ServerBindings, LoadServerBindings) }, + { "channelBindings", (a, n) => a.ChannelBindings = n.CreateMapWithReference(ReferenceType.ChannelBindings, LoadChannelBindings) }, + { "operationBindings", (a, n) => a.OperationBindings = n.CreateMapWithReference(ReferenceType.OperationBindings, LoadOperationBindings) }, + { "messageBindings", (a, n) => a.MessageBindings = n.CreateMapWithReference(ReferenceType.MessageBindings, LoadMessageBindings) }, }; private static PatternFieldMap componentsPatternFields = diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs index 0328c4ee..5ebbc49b 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs @@ -12,7 +12,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static void ParseMap( + internal static void ParseMap( MapNode mapNode, T domainObject, FixedFieldMap fixedFieldMap, @@ -29,7 +29,7 @@ private static void ParseMap( } } - private static void ProcessAnyFields( + internal static void ProcessAnyFields( MapNode mapNode, T domainObject, AnyFieldMap anyFieldMap) @@ -58,7 +58,7 @@ private static void ProcessAnyFields( } } - private static void ProcessAnyListFields( + internal static void ProcessAnyListFields( MapNode mapNode, T domainObject, AnyListFieldMap anyListFieldMap) @@ -163,7 +163,7 @@ public static IAsyncApiAny LoadAny(ParseNode node) return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); } - private static IAsyncApiExtension LoadExtension(string name, ParseNode node) + public static IAsyncApiExtension LoadExtension(string name, ParseNode node) { try { diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs index c3198737..9a180e84 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs @@ -4,12 +4,8 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; - using LEGO.AsyncAPI.Models.Bindings.Http; - using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { @@ -40,16 +36,20 @@ internal static AsyncApiBindings LoadMessageBindings(ParseNode internal static IMessageBinding LoadMessageBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try { - case BindingType.Kafka: - return LoadBinding("MessageBinding", property.Value, kafkaMessageBindingFixedFields); - case BindingType.Http: - return LoadBinding("MessageBinding", property.Value, httpMessageBindingFixedFields); - default: - throw new AsyncApiException($"MessageBinding {property.Name} is not supported"); + if (node.Context.MessageBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } } + catch (AsyncApiException ex) + { + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); + } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs index 97e40ba7..63d0512c 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs @@ -2,12 +2,12 @@ namespace LEGO.AsyncAPI.Readers { + using System.Collections.Generic; + using System.Linq; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Extensions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.ParseNodes; - using System.Collections.Generic; - using System.Linq; /// /// Class containing logic to deserialize AsyncApi document into @@ -21,10 +21,10 @@ internal static partial class AsyncApiV2Deserializer "messageId", (a, n) => { a.MessageId = n.GetScalarValue(); } }, { - "headers", (a, n) => { a.Headers = LoadSchema(n); } + "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, { - "payload", (a, n) => { a.Payload = LoadSchema(n); } + "payload", (a, n) => { a.Payload = JsonSchemaDeserializer.LoadSchema(n); } }, { "correlationId", (a, n) => { a.CorrelationId = LoadCorrelationId(n); } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs index 7e142ee2..eca8af64 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs @@ -11,7 +11,7 @@ internal static partial class AsyncApiV2Deserializer private static FixedFieldMap messageTraitFixedFields = new() { { "messageId", (a, n) => { a.MessageId = n.GetScalarValue(); } }, - { "headers", (a, n) => { a.Headers = LoadSchema(n); } }, + { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, { "correlationId", (a, n) => { a.CorrelationId = LoadCorrelationId(n); } }, { "schemaFormat", (a, n) => { a.SchemaFormat = n.GetScalarValue(); } }, { "contentType", (a, n) => { a.ContentType = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs index 03631600..4d4eb85f 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs @@ -4,16 +4,14 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { internal static AsyncApiBindings LoadOperationBindings(ParseNode node) { - var mapNode = node.CheckMapNode("operationBinding"); + var mapNode = node.CheckMapNode("operationBindings"); var operationBindings = new AsyncApiBindings(); @@ -38,16 +36,20 @@ internal static AsyncApiBindings LoadOperationBindings(ParseN internal static IOperationBinding LoadOperationBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try { - case BindingType.Kafka: - return LoadBinding("OperationBinding", property.Value, kafkaOperationBindingFixedFields); - case BindingType.Http: - return LoadBinding("OperationBinding", property.Value, httpOperationBindingFixedFields); - default: - throw new AsyncApiException($"OperationBinding {property.Name} is not supported"); + if (node.Context.OperationBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } } + catch (AsyncApiException ex) + { + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); + } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs index 3841fbf0..bff810f1 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs @@ -11,7 +11,7 @@ internal static partial class AsyncApiV2Deserializer private static FixedFieldMap parameterFixedFields = new() { { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, - { "schema", (a, n) => { a.Schema = LoadSchema(n); } }, + { "schema", (a, n) => { a.Schema = JsonSchemaDeserializer.LoadSchema(n); } }, { "location", (a, n) => { a.Location = n.GetScalarValue(); } }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index e63d3311..88dc8efb 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Readers using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; - internal static partial class AsyncApiV2Deserializer + public class JsonSchemaDeserializer { private static readonly FixedFieldMap schemaFixedFields = new () { @@ -144,7 +144,7 @@ internal static partial class AsyncApiV2Deserializer "discriminator", (a, n) => { a.Discriminator = n.GetScalarValue(); } }, { - "externalDocs", (a, n) => { a.ExternalDocs = LoadExternalDocs(n); } + "externalDocs", (a, n) => { a.ExternalDocs = AsyncApiV2Deserializer.LoadExternalDocs(n); } }, { "deprecated", (a, n) => { a.Deprecated = bool.Parse(n.GetScalarValue()); } @@ -154,7 +154,7 @@ internal static partial class AsyncApiV2Deserializer private static readonly PatternFieldMap schemaPatternFields = new() { - { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) }, + { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) }, }; private static readonly AnyFieldMap schemaAnyFields = new() @@ -201,8 +201,8 @@ public static AsyncApiSchema LoadSchema(ParseNode node) propertyNode.ParseField(schema, schemaFixedFields, schemaPatternFields); } - ProcessAnyFields(mapNode, schema, schemaAnyFields); - ProcessAnyListFields(mapNode, schema, schemaAnyListFields); + AsyncApiV2Deserializer.ProcessAnyFields(mapNode, schema, schemaAnyFields); + AsyncApiV2Deserializer.ProcessAnyListFields(mapNode, schema, schemaAnyListFields); return schema; } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs index 632d85ab..44c821d3 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs @@ -4,19 +4,21 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { internal static AsyncApiBindings LoadServerBindings(ParseNode node) { - var mapNode = node.CheckMapNode("serverBinding"); + var mapNode = node.CheckMapNode("serverBindings"); + var pointer = mapNode.GetReferencePointer(); + if (pointer != null) + { + return mapNode.GetReferencedObject>(ReferenceType.ServerBindings, pointer); + } var serverBindings = new AsyncApiBindings(); - foreach (var property in mapNode) { var serverBinding = LoadServerBinding(property); @@ -38,16 +40,20 @@ internal static AsyncApiBindings LoadServerBindings(ParseNode no internal static IServerBinding LoadServerBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try + { + if (node.Context.ServerBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } + } + catch (AsyncApiException ex) { - case BindingType.Kafka: - return LoadBinding("ServerBinding", property.Value, kafkaServerBindingFixedFields); - case BindingType.Pulsar: - return LoadBinding("ServerBinding", property.Value, pulsarServerBindingFixedFields); - default: - throw new AsyncApiException($"ServerBinding {property.Name} is not supported"); + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs index b4f34131..c3878072 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs @@ -35,7 +35,7 @@ public AsyncApiV2VersionService(AsyncApiDiagnostic diagnostic) [typeof(AsyncApiOAuthFlows)] = AsyncApiV2Deserializer.LoadOAuthFlows, [typeof(AsyncApiOperation)] = AsyncApiV2Deserializer.LoadOperation, [typeof(AsyncApiParameter)] = AsyncApiV2Deserializer.LoadParameter, - [typeof(AsyncApiSchema)] = AsyncApiV2Deserializer.LoadSchema, + [typeof(AsyncApiSchema)] = JsonSchemaDeserializer.LoadSchema, [typeof(AsyncApiSecurityRequirement)] = AsyncApiV2Deserializer.LoadSecurityRequirement, [typeof(AsyncApiSecurityScheme)] = AsyncApiV2Deserializer.LoadSecurityScheme, [typeof(AsyncApiServer)] = AsyncApiV2Deserializer.LoadServer, diff --git a/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs b/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs new file mode 100644 index 00000000..64e5508e --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs @@ -0,0 +1,43 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers +{ + using LEGO.AsyncAPI.Exceptions; + using LEGO.AsyncAPI.Extensions; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public static class ExtensionHelpers + { + public static PatternFieldMap GetExtensionsFieldMap() where T : IAsyncApiExtensible + { + return new () + { + { + s => s.StartsWith("x-"), + (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) + }, + }; + } + + public static IAsyncApiExtension LoadExtension(string name, ParseNode node) + { + try + { + if (node.Context.ExtensionParsers.TryGetValue(name, out var parser)) + { + return parser( + AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny())); + } + } + catch (AsyncApiException ex) + { + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); + } + + return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/YamlHelper.cs b/src/LEGO.AsyncAPI.Readers/YamlHelper.cs index f29522f0..0f159c73 100644 --- a/src/LEGO.AsyncAPI.Readers/YamlHelper.cs +++ b/src/LEGO.AsyncAPI.Readers/YamlHelper.cs @@ -6,7 +6,6 @@ namespace LEGO.AsyncAPI.Readers using System.Linq; using LEGO.AsyncAPI.Exceptions; using YamlDotNet.RepresentationModel; - internal static class YamlHelper { public static string GetScalarValue(this YamlNode node) diff --git a/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs b/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs index 150e12a0..b9403b07 100644 --- a/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs @@ -16,12 +16,5 @@ public sealed class MethodExpression : RuntimeExpression /// Gets the expression string. /// public override string Expression { get; } = Method; - - /// - /// Private constructor. - /// - public MethodExpression() - { - } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs b/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs index 81b1eabd..426020ce 100644 --- a/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs +++ b/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs @@ -7,12 +7,12 @@ namespace LEGO.AsyncAPI.Extensions using LEGO.AsyncAPI.Models.Interfaces; /// - /// Extension methods to verify validatity and add an extension to Extensions property. + /// Extension methods to verify validity and add an extension to Extensions property. /// public static class AsyncApiExtensibleExtensions { /// - /// Add extension into the Extensions + /// Add extension into the Extensions. /// /// . /// The extensible AsyncApi element. diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs new file mode 100644 index 00000000..4034b4e9 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs @@ -0,0 +1,41 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public abstract class AsyncApiBinding : IBinding + { + public abstract string BindingKey { get; } + + public bool UnresolvedReference { get; set; } + + public AsyncApiReference Reference { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public string BindingVersion { get; set; } + + public void SerializeV2(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + + this.SerializeProperties(writer); + } + + public abstract void SerializeProperties(IAsyncApiWriter writer); + } +} diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs index b2784755..d039cddf 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs @@ -4,11 +4,10 @@ namespace LEGO.AsyncAPI.Models { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - public class AsyncApiBindings : Dictionary, IAsyncApiSerializable, IAsyncApiReferenceable + public class AsyncApiBindings : Dictionary, IAsyncApiReferenceable where TBinding : IBinding { public bool UnresolvedReference { get; set; } @@ -17,7 +16,7 @@ public class AsyncApiBindings : Dictionary, IAs public void Add(TBinding binding) { - this[binding.Type] = binding; + this[binding.BindingKey] = binding; } public void SerializeV2(IAsyncApiWriter writer) @@ -51,7 +50,7 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) var bindingType = binding.Key; var bindingValue = binding.Value; - writer.WritePropertyName(bindingType.GetDisplayName()); + writer.WritePropertyName(bindingType); bindingValue.SerializeV2(writer); } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs b/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs index 9e8e6e02..6c8a2a4c 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs @@ -69,22 +69,22 @@ public class AsyncApiComponents : IAsyncApiExtensible, IAsyncApiSerializable /// /// An object to hold reusable Server Bindings Objects. /// - public IDictionary ServerBindings { get; set; } = new Dictionary(); + public IDictionary> ServerBindings { get; set; } = new Dictionary>(); /// /// An object to hold reusable Channel Bindings Objects. /// - public IDictionary ChannelBindings { get; set; } = new Dictionary(); + public IDictionary> ChannelBindings { get; set; } = new Dictionary>(); /// /// An object to hold reusable Operation Bindings Objects. /// - public IDictionary OperationBindings { get; set; } = new Dictionary(); + public IDictionary> OperationBindings { get; set; } = new Dictionary>(); /// /// An object to hold reusable Message Bindings Objects. /// - public IDictionary MessageBindings { get; set; } = new Dictionary(); + public IDictionary> MessageBindings { get; set; } = new Dictionary>(); public IDictionary Extensions { get; set; } = new Dictionary(); @@ -311,7 +311,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.ServerBinding && + component.Reference.Type == ReferenceType.ServerBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); @@ -329,7 +329,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.ChannelBinding && + component.Reference.Type == ReferenceType.ChannelBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); @@ -347,7 +347,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.OperationBinding && + component.Reference.Type == ReferenceType.OperationBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); @@ -365,7 +365,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.MessageBinding && + component.Reference.Type == ReferenceType.MessageBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs index 1558dcc2..b28168e2 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs @@ -7,7 +7,7 @@ namespace LEGO.AsyncAPI.Models using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using Services; + using LEGO.AsyncAPI.Services; /// /// This is the root document object for the API specification. It combines resource listing and API declaration together into one document. @@ -189,13 +189,13 @@ public IAsyncApiReferenceable ResolveReference(AsyncApiReference reference) return this.Components.OperationTraits[reference.Id]; case ReferenceType.MessageTrait: return this.Components.MessageTraits[reference.Id]; - case ReferenceType.ServerBinding: + case ReferenceType.ServerBindings: return this.Components.ServerBindings[reference.Id]; - case ReferenceType.ChannelBinding: + case ReferenceType.ChannelBindings: return this.Components.ChannelBindings[reference.Id]; - case ReferenceType.OperationBinding: + case ReferenceType.OperationBindings: return this.Components.OperationBindings[reference.Id]; - case ReferenceType.MessageBinding: + case ReferenceType.MessageBindings: return this.Components.MessageBindings[reference.Id]; default: throw new AsyncApiException("Invalid reference type."); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs index 45e61cbf..c9a5c309 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs @@ -4,7 +4,7 @@ namespace LEGO.AsyncAPI.Models { using LEGO.AsyncAPI.Writers; - internal static class AsyncApiWriterExtensions + public static class AsyncApiWriterExtensions { internal static AsyncApiWriterSettings GetSettings(this IAsyncApiWriter asyncApiWriter) { diff --git a/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs b/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs deleted file mode 100644 index b88d3d5d..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings -{ - using LEGO.AsyncAPI.Attributes; - - public enum BindingType - { - [Display("kafka")] - Kafka, - - [Display("http")] - Http, - - [Display("websockets")] - Websockets, - - [Display("pulsar")] - Pulsar, - } -} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs deleted file mode 100644 index eeb5d05f..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings.Http -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// Binding class for http messaging channels. - /// - public class HttpMessageBinding : IMessageBinding - { - - /// - /// A Schema object containing the definitions for HTTP-specific headers. This schema MUST be of type object and have a properties key. - /// - public AsyncApiSchema Headers { get; set; } - - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - - /// - /// Indicates if object is populated with data or is just a reference to the data - /// - public bool UnresolvedReference { get; set; } - - /// - /// Reference object. - /// - public AsyncApiReference Reference { get; set; } - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - - writer.WriteOptionalObject(AsyncApiConstants.Headers, this.Headers, (w, h) => h.SerializeV2(w)); - writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - - writer.WriteEndObject(); - } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - public BindingType Type => BindingType.Http; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs deleted file mode 100644 index 85ec18d6..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings.Kafka -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// Binding class for Kafka operations. - /// - public class KafkaOperationBinding : IOperationBinding - { - /// - /// Id of the consumer group. - /// - public AsyncApiSchema GroupId { get; set; } - - /// - /// Id of the consumer inside a consumer group. - /// - public AsyncApiSchema ClientId { get; set; } - - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public BindingType Type => BindingType.Kafka; - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - writer.WriteOptionalObject(AsyncApiConstants.GroupId, this.GroupId, (w, h) => h.SerializeV2(w)); - writer.WriteOptionalObject(AsyncApiConstants.ClientId, this.ClientId, (w, h) => h.SerializeV2(w)); - writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - - writer.WriteEndObject(); - } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs deleted file mode 100644 index 2fc0e15c..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings.Pulsar -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// Binding class for Pulsar server settings. - /// - public class PulsarServerBinding : IServerBinding - { - /// - /// The pulsar tenant. If omitted, "public" must be assumed. - /// - public string Tenant { get; set; } - - /// - /// The version of this binding. - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Pulsar; - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - - writer.WriteOptionalProperty(AsyncApiConstants.Tenant, this.Tenant); - writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - - writer.WriteEndObject(); - } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs b/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs index c427db45..44e4573d 100644 --- a/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs +++ b/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs @@ -1,15 +1,13 @@ // Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Models.Interfaces { - using LEGO.AsyncAPI.Models.Bindings; - /// /// Describes a protocol-specific binding. /// - public interface IBinding : IAsyncApiReferenceable, IAsyncApiExtensible + public interface IBinding : IAsyncApiSerializable, IAsyncApiExtensible { - public BindingType Type { get; } + public string BindingKey { get; } - public string BindingVersion { get; set; } + public string BindingVersion { get; set; } } } diff --git a/src/LEGO.AsyncAPI/Models/ReferenceType.cs b/src/LEGO.AsyncAPI/Models/ReferenceType.cs index 8195e197..8903dd73 100644 --- a/src/LEGO.AsyncAPI/Models/ReferenceType.cs +++ b/src/LEGO.AsyncAPI/Models/ReferenceType.cs @@ -56,22 +56,22 @@ public enum ReferenceType /// /// ServerBindings item. /// - [Display("serverBindings")] ServerBinding, + [Display("serverBindings")] ServerBindings, /// /// ChannelBindings item. /// - [Display("channelBindings")] ChannelBinding, + [Display("channelBindings")] ChannelBindings, /// /// OperationBindings item. /// - [Display("operationBindings")] OperationBinding, + [Display("operationBindings")] OperationBindings, /// /// MessageBindings item. /// - [Display("messageBindings")] MessageBinding, + [Display("messageBindings")] MessageBindings, /// /// Examples item. @@ -82,6 +82,10 @@ public enum ReferenceType /// Headers item. /// [Display("headers")] Header, - ServerVariable, + + /// + /// The server variable + /// + [Display("serverVariable")] ServerVariable, } -} \ No newline at end of file +} diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs index 85f2d4da..c1e45d6a 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs @@ -5,9 +5,9 @@ namespace LEGO.AsyncAPI.Services using System; using System.Collections.Generic; using System.Linq; - using Exceptions; - using Models; - using Models.Interfaces; + using LEGO.AsyncAPI.Exceptions; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; /// /// This class is used to walk an AsyncApiDocument and convert unresolved references to references to populated objects @@ -64,8 +64,7 @@ public override void Visit(AsyncApiDocument doc) public override void Visit(AsyncApiChannel channel) { this.ResolveMap(channel.Parameters); - var bindingDictionary = channel.Bindings.Select(binding => binding.Value).ToDictionary(x => x.Type.GetDisplayName()); - this.ResolveMap(bindingDictionary); + this.ResolveObject(channel.Bindings, r => channel.Bindings = r); } public override void Visit(AsyncApiMessageTrait trait) @@ -81,8 +80,7 @@ public override void Visit(AsyncApiOperation operation) { this.ResolveList(operation.Message); this.ResolveList(operation.Traits); - var bindingDictionary = operation.Bindings.Select(binding => binding.Value).ToDictionary(x => x.Type.GetDisplayName()); - this.ResolveMap(bindingDictionary); + this.ResolveObject(operation.Bindings, r => operation.Bindings = r); } public override void Visit(AsyncApiMessage message) @@ -91,19 +89,12 @@ public override void Visit(AsyncApiMessage message) this.ResolveObject(message.Payload, r => message.Payload = r); this.ResolveList(message.Traits); this.ResolveObject(message.CorrelationId, r => message.CorrelationId = r); - var bindingDictionary = message.Bindings.Select(binding => binding.Value).ToDictionary(x => x.Type.GetDisplayName()); - this.ResolveMap(bindingDictionary); + this.ResolveObject(message.Bindings, r => message.Bindings = r); } - /// - /// Resolve all references to bindings. - /// - public override void Visit(AsyncApiBindings bindings) + public override void Visit(AsyncApiServer server) { - foreach (var binding in bindings.Values.ToList()) - { - this.ResolveObject(binding, resolvedBinding => bindings[binding.Type] = resolvedBinding); - } + this.ResolveObject(server.Bindings, r => server.Bindings = r); } /// diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs index 9519f3ba..bac5482f 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs @@ -15,7 +15,7 @@ public abstract class AsyncApiVisitorBase private readonly Stack path = new Stack(); /// - /// Properties available to identify context of where an object is within AsyncApi Document + /// Properties available to identify context of where an object is within AsyncApi Document. /// public CurrentKeys CurrentKeys { get; } = new CurrentKeys(); @@ -171,14 +171,6 @@ public virtual void Visit(AsyncApiOAuthFlow asyncApiOAuthFlow) { } - /// - /// Visits - /// - public virtual void Visit(AsyncApiBindings bindings) - where TBinding : class, IBinding - { - } - /// /// Visits /// diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs index c3ea6af8..1e0f7330 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs @@ -73,6 +73,17 @@ internal void Walk(AsyncApiComponents components) } }); + this.Walk(AsyncApiConstants.ServerBindings, () => + { + if (components.ServerBindings != null) + { + foreach (var item in components.ServerBindings) + { + this.Walk(item.Key, () => this.Walk(item.Value, isComponent: true)); + } + } + }); + this.Walk(AsyncApiConstants.Parameters, () => { if (components.Parameters != null) @@ -532,48 +543,44 @@ internal void Walk(AsyncApiMessageTrait trait, bool isComponent = false) this.Walk(trait as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings serverBindings) + internal void Walk(AsyncApiBindings serverBindings, bool isComponent = false) { - if (serverBindings is null) + if (serverBindings == null || this.ProcessAsReference(serverBindings, isComponent)) { return; } this.visitor.Visit(serverBindings); - this.Walk(serverBindings as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings channelBindings) + internal void Walk(AsyncApiBindings channelBindings, bool isComponent = false) { - if (channelBindings is null) + if (channelBindings == null || this.ProcessAsReference(channelBindings, isComponent)) { return; } this.visitor.Visit(channelBindings); - this.Walk(channelBindings as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings operationBindings) + internal void Walk(AsyncApiBindings operationBindings, bool isComponent = false) { - if (operationBindings is null) + if (operationBindings == null || this.ProcessAsReference(operationBindings, isComponent)) { return; } this.visitor.Visit(operationBindings); - this.Walk(operationBindings as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings messageBindings) + internal void Walk(AsyncApiBindings messageBindings, bool isComponent = false) { - if (messageBindings is null) + if (messageBindings == null || this.ProcessAsReference(messageBindings, isComponent)) { return; } this.visitor.Visit(messageBindings); - this.Walk(messageBindings as IAsyncApiExtensible); } internal void Walk(IList examples) @@ -703,7 +710,6 @@ internal void Walk(AsyncApiServer server, bool isComponent = false) this.visitor.Visit(server); this.Walk(AsyncApiConstants.Variables, () => this.Walk(server.Variables)); this.Walk(AsyncApiConstants.Security, () => this.Walk(server.Security)); - this.Walk(AsyncApiConstants.Bindings, () => this.Walk(server.Bindings)); this.visitor.Visit(server as IAsyncApiExtensible); } diff --git a/src/LEGO.AsyncAPI/Services/CurrentKeys.cs b/src/LEGO.AsyncAPI/Services/CurrentKeys.cs index f070a15c..3544eb21 100644 --- a/src/LEGO.AsyncAPI/Services/CurrentKeys.cs +++ b/src/LEGO.AsyncAPI/Services/CurrentKeys.cs @@ -4,6 +4,8 @@ namespace LEGO.AsyncAPI.Services { public class CurrentKeys { + public string ServerBindings { get; internal set; } + public string Channel { get; internal set; } public string Extension { get; internal set; } diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs index 019c2ef4..79576005 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs @@ -3,7 +3,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Validations; [AsyncApiRule] diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs index 5422eea8..3e6a21c8 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Validations; - using System.Linq; [AsyncApiRule] public static class AsyncApiCorrelationIdRules diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs index 3cba8059..27076369 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { - using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using LEGO.AsyncAPI.Models; diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs index 8ce2a099..710b30e5 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { - using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Validations; diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs index ce735c93..69b8c5ee 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs @@ -3,7 +3,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Validations; [AsyncApiRule] diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs index e1649d4e..073066da 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs @@ -2,9 +2,9 @@ namespace LEGO.AsyncAPI.Validation.Rules { + using System.Linq; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Validations; - using System.Linq; [AsyncApiRule] public static class AsyncApiOAuthFlowRules diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs index bb421471..244686c5 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Validations; - using System.Linq; [AsyncApiRule] public static class AsyncApiTagRules diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs index 9d89bba3..17229818 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs @@ -2,8 +2,8 @@ namespace LEGO.AsyncAPI.Writers { - using LEGO.AsyncAPI.Exceptions; using System; + using LEGO.AsyncAPI.Exceptions; public class AsyncApiWriterException : AsyncApiException { diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs index 5953e9d1..ec9c96b6 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs @@ -138,6 +138,12 @@ public static void WriteOptionalObject( { if (value != null) { + if (value is IAsyncApiReferenceable refer && refer.Reference != null) + { + writer.WriteRequiredObject(name, value, action); + return; + } + var values = value as IEnumerable; if (values != null && !values.GetEnumerator().MoveNext()) { diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs index b6edd619..b3f061e3 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs @@ -40,7 +40,7 @@ public ReferenceInlineSetting ReferenceInline /// public bool InlineReferences { get; set; } = false; - internal bool ShouldInlineReference(AsyncApiReference reference) + public bool ShouldInlineReference(AsyncApiReference reference) { return this.InlineReferences; } diff --git a/src/LEGO.AsyncAPI/Writers/StringExtensions.cs b/src/LEGO.AsyncAPI/Writers/StringExtensions.cs index 15697bd3..e4efe5f7 100644 --- a/src/LEGO.AsyncAPI/Writers/StringExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/StringExtensions.cs @@ -4,7 +4,7 @@ namespace LEGO.AsyncAPI.Writers { using System; using System.Reflection; - using Attributes; + using LEGO.AsyncAPI.Attributes; public static class StringExtensions { diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs index 49b32d80..f800a1f7 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs @@ -1,8 +1,10 @@ -namespace LEGO.AsyncAPI.Tests +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests { + using System; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; - using System; internal class AsyncApiDocumentBuilder { @@ -154,47 +156,47 @@ public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiMessageTrait me return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IServerBinding serverBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings serverBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.ServerBindings.Add(key, serverBinding); + this.document.Components.ServerBindings.Add(key, serverBindings); return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IChannelBinding channelBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings channelBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.ChannelBindings.Add(key, channelBinding); + this.document.Components.ChannelBindings.Add(key, channelBindings); return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IOperationBinding operationBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings operationBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.OperationBindings.Add(key, operationBinding); + this.document.Components.OperationBindings.Add(key, operationBindings); return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IMessageBinding messageBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings messageBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.MessageBindings.Add(key, messageBinding); + this.document.Components.MessageBindings.Add(key, messageBindings); return this; } diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 75db39a9..3c90d791 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -1,15 +1,18 @@ -namespace LEGO.AsyncAPI.Tests +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests { using System; using System.Collections.Generic; 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.Models; using LEGO.AsyncAPI.Models.Any; - using LEGO.AsyncAPI.Models.Bindings; - using LEGO.AsyncAPI.Models.Bindings.Http; - using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Writers; @@ -17,86 +20,6 @@ public class AsyncApiDocumentV2Tests { - - [Test] - public void test() - { - var input = @"asyncapi: 2.0.0 -info: - title: Sites - version: 1.0.0 - description: Responsible for emitting the site on/off - x-application-id: APP-02042 - x-audience: component-internal -channels: - site-events: - subscribe: - message: - payload: - properties: - data: - '$ref': '#/components/schemas/DtosSiteUpdatedEvent' - description: The actual payload of the event - datacontenttype: - description: Always application/json - type: string - example: application/json - id: - description: The unique ID of the event - type: string - example: 3489d4b1e21badf3665dae24c6526169 - source: - description: The source of the event - type: string - example: LEGO.OmnichannelFulfilment.DeliveryOrchestration/Sites - specversion: - description: The CloudEvents schema version used - type: string - example: 1.0 - time: - description: The time the event was published - format: date-time - type: string - example: 2022-10-13T11:57:36.268054757Z - type: - description: The type of the event - type: string - example: siteUpdatedV1 - traceparent: - description: The value to propagate context information that enables distributed tracing scenarios - type: string - example: 3489d4b1e21badf3665dae24c6526169 - type: object - summary: Subscriber message - description: All data used for turning on or off a site request - x-classification: green - x-datalakesubscription: false - x-eventarchetype: objectchanged - x-eventdurability: persistent -components: - schemas: - DtosSiteUpdatedEvent: - properties: - enabled: - description: The Enabled shows the current status of the site - type: boolean - examples: - - false - siteId: - description: The SiteCode related to the site that is being turn on or off - type: string - examples: - - 489 - reason: - description: The reason the site is being turned on or off - type: string - examples: - - Workers striking require us to temporary close the site. - type: object -"; - var serialized = new AsyncApiStringReader().Read(input, out var diag); - - } [Test] public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() { @@ -748,7 +671,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Bindings = new AsyncApiBindings() { { - BindingType.Kafka, new KafkaOperationBinding() + "kafka", new KafkaOperationBinding() { ClientId = new AsyncApiSchema() { @@ -770,7 +693,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -1201,937 +1124,95 @@ public void SerializeV2_WithFullSpec_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] - public void tesT() + public void Serialize_WithBindingReferences_SerializesDeserializes() { - var spec = @"asyncapi: '2.6.0' -defaultContentType: 'application/json' -info: - title: '{TITLE}' - version: '{VERSION}' - x-audience: company-internal - x-application-id: APP-01575 - x-eventdeduplication: false - contact: - name: Team Deadlock - email: Deadlock@o365.corp.LEGO.com - url: https://legogroup.atlassian.net/wiki/spaces/TD/pages/37143022928/Consent+Service - description: | - Emits events related to consent changes for both LEGO Account users and anonymous users. - This includes both parental consents and cookie consents. -channels: - userconsents.objectchanged: - x-eventarchetype: objectchanged - x-eventdurability: persistent - x-classification: yellow - description: | - A topic for events regarding changes to user consents. The event archetype is set to 'objectchanged' which will enable tombstoning and compaction. - subscribe: - operationId: UserConsentsObjectChanged - message: - oneOf: - - $ref: '#/components/messages/UserConsentsObjectCreated' - - $ref: '#/components/messages/UserConsentsObjectChanged' - - $ref: '#/components/messages/UserConsentsObjectDeleted' - userconsents.fieldchanged: - x-eventarchetype: fieldchanged - x-eventdurability: persistent - x-classification: yellow - description: | - A topic for deleted user consents events. The event archetype is set to 'fieldchanged' in order to enforce a 28 days retention policy. - subscribe: - operationId: UserConsentsFieldChanged - message: - oneOf: - - $ref: '#/components/messages/UserConsentFieldCreated' - - $ref: '#/components/messages/UserConsentFieldChanged' - - $ref: '#/components/messages/UserConsentFieldDeleted' - anonymousconsent.fieldchange: - x-eventarchetype: fieldchanged - x-eventdurability: persistent - x-classification: green - description: | - A topic for events regarding changes to anonymous consents. The event archetype is set to 'fieldchanged' in order to enforce a 28 days retention policy. - subscribe: - operationId: AnonymousConsentsFieldChanged - message: - oneOf: - - $ref: '#/components/messages/AnonymousConsentFieldChange' - experiences.events: - x-eventarchetype: objectchanged - x-eventdurability: persistent - x-classification: yellow - description: | - A topic for experience events. The event archetype is set to 'objectchanged' in order to store event forever. - subscribe: - operationId: ExperiencesChanged - message: - oneOf: - - $ref: '#/components/messages/ExperienceCreated' - - $ref: '#/components/messages/ExperienceDeleted' - - $ref: '#/components/messages/ExperienceUpdated' - - $ref: '#/components/messages/ExperienceClientAdded' - - $ref: '#/components/messages/ExperienceClientRemoved' - - $ref: '#/components/messages/ExperienceConsentOptionAdded' - - $ref: '#/components/messages/ExperienceConsentOptionRemoved' -components: - schemas: - EnvelopeBase: - type: object - properties: - type: - type: string - description: The type of event. - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - type: object - - EnvelopeOfUserConsentsObjectCreated: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentsObjectCreated] - data: - $ref: '#/components/schemas/UserConsentsObjectCreated' - - EnvelopeOfUserConsentsObjectChanged: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentsObjectChanged] - data: - $ref: '#/components/schemas/UserConsentsObjectChanged' - EnvelopeOfUserConsentsObjectDeleted: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentsObjectDeleted] - data: - $ref: '#/components/schemas/UserConsentsObjectDeleted' - UserConsentsObjectCreated: - type: object - required: - - changeType - - changeTime - - userId - - consents - properties: - changeType: - type: string - enum: ['created'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consents: - type: array - items: - $ref: '#/components/schemas/UserConsent' - UserConsentsObjectChanged: - type: object - required: - - changeType - - changeTime - - userId - - consents - properties: - changeType: - type: string - enum: ['updated'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consents: - type: array - items: - $ref: '#/components/schemas/UserConsent' - UserConsentsObjectDeleted: - type: object - required: - - changeType - - changeTime - - userId - properties: - changeType: - type: string - enum: ['deleted'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - UserConsent: - type: object - required: - - consentId - - consenterUserId - - consentState - - culture - properties: - consentId: - type: string - format: uri - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - consenterUserId: - type: string - format: guid - description: The ID of the user giving the consent. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentState: - type: string - description: The state of the consent for the given consent option - enum: ['granted','denied','undecided'] - culture: - type: string - minLength: 5 - maxLength: 5 - description: The culture where the consent was changed. - examples: - - da-DK - - en-US - - en-GB - submissionSource: - type: string - description: The method used by the user to submit the cookies - enum: ['prebannerAcceptAll', 'prebannerRejectAll', 'savePrefButton', 'cloned' ] - - EnvelopeOfUserConsentFieldCreatedEvent: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentFieldCreatedEvent] - data: - $ref: '#/components/schemas/UserConsentFieldCreatedEvent' - EnvelopeOfUserConsentFieldChangedEvent: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentFieldChangedEvent] - data: - $ref: '#/components/schemas/UserConsentFieldChangedEvent' - EnvelopeOfUserConsentFieldDeletedEvent: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentFieldDeletedEvent] - data: - $ref: '#/components/schemas/UserConsentFieldDeletedEvent' - - UserConsentFieldCreatedEvent: - type: object - required: - - changeType - - changeTime - - userId - - consentId - - consenterUserId - - consentState - - culture - properties: - changeType: - type: string - enum: ['created','updated','deleted'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentId: - type: string - format: uri - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - consenterUserId: - type: string - format: guid - description: The ID of the user giving the consent. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentState: - type: string - description: The state of the consent for the given consent option - enum: ['granted','denied','undecided'] - culture: - type: string - minLength: 5 - maxLength: 5 - description: The culture where the consent was changed. - examples: - - da-DK - - en-US - - en-GB - submissionSource: - type: string - description: The method used by the user to submit the cookies - enum: ['prebannerAcceptAll', 'prebannerRejectAll', 'savePrefButton', 'cloned' ] - - UserConsentFieldChangedEvent: - type: object - required: - - changeType - - changeTime - - userId - - consentId - - consenterUserId - - consentState - - culture - properties: - changeType: - type: string - enum: ['created','updated','deleted'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentId: - type: string - format: uri - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - consenterUserId: - type: string - format: guid - description: The ID of the user giving the consent. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentState: - type: string - description: The state of the consent for the given consent option - enum: ['granted','denied','undecided'] - culture: - type: string - minLength: 5 - maxLength: 5 - description: The culture where the consent was changed. - examples: - - da-DK - - en-US - - en-GB - submissionSource: - type: string - description: The method used by the user to submit the cookies - enum: ['prebannerAcceptAll', 'prebannerRejectAll', 'savePrefButton', 'cloned' ] - - UserConsentFieldDeletedEvent: - type: object - required: - - changeType - - changeTime - - userId - - consentId - properties: - changeType: - type: string - enum: ['deleted'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentId: - type: string - format: uri - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - - EnvelopeOfAnonymousConsentFieldChangeEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'anonymousconsents.fieldchanged'. - enum: [anonymousconsents.fieldchanged] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/AnonymousConsentFieldChangeEvent' - EnvelopeOfExperienceCreatedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.created'. - enum: [experience.created] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceCreatedEvent' - EnvelopeOfExperienceDeletedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.deleted'. - enum: [experience.deleted] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceDeletedEvent' - EnvelopeOfExperienceUpdatedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.updated'. - enum: [experience.updated] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceUpdatedEvent' - EnvelopeOfExperienceClientAddedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.client.added'. - enum: [experience.client.added] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceClientAddedEvent' - EnvelopeOfExperienceClientRemovedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.client.removed'. - enum: [experience.client.removed] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceClientRemovedEvent' - EnvelopeOfExperienceConsentOptionAddedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.consentoption.added'. - enum: [experience.consentoption.added] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceConsentOptionAddedEvent' - EnvelopeOfExperienceConsentOptionRemovedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.consentoption.removed'. - enum: [experience.consentoption.removed] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceConsentOptionRemovedEvent' - - AnonymousConsentFieldChangeEvent: - type: object - required: - - ChangeType - - ChangeTime - - AnonymousUserId - - ConsentId - - ConsentState - - Culture - properties: - changeType: - type: string - description: The change type - examples: - - Created - - Updated - - Deleted - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - anonymousUserId: - type: string - format: guid - description: The anonymous user ID. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentId: - type: string - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - consentState: - type: string - description: The state of the consent for the given consent id - examples: - - granted - - denied - - undecided - culture: - type: string - minLength: 5 - maxLength: 5 - description: The culture where the consent was changed. - examples: - - da-DK - - en-US - - en-GB - ExperienceCreatedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - name - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - name: - type: string - description: name of experience - minLength: 1 - maxLength: 100 - examples: - - LEGO Webshop - ExperienceUpdatedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - name - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - name: - type: string - description: name of experience - minLength: 1 - maxLength: 100 - examples: - - LEGO Webshop - ExperienceDeletedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - ExperienceClientAddedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - clientId - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - clientId: - type: string - format: guid - description: Identity client id. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - ExperienceClientRemovedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - clientId - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - clientId: - type: string - format: guid - description: Identity client id. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - ExperienceConsentOptionAddedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - consentOption - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - clientId: - type: string - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - ExperienceConsentOptionRemovedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - consentOption - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - clientId: - type: string - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - messages: - UserConsentsObjectCreated: - messageId: UserConsentsObjectCreated - name: User consents object created - title: The object state of consents for a user - description: An event emitted whenever a user's consent is created. - tags: - - name: user - - name: consents - - name: created - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentsObjectCreated' - UserConsentsObjectChanged: - messageId: UserConsentsObjectChanged - name: User consents object changed - title: The object state of consents for a user - description: An event emitted whenever a user's consent is changed. - tags: - - name: user - - name: consents - - name: changed - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentsObjectChanged' - UserConsentsObjectDeleted: - messageId: UserConsentsObjectDeleted - name: User consents object deleted - title: The object state of consents for a user - description: An event emitted whenever a user's consent is deleted. - tags: - - name: user - - name: consents - - name: deleted - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentsObjectDeleted' - UserConsentFieldCreated: - messageId: UserConsentFieldCreated - name: User consent field created - title: The change of 1 specific consent being created - description: An event emitted whenever a user's consent is created. - tags: - - name: user - - name: consents - - name: created - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentFieldCreatedEvent' - UserConsentFieldChanged: - messageId: UserConsentFieldChanged - name: User consent field changed - title: The change of 1 specific consent being changed - description: An event emitted whenever a user's consent is changed. - tags: - - name: user - - name: consents - - name: changed - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentFieldChangedEvent' - UserConsentFieldDeleted: - messageId: UserConsentFieldDeleted - name: User consent field deleted - title: The change of 1 specific consent being deleted - description: An event emitted whenever a user's consent is deleted. - tags: - - name: user - - name: consents - - name: deleted - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentFieldDeletedEvent' - AnonymousConsentFieldChange: - messageId: AnonymousConsentFieldChange - name: Anonymous consent field change - title: Anonymous consent field change event - description: An event emitted whenever an anonymous consent is changed (added, updated or removed). - tags: - - name: anonymous - - name: consents - - name: deleted - payload: - $ref: '#/components/schemas/EnvelopeOfAnonymousConsentFieldChangeEvent' - ExperienceCreated: - messageId: ExperienceCreated - name: Experience created - title: Experience created event - description: An event emitted whenever an experience is created. - tags: - - name: experience - - name: created - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceCreatedEvent' - ExperienceDeleted: - messageId: ExperienceDeleted - name: Experience deleted - title: Experience deleted event - description: An event emitted whenever an experience is deleted. - tags: - - name: experience - - name: deleted - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceDeletedEvent' - ExperienceUpdated: - messageId: ExperienceUpdated - name: Experience updated - title: Experience updated event - description: An event emitted whenever an experience is updated. - tags: - - name: experience - - name: updated - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceUpdatedEvent' - ExperienceClientAdded: - messageId: ExperienceClientAdded - name: Experience client added - title: Experience client added event - description: An event emitted whenever a client is added to experience. - tags: - - name: experience - - name: updated - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceClientAddedEvent' - ExperienceClientRemoved: - messageId: ExperienceClientRemoved - name: Experience client removed - title: Experience client removed event - description: An event emitted whenever a client is removed from experience. - tags: - - name: experience - - name: updated - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceClientRemovedEvent' - ExperienceConsentOptionAdded: - messageId: ExperienceConsentOptionAdded - name: Experience consent option added - title: Experience consent option added event - description: An event emitted whenever an consent option is added to experience. - tags: - - name: experience - - name: updated - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceConsentOptionAddedEvent' - ExperienceConsentOptionRemoved: - messageId: ExperienceConsentOptionRemoved - name: Experience consent option removed - title: Experience consent option removed event - description: An event emitted whenever an consent option is removed from experience. - tags: - - name: experience - - name: updated - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceConsentOptionRemovedEvent' -"; + var doc = new AsyncApiDocument(); + doc.Info = new AsyncApiInfo() + { + Description = "test description" + }; + doc.Servers.Add("production", new AsyncApiServer + { + Description = "test description", + Protocol = "pulsar+ssl", + Url = "example.com", + Bindings = new AsyncApiBindings() + { + Reference = new AsyncApiReference() + { + Type = ReferenceType.ServerBindings, + Id = "bindings", + }, + }, + }); + doc.Components = new AsyncApiComponents() + { + Channels = new Dictionary() + { + { "otherchannel", new AsyncApiChannel() + { + Publish = new AsyncApiOperation() + { + Description = "test", + }, + Bindings = new AsyncApiBindings() + { + Reference = new AsyncApiReference() + { + Type = ReferenceType.ChannelBindings, + Id = "bindings", + }, + }, + } + } + }, + ServerBindings = new Dictionary>() + { + { + "bindings", new AsyncApiBindings() + { + new PulsarServerBinding() + { + Tenant = "staging" + }, + } + } + }, + ChannelBindings = new Dictionary>() + { + { + "bindings", new AsyncApiBindings() + { + new PulsarChannelBinding() + { + Namespace = "users", + Persistence = AsyncAPI.Models.Bindings.Pulsar.Persistence.Persistent, + } + } + } + }, + }; + doc.Channels.Add("testChannel", + new AsyncApiChannel + { + Reference = new AsyncApiReference() + { + Type = ReferenceType.Channel, + Id = "otherchannel" + } + }); + var actual = doc.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); - var reader = new AsyncApiStringReader(); - var deserialized = reader.Read(spec, out var diagnostic); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.AddRange(BindingsCollection.Pulsar); + var reader = new AsyncApiStringReader(settings); + var deserialized = reader.Read(actual, out var diagnostic); } - + [Test] public void Serializev2_WithBindings_Serializes() { @@ -2219,18 +1300,20 @@ public void Serializev2_WithBindings_Serializes() }); var actual = doc.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); - var reader = new AsyncApiStringReader(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.AddRange(BindingsCollection.All); + var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); Assert.AreEqual(2, deserialized.Channels.First().Value.Publish.Message.First().Bindings.Count); var binding = deserialized.Channels.First().Value.Publish.Message.First().Bindings.First(); - Assert.AreEqual(BindingType.Http, binding.Key); + Assert.AreEqual("http", binding.Key); var httpBinding = binding.Value as HttpMessageBinding; Assert.AreEqual("this mah binding", httpBinding.Headers.Description); diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs index 7059b6fe..7e66eb06 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs @@ -1,18 +1,20 @@ -using FluentAssertions; -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Any; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Readers; -using LEGO.AsyncAPI.Readers.ParseNodes; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using YamlDotNet.RepresentationModel; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests { + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using FluentAssertions; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Any; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using NUnit.Framework; + using YamlDotNet.RepresentationModel; + public class AsyncApiLicenseTests { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index e2aeac00..a5d6a7a6 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Tests { using System; diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs new file mode 100644 index 00000000..5bf7379c --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs @@ -0,0 +1,124 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings +{ + using System.Collections.Generic; + using Extensions; + using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Any; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + using NUnit.Framework; + + public class NestedConfiguration : IAsyncApiExtensible + { + public string Name { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public static FixedFieldMap fixedFieldMap = new() + { + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + }; + + public void SerializeProperties(IAsyncApiWriter writer) + { + writer.WriteStartObject(); + writer.WriteOptionalProperty("name", this.Name); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } + + public class MyBinding : ChannelBinding + { + public string Custom { get; set; } + + public override string BindingKey => "my"; + + public NestedConfiguration NestedConfiguration { get; set; } + + public IAsyncApiAny Any { get; set; } + + protected override FixedFieldMap FixedFieldMap => new FixedFieldMap() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "custom", (a, n) => { a.Custom = n.GetScalarValue(); } }, + { "any", (a, n) => { a.Any = n.CreateAny(); } }, + { "nestedConfiguration", (a, n) => { a.NestedConfiguration = n.ParseMapWithExtensions(NestedConfiguration.fixedFieldMap); } }, + }; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + writer.WriteStartObject(); + writer.WriteRequiredProperty("custom", this.Custom); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteRequiredObject("any", this.Any, (w, p) => w.WriteAny(p)); + writer.WriteOptionalObject("nestedConfiguration", this.NestedConfiguration, (w, r) => r.SerializeProperties(w)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } + + public class CustomBinding_Should + { + [Test] + public void CustomBinding_SerializesDeserializes() + { + // Arrange + var expected = +@"bindings: + my: + custom: someValue + bindingVersion: 0.1.0 + any: + anyKeyName: anyValue + nestedConfiguration: + name: nested + x-myNestedExtension: nestedValue + x-myextension: someValue"; + + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new MyBinding + { + Custom = "someValue", + Any = new AsyncApiObject() + { + { "anyKeyName", new AsyncApiString("anyValue") }, + }, + BindingVersion = "0.1.0", + NestedConfiguration = new NestedConfiguration() + { + Name = "nested", + Extensions = new Dictionary() + { + { "x-myNestedExtension", new AsyncApiString("nestedValue") }, + }, + }, + Extensions = new Dictionary() + { + { "x-myextension", new AsyncApiString("someValue") }, + }, + }); + + // Act + var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(new MyBinding()); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(channel); + } + } +} diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs index 01ce4885..0b00bbf7 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs @@ -1,8 +1,11 @@ -namespace LEGO.AsyncAPI.Tests.Bindings.Http +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.Http { using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.Http; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -32,11 +35,12 @@ public void HttpMessageBinding_FilledObject_SerializesAndDeserializes() var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Http); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(message); } @@ -56,7 +60,7 @@ public void HttpOperationBinding_FilledObject_SerializesAndDeserializes() operation.Bindings.Add(new HttpOperationBinding { - Type = "request", + Type = HttpOperationBinding.HttpOperationType.Request, Method = "POST", Query = new AsyncApiSchema { @@ -68,11 +72,12 @@ public void HttpOperationBinding_FilledObject_SerializesAndDeserializes() var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Http); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(operation); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index 49e1e515..22bf8d2d 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -1,11 +1,15 @@ -namespace LEGO.AsyncAPI.Tests.Bindings.Kafka +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.Kafka { + using System.Collections.Generic; using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Readers; using NUnit.Framework; - using System.Collections.Generic; internal class KafkaBindings_Should { @@ -50,11 +54,12 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() // Assert actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(channel); } @@ -86,11 +91,12 @@ public void KafkaServerBinding_WithFilledObject_SerializesAndDeserializes() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(server); } @@ -124,11 +130,12 @@ public void KafkaMessageBinding_WithFilledObject_SerializesAndDeserializes() var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(message); } @@ -163,10 +170,12 @@ public void KafkaOperationBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(operation); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs index 05fd4f98..15577763 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs @@ -1,13 +1,14 @@ -using LEGO.AsyncAPI.Models.Bindings; - +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Bindings.Pulsar { + using System.Collections.Generic; using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Pulsar; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Bindings.Pulsar; using LEGO.AsyncAPI.Readers; using NUnit.Framework; - using System.Collections.Generic; internal class PulsarBindings_Should { @@ -59,10 +60,12 @@ public void PulsarChannelBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(channel); } @@ -76,10 +79,12 @@ public void PulsarChannelBindingNamespaceDefaultToNull() persistence: persistent"; // Act - // Assert - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - Assert.AreEqual(null, ((PulsarChannelBinding)binding.Bindings[BindingType.Pulsar]).Namespace); + // Assert + Assert.AreEqual(null, ((PulsarChannelBinding)binding.Bindings["pulsar"]).Namespace); } [Test] @@ -93,9 +98,12 @@ public void PulsarChannelBindingPropertiesExceptNamespaceDefaultToNull() // Act // Assert - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - var pulsarBinding = ((PulsarChannelBinding) binding.Bindings[BindingType.Pulsar]); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var pulsarBinding = ((PulsarChannelBinding)binding.Bindings["pulsar"]); + Assert.AreEqual("staging", pulsarBinding.Namespace); Assert.AreEqual(null, pulsarBinding.Persistence); Assert.AreEqual(null, pulsarBinding.Compaction); Assert.AreEqual(null, pulsarBinding.GeoReplication); @@ -130,11 +138,12 @@ public void PulsarServerBinding_WithFilledObject_SerializesAndDeserializes() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(server); } @@ -165,12 +174,13 @@ public void ServerBindingVersionDefaultsToNull() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); - Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings[BindingType.Pulsar]).BindingVersion); + Assert.AreEqual(expected, actual); + Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings["pulsar"]).BindingVersion); binding.Should().BeEquivalentTo(server); } @@ -201,12 +211,13 @@ public void ServerTenantDefaultsToNull() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); - Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings[BindingType.Pulsar]).Tenant); + Assert.AreEqual(expected, actual); + Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings["pulsar"]).Tenant); binding.Should().BeEquivalentTo(server); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs index 0f934b91..2a57b997 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs @@ -1,8 +1,11 @@ -namespace LEGO.AsyncAPI.Tests.Bindings.WebSockets +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.WebSockets { using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.WebSockets; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.WebSockets; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -40,10 +43,12 @@ public void WebSocketChannelBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Websockets); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(channel); } } diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index c55609e5..692c8e06 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -28,6 +28,7 @@ + @@ -41,6 +42,10 @@ + + + + diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs index dfaa6764..05f91943 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs @@ -1,9 +1,11 @@ -namespace LEGO.AsyncAPI.Tests.Models +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Models { using System.Collections.Generic; + using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Bindings.WebSockets; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.Kafka; - using LEGO.AsyncAPI.Models.Bindings.WebSockets; using LEGO.AsyncAPI.Models.Interfaces; using NUnit.Framework; @@ -69,7 +71,7 @@ public void AsyncApiChannel_WithWebSocketsBinding_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -104,7 +106,7 @@ public void AsyncApiChannel_WithKafkaBinding_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs index 817e2bc3..4cdbf307 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiContact_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs index 0721426c..e6720030 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiExternalDocumentation_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs index 31fdb8d0..7c4e9a73 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiInfo_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs index 5cd8f59a..eb412d21 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiLicense_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs index 3df3266e..b6f40e8c 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiMessageExample_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index d2dabcd3..f86a9ad4 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -1,13 +1,15 @@ -namespace LEGO.AsyncAPI.Tests.Models +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Models { using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Any; - using LEGO.AsyncAPI.Models.Bindings; - using LEGO.AsyncAPI.Models.Bindings.Http; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -90,7 +92,7 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); message.Should().BeEquivalentTo(deserializedMessage); } @@ -131,7 +133,7 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); message.Should().BeEquivalentTo(deserializedMessage); } @@ -275,7 +277,7 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Bindings = new AsyncApiBindings() { { - BindingType.Http, new HttpMessageBinding + "http", new HttpMessageBinding { Headers = new AsyncApiSchema { @@ -370,10 +372,12 @@ public void AsyncApiMessage_WithFilledObject_Serializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.All); + var deserializedMessage = new AsyncApiStringReader(settings).ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); message.Should().BeEquivalentTo(deserializedMessage); } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs index 207fe140..93fc79e1 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiOAuthFlow_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs index f0778cec..8d8bd4f5 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs @@ -1,15 +1,17 @@ -using System; -using System.Globalization; -using System.IO; -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Bindings.Http; -using LEGO.AsyncAPI.Models.Bindings.Kafka; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using System.Globalization; + using System.IO; + using LEGO.AsyncAPI.Bindings.Http; + using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + using NUnit.Framework; + public class AsyncApiOperation_Should { [Test] @@ -46,7 +48,7 @@ public void SerializeV2_WithMultipleMessages_SerializesWithOneOf() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -69,7 +71,7 @@ public void SerializeV2_WithSingleMessage_Serializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -78,7 +80,7 @@ public void AsyncApiOperation_WithBindings_Serializes() var expected = @"bindings: http: - type: type + type: request method: PUT query: description: some query @@ -95,7 +97,7 @@ public void AsyncApiOperation_WithBindings_Serializes() { new HttpOperationBinding { - Type = "type", + Type = HttpOperationBinding.HttpOperationType.Request, Method = "PUT", Query = new AsyncApiSchema { @@ -125,7 +127,7 @@ public void AsyncApiOperation_WithBindings_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 57101ed9..9395373d 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -1,13 +1,15 @@ -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Writers; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Writers; + using NUnit.Framework; + public class AsyncApiSchema_Should { private string NoInlinedReferences => @@ -150,7 +152,7 @@ public void Serialize_WithInliningOptions_ShouldInlineAccordingly(bool shouldInl expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs index f8872233..b92224df 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs @@ -1,9 +1,12 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiSecurityRequirement_Should { [Test] @@ -16,5 +19,14 @@ public void SerializeV2_WithNullWriter_Throws() // Assert Assert.Throws(() => { asyncApiSecurityRequirement.SerializeV2(null); }); } + + [Test] + public void SerializeV2_Serializes() + { + var asyncApiSecurityRequirement = new AsyncApiSecurityRequirement(); + asyncApiSecurityRequirement.Add(new AsyncApiSecurityScheme { Type = SecuritySchemeType.ApiKey }, new List { "string" }); + + var output = asyncApiSecurityRequirement.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs index 52010434..880694d6 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs @@ -1,11 +1,13 @@ -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Bindings.Kafka; -using LEGO.AsyncAPI.Models.Interfaces; -using NUnit.Framework; -using System.Collections.Generic; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System.Collections.Generic; + using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using NUnit.Framework; + internal class AsyncApiServer_Should { [Test] @@ -71,7 +73,7 @@ public void AsyncApiServer_Serializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -104,7 +106,7 @@ public void AsyncApiServer_WithKafkaBinding_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } } } diff --git a/test/LEGO.AsyncAPI.Tests/StringExtensions.cs b/test/LEGO.AsyncAPI.Tests/StringExtensions.cs index 6a3c89fd..2f0fddb8 100644 --- a/test/LEGO.AsyncAPI.Tests/StringExtensions.cs +++ b/test/LEGO.AsyncAPI.Tests/StringExtensions.cs @@ -1,4 +1,6 @@ -namespace LEGO.AsyncAPI.Tests +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests { using System; diff --git a/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs b/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs index 70929de1..48446738 100644 --- a/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs +++ b/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs @@ -1,8 +1,10 @@ -using LEGO.AsyncAPI.Validations; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Validation { + using LEGO.AsyncAPI.Validations; + using NUnit.Framework; + public class ValidationRuleSetTests { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/stylecop.json b/test/LEGO.AsyncAPI.Tests/stylecop.json new file mode 100644 index 00000000..0a8f4661 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/stylecop.json @@ -0,0 +1,15 @@ +{ + // ACTION REQUIRED: This file was automatically added to your project, but it + // will not take effect until additional steps are taken to enable it. See the + // following page for additional information: + // + // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md + + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "The LEGO Group", + "xmlHeader": false + } + } +} From d24248441bb7014f7f39a5ac9b56265ed20ed825 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Mon, 5 Jun 2023 09:26:39 +0200 Subject: [PATCH 33/39] ci: force pre-releases --- .github/workflows/release-internal.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release-internal.yml b/.github/workflows/release-internal.yml index 66df465a..fef5c2e4 100644 --- a/.github/workflows/release-internal.yml +++ b/.github/workflows/release-internal.yml @@ -20,6 +20,7 @@ jobs: uses: actions/checkout@v1 - name: Semantic Release + id: semantic uses: cycjimmy/semantic-release-action@v3 with: dry_run: true @@ -27,8 +28,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} outputs: - trigger_release: ${{ steps.semantic.outputs.new_release_published }} - version: ${{ steps.semantic.outputs.new_release_published == 'true' && steps.semantic.outputs.new_release_version }} + version: ${{ steps.semantic.outputs.new_release_version }} pre-release: runs-on: ubuntu-latest @@ -43,13 +43,10 @@ jobs: uses: actions/checkout@v1 - name: Setup .NET Core @ Latest - if: needs.check.outputs.trigger_release == 'true' uses: actions/setup-dotnet@v1 - name: Build ${{ matrix.package-name }} project and pack NuGet package - if: needs.check.outputs.trigger_release == 'true' run: dotnet pack src/${{ matrix.package-name }}/${{ matrix.package-name }}.csproj -c Release -o out-${{ matrix.package-name }} -p:PackageVersion=${{ needs.check.outputs.version }}-beta - name: Push generated package to GitHub Packages registry - if: needs.check.outputs.trigger_release == 'true' run: dotnet nuget push out-${{ matrix.package-name }}/*.nupkg -s https://api.nuget.org/v3/index.json --skip-duplicate -n --api-key ${{secrets.NUGET}} From 2e4bf25f435e71211ebfba5318eddc76bb1905de Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Mon, 5 Jun 2023 09:47:25 +0200 Subject: [PATCH 34/39] chore: update readme with pre-release shields --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cacd288a..199a055c 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,16 @@ The AsyncAPI.NET SDK contains a useful object model for the AsyncAPI specificati ## Installation Install the NuGet packages: +### AsyncAPI.NET +[![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET?label=AsyncAPI.NET&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) +[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET?label=AsyncAPI.NET&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) +### AsyncAPI.NET.Readers [![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Readers?label=AsyncAPI.NET.Readers&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) -[![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET?label=AsyncAPI.NET&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) +[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET.Readers?label=AsyncAPI.NET.Readers&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) + +### AsyncAPI.NET.Bindings +[![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Bindings?label=AsyncAPI.NET.Bindings&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Bindings/) ## Example Usage From 2c934e7be3c5f7f44c8051a61017eb3474b0461d Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 9 Jun 2023 12:08:14 +0100 Subject: [PATCH 35/39] feat: update nested extensible objects and tests --- src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs | 6 +- .../Sns/DeliveryPolicy.cs | 6 +- .../Sns/FilterPolicy.cs | 7 +- src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs | 7 +- src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs | 6 +- src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs | 7 +- .../Sns/RedrivePolicy.cs | 9 +- .../Sns/SnsChannelBinding.cs | 7 +- .../Sns/SnsOperationBinding.cs | 17 +- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 5 +- .../Bindings/Sns/SnsBindings_Should.cs | 165 +++++++++++++++++- 11 files changed, 217 insertions(+), 25 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs index 7b7fab89..a7390502 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs @@ -4,8 +4,9 @@ namespace LEGO.AsyncAPI.Bindings.Sns using LEGO.AsyncAPI.Attributes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; + using System.Collections.Generic; - public class Consumer : IAsyncApiElement + public class Consumer : IAsyncApiExtensible { /// /// What protocol will this endpoint receive messages by? @@ -41,6 +42,8 @@ public class Consumer : IAsyncApiElement /// The display name to use with an SNS subscription /// public string DisplayName { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) { @@ -57,6 +60,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteOptionalObject("redrivePolicy", this.RedrivePolicy, (w, p) => p.Serialize(w)); writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); writer.WriteOptionalProperty("displayName", this.DisplayName); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs index 5fff723b..2cb93290 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs @@ -4,8 +4,9 @@ namespace LEGO.AsyncAPI.Bindings.Sns using LEGO.AsyncAPI.Attributes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; + using System.Collections.Generic; - public class DeliveryPolicy : IAsyncApiElement + public class DeliveryPolicy : IAsyncApiExtensible { /// /// The minimum delay for a retry in seconds. @@ -46,6 +47,8 @@ public class DeliveryPolicy : IAsyncApiElement /// The maximum number of deliveries per second, per subscription. /// public int? MaxReceivesPerSecond { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) { @@ -63,6 +66,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteOptionalProperty("numMaxDelayRetries", this.NumMaxDelayRetries); writer.WriteOptionalProperty("backoffFunction", this.BackoffFunction.GetDisplayName()); writer.WriteOptionalProperty("maxReceivesPerSecond", this.MaxReceivesPerSecond); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs index db4e0415..717e94fc 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs @@ -3,13 +3,17 @@ namespace LEGO.AsyncAPI.Bindings.Sns using System; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; + using System.Collections.Generic; - public class FilterPolicy : IAsyncApiElement + + public class FilterPolicy : IAsyncApiExtensible { /// /// A map of a message attribute to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint. /// public IAsyncApiAny Attributes { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) { @@ -20,6 +24,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredObject("attributes", this.Attributes, (w, a) => w.WriteAny(a)); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs index 86b09e45..c253e51e 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs @@ -3,8 +3,10 @@ namespace LEGO.AsyncAPI.Bindings.Sns using System; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; + using System.Collections.Generic; - public class Identifier : IAsyncApiElement + + public class Identifier : IAsyncApiExtensible { public string Url { get; set; } @@ -15,6 +17,8 @@ public class Identifier : IAsyncApiElement public string Arn { get; set; } public string Name { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) { @@ -29,6 +33,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteOptionalProperty("phone", this.Phone); writer.WriteOptionalProperty("arn", this.Arn); writer.WriteOptionalProperty("name", this.Name); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs index b0352f01..ff2b26e7 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs @@ -4,8 +4,9 @@ namespace LEGO.AsyncAPI.Bindings.Sns using LEGO.AsyncAPI.Attributes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; + using System.Collections.Generic; - public class Ordering : IAsyncApiElement + public class Ordering : IAsyncApiExtensible { /// /// What type of SNS Topic is this? @@ -16,6 +17,8 @@ public class Ordering : IAsyncApiElement /// True to turn on de-duplication of messages for a channel. /// public bool ContentBasedDeduplication { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) { @@ -27,6 +30,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("type", this.Type.GetDisplayName()); writer.WriteOptionalProperty("contentBasedDeduplication", this.ContentBasedDeduplication); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs index a51bdf46..123ba944 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs @@ -6,13 +6,15 @@ namespace LEGO.AsyncAPI.Bindings.Sns using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - public class Policy : IAsyncApiElement + public class Policy : IAsyncApiExtensible { /// /// An array of statement objects, each of which controls a permission for this topic. /// public List Statements { get; set; } - + + public IDictionary Extensions { get; set; } = new Dictionary(); + public void Serialize(IAsyncApiWriter writer) { if (writer is null) @@ -22,6 +24,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteOptionalCollection("statements", this.Statements, (w, t) => t.Serialize(w)); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs index 82ea2810..8de67d60 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs @@ -3,8 +3,10 @@ namespace LEGO.AsyncAPI.Bindings.Sns using System; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; + using System.Collections.Generic; - public class RedrivePolicy : IAsyncApiElement + + public class RedrivePolicy : IAsyncApiExtensible { /// /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. @@ -15,7 +17,9 @@ public class RedrivePolicy : IAsyncApiElement /// The number of times a message is delivered to the source queue before being moved to the dead-letter queue. /// public int? MaxReceiveCount { get; set; } - + + public IDictionary Extensions { get; set; } = new Dictionary(); + public void Serialize(IAsyncApiWriter writer) { if (writer is null) @@ -26,6 +30,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredObject("deadLetterQueue", this.DeadLetterQueue, (w, q) => q.Serialize(w)); writer.WriteOptionalProperty("maxReceiveCount", this.MaxReceiveCount); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 36c61022..237e6765 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -40,8 +40,8 @@ public class SnsChannelBinding : ChannelBinding protected override FixedFieldMap FixedFieldMap => new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, - { "type", (a, n) => { a.Ordering = n.ParseMap(this.orderingFixedFields); } }, - { "policy", (a, n) => { a.Policy = n.ParseMap(this.policyFixedFields); } }, + { "type", (a, n) => { a.Ordering = n.ParseMapWithExtensions(this.orderingFixedFields); } }, + { "policy", (a, n) => { a.Policy = n.ParseMapWithExtensions(this.policyFixedFields); } }, { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, }; @@ -53,7 +53,7 @@ public class SnsChannelBinding : ChannelBinding private FixedFieldMap policyFixedFields = new() { - { "statements", (a, n) => { a.Statements = n.CreateList(s => s.ParseMap(statementFixedFields)); } }, + { "statements", (a, n) => { a.Statements = n.CreateList(s => s.ParseMapWithExtensions(statementFixedFields)); } }, }; private static FixedFieldMap statementFixedFields = new() @@ -91,6 +91,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteOptionalObject("ordering", this.Ordering, (w, t) => t.Serialize(w)); writer.WriteOptionalObject("policy", this.Policy, (w, t) => t.Serialize(w)); writer.WriteOptionalMap("tags", this.Tags, (w, t) => w.WriteValue(t)); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs index 78917ee4..d35a46f5 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -30,9 +30,9 @@ public class SnsOperationBinding : OperationBinding protected override FixedFieldMap FixedFieldMap => new() { - { "topic", (a, n) => { a.Topic = n.ParseMap(this.identifierFixFields); } }, - { "consumers", (a, n) => { a.Consumers = n.CreateList(s => s.ParseMap(this.consumerFixedFields)); } }, - { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMap(this.deliveryPolicyFixedFields); } }, + { "topic", (a, n) => { a.Topic = n.ParseMapWithExtensions(this.identifierFixFields); } }, + { "consumers", (a, n) => { a.Consumers = n.CreateList(s => s.ParseMapWithExtensions(this.consumerFixedFields)); } }, + { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMapWithExtensions(this.deliveryPolicyFixedFields); } }, }; private FixedFieldMap identifierFixFields => new() @@ -47,11 +47,11 @@ public class SnsOperationBinding : OperationBinding private FixedFieldMap consumerFixedFields => new () { { "protocol", (a, n) => { a.Protocol = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "endpoint", (a, n) => { a.Endpoint = n.ParseMap(this.identifierFixFields); } }, - { "filterPolicy", (a, n) => { a.FilterPolicy = n.ParseMap(this.filterPolicyFixedFields); } }, + { "endpoint", (a, n) => { a.Endpoint = n.ParseMapWithExtensions(this.identifierFixFields); } }, + { "filterPolicy", (a, n) => { a.FilterPolicy = n.ParseMapWithExtensions(this.filterPolicyFixedFields); } }, { "rawMessageDelivery", (a, n) => { a.RawMessageDelivery = n.GetBooleanValue(); } }, - { "redrivePolicy", (a, n) => { a.RedrivePolicy = n.ParseMap(this.redrivePolicyFixedFields); } }, - { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMap(this.deliveryPolicyFixedFields); } }, + { "redrivePolicy", (a, n) => { a.RedrivePolicy = n.ParseMapWithExtensions(this.redrivePolicyFixedFields); } }, + { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMapWithExtensions(this.deliveryPolicyFixedFields); } }, { "displayName", (a, n) => { a.DisplayName = n.GetScalarValue(); } }, }; @@ -62,7 +62,7 @@ public class SnsOperationBinding : OperationBinding private FixedFieldMap redrivePolicyFixedFields => new() { - { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMap(identifierFixFields); } }, + { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMapWithExtensions(identifierFixFields); } }, { "maxReceiveCount", (a, n) => { a.MaxReceiveCount = n.GetIntegerValue(); } }, }; @@ -89,6 +89,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteOptionalObject("topic", this.Topic, (w, t) => t.Serialize(w)); writer.WriteOptionalCollection("consumers", this.Consumers, (w, c) => c.Serialize(w)); writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 201c663b..ed7f71da 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -7,7 +7,7 @@ namespace LEGO.AsyncAPI.Bindings.Sns using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - public class Statement : IAsyncApiElement + public class Statement : IAsyncApiExtensible { public Effect Effect { get; set; } @@ -22,6 +22,8 @@ public class Statement : IAsyncApiElement /// The SNS permission being allowed or denied e.g. sns:Publish /// public StringOrStringList Action { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) { @@ -34,6 +36,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Serialize(w)); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index cf34ba59..14a2bab3 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -1,5 +1,6 @@ using System; using LEGO.AsyncAPI.Models.Any; +using LEGO.AsyncAPI.Models.Interfaces; using BindingsCollection = LEGO.AsyncAPI.Bindings.BindingsCollection; namespace LEGO.AsyncAPI.Tests.Bindings.Sns @@ -18,13 +19,15 @@ internal class SnsBindings_Should public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange - var expected = + var expected = @"bindings: sns: name: myTopic ordering: type: FIFO contentBasedDeduplication: true + x-orderingExtension: + orderingXPropertyName: orderingXPropertyValue policy: statements: - effect: Deny @@ -37,9 +40,15 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() - arn:aws:iam::123456789012:user/alex.wichmann - arn:aws:iam::123456789012:user/dec.kolakowski action: sns:Create + x-statementExtension: + statementXPropertyName: statementXPropertyValue + x-policyExtension: + policyXPropertyName: policyXPropertyValue tags: owner: AsyncAPI.NET - platform: AsyncAPIOrg"; + platform: AsyncAPIOrg + x-bindingExtension: + bindingXPropertyName: bindingXPropertyValue"; var channel = new AsyncApiChannel(); channel.Bindings.Add(new SnsChannelBinding() @@ -49,6 +58,16 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { Type = OrderingType.Fifo, ContentBasedDeduplication = true, + Extensions = new Dictionary() + { + { + "x-orderingExtension", + new AsyncApiObject() + { + { "orderingXPropertyName", new AsyncApiString("orderingXPropertyValue") }, + } + }, + }, }, Policy = new Policy() { @@ -85,6 +104,26 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { StringValue = "sns:Create", }, + Extensions = new Dictionary() + { + { + "x-statementExtension", + new AsyncApiObject() + { + { "statementXPropertyName", new AsyncApiString("statementXPropertyValue") }, + } + }, + }, + }, + }, + Extensions = new Dictionary() + { + { + "x-policyExtension", + new AsyncApiObject() + { + { "policyXPropertyName", new AsyncApiString("policyXPropertyValue") }, + } }, }, }, @@ -93,6 +132,16 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { "owner", "AsyncAPI.NET" }, { "platform", "AsyncAPIOrg" }, }, + Extensions = new Dictionary() + { + { + "x-bindingExtension", + new AsyncApiObject() + { + { "bindingXPropertyName", new AsyncApiString("bindingXPropertyValue") }, + } + }, + }, }); // Act @@ -121,10 +170,14 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() sns: topic: name: someTopic + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue consumers: - protocol: sqs endpoint: name: someQueue + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue filterPolicy: attributes: store: @@ -138,11 +191,17 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() - rugby - football - baseball + x-filterPolicyExtension: + filterPolicyXPropertyName: filterPolicyXPropertyValue rawMessageDelivery: false redrivePolicy: deadLetterQueue: arn: arn:aws:SQS:eu-west-1:0000000:123456789 + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue maxReceiveCount: 25 + x-redrivePolicyExtension: + redrivePolicyXPropertyName: redrivePolicyXPropertyValue deliveryPolicy: minDelayTarget: 10 maxDelayTarget: 100 @@ -152,6 +211,10 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() numMaxDelayRetries: 5 backoffFunction: linear maxReceivesPerSecond: 2 + x-deliveryPolicyExtension: + deliveryPolicyXPropertyName: deliveryPolicyXPropertyValue + x-consumerExtension: + consumerXPropertyName: consumerXPropertyValue deliveryPolicy: minDelayTarget: 10 maxDelayTarget: 100 @@ -160,7 +223,11 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() numMinDelayRetries: 3 numMaxDelayRetries: 5 backoffFunction: geometric - maxReceivesPerSecond: 10"; + maxReceivesPerSecond: 10 + x-deliveryPolicyExtension: + deliveryPolicyXPropertyName: deliveryPolicyXPropertyValue + x-bindingExtension: + bindingXPropertyName: bindingXPropertyValue"; var operation = new AsyncApiOperation(); operation.Bindings.Add(new SnsOperationBinding() @@ -168,6 +235,16 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() Topic = new Identifier() { Name = "someTopic", + Extensions = new Dictionary() + { + { + "x-identifierExtension", + new AsyncApiObject() + { + { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + } + }, + }, }, Consumers = new List() { @@ -177,6 +254,16 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() Endpoint = new Identifier() { Name = "someQueue", + Extensions = new Dictionary() + { + { + "x-identifierExtension", + new AsyncApiObject() + { + { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + } + }, + }, }, FilterPolicy = new FilterPolicy() { @@ -208,15 +295,45 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() } }, }, + Extensions = new Dictionary() + { + { + "x-filterPolicyExtension", + new AsyncApiObject() + { + { "filterPolicyXPropertyName", new AsyncApiString("filterPolicyXPropertyValue") }, + } + }, + }, }, RawMessageDelivery = false, RedrivePolicy = new RedrivePolicy() { DeadLetterQueue = new Identifier() { - Arn = "arn:aws:SQS:eu-west-1:0000000:123456789" + Arn = "arn:aws:SQS:eu-west-1:0000000:123456789", + Extensions = new Dictionary() + { + { + "x-identifierExtension", + new AsyncApiObject() + { + { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + } + }, + }, }, MaxReceiveCount = 25, + Extensions = new Dictionary() + { + { + "x-redrivePolicyExtension", + new AsyncApiObject() + { + { "redrivePolicyXPropertyName", new AsyncApiString("redrivePolicyXPropertyValue") }, + } + }, + }, }, DeliveryPolicy = new DeliveryPolicy() { @@ -228,6 +345,26 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() NumMaxDelayRetries = 5, BackoffFunction = BackoffFunction.Linear, MaxReceivesPerSecond = 2, + Extensions = new Dictionary() + { + { + "x-deliveryPolicyExtension", + new AsyncApiObject() + { + { "deliveryPolicyXPropertyName", new AsyncApiString("deliveryPolicyXPropertyValue") }, + } + }, + }, + }, + Extensions = new Dictionary() + { + { + "x-consumerExtension", + new AsyncApiObject() + { + { "consumerXPropertyName", new AsyncApiString("consumerXPropertyValue") }, + } + }, }, }, }, @@ -241,6 +378,26 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() NumMaxDelayRetries = 5, BackoffFunction = BackoffFunction.Geometric, MaxReceivesPerSecond = 10, + Extensions = new Dictionary() + { + { + "x-deliveryPolicyExtension", + new AsyncApiObject() + { + { "deliveryPolicyXPropertyName", new AsyncApiString("deliveryPolicyXPropertyValue") }, + } + }, + }, + }, + Extensions = new Dictionary() + { + { + "x-bindingExtension", + new AsyncApiObject() + { + { "bindingXPropertyName", new AsyncApiString("bindingXPropertyValue") }, + } + }, }, }); From 6c4b0da5fafadcc95d5e5d46910b42818758b659 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 9 Jun 2023 14:13:01 +0100 Subject: [PATCH 36/39] feat: integrate StringOrStringList to SNS --- .../Sns/SnsChannelBinding.cs | 19 ++--------- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 34 ++----------------- .../Bindings/Pulsar/PulsarBindings_Should.cs | 2 +- .../Bindings/Sns/SnsBindings_Should.cs | 32 ++++++----------- .../Bindings/Sqs/SqsBindings_should.cs | 4 +-- 5 files changed, 17 insertions(+), 74 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 237e6765..9a79333a 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -59,24 +59,9 @@ public class SnsChannelBinding : ChannelBinding private static FixedFieldMap statementFixedFields = new() { { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = LoadStringOrStringList(n, "principal"); } }, - { "action", (a, n) => { a.Action = LoadStringOrStringList(n, "action"); } }, + { "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } }, + { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, }; - - public static StringOrStringList LoadStringOrStringList(ParseNode node, string nodeName) - { - var stringOrStringList = new StringOrStringList(); - if (node is ValueNode) - { - stringOrStringList.StringValue = node.GetScalarValue(); - } - else - { - stringOrStringList.StringList = node.CreateSimpleList(s => s.GetScalarValue()); - } - - return stringOrStringList; - } /// public override void SerializeProperties(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index ed7f71da..21ac7c7b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -34,8 +34,8 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); - writer.WriteRequiredObject("action", this.Action, (w, t) => t.Serialize(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } @@ -48,34 +48,4 @@ public enum Effect [Display("Deny")] Deny, } - - public class StringOrStringList : IAsyncApiElement - { - public string StringValue { get; set; } - - public List StringList { get; set; } - - public void Serialize(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.StringValue != null) - { - writer.WriteValue(this.StringValue); - } - else - { - writer.WriteStartArray(); - foreach (var v in this.StringList) - { - writer.WriteValue(v); - } - - writer.WriteEndArray(); - } - } - } } \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs index 899d55ca..15577763 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs @@ -30,7 +30,7 @@ public void PulsarChannelBinding_WithFilledObject_SerializesAndDeserializes() size: 1000 ttl: 360 deduplication: true - bindingVersion: '0.1.0'"; + bindingVersion: 0.1.0"; var channel = new AsyncApiChannel(); channel.Bindings.Add(new PulsarChannelBinding diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 14a2bab3..7a2269bb 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -76,34 +76,22 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new StringOrStringList() + Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Action = new StringOrStringList(new AsyncApiArray() { - StringValue = "arn:aws:iam::123456789012:user/alex.wichmann", - }, - Action = new StringOrStringList() - { - StringList = new List() - { - "sns:Publish", - "sns:Delete", - }, - }, + new AsyncApiString("sns:Publish"), + new AsyncApiString("sns:Delete") + }), }, new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList() + Principal = new StringOrStringList(new AsyncApiArray() { - StringList = new List() - { - "arn:aws:iam::123456789012:user/alex.wichmann", - "arn:aws:iam::123456789012:user/dec.kolakowski", - }, - }, - Action = new StringOrStringList() - { - StringValue = "sns:Create", - }, + new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann"), + new AsyncApiString("arn:aws:iam::123456789012:user/dec.kolakowski") + }), + Action = new StringOrStringList(new AsyncApiString("sns:Create")), Extensions = new Dictionary() { { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index b2cd0eb2..34c361e1 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -190,8 +190,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), Action = new StringOrStringList(new AsyncApiArray() { - new AsyncApiString("sqs:*") - }) + new AsyncApiString("sqs:*"), + }), }, }, }, From cd94f2c0e5a4bbabaf3d2ae54ffd237dcbc75f9f Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Fri, 9 Jun 2023 14:15:05 +0100 Subject: [PATCH 37/39] feat: reformat files --- src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs | 4 ++-- src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs | 6 +++--- src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs | 5 ++--- src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs | 5 ++--- src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs | 4 ++-- src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs | 5 ++--- src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs | 4 ---- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 2 +- 9 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs index a7390502..737a78c7 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs @@ -1,10 +1,10 @@ namespace LEGO.AsyncAPI.Bindings.Sns { using System; + using System.Collections.Generic; using LEGO.AsyncAPI.Attributes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using System.Collections.Generic; public class Consumer : IAsyncApiExtensible { @@ -42,7 +42,7 @@ public class Consumer : IAsyncApiExtensible /// The display name to use with an SNS subscription /// public string DisplayName { get; set; } - + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs index 2cb93290..f9421ca9 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs @@ -1,11 +1,11 @@ namespace LEGO.AsyncAPI.Bindings.Sns { using System; + using System.Collections.Generic; using LEGO.AsyncAPI.Attributes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using System.Collections.Generic; - + public class DeliveryPolicy : IAsyncApiExtensible { /// @@ -47,7 +47,7 @@ public class DeliveryPolicy : IAsyncApiExtensible /// The maximum number of deliveries per second, per subscription. /// public int? MaxReceivesPerSecond { get; set; } - + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs index 717e94fc..47530cc0 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs @@ -1,10 +1,9 @@ namespace LEGO.AsyncAPI.Bindings.Sns { using System; + using System.Collections.Generic; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using System.Collections.Generic; - public class FilterPolicy : IAsyncApiExtensible { @@ -12,7 +11,7 @@ public class FilterPolicy : IAsyncApiExtensible /// A map of a message attribute to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint. /// public IAsyncApiAny Attributes { get; set; } - + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs index c253e51e..0e6466b0 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs @@ -1,10 +1,9 @@ namespace LEGO.AsyncAPI.Bindings.Sns { using System; + using System.Collections.Generic; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using System.Collections.Generic; - public class Identifier : IAsyncApiExtensible { @@ -17,7 +16,7 @@ public class Identifier : IAsyncApiExtensible public string Arn { get; set; } public string Name { get; set; } - + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs index ff2b26e7..4ed81eec 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs @@ -1,10 +1,10 @@ namespace LEGO.AsyncAPI.Bindings.Sns { using System; + using System.Collections.Generic; using LEGO.AsyncAPI.Attributes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using System.Collections.Generic; public class Ordering : IAsyncApiExtensible { diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs index 123ba944..685b2f8d 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs @@ -1,7 +1,7 @@ namespace LEGO.AsyncAPI.Bindings.Sns { - using System.Collections.Generic; using System; + using System.Collections.Generic; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; @@ -12,7 +12,7 @@ public class Policy : IAsyncApiExtensible /// An array of statement objects, each of which controls a permission for this topic. /// public List Statements { get; set; } - + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs index 8de67d60..4e5e6340 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs @@ -1,10 +1,9 @@ namespace LEGO.AsyncAPI.Bindings.Sns { using System; + using System.Collections.Generic; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using System.Collections.Generic; - public class RedrivePolicy : IAsyncApiExtensible { @@ -17,7 +16,7 @@ public class RedrivePolicy : IAsyncApiExtensible /// The number of times a message is delivered to the source queue before being moved to the dead-letter queue. /// public int? MaxReceiveCount { get; set; } - + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 9a79333a..a0df69a9 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -1,14 +1,10 @@ namespace LEGO.AsyncAPI.Bindings.Sns { - using LEGO.AsyncAPI.Models.Interfaces; using System; using System.Collections.Generic; - using System.Linq; using LEGO.AsyncAPI.Bindings; - using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; - using YamlDotNet.Core.Tokens; /// /// Binding class for SNS channel settings. diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 21ac7c7b..c21ecc80 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -22,7 +22,7 @@ public class Statement : IAsyncApiExtensible /// The SNS permission being allowed or denied e.g. sns:Publish /// public StringOrStringList Action { get; set; } - + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) From 8f08e2bb443597b6aadbda0680557922f8e65abf Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Mon, 12 Jun 2023 10:04:34 +0100 Subject: [PATCH 38/39] feat: update consumer description wording --- src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs index 737a78c7..a4981710 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs @@ -9,12 +9,12 @@ namespace LEGO.AsyncAPI.Bindings.Sns public class Consumer : IAsyncApiExtensible { /// - /// What protocol will this endpoint receive messages by? + /// The protocol this endpoint will receive messages by. /// public Protocol Protocol { get; set; } /// - /// Where are messages being delivered to? + /// The endpoint messages are delivered to. /// public Identifier Endpoint { get; set; } @@ -39,7 +39,7 @@ public class Consumer : IAsyncApiExtensible public DeliveryPolicy DeliveryPolicy { get; set; } /// - /// The display name to use with an SNS subscription + /// The display name to use with an SNS subscription. /// public string DisplayName { get; set; } From da4e8d622701e972dfdbd40f1ea63b8847284382 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski Date: Mon, 12 Jun 2023 10:13:40 +0100 Subject: [PATCH 39/39] feat: match wording to spec --- src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs index a4981710..46548977 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Bindings.Sns public class Consumer : IAsyncApiExtensible { /// - /// The protocol this endpoint will receive messages by. + /// The protocol that this endpoint will receive messages by. /// public Protocol Protocol { get; set; }