diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs index 38b21da9..fca3b30c 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs @@ -39,25 +39,25 @@ public static Condition Parse(ParseNode node) switch (node) { case MapNode mapNode: - { - var conditionValues = new Dictionary>(); - foreach (var conditionNode in mapNode) { - switch (conditionNode.Value) + var conditionValues = new Dictionary>(); + foreach (var conditionNode in mapNode) { - case MapNode conditionValueNode: - conditionValues.Add(conditionNode.Name, new Dictionary(conditionValueNode.Select(x => - new KeyValuePair(x.Name, StringOrStringList.Parse(x.Value))) - .ToDictionary(x => x.Key, x => x.Value))); - break; - default: - throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " + - $"AWS condition values should be one or more key value pairs."); + switch (conditionNode.Value) + { + case MapNode conditionValueNode: + conditionValues.Add(conditionNode.Name, new Dictionary(conditionValueNode.Select(x => + new KeyValuePair(x.Name, StringOrStringList.Parse(x.Value))) + .ToDictionary(x => x.Key, x => x.Value))); + break; + default: + throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " + + $"AWS condition values should be one or more key value pairs."); + } } - } - return new Condition(conditionValues); - } + return new Condition(conditionValues); + } default: throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " + diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs index a803f6c2..2d630641 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sns; using System; @@ -28,20 +30,20 @@ public static Principal Parse(ParseNode node) return new PrincipalStar(); case MapNode mapNode: - { - var propertyNode = mapNode.First(); - if (!IsValidPrincipalProperty(propertyNode.Name)) { - throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + - $"Node should contain a valid AWS principal property name."); - } + var propertyNode = mapNode.First(); + if (!IsValidPrincipalProperty(propertyNode.Name)) + { + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Node should contain a valid AWS principal property name."); + } - var principalValue = new KeyValuePair( - propertyNode.Name, - StringOrStringList.Parse(propertyNode.Value)); + var principalValue = new KeyValuePair( + propertyNode.Name, + StringOrStringList.Parse(propertyNode.Value)); - return new PrincipalObject(principalValue); - } + return new PrincipalObject(principalValue); + } default: throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs index 209be8bf..946fa4d6 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sns; using System; diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs index c885d252..bd0e37bf 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sns; using System; diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs index 350b7a59..6f2dbba9 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -59,7 +59,7 @@ public class SnsOperationBinding : OperationBinding private FixedFieldMap redrivePolicyFixedFields => new() { - { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMapWithExtensions(identifierFixFields); } }, + { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMapWithExtensions(this.identifierFixFields); } }, { "maxReceiveCount", (a, n) => { a.MaxReceiveCount = n.GetIntegerValue(); } }, }; diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index da93cfbf..5a7a248c 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Bindings.Sns using System; using System.Collections.Generic; using LEGO.AsyncAPI.Attributes; - using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs index 93bdf733..9172f3e3 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs @@ -39,25 +39,25 @@ public static Condition Parse(ParseNode node) switch (node) { case MapNode mapNode: - { - var conditionValues = new Dictionary>(); - foreach (var conditionNode in mapNode) { - switch (conditionNode.Value) + var conditionValues = new Dictionary>(); + foreach (var conditionNode in mapNode) { - case MapNode conditionValueNode: - conditionValues.Add(conditionNode.Name, new Dictionary(conditionValueNode.Select(x => - new KeyValuePair(x.Name, StringOrStringList.Parse(x.Value))) - .ToDictionary(x => x.Key, x => x.Value))); - break; - default: - throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " + - $"AWS condition values should be one or more key value pairs."); + switch (conditionNode.Value) + { + case MapNode conditionValueNode: + conditionValues.Add(conditionNode.Name, new Dictionary(conditionValueNode.Select(x => + new KeyValuePair(x.Name, StringOrStringList.Parse(x.Value))) + .ToDictionary(x => x.Key, x => x.Value))); + break; + default: + throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " + + $"AWS condition values should be one or more key value pairs."); + } } - } - return new Condition(conditionValues); - } + return new Condition(conditionValues); + } default: throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " + diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs index 2821f952..eee2b4fe 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs @@ -30,20 +30,20 @@ public static Principal Parse(ParseNode node) return new PrincipalStar(); case MapNode mapNode: - { - var propertyNode = mapNode.First(); - if (!IsValidPrincipalProperty(propertyNode.Name)) { - throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + - $"Node should contain a valid AWS principal property name."); - } + var propertyNode = mapNode.First(); + if (!IsValidPrincipalProperty(propertyNode.Name)) + { + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Node should contain a valid AWS principal property name."); + } - var principalValue = new KeyValuePair( - propertyNode.Name, - StringOrStringList.Parse(propertyNode.Value)); + var principalValue = new KeyValuePair( + propertyNode.Name, + StringOrStringList.Parse(propertyNode.Value)); - return new PrincipalObject(principalValue); - } + return new PrincipalObject(principalValue); + } default: throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs index 61e6e546..a1613247 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sqs; using System; diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs index 1705b966..a49b02c6 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sqs; using System; diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs index 4a9c5303..0c2873d2 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs @@ -5,7 +5,6 @@ namespace LEGO.AsyncAPI.Bindings.Sqs using System; using System.Collections.Generic; using LEGO.AsyncAPI.Attributes; - using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiDiagnostics.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiDiagnostics.cs index faceb39e..f1412e25 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiDiagnostics.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiDiagnostics.cs @@ -13,5 +13,18 @@ public class AsyncApiDiagnostic : IDiagnostic public IList Warnings { get; set; } = new List(); public AsyncApiVersion SpecificationVersion { get; set; } + + public void Append(AsyncApiDiagnostic diagnosticToAdd) + { + foreach (var error in diagnosticToAdd.Errors) + { + this.Errors.Add(new(error.Pointer, error.Message)); + } + + foreach (var warning in diagnosticToAdd.Warnings) + { + this.Warnings.Add(new(warning.Pointer, warning.Message)); + } + } } } diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiExternalReferenceResolver.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiExternalReferenceResolver.cs deleted file mode 100644 index a207344a..00000000 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiExternalReferenceResolver.cs +++ /dev/null @@ -1,256 +0,0 @@ -using LEGO.AsyncAPI.Readers.Exceptions; -using LEGO.AsyncAPI.Services; - -namespace LEGO.AsyncAPI.Readers -{ - using System; - using System.Collections.Generic; - using System.Linq; - 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. - /// - internal class AsyncApiExternalReferenceResolver : AsyncApiVisitorBase - { - private AsyncApiDocument currentDocument; - private List errors = new List(); - private AsyncApiReaderSettings readerSettings; - - public AsyncApiExternalReferenceResolver( - AsyncApiDocument currentDocument, - AsyncApiReaderSettings readerSettings) - { - this.currentDocument = currentDocument; - this.readerSettings = readerSettings; - } - - public IEnumerable Errors - { - get - { - return this.errors; - } - } - - public override void Visit(IAsyncApiReferenceable referenceable) - { - if (referenceable.Reference != null) - { - referenceable.Reference.HostDocument = this.currentDocument; - } - } - - public override void Visit(AsyncApiComponents components) - { - this.ResolveMap(components.Parameters); - this.ResolveMap(components.Channels); - this.ResolveMap(components.Schemas); - this.ResolveMap(components.Servers); - this.ResolveMap(components.CorrelationIds); - this.ResolveMap(components.MessageTraits); - this.ResolveMap(components.OperationTraits); - this.ResolveMap(components.SecuritySchemes); - this.ResolveMap(components.ChannelBindings); - this.ResolveMap(components.MessageBindings); - this.ResolveMap(components.OperationBindings); - this.ResolveMap(components.ServerBindings); - this.ResolveMap(components.Messages); - } - - public override void Visit(AsyncApiDocument doc) - { - this.ResolveMap(doc.Servers); - this.ResolveMap(doc.Channels); - } - - public override void Visit(AsyncApiChannel channel) - { - this.ResolveMap(channel.Parameters); - this.ResolveObject(channel.Bindings, r => channel.Bindings = r); - } - - public override void Visit(AsyncApiMessageTrait trait) - { - this.ResolveObject(trait.CorrelationId, r => trait.CorrelationId = r); - this.ResolveObject(trait.Headers, r => trait.Headers = r); - } - - /// - /// Resolve all references used in an operation. - /// - public override void Visit(AsyncApiOperation operation) - { - this.ResolveList(operation.Message); - this.ResolveList(operation.Traits); - this.ResolveObject(operation.Bindings, r => operation.Bindings = r); - } - - public override void Visit(AsyncApiMessage message) - { - this.ResolveObject(message.Headers, r => message.Headers = r); - switch (message.Payload) - { - case AsyncApiJsonSchemaPayload json: - this.ResolveObject(message.Payload as AsyncApiJsonSchemaPayload, r => message.Payload = new AsyncApiJsonSchemaPayload(r)); - break; - case AsyncApiAvroSchemaPayload avro: - // ToFix: this might not resolve correctly. - this.ResolveObject(message.Payload as AsyncApiAvroSchemaPayload, r => message.Payload = r); - break; - default: - break; - } - - this.ResolveList(message.Traits); - this.ResolveObject(message.CorrelationId, r => message.CorrelationId = r); - this.ResolveObject(message.Bindings, r => message.Bindings = r); - } - - public override void Visit(AsyncApiServer server) - { - this.ResolveObject(server.Bindings, r => server.Bindings = r); - } - - /// - /// Resolve all references to SecuritySchemes. - /// - public override void Visit(AsyncApiSecurityRequirement securityRequirement) - { - foreach (var scheme in securityRequirement.Keys.ToList()) - { - this.ResolveObject(scheme, (resolvedScheme) => - { - if (resolvedScheme != null) - { - // If scheme was unresolved - // copy Scopes and remove old unresolved scheme - var scopes = securityRequirement[scheme]; - securityRequirement.Remove(scheme); - securityRequirement.Add(resolvedScheme, scopes); - } - }); - } - } - - /// - /// Resolve all references to parameters. - /// - public override void Visit(IList parameters) - { - this.ResolveList(parameters); - } - - /// - /// Resolve all references used in a parameter. - /// - public override void Visit(AsyncApiParameter parameter) - { - this.ResolveObject(parameter.Schema, r => parameter.Schema = r); - } - - /// - /// Resolve all references used in a schema. - /// - public override void Visit(AsyncApiJsonSchema schema) - { - this.ResolveObject(schema.Items, r => schema.Items = r); - this.ResolveList(schema.OneOf); - this.ResolveList(schema.AllOf); - this.ResolveList(schema.AnyOf); - this.ResolveObject(schema.Contains, r => schema.Contains = r); - this.ResolveObject(schema.Else, r => schema.Else = r); - this.ResolveObject(schema.If, r => schema.If = r); - this.ResolveObject(schema.Items, r => schema.Items = r); - this.ResolveObject(schema.Not, r => schema.Not = r); - this.ResolveObject(schema.Then, r => schema.Then = r); - this.ResolveObject(schema.PropertyNames, r => schema.PropertyNames = r); - this.ResolveObject(schema.AdditionalProperties, r => schema.AdditionalProperties = r); - this.ResolveMap(schema.Properties); - } - - private void ResolveObject(T entity, Action assign) - where T : class, IAsyncApiReferenceable, new() - { - if (entity == null) - { - return; - } - - if (this.IsUnresolvedReference(entity)) - { - assign(this.ResolveReference(entity.Reference)); - } - } - - private void ResolveList(IList list) - where T : class, IAsyncApiReferenceable, new() - { - if (list == null) - { - return; - } - - for (int i = 0; i < list.Count; i++) - { - var entity = list[i]; - if (this.IsUnresolvedReference(entity)) - { - list[i] = this.ResolveReference(entity.Reference); - } - } - } - - private void ResolveMap(IDictionary map) - where T : class, IAsyncApiReferenceable, new() - { - if (map == null) - { - return; - } - - foreach (var key in map.Keys.ToList()) - { - var entity = map[key]; - if (this.IsUnresolvedReference(entity)) - { - map[key] = this.ResolveReference(entity.Reference); - } - } - } - - private T ResolveReference(AsyncApiReference reference) - where T : class, IAsyncApiReferenceable, new() - { - if (reference.IsExternal) - { - if (this.readerSettings.ExternalReferenceReader is null) - { - throw new AsyncApiReaderException( - "External reference configured in AsyncApi document but no implementation provided for ExternalReferenceReader."); - } - - // read external content - var externalContent = this.readerSettings.ExternalReferenceReader.Load(reference.Reference); - - // read external object content - var reader = new AsyncApiStringReader(this.readerSettings); - var externalAsyncApiContent = reader.ReadFragment(externalContent, AsyncApiVersion.AsyncApi2_0, out var diagnostic); - foreach (var error in diagnostic.Errors) - { - this.errors.Add(error); - } - - return externalAsyncApiContent; - } - - return null; - } - - private bool IsUnresolvedReference(IAsyncApiReferenceable possibleReference) - { - return (possibleReference != null && possibleReference.UnresolvedReference); - } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs index 6b340651..4512fb44 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs @@ -2,18 +2,22 @@ namespace LEGO.AsyncAPI.Readers { - using System.Collections.Generic; + using System; + using System.IO; using System.Linq; using System.Text.Json.Nodes; using System.Threading; using System.Threading.Tasks; + using Json.Pointer; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Extensions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Interface; + using LEGO.AsyncAPI.Readers.Services; using LEGO.AsyncAPI.Services; using LEGO.AsyncAPI.Validations; + using YamlDotNet.RepresentationModel; /// /// Service class for converting contents of TextReader into AsyncApiDocument instances. @@ -21,6 +25,7 @@ namespace LEGO.AsyncAPI.Readers internal class AsyncApiJsonDocumentReader : IAsyncApiReader { private readonly AsyncApiReaderSettings settings; + private ParsingContext context; /// /// Initializes a new instance of the class. @@ -40,7 +45,7 @@ public AsyncApiJsonDocumentReader(AsyncApiReaderSettings settings = null) public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic) { diagnostic = new AsyncApiDiagnostic(); - var context = new ParsingContext(diagnostic, this.settings) + this.context ??= new ParsingContext(diagnostic, this.settings) { ExtensionParsers = this.settings.ExtensionParsers, ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), @@ -52,8 +57,7 @@ public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic) AsyncApiDocument document = null; try { - document = context.Parse(input); - + document = this.context.Parse(input); this.ResolveReferences(diagnostic, document); } catch (AsyncApiException ex) @@ -81,17 +85,21 @@ public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic) public async Task ReadAsync(JsonNode input, CancellationToken cancellationToken = default) { var diagnostic = new AsyncApiDiagnostic(); - var context = new ParsingContext(diagnostic, this.settings) + this.context ??= new ParsingContext(diagnostic, this.settings) { 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; try { // Parse the AsyncApi Document - document = context.Parse(input); - this.ResolveReferences(diagnostic, document); + document = this.context.Parse(input); + await this.ResolveReferencesAsync(diagnostic, document); } catch (AsyncApiException ex) { @@ -131,7 +139,7 @@ public T ReadFragment(JsonNode input, AsyncApiVersion version, out AsyncApiDi where T : IAsyncApiElement { diagnostic = new AsyncApiDiagnostic(); - var context = new ParsingContext(diagnostic, this.settings) + this.context ??= new ParsingContext(diagnostic, this.settings) { ExtensionParsers = this.settings.ExtensionParsers, ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), @@ -144,7 +152,7 @@ public T ReadFragment(JsonNode input, AsyncApiVersion version, out AsyncApiDi try { // Parse the AsyncApi element - element = context.ParseFragment(input, version); + element = this.context.ParseFragment(input, version); } catch (AsyncApiException ex) { @@ -164,50 +172,256 @@ public T ReadFragment(JsonNode input, AsyncApiVersion version, out AsyncApiDi return (T)element; } - private void ResolveReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument document) + private async Task ResolveReferencesAsync(AsyncApiDiagnostic diagnostic, IAsyncApiSerializable serializable) { - switch (this.settings.ReferenceResolution) + if (this.settings.ReferenceResolution == ReferenceResolutionSetting.DoNotResolveReferences) { - case ReferenceResolutionSetting.ResolveAllReferences: - this.ResolveAllReferences(diagnostic, document); - break; - case ReferenceResolutionSetting.ResolveInternalReferences: - this.ResolveInternalReferences(diagnostic, document); - break; - case ReferenceResolutionSetting.DoNotResolveReferences: - break; + return; + } + + var collector = new AsyncApiReferenceCollector(this.context.Workspace); + var walker = new AsyncApiWalker(collector); + walker.Walk(serializable); + + foreach (var reference in collector.References) + { + if (this.context.Workspace.Contains(reference.Reference.Reference)) + { + continue; + } + + IAsyncApiSerializable component = null; + if (reference.Reference.IsExternal) + { + if (this.settings.ReferenceResolution != ReferenceResolutionSetting.ResolveAllReferences) + { + continue; + } + + component = await this.ResolveExternalReferenceAsync(diagnostic, reference); + } + else + { + var stream = this.context.Workspace.ResolveReference(string.Empty); // get whole document. + component = this.ResolveStreamReference(stream, reference, diagnostic); + } + + if (component == null) + { + diagnostic.Errors.Add(new AsyncApiError(string.Empty, $"Unable to deserialize reference '{reference.Reference.Reference}'")); + continue; + } + + this.context.Workspace.RegisterComponent(reference.Reference.Reference, component); + await this.ResolveReferencesAsync(diagnostic, component); + } + } + + private void ResolveReferences(AsyncApiDiagnostic diagnostic, IAsyncApiSerializable serializable) + { + if (this.settings.ReferenceResolution == ReferenceResolutionSetting.DoNotResolveReferences) + { + return; + } + + var collector = new AsyncApiReferenceCollector(this.context.Workspace); + var walker = new AsyncApiWalker(collector); + walker.Walk(serializable); + + foreach (var reference in collector.References) + { + if (this.context.Workspace.Contains(reference.Reference.Reference)) + { + continue; + } + + IAsyncApiSerializable component = null; + if (reference.Reference.IsExternal) + { + if (this.settings.ReferenceResolution != ReferenceResolutionSetting.ResolveAllReferences) + { + continue; + } + + component = this.ResolveExternalReference(diagnostic, reference); + } + else + { + var stream = this.context.Workspace.ResolveReference(string.Empty); // get whole document. + component = this.ResolveStreamReference(stream, reference, diagnostic); + } + + if (component == null) + { + diagnostic.Errors.Add(new AsyncApiError(string.Empty, $"Unable to deserialize reference '{reference.Reference.Reference}'")); + continue; + } + + this.context.Workspace.RegisterComponent(reference.Reference.Reference, component); + this.ResolveReferences(diagnostic, component); } } - private void ResolveAllReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument document) + private IAsyncApiSerializable ResolveExternalReference(AsyncApiDiagnostic diagnostic, IAsyncApiReferenceable reference) { - this.ResolveInternalReferences(diagnostic, document); - this.ResolveExternalReferences(diagnostic, document); + if (reference is null) + { + throw new ArgumentNullException(nameof(reference)); + } + + var loader = this.settings.ExternalReferenceLoader ??= new DefaultStreamLoader(); + try + { + Stream stream; + if (this.context.Workspace.Contains(reference.Reference.ExternalResource)) + { + stream = this.context.Workspace.ResolveReference(reference.Reference.ExternalResource); + } + else + { + stream = loader.Load(new Uri(reference.Reference.ExternalResource, UriKind.RelativeOrAbsolute)); + this.context.Workspace.RegisterComponent(reference.Reference.ExternalResource, stream); + } + + return this.ResolveStreamReference(stream, reference, diagnostic); + } + catch (AsyncApiException ex) + { + diagnostic.Errors.Add(new AsyncApiError(ex)); + return null; + } } - private void ResolveInternalReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument document) + private async Task ResolveExternalReferenceAsync(AsyncApiDiagnostic diagnostic, IAsyncApiReferenceable reference) { - var errors = new List(); + if (reference is null) + { + throw new ArgumentNullException(nameof(reference)); + } - var reader = new AsyncApiStringReader(this.settings); - errors.AddRange(document.ResolveReferences()); + var loader = this.settings.ExternalReferenceLoader ??= new DefaultStreamLoader(); + try + { + Stream stream; + if (this.context.Workspace.Contains(reference.Reference.ExternalResource)) + { + stream = this.context.Workspace.ResolveReference(reference.Reference.ExternalResource); + } + else + { + stream = await loader.LoadAsync(new Uri(reference.Reference.ExternalResource, UriKind.RelativeOrAbsolute)); + this.context.Workspace.RegisterComponent(reference.Reference.ExternalResource, stream); + } - foreach (var item in errors) + return this.ResolveStreamReference(stream, reference, diagnostic); + } + catch (AsyncApiException ex) { - diagnostic.Errors.Add(item); + diagnostic.Errors.Add(new AsyncApiError(ex)); + return null; } } - private void ResolveExternalReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument document) + private JsonNode ReadToJson(Stream stream) { - var resolver = new AsyncApiExternalReferenceResolver(document, this.settings); - var walker = new AsyncApiWalker(resolver); - walker.Walk(document); + if (stream != null) + { + var reader = new StreamReader(stream); + var yamlStream = new YamlStream(); + yamlStream.Load(reader); + return yamlStream.Documents.First().ToJsonNode(this.settings.CultureInfo); + } + + return default; + } + + private IAsyncApiSerializable ResolveStreamReference(Stream stream, IAsyncApiReferenceable reference, AsyncApiDiagnostic diagnostic) + { + JsonNode json = null; + try + { + json = this.ReadToJson(stream); + } + catch + { + diagnostic.Errors.Add(new AsyncApiError(string.Empty, $"Unable to deserialize reference: '{reference.Reference.Reference}'")); + return null; + } - foreach (var error in resolver.Errors) + if (reference.Reference.IsFragment) { - diagnostic.Errors.Add(error); + var pointer = JsonPointer.Parse(reference.Reference.FragmentId); + if (pointer.TryEvaluate(json, out var pointerResult)) + { + json = pointerResult; + } + else + { + diagnostic.Errors.Add(new AsyncApiError(reference.Reference.Reference, "Could not resolve reference fragment.")); + return null; + } } + + AsyncApiDiagnostic fragmentDiagnostic = new AsyncApiDiagnostic(); + IAsyncApiSerializable result = null; + switch (reference.Reference.Type) + { + case ReferenceType.Schema: + if (reference is AsyncApiJsonSchemaReference) + { + result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + } + + if (reference is AsyncApiAvroSchemaReference) + { + result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + } + + break; + + case ReferenceType.Server: + result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.Channel: + result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.Message: + result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.SecurityScheme: + result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.Parameter: + result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.CorrelationId: + result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.OperationTrait: + result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.MessageTrait: + result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.ServerBindings: + result = this.ReadFragment>(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.ChannelBindings: + result = this.ReadFragment>(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.OperationBindings: + result = this.ReadFragment>(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + case ReferenceType.MessageBindings: + result = this.ReadFragment>(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic); + break; + default: + diagnostic.Errors.Add(new AsyncApiError(reference.Reference.Reference, "Could not resolve reference.")); + break; + } + + diagnostic.Append(fragmentDiagnostic); + return result; } } -} \ No newline at end of file +} diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index 341a743c..890b83e6 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -72,6 +72,11 @@ public ICollection> /// /// External reference reader implementation provided by users for reading external resources. /// - public IAsyncApiExternalReferenceReader ExternalReferenceReader { get; set; } + public IStreamLoader ExternalReferenceLoader { get; set; } = null; + + /// + /// URL where relative references should be resolved from if. + /// + public Uri BaseUrl { get; set; } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReferenceHostDocumentResolver.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReferenceHostDocumentResolver.cs new file mode 100644 index 00000000..362b7011 --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReferenceHostDocumentResolver.cs @@ -0,0 +1,28 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Services; + + internal class AsyncApiReferenceWorkspaceResolver : AsyncApiVisitorBase + { + private AsyncApiWorkspace workspace; + + public AsyncApiReferenceWorkspaceResolver( + AsyncApiWorkspace workspace) + { + this.workspace = workspace; + } + + public override void Visit(IAsyncApiReferenceable referenceable) + { + if (referenceable.Reference != null) + { + referenceable.Reference.Workspace = this.workspace; + } + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs index 8d1eafc6..78b6973a 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs @@ -122,7 +122,7 @@ static JsonNode LoadYamlDocument(TextReader input, AsyncApiReaderSettings settin { var yamlStream = new YamlStream(); yamlStream.Load(input); - return yamlStream.Documents.First().ToJsonNode(settings); + return yamlStream.Documents.First().ToJsonNode(settings.CultureInfo); } } } diff --git a/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiExternalReferenceReader.cs b/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiExternalReferenceReader.cs deleted file mode 100644 index 6eef5608..00000000 --- a/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiExternalReferenceReader.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace LEGO.AsyncAPI.Readers; - -/// -/// Interface that provides method for reading external references.å. -/// -public interface IAsyncApiExternalReferenceReader -{ - /// - /// Method that returns the AsyncAPI content that the external reference from the $ref points to. - /// - /// The content address of the $ref. - /// The content of the reference as a string. - public string Load(string reference); -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj index 818a6305..90a0d82f 100644 --- a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj +++ b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj @@ -6,6 +6,7 @@ AsyncAPI.NET.Readers LEGO.AsyncAPI.Readers LEGO.AsyncAPI.Readers + netstandard2.0;net8 @@ -17,6 +18,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs index 63bea6fc..8f392f35 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs @@ -116,11 +116,7 @@ public override Dictionary CreateMapWithReference( if (entry.value.Reference == null) { - entry.value.Reference = new AsyncApiReference() - { - Type = referenceType, - Id = entry.key, - }; + entry.value.Reference = new AsyncApiReference(entry.key, referenceType); } } finally @@ -185,7 +181,6 @@ public T GetReferencedObject(ReferenceType referenceType, string referenceId) { return new T() { - UnresolvedReference = true, Reference = this.Context.VersionService.ConvertToAsyncApiReference(referenceId, referenceType), }; } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs index 76add0b2..246018b4 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs @@ -12,6 +12,7 @@ public class ValueNode : ParseNode { private readonly JsonNode node; private string cachedScalarValue; + public ValueNode(ParsingContext context, JsonNode node) : base( context) diff --git a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs index 9bcc051b..5fb0c2b3 100644 --- a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs +++ b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs @@ -4,7 +4,9 @@ namespace LEGO.AsyncAPI.Readers { using System; using System.Collections.Generic; + using System.IO; using System.Linq; + using System.Text.Json; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; @@ -44,6 +46,8 @@ internal Dictionary> ExtensionPars /// public AsyncApiReaderSettings Settings { get; } + public AsyncApiWorkspace Workspace { get; } + ///// ///// Initializes a new instance of the class. ///// @@ -63,6 +67,7 @@ public ParsingContext(AsyncApiDiagnostic diagnostic, AsyncApiReaderSettings sett { this.Diagnostic = diagnostic; this.Settings = settings; + this.Workspace = new AsyncApiWorkspace(); } internal AsyncApiDocument Parse(JsonNode jsonNode) @@ -78,6 +83,10 @@ internal AsyncApiDocument Parse(JsonNode jsonNode) case string version when version.StartsWith("2"): this.VersionService = new AsyncApiV2VersionService(this.Diagnostic); doc = this.VersionService.LoadDocument(this.RootNode); + + // Register components + this.Workspace.RegisterComponents(doc); // pre-register components. + this.Workspace.RegisterComponent(string.Empty, this.ParseToStream(jsonNode)); // register root document. this.Diagnostic.SpecificationVersion = AsyncApiVersion.AsyncApi2_0; break; @@ -88,6 +97,18 @@ internal AsyncApiDocument Parse(JsonNode jsonNode) return doc; } + private Stream ParseToStream(JsonNode node) + { + var stream = new MemoryStream(); + using (var writer = new Utf8JsonWriter(stream)) + { + node.WriteTo(writer); + } + + stream.Position = 0; + return stream; + } + internal T ParseFragment(JsonNode jsonNode, AsyncApiVersion version) where T : IAsyncApiElement { diff --git a/src/LEGO.AsyncAPI.Readers/Services/AsyncApiRemoteReferenceCollector.cs b/src/LEGO.AsyncAPI.Readers/Services/AsyncApiRemoteReferenceCollector.cs new file mode 100644 index 00000000..4094fc6a --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/Services/AsyncApiRemoteReferenceCollector.cs @@ -0,0 +1,47 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers.Services +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Services; + + internal class AsyncApiReferenceCollector : AsyncApiVisitorBase + { + private readonly List references = new(); + private AsyncApiWorkspace workspace; + + public AsyncApiReferenceCollector( + AsyncApiWorkspace workspace) + { + this.workspace = workspace; + } + /// + /// List of all external references collected from AsyncApiDocument. + /// + public IEnumerable References + { + get + { + return this.references; + } + } + + /// + /// Collect reference for each reference. + /// + /// + public override void Visit(IAsyncApiReferenceable referenceable) + { + if (referenceable.Reference != null) + { + if (referenceable.Reference.Workspace == null) + { + referenceable.Reference.Workspace = this.workspace; + } + + this.references.Add(referenceable); + } + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs b/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs index 6e506730..3d949a23 100644 --- a/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs +++ b/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs @@ -1,4 +1,4 @@ -// Copyright (c) The LEGO Group. All rights reserved. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Readers.Services { @@ -6,46 +6,54 @@ namespace LEGO.AsyncAPI.Readers.Services using System.IO; using System.Net.Http; using System.Threading.Tasks; + using LEGO.AsyncAPI.Readers.Exceptions; using LEGO.AsyncAPI.Readers.Interface; internal class DefaultStreamLoader : IStreamLoader { - private readonly Uri baseUrl; - private HttpClient httpClient = new HttpClient(); - - public DefaultStreamLoader(Uri baseUrl) - { - this.baseUrl = baseUrl; - } + private static readonly HttpClient HttpClient = new HttpClient(); public Stream Load(Uri uri) { - var absoluteUri = new Uri(this.baseUrl, uri); - switch (uri.Scheme) + try + { + switch (uri.Scheme) + { + case "file": + return File.OpenRead(uri.AbsolutePath); + case "http": + case "https": + return HttpClient.GetStreamAsync(uri).GetAwaiter().GetResult(); + default: + throw new ArgumentException("Unsupported scheme"); + } + } + catch (Exception ex) { - case "file": - return File.OpenRead(absoluteUri.AbsolutePath); - case "http": - case "https": - return this.httpClient.GetStreamAsync(absoluteUri).GetAwaiter().GetResult(); - default: - throw new ArgumentException("Unsupported scheme"); + + throw new AsyncApiReaderException($"Something went wrong trying to fetch '{uri.OriginalString}. {ex.Message}'", ex); } } public async Task LoadAsync(Uri uri) { - var absoluteUri = new Uri(this.baseUrl, uri); - - switch (absoluteUri.Scheme) + try { - case "file": - return File.OpenRead(absoluteUri.AbsolutePath); - case "http": - case "https": - return await this.httpClient.GetStreamAsync(absoluteUri); - default: - throw new ArgumentException("Unsupported scheme"); + switch (uri.Scheme) + { + case "file": + return File.OpenRead(uri.AbsolutePath); + case "http": + case "https": + return await HttpClient.GetStreamAsync(uri); + default: + throw new ArgumentException("Unsupported scheme"); + } + } + catch (Exception ex) + { + + throw new AsyncApiReaderException($"Something went wrong trying to fetch '{uri.OriginalString}'. {ex.Message}", ex); } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs index baae8873..348005e0 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs @@ -2,8 +2,6 @@ namespace LEGO.AsyncAPI.Readers { - using System; - using System.Threading; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Avro.LogicalTypes; @@ -214,7 +212,7 @@ public class AsyncApiAvroSchemaDeserializer { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() }, }; - public static AvroSchema LoadSchema(ParseNode node) + public static AsyncApiAvroSchema LoadSchema(ParseNode node) { if (node is ValueNode valueNode) { @@ -238,11 +236,7 @@ public static AvroSchema LoadSchema(ParseNode node) if (pointer != null) { - return new AvroRecord - { - UnresolvedReference = true, - Reference = node.Context.VersionService.ConvertToAsyncApiReference(pointer, ReferenceType.Schema), - }; + return new AsyncApiAvroSchemaReference(pointer); } var isLogicalType = mapNode["logicalType"] != null; @@ -286,7 +280,7 @@ public static AvroSchema LoadSchema(ParseNode node) throw new AsyncApiReaderException("Invalid node type"); } - private static AvroSchema LoadLogicalType(MapNode mapNode) + private static AsyncApiAvroSchema LoadLogicalType(MapNode mapNode) { var type = mapNode["logicalType"]?.Value.GetScalarValue(); switch (type) diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs index 1aab9e85..aa91b942 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs @@ -15,7 +15,7 @@ internal static AsyncApiBindings LoadChannelBindings(ParseNode var pointer = mapNode.GetReferencePointer(); if (pointer != null) { - return mapNode.GetReferencedObject>(ReferenceType.ChannelBindings, pointer); + return new AsyncApiBindingsReference(pointer); } var channelBindings = new AsyncApiBindings(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs index 1d6acae6..7b9e3e63 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs @@ -30,7 +30,7 @@ public static AsyncApiChannel LoadChannel(ParseNode node) var pointer = mapNode.GetReferencePointer(); if (pointer != null) { - return mapNode.GetReferencedObject(ReferenceType.Channel, pointer); + return new AsyncApiChannelReference(pointer); } var pathItem = new AsyncApiChannel(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs index df13308a..75c50873 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs @@ -10,19 +10,19 @@ internal static partial class AsyncApiV2Deserializer { private static FixedFieldMap componentsFixedFields = new() { - { "schemas", (a, n) => a.Schemas = n.CreateMapWithReference(ReferenceType.Schema, AsyncApiSchemaDeserializer.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) }, - { "securitySchemes", (a, n) => a.SecuritySchemes = n.CreateMapWithReference(ReferenceType.SecurityScheme, LoadSecurityScheme) }, - { "parameters", (a, n) => a.Parameters = n.CreateMapWithReference(ReferenceType.Parameter, LoadParameter) }, - { "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.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) }, + { "schemas", (a, n) => a.Schemas = n.CreateMap(AsyncApiSchemaDeserializer.LoadSchema) }, + { "servers", (a, n) => a.Servers = n.CreateMap(LoadServer) }, + { "channels", (a, n) => a.Channels = n.CreateMap(LoadChannel) }, + { "messages", (a, n) => a.Messages = n.CreateMap(LoadMessage) }, + { "securitySchemes", (a, n) => a.SecuritySchemes = n.CreateMap(LoadSecurityScheme) }, + { "parameters", (a, n) => a.Parameters = n.CreateMap(LoadParameter) }, + { "correlationIds", (a, n) => a.CorrelationIds = n.CreateMap(LoadCorrelationId) }, + { "operationTraits", (a, n) => a.OperationTraits = n.CreateMap(LoadOperationTrait) }, + { "messageTraits", (a, n) => a.MessageTraits = n.CreateMap(LoadMessageTrait) }, + { "serverBindings", (a, n) => a.ServerBindings = n.CreateMap(LoadServerBindings) }, + { "channelBindings", (a, n) => a.ChannelBindings = n.CreateMap(LoadChannelBindings) }, + { "operationBindings", (a, n) => a.OperationBindings = n.CreateMap(LoadOperationBindings) }, + { "messageBindings", (a, n) => a.MessageBindings = n.CreateMap(LoadMessageBindings) }, }; private static PatternFieldMap componentsPatternFields = diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs index 0b8e88f0..ee908181 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs @@ -31,7 +31,7 @@ public static AsyncApiCorrelationId LoadCorrelationId(ParseNode node) var pointer = mapNode.GetReferencePointer(); if (pointer != null) { - return mapNode.GetReferencedObject(ReferenceType.CorrelationId, pointer); + return new AsyncApiCorrelationIdReference(pointer); } var correlationId = new AsyncApiCorrelationId(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs index 9a180e84..eb58ca1f 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs @@ -12,9 +12,13 @@ internal static partial class AsyncApiV2Deserializer internal static AsyncApiBindings LoadMessageBindings(ParseNode node) { var mapNode = node.CheckMapNode("messageBindings"); + var pointer = mapNode.GetReferencePointer(); + if (pointer != null) + { + return new AsyncApiBindingsReference(pointer); + } var messageBindings = new AsyncApiBindings(); - foreach (var property in mapNode) { var messageBinding = LoadMessageBinding(property); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs index 2670d88a..fe7b8d4a 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs @@ -88,9 +88,9 @@ private static IAsyncApiMessagePayload LoadPayload(ParseNode n, string format) case null: case "": case var _ when SupportedJsonSchemaFormats.Where(s => format.StartsWith(s)).Any(): - return new AsyncApiJsonSchemaPayload(AsyncApiSchemaDeserializer.LoadSchema(n)); + return AsyncApiSchemaDeserializer.LoadSchema(n); case var _ when SupportedAvroSchemaFormats.Where(s => format.StartsWith(s)).Any(): - return new AsyncApiAvroSchemaPayload(AsyncApiAvroSchemaDeserializer.LoadSchema(n)); + return AsyncApiAvroSchemaDeserializer.LoadSchema(n); default: var supportedFormats = SupportedJsonSchemaFormats.Concat(SupportedAvroSchemaFormats); throw new AsyncApiException($"'Could not deserialize Payload. Supported formats are {string.Join(", ", supportedFormats)}"); @@ -137,7 +137,7 @@ public static AsyncApiMessage LoadMessage(ParseNode node) var pointer = mapNode.GetReferencePointer(); if (pointer != null) { - return mapNode.GetReferencedObject(ReferenceType.Message, pointer); + return new AsyncApiMessageReference(pointer); } var message = new AsyncApiMessage(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs index 0d229e92..40e8a945 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs @@ -38,7 +38,7 @@ public static AsyncApiMessageTrait LoadMessageTrait(ParseNode node) if (pointer != null) { - return mapNode.GetReferencedObject(ReferenceType.MessageTrait, pointer); + return new AsyncApiMessageTraitReference(pointer); } var messageTrait = new AsyncApiMessageTrait(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs index 4d4eb85f..0fb5db0a 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs @@ -12,9 +12,13 @@ internal static partial class AsyncApiV2Deserializer internal static AsyncApiBindings LoadOperationBindings(ParseNode node) { var mapNode = node.CheckMapNode("operationBindings"); + var pointer = mapNode.GetReferencePointer(); + if (pointer != null) + { + return new AsyncApiBindingsReference(pointer); + } var operationBindings = new AsyncApiBindings(); - foreach (var property in mapNode) { var operationBinding = LoadOperationBinding(property); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs index 561456e9..ee503d91 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs @@ -31,7 +31,7 @@ public static AsyncApiOperationTrait LoadOperationTrait(ParseNode node) var pointer = mapNode.GetReferencePointer(); if (pointer != null) { - return mapNode.GetReferencedObject(ReferenceType.OperationTrait, pointer); + return new AsyncApiOperationTraitReference(pointer); } var operationTrait = new AsyncApiOperationTrait(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs index c77f48f4..e7628c12 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs @@ -28,7 +28,7 @@ public static AsyncApiParameter LoadParameter(ParseNode node) var pointer = mapNode.GetReferencePointer(); if (pointer != null) { - return mapNode.GetReferencedObject(ReferenceType.Parameter, pointer); + return new AsyncApiParameterReference(pointer); } var parameter = new AsyncApiParameter(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index ba3130a0..45b74674 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -229,11 +229,7 @@ public static AsyncApiJsonSchema LoadSchema(ParseNode node) if (pointer != null) { - return new AsyncApiJsonSchema - { - UnresolvedReference = true, - Reference = node.Context.VersionService.ConvertToAsyncApiReference(pointer, ReferenceType.Schema), - }; + return new AsyncApiJsonSchemaReference(pointer); } var schema = new AsyncApiJsonSchema(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecurityRequirementDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecurityRequirementDeserializer.cs index e3574f6a..f9f02907 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecurityRequirementDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecurityRequirementDeserializer.cs @@ -36,19 +36,11 @@ public static AsyncApiSecurityRequirement LoadSecurityRequirement(ParseNode node return securityRequirement; } - private static AsyncApiSecurityScheme LoadSecuritySchemeByReference( + private static AsyncApiSecuritySchemeReference LoadSecuritySchemeByReference( ParsingContext context, string schemeName) { - var securitySchemeObject = new AsyncApiSecurityScheme - { - UnresolvedReference = true, - Reference = new AsyncApiReference - { - Id = schemeName, - Type = ReferenceType.SecurityScheme, - }, - }; + var securitySchemeObject = new AsyncApiSecuritySchemeReference(schemeName); return securitySchemeObject; } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs index 92708116..37481197 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs @@ -56,7 +56,7 @@ public static AsyncApiSecurityScheme LoadSecurityScheme(ParseNode node) var pointer = mapNode.GetReferencePointer(); if (pointer != null) { - return mapNode.GetReferencedObject(ReferenceType.SecurityScheme, pointer); + return new AsyncApiSecuritySchemeReference(pointer); } var securityScheme = new AsyncApiSecurityScheme(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs index 44c821d3..18a8ea84 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs @@ -15,7 +15,7 @@ internal static AsyncApiBindings LoadServerBindings(ParseNode no var pointer = mapNode.GetReferencePointer(); if (pointer != null) { - return mapNode.GetReferencedObject>(ReferenceType.ServerBindings, pointer); + return new AsyncApiBindingsReference(pointer); } var serverBindings = new AsyncApiBindings(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs index 6eae00af..9d72d588 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs @@ -52,7 +52,7 @@ public static AsyncApiServer LoadServer(ParseNode node) var pointer = mapNode.GetReferencePointer(); if (pointer != null) { - return mapNode.GetReferencedObject(ReferenceType.Server, pointer); + return new AsyncApiServerReference(pointer); } var server = new AsyncApiServer(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs index b773d255..a47ec6a6 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs @@ -41,7 +41,7 @@ public static AsyncApiServerVariable LoadServerVariable(ParseNode node) var pointer = mapNode.GetReferencePointer(); if (pointer != null) { - return mapNode.GetReferencedObject(ReferenceType.ServerVariable, pointer); + return new AsyncApiServerVariableReference(pointer); } var serverVariable = new AsyncApiServerVariable(); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs index d70f5bde..6cbca7ed 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs @@ -9,7 +9,6 @@ namespace LEGO.AsyncAPI.Readers.V2 using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Interface; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal class AsyncApiV2VersionService : IAsyncApiVersionService { @@ -36,16 +35,21 @@ public AsyncApiV2VersionService(AsyncApiDiagnostic diagnostic) [typeof(AsyncApiOperation)] = AsyncApiV2Deserializer.LoadOperation, [typeof(AsyncApiParameter)] = AsyncApiV2Deserializer.LoadParameter, [typeof(AsyncApiJsonSchema)] = AsyncApiSchemaDeserializer.LoadSchema, - [typeof(AvroSchema)] = AsyncApiAvroSchemaDeserializer.LoadSchema, - [typeof(AsyncApiJsonSchemaPayload)] = AsyncApiV2Deserializer.LoadJsonSchemaPayload, - [typeof(AsyncApiAvroSchemaPayload)] = AsyncApiV2Deserializer.LoadAvroPayload, + [typeof(AsyncApiAvroSchema)] = AsyncApiAvroSchemaDeserializer.LoadSchema, + [typeof(AsyncApiJsonSchema)] = AsyncApiV2Deserializer.LoadJsonSchemaPayload, + [typeof(AsyncApiAvroSchema)] = AsyncApiV2Deserializer.LoadAvroPayload, [typeof(AsyncApiSecurityRequirement)] = AsyncApiV2Deserializer.LoadSecurityRequirement, [typeof(AsyncApiSecurityScheme)] = AsyncApiV2Deserializer.LoadSecurityScheme, [typeof(AsyncApiServer)] = AsyncApiV2Deserializer.LoadServer, [typeof(AsyncApiServerVariable)] = AsyncApiV2Deserializer.LoadServerVariable, [typeof(AsyncApiTag)] = AsyncApiV2Deserializer.LoadTag, [typeof(AsyncApiMessage)] = AsyncApiV2Deserializer.LoadMessage, + [typeof(AsyncApiMessageTrait)] = AsyncApiV2Deserializer.LoadMessageTrait, [typeof(AsyncApiChannel)] = AsyncApiV2Deserializer.LoadChannel, + [typeof(AsyncApiBindings)] = AsyncApiV2Deserializer.LoadServerBindings, + [typeof(AsyncApiBindings)] = AsyncApiV2Deserializer.LoadChannelBindings, + [typeof(AsyncApiBindings)] = AsyncApiV2Deserializer.LoadMessageBindings, + [typeof(AsyncApiBindings)] = AsyncApiV2Deserializer.LoadOperationBindings, }; /// @@ -62,78 +66,15 @@ public AsyncApiReference ConvertToAsyncApiReference( throw new AsyncApiException($"The reference string '{reference}' has invalid format."); } - var segments = reference.Split('#'); - if (segments.Length == 1) + try { - if (type == ReferenceType.SecurityScheme) - { - return new AsyncApiReference - { - Type = type, - Id = reference, - }; - } - - var asyncApiReference = new AsyncApiReference(); - asyncApiReference.Type = type; - if (reference.StartsWith("/")) - { - asyncApiReference.IsFragment = true; - } - - asyncApiReference.ExternalResource = segments[0]; - - return asyncApiReference; + return new AsyncApiReference(reference, type); } - else if (segments.Length == 2) + catch (AsyncApiException ex) { - // Local reference - if (reference.StartsWith("#")) - { - try - { - return this.ParseReference(segments[1]); - } - catch (AsyncApiException ex) - { - this.Diagnostic.Errors.Add(new AsyncApiError(ex)); - return null; - } - } - - var id = segments[1]; - var asyncApiReference = new AsyncApiReference(); - if (id.StartsWith("/components/")) - { - var localSegments = segments[1].Split('/'); - var referencedType = localSegments[2].GetEnumFromDisplayName(); - if (type == null) - { - type = referencedType; - } - else - { - if (type != referencedType) - { - throw new AsyncApiException("Referenced type mismatch"); - } - } - - id = localSegments[3]; - } - else - { - asyncApiReference.IsFragment = true; - } - - asyncApiReference.ExternalResource = segments[0]; - asyncApiReference.Type = type; - asyncApiReference.Id = id; - - return asyncApiReference; + this.Diagnostic.Errors.Add(new AsyncApiError(ex)); + return null; } - - throw new AsyncApiException($"The reference string '{reference}' has invalid format."); } public AsyncApiDocument LoadDocument(RootNode rootNode) @@ -146,27 +87,5 @@ public T LoadElement(ParseNode node) { return (T)this.loaders[typeof(T)](node); } - - private AsyncApiReference ParseReference(string localReference) - { - if (string.IsNullOrWhiteSpace(localReference)) - { - throw new ArgumentException( - $"The argument '{nameof(localReference)}' is null, empty or consists only of white-space."); - } - - var segments = localReference.Split('/'); - - if (segments.Length == 4) - { - if (segments[1] == "components") - { - var referenceType = segments[2].GetEnumFromDisplayName(); - return new AsyncApiReference { Type = referenceType, Id = segments[3] }; - } - } - - throw new AsyncApiException($"The reference string '{localReference}' has invalid format."); - } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/YamlConverter.cs b/src/LEGO.AsyncAPI.Readers/YamlConverter.cs index 648ee50a..942bc28f 100644 --- a/src/LEGO.AsyncAPI.Readers/YamlConverter.cs +++ b/src/LEGO.AsyncAPI.Readers/YamlConverter.cs @@ -10,55 +10,55 @@ namespace LEGO.AsyncAPI.Readers internal static class YamlConverter { - public static JsonNode ToJsonNode(this YamlDocument yamlDocument, AsyncApiReaderSettings settings) + public static JsonNode ToJsonNode(this YamlDocument yamlDocument, CultureInfo cultureInfo) { - return yamlDocument.RootNode.ToJsonNode(settings); + return yamlDocument.RootNode.ToJsonNode(cultureInfo); } - public static JsonObject ToJsonObject(this YamlMappingNode yamlMappingNode, AsyncApiReaderSettings settings) + public static JsonObject ToJsonObject(this YamlMappingNode yamlMappingNode, CultureInfo cultureInfo) { var node = new JsonObject(); foreach (var keyValuePair in yamlMappingNode) { var key = ((YamlScalarNode)keyValuePair.Key).Value!; - node[key] = keyValuePair.Value.ToJsonNode(settings); + node[key] = keyValuePair.Value.ToJsonNode(cultureInfo); } return node; } - public static JsonArray ToJsonArray(this YamlSequenceNode yaml, AsyncApiReaderSettings settings) + public static JsonArray ToJsonArray(this YamlSequenceNode yaml, CultureInfo cultureInfo) { var node = new JsonArray(); foreach (var value in yaml) { - node.Add(value.ToJsonNode(settings)); + node.Add(value.ToJsonNode(cultureInfo)); } return node; } - public static JsonNode ToJsonNode(this YamlNode yaml, AsyncApiReaderSettings settings) + public static JsonNode ToJsonNode(this YamlNode yaml, CultureInfo cultureInfo) { return yaml switch { - YamlMappingNode map => map.ToJsonObject(settings), - YamlSequenceNode seq => seq.ToJsonArray(settings), - YamlScalarNode scalar => scalar.ToJsonValue(settings), + YamlMappingNode map => map.ToJsonObject(cultureInfo), + YamlSequenceNode seq => seq.ToJsonArray(cultureInfo), + YamlScalarNode scalar => scalar.ToJsonValue(cultureInfo), _ => throw new NotSupportedException("This yaml isn't convertible to JSON"), }; } - private static JsonValue ToJsonValue(this YamlScalarNode yaml, AsyncApiReaderSettings settings) + private static JsonValue ToJsonValue(this YamlScalarNode yaml, CultureInfo cultureInfo) { switch (yaml.Style) { case ScalarStyle.Plain: - return decimal.TryParse(yaml.Value, NumberStyles.Float, settings.CultureInfo, out var d) + return decimal.TryParse(yaml.Value, NumberStyles.Float, cultureInfo, out var d) ? JsonValue.Create(d) : bool.TryParse(yaml.Value, out var b) ? JsonValue.Create(b) - : JsonValue.Create(yaml.Value) !; + : JsonValue.Create(yaml.Value)!; case ScalarStyle.SingleQuoted: case ScalarStyle.DoubleQuoted: case ScalarStyle.Literal: diff --git a/src/LEGO.AsyncAPI/AsyncApiWorkspace.cs b/src/LEGO.AsyncAPI/AsyncApiWorkspace.cs new file mode 100644 index 00000000..5d581e22 --- /dev/null +++ b/src/LEGO.AsyncAPI/AsyncApiWorkspace.cs @@ -0,0 +1,192 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI +{ + using System; + using System.Collections.Generic; + using System.IO; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + + public class AsyncApiWorkspace + { + private readonly Dictionary artifactsRegistry = new(); + private readonly Dictionary resolvedReferenceRegistry = new(); + + public void RegisterComponents(AsyncApiDocument document) + { + if (document?.Components == null) + { + return; + } + + string baseUri = "#/components/"; + string location; + + // Register Schema + foreach (var item in document.Components.Schemas) + { + location = baseUri + ReferenceType.Schema.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register Parameters + foreach (var item in document.Components.Parameters) + { + location = baseUri + ReferenceType.Parameter.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register Channels + foreach (var item in document.Components.Channels) + { + location = baseUri + ReferenceType.Channel.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register Servers + foreach (var item in document.Components.Servers) + { + location = baseUri + ReferenceType.Server.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register ServerVariables + foreach (var item in document.Components.ServerVariables) + { + location = baseUri + ReferenceType.ServerVariable.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register Messages + foreach (var item in document.Components.Messages) + { + location = baseUri + ReferenceType.Message.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register SecuritySchemes + foreach (var item in document.Components.SecuritySchemes) + { + location = baseUri + ReferenceType.SecurityScheme.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + this.RegisterComponent(item.Key, item.Value); + } + + // Register Parameters + foreach (var item in document.Components.Parameters) + { + location = baseUri + ReferenceType.Parameter.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register CorrelationIds + foreach (var item in document.Components.CorrelationIds) + { + location = baseUri + ReferenceType.CorrelationId.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register OperationTraits + foreach (var item in document.Components.OperationTraits) + { + location = baseUri + ReferenceType.OperationTrait.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register MessageTraits + foreach (var item in document.Components.MessageTraits) + { + location = baseUri + ReferenceType.MessageTrait.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register ServerBindings + foreach (var item in document.Components.ServerBindings) + { + location = baseUri + ReferenceType.ServerBindings.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register ChannelBindings + foreach (var item in document.Components.ChannelBindings) + { + location = baseUri + ReferenceType.ChannelBindings.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register OperationBindings + foreach (var item in document.Components.OperationBindings) + { + location = baseUri + ReferenceType.OperationBindings.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + + // Register MessageBindings + foreach (var item in document.Components.MessageBindings) + { + location = baseUri + ReferenceType.MessageBindings.GetDisplayName() + "/" + item.Key; + this.RegisterComponent(location, item.Value); + } + } + + public bool RegisterComponent(string location, T component) + { + var uri = this.ToLocationUrl(location); + if (component is IAsyncApiSerializable referenceable) + { + if (!this.resolvedReferenceRegistry.ContainsKey(uri)) + { + this.resolvedReferenceRegistry[uri] = referenceable; + return true; + } + } + + if (component is Stream stream) + { + if (!this.artifactsRegistry.ContainsKey(uri)) + { + this.artifactsRegistry[uri] = stream; + } + return true; + } + + return false; + } + + public bool Contains(string location) + { + var key = this.ToLocationUrl(location); + return this.resolvedReferenceRegistry.ContainsKey(key) || this.artifactsRegistry.ContainsKey(key); + } + + public T ResolveReference(AsyncApiReference reference) + where T : class + { + return this.ResolveReference(reference.Reference); + } + + public T ResolveReference(string location) + where T : class + { + var uri = this.ToLocationUrl(location); + if (this.resolvedReferenceRegistry.TryGetValue(uri, out var referenceableValue)) + { + return referenceableValue as T; + } + + if (this.artifactsRegistry.TryGetValue(uri, out var stream)) + { + stream.Position = 0; + return (T)(object)stream; + } + + return default; + } + + private Uri ToLocationUrl(string location) + { + return new(location, UriKind.RelativeOrAbsolute); + } + } +} diff --git a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj index 83de3df0..c73cc9d0 100644 --- a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj +++ b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj @@ -5,6 +5,7 @@ AsyncAPI.NET LEGO.AsyncAPI LEGO.AsyncAPI + netstandard2.0;net8 diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiAvroSchemaPayload.cs b/src/LEGO.AsyncAPI/Models/AsyncApiAvroSchemaPayload.cs deleted file mode 100644 index 0e9d1cb9..00000000 --- a/src/LEGO.AsyncAPI/Models/AsyncApiAvroSchemaPayload.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models -{ - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - using System; - - public class AsyncApiAvroSchemaPayload : IAsyncApiMessagePayload - { - private readonly AvroSchema schema; - - public AsyncApiAvroSchemaPayload(AvroSchema schema) - { - this.schema = schema; - } - - public AsyncApiAvroSchemaPayload() - { - this.schema = new AvroRecord(); - } - - public bool TryGetAs(out T schema) - where T : AvroSchema - { - schema = this.schema as T; - return schema != null; - } - - public bool Is() - where T : AvroSchema - { - return this.schema is T; - } - - public Type GetSchemaType() - { - return this.schema.GetType(); - } - - public bool UnresolvedReference { get => this.schema.UnresolvedReference; set => this.schema.UnresolvedReference = value; } - - public AsyncApiReference Reference { get => this.schema.Reference; set => this.schema.Reference = value; } - - public void SerializeV2(IAsyncApiWriter writer) - { - var settings = writer.GetSettings(); - - if (this.Reference != null) - { - if (!settings.ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - } - - this.SerializeV2WithoutReference(writer); - } - - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - this.schema.SerializeV2(writer); - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs index 4034b4e9..a2c8303a 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Bindings { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; @@ -12,10 +11,6 @@ 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; } @@ -27,12 +22,6 @@ public void SerializeV2(IAsyncApiWriter writer) throw new ArgumentNullException(nameof(writer)); } - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - this.SerializeProperties(writer); } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs index f11858aa..260629ae 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs @@ -3,39 +3,17 @@ namespace LEGO.AsyncAPI.Models { using System; + using System.Collections; using System.Collections.Generic; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - public class AsyncApiBindings : Dictionary, IAsyncApiReferenceable + public class AsyncApiBindings : IDictionary, IAsyncApiSerializable where TBinding : IBinding { - public bool UnresolvedReference { get; set; } + private Dictionary inner = new Dictionary(); - public AsyncApiReference Reference { get; set; } - - public void Add(TBinding binding) - { - this[binding.BindingKey] = binding; - } - - 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 void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { if (writer is null) { @@ -56,5 +34,79 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteEndObject(); } + + public virtual void Add(TBinding binding) + { + this[binding.BindingKey] = binding; + } + + public virtual TBinding this[string key] + { + get => inner[key]; + set => inner[key] = value; + } + + public virtual ICollection Keys => inner.Keys; + + public virtual ICollection Values => inner.Values; + + public virtual int Count => inner.Count; + + public virtual bool IsReadOnly => ((IDictionary)inner).IsReadOnly; + + public virtual void Add(string key, TBinding value) + { + inner.Add(key, value); + } + + public virtual bool ContainsKey(string key) + { + return inner.ContainsKey(key); + } + + public virtual bool Remove(string key) + { + return inner.Remove(key); + } + + public virtual bool TryGetValue(string key, out TBinding value) + { + return inner.TryGetValue(key, out value); + } + + public virtual void Add(KeyValuePair item) + { + ((IDictionary)inner).Add(item); + } + + public virtual void Clear() + { + inner.Clear(); + } + + public virtual bool Contains(KeyValuePair item) + { + return ((IDictionary)inner).Contains(item); + } + + public virtual void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)inner).CopyTo(array, arrayIndex); + } + + public virtual bool Remove(KeyValuePair item) + { + return ((IDictionary)inner).Remove(item); + } + + public virtual IEnumerator> GetEnumerator() + { + return inner.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return inner.GetEnumerator(); + } } } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiChannel.cs b/src/LEGO.AsyncAPI/Models/AsyncApiChannel.cs index 2ff637f5..a7e25963 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiChannel.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiChannel.cs @@ -10,12 +10,12 @@ namespace LEGO.AsyncAPI.Models /// /// Describes the operations available on a single channel. /// - public class AsyncApiChannel : IAsyncApiReferenceable, IAsyncApiExtensible + public class AsyncApiChannel : IAsyncApiSerializable, IAsyncApiExtensible { /// /// an optional description of this channel item. CommonMark syntax can be used for rich text representation. /// - public string Description { get; set; } + public virtual string Description { get; set; } /// /// the servers on which this channel is available, specified as an optional unordered list of names (string keys) of Server Objects defined in the Servers Object (a map). @@ -23,52 +23,32 @@ public class AsyncApiChannel : IAsyncApiReferenceable, IAsyncApiExtensible /// /// If servers is absent or empty then this channel must be available on all servers defined in the Servers Object. /// - public IList Servers { get; set; } = new List(); + public virtual IList Servers { get; set; } = new List(); /// /// a definition of the SUBSCRIBE operation, which defines the messages produced by the application and sent to the channel. /// - public AsyncApiOperation Subscribe { get; set; } + public virtual AsyncApiOperation Subscribe { get; set; } /// /// a definition of the PUBLISH operation, which defines the messages consumed by the application from the channel. /// - public AsyncApiOperation Publish { get; set; } + public virtual AsyncApiOperation Publish { get; set; } /// /// a map of the parameters included in the channel name. It SHOULD be present only when using channels with expressions (as defined by RFC 6570 section 2.2). /// - public IDictionary Parameters { get; set; } = new Dictionary(); + public virtual IDictionary Parameters { get; set; } = new Dictionary(); /// /// a map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the channel. /// - public AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings(); + public virtual AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings(); /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public virtual IDictionary Extensions { get; set; } = new Dictionary(); - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { 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.SerializeV2WithoutReference(writer); - } - - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { if (writer is null) { @@ -92,11 +72,9 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) // parameters writer.WriteOptionalMap(AsyncApiConstants.Parameters, this.Parameters, (writer, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.Channel && - component.Reference.Id == key) + if (component is AsyncApiParameterReference reference) { - component.SerializeV2WithoutReference(writer); + reference.SerializeV2(writer); } else { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs b/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs index b64f5a66..0d00a9d9 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs @@ -97,21 +97,21 @@ public void SerializeV2(IAsyncApiWriter writer) // If references have been inlined we don't need the to render the components section // however if they have cycles, then we will need a component rendered - if (writer.GetSettings().InlineReferences) + if (writer.GetSettings().InlineLocalReferences) { var loops = writer.GetSettings().LoopDetector.Loops; writer.WriteStartObject(); - if (loops.TryGetValue(typeof(AsyncApiJsonSchema), out List schemas)) + if (loops.TryGetValue(typeof(AsyncApiJsonSchemaReference), out List schemas)) { - var asyncApiSchemas = schemas.Cast().Distinct().ToList() - .ToDictionary(k => k.Reference.Id); + var asyncApiSchemas = schemas.Cast().Distinct().ToList() + .ToDictionary(k => k.Reference.FragmentId); writer.WriteOptionalMap( AsyncApiConstants.Schemas, this.Schemas, (w, key, component) => { - component.SerializeV2WithoutReference(w); + component.SerializeV2(w); }); } @@ -130,11 +130,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.Schemas, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.Schema && - component.Reference.Id == key) + if (component is AsyncApiJsonSchemaReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -148,11 +146,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.Servers, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.Server && - component.Reference.Id == key) + if (component is AsyncApiServerReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -166,11 +162,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.ServerVariables, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.ServerVariable && - component.Reference.Id == key) + if (component is AsyncApiServerVariableReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -184,11 +178,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.Channels, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.Channel && - component.Reference.Id == key) + if (component is AsyncApiChannelReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -202,11 +194,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.Messages, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.Message && - component.Reference.Id == key) + if (component is AsyncApiMessageReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -220,11 +210,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.SecuritySchemes, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.SecurityScheme && - component.Reference.Id == key) + if (component is AsyncApiSecuritySchemeReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -238,11 +226,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.Parameters, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.Parameter && - component.Reference.Id == key) + if (component is AsyncApiParameterReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -256,11 +242,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.CorrelationIds, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.CorrelationId && - component.Reference.Id == key) + if (component is AsyncApiCorrelationIdReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -274,11 +258,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.OperationTraits, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.OperationTrait && - component.Reference.Id == key) + if (component is AsyncApiOperationTraitReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -292,11 +274,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.MessageTraits, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.MessageTrait && - component.Reference.Id == key) + if (component is AsyncApiMessageTraitReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -310,11 +290,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.ServerBindings, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.ServerBindings && - component.Reference.Id == key) + if (component is AsyncApiBindingsReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -328,11 +306,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.ChannelBindings, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.ChannelBindings && - component.Reference.Id == key) + if (component is AsyncApiBindingsReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -346,11 +322,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.OperationBindings, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.OperationBindings && - component.Reference.Id == key) + if (component is AsyncApiBindingsReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { @@ -364,11 +338,9 @@ public void SerializeV2(IAsyncApiWriter writer) this.MessageBindings, (w, key, component) => { - if (component.Reference != null && - component.Reference.Type == ReferenceType.MessageBindings && - component.Reference.Id == key) + if (component is AsyncApiBindingsReference reference) { - component.SerializeV2WithoutReference(w); + reference.SerializeV2(w); } else { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiCorrelationId.cs b/src/LEGO.AsyncAPI/Models/AsyncApiCorrelationId.cs index 156e7828..b3342110 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiCorrelationId.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiCorrelationId.cs @@ -10,44 +10,22 @@ namespace LEGO.AsyncAPI.Models /// /// An object that specifies an identifier at design time that can used for message tracing and correlation. /// - public class AsyncApiCorrelationId : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyncApiSerializable + public class AsyncApiCorrelationId : IAsyncApiExtensible, IAsyncApiSerializable { /// /// an optional description of the identifier. CommonMark syntax can be used for rich text representation. /// - public string Description { get; set; } + public virtual string Description { get; set; } /// /// REQUIRED. A runtime expression that specifies the location of the correlation ID. /// - public string Location { get; set; } + public virtual string Location { get; set; } /// - public bool UnresolvedReference { get; set; } + public virtual IDictionary Extensions { get; set; } = new Dictionary(); - /// - public AsyncApiReference Reference { get; set; } - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - 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 void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { if (writer is null) { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs index 5be55202..5d90e7a8 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs @@ -4,10 +4,8 @@ namespace LEGO.AsyncAPI.Models { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - 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. @@ -73,10 +71,7 @@ public void SerializeV2(IAsyncApiWriter writer) throw new ArgumentNullException(nameof(writer)); } - if (writer.GetSettings().InlineReferences) - { - this.ResolveReferences(); - } + writer.Workspace.RegisterComponents(this); writer.WriteStartObject(); @@ -90,37 +85,13 @@ public void SerializeV2(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.Id, this.Id); // servers - writer.WriteOptionalMap(AsyncApiConstants.Servers, this.Servers, (writer, key, component) => - { - if (component.Reference != null && - component.Reference.Type == ReferenceType.Server && - component.Reference.Id == key) - { - component.SerializeV2WithoutReference(writer); - } - else - { - component.SerializeV2(writer); - } - }); + writer.WriteOptionalMap(AsyncApiConstants.Servers, this.Servers, (writer, key, component) => component.SerializeV2(writer)); // content type writer.WriteOptionalProperty(AsyncApiConstants.DefaultContentType, this.DefaultContentType); // channels - writer.WriteRequiredMap(AsyncApiConstants.Channels, this.Channels, (writer, key, component) => - { - if (component.Reference != null && - component.Reference.Type == ReferenceType.Channel && - component.Reference.Id == key) - { - component.SerializeV2WithoutReference(writer); - } - else - { - component.SerializeV2(writer); - } - }); + writer.WriteRequiredMap(AsyncApiConstants.Channels, this.Channels, (writer, key, component) => component.SerializeV2(writer)); // components writer.WriteOptionalObject(AsyncApiConstants.Components, this.Components, (w, c) => c.SerializeV2(w)); @@ -136,76 +107,5 @@ public void SerializeV2(IAsyncApiWriter writer) writer.WriteEndObject(); } - - public IEnumerable ResolveReferences() - { - var resolver = new AsyncApiReferenceResolver(this); - var walker = new AsyncApiWalker(resolver); - walker.Walk(this); - return resolver.Errors; - } - - internal T ResolveReference(AsyncApiReference reference) - where T : class, IAsyncApiReferenceable - { - return this.ResolveReference(reference) as T; - } - - internal IAsyncApiReferenceable ResolveReference(AsyncApiReference reference) - { - if (reference == null) - { - return null; - } - - if (!reference.Type.HasValue) - { - throw new ArgumentException("Reference must have a type."); - } - - if (this.Components == null) - { - throw new AsyncApiException(string.Format("Invalid reference Id: '{0}'", reference.Id)); - } - - try - { - switch (reference.Type) - { - case ReferenceType.Schema: - return this.Components.Schemas[reference.Id]; - case ReferenceType.Server: - return this.Components.Servers[reference.Id]; - case ReferenceType.Channel: - return this.Components.Channels[reference.Id]; - case ReferenceType.Message: - return this.Components.Messages[reference.Id]; - case ReferenceType.SecurityScheme: - return this.Components.SecuritySchemes[reference.Id]; - case ReferenceType.Parameter: - return this.Components.Parameters[reference.Id]; - case ReferenceType.CorrelationId: - return this.Components.CorrelationIds[reference.Id]; - case ReferenceType.OperationTrait: - return this.Components.OperationTraits[reference.Id]; - case ReferenceType.MessageTrait: - return this.Components.MessageTraits[reference.Id]; - case ReferenceType.ServerBindings: - return this.Components.ServerBindings[reference.Id]; - case ReferenceType.ChannelBindings: - return this.Components.ChannelBindings[reference.Id]; - case ReferenceType.OperationBindings: - return this.Components.OperationBindings[reference.Id]; - case ReferenceType.MessageBindings: - return this.Components.MessageBindings[reference.Id]; - default: - throw new AsyncApiException("Invalid reference type."); - } - } - catch (KeyNotFoundException) - { - throw new AsyncApiException(string.Format("Invalid reference Id: '{0}'", reference.Id)); - } - } } } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiMessage.cs b/src/LEGO.AsyncAPI/Models/AsyncApiMessage.cs index e0cc6776..6853222d 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiMessage.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiMessage.cs @@ -1,4 +1,4 @@ -// Copyright (c) The LEGO Group. All rights reserved. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Models { @@ -10,27 +10,27 @@ namespace LEGO.AsyncAPI.Models /// /// Describes a message received on a given channel and operation. /// - public class AsyncApiMessage : IAsyncApiExtensible, IAsyncApiReferenceable, IAsyncApiSerializable + public class AsyncApiMessage : IAsyncApiExtensible, IAsyncApiSerializable { /// /// Unique string used to identify the message. The id MUST be unique among all messages described in the API. /// - public string MessageId { get; set; } + public virtual string MessageId { get; set; } /// /// schema definition of the application headers. Schema MUST be of type "object". /// - public AsyncApiJsonSchema Headers { get; set; } + public virtual AsyncApiJsonSchema Headers { get; set; } /// /// definition of the message payload. It can be of any type but defaults to Schema object. It must match the schema format, including encoding type - e.g Avro should be inlined as either a YAML or JSON object NOT a string to be parsed as YAML or JSON. /// - public IAsyncApiMessagePayload Payload { get; set; } + public virtual IAsyncApiMessagePayload Payload { get; set; } /// /// definition of the correlation ID used for message tracing or matching. /// - public AsyncApiCorrelationId CorrelationId { get; set; } + public virtual AsyncApiCorrelationId CorrelationId { get; set; } /// /// a string containing the name of the schema format used to define the message payload. @@ -38,84 +38,62 @@ public class AsyncApiMessage : IAsyncApiExtensible, IAsyncApiReferenceable, IAsy /// /// If omitted, implementations should parse the payload as a Schema object. /// - public string SchemaFormat { get; set; } + public virtual string SchemaFormat { get; set; } /// /// the content type to use when encoding/decoding a message's payload. /// - public string ContentType { get; set; } + public virtual string ContentType { get; set; } /// /// a machine-friendly name for the message. /// - public string Name { get; set; } + public virtual string Name { get; set; } /// /// a human-friendly title for the message. /// - public string Title { get; set; } + public virtual string Title { get; set; } /// /// a short summary of what the message is about. /// - public string Summary { get; set; } + public virtual string Summary { get; set; } /// /// a verbose explanation of the message. CommonMark syntax can be used for rich text representation. /// - public string Description { get; set; } + public virtual string Description { get; set; } /// /// a list of tags for API documentation control. Tags can be used for logical grouping of messages. /// - public IList Tags { get; set; } = new List(); + public virtual IList Tags { get; set; } = new List(); /// /// additional external documentation for this message. /// - public AsyncApiExternalDocumentation ExternalDocs { get; set; } + public virtual AsyncApiExternalDocumentation ExternalDocs { get; set; } /// /// a map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the message. /// - public AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings(); + public virtual AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings(); /// /// list of examples. /// - public IList Examples { get; set; } = new List(); + public virtual IList Examples { get; set; } = new List(); /// /// a list of traits to apply to the message object. Traits MUST be merged into the message object using the JSON Merge Patch algorithm in the same order they are defined here. The resulting object MUST be a valid Message Object. /// - public IList Traits { get; set; } = new List(); + public virtual IList Traits { get; set; } = new List(); /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public virtual IDictionary Extensions { get; set; } = new Dictionary(); - /// - public bool UnresolvedReference { get; set; } - - /// - public AsyncApiReference Reference { 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.SerializeV2WithoutReference(writer); - } - - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { if (writer is null) { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiMessageTrait.cs b/src/LEGO.AsyncAPI/Models/AsyncApiMessageTrait.cs index c14a9a24..1e62adc0 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiMessageTrait.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiMessageTrait.cs @@ -10,22 +10,22 @@ namespace LEGO.AsyncAPI.Models /// /// Describes a trait that MAY be applied to a Message Object. /// - public class AsyncApiMessageTrait : IAsyncApiExtensible, IAsyncApiReferenceable, IAsyncApiSerializable + public class AsyncApiMessageTrait : IAsyncApiExtensible, IAsyncApiSerializable { /// /// Unique string used to identify the message. The id MUST be unique among all messages described in the API. /// - public string MessageId { get; set; } + public virtual string MessageId { get; set; } /// /// schema definition of the application headers. Schema MUST be of type "object". /// - public AsyncApiJsonSchema Headers { get; set; } + public virtual AsyncApiJsonSchema Headers { get; set; } /// /// definition of the correlation ID used for message tracing or matching. /// - public AsyncApiCorrelationId CorrelationId { get; set; } + public virtual AsyncApiCorrelationId CorrelationId { get; set; } /// /// a string containing the name of the schema format used to define the message payload. @@ -33,79 +33,56 @@ public class AsyncApiMessageTrait : IAsyncApiExtensible, IAsyncApiReferenceable, /// /// If omitted, implementations should parse the payload as a Schema object. /// - public string SchemaFormat { get; set; } + public virtual string SchemaFormat { get; set; } /// /// the content type to use when encoding/decoding a message's payload. /// - public string ContentType { get; set; } + public virtual string ContentType { get; set; } /// /// a machine-friendly name for the message. /// - public string Name { get; set; } + public virtual string Name { get; set; } /// /// a human-friendly title for the message. /// - public string Title { get; set; } + public virtual string Title { get; set; } /// /// a short summary of what the message is about. /// - public string Summary { get; set; } + public virtual string Summary { get; set; } /// /// a verbose explanation of the message. CommonMark syntax can be used for rich text representation. /// - public string Description { get; set; } + public virtual string Description { get; set; } /// /// a list of tags for API documentation control. Tags can be used for logical grouping of messages. /// - public IList Tags { get; set; } = new List(); + public virtual IList Tags { get; set; } = new List(); /// /// additional external documentation for this message. /// - public AsyncApiExternalDocumentation ExternalDocs { get; set; } - + public virtual AsyncApiExternalDocumentation ExternalDocs { get; set; } /// /// a map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the message. /// - public AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings(); + public virtual AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings(); /// /// list of examples. /// - public IList Examples { get; set; } = new List(); - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - /// - public bool UnresolvedReference { get; set; } + public virtual IList Examples { get; set; } = new List(); /// - public AsyncApiReference Reference { 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.SerializeV2WithoutReference(writer); - } + public virtual IDictionary Extensions { get; set; } = new Dictionary(); - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { if (writer is null) { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiOperationTrait.cs b/src/LEGO.AsyncAPI/Models/AsyncApiOperationTrait.cs index c7e0d10e..9260808e 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiOperationTrait.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiOperationTrait.cs @@ -10,7 +10,7 @@ namespace LEGO.AsyncAPI.Models /// /// Describes a trait that MAY be applied to an Operation Object. /// - public class AsyncApiOperationTrait : IAsyncApiExtensible, IAsyncApiReferenceable, IAsyncApiSerializable + public class AsyncApiOperationTrait : IAsyncApiExtensible, IAsyncApiSerializable { /// /// unique string used to identify the operation. @@ -18,59 +18,37 @@ public class AsyncApiOperationTrait : IAsyncApiExtensible, IAsyncApiReferenceabl /// /// The id MUST be unique among all operations described in the API. /// - public string OperationId { get; set; } + public virtual string OperationId { get; set; } /// /// a short summary of what the operation is about. /// - public string Summary { get; set; } + public virtual string Summary { get; set; } /// /// a short summary of what the operation is about. /// - public string Description { get; set; } + public virtual string Description { get; set; } /// /// a list of tags for API documentation control. Tags can be used for logical grouping of operations. /// - public IList Tags { get; set; } = new List(); + public virtual IList Tags { get; set; } = new List(); /// /// additional external documentation for this operation. /// - public AsyncApiExternalDocumentation ExternalDocs { get; set; } + public virtual AsyncApiExternalDocumentation ExternalDocs { get; set; } /// /// a map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the operation. /// - public AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings(); + public virtual AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings(); /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public virtual IDictionary Extensions { get; set; } = new Dictionary(); - /// - public bool UnresolvedReference { get; set; } - - /// - public AsyncApiReference Reference { 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.SerializeV2WithoutReference(writer); - } - - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { if (writer is null) { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiParameter.cs b/src/LEGO.AsyncAPI/Models/AsyncApiParameter.cs index 4c3ef683..1db08626 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiParameter.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiParameter.cs @@ -1,4 +1,4 @@ -// Copyright (c) The LEGO Group. All rights reserved. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Models { @@ -10,49 +10,27 @@ namespace LEGO.AsyncAPI.Models /// /// Describes a parameter included in a channel name. /// - public class AsyncApiParameter : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyncApiSerializable + public class AsyncApiParameter : IAsyncApiExtensible, IAsyncApiSerializable { /// /// Gets or sets a verbose explanation of the parameter. CommonMark syntax can be used for rich text representation. /// - public string Description { get; set; } + public virtual string Description { get; set; } /// /// Gets or sets definition of the parameter. /// - public AsyncApiJsonSchema Schema { get; set; } + public virtual AsyncApiJsonSchema Schema { get; set; } /// /// Gets or sets a runtime expression that specifies the location of the parameter value. /// - public string Location { get; set; } + public virtual string Location { get; set; } /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public virtual IDictionary Extensions { get; set; } = new Dictionary(); - /// - public bool UnresolvedReference { get; set; } - - /// - public AsyncApiReference Reference { 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.SerializeV2WithoutReference(writer); - } - - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { if (writer is null) { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiReference.cs b/src/LEGO.AsyncAPI/Models/AsyncApiReference.cs index 9475310e..38386b7b 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiReference.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiReference.cs @@ -3,6 +3,7 @@ namespace LEGO.AsyncAPI.Models { using System; + using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; @@ -11,13 +12,81 @@ namespace LEGO.AsyncAPI.Models /// public class AsyncApiReference : IAsyncApiSerializable { + private string originalString; + + public AsyncApiReference(string reference, ReferenceType? type) + { + if (string.IsNullOrWhiteSpace(reference)) + { + throw new AsyncApiException($"The reference string '{reference}' has invalid format."); + } + + this.originalString = reference; + var segments = reference.Split('#'); + if (segments.Length == 1) + { + if (type == ReferenceType.SecurityScheme) + { + this.Type = ReferenceType.SecurityScheme; + this.FragmentId = reference; + return; + } + + this.Type = type; + this.ExternalResource = segments[0]; + + return; + } + else if (segments.Length == 2) + { + if (reference.StartsWith("#")) // is local reference + { + var localSegments = reference.Split('/'); + + if (localSegments.Length == 4) + { + if (localSegments[1] == "components") // is local components reference + { + var referencedType = localSegments[2].GetEnumFromDisplayName(); + if (type == null || type == ReferenceType.None) + { + type = referencedType; + } + else + { + if (type != referencedType) + { + throw new AsyncApiException("Referenced type mismatch"); + } + } + } + } + } + else + { + this.ExternalResource = segments[0]; + } + + this.Type = type; + this.FragmentId = segments[1]; + + return; + } + + throw new AsyncApiException($"The reference string '{reference}' has invalid format."); + } + /// /// External resource in the reference. /// It maybe: /// 1. a absolute/relative file path, for example: ../commons/pet.json /// 2. a Url, for example: http://localhost/pet.json. /// - public string ExternalResource { get; set; } + public string ExternalResource + { + get; + set; + } /// /// Gets or sets the element type referenced. @@ -27,17 +96,17 @@ public class AsyncApiReference : IAsyncApiSerializable /// /// Gets or sets the identifier of the reusable component of one particular ReferenceType. /// - public string Id { get; set; } + public string FragmentId { get; set; } /// /// Gets or sets the AsyncApiDocument that is hosting the AsyncApiReference instance. This is used to enable dereferencing the reference. /// - public AsyncApiDocument HostDocument { get; set; } = null; + public AsyncApiWorkspace Workspace { get; set; } = null; /// /// Gets a flag indicating whether a file is a valid OpenAPI document or a fragment. /// - public bool IsFragment { get; set; } = false; + public bool IsFragment => this.FragmentId != null; /// /// Gets a flag indicating whether this reference is an external reference. @@ -51,22 +120,7 @@ public string Reference { get { - if (this.IsExternal) - { - return this.GetExternalReferenceV2(); - } - - if (!this.Type.HasValue) - { - throw new ArgumentNullException(nameof(this.Type)); - } - - //if (this.Type == ReferenceType.SecurityScheme) - //{ - // return this.Id; - //} - - return "#/components/" + this.Type.GetDisplayName() + "/" + this.Id; + return this.originalString; } } @@ -97,17 +151,7 @@ public void SerializeV2(IAsyncApiWriter writer) private string GetExternalReferenceV2() { - if (this.Id != null) - { - if (this.IsFragment) - { - return this.ExternalResource + "#" + this.Id; - } - - return this.ExternalResource + "#/components/" + this.Type.GetDisplayName() + "/" + this.Id; - } - - return this.ExternalResource; + return this.ExternalResource + (this.FragmentId != null ? "#" + this.FragmentId : string.Empty); } public void Write(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiReferenceEqualityComparer.cs b/src/LEGO.AsyncAPI/Models/AsyncApiReferenceEqualityComparer.cs index cbdf812c..da7abd22 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiReferenceEqualityComparer.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiReferenceEqualityComparer.cs @@ -31,7 +31,7 @@ public bool Equals(IAsyncApiReferenceable x, IAsyncApiReferenceable y) return false; } - return x.Reference.Id == y.Reference.Id; + return x.Reference.FragmentId == y.Reference.FragmentId; } /// @@ -39,7 +39,7 @@ public bool Equals(IAsyncApiReferenceable x, IAsyncApiReferenceable y) /// public int GetHashCode(IAsyncApiReferenceable obj) { - return obj?.Reference?.Id == null ? 0 : obj.Reference.Id.GetHashCode(); + return obj?.Reference?.FragmentId == null ? 0 : obj.Reference.FragmentId.GetHashCode(); } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSchemaPayload.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSchemaPayload.cs deleted file mode 100644 index 91c6f68f..00000000 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSchemaPayload.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models -{ - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - public class AsyncApiJsonSchemaPayload : IAsyncApiMessagePayload - { - private readonly AsyncApiJsonSchema schema; - - public AsyncApiJsonSchemaPayload() - { - this.schema = new AsyncApiJsonSchema(); - } - - public AsyncApiJsonSchemaPayload(AsyncApiJsonSchema schema) - { - this.schema = schema; - } - - public string Title { get => this.schema.Title; set => this.schema.Title = value; } - - public SchemaType? Type { get => this.schema.Type; set => this.schema.Type = value; } - - public string Format { get => this.schema.Format; set => this.schema.Format = value; } - - public string Description { get => this.schema.Description; set => this.schema.Description = value; } - - public double? Maximum { get => this.schema.Maximum; set => this.schema.Maximum = value; } - - public double? ExclusiveMaximum { get => this.schema.ExclusiveMaximum; set => this.schema.ExclusiveMaximum = value; } - - public double? Minimum { get => this.schema.Minimum; set => this.schema.Minimum = value; } - - public double? ExclusiveMinimum { get => this.schema.ExclusiveMinimum; set => this.schema.ExclusiveMinimum = value; } - - public int? MaxLength { get => this.schema.MaxLength; set => this.schema.MaxLength = value; } - - public int? MinLength { get => this.schema.MinLength; set => this.schema.MinLength = value; } - - public string Pattern { get => this.schema.Pattern; set => this.schema.Pattern = value; } - - public double? MultipleOf { get => this.schema.MultipleOf; set => this.schema.MultipleOf = value; } - - public AsyncApiAny Default { get => this.schema.Default; set => this.schema.Default = value; } - - public bool ReadOnly { get => this.schema.ReadOnly; set => this.schema.ReadOnly = value; } - - public bool WriteOnly { get => this.schema.WriteOnly; set => this.schema.WriteOnly = value; } - - public IList AllOf { get => this.schema.AllOf; set => this.schema.AllOf = value; } - - public IList OneOf { get => this.schema.OneOf; set => this.schema.OneOf = value; } - - public IList AnyOf { get => this.schema.AnyOf; set => this.schema.AnyOf = value; } - - public AsyncApiJsonSchema Not { get => this.schema.Not; set => this.schema.Not = value; } - - public AsyncApiJsonSchema Contains { get => this.schema.Contains; set => this.schema.Contains = value; } - - public AsyncApiJsonSchema If { get => this.schema.If; set => this.schema.If = value; } - - public AsyncApiJsonSchema Then { get => this.schema.Then; set => this.schema.Then = value; } - - public AsyncApiJsonSchema Else { get => this.schema.Else; set => this.schema.Else = value; } - - public ISet Required { get => this.schema.Required; set => this.schema.Required = value; } - - public AsyncApiJsonSchema Items { get => this.schema.Items; set => this.schema.Items = value; } - - public AsyncApiJsonSchema AdditionalItems { get => this.schema.AdditionalItems; set => this.schema.AdditionalItems = value; } - - public int? MaxItems { get => this.schema.MaxItems; set => this.schema.MaxItems = value; } - - public int? MinItems { get => this.schema.MinItems; set => this.schema.MinItems = value; } - - public bool? UniqueItems { get => this.schema.UniqueItems; set => this.schema.UniqueItems = value; } - - public IDictionary Properties { get => this.schema.Properties; set => this.schema.Properties = value; } - - public int? MaxProperties { get => this.schema.MaxProperties; set => this.schema.MaxProperties = value; } - - public int? MinProperties { get => this.schema.MinProperties; set => this.schema.MinProperties = value; } - - public IDictionary PatternProperties { get => this.schema.PatternProperties; set => this.schema.PatternProperties = value; } - - public AsyncApiJsonSchema PropertyNames { get => this.schema.PropertyNames; set => this.schema.PropertyNames = value; } - - public string Discriminator { get => this.schema.Discriminator; set => this.schema.Discriminator = value; } - - public IList Enum { get => this.schema.Enum; set => this.schema.Enum = value; } - - public IList Examples { get => this.schema.Examples; set => this.schema.Examples = value; } - - public AsyncApiAny Const { get => this.schema.Const; set => this.schema.Const = value; } - - public bool Nullable { get => this.schema.Nullable; set => this.schema.Nullable = value; } - - public AsyncApiExternalDocumentation ExternalDocs { get => this.schema.ExternalDocs; set => this.schema.ExternalDocs = value; } - - public bool Deprecated { get => this.schema.Deprecated; set => this.schema.Deprecated = value; } - - public bool UnresolvedReference { get => this.schema.UnresolvedReference; set => this.schema.UnresolvedReference = value; } - - public AsyncApiReference Reference { get => this.schema.Reference; set => this.schema.Reference = value; } - - public IDictionary Extensions { get => this.schema.Extensions; set => this.schema.Extensions = value; } - - public AsyncApiJsonSchema AdditionalProperties { get => this.schema.AdditionalProperties; set => this.schema.AdditionalProperties = value; } - - public static implicit operator AsyncApiJsonSchema(AsyncApiJsonSchemaPayload payload) => payload.schema; - - public static implicit operator AsyncApiJsonSchemaPayload(AsyncApiJsonSchema schema) => new AsyncApiJsonSchemaPayload(schema); - - public void SerializeV2(IAsyncApiWriter writer) - { - this.schema.SerializeV2(writer); - } - - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - this.schema.SerializeV2WithoutReference(writer); - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSecurityRequirement.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSecurityRequirement.cs index 475497d2..95e93a89 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSecurityRequirement.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSecurityRequirement.cs @@ -7,7 +7,7 @@ namespace LEGO.AsyncAPI.Models using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - public class AsyncApiSecurityRequirement : Dictionary>, IAsyncApiSerializable + public class AsyncApiSecurityRequirement : Dictionary>, IAsyncApiSerializable { /// /// Initializes a new instance of the class. @@ -45,7 +45,7 @@ public void SerializeV2(IAsyncApiWriter writer) } // securityScheme.SerializeV2(writer); - writer.WritePropertyName(securityScheme.Reference.Id); + writer.WritePropertyName(securityScheme.Reference.FragmentId); writer.WriteStartArray(); foreach (var scope in scopes) diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSecurityScheme.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSecurityScheme.cs index 08fd3d67..4c5bb0ff 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSecurityScheme.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSecurityScheme.cs @@ -7,89 +7,60 @@ namespace LEGO.AsyncAPI.Models using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - public class AsyncApiSecurityScheme : IAsyncApiSerializable, IAsyncApiReferenceable, IAsyncApiExtensible + public class AsyncApiSecurityScheme : IAsyncApiSerializable, IAsyncApiExtensible { /// /// REQUIRED. The type of the security scheme. Valid values are "userPassword", "apiKey", "X509", "symmetricEncryption", "asymmetricEncryption", "httpApiKey", "http", "oauth2", "openIdConnect", "plain", "scramSha256", "scramSha512", and "gssapi". /// - public SecuritySchemeType Type { get; set; } + public virtual SecuritySchemeType Type { get; set; } /// /// A short description for security scheme. CommonMark syntax MAY be used for rich text representation. /// - public string Description { get; set; } + public virtual string Description { get; set; } /// /// REQUIRED. The name of the header, query or cookie parameter to be used. /// - public string Name { get; set; } + public virtual string Name { get; set; } /// /// REQUIRED. The location of the API key. Valid values are "user" and "password" for apiKey and "query", "header" or "cookie" for httpApiKey. /// - public ParameterLocation In { get; set; } + public virtual ParameterLocation In { get; set; } /// /// REQUIRED. The name of the HTTP Authorization scheme to be used /// in the Authorization header as defined in RFC7235. /// - public string Scheme { get; set; } + public virtual string Scheme { get; set; } /// /// A hint to the client to identify how the bearer token is formatted. /// Bearer tokens are usually generated by an authorization server, /// so this information is primarily for documentation purposes. /// - public string BearerFormat { get; set; } + public virtual string BearerFormat { get; set; } /// /// REQUIRED. An object containing configuration information for the flow types supported. /// - public AsyncApiOAuthFlows Flows { get; set; } + public virtual AsyncApiOAuthFlows Flows { get; set; } /// /// REQUIRED. OpenId Connect URL to discover OAuth2 configuration values. /// - public Uri OpenIdConnectUrl { get; set; } + public virtual Uri OpenIdConnectUrl { get; set; } /// /// Specification Extensions. /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - /// - /// 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 Async Api v2.3. - /// - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } + public virtual IDictionary Extensions { get; set; } = new Dictionary(); /// /// Serialize to AsyncApi V2 document without using reference. /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartObject(); @@ -143,47 +114,5 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteEndObject(); } - - /// - /// Arbitrarily chooses one object from the - /// to populate in V2 security scheme. - /// - private static void WriteOAuthFlowForV2(IAsyncApiWriter writer, AsyncApiOAuthFlows flows) - { - if (flows != null) - { - if (flows.Implicit != null) - { - WriteOAuthFlowForV2(writer, AsyncApiConstants.Implicit, flows.Implicit); - } - else if (flows.Password != null) - { - WriteOAuthFlowForV2(writer, AsyncApiConstants.Password, flows.Password); - } - else if (flows.ClientCredentials != null) - { - WriteOAuthFlowForV2(writer, AsyncApiConstants.Application, flows.ClientCredentials); - } - else if (flows.AuthorizationCode != null) - { - WriteOAuthFlowForV2(writer, AsyncApiConstants.AccessCode, flows.AuthorizationCode); - } - } - } - - private static void WriteOAuthFlowForV2(IAsyncApiWriter writer, string flowValue, AsyncApiOAuthFlow flow) - { - // flow - writer.WriteOptionalProperty(AsyncApiConstants.Flow, flowValue); - - // authorizationUrl - writer.WriteOptionalProperty(AsyncApiConstants.AuthorizationUrl, flow.AuthorizationUrl?.ToString()); - - // tokenUrl - writer.WriteOptionalProperty(AsyncApiConstants.TokenUrl, flow.TokenUrl?.ToString()); - - // scopes - writer.WriteOptionalMap(AsyncApiConstants.Scopes, flow.Scopes, (w, s) => w.WriteValue(s)); - } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiServer.cs b/src/LEGO.AsyncAPI/Models/AsyncApiServer.cs index 5d4fa071..396b65d9 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiServer.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiServer.cs @@ -2,40 +2,35 @@ namespace LEGO.AsyncAPI.Models { - using System; using System.Collections.Generic; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - - /// - /// The definition of a server this application MAY connect to. - /// - public class AsyncApiServer : IAsyncApiSerializable, IAsyncApiExtensible, IAsyncApiReferenceable + public class AsyncApiServer : IAsyncApiSerializable, IAsyncApiExtensible { /// /// REQUIRED. A URL to the target host. /// - public string Url { get; set; } + public virtual string Url { get; set; } /// /// REQUIRED. The protocol this URL supports for connection. /// - public string Protocol { get; set; } + public virtual string Protocol { get; set; } /// /// the version of the protocol used for connection. /// - public string ProtocolVersion { get; set; } + public virtual string ProtocolVersion { get; set; } /// /// an optional string describing the host designated by the URL. /// - public string Description { get; set; } + public virtual string Description { get; set; } /// /// a map between a variable name and its value. The value is used for substitution in the server's URL template. /// - public IDictionary Variables { get; set; } = new Dictionary(); + public virtual IDictionary Variables { get; set; } = new Dictionary(); /// /// a declaration of which security mechanisms can be used with this server. The list of values includes alternative security requirement objects that can be used. @@ -43,42 +38,22 @@ public class AsyncApiServer : IAsyncApiSerializable, IAsyncApiExtensible, IAsync /// /// The name used for each property MUST correspond to a security scheme declared in the Security Schemes under the Components Object. /// - public IList Security { get; set; } = new List(); + public virtual IList Security { get; set; } = new List(); /// /// A list of tags for logical grouping and categorization of servers. /// - public IList Tags { get; set; } = new List(); + public virtual IList Tags { get; set; } = new List(); /// /// a map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the server. /// - public AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings(); + public virtual AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings(); /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { 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.SerializeV2WithoutReference(writer); - } + public virtual IDictionary Extensions { get; set; } = new Dictionary(); - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartObject(); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiServerVariable.cs b/src/LEGO.AsyncAPI/Models/AsyncApiServerVariable.cs index ed02634a..57069b99 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiServerVariable.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiServerVariable.cs @@ -10,52 +10,32 @@ namespace LEGO.AsyncAPI.Models /// /// An object representing a Server Variable for server URL template substitution. /// - public class AsyncApiServerVariable : IAsyncApiSerializable, IAsyncApiExtensible, IAsyncApiReferenceable + public class AsyncApiServerVariable : IAsyncApiSerializable, IAsyncApiExtensible { /// /// An enumeration of string values to be used if the substitution options are from a limited set. /// - public IList Enum { get; set; } = new List(); + public virtual IList Enum { get; set; } = new List(); /// /// The default value to use for substitution, and to send, if an alternate value is not supplied. /// - public string Default { get; set; } + public virtual string Default { get; set; } /// /// An optional description for the server variable. CommonMark syntax MAY be used for rich text representation. /// - public string Description { get; set; } + public virtual string Description { get; set; } /// /// An array of examples of the server variable. /// - public IList Examples { get; set; } = new List(); + public virtual IList Examples { get; set; } = new List(); /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public virtual IDictionary Extensions { get; set; } = new Dictionary(); - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { 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.SerializeV2WithoutReference(writer); - } - - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { if (writer is null) { diff --git a/src/LEGO.AsyncAPI/Models/Avro/AsyncApiAvroSchema.cs b/src/LEGO.AsyncAPI/Models/Avro/AsyncApiAvroSchema.cs new file mode 100644 index 00000000..3e1fa291 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/Avro/AsyncApiAvroSchema.cs @@ -0,0 +1,45 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public abstract class AsyncApiAvroSchema : IAsyncApiSerializable, IAsyncApiMessagePayload + { + public abstract string Type { get; } + + /// + /// A map of properties not in the schema, but added as additional metadata. + /// + public abstract IDictionary Metadata { get; set; } + + public static implicit operator AsyncApiAvroSchema(AvroPrimitiveType type) + { + return new AvroPrimitive(type); + } + + public abstract void SerializeV2(IAsyncApiWriter writer); + + public virtual bool TryGetAs(out T result) + where T : AsyncApiAvroSchema + { + result = this as T; + return result != null; + } + + public virtual bool Is() + where T : AsyncApiAvroSchema + { + return this is T; + } + + public virtual T As() + where T : AsyncApiAvroSchema + { + return this as T; + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/Avro/AvroArray.cs b/src/LEGO.AsyncAPI/Models/Avro/AvroArray.cs index 0958fa3d..d00e6f79 100644 --- a/src/LEGO.AsyncAPI/Models/Avro/AvroArray.cs +++ b/src/LEGO.AsyncAPI/Models/Avro/AvroArray.cs @@ -6,21 +6,21 @@ namespace LEGO.AsyncAPI.Models using System.Linq; using LEGO.AsyncAPI.Writers; - public class AvroArray : AvroSchema + public class AvroArray : AsyncApiAvroSchema { public override string Type { get; } = "array"; /// /// The schema of the array's items. /// - public AvroSchema Items { get; set; } + public AsyncApiAvroSchema Items { get; set; } /// /// A map of properties not in the schema, but added as additional metadata. /// public override IDictionary Metadata { get; set; } = new Dictionary(); - public override void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartObject(); writer.WriteOptionalProperty("type", this.Type); diff --git a/src/LEGO.AsyncAPI/Models/Avro/AvroEnum.cs b/src/LEGO.AsyncAPI/Models/Avro/AvroEnum.cs index dbfbb627..ced40502 100644 --- a/src/LEGO.AsyncAPI/Models/Avro/AvroEnum.cs +++ b/src/LEGO.AsyncAPI/Models/Avro/AvroEnum.cs @@ -6,7 +6,7 @@ namespace LEGO.AsyncAPI.Models using System.Linq; using LEGO.AsyncAPI.Writers; - public class AvroEnum : AvroSchema + public class AvroEnum : AsyncApiAvroSchema { public override string Type { get; } = "enum"; @@ -45,7 +45,7 @@ public class AvroEnum : AvroSchema /// public override IDictionary Metadata { get; set; } = new Dictionary(); - public override void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartObject(); writer.WriteOptionalProperty("type", this.Type); diff --git a/src/LEGO.AsyncAPI/Models/Avro/AvroField.cs b/src/LEGO.AsyncAPI/Models/Avro/AvroField.cs index 659f1a4a..15763861 100644 --- a/src/LEGO.AsyncAPI/Models/Avro/AvroField.cs +++ b/src/LEGO.AsyncAPI/Models/Avro/AvroField.cs @@ -34,7 +34,7 @@ public class AvroField : IAsyncApiSerializable /// /// The type of the field. Can be a primitive type, a complex type, or a union. /// - public AvroSchema Type { get; set; } + public AsyncApiAvroSchema Type { get; set; } /// /// The documentation for the field. diff --git a/src/LEGO.AsyncAPI/Models/Avro/AvroFixed.cs b/src/LEGO.AsyncAPI/Models/Avro/AvroFixed.cs index 241e8c1e..e50ec714 100644 --- a/src/LEGO.AsyncAPI/Models/Avro/AvroFixed.cs +++ b/src/LEGO.AsyncAPI/Models/Avro/AvroFixed.cs @@ -6,7 +6,7 @@ namespace LEGO.AsyncAPI.Models using System.Linq; using LEGO.AsyncAPI.Writers; - public class AvroFixed : AvroSchema + public class AvroFixed : AsyncApiAvroSchema { public override string Type { get; } = "fixed"; @@ -36,7 +36,7 @@ public class AvroFixed : AvroSchema /// public override IDictionary Metadata { get; set; } = new Dictionary(); - public override void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartObject(); writer.WriteOptionalProperty("type", this.Type); diff --git a/src/LEGO.AsyncAPI/Models/Avro/AvroMap.cs b/src/LEGO.AsyncAPI/Models/Avro/AvroMap.cs index 8b3028c6..ba762d69 100644 --- a/src/LEGO.AsyncAPI/Models/Avro/AvroMap.cs +++ b/src/LEGO.AsyncAPI/Models/Avro/AvroMap.cs @@ -6,7 +6,7 @@ namespace LEGO.AsyncAPI.Models using System.Linq; using LEGO.AsyncAPI.Writers; - public class AvroMap : AvroSchema + public class AvroMap : AsyncApiAvroSchema { public override string Type { get; } = "map"; @@ -17,7 +17,7 @@ public class AvroMap : AvroSchema /// public override IDictionary Metadata { get; set; } = new Dictionary(); - public override void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartObject(); writer.WriteOptionalProperty("type", this.Type); diff --git a/src/LEGO.AsyncAPI/Models/Avro/AvroPrimitive.cs b/src/LEGO.AsyncAPI/Models/Avro/AvroPrimitive.cs index 4d12c792..fe97023a 100644 --- a/src/LEGO.AsyncAPI/Models/Avro/AvroPrimitive.cs +++ b/src/LEGO.AsyncAPI/Models/Avro/AvroPrimitive.cs @@ -6,7 +6,7 @@ namespace LEGO.AsyncAPI.Models using System.Linq; using LEGO.AsyncAPI.Writers; - public class AvroPrimitive : AvroSchema + public class AvroPrimitive : AsyncApiAvroSchema { public override string Type { get; } @@ -20,7 +20,7 @@ public AvroPrimitive(AvroPrimitiveType type) this.Type = type.GetDisplayName(); } - public override void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeV2(IAsyncApiWriter writer) { writer.WriteValue(this.Type); if (this.Metadata.Any()) diff --git a/src/LEGO.AsyncAPI/Models/Avro/AvroRecord.cs b/src/LEGO.AsyncAPI/Models/Avro/AvroRecord.cs index 04642f5c..bbb085a9 100644 --- a/src/LEGO.AsyncAPI/Models/Avro/AvroRecord.cs +++ b/src/LEGO.AsyncAPI/Models/Avro/AvroRecord.cs @@ -6,7 +6,7 @@ namespace LEGO.AsyncAPI.Models using System.Linq; using LEGO.AsyncAPI.Writers; - public class AvroRecord : AvroSchema + public class AvroRecord : AsyncApiAvroSchema { public override string Type { get; } = "record"; @@ -40,7 +40,7 @@ public class AvroRecord : AvroSchema /// public override IDictionary Metadata { get; set; } = new Dictionary(); - public override void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartObject(); writer.WriteOptionalProperty("type", this.Type); diff --git a/src/LEGO.AsyncAPI/Models/Avro/AvroSchema.cs b/src/LEGO.AsyncAPI/Models/Avro/AvroSchema.cs deleted file mode 100644 index fce1df2d..00000000 --- a/src/LEGO.AsyncAPI/Models/Avro/AvroSchema.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - public abstract class AvroSchema : IAsyncApiSerializable, IAsyncApiReferenceable - { - public abstract string Type { get; } - - /// - /// A map of properties not in the schema, but added as additional metadata. - /// - public abstract IDictionary Metadata { get; set; } - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public static implicit operator AvroSchema(AvroPrimitiveType type) - { - return new AvroPrimitive(type); - } - - 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 abstract void SerializeV2WithoutReference(IAsyncApiWriter writer); - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/Avro/AvroUnion.cs b/src/LEGO.AsyncAPI/Models/Avro/AvroUnion.cs index 5762935d..df5832e1 100644 --- a/src/LEGO.AsyncAPI/Models/Avro/AvroUnion.cs +++ b/src/LEGO.AsyncAPI/Models/Avro/AvroUnion.cs @@ -6,21 +6,21 @@ namespace LEGO.AsyncAPI.Models using System.Linq; using LEGO.AsyncAPI.Writers; - public class AvroUnion : AvroSchema + public class AvroUnion : AsyncApiAvroSchema { public override string Type { get; } = "map"; /// /// The types in this union. /// - public IList Types { get; set; } = new List(); + public IList Types { get; set; } = new List(); /// /// A map of properties not in the schema, but added as additional metadata. /// public override IDictionary Metadata { get; set; } = new Dictionary(); - public override void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartArray(); foreach (var type in this.Types) diff --git a/src/LEGO.AsyncAPI/Models/Avro/LogicalTypes/AvroDuration.cs b/src/LEGO.AsyncAPI/Models/Avro/LogicalTypes/AvroDuration.cs index 01469d0b..44b5b8c6 100644 --- a/src/LEGO.AsyncAPI/Models/Avro/LogicalTypes/AvroDuration.cs +++ b/src/LEGO.AsyncAPI/Models/Avro/LogicalTypes/AvroDuration.cs @@ -11,7 +11,7 @@ public class AvroDuration : AvroFixed public new int Size { get; } = 12; - public override void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartObject(); writer.WriteOptionalProperty("type", this.Type); diff --git a/src/LEGO.AsyncAPI/Models/Avro/LogicalTypes/AvroLogicalType.cs b/src/LEGO.AsyncAPI/Models/Avro/LogicalTypes/AvroLogicalType.cs index 235899a1..cb0f5b3c 100644 --- a/src/LEGO.AsyncAPI/Models/Avro/LogicalTypes/AvroLogicalType.cs +++ b/src/LEGO.AsyncAPI/Models/Avro/LogicalTypes/AvroLogicalType.cs @@ -18,7 +18,7 @@ public virtual void SerializeV2Core(IAsyncApiWriter writer) { } - public override void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartObject(); writer.WriteOptionalProperty("type", this.Type); diff --git a/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiPayload.cs b/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiPayload.cs index 76112af9..1da08177 100644 --- a/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiPayload.cs +++ b/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiPayload.cs @@ -2,7 +2,7 @@ namespace LEGO.AsyncAPI.Models.Interfaces { - public interface IAsyncApiMessagePayload : IAsyncApiSerializable, IAsyncApiReferenceable + public interface IAsyncApiMessagePayload : IAsyncApiSerializable { } } diff --git a/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiReferenceable.cs b/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiReferenceable.cs index 9f0bc64c..b1de15ae 100644 --- a/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiReferenceable.cs +++ b/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiReferenceable.cs @@ -2,23 +2,16 @@ namespace LEGO.AsyncAPI.Models.Interfaces { - using LEGO.AsyncAPI.Writers; - public interface IAsyncApiReferenceable : IAsyncApiSerializable { /// /// Indicates if object is populated with data or is just a reference to the data. /// - bool UnresolvedReference { get; set; } + bool UnresolvedReference { get; } /// /// Reference object. /// AsyncApiReference Reference { get; set; } - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - void SerializeV2WithoutReference(IAsyncApiWriter writer); } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/JsonSchema/AsyncApiSchema.cs b/src/LEGO.AsyncAPI/Models/JsonSchema/AsyncApiJsonSchema.cs similarity index 78% rename from src/LEGO.AsyncAPI/Models/JsonSchema/AsyncApiSchema.cs rename to src/LEGO.AsyncAPI/Models/JsonSchema/AsyncApiJsonSchema.cs index 40dbf4cc..3d36eaf1 100644 --- a/src/LEGO.AsyncAPI/Models/JsonSchema/AsyncApiSchema.cs +++ b/src/LEGO.AsyncAPI/Models/JsonSchema/AsyncApiJsonSchema.cs @@ -11,69 +11,69 @@ namespace LEGO.AsyncAPI.Models /// /// The Schema Object allows the definition of input and output data types. /// - public class AsyncApiJsonSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyncApiSerializable + public class AsyncApiJsonSchema : IAsyncApiExtensible, IAsyncApiSerializable, IAsyncApiMessagePayload { /// /// follow JSON Schema definition. Short text providing information about the data. /// - public string Title { get; set; } + public virtual string Title { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public SchemaType? Type { get; set; } + public virtual SchemaType? Type { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public string Format { get; set; } + public virtual string Format { get; set; } /// /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 /// CommonMark syntax MAY be used for rich text representation. /// - public string Description { get; set; } + public virtual string Description { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public double? Maximum { get; set; } + public virtual double? Maximum { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public double? ExclusiveMaximum { get; set; } + public virtual double? ExclusiveMaximum { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public double? Minimum { get; set; } + public virtual double? Minimum { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public double? ExclusiveMinimum { get; set; } + public virtual double? ExclusiveMinimum { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public int? MaxLength { get; set; } + public virtual int? MaxLength { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public int? MinLength { get; set; } + public virtual int? MinLength { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html /// This string SHOULD be a valid regular expression, according to the ECMA 262 regular expression dialect. /// - public string Pattern { get; set; } + public virtual string Pattern { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public double? MultipleOf { get; set; } + public virtual double? MultipleOf { get; set; } /// /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 @@ -81,7 +81,7 @@ public class AsyncApiJsonSchema : IAsyncApiReferenceable, IAsyncApiExtensible, I /// Unlike JSON Schema, the value MUST conform to the defined type for the Schema Object defined at the same level. /// For example, if type is string, then default can be "foo" but cannot be 1. /// - public AsyncApiAny Default { get; set; } + public virtual AsyncApiAny Default { get; set; } /// /// a value indicating whether relevant only for Schema "properties" definitions. Declares the property as "read only". @@ -91,7 +91,7 @@ public class AsyncApiJsonSchema : IAsyncApiReferenceable, IAsyncApiExtensible, I /// A property MUST NOT be marked as both readOnly and writeOnly being true. /// Default value is false. /// - public bool ReadOnly { get; set; } + public virtual bool ReadOnly { get; set; } /// /// a value indicating whether relevant only for Schema "properties" definitions. Declares the property as "write only". @@ -101,162 +101,156 @@ public class AsyncApiJsonSchema : IAsyncApiReferenceable, IAsyncApiExtensible, I /// A property MUST NOT be marked as both readOnly and writeOnly being true. /// Default value is false. /// - public bool WriteOnly { get; set; } + public virtual bool WriteOnly { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html /// Inline or referenced schema MUST be of a Schema Object and not a standard JSON Schema. /// - public IList AllOf { get; set; } = new List(); + public virtual IList AllOf { get; set; } = new List(); /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html /// Inline or referenced schema MUST be of a Schema Object and not a standard JSON Schema. /// - public IList OneOf { get; set; } = new List(); + public virtual IList OneOf { get; set; } = new List(); /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html /// Inline or referenced schema MUST be of a Schema Object and not a standard JSON Schema. /// - public IList AnyOf { get; set; } = new List(); + public virtual IList AnyOf { get; set; } = new List(); /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html /// Inline or referenced schema MUST be of a Schema Object and not a standard JSON Schema. /// - public AsyncApiJsonSchema Not { get; set; } + public virtual AsyncApiJsonSchema Not { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public AsyncApiJsonSchema Contains { get; set; } + public virtual AsyncApiJsonSchema Contains { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public AsyncApiJsonSchema If { get; set; } + public virtual AsyncApiJsonSchema If { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public AsyncApiJsonSchema Then { get; set; } + public virtual AsyncApiJsonSchema Then { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public AsyncApiJsonSchema Else { get; set; } + public virtual AsyncApiJsonSchema Else { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public ISet Required { get; set; } = new HashSet(); + public virtual ISet Required { get; set; } = new HashSet(); /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html /// Value MUST be an object and not an array. Inline or referenced schema MUST be of a Schema Object /// and not a standard JSON Schema. items MUST be present if the type is array. /// - public AsyncApiJsonSchema Items { get; set; } + public virtual AsyncApiJsonSchema Items { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html /// Value MUST be an object and not an array. Inline or referenced schema MUST be of a Schema Object /// and not a standard JSON Schema. items MUST be present if the type is array. /// - public AsyncApiJsonSchema AdditionalItems { get; set; } + public virtual AsyncApiJsonSchema AdditionalItems { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public int? MaxItems { get; set; } + public virtual int? MaxItems { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public int? MinItems { get; set; } + public virtual int? MinItems { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public bool? UniqueItems { get; set; } + public virtual bool? UniqueItems { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html /// Property definitions MUST be a Schema Object and not a standard JSON Schema (inline or referenced). /// - public IDictionary Properties { get; set; } = new Dictionary(); + public virtual IDictionary Properties { get; set; } = new Dictionary(); /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public int? MaxProperties { get; set; } + public virtual int? MaxProperties { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public int? MinProperties { get; set; } + public virtual int? MinProperties { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html /// Value can be boolean or object. Inline or referenced schema /// MUST be of a Schema Object and not a standard JSON Schema. /// - public AsyncApiJsonSchema AdditionalProperties { get; set; } + public virtual AsyncApiJsonSchema AdditionalProperties { get; set; } - public IDictionary PatternProperties { get; set; } = new Dictionary(); + public virtual IDictionary PatternProperties { get; set; } = new Dictionary(); /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public AsyncApiJsonSchema PropertyNames { get; set; } + public virtual AsyncApiJsonSchema PropertyNames { get; set; } /// /// adds support for polymorphism. /// The discriminator is the schema property name that is used to differentiate between other schema that inherit this schema. /// - public string Discriminator { get; set; } + public virtual string Discriminator { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public IList Enum { get; set; } = new List(); + public virtual IList Enum { get; set; } = new List(); /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public IList Examples { get; set; } = new List(); + public virtual IList Examples { get; set; } = new List(); /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public AsyncApiAny Const { get; set; } + public virtual AsyncApiAny Const { get; set; } /// /// a value indicating whether allows sending a null value for the defined schema. Default value is false. /// - public bool Nullable { get; set; } + public virtual bool Nullable { get; set; } /// /// additional external documentation for this schema. /// - public AsyncApiExternalDocumentation ExternalDocs { get; set; } + public virtual AsyncApiExternalDocumentation ExternalDocs { get; set; } /// /// a value indicating whether specifies that a schema is deprecated and SHOULD be transitioned out of usage. /// Default value is false. /// - public bool Deprecated { get; set; } + public virtual bool Deprecated { get; set; } - /// - public bool UnresolvedReference { get; set; } + public virtual IDictionary Extensions { get; set; } = new Dictionary(); - /// - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); - - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public virtual void SerializeV2(IAsyncApiWriter writer) { writer.WriteStartObject(); @@ -420,55 +414,5 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteEndObject(); } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - var target = this; - - var settings = writer.GetSettings(); - - if (this.Reference != null) - { - if (!settings.ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - // If Loop is detected then just Serialize as a reference. - if (!settings.LoopDetector.PushLoop(this)) - { - settings.LoopDetector.SaveLoop(this); - this.Reference.SerializeV2(writer); - return; - } - - target = this.GetReferenced(this.Reference.HostDocument); - } - - target.SerializeV2WithoutReference(writer); - - if (this.Reference != null) - { - settings.LoopDetector.PopLoop(); - } - } - - public AsyncApiJsonSchema GetReferenced(AsyncApiDocument document) - { - if (this.Reference != null && document != null) - { - return document.ResolveReference(this.Reference); - } - else - { - return this; - } - } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/MessagePayloadExtensions.cs b/src/LEGO.AsyncAPI/Models/MessagePayloadExtensions.cs new file mode 100644 index 00000000..069869b6 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/MessagePayloadExtensions.cs @@ -0,0 +1,30 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + using System; + + public static class MessagePayloadExtensions + { + public static bool TryGetAs(this IAsyncApiMessagePayload payload, out T result) + where T : class, IAsyncApiMessagePayload + { + result = payload as T; + return result != null; + } + + public static T As(this IAsyncApiMessagePayload payload) + where T : class, IAsyncApiMessagePayload + { + return payload as T; + } + + public static bool Is(this IAsyncApiMessagePayload payload) + where T : class, IAsyncApiMessagePayload + { + return payload is T; + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiAvroSchemaReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiAvroSchemaReference.cs new file mode 100644 index 00000000..decfc0c6 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiAvroSchemaReference.cs @@ -0,0 +1,80 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class AsyncApiAvroSchemaReference : AsyncApiAvroSchema, IAsyncApiReferenceable + { + private AsyncApiAvroSchema target; + + private AsyncApiAvroSchema Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference); + return this.target; + } + } + + public AsyncApiAvroSchemaReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.Schema); + } + + public override string Type => this.Target?.Type; + + public override IDictionary Metadata { get => this.Target?.Metadata; set => this.Target.Metadata = value; } + + public bool UnresolvedReference { get { return this.Target == null; } } + + public AsyncApiReference Reference { get; set; } + + public override T As() + { + if (this.Target == null) + { + return null; + } + return this.Target.As(); + } + + public override bool Is() + { + if (this.Target == null) + { + return false; + } + return this.Target.Is(); + } + + public override bool TryGetAs(out T result) + { + if (this.Target == null) + { + result = default; + return false; + } + return this.Target.TryGetAs(out result); + } + + public override 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.Target.SerializeV2(writer); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiBindingsReference{TBinding}.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiBindingsReference{TBinding}.cs new file mode 100644 index 00000000..378d67d0 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiBindingsReference{TBinding}.cs @@ -0,0 +1,135 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class AsyncApiBindingsReference : AsyncApiBindings, IAsyncApiReferenceable + where TBinding : IBinding + { + public bool UnresolvedReference { get => this.target == null; } + + public AsyncApiReference Reference { get; set; } + + private AsyncApiBindings target; + + private AsyncApiBindings Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference>(this.Reference); + return this.target; + } + } + + public override void Add(TBinding binding) + { + this.Target.Add(binding); + } + + public override ICollection Keys => this.Target.Keys; + + public override ICollection Values => this.Target.Values; + + public override int Count => this.Target.Count; + + public override bool IsReadOnly => this.Target.IsReadOnly; + + public AsyncApiBindingsReference(string reference) + { + ReferenceType type = ReferenceType.None; + if (typeof(TBinding) == typeof(IServerBinding)) + { + type = ReferenceType.ServerBindings; + } + if (typeof(TBinding) == typeof(IMessageBinding)) + { + type = ReferenceType.MessageBindings; + } + if (typeof(TBinding) == typeof(IOperationBinding)) + { + type = ReferenceType.OperationBindings; + } + if (typeof(TBinding) == typeof(IChannelBinding)) + { + type = ReferenceType.ChannelBindings; + } + + if (type == ReferenceType.None) + { + throw new NotImplementedException($"Binding type '{typeof(TBinding)}' not supported."); + } + + this.Reference = new AsyncApiReference(reference, type); + } + + public override 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.Target.SerializeV2(writer); + } + + public override void Add(string key, TBinding value) + { + this.Target.Add(key, value); + } + + public override bool ContainsKey(string key) + { + return this.Target.ContainsKey(key); + } + + public override bool Remove(string key) + { + return this.Target.Remove(key); + } + + public override bool TryGetValue(string key, out TBinding value) + { + return this.Target.TryGetValue(key, out value); + } + + public override void Add(KeyValuePair item) + { + this.Target.Add(item); + } + + public override void Clear() + { + this.Target.Clear(); + } + + public override bool Contains(KeyValuePair item) + { + return this.Target.Contains(item); + } + + public override void CopyTo(KeyValuePair[] array, int arrayIndex) + { + this.Target.CopyTo(array, arrayIndex); + } + + public override bool Remove(KeyValuePair item) + { + return this.Target.Remove(item); + } + + public override IEnumerator> GetEnumerator() + { + return this.Target.GetEnumerator(); + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiChannelReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiChannelReference.cs new file mode 100644 index 00000000..437c4e50 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiChannelReference.cs @@ -0,0 +1,59 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class AsyncApiChannelReference : AsyncApiChannel, IAsyncApiReferenceable + { + private AsyncApiChannel target; + + private AsyncApiChannel Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference.Reference); + return this.target; + } + } + + public AsyncApiChannelReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.Channel); + } + + public override string Description { get => this.Target?.Description; set => this.Target.Description = value; } + + public override IList Servers { get => this.Target?.Servers; set => this.Target.Servers = value; } + + public override AsyncApiOperation Subscribe { get => this.Target?.Subscribe; set => this.Target.Subscribe = value; } + + public override AsyncApiOperation Publish { get => this.Target?.Publish; set => this.Target.Publish = value; } + + public override IDictionary Parameters { get => this.Target?.Parameters; set => this.Target.Parameters = value; } + + public override AsyncApiBindings Bindings { get => this.Target?.Bindings; set => this.Target.Bindings = value; } + + public override IDictionary Extensions { get => this.Target?.Extensions; set => this.Target.Extensions = value; } + + public AsyncApiReference Reference { get; set; } + + public bool UnresolvedReference { get { return this.Target == null; } } + + public override void SerializeV2(IAsyncApiWriter writer) + { + if (!writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + else + { + this.Reference.Workspace = writer.Workspace; + this.Target.SerializeV2(writer); + } + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiCorrelationIdReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiCorrelationIdReference.cs new file mode 100644 index 00000000..89784131 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiCorrelationIdReference.cs @@ -0,0 +1,54 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// The definition of a correlation ID this application MAY use. + /// + public class AsyncApiCorrelationIdReference : AsyncApiCorrelationId, IAsyncApiReferenceable + { + private AsyncApiCorrelationId target; + + private AsyncApiCorrelationId Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference.Reference); + return this.target; + } + } + + public AsyncApiCorrelationIdReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.CorrelationId); + } + + public override string Description { get => this.Target?.Description; set => this.Target.Description = value; } + + public override string Location { get => this.Target?.Location; set => this.Target.Location = value; } + + public override IDictionary Extensions { get => this.Target?.Extensions; set => this.Target.Extensions = value; } + + public AsyncApiReference Reference { get; set; } + + public bool UnresolvedReference { get { return this.Target == null; } } + + public override void SerializeV2(IAsyncApiWriter writer) + { + if (!writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + else + { + this.Reference.Workspace = writer.Workspace; + this.Target.SerializeV2(writer); + } + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiJsonSchemaReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiJsonSchemaReference.cs new file mode 100644 index 00000000..a7211559 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiJsonSchemaReference.cs @@ -0,0 +1,319 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class AsyncApiJsonSchemaReference : AsyncApiJsonSchema, IAsyncApiReferenceable + { + private AsyncApiJsonSchema target; + + private AsyncApiJsonSchema Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference); + return this.target; + } + } + + public AsyncApiJsonSchemaReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.Schema); + } + + public AsyncApiReference Reference { get; set; } + + public bool UnresolvedReference + { + get { return this.Target == null; } + } + + public override string Title + { + get => this.Target?.Title; + set => this.Target.Title = value; + } + + public override SchemaType? Type + { + get => this.Target?.Type; + set => this.Target.Type = value; + } + + public override string Format + { + get => this.Target?.Format; + set => this.Target.Format = value; + } + + public override string Description + { + get => this.Target?.Description; + set => this.Target.Description = value; + } + + public override double? Maximum + { + get => this.Target?.Maximum; + set => this.Target.Maximum = value; + } + + public override double? ExclusiveMaximum + { + get => this.Target?.ExclusiveMaximum; + set => this.Target.ExclusiveMaximum = value; + } + + public override double? Minimum + { + get => this.Target?.Minimum; + set => this.Target.Minimum = value; + } + + public override double? ExclusiveMinimum + { + get => this.Target?.ExclusiveMinimum; + set => this.Target.ExclusiveMinimum = value; + } + + public override int? MaxLength + { + get => this.Target?.MaxLength; + set => this.Target.MaxLength = value; + } + + public override int? MinLength + { + get => this.Target?.MinLength; + set => this.Target.MinLength = value; + } + + public override string Pattern + { + get => this.Target?.Pattern; + set => this.Target.Pattern = value; + } + + public override double? MultipleOf + { + get => this.Target?.MultipleOf; + set => this.Target.MultipleOf = value; + } + + public override AsyncApiAny Default + { + get => this.Target?.Default; + set => this.Target.Default = value; + } + + public override bool ReadOnly + { + get => this.Target.ReadOnly; + set => this.Target.ReadOnly = value; + } + + public override bool WriteOnly + { + get => this.Target.WriteOnly; + set => this.Target.WriteOnly = value; + } + + public override IList AllOf + { + get => this.Target?.AllOf; + set => this.Target.AllOf = value; + } + + public override IList OneOf + { + get => this.Target?.OneOf; + set => this.Target.OneOf = value; + } + + public override IList AnyOf + { + get => this.Target?.AnyOf; + set => this.Target.AnyOf = value; + } + + public override AsyncApiJsonSchema Not + { + get => this.Target?.Not; + set => this.Target.Not = value; + } + + public override AsyncApiJsonSchema Contains + { + get => this.Target?.Contains; + set => this.Target.Contains = value; + } + + public override AsyncApiJsonSchema If + { + get => this.Target?.If; + set => this.Target.If = value; + } + + public override AsyncApiJsonSchema Then + { + get => this.Target?.Then; + set => this.Target.Then = value; + } + + public override AsyncApiJsonSchema Else + { + get => this.Target?.Else; + set => this.Target.Else = value; + } + + public override ISet Required + { + get => this.Target?.Required; + set => this.Target.Required = value; + } + + public override AsyncApiJsonSchema Items + { + get => this.Target?.Items; + set => this.Target.Items = value; + } + + public override AsyncApiJsonSchema AdditionalItems + { + get => this.Target?.AdditionalItems; + set => this.Target.AdditionalItems = value; + } + + public override int? MaxItems + { + get => this.Target?.MaxItems; + set => this.Target.MaxItems = value; + } + + public override int? MinItems + { + get => this.Target?.MinItems; + set => this.Target.MinItems = value; + } + + public override bool? UniqueItems + { + get => this.Target?.UniqueItems; + set => this.Target.UniqueItems = value; + } + + public override IDictionary Properties + { + get => this.Target?.Properties; + set => this.Target.Properties = value; + } + + public override int? MaxProperties + { + get => this.Target?.MaxProperties; + set => this.Target.MaxProperties = value; + } + + public override int? MinProperties + { + get => this.Target?.MinProperties; + set => this.Target.MinProperties = value; + } + + public override AsyncApiJsonSchema AdditionalProperties + { + get => this.Target?.AdditionalProperties; + set => this.Target.AdditionalProperties = value; + } + + public override IDictionary PatternProperties + { + get => this.Target?.PatternProperties; + set => this.Target.PatternProperties = value; + } + + public override AsyncApiJsonSchema PropertyNames + { + get => this.Target?.PropertyNames; + set => this.Target.PropertyNames = value; + } + + public override string Discriminator + { + get => this.Target?.Discriminator; + set => this.Target.Discriminator = value; + } + + public override IList Enum + { + get => this.Target?.Enum; + set => this.Target.Enum = value; + } + + public override IList Examples + { + get => this.Target?.Examples; + set => this.Target.Examples = value; + } + + public override AsyncApiAny Const + { + get => this.Target?.Const; + set => this.Target.Const = value; + } + + public override bool Nullable + { + get => this.Target.Nullable; + set => this.Target.Nullable = value; + } + + public override AsyncApiExternalDocumentation ExternalDocs + { + get => this.Target?.ExternalDocs; + set => this.Target.ExternalDocs = value; + } + + public override bool Deprecated + { + get => this.Target.Deprecated; + set => this.Target.Deprecated = value; + } + + public override IDictionary Extensions + { + get => this.Target?.Extensions; + set => this.Target.Extensions = value; + } + + public override void SerializeV2(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + var settings = writer.GetSettings(); + if (!settings.ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + + this.Reference.Workspace = writer.Workspace; + // If Loop is detected then just Serialize as a reference. + if (!settings.LoopDetector.PushLoop(this)) + { + settings.LoopDetector.SaveLoop(this); + this.Reference.SerializeV2(writer); + return; + } + + this.Target.SerializeV2(writer); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiMessageReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiMessageReference.cs new file mode 100644 index 00000000..05aa8ed8 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiMessageReference.cs @@ -0,0 +1,80 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// The definition of a message this application MAY use. + /// + public class AsyncApiMessageReference : AsyncApiMessage, IAsyncApiReferenceable + { + private AsyncApiMessage target; + + private AsyncApiMessage Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference.Reference); + return this.target; + } + } + + public AsyncApiMessageReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.Message); + } + + public override string MessageId { get => this.Target?.MessageId; set => this.Target.MessageId = value; } + + public override AsyncApiJsonSchema Headers { get => this.Target?.Headers; set => this.Target.Headers = value; } + + public override IAsyncApiMessagePayload Payload { get => this.Target?.Payload; set => this.Target.Payload = value; } + + public override AsyncApiCorrelationId CorrelationId { get => this.Target?.CorrelationId; set => this.Target.CorrelationId = value; } + + public override string SchemaFormat { get => this.Target?.SchemaFormat; set => this.Target.SchemaFormat = value; } + + public override string ContentType { get => this.Target?.ContentType; set => this.Target.ContentType = value; } + + public override string Name { get => this.Target?.Name; set => this.Target.Name = value; } + + public override string Title { get => this.Target?.Title; set => this.Target.Title = value; } + + public override string Summary { get => this.Target?.Summary; set => this.Target.Summary = value; } + + public override string Description { get => this.Target?.Description; set => this.Target.Description = value; } + + public override IList Tags { get => this.Target?.Tags; set => this.Target.Tags = value; } + + public override AsyncApiExternalDocumentation ExternalDocs { get => this.Target?.ExternalDocs; set => this.Target.ExternalDocs = value; } + + public override AsyncApiBindings Bindings { get => this.Target?.Bindings; set => this.Target.Bindings = value; } + + public override IList Examples { get => this.Target?.Examples; set => this.Target.Examples = value; } + + public override IList Traits { get => this.Target?.Traits; set => this.Target.Traits = value; } + + public override IDictionary Extensions { get => this.Target?.Extensions; set => this.Target.Extensions = value; } + + public AsyncApiReference Reference { get; set; } + + public bool UnresolvedReference { get { return this.Target == null; } } + + public override void SerializeV2(IAsyncApiWriter writer) + { + if (!writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + else + { + this.Reference.Workspace = writer.Workspace; + this.Target.SerializeV2(writer); + } + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiMessageTraitReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiMessageTraitReference.cs new file mode 100644 index 00000000..118ad4b9 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiMessageTraitReference.cs @@ -0,0 +1,76 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// The definition of a message trait this application MAY use. + /// + public class AsyncApiMessageTraitReference : AsyncApiMessageTrait, IAsyncApiReferenceable + { + private AsyncApiMessageTrait target; + + private AsyncApiMessageTrait Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference); + return this.target; + } + } + + public AsyncApiMessageTraitReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.MessageTrait); + } + + public override string MessageId { get => this.Target?.MessageId; set => this.Target.MessageId = value; } + + public override AsyncApiJsonSchema Headers { get => this.Target?.Headers; set => this.Target.Headers = value; } + + public override AsyncApiCorrelationId CorrelationId { get => this.Target?.CorrelationId; set => this.Target.CorrelationId = value; } + + public override string SchemaFormat { get => this.Target?.SchemaFormat; set => this.Target.SchemaFormat = value; } + + public override string ContentType { get => this.Target?.ContentType; set => this.Target.ContentType = value; } + + public override string Name { get => this.Target?.Name; set => this.Target.Name = value; } + + public override string Title { get => this.Target?.Title; set => this.Target.Title = value; } + + public override string Summary { get => this.Target?.Summary; set => this.Target.Summary = value; } + + public override string Description { get => this.Target?.Description; set => this.Target.Description = value; } + + public override IList Tags { get => this.Target?.Tags; set => this.Target.Tags = value; } + + public override AsyncApiExternalDocumentation ExternalDocs { get => this.Target?.ExternalDocs; set => this.Target.ExternalDocs = value; } + + public override AsyncApiBindings Bindings { get => this.Target?.Bindings; set => this.Target.Bindings = value; } + + public override IList Examples { get => this.Target?.Examples; set => this.Target.Examples = value; } + + public override IDictionary Extensions { get => this.Target?.Extensions; set => this.Target.Extensions = value; } + + public AsyncApiReference Reference { get; set; } + + public bool UnresolvedReference { get { return this.Target == null; } } + + public override void SerializeV2(IAsyncApiWriter writer) + { + if (!writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + else + { + this.Reference.Workspace = writer.Workspace; + this.Target.SerializeV2(writer); + } + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiOperationTraitReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiOperationTraitReference.cs new file mode 100644 index 00000000..36bb3bd9 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiOperationTraitReference.cs @@ -0,0 +1,62 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// The definition of an operation trait this application MAY use. + /// + public class AsyncApiOperationTraitReference : AsyncApiOperationTrait, IAsyncApiReferenceable + { + private AsyncApiOperationTrait target; + + private AsyncApiOperationTrait Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference); + return this.target; + } + } + + public AsyncApiOperationTraitReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.OperationTrait); + } + + public override string OperationId { get => this.Target?.OperationId; set => this.Target.OperationId = value; } + + public override string Summary { get => this.Target?.Summary; set => this.Target.Summary = value; } + + public override string Description { get => this.Target?.Description; set => this.Target.Description = value; } + + public override IList Tags { get => this.Target?.Tags; set => this.Target.Tags = value; } + + public override AsyncApiExternalDocumentation ExternalDocs { get => this.Target?.ExternalDocs; set => this.Target.ExternalDocs = value; } + + public override AsyncApiBindings Bindings { get => this.Target?.Bindings; set => this.Target.Bindings = value; } + + public override IDictionary Extensions { get => this.Target?.Extensions; set => this.Target.Extensions = value; } + + public AsyncApiReference Reference { get; set; } + + public bool UnresolvedReference { get { return this.Target == null; } } + + public override void SerializeV2(IAsyncApiWriter writer) + { + if (!writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + else + { + this.Reference.Workspace = writer.Workspace; + this.Target.SerializeV2(writer); + } + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiParameterReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiParameterReference.cs new file mode 100644 index 00000000..1c570ca0 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiParameterReference.cs @@ -0,0 +1,56 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// The definition of a parameter this application MAY use. + /// + public class AsyncApiParameterReference : AsyncApiParameter, IAsyncApiReferenceable + { + private AsyncApiParameter target; + + private AsyncApiParameter Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference); + return this.target; + } + } + + public AsyncApiParameterReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.Parameter); + } + + public override string Description { get => this.Target?.Description; set => this.Target.Description = value; } + + public override AsyncApiJsonSchema Schema { get => this.Target?.Schema; set => this.Target.Schema = value; } + + public override string Location { get => this.Target?.Location; set => this.Target.Location = value; } + + public override IDictionary Extensions { get => this.Target?.Extensions; set => this.Target.Extensions = value; } + + public AsyncApiReference Reference { get; set; } + + public bool UnresolvedReference { get { return this.Target == null; } } + + public override void SerializeV2(IAsyncApiWriter writer) + { + if (!writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + else + { + this.Reference.Workspace = writer.Workspace; + this.Target.SerializeV2(writer); + } + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiSecuritySchemeReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiSecuritySchemeReference.cs new file mode 100644 index 00000000..7e8440ef --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiSecuritySchemeReference.cs @@ -0,0 +1,67 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// The definition of a security scheme this application MAY use. + /// + public class AsyncApiSecuritySchemeReference : AsyncApiSecurityScheme, IAsyncApiReferenceable + { + private AsyncApiSecurityScheme target; + + private AsyncApiSecurityScheme Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference); + return this.target; + } + } + + public AsyncApiSecuritySchemeReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.SecurityScheme); + } + + public override SecuritySchemeType Type { get => this.Target.Type; set => this.Target.Type = value; } + + public override string Description { get => this.Target?.Description; set => this.Target.Description = value; } + + public override string Name { get => this.Target?.Name; set => this.Target.Name = value; } + + public override ParameterLocation In { get => this.Target.In; set => this.Target.In = value; } + + public override string Scheme { get => this.Target?.Scheme; set => this.Target.Scheme = value; } + + public override string BearerFormat { get => this.Target?.BearerFormat; set => this.Target.BearerFormat = value; } + + public override AsyncApiOAuthFlows Flows { get => this.Target?.Flows; set => this.Target.Flows = value; } + + public override Uri OpenIdConnectUrl { get => this.Target?.OpenIdConnectUrl; set => this.Target.OpenIdConnectUrl = value; } + + public override IDictionary Extensions { get => this.Target?.Extensions; set => this.Target.Extensions = value; } + + public AsyncApiReference Reference { get; set; } + + public bool UnresolvedReference { get { return this.Target == null; } } + + public override void SerializeV2(IAsyncApiWriter writer) + { + if (!writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + else + { + this.Reference.Workspace = writer.Workspace; + this.Target.SerializeV2(writer); + } + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiServerReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiServerReference.cs new file mode 100644 index 00000000..1ef0d510 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiServerReference.cs @@ -0,0 +1,66 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// The definition of a server this application MAY connect to. + /// + public class AsyncApiServerReference : AsyncApiServer, IAsyncApiReferenceable + { + private AsyncApiServer target; + + private AsyncApiServer Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference); + return this.target; + } + } + + public AsyncApiServerReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.Server); + } + + public override string Url { get => this.Target?.Url; set => this.Target.Url = value; } + + public override string Protocol { get => this.Target?.Protocol; set => this.Target.Protocol = value; } + + public override string ProtocolVersion { get => this.Target?.ProtocolVersion; set => this.Target.ProtocolVersion = value; } + + public override string Description { get => this.Target?.Description; set => this.Target.Description = value; } + + public override IDictionary Variables { get => this.Target?.Variables; set => this.Target.Variables = value; } + + public override IList Security { get => this.Target?.Security; set => this.Target.Security = value; } + + public override IList Tags { get => this.Target?.Tags; set => this.Target.Tags = value; } + + public override AsyncApiBindings Bindings { get => this.Target?.Bindings; set => this.Target.Bindings = value; } + + public override IDictionary Extensions { get => this.Target?.Extensions; set => this.Target.Extensions = value; } + + public AsyncApiReference Reference { get; set; } + + public bool UnresolvedReference { get { return this.Target == null; } } + + public override void SerializeV2(IAsyncApiWriter writer) + { + if (!writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + else + { + this.Reference.Workspace = writer.Workspace; + this.Target.SerializeV2(writer); + } + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/References/AsyncApiServerVariableReference.cs b/src/LEGO.AsyncAPI/Models/References/AsyncApiServerVariableReference.cs new file mode 100644 index 00000000..c5bb968d --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/References/AsyncApiServerVariableReference.cs @@ -0,0 +1,57 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// The definition of a server variable this application MAY use. + /// + public class AsyncApiServerVariableReference : AsyncApiServerVariable, IAsyncApiReferenceable + { + private AsyncApiServerVariable target; + + private AsyncApiServerVariable Target + { + get + { + this.target ??= this.Reference.Workspace?.ResolveReference(this.Reference); + return this.target; + } + } + + public AsyncApiServerVariableReference(string reference) + { + this.Reference = new AsyncApiReference(reference, ReferenceType.ServerVariable); + } + public override IList Enum { get => this.Target?.Enum; set => this.Target.Enum = value; } + + public override string Default { get => this.Target?.Default; set => this.Target.Default = value; } + + public override string Description { get => this.Target?.Description; set => this.Target.Description = value; } + + public override IList Examples { get => this.Target?.Examples; set => this.Target.Examples = value; } + + public override IDictionary Extensions { get => this.Target?.Extensions; set => this.Target.Extensions = value; } + + public AsyncApiReference Reference { get; set; } + + public bool UnresolvedReference { get { return this.Target == null; } } + + public override void SerializeV2(IAsyncApiWriter writer) + { + if (!writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + else + { + this.Reference.Workspace = writer.Workspace; + this.Target.SerializeV2(writer); + } + } + } +} diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs deleted file mode 100644 index e541bd1d..00000000 --- a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Services -{ - using System; - using System.Collections.Generic; - using System.Linq; - 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. - /// - internal class AsyncApiReferenceResolver : AsyncApiVisitorBase - { - private AsyncApiDocument currentDocument; - private List errors = new List(); - - public AsyncApiReferenceResolver( - AsyncApiDocument currentDocument) - { - this.currentDocument = currentDocument; - } - - public IEnumerable Errors - { - get - { - return this.errors; - } - } - - public override void Visit(IAsyncApiReferenceable referenceable) - { - if (referenceable.Reference != null) - { - referenceable.Reference.HostDocument = this.currentDocument; - } - } - - public override void Visit(AsyncApiComponents components) - { - this.ResolveMap(components.Parameters); - this.ResolveMap(components.Channels); - this.ResolveMap(components.Schemas); - this.ResolveMap(components.Servers); - this.ResolveMap(components.CorrelationIds); - this.ResolveMap(components.MessageTraits); - this.ResolveMap(components.OperationTraits); - this.ResolveMap(components.SecuritySchemes); - this.ResolveMap(components.ChannelBindings); - this.ResolveMap(components.MessageBindings); - this.ResolveMap(components.OperationBindings); - this.ResolveMap(components.ServerBindings); - this.ResolveMap(components.Messages); - } - - public override void Visit(AsyncApiDocument doc) - { - this.ResolveMap(doc.Servers); - this.ResolveMap(doc.Channels); - } - - public override void Visit(AsyncApiChannel channel) - { - this.ResolveMap(channel.Parameters); - this.ResolveObject(channel.Bindings, r => channel.Bindings = r); - } - - public override void Visit(AsyncApiMessageTrait trait) - { - this.ResolveObject(trait.CorrelationId, r => trait.CorrelationId = r); - this.ResolveObject(trait.Headers, r => trait.Headers = r); - } - - /// - /// Resolve all references used in an operation. - /// - public override void Visit(AsyncApiOperation operation) - { - this.ResolveList(operation.Message); - this.ResolveList(operation.Traits); - this.ResolveObject(operation.Bindings, r => operation.Bindings = r); - } - - public override void Visit(AsyncApiMessage message) - { - this.ResolveObject(message.Headers, r => message.Headers = r); - - // #ToFix Resolve references correctly - if (message.Payload is AsyncApiJsonSchemaPayload) - { - this.ResolveObject(message.Payload as AsyncApiJsonSchemaPayload, r => message.Payload = new AsyncApiJsonSchemaPayload(r)); - } - - this.ResolveList(message.Traits); - this.ResolveObject(message.CorrelationId, r => message.CorrelationId = r); - this.ResolveObject(message.Bindings, r => message.Bindings = r); - } - - public override void Visit(AsyncApiServer server) - { - this.ResolveObject(server.Bindings, r => server.Bindings = r); - } - - /// - /// Resolve all references to SecuritySchemes. - /// - public override void Visit(AsyncApiSecurityRequirement securityRequirement) - { - foreach (var scheme in securityRequirement.Keys.ToList()) - { - this.ResolveObject(scheme, (resolvedScheme) => - { - if (resolvedScheme != null) - { - // If scheme was unresolved - // copy Scopes and remove old unresolved scheme - var scopes = securityRequirement[scheme]; - securityRequirement.Remove(scheme); - securityRequirement.Add(resolvedScheme, scopes); - } - }); - } - } - - /// - /// Resolve all references to parameters. - /// - public override void Visit(IList parameters) - { - this.ResolveList(parameters); - } - - /// - /// Resolve all references used in a parameter. - /// - public override void Visit(AsyncApiParameter parameter) - { - this.ResolveObject(parameter.Schema, r => parameter.Schema = r); - } - - /// - /// Resolve all references used in a schema. - /// - public override void Visit(AsyncApiJsonSchema schema) - { - this.ResolveObject(schema.Items, r => schema.Items = r); - this.ResolveList(schema.OneOf); - this.ResolveList(schema.AllOf); - this.ResolveList(schema.AnyOf); - this.ResolveObject(schema.Contains, r => schema.Contains = r); - this.ResolveObject(schema.Else, r => schema.Else = r); - this.ResolveObject(schema.If, r => schema.If = r); - this.ResolveObject(schema.Items, r => schema.Items = r); - this.ResolveObject(schema.Not, r => schema.Not = r); - this.ResolveObject(schema.Then, r => schema.Then = r); - this.ResolveObject(schema.PropertyNames, r => schema.PropertyNames = r); - this.ResolveObject(schema.AdditionalProperties, r => schema.AdditionalProperties = r); - this.ResolveMap(schema.Properties); - } - - private void ResolveObject(T entity, Action assign) - where T : class, IAsyncApiReferenceable, new() - { - if (entity == null) - { - return; - } - - if (this.IsUnresolvedReference(entity)) - { - assign(this.ResolveReference(entity.Reference)); - } - } - - private void ResolveList(IList list) - where T : class, IAsyncApiReferenceable, new() - { - if (list == null) - { - return; - } - - for (int i = 0; i < list.Count; i++) - { - var entity = list[i]; - if (this.IsUnresolvedReference(entity)) - { - list[i] = this.ResolveReference(entity.Reference); - } - } - } - - private void ResolveMap(IDictionary map) - where T : class, IAsyncApiReferenceable, new() - { - if (map == null) - { - return; - } - - foreach (var key in map.Keys.ToList()) - { - var entity = map[key]; - if (this.IsUnresolvedReference(entity)) - { - map[key] = this.ResolveReference(entity.Reference); - } - } - } - - private T ResolveReference(AsyncApiReference reference) - where T : class, IAsyncApiReferenceable, new() - { - // external references are resolved by the AsyncApiExternalReferenceResolver - if (reference.IsExternal) - { - return new() - { - UnresolvedReference = true, - Reference = reference, - }; - } - - try - { - var resolvedReference = this.currentDocument.ResolveReference(reference) as T; - if (resolvedReference == null) - { - throw new AsyncApiException($"Cannot resolve reference '{reference.Reference}' to '{typeof(T).Name}'."); - } - - return resolvedReference; - } - catch (AsyncApiException ex) - { - this.errors.Add(new AsyncApiReferenceError(ex)); - return null; - } - } - - private bool IsUnresolvedReference(IAsyncApiReferenceable possibleReference) - { - return (possibleReference != null && possibleReference.UnresolvedReference); - } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs index a4f7b0b1..09ffe62e 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs @@ -55,11 +55,15 @@ public virtual void Visit(AsyncApiDocument doc) { } - public virtual void Visit(AsyncApiJsonSchemaPayload jsonPayload) + public virtual void Visit(IAsyncApiMessagePayload payload) { } - public virtual void Visit(AsyncApiAvroSchemaPayload avroPayload) + public virtual void Visit(AsyncApiAvroSchema item) + { + } + + public virtual void Visit(AsyncApiJsonSchema item) { } @@ -152,13 +156,6 @@ public virtual void Visit(AsyncApiExternalDocumentation externalDocs) { } - /// - /// Visits . - /// - public virtual void Visit(AsyncApiJsonSchema schema) - { - } - public virtual void Visit(AsyncApiMessage message) { } diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs index be230589..2ee2226b 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs @@ -1,4 +1,4 @@ -// Copyright (c) The LEGO Group. All rights reserved. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Services { @@ -221,8 +221,9 @@ internal void Walk(AsyncApiOAuthFlow oAuthFlow) internal void Walk(AsyncApiSecurityScheme securityScheme, bool isComponent = false) { - if (securityScheme == null || this.ProcessAsReference(securityScheme, isComponent)) + if (securityScheme is AsyncApiSecuritySchemeReference) { + this.Walk(securityScheme as IAsyncApiReferenceable); return; } @@ -270,22 +271,21 @@ internal void Walk(AsyncApiExternalDocumentation externalDocs) internal void Walk(AsyncApiChannel channel, bool isComponent = false) { - if (channel == null || this.ProcessAsReference(channel, isComponent)) + if (channel == null) { return; } - - this.visitor.Visit(channel); - - if (channel != null) + if (channel is AsyncApiChannelReference) { - this.Walk(AsyncApiConstants.Subscribe, () => this.Walk(channel.Subscribe)); - this.Walk(AsyncApiConstants.Publish, () => this.Walk(channel.Publish)); - - this.Walk(AsyncApiConstants.Bindings, () => this.Walk(channel.Bindings)); - this.Walk(AsyncApiConstants.Parameters, () => this.Walk(channel.Parameters)); + this.Walk(channel as IAsyncApiReferenceable); + return; } + this.Walk(AsyncApiConstants.Subscribe, () => this.Walk(channel.Subscribe)); + this.Walk(AsyncApiConstants.Publish, () => this.Walk(channel.Publish)); + + this.Walk(AsyncApiConstants.Bindings, () => this.Walk(channel.Bindings)); + this.Walk(AsyncApiConstants.Parameters, () => this.Walk(channel.Parameters)); this.Walk(channel as IAsyncApiExtensible); } @@ -311,8 +311,9 @@ private void Walk(IDictionary parameters) internal void Walk(AsyncApiParameter parameter, bool isComponent = false) { - if (parameter == null || this.ProcessAsReference(parameter, isComponent)) + if (parameter is AsyncApiParameterReference reference) { + this.Walk(reference as IAsyncApiReferenceable); return; } @@ -326,15 +327,41 @@ internal void Walk(AsyncApiParameter parameter, bool isComponent = false) this.Walk(parameter as IAsyncApiExtensible); } - internal void Walk(AsyncApiAvroSchemaPayload payload) + internal void Walk(IAsyncApiMessagePayload payload) { this.visitor.Visit(payload); + if (payload is AsyncApiJsonSchema jsonSchema) + { + this.Walk(AsyncApiConstants.Payload, () => this.Walk((AsyncApiJsonSchema)payload)); + } + + if (payload is AsyncApiAvroSchema avroPayload) + { + this.Walk(AsyncApiConstants.Payload, () => this.Walk(avroPayload)); + } + } + + internal void Walk(AsyncApiAvroSchema schema) + { + if (schema is AsyncApiAvroSchemaReference reference) + { + this.Walk(reference as IAsyncApiReferenceable); + return; + } + + this.visitor.Visit(schema); } internal void Walk(AsyncApiJsonSchema schema, bool isComponent = false) { - if (schema == null || this.ProcessAsReference(schema, isComponent)) + if (schema == null) + { + return; + } + + if (schema is AsyncApiJsonSchemaReference reference) { + this.Walk(reference as IAsyncApiReferenceable); return; } @@ -510,8 +537,9 @@ private void Walk(IList traits) internal void Walk(AsyncApiOperationTrait trait, bool isComponent = false) { - if (trait == null || this.ProcessAsReference(trait, isComponent)) + if (trait is AsyncApiOperationTraitReference reference) { + this.Walk(reference as IAsyncApiReferenceable); return; } @@ -529,8 +557,9 @@ internal void Walk(AsyncApiOperationTrait trait, bool isComponent = false) internal void Walk(AsyncApiMessage message, bool isComponent = false) { - if (message == null || this.ProcessAsReference(message, isComponent)) + if (message is AsyncApiMessageReference reference) { + this.Walk(reference as IAsyncApiReferenceable); return; } @@ -539,16 +568,7 @@ internal void Walk(AsyncApiMessage message, bool isComponent = false) if (message != null) { this.Walk(AsyncApiConstants.Headers, () => this.Walk(message.Headers)); - if (message.Payload is AsyncApiJsonSchemaPayload payload) - { - this.Walk(AsyncApiConstants.Payload, () => this.Walk((AsyncApiJsonSchema)payload)); - } - - if (message.Payload is AsyncApiAvroSchemaPayload avroPayload) - { - this.Walk(AsyncApiConstants.Payload, () => this.Walk(avroPayload)); - } - + this.Walk(message.Payload); this.Walk(AsyncApiConstants.CorrelationId, () => this.Walk(message.CorrelationId)); this.Walk(AsyncApiConstants.Tags, () => this.Walk(message.Tags)); this.Walk(AsyncApiConstants.Examples, () => this.Walk(message.Examples)); @@ -581,8 +601,9 @@ private void Walk(IList traits) internal void Walk(AsyncApiMessageTrait trait, bool isComponent = false) { - if (trait == null || this.ProcessAsReference(trait, isComponent)) + if (trait is AsyncApiMessageTraitReference reference) { + this.Walk(reference as IAsyncApiReferenceable); return; } @@ -603,8 +624,9 @@ internal void Walk(AsyncApiMessageTrait trait, bool isComponent = false) internal void Walk(AsyncApiBindings serverBindings, bool isComponent = false) { - if (serverBindings == null || this.ProcessAsReference(serverBindings, isComponent)) + if (serverBindings is AsyncApiBindingsReference reference) { + this.Walk(reference as IAsyncApiReferenceable); return; } @@ -632,8 +654,9 @@ internal void Walk(IServerBinding binding) internal void Walk(AsyncApiBindings channelBindings, bool isComponent = false) { - if (channelBindings == null || this.ProcessAsReference(channelBindings, isComponent)) + if (channelBindings is AsyncApiBindingsReference reference) { + this.Walk(reference as IAsyncApiReferenceable); return; } @@ -661,8 +684,9 @@ internal void Walk(IChannelBinding binding) internal void Walk(AsyncApiBindings operationBindings, bool isComponent = false) { - if (operationBindings == null || this.ProcessAsReference(operationBindings, isComponent)) + if (operationBindings is AsyncApiBindingsReference reference) { + this.Walk(reference as IAsyncApiReferenceable); return; } @@ -690,8 +714,9 @@ internal void Walk(IOperationBinding binding) internal void Walk(AsyncApiBindings messageBindings, bool isComponent = false) { - if (messageBindings == null || this.ProcessAsReference(messageBindings, isComponent)) + if (messageBindings is AsyncApiBindingsReference reference) { + this.Walk(reference as IAsyncApiReferenceable); return; } @@ -775,8 +800,9 @@ internal void Walk(IDictionary anys) internal void Walk(AsyncApiCorrelationId correlationId, bool isComponent = false) { - if (correlationId == null || this.ProcessAsReference(correlationId, isComponent)) + if (correlationId is AsyncApiCorrelationIdReference) { + this.Walk(correlationId as IAsyncApiReferenceable); return; } @@ -836,8 +862,9 @@ internal void Walk(AsyncApiInfo info) internal void Walk(AsyncApiServer server, bool isComponent = false) { - if (server == null || this.ProcessAsReference(server, isComponent)) + if (server is AsyncApiServerReference) { + this.Walk(server as IAsyncApiReferenceable); return; } @@ -875,6 +902,11 @@ internal void Walk(AsyncApiSecurityRequirement securityRequirement) } this.visitor.Visit(securityRequirement); + foreach (var item in securityRequirement.Keys) + { + this.Walk(item as IAsyncApiReferenceable); + } + this.Walk(securityRequirement as IAsyncApiExtensible); } @@ -1057,6 +1089,7 @@ public void Walk(IAsyncApiElement element) case AsyncApiOperation e: this.Walk(e); break; case AsyncApiParameter e: this.Walk(e); break; case AsyncApiJsonSchema e: this.Walk(e); break; + case AsyncApiAvroSchema e: this.Walk(e); break; case AsyncApiSecurityRequirement e: this.Walk(e); break; case AsyncApiSecurityScheme e: this.Walk(e); break; case AsyncApiServer e: this.Walk(e); break; diff --git a/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs b/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs index e501e265..95ecf5c5 100644 --- a/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs +++ b/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs @@ -130,6 +130,10 @@ public void AddWarning(AsyncApiValidatorWarning warning) /// The object to be validated. public override void Visit(AsyncApiJsonSchema item) => this.Validate(item); + public override void Visit(AsyncApiAvroSchema item) => this.Validate(item); + + public override void Visit(IAsyncApiMessagePayload item) => this.Validate(item); + /// /// Execute validation rules against an . /// @@ -144,10 +148,6 @@ public void AddWarning(AsyncApiValidatorWarning warning) public override void Visit(IMessageBinding item) => this.Validate(item); - public override void Visit(AsyncApiAvroSchemaPayload item) => this.Validate(item); - - public override void Visit(AsyncApiJsonSchemaPayload item) => this.Validate(item); - /// /// Execute validation rules against an . /// @@ -186,7 +186,7 @@ private void Validate(object item, Type type) // Validate unresolved references as references var potentialReference = item as IAsyncApiReferenceable; - if (potentialReference != null && potentialReference.UnresolvedReference) + if (potentialReference != null) { type = typeof(IAsyncApiReferenceable); } diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiAvroRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiAvroRules.cs index b12c88b2..364986bf 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiAvroRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiAvroRules.cs @@ -5,53 +5,57 @@ namespace LEGO.AsyncAPI.Validation.Rules using System.Linq; using System.Text.RegularExpressions; using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Validations; [AsyncApiRule] - public static class AsyncApiAvroRules + public static class AsyncApiMessagePayloadRules { /// /// The key regex. /// public static Regex NameRegex = new Regex(@"^[A-Za-z_][A-Za-z0-9_]*$"); - public static ValidationRule NameRegularExpression => - new ValidationRule( - (context, avroPayload) => + public static ValidationRule NameRegularExpression => + new ValidationRule( + (context, messagePayload) => { string name = null; context.Enter("name"); - if (avroPayload.TryGetAs(out var record)) + if (messagePayload is AsyncApiAvroSchema avroPayload) { - name = record.Name; - } + if (avroPayload.TryGetAs(out var record)) + { + name = record.Name; + } - if (avroPayload.TryGetAs(out var @enum)) - { - name = @enum.Name; - if (@enum.Symbols.Any(symbol => !NameRegex.IsMatch(symbol))) + if (avroPayload.TryGetAs(out var @enum)) { - context.CreateError( - "SymbolsRegularExpression", - string.Format(Resource.Validation_SymbolsMustMatchRegularExpression, NameRegex.ToString())); + name = @enum.Name; + if (@enum.Symbols.Any(symbol => !NameRegex.IsMatch(symbol))) + { + context.CreateError( + "SymbolsRegularExpression", + string.Format(Resource.Validation_SymbolsMustMatchRegularExpression, NameRegex.ToString())); + } } - } - if (avroPayload.TryGetAs(out var @fixed)) - { - name = @fixed.Name; - } + if (avroPayload.TryGetAs(out var @fixed)) + { + name = @fixed.Name; + } - if (name == null) - { - return; - } + if (name == null) + { + return; + } - if (!NameRegex.IsMatch(record.Name)) - { - context.CreateError( - nameof(NameRegex), - string.Format(Resource.Validation_NameMustMatchRegularExpr, name, NameRegex.ToString())); + if (!NameRegex.IsMatch(record.Name)) + { + context.CreateError( + nameof(NameRegex), + string.Format(Resource.Validation_NameMustMatchRegularExpr, name, NameRegex.ToString())); + } } context.Exit(); diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs index d866c4df..e82f3c7c 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs @@ -67,7 +67,7 @@ public static class AsyncApiDocumentRules finally { context.Exit(); - } + } }); private static string GetKeySignature(string path) diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs index 7cc6ee02..d6d6b750 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs @@ -2,6 +2,7 @@ namespace LEGO.AsyncAPI.Writers { + using LEGO.AsyncAPI.Models; using System; using System.Collections.Generic; using System.IO; @@ -67,6 +68,8 @@ public AsyncApiWriterBase(TextWriter textWriter, AsyncApiWriterSettings settings /// protected TextWriter Writer { get; } + public AsyncApiWorkspace Workspace { get; set; } = new AsyncApiWorkspace(); + /// /// Write start object. /// diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs index 2b663bb2..de7b2fb0 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs @@ -21,7 +21,7 @@ static AsyncApiWriterSettings() /// public AsyncApiWriterSettings() { - this.InlineReferences = false; + this.InlineLocalReferences = false; this.LoopDetector = new LoopDetector(); } @@ -46,10 +46,10 @@ public ReferenceInlineSetting ReferenceInline switch (this.referenceInline) { case ReferenceInlineSetting.DoNotInlineReferences: - this.InlineReferences = false; + this.InlineLocalReferences = false; break; case ReferenceInlineSetting.InlineReferences: - this.InlineReferences = true; + this.InlineLocalReferences = true; break; } } @@ -58,7 +58,7 @@ public ReferenceInlineSetting ReferenceInline /// /// Gets or sets a value indicating whether indicates if local references should be rendered as an inline object. /// - public bool InlineReferences { get; set; } + public bool InlineLocalReferences { get; set; } /// /// Figures out if a loop exists. @@ -72,7 +72,12 @@ public ReferenceInlineSetting ReferenceInline /// True if it should be inlined otherwise false. public bool ShouldInlineReference(AsyncApiReference reference) { - return this.InlineReferences; + if (reference.IsExternal) + { + return false; + } + + return this.InlineLocalReferences; } } } diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs index a278ebcd..0118e2b4 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs @@ -28,16 +28,6 @@ static AsyncApiYamlWriter() YamlControlCharacters = new char[] { '\0', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\a', '\b', '\t', '\n', '\v', '\f', '\r', '\x0e', '\x0f', '\x10', '\x11', '\x12', '\x13', '\x14', '\x15', '\x16', '\x17', '\x18', '\x19', '\x1a', '\x1b', '\x1c', '\x1d', '\x1e', '\x1f', }; } - /// - /// Initializes a new instance of the class. - /// - /// The text writer. - [Obsolete($"Please use overridden constructor that takes in a {nameof(AsyncApiWriterSettings)} instance.")] - public AsyncApiYamlWriter(TextWriter textWriter) - : this(textWriter, new AsyncApiWriterSettings()) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/src/LEGO.AsyncAPI/Writers/IAsyncApiWriter.cs b/src/LEGO.AsyncAPI/Writers/IAsyncApiWriter.cs index 7faff47f..e85bb834 100644 --- a/src/LEGO.AsyncAPI/Writers/IAsyncApiWriter.cs +++ b/src/LEGO.AsyncAPI/Writers/IAsyncApiWriter.cs @@ -1,9 +1,13 @@ // Copyright (c) The LEGO Group. All rights reserved. +using LEGO.AsyncAPI.Models; + namespace LEGO.AsyncAPI.Writers { public interface IAsyncApiWriter { + AsyncApiWorkspace Workspace { get; set; } + /// /// Write the start object. /// diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 355d4f87..9b863969 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -226,14 +226,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() new AsyncApiSecurityRequirement { { - new AsyncApiSecurityScheme() - { - Reference = new AsyncApiReference() - { - Id = "saslScram", - Type = ReferenceType.SecurityScheme, - }, - }, new List() + new AsyncApiSecuritySchemeReference("saslScram"), new List() }, }, }, @@ -266,14 +259,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() new AsyncApiSecurityRequirement { { - new AsyncApiSecurityScheme() - { - Reference = new AsyncApiReference() - { - Id = "certs", - Type = ReferenceType.SecurityScheme, - }, - }, new List() + new AsyncApiSecuritySchemeReference("certs"), new List() }, }, }, @@ -305,14 +291,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Parameters = new Dictionary { { - "streetlightId", new AsyncApiParameter() - { - Reference = new AsyncApiReference() - { - Id = "streetlightId", - Type = ReferenceType.Parameter, - }, - } + "streetlightId", new AsyncApiParameterReference("#/components/parameters/streetlightId") }, }, Publish = new AsyncApiOperation() @@ -321,25 +300,11 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() OperationId = "receiveLightMeasurement", Traits = new List { - new AsyncApiOperationTrait() - { - Reference = new AsyncApiReference() - { - Id = "kafka", - Type = ReferenceType.OperationTrait, - }, - }, + new AsyncApiOperationTraitReference("#/components/operationTraits/kafka"), }, Message = new List { - new AsyncApiMessage() - { - Reference = new AsyncApiReference() - { - Id = "lightMeasured", - Type = ReferenceType.Message, - }, - }, + new AsyncApiMessageReference("#/components/messages/lightMeasured"), }, }, }) @@ -350,14 +315,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Parameters = new Dictionary { { - "streetlightId", new AsyncApiParameter() - { - Reference = new AsyncApiReference() - { - Id = "streetlightId", - Type = ReferenceType.Parameter, - }, - } + "streetlightId", new AsyncApiParameterReference("#/components/parameters/streetlightId") }, }, Subscribe = new AsyncApiOperation() @@ -365,25 +323,11 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() OperationId = "turnOn", Traits = new List { - new AsyncApiOperationTrait() - { - Reference = new AsyncApiReference() - { - Id = "kafka", - Type = ReferenceType.OperationTrait, - }, - }, + new AsyncApiOperationTraitReference("#/components/operationTraits/kafka"), }, Message = new List { - new AsyncApiMessage() - { - Reference = new AsyncApiReference() - { - Id = "turnOnOff", - Type = ReferenceType.Message, - }, - }, + new AsyncApiMessageReference("#/components/messages/turnOnOff"), }, }, }) @@ -394,14 +338,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Parameters = new Dictionary { { - "streetlightId", new AsyncApiParameter() - { - Reference = new AsyncApiReference() - { - Id = "streetlightId", - Type = ReferenceType.Parameter, - }, - } + "streetlightId", new AsyncApiParameterReference("#/components/parameters/streetlightId") }, }, Subscribe = new AsyncApiOperation() @@ -409,25 +346,11 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() OperationId = "turnOff", Traits = new List { - new AsyncApiOperationTrait() - { - Reference = new AsyncApiReference() - { - Id = "kafka", - Type = ReferenceType.OperationTrait, - }, - }, + new AsyncApiOperationTraitReference("#/components/operationTraits/kafka"), }, Message = new List { - new AsyncApiMessage() - { - Reference = new AsyncApiReference() - { - Id = "turnOnOff", - Type = ReferenceType.Message, - }, - }, + new AsyncApiMessageReference("#/components/messages/turnOnOff"), }, }, }) @@ -438,14 +361,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Parameters = new Dictionary { { - "streetlightId", new AsyncApiParameter() - { - Reference = new AsyncApiReference() - { - Id = "streetlightId", - Type = ReferenceType.Parameter, - }, - } + "streetlightId", new AsyncApiParameterReference("#/components/parameters/streetlightId") }, }, Subscribe = new AsyncApiOperation() @@ -453,25 +369,11 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() OperationId = "dimLight", Traits = new List { - new AsyncApiOperationTrait() - { - Reference = new AsyncApiReference() - { - Id = "kafka", - Type = ReferenceType.OperationTrait, - }, - }, + new AsyncApiOperationTraitReference("#/components/operationTraits/kafka"), }, Message = new List { - new AsyncApiMessage() - { - Reference = new AsyncApiReference() - { - Id = "dimLight", - Type = ReferenceType.Message, - }, - }, + new AsyncApiMessageReference("#/components/messages/dimLight"), }, }, }) @@ -483,23 +385,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() ContentType = "application/json", Traits = new List() { - new AsyncApiMessageTrait() - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.MessageTrait, - Id = "commonHeaders", - }, - }, - }, - Payload = new AsyncApiJsonSchemaPayload - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.Schema, - Id = "lightMeasuredPayload", - }, + new AsyncApiMessageTraitReference("#/components/messageTraits/commonHeaders"), }, + Payload = new AsyncApiJsonSchemaReference("#/components/schemas/lightMeasuredPayload"), }) .WithComponent("turnOnOff", new AsyncApiMessage() { @@ -508,23 +396,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Summary = "Command a particular streetlight to turn the lights on or off.", Traits = new List() { - new AsyncApiMessageTrait() - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.MessageTrait, - Id = "commonHeaders", - }, - }, - }, - Payload = new AsyncApiJsonSchemaPayload() - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.Schema, - Id = "turnOnOffPayload", - }, + new AsyncApiMessageTraitReference("#/components/messageTraits/commonHeaders"), }, + Payload = new AsyncApiJsonSchemaReference("#/components/schemas/turnOnOffPayload"), }) .WithComponent("dimLight", new AsyncApiMessage() { @@ -533,23 +407,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Summary = "Command a particular streetlight to dim the lights.", Traits = new List() { - new AsyncApiMessageTrait() - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.MessageTrait, - Id = "commonHeaders", - }, - }, - }, - Payload = new AsyncApiJsonSchemaPayload() - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.Schema, - Id = "dimLightPayload", - }, + new AsyncApiMessageTraitReference("#/components/messageTraits/commonHeaders"), }, + Payload = new AsyncApiJsonSchemaReference("#/components/schemas/dimLightPayload"), }) .WithComponent("lightMeasuredPayload", new AsyncApiJsonSchema() { @@ -565,14 +425,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() } }, { - "sentAt", new AsyncApiJsonSchema() - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.Schema, - Id = "sentAt", - }, - } + "sentAt", new AsyncApiJsonSchemaReference("#/components/schemas/sentAt") }, }, }) @@ -594,14 +447,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() } }, { - "sentAt", new AsyncApiJsonSchema() - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.Schema, - Id = "sentAt", - }, - } + "sentAt", new AsyncApiJsonSchemaReference("#/components/schemas/sentAt") }, }, }) @@ -620,14 +466,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() } }, { - "sentAt", new AsyncApiJsonSchema() - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.Schema, - Id = "sentAt", - }, - } + "sentAt", new AsyncApiJsonSchemaReference("#/components/schemas/sentAt") }, }, }) @@ -921,14 +760,8 @@ public void SerializeV2_WithFullSpec_Serializes() new AsyncApiSecurityRequirement { { - new AsyncApiSecurityScheme() - { - Reference = new AsyncApiReference() - { - Id = securitySchemeName, - Type = ReferenceType.SecurityScheme, - }, - }, new List + new AsyncApiSecuritySchemeReference(securitySchemeName), + new List { requirementString, } @@ -1120,7 +953,7 @@ public void SerializeV2_WithFullSpec_Serializes() }; var outputString = new StringWriter(); - var writer = new AsyncApiYamlWriter(outputString); + var writer = new AsyncApiYamlWriter(outputString, AsyncApiWriterSettings.Default); // Act document.SerializeV2(writer); @@ -1177,7 +1010,7 @@ public void Read_WithAvroSchemaPayload_NoErrors() // Assert diagnostics.Errors.Should().HaveCount(0); - result.Channels.First().Value.Publish.Message.First().Payload.As().TryGetAs(out var record).Should().BeTrue(); + result.Channels.First().Value.Publish.Message.First().Payload.As().TryGetAs(out var record).Should().BeTrue(); record.Name.Should().Be("UserSignedUp"); } @@ -1230,12 +1063,44 @@ public void Read_WithJsonSchemaReference_NoErrors() // Assert diagnostics.Errors.Should().HaveCount(0); result.Channels.First().Value.Publish.Message.First().Title.Should().Be("Message for schema validation testing that is a json object"); - result.Channels.First().Value.Publish.Message.First().Payload.As().Properties.Should().HaveCount(1); + result.Channels.First().Value.Publish.Message.First().Payload.As().Properties.Should().HaveCount(1); } [Test] public void Serialize_WithBindingReferences_SerializesDeserializes() { + var expected = + """ + asyncapi: 2.6.0 + info: + description: test description + servers: + production: + url: example.com + protocol: pulsar+ssl + description: test description + bindings: + $ref: '#/components/serverBindings/bindings' + channels: + testChannel: + $ref: '#/components/channels/otherchannel' + components: + channels: + otherchannel: + publish: + description: test + bindings: + $ref: '#/components/channelBindings/bindings' + serverBindings: + bindings: + pulsar: + tenant: staging + channelBindings: + bindings: + pulsar: + namespace: users + persistence: persistent + """; var doc = new AsyncApiDocument(); doc.Info = new AsyncApiInfo() { @@ -1246,14 +1111,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() Description = "test description", Protocol = "pulsar+ssl", Url = "example.com", - Bindings = new AsyncApiBindings() - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.ServerBindings, - Id = "bindings", - }, - }, + Bindings = new AsyncApiBindingsReference("#/components/serverBindings/bindings") }); doc.Components = new AsyncApiComponents() { @@ -1266,14 +1124,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() { Description = "test", }, - Bindings = new AsyncApiBindings() - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.ChannelBindings, - Id = "bindings", - }, - }, + Bindings = new AsyncApiBindingsReference("#/components/channelBindings/bindings") } }, }, @@ -1305,20 +1156,20 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() }; doc.Channels.Add( "testChannel", - new AsyncApiChannel - { - Reference = new AsyncApiReference() - { - Type = ReferenceType.Channel, - Id = "otherchannel", - }, - }); + new AsyncApiChannelReference("#/components/channels/otherchannel")); var actual = doc.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); + actual.Should().BePlatformAgnosticEquivalentTo(expected); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Pulsar; var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); + var serverBindings = deserialized.Servers.First().Value.Bindings; + serverBindings.TryGetValue(out var binding); + binding.Tenant.Should().Be("staging"); + + var reserialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + reserialized.Should().BePlatformAgnosticEquivalentTo(expected); } [Test] diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs index 1edcbe37..97fd1634 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs @@ -70,7 +70,7 @@ public void LoadLicense_WithJson_Deserializes() var settings = new AsyncApiReaderSettings(); var context = new ParsingContext(diagnostic, settings); - var node = new MapNode(context, JsonNode.Parse(stream)); + var node = new MapNode(context, JsonNode.Parse(stream).ToJsonString()); // Act var actual = AsyncApiV2Deserializer.LoadLicense(node); diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index 036fae56..881b7826 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -5,7 +5,6 @@ namespace LEGO.AsyncAPI.Tests using System; using System.Collections.Generic; using System.Linq; - using System.Text.Json.Nodes; using FluentAssertions; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; @@ -23,6 +22,32 @@ public void Read_WithMissingEverything_DeserializesWithErrors() var doc = reader.Read(yaml, out var diagnostic); } + [Test] + public void Read_WithComponentBindings_Deserializes() + { + var yaml = @" + asyncapi: 2.6.0 + info: + title: test + version: 1.0.0 + channels: + workspace: + publish: + bindings: + http: + type: response + message: + $ref: '#/components/messages/WorkspaceEventPayload' + components: + messages: + WorkspaceEventPayload: + schemaFormat: application/schema+yaml;version=draft-07 + "; + var reader = new AsyncApiStringReader(); + var doc = reader.Read(yaml, out var diagnostic); + Assert.AreEqual("application/schema+yaml;version=draft-07", doc.Components.Messages["WorkspaceEventPayload"].SchemaFormat); + } + [Test] public void Read_WithExtensionParser_Parses() { @@ -432,7 +457,7 @@ public void Read_WithBasicPlusSecuritySchemeDeserializes() [Test] public void Read_WithWrongReference_AddsError() { - var yaml = + var yaml = """ asyncapi: 2.3.0 info: @@ -452,7 +477,7 @@ public void Read_WithWrongReference_AddsError() var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); diagnostic.Errors.Should().NotBeEmpty(); - doc.Channels.Values.First().Publish.Message.First().Should().BeNull(); + doc.Channels.Values.First().Publish.Message.Should().BeEmpty(); } [Test] @@ -700,7 +725,7 @@ public void Read_WithBasicPlusSecurityRequirementsDeserializes() implicit: authorizationUrl: https://example.com/api/oauth/dialog scopes: - write:pets: modify pets in your account + write:pets: modify pets in your account read:pets: read your pets """; var reader = new AsyncApiStringReader(); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs index 51815a25..203177ac 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings.WebSockets using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers; using NUnit.Framework; - + public class WebSocketBindings_Should : TestBase { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index bd35d2ae..46589db0 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -10,10 +10,9 @@ - + - diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs index 1dfdc59a..c0b233fc 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs @@ -1,12 +1,12 @@ // Copyright (c) The LEGO Group. All rights reserved. -using System.Collections.Generic; -using System.Linq; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; - namespace LEGO.AsyncAPI.Tests { + using System.Collections.Generic; + using System.Linq; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiAnyTests { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs index 4fbc2512..24f37628 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs @@ -29,8 +29,8 @@ public void AsyncApiChannel_WithInlineParameter_DoesNotCreateReference() - 97845c62-329c-4d87-ad24-4f611b909a10 """; - var channel = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var _ ); - channel.Parameters.First().Value.Reference.Should().BeNull(); + var channel = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var _); + channel.Parameters.First().Value.Should().NotBeOfType(); } [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index b3705e45..c2e7142e 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -41,7 +41,7 @@ public void AsyncApiMessage_WithNoType_DeserializesToDefault() // Assert diagnostic.Errors.Should().BeEmpty(); - message.Payload.As().Properties.First().Value.Enum.Should().HaveCount(2); + message.Payload.As().Properties.First().Value.Enum.Should().HaveCount(2); } [Test] @@ -104,7 +104,7 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() """; var message = new AsyncApiMessage(); - message.Payload = new AsyncApiJsonSchemaPayload() + message.Payload = new AsyncApiJsonSchema() { Properties = new Dictionary() { @@ -126,7 +126,7 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() actual.Should() .BePlatformAgnosticEquivalentTo(expected); message.Should().BeEquivalentTo(deserializedMessage); - } + } [Test] public void AsyncApiMessage_WithJsonSchemaFormat_Serializes() @@ -145,7 +145,7 @@ public void AsyncApiMessage_WithJsonSchemaFormat_Serializes() var message = new AsyncApiMessage(); message.SchemaFormat = "application/vnd.aai.asyncapi+json;version=2.6.0"; - message.Payload = new AsyncApiJsonSchemaPayload() + message.Payload = new AsyncApiJsonSchema() { Properties = new Dictionary() { @@ -205,7 +205,7 @@ public void AsyncApiMessage_WithAvroSchemaFormat_Serializes() }, }, }; - message.Payload = new AsyncApiAvroSchemaPayload(schema); + message.Payload = schema; // Act var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); @@ -232,9 +232,11 @@ public void AsyncApiMessage_WithAvroAsReference_Deserializes() var deserializedMessage = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out _); // Assert - deserializedMessage.Payload.Reference.Should().NotBeNull(); - deserializedMessage.Payload.Reference.IsExternal.Should().BeTrue(); - deserializedMessage.Payload.Reference.IsFragment.Should().BeTrue(); + var payloadReference = deserializedMessage.Payload as AsyncApiAvroSchemaReference; + payloadReference.UnresolvedReference.Should().BeTrue(); + payloadReference.Reference.Should().NotBeNull(); + payloadReference.Reference.IsExternal.Should().BeTrue(); + payloadReference.Reference.IsFragment.Should().BeTrue(); } @@ -326,7 +328,7 @@ public void AsyncApiMessage_WithFilledObject_Serializes() }), }, }, - Payload = new AsyncApiJsonSchemaPayload() + Payload = new AsyncApiJsonSchema() { Properties = new Dictionary { diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs index 16d58b4c..3509fc1a 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs @@ -1,16 +1,168 @@ -// Copyright (c) The LEGO Group. All rights reserved. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests { + using System; + using System.IO; using System.Linq; + using System.Threading.Tasks; using FluentAssertions; - using LEGO.AsyncAPI.Extensions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.Interface; + using LEGO.AsyncAPI.Readers.V2; using NUnit.Framework; public class AsyncApiReference_Should : TestBase { + [Test] + public void ReferencePointers() + { + var diag = new AsyncApiDiagnostic(); + var versionService = new AsyncApiV2VersionService(diag); + var externalFragment = versionService.ConvertToAsyncApiReference("https://github.com/test/test#whatever", ReferenceType.None); + var internalFragment = versionService.ConvertToAsyncApiReference("#/components/servers/server1", ReferenceType.None); + var localFile = versionService.ConvertToAsyncApiReference("./local/some/folder/whatever.yaml", ReferenceType.None); + var externalFile = versionService.ConvertToAsyncApiReference("https://github.com/test/whatever.yaml", ReferenceType.None); + + externalFragment.ExternalResource.Should().Be("https://github.com/test/test"); + externalFragment.FragmentId.Should().Be("whatever"); + externalFragment.Reference.Should().Be("https://github.com/test/test#whatever"); + externalFragment.IsFragment.Should().BeTrue(); + externalFragment.IsExternal.Should().BeTrue(); + + internalFragment.ExternalResource.Should().BeNull(); + internalFragment.FragmentId.Should().Be("/components/servers/server1"); + internalFragment.Reference.Should().Be("#/components/servers/server1"); + internalFragment.IsFragment.Should().BeTrue(); + internalFragment.IsExternal.Should().BeFalse(); + + localFile.ExternalResource.Should().Be("./local/some/folder/whatever.yaml"); + localFile.Reference.Should().Be("./local/some/folder/whatever.yaml"); + localFile.FragmentId.Should().Be(null); + localFile.IsFragment.Should().BeFalse(); + + externalFile.ExternalResource.Should().Be("https://github.com/test/whatever.yaml"); + externalFile.Reference.Should().Be("https://github.com/test/whatever.yaml"); + externalFile.FragmentId.Should().Be(null); + externalFile.IsFragment.Should().BeFalse(); + } + + [Test] + public void Reference() + { + var json = + """ + { + "asyncapi": "2.6.0", + "info": { }, + "servers": { + "production": { + "$ref": "https://github.com/test/test#whatever" + } + } + } + """; + + var doc = new AsyncApiStringReader().Read(json, out var diag); + var reference = doc.Servers.First().Value as AsyncApiServerReference; + reference.Reference.ExternalResource.Should().Be("https://github.com/test/test"); + reference.Reference.FragmentId.Should().Be("whatever"); + reference.Reference.IsFragment.Should().BeTrue(); + } + + [Test] + public void ExternalFragmentReference_ResolvesFragtment() + { + var externalJson = + """ + { + "servers": [ + { + "url": "wss://production.gigantic-server.com:443", + "protocol": "wss", + "protocolVersion": "1.0.0", + "description": "The production API server", + "variables": { + "username": { + "default": "demo", + "description": "This value is assigned by the service provider" + }, + "password": { + "default": "demo", + "description": "This value is assigned by the service provider" + } + } + } + ] + } + """; + + var json = + """ + { + "asyncapi": "2.6.0", + "info": { }, + "servers": { + "production": { + "$ref": "whatever/whatever.json#/servers/0" + } + } + } + """; + + var doc = new AsyncApiStringReader(new AsyncApiReaderSettings { ReferenceResolution = ReferenceResolutionSetting.ResolveAllReferences, ExternalReferenceLoader = new MockStringLoader(externalJson) }).Read(json, out var diag); + var reference = doc.Servers.First().Value as AsyncApiServerReference; + reference.Reference.FragmentId.Should().Be("/servers/0"); + reference.Reference.IsFragment.Should().BeTrue(); + reference.Url.Should().Be("wss://production.gigantic-server.com:443"); + } + + [Test] + public void ServerReference_WithComponentReference_ResolvesReference() + { + var json = + """ + { + "asyncapi": "2.6.0", + "info": { }, + "servers": { + "production": { + "$ref": "#/components/servers/whatever" + } + }, + "components": { + "servers": { + "whatever": { + "url": "wss://production.gigantic-server.com:443", + "protocol": "wss", + "protocolVersion": "1.0.0", + "description": "The production API server", + "variables": { + "username": { + "default": "demo", + "description": "This value is assigned by the service provider" + }, + "password": { + "default": "demo", + "description": "This value is assigned by the service provider" + } + } + } + } + } + } + """; + + var doc = new AsyncApiStringReader().Read(json, out var diag); + var reference = doc.Servers.First().Value as AsyncApiServerReference; + reference.Reference.ExternalResource.Should().BeNull(); + reference.Reference.FragmentId.Should().Be("/components/servers/whatever"); + reference.Reference.IsFragment.Should().BeTrue(); + reference.Url.Should().Be("wss://production.gigantic-server.com:443"); + + } + [Test] public void AsyncApiReference_WithExternalFragmentUriReference_AllowReference() { @@ -26,12 +178,12 @@ public void AsyncApiReference_WithExternalFragmentUriReference_AllowReference() // Assert diagnostic.Errors.Should().BeEmpty(); - var payload = deserialized.Payload.As(); + var payload = deserialized.Payload.As(); payload.UnresolvedReference.Should().BeTrue(); var reference = payload.Reference; reference.ExternalResource.Should().Be("http://example.com/some-resource"); - reference.Id.Should().Be("/path/to/external/fragment"); + reference.FragmentId.Should().Be("/path/to/external/fragment"); reference.IsFragment.Should().BeTrue(); reference.IsExternal.Should().BeTrue(); reference.Type.Should().Be(ReferenceType.Schema); @@ -55,14 +207,14 @@ public void AsyncApiReference_WithFragmentReference_AllowReference() // Assert diagnostic.Errors.Should().BeEmpty(); - var payload = deserialized.Payload.As(); + var payload = deserialized.Payload.As(); payload.UnresolvedReference.Should().BeTrue(); var reference = payload.Reference; reference.Type.Should().Be(ReferenceType.Schema); reference.ExternalResource.Should().Be("/fragments/myFragment"); - reference.Id.Should().BeNull(); - reference.IsFragment.Should().BeTrue(); + reference.FragmentId.Should().BeNull(); + reference.IsFragment.Should().BeFalse(); reference.IsExternal.Should().BeTrue(); var expected = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual.Should() @@ -84,12 +236,12 @@ public void AsyncApiReference_WithInternalComponentReference_AllowReference() // Assert diagnostic.Errors.Should().BeEmpty(); - var payload = deserialized.Payload.As(); + var payload = deserialized.Payload.As(); var reference = payload.Reference; reference.ExternalResource.Should().BeNull(); reference.Type.Should().Be(ReferenceType.Schema); - reference.Id.Should().Be("test"); - reference.IsFragment.Should().BeFalse(); + reference.FragmentId.Should().Be("/components/schemas/test"); + reference.IsFragment.Should().BeTrue(); reference.IsExternal.Should().BeFalse(); var expected = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); @@ -112,10 +264,10 @@ public void AsyncApiReference_WithExternalFragmentReference_AllowReference() // Assert diagnostic.Errors.Should().BeEmpty(); - var payload = deserialized.Payload.As(); + var payload = deserialized.Payload.As(); var reference = payload.Reference; reference.ExternalResource.Should().Be("./myjsonfile.json"); - reference.Id.Should().Be("/fragment"); + reference.FragmentId.Should().Be("/fragment"); reference.IsFragment.Should().BeTrue(); reference.IsExternal.Should().BeTrue(); @@ -139,12 +291,12 @@ public void AsyncApiReference_WithExternalComponentReference_AllowReference() // Assert diagnostic.Errors.Should().BeEmpty(); - var payload = deserialized.Payload.As(); + var payload = deserialized.Payload.As(); var reference = payload.Reference; reference.ExternalResource.Should().Be("./someotherdocument.json"); reference.Type.Should().Be(ReferenceType.Schema); - reference.Id.Should().Be("test"); - reference.IsFragment.Should().BeFalse(); + reference.FragmentId.Should().Be("/components/schemas/test"); + reference.IsFragment.Should().BeTrue(); reference.IsExternal.Should().BeTrue(); var expected = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); @@ -181,47 +333,16 @@ public void AsyncApiDocument_WithInternalComponentReference_ResolvesReference() // Assert diagnostic.Errors.Should().BeEmpty(); - var channel = deserialized.Channels.First().Value; + var channel = deserialized.Channels.First().Value as AsyncApiChannelReference; channel.UnresolvedReference.Should().BeFalse(); channel.Description.Should().Be("customDescription"); channel.Reference.ExternalResource.Should().BeNull(); - channel.Reference.Id.Should().Be("myChannel"); + channel.Reference.FragmentId.Should().Be("/components/channels/myChannel"); channel.Reference.IsExternal.Should().BeFalse(); channel.Reference.Type.Should().Be(ReferenceType.Channel); } - [Test] - public void AsyncApiDocument_WithNoConfiguredExternalReferenceReader_ThrowsError() - { - // Arrange - var actual = """ - asyncapi: 2.6.0 - info: - title: My AsyncAPI Document - version: 1.0.0 - channels: - myChannel: - $ref: http://example.com/channel.json - """; - - var settings = new AsyncApiReaderSettings() - { - ReferenceResolution = ReferenceResolutionSetting.ResolveAllReferences, - }; - var reader = new AsyncApiStringReader(settings); - - // Act - reader.Read(actual, out var diagnostic); - - // Assert - diagnostic.Errors.Count.Should().Be(1); - var error = diagnostic.Errors.First(); - error.Message.Should() - .Be( - "External reference configured in AsyncApi document but no implementation provided for ExternalReferenceReader."); - } - [Test] public void AsyncApiDocument_WithExternalReferenceOnlySetToResolveInternalReferences_DoesNotResolve() { @@ -247,13 +368,13 @@ public void AsyncApiDocument_WithExternalReferenceOnlySetToResolveInternalRefere // Assert diagnostic.Errors.Should().BeEmpty(); - var channel = deserialized.Channels.First().Value; + var channel = deserialized.Channels.First().Value as AsyncApiChannelReference; channel.UnresolvedReference.Should().BeTrue(); channel.Description.Should().BeNull(); channel.Reference.ExternalResource.Should().Be("http://example.com/channel.json"); channel.Reference.Type.Should().Be(ReferenceType.Channel); - channel.Reference.Id.Should().BeNull(); + channel.Reference.FragmentId.Should().BeNull(); channel.Reference.IsExternal.Should().BeTrue(); channel.Reference.IsFragment.Should().BeFalse(); } @@ -273,10 +394,10 @@ public void AsyncApiReference_WithExternalReference_AllowsReferenceDoesNotResolv // Assert diagnostic.Errors.Should().BeEmpty(); - var payload = deserialized.Payload.As(); + var payload = deserialized.Payload.As(); var reference = payload.Reference; reference.ExternalResource.Should().Be("http://example.com/json.json"); - reference.Id.Should().BeNull(); + reference.FragmentId.Should().BeNull(); reference.IsExternal.Should().BeTrue(); reference.IsFragment.Should().BeFalse(); diagnostic.Errors.Should().BeEmpty(); @@ -305,18 +426,18 @@ public void AsyncApiReference_WithExternalResourcesInterface_DeserializesCorrect var settings = new AsyncApiReaderSettings { ReferenceResolution = ReferenceResolutionSetting.ResolveAllReferences, - ExternalReferenceReader = new MockExternalReferenceReader(), + ExternalReferenceLoader = new MockJsonSchemaLoader(), }; var reader = new AsyncApiStringReader(settings); var doc = reader.Read(yaml, out var diagnostic); var message = doc.Channels["workspace"].Publish.Message.First(); message.Name.Should().Be("Test"); - var payload = message.Payload.As(); - payload.Properties.Count.Should().Be(3); + var payload = message.Payload.As(); + payload.Properties.Count.Should().Be(1); } [Test] - public void AvroReference_WithExternalResourcesInterface_DeserializesCorrectly() + public void AsyncApiReference_DocumentLevelReferencePointer_DeserializesCorrectly() { var yaml = """ asyncapi: 2.3.0 @@ -327,114 +448,148 @@ public void AvroReference_WithExternalResourcesInterface_DeserializesCorrectly() workspace: publish: message: - schemaFormat: 'application/vnd.apache.avro+yaml;version=1.9.0' - payload: - $ref: 'path/to/user-create.avsc/#UserCreate' + title: test message + other: + $ref: "#/channels/workspace" """; + + var reader = new AsyncApiStringReader(); + var doc = reader.Read(yaml, out var diagnostic); + doc.Channels.Should().HaveCount(2); + doc.Channels["other"].Publish.Message.First().Title.Should().Be("test message"); + } + + [Test] + public void AsyncApiReference_WithExternalAvroResource_DeserializesCorrectly() + { + var avroPayload = + """ + { + "type": "record", + "name": "thecodebuzz_schema", + "namespace": "thecodebuzz.avro", + "fields": [ + { + "name": "username", + "type": "string", + "doc": "Name of the user account on Thecodebuzz.com" + }, + { + "name": "email", + "type": "string", + "doc": "The email of the user logging message on the blog" + }, + { + "name": "timestamp", + "type": "long", + "doc": "time in seconds" + } + ], + "doc:": "A basic schema for storing thecodebuzz blogs messages" + } + """; + + var yaml = """ + asyncapi: 2.6.0 + info: + title: test + version: 1.0.0 + channels: + workspace: + publish: + message: + payload: + $ref: ./some/path/to/external/payload.json + schemaFormat: application/vnd.apache.avro + name: Test + title: Test message + summary: Test. + """; var settings = new AsyncApiReaderSettings { ReferenceResolution = ReferenceResolutionSetting.ResolveAllReferences, - ExternalReferenceReader = new MockExternalAvroReferenceReader(), + ExternalReferenceLoader = new MockStringLoader(avroPayload), }; var reader = new AsyncApiStringReader(settings); var doc = reader.Read(yaml, out var diagnostic); - var payload = doc.Channels["workspace"].Publish.Message.First().Payload; - payload.Should().BeAssignableTo(typeof(AsyncApiAvroSchemaPayload)); - var avro = payload as AsyncApiAvroSchemaPayload; - avro.TryGetAs(out var record); - record.Name.Should().Be("SomeEvent"); + var message = doc.Channels["workspace"].Publish.Message.First(); + var payload = message.Payload.As(); + payload.As().Name.Should().Be("thecodebuzz_schema"); + + doc.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0).Should() + .BePlatformAgnosticEquivalentTo(yaml); + } } - public class MockExternalAvroReferenceReader : IAsyncApiExternalReferenceReader + public class MockStringLoader : IStreamLoader { - public string Load(string reference) + public MockStringLoader(string input) { - return - """ - { - "type": "record", - "name": "SomeEvent", - "namespace": "my.namspace.for.event", - "fields": [ - { - "name": "countryCode", - "type": "string", - "doc": "Country of the partner, (e.g. DE)" - }, - { - "name": "occurredOn", - "type": "string", - "doc": "Timestamp of when action occurred." - }, - { - "name": "partnerId", - "type": "string", - "doc": "Id of the partner" - }, - { - "name": "platformSource", - "type": "string", - "doc": "Platform source" - } - ], - "example": { - "occurredOn": "2023-11-03T09:56.582+00:00", - "partnerId": "1", - "platformSource": "Brecht", - "countryCode": "DE" - } - } - """; - } + this.input = input; + } + + private readonly string input; + + public Stream Load(Uri uri) + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + writer.Write(this.input); + writer.Flush(); + stream.Position = 0; + return stream; + } + + public Task LoadAsync(Uri uri) + { + return Task.FromResult(this.Load(uri)); + } } - public class MockExternalReferenceReader : IAsyncApiExternalReferenceReader + + public class MockJsonSchemaLoader : IStreamLoader { - public string Load(string reference) + const string Message = + """ + name: Test + title: Test message + summary: Test. + schemaFormat: application/schema+yaml;version=draft-07 + contentType: application/cloudevents+json + payload: + $ref: "./some/path/to/schema.yaml" + """; + + const string Schema = + """ + type: object + properties: + lumens: + type: integer + minimum: 0 + description: Light intensity measured in lumens. + """; + + public Stream Load(Uri uri) { - if (reference == "./some/path/to/external/message.yaml") + var stream = new MemoryStream(); + var writer = new StreamWriter(stream); + if (uri.ToString() == "./some/path/to/external/message.yaml") { - return """ - name: Test - title: Test message - summary: Test. - schemaFormat: application/schema+yaml;version=draft-07 - contentType: application/cloudevents+json - payload: - $ref: "./some/path/to/schema.yaml" - """; + writer.Write(Message); + } + else + { + writer.Write(Schema); } + writer.Flush(); + stream.Position = 0; + return stream; + } - return """ - type: object - properties: - orderId: - description: The ID of the order. - type: string - format: uuid - name: - description: Name of order. - type: string - orderDetails: - description: User details. - type: object - properties: - userId: - description: User Id. - type: string - format: uuid - userName: - description: User name. - type: string - required: - - orderId - example: - orderId: 8f9189f8-653b-4849-a1ec-c838c030bd67 - handler: SomeName - orderDetails: - userId: Admin - userName: Admin - """; + public Task LoadAsync(Uri uri) + { + return Task.FromResult(this.Load(uri)); } } } \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 7e00efab..af2badf8 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -1,4 +1,4 @@ -// Copyright (c) The LEGO Group. All rights reserved. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { @@ -220,12 +220,6 @@ public class AsyncApiSchema_Should : TestBase { Url = new Uri("http://example.com/externalDocs"), }, - - Reference = new AsyncApiReference - { - Type = ReferenceType.Schema, - Id = "schemaObject1", - }, }; public static AsyncApiJsonSchema AdvancedSchemaWithRequiredPropertiesObject = new AsyncApiJsonSchema @@ -370,7 +364,7 @@ public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() public void Deserialize_WithAdvancedSchema_Works() { // Arrange - var json = GetTestData(); + var json = this.GetTestData(); var expected = AdvancedSchemaObject; // Act @@ -420,14 +414,14 @@ public void Serialize_WithInliningOptions_ShouldInlineAccordingly(bool shouldInl { new AsyncApiMessage { - Payload = new AsyncApiJsonSchemaPayload + Payload = new AsyncApiJsonSchema { Type = SchemaType.Object, Required = new HashSet { "testB" }, Properties = new Dictionary { - { "testC", new AsyncApiJsonSchema { Reference = new AsyncApiReference { Type = ReferenceType.Schema, Id = "testC" } } }, - { "testB", new AsyncApiJsonSchema { Reference = new AsyncApiReference { Type = ReferenceType.Schema, Id = "testB" } } }, + { "testC", new AsyncApiJsonSchemaReference("#/components/schemas/testC") }, + { "testB", new AsyncApiJsonSchemaReference("#/components/schemas/testB") }, }, }, }, @@ -440,14 +434,14 @@ public void Serialize_WithInliningOptions_ShouldInlineAccordingly(bool shouldInl Type = SchemaType.Object, Properties = new Dictionary { - { "testD", new AsyncApiJsonSchema { Reference = new AsyncApiReference { Type = ReferenceType.Schema, Id = "testD" } } }, + { "testD", new AsyncApiJsonSchemaReference("#/components/schemas/testD") }, }, }) .WithComponent("testB", new AsyncApiJsonSchema() { Description = "test", Type = SchemaType.Boolean }) .Build(); var outputString = new StringWriter(); - var writer = new AsyncApiYamlWriter(outputString, new AsyncApiWriterSettings { InlineReferences = shouldInline }); + var writer = new AsyncApiYamlWriter(outputString, new AsyncApiWriterSettings { InlineLocalReferences = shouldInline }); // Act asyncApiDocument.SerializeV2(writer); diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs index b92224df..273e1a7d 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs @@ -24,7 +24,7 @@ public void SerializeV2_WithNullWriter_Throws() public void SerializeV2_Serializes() { var asyncApiSecurityRequirement = new AsyncApiSecurityRequirement(); - asyncApiSecurityRequirement.Add(new AsyncApiSecurityScheme { Type = SecuritySchemeType.ApiKey }, new List { "string" }); + asyncApiSecurityRequirement.Add(new AsyncApiSecuritySchemeReference("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 8b14560b..c306fb73 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs @@ -48,15 +48,8 @@ public void AsyncApiServer_Serializes() new AsyncApiSecurityRequirement { { - new AsyncApiSecurityScheme() - { - Reference = new AsyncApiReference() - { - Id = "schem1", - Type = ReferenceType.SecurityScheme, - }, - Name = "scheme1", - }, new List + new AsyncApiSecuritySchemeReference("schem1") + , new List { "requirement", } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AvroSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AvroSchema_Should.cs index 4dcce166..88cb7c87 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AvroSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AvroSchema_Should.cs @@ -28,7 +28,7 @@ public void Serialize_WithDefaultNull_SetJsonNull() default: null """; - var model = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var diag); + var model = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var diag); var reserialized = model.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); reserialized.Should().Contain("default\": null"); } @@ -72,7 +72,7 @@ public void Deserialize_WithMetadata_CreatesMetadata() } } """; - var model = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var diag); + var model = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var diag); model.Metadata.Should().HaveCount(1); var reserialized = model.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); @@ -218,7 +218,7 @@ public void SerializeV2_SerializesCorrectly() Name = "contact", Type = new AvroUnion { - Types = new List + Types = new List { AvroPrimitiveType.Null, new AvroRecord @@ -294,7 +294,7 @@ public void SerializeV2_WithLogicalTypes_SerializesCorrectly() """; // Act - var model = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var diag); + var model = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var diag); var serialized = model.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); // Assert model.As().Items.As().Types.Should().HaveCount(8); @@ -438,7 +438,7 @@ public void ReadFragment_DeserializesCorrectly() Name = "contact", Type = new AvroUnion { - Types = new List + Types = new List { AvroPrimitiveType.Null, new AvroRecord @@ -458,7 +458,7 @@ public void ReadFragment_DeserializesCorrectly() }; // Act - var actual = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + var actual = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var diagnostic); // Assert actual.Should() diff --git a/test/LEGO.AsyncAPI.Tests/Validation/ValidationRuleTests.cs b/test/LEGO.AsyncAPI.Tests/Validation/ValidationRuleTests.cs index 5d9f7bec..2b1d3ebf 100644 --- a/test/LEGO.AsyncAPI.Tests/Validation/ValidationRuleTests.cs +++ b/test/LEGO.AsyncAPI.Tests/Validation/ValidationRuleTests.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Tests.Validation { using FluentAssertions; using LEGO.AsyncAPI.Readers; - using LEGO.AsyncAPI.Validations; using NUnit.Framework; using System.Linq;