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 + } + } +}