From bcfde7cc04f8634fac2cb9edcfa6c1c3eda34cdd Mon Sep 17 00:00:00 2001 From: Akshat Nema <76521428+akshatnema@users.noreply.github.com> Date: Fri, 31 Mar 2023 17:20:04 +0530 Subject: [PATCH 01/84] chore: updated `.asyncapi-tool` file (#103) --- .asyncapi-tool | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.asyncapi-tool b/.asyncapi-tool index 71583182..c15324ea 100644 --- a/.asyncapi-tool +++ b/.asyncapi-tool @@ -14,7 +14,7 @@ "categories": [ "converters", "code-first", - "validator", + "validator" ], "hasCommercial": false } From f1058ac6abf05396643a6bd35165623d2fe6eddd Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Thu, 20 Apr 2023 12:51:02 +0200 Subject: [PATCH 02/84] chore: add ability to throw AsyncApiExceptions during extension parsing (#105) --- .../AsyncApiYamlDocumentReader.cs | 6 +- .../V2/AsyncApiDeserializer.cs | 16 +- .../V2/AsyncApiSchemaDeserializer.cs | 2 +- .../AsyncApiDocumentV2Tests.cs | 1008 +++++++++++++++++ .../AsyncApiReaderTests.cs | 85 ++ 5 files changed, 1108 insertions(+), 9 deletions(-) diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs index 655f2847..9dae2141 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs @@ -72,7 +72,7 @@ public AsyncApiDocument Read(YamlDocument input, out AsyncApiDiagnostic diagnost return document; } - public async Task ReadAsync(YamlDocument input) + public Task ReadAsync(YamlDocument input) { var diagnostic = new AsyncApiDiagnostic(); var context = new ParsingContext(diagnostic) @@ -102,11 +102,11 @@ public async Task ReadAsync(YamlDocument input) } } - return new ReadResult + return Task.FromResult(new ReadResult { AsyncApiDocument = document, AsyncApiDiagnostic = diagnostic, - }; + }); } private void ResolveReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument document) diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs index 64be8b77..0328c4ee 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs @@ -165,15 +165,21 @@ public static IAsyncApiAny LoadAny(ParseNode node) private static IAsyncApiExtension LoadExtension(string name, ParseNode node) { - if (node.Context.ExtensionParsers.TryGetValue(name, out var parser)) + try { - return parser( - AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny())); + if (node.Context.ExtensionParsers.TryGetValue(name, out var parser)) + { + return parser( + AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny())); + } } - else + catch (AsyncApiException ex) { - return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); } + + return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); } private static string LoadString(ParseNode node) diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index e0504733..e63d3311 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -11,7 +11,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static readonly FixedFieldMap schemaFixedFields = new() + private static readonly FixedFieldMap schemaFixedFields = new () { { "title", (a, n) => { a.Title = n.GetScalarValue(); } diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 3f4f6591..75db39a9 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -17,6 +17,86 @@ public class AsyncApiDocumentV2Tests { + + [Test] + public void test() + { + var input = @"asyncapi: 2.0.0 +info: + title: Sites + version: 1.0.0 + description: Responsible for emitting the site on/off + x-application-id: APP-02042 + x-audience: component-internal +channels: + site-events: + subscribe: + message: + payload: + properties: + data: + '$ref': '#/components/schemas/DtosSiteUpdatedEvent' + description: The actual payload of the event + datacontenttype: + description: Always application/json + type: string + example: application/json + id: + description: The unique ID of the event + type: string + example: 3489d4b1e21badf3665dae24c6526169 + source: + description: The source of the event + type: string + example: LEGO.OmnichannelFulfilment.DeliveryOrchestration/Sites + specversion: + description: The CloudEvents schema version used + type: string + example: 1.0 + time: + description: The time the event was published + format: date-time + type: string + example: 2022-10-13T11:57:36.268054757Z + type: + description: The type of the event + type: string + example: siteUpdatedV1 + traceparent: + description: The value to propagate context information that enables distributed tracing scenarios + type: string + example: 3489d4b1e21badf3665dae24c6526169 + type: object + summary: Subscriber message + description: All data used for turning on or off a site request + x-classification: green + x-datalakesubscription: false + x-eventarchetype: objectchanged + x-eventdurability: persistent +components: + schemas: + DtosSiteUpdatedEvent: + properties: + enabled: + description: The Enabled shows the current status of the site + type: boolean + examples: + - false + siteId: + description: The SiteCode related to the site that is being turn on or off + type: string + examples: + - 489 + reason: + description: The reason the site is being turned on or off + type: string + examples: + - Workers striking require us to temporary close the site. + type: object +"; + var serialized = new AsyncApiStringReader().Read(input, out var diag); + + } [Test] public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() { @@ -1124,6 +1204,934 @@ public void SerializeV2_WithFullSpec_Serializes() Assert.AreEqual(actual, expected); } + [Test] + public void tesT() + { + var spec = @"asyncapi: '2.6.0' +defaultContentType: 'application/json' +info: + title: '{TITLE}' + version: '{VERSION}' + x-audience: company-internal + x-application-id: APP-01575 + x-eventdeduplication: false + contact: + name: Team Deadlock + email: Deadlock@o365.corp.LEGO.com + url: https://legogroup.atlassian.net/wiki/spaces/TD/pages/37143022928/Consent+Service + description: | + Emits events related to consent changes for both LEGO Account users and anonymous users. + This includes both parental consents and cookie consents. +channels: + userconsents.objectchanged: + x-eventarchetype: objectchanged + x-eventdurability: persistent + x-classification: yellow + description: | + A topic for events regarding changes to user consents. The event archetype is set to 'objectchanged' which will enable tombstoning and compaction. + subscribe: + operationId: UserConsentsObjectChanged + message: + oneOf: + - $ref: '#/components/messages/UserConsentsObjectCreated' + - $ref: '#/components/messages/UserConsentsObjectChanged' + - $ref: '#/components/messages/UserConsentsObjectDeleted' + userconsents.fieldchanged: + x-eventarchetype: fieldchanged + x-eventdurability: persistent + x-classification: yellow + description: | + A topic for deleted user consents events. The event archetype is set to 'fieldchanged' in order to enforce a 28 days retention policy. + subscribe: + operationId: UserConsentsFieldChanged + message: + oneOf: + - $ref: '#/components/messages/UserConsentFieldCreated' + - $ref: '#/components/messages/UserConsentFieldChanged' + - $ref: '#/components/messages/UserConsentFieldDeleted' + anonymousconsent.fieldchange: + x-eventarchetype: fieldchanged + x-eventdurability: persistent + x-classification: green + description: | + A topic for events regarding changes to anonymous consents. The event archetype is set to 'fieldchanged' in order to enforce a 28 days retention policy. + subscribe: + operationId: AnonymousConsentsFieldChanged + message: + oneOf: + - $ref: '#/components/messages/AnonymousConsentFieldChange' + experiences.events: + x-eventarchetype: objectchanged + x-eventdurability: persistent + x-classification: yellow + description: | + A topic for experience events. The event archetype is set to 'objectchanged' in order to store event forever. + subscribe: + operationId: ExperiencesChanged + message: + oneOf: + - $ref: '#/components/messages/ExperienceCreated' + - $ref: '#/components/messages/ExperienceDeleted' + - $ref: '#/components/messages/ExperienceUpdated' + - $ref: '#/components/messages/ExperienceClientAdded' + - $ref: '#/components/messages/ExperienceClientRemoved' + - $ref: '#/components/messages/ExperienceConsentOptionAdded' + - $ref: '#/components/messages/ExperienceConsentOptionRemoved' +components: + schemas: + EnvelopeBase: + type: object + properties: + type: + type: string + description: The type of event. + correlationId: + type: string + description: | + The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header + data: + type: object + + EnvelopeOfUserConsentsObjectCreated: + allOf: + - $ref: '#/components/schemas/EnvelopeBase' + - type: object + properties: + type: + enum: [UserConsentsObjectCreated] + data: + $ref: '#/components/schemas/UserConsentsObjectCreated' + + EnvelopeOfUserConsentsObjectChanged: + allOf: + - $ref: '#/components/schemas/EnvelopeBase' + - type: object + properties: + type: + enum: [UserConsentsObjectChanged] + data: + $ref: '#/components/schemas/UserConsentsObjectChanged' + EnvelopeOfUserConsentsObjectDeleted: + allOf: + - $ref: '#/components/schemas/EnvelopeBase' + - type: object + properties: + type: + enum: [UserConsentsObjectDeleted] + data: + $ref: '#/components/schemas/UserConsentsObjectDeleted' + UserConsentsObjectCreated: + type: object + required: + - changeType + - changeTime + - userId + - consents + properties: + changeType: + type: string + enum: ['created'] + description: The change type of the event. + changeTime: + type: string + format: date-time + description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + userId: + type: string + format: guid + description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + consents: + type: array + items: + $ref: '#/components/schemas/UserConsent' + UserConsentsObjectChanged: + type: object + required: + - changeType + - changeTime + - userId + - consents + properties: + changeType: + type: string + enum: ['updated'] + description: The change type of the event. + changeTime: + type: string + format: date-time + description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + userId: + type: string + format: guid + description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + consents: + type: array + items: + $ref: '#/components/schemas/UserConsent' + UserConsentsObjectDeleted: + type: object + required: + - changeType + - changeTime + - userId + properties: + changeType: + type: string + enum: ['deleted'] + description: The change type of the event. + changeTime: + type: string + format: date-time + description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + userId: + type: string + format: guid + description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + UserConsent: + type: object + required: + - consentId + - consenterUserId + - consentState + - culture + properties: + consentId: + type: string + format: uri + description: The consent option URI + examples: + - self-consent://global/analytic-cookies + - self-consent://global/necessary-cookies + - self-consent://global/lego-marketing-cookies + consenterUserId: + type: string + format: guid + description: The ID of the user giving the consent. The GUID is 32 digits separated by hyphens and is not considered to be PII. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + consentState: + type: string + description: The state of the consent for the given consent option + enum: ['granted','denied','undecided'] + culture: + type: string + minLength: 5 + maxLength: 5 + description: The culture where the consent was changed. + examples: + - da-DK + - en-US + - en-GB + submissionSource: + type: string + description: The method used by the user to submit the cookies + enum: ['prebannerAcceptAll', 'prebannerRejectAll', 'savePrefButton', 'cloned' ] + + EnvelopeOfUserConsentFieldCreatedEvent: + allOf: + - $ref: '#/components/schemas/EnvelopeBase' + - type: object + properties: + type: + enum: [UserConsentFieldCreatedEvent] + data: + $ref: '#/components/schemas/UserConsentFieldCreatedEvent' + EnvelopeOfUserConsentFieldChangedEvent: + allOf: + - $ref: '#/components/schemas/EnvelopeBase' + - type: object + properties: + type: + enum: [UserConsentFieldChangedEvent] + data: + $ref: '#/components/schemas/UserConsentFieldChangedEvent' + EnvelopeOfUserConsentFieldDeletedEvent: + allOf: + - $ref: '#/components/schemas/EnvelopeBase' + - type: object + properties: + type: + enum: [UserConsentFieldDeletedEvent] + data: + $ref: '#/components/schemas/UserConsentFieldDeletedEvent' + + UserConsentFieldCreatedEvent: + type: object + required: + - changeType + - changeTime + - userId + - consentId + - consenterUserId + - consentState + - culture + properties: + changeType: + type: string + enum: ['created','updated','deleted'] + description: The change type of the event. + changeTime: + type: string + format: date-time + description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + userId: + type: string + format: guid + description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + consentId: + type: string + format: uri + description: The consent option URI + examples: + - self-consent://global/analytic-cookies + - self-consent://global/necessary-cookies + - self-consent://global/lego-marketing-cookies + consenterUserId: + type: string + format: guid + description: The ID of the user giving the consent. The GUID is 32 digits separated by hyphens and is not considered to be PII. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + consentState: + type: string + description: The state of the consent for the given consent option + enum: ['granted','denied','undecided'] + culture: + type: string + minLength: 5 + maxLength: 5 + description: The culture where the consent was changed. + examples: + - da-DK + - en-US + - en-GB + submissionSource: + type: string + description: The method used by the user to submit the cookies + enum: ['prebannerAcceptAll', 'prebannerRejectAll', 'savePrefButton', 'cloned' ] + + UserConsentFieldChangedEvent: + type: object + required: + - changeType + - changeTime + - userId + - consentId + - consenterUserId + - consentState + - culture + properties: + changeType: + type: string + enum: ['created','updated','deleted'] + description: The change type of the event. + changeTime: + type: string + format: date-time + description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + userId: + type: string + format: guid + description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + consentId: + type: string + format: uri + description: The consent option URI + examples: + - self-consent://global/analytic-cookies + - self-consent://global/necessary-cookies + - self-consent://global/lego-marketing-cookies + consenterUserId: + type: string + format: guid + description: The ID of the user giving the consent. The GUID is 32 digits separated by hyphens and is not considered to be PII. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + consentState: + type: string + description: The state of the consent for the given consent option + enum: ['granted','denied','undecided'] + culture: + type: string + minLength: 5 + maxLength: 5 + description: The culture where the consent was changed. + examples: + - da-DK + - en-US + - en-GB + submissionSource: + type: string + description: The method used by the user to submit the cookies + enum: ['prebannerAcceptAll', 'prebannerRejectAll', 'savePrefButton', 'cloned' ] + + UserConsentFieldDeletedEvent: + type: object + required: + - changeType + - changeTime + - userId + - consentId + properties: + changeType: + type: string + enum: ['deleted'] + description: The change type of the event. + changeTime: + type: string + format: date-time + description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + userId: + type: string + format: guid + description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + consentId: + type: string + format: uri + description: The consent option URI + examples: + - self-consent://global/analytic-cookies + - self-consent://global/necessary-cookies + - self-consent://global/lego-marketing-cookies + + EnvelopeOfAnonymousConsentFieldChangeEvent: + type: object + properties: + type: + type: string + description: The type of event. Will always have the value 'anonymousconsents.fieldchanged'. + enum: [anonymousconsents.fieldchanged] + correlationId: + type: string + description: | + The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header + data: + $ref: '#/components/schemas/AnonymousConsentFieldChangeEvent' + EnvelopeOfExperienceCreatedEvent: + type: object + properties: + type: + type: string + description: The type of event. Will always have the value 'experience.created'. + enum: [experience.created] + correlationId: + type: string + description: | + The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header + data: + $ref: '#/components/schemas/ExperienceCreatedEvent' + EnvelopeOfExperienceDeletedEvent: + type: object + properties: + type: + type: string + description: The type of event. Will always have the value 'experience.deleted'. + enum: [experience.deleted] + correlationId: + type: string + description: | + The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header + data: + $ref: '#/components/schemas/ExperienceDeletedEvent' + EnvelopeOfExperienceUpdatedEvent: + type: object + properties: + type: + type: string + description: The type of event. Will always have the value 'experience.updated'. + enum: [experience.updated] + correlationId: + type: string + description: | + The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header + data: + $ref: '#/components/schemas/ExperienceUpdatedEvent' + EnvelopeOfExperienceClientAddedEvent: + type: object + properties: + type: + type: string + description: The type of event. Will always have the value 'experience.client.added'. + enum: [experience.client.added] + correlationId: + type: string + description: | + The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header + data: + $ref: '#/components/schemas/ExperienceClientAddedEvent' + EnvelopeOfExperienceClientRemovedEvent: + type: object + properties: + type: + type: string + description: The type of event. Will always have the value 'experience.client.removed'. + enum: [experience.client.removed] + correlationId: + type: string + description: | + The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header + data: + $ref: '#/components/schemas/ExperienceClientRemovedEvent' + EnvelopeOfExperienceConsentOptionAddedEvent: + type: object + properties: + type: + type: string + description: The type of event. Will always have the value 'experience.consentoption.added'. + enum: [experience.consentoption.added] + correlationId: + type: string + description: | + The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header + data: + $ref: '#/components/schemas/ExperienceConsentOptionAddedEvent' + EnvelopeOfExperienceConsentOptionRemovedEvent: + type: object + properties: + type: + type: string + description: The type of event. Will always have the value 'experience.consentoption.removed'. + enum: [experience.consentoption.removed] + correlationId: + type: string + description: | + The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header + data: + $ref: '#/components/schemas/ExperienceConsentOptionRemovedEvent' + + AnonymousConsentFieldChangeEvent: + type: object + required: + - ChangeType + - ChangeTime + - AnonymousUserId + - ConsentId + - ConsentState + - Culture + properties: + changeType: + type: string + description: The change type + examples: + - Created + - Updated + - Deleted + changeTime: + type: string + format: date-time + description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + anonymousUserId: + type: string + format: guid + description: The anonymous user ID. The GUID is 32 digits separated by hyphens and is not considered to be PII. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + consentId: + type: string + description: The consent option URI + examples: + - self-consent://global/analytic-cookies + - self-consent://global/necessary-cookies + - self-consent://global/lego-marketing-cookies + consentState: + type: string + description: The state of the consent for the given consent id + examples: + - granted + - denied + - undecided + culture: + type: string + minLength: 5 + maxLength: 5 + description: The culture where the consent was changed. + examples: + - da-DK + - en-US + - en-GB + ExperienceCreatedEvent: + type: object + required: + - experienceId + - occurredOnTimestamp + - name + properties: + experienceId: + type: string + description: The experience id. + minLength: 1 + maxLength: 40 + examples: + - lego.com + occurredOnTimestamp: + type: string + format: date-time + description: 'The date and time of when experience was created. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + name: + type: string + description: name of experience + minLength: 1 + maxLength: 100 + examples: + - LEGO Webshop + ExperienceUpdatedEvent: + type: object + required: + - experienceId + - occurredOnTimestamp + - name + properties: + experienceId: + type: string + description: The experience id. + minLength: 1 + maxLength: 40 + examples: + - lego.com + occurredOnTimestamp: + type: string + format: date-time + description: 'The date and time of when experience was created. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + name: + type: string + description: name of experience + minLength: 1 + maxLength: 100 + examples: + - LEGO Webshop + ExperienceDeletedEvent: + type: object + required: + - experienceId + - occurredOnTimestamp + properties: + experienceId: + type: string + description: The experience id. + minLength: 1 + maxLength: 40 + examples: + - lego.com + occurredOnTimestamp: + type: string + format: date-time + description: 'The date and time of when experience was created. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + ExperienceClientAddedEvent: + type: object + required: + - experienceId + - occurredOnTimestamp + - clientId + properties: + experienceId: + type: string + description: The experience id. + minLength: 1 + maxLength: 40 + examples: + - lego.com + occurredOnTimestamp: + type: string + format: date-time + description: 'The date and time of when experience was created. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + clientId: + type: string + format: guid + description: Identity client id. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + ExperienceClientRemovedEvent: + type: object + required: + - experienceId + - occurredOnTimestamp + - clientId + properties: + experienceId: + type: string + description: The experience id. + minLength: 1 + maxLength: 40 + examples: + - lego.com + occurredOnTimestamp: + type: string + format: date-time + description: 'The date and time of when experience was created. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + clientId: + type: string + format: guid + description: Identity client id. + minLength: 36 + maxLength: 36 + examples: + - 95b2cb5f-d551-4106-805c-9b800b1a0133 + ExperienceConsentOptionAddedEvent: + type: object + required: + - experienceId + - occurredOnTimestamp + - consentOption + properties: + experienceId: + type: string + description: The experience id. + minLength: 1 + maxLength: 40 + examples: + - lego.com + occurredOnTimestamp: + type: string + format: date-time + description: 'The date and time of when experience was created. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + clientId: + type: string + description: The consent option URI + examples: + - self-consent://global/analytic-cookies + - self-consent://global/necessary-cookies + - self-consent://global/lego-marketing-cookies + ExperienceConsentOptionRemovedEvent: + type: object + required: + - experienceId + - occurredOnTimestamp + - consentOption + properties: + experienceId: + type: string + description: The experience id. + minLength: 1 + maxLength: 40 + examples: + - lego.com + occurredOnTimestamp: + type: string + format: date-time + description: 'The date and time of when experience was created. The format is in RFC 3339.' + examples: + - 2023-01-18T13:19:08.132084 + clientId: + type: string + description: The consent option URI + examples: + - self-consent://global/analytic-cookies + - self-consent://global/necessary-cookies + - self-consent://global/lego-marketing-cookies + messages: + UserConsentsObjectCreated: + messageId: UserConsentsObjectCreated + name: User consents object created + title: The object state of consents for a user + description: An event emitted whenever a user's consent is created. + tags: + - name: user + - name: consents + - name: created + payload: + $ref: '#/components/schemas/EnvelopeOfUserConsentsObjectCreated' + UserConsentsObjectChanged: + messageId: UserConsentsObjectChanged + name: User consents object changed + title: The object state of consents for a user + description: An event emitted whenever a user's consent is changed. + tags: + - name: user + - name: consents + - name: changed + payload: + $ref: '#/components/schemas/EnvelopeOfUserConsentsObjectChanged' + UserConsentsObjectDeleted: + messageId: UserConsentsObjectDeleted + name: User consents object deleted + title: The object state of consents for a user + description: An event emitted whenever a user's consent is deleted. + tags: + - name: user + - name: consents + - name: deleted + payload: + $ref: '#/components/schemas/EnvelopeOfUserConsentsObjectDeleted' + UserConsentFieldCreated: + messageId: UserConsentFieldCreated + name: User consent field created + title: The change of 1 specific consent being created + description: An event emitted whenever a user's consent is created. + tags: + - name: user + - name: consents + - name: created + payload: + $ref: '#/components/schemas/EnvelopeOfUserConsentFieldCreatedEvent' + UserConsentFieldChanged: + messageId: UserConsentFieldChanged + name: User consent field changed + title: The change of 1 specific consent being changed + description: An event emitted whenever a user's consent is changed. + tags: + - name: user + - name: consents + - name: changed + payload: + $ref: '#/components/schemas/EnvelopeOfUserConsentFieldChangedEvent' + UserConsentFieldDeleted: + messageId: UserConsentFieldDeleted + name: User consent field deleted + title: The change of 1 specific consent being deleted + description: An event emitted whenever a user's consent is deleted. + tags: + - name: user + - name: consents + - name: deleted + payload: + $ref: '#/components/schemas/EnvelopeOfUserConsentFieldDeletedEvent' + AnonymousConsentFieldChange: + messageId: AnonymousConsentFieldChange + name: Anonymous consent field change + title: Anonymous consent field change event + description: An event emitted whenever an anonymous consent is changed (added, updated or removed). + tags: + - name: anonymous + - name: consents + - name: deleted + payload: + $ref: '#/components/schemas/EnvelopeOfAnonymousConsentFieldChangeEvent' + ExperienceCreated: + messageId: ExperienceCreated + name: Experience created + title: Experience created event + description: An event emitted whenever an experience is created. + tags: + - name: experience + - name: created + payload: + $ref: '#/components/schemas/EnvelopeOfExperienceCreatedEvent' + ExperienceDeleted: + messageId: ExperienceDeleted + name: Experience deleted + title: Experience deleted event + description: An event emitted whenever an experience is deleted. + tags: + - name: experience + - name: deleted + payload: + $ref: '#/components/schemas/EnvelopeOfExperienceDeletedEvent' + ExperienceUpdated: + messageId: ExperienceUpdated + name: Experience updated + title: Experience updated event + description: An event emitted whenever an experience is updated. + tags: + - name: experience + - name: updated + payload: + $ref: '#/components/schemas/EnvelopeOfExperienceUpdatedEvent' + ExperienceClientAdded: + messageId: ExperienceClientAdded + name: Experience client added + title: Experience client added event + description: An event emitted whenever a client is added to experience. + tags: + - name: experience + - name: updated + payload: + $ref: '#/components/schemas/EnvelopeOfExperienceClientAddedEvent' + ExperienceClientRemoved: + messageId: ExperienceClientRemoved + name: Experience client removed + title: Experience client removed event + description: An event emitted whenever a client is removed from experience. + tags: + - name: experience + - name: updated + payload: + $ref: '#/components/schemas/EnvelopeOfExperienceClientRemovedEvent' + ExperienceConsentOptionAdded: + messageId: ExperienceConsentOptionAdded + name: Experience consent option added + title: Experience consent option added event + description: An event emitted whenever an consent option is added to experience. + tags: + - name: experience + - name: updated + payload: + $ref: '#/components/schemas/EnvelopeOfExperienceConsentOptionAddedEvent' + ExperienceConsentOptionRemoved: + messageId: ExperienceConsentOptionRemoved + name: Experience consent option removed + title: Experience consent option removed event + description: An event emitted whenever an consent option is removed from experience. + tags: + - name: experience + - name: updated + payload: + $ref: '#/components/schemas/EnvelopeOfExperienceConsentOptionRemovedEvent' +"; + + var reader = new AsyncApiStringReader(); + var deserialized = reader.Read(spec, out var diagnostic); + } + [Test] public void Serializev2_WithBindings_Serializes() { diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index 303b5fa9..e2aeac00 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -1,8 +1,12 @@ namespace LEGO.AsyncAPI.Tests { using System; + using System.Collections.Generic; using System.Linq; + using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Any; + using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -16,6 +20,87 @@ public void Read_WithMissingEverything_DeserializesWithErrors() var doc = reader.Read(yaml, out var diagnostic); } + [Test] + public void Read_WithExtensionParser_Parses() + { + var extensionName = "x-someValue"; + var yaml = @$"asyncapi: 2.3.0 +info: + title: test + version: 1.0.0 + contact: + name: API Support + url: https://www.example.com/support + email: support@example.com +channels: + workspace: + {extensionName}: onetwothreefour +"; + Func valueExtensionParser = (any) => + { + if (any.AnyType == AnyType.Primitive && any is AsyncApiString value) + { + if (value.Value == "onetwothreefour") + { + return new AsyncApiInteger(1234); + } + } + + return new AsyncApiString("No value provided"); + }; + + var settings = new AsyncApiReaderSettings + { + ExtensionParsers = new Dictionary> + { + { extensionName, valueExtensionParser }, + }, + }; + + var reader = new AsyncApiStringReader(settings); + var doc = reader.Read(yaml, out var diagnostic); + Assert.AreEqual((doc.Channels["workspace"].Extensions[extensionName] as AsyncApiInteger).Value, 1234); + } + + [Test] + public void Read_WithThrowingExtensionParser_AddsToDiagnostics() + { + var extensionName = "x-fail"; + var yaml = @$"asyncapi: 2.3.0 +info: + title: test + version: 1.0.0 + contact: + name: API Support + url: https://www.example.com/support + email: support@example.com +channels: + workspace: + {extensionName}: onetwothreefour +"; + Func failingExtensionParser = (any) => + { + throw new AsyncApiException("Failed to parse"); + }; + + var settings = new AsyncApiReaderSettings + { + ExtensionParsers = new Dictionary> + { + { extensionName, failingExtensionParser }, + }, + }; + + var reader = new AsyncApiStringReader(settings); + var doc = reader.Read(yaml, out var diagnostic); + + Assert.IsNotEmpty(diagnostic.Errors); + + var error = diagnostic.Errors.First(); + Assert.AreEqual("#/channels/workspace/x-fail", error.Pointer); + Assert.AreEqual("Failed to parse", error.Message); + } + [Test] public void Read_WithBasicPlusContact_Deserializes() { From d38c33f14d6de73e2563e29534965b06d423edac Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Wed, 3 May 2023 08:44:17 +0200 Subject: [PATCH 03/84] feat(Bindings)!: separate bindings and allow for custom bindings. (#107) BREAKING CHANGE: Bindings have been moved to a separate project --- .github/labeler.yml | 2 + .github/workflows/release-internal.yml | 40 +- .github/workflows/release-package.yml | 2 +- AsyncAPI.sln | 6 + src/LEGO.AsyncAPI.Bindings/Binding.cs | 14 + src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs | 49 + .../BindingsCollection.cs | 71 ++ .../ChannelBinding{T}.cs | 16 + .../Http/HttpMessageBinding.cs | 50 + .../Http/HttpOperationBinding.cs | 60 +- .../Kafka/KafkaChannelBinding.cs | 57 +- .../Kafka/KafkaMessageBinding.cs | 57 +- .../Kafka/KafkaOperationBinding.cs | 54 + .../Kafka/KafkaServerBinding.cs | 45 +- .../Kafka/TopicConfigurationObject.cs | 10 +- .../LEGO.AsyncAPI.Bindings.csproj | 43 + .../MessageBinding{T}.cs | 16 + .../OperationBinding{T}.cs | 16 + .../Pulsar/Persistence.cs | 0 .../Pulsar/PulsarChannelBinding.cs | 62 +- .../Pulsar/PulsarServerBinding.cs | 42 + .../Pulsar/RetentionDefinition.cs | 0 .../ServerBinding{T}.cs | 16 + .../WebSockets/WebSocketsChannelBinding.cs | 48 +- src/LEGO.AsyncAPI.Bindings/stylecop.json | 15 + .../AsyncApiReaderSettings.cs | 6 + .../AsyncApiStreamReader.cs | 6 +- .../AsyncApiStringReader.cs | 4 +- .../AsyncApiTextReader.cs | 4 +- .../AsyncApiYamlDocumentReader.cs | 8 + .../BindingDeserializer.cs | 31 + .../AsyncApiHttpBindingsDeserializer.cs | 25 - .../AsyncApiKafkaBindingsDeserializer.cs | 59 - .../AsyncApiPulsarBindingsDeserializer.cs | 43 - .../AsyncApiWebSocketsBindingsDeserializer.cs | 18 - .../Interface/IBindingParser{T}.cs | 12 + .../LEGO.AsyncAPI.Readers.csproj | 4 + .../ParseNodes/FixedFieldMap.cs | 4 +- .../ParseNodes/MapNode.cs | 50 +- .../ParseNodes/ParseNode.cs | 2 +- .../ParseNodes/PatternFieldMap.cs | 2 +- .../ParseNodes/PropertyNode.cs | 7 +- .../ParseNodes/ValueNode.cs | 2 +- src/LEGO.AsyncAPI.Readers/ParsingContext.cs | 8 + .../V2/AsyncApiBindingDeserializer.cs | 72 -- .../V2/AsyncApiChannelBindingDeserializer.cs | 34 +- .../V2/AsyncApiComponentsDeserializer.cs | 10 +- .../V2/AsyncApiDeserializer.cs | 8 +- .../V2/AsyncApiMessageBindingDeserializer.cs | 24 +- .../V2/AsyncApiMessageDeserializer.cs | 8 +- .../V2/AsyncApiMessageTraitDeserializer.cs | 2 +- .../AsyncApiOperationBindingDeserializer.cs | 24 +- .../V2/AsyncApiParameterDeserializer.cs | 2 +- .../V2/AsyncApiSchemaDeserializer.cs | 10 +- .../V2/AsyncApiServerBindingDeserializer.cs | 30 +- .../V2/AsyncApiV2VersionService.cs | 2 +- .../V2/ExtensionHelpers.cs | 43 + src/LEGO.AsyncAPI.Readers/YamlHelper.cs | 1 - .../Expressions/MethodExpression.cs | 7 - .../AsyncApiExtensibleExtensions.cs | 4 +- src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs | 41 + src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs | 7 +- .../Models/AsyncApiComponents.cs | 16 +- src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs | 10 +- .../Models/AsyncApiWriterExtensions.cs | 2 +- .../Models/Bindings/BindingType.cs | 21 - .../Bindings/Http/HttpMessageBinding.cs | 75 -- .../Bindings/Kafka/KafkaOperationBinding.cs | 73 -- .../Bindings/Pulsar/PulsarServerBinding.cs | 66 - .../Models/Interfaces/IBinding.cs | 8 +- src/LEGO.AsyncAPI/Models/ReferenceType.cs | 16 +- .../Services/AsyncApiReferenceResolver.cs | 25 +- .../Services/AsyncApiVisitorBase.cs | 10 +- src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs | 32 +- src/LEGO.AsyncAPI/Services/CurrentKeys.cs | 2 + .../Validation/Rules/AsyncApiContactRules.cs | 1 - .../Rules/AsyncApiCorrelationIdRules.cs | 1 - .../Validation/Rules/AsyncApiDocumentRules.cs | 1 - .../Rules/AsyncApiExtensionRules.cs | 1 - .../Validation/Rules/AsyncApiLicenseRules.cs | 1 - .../Rules/AsyncApiOAuthFlowRules.cs | 2 +- .../Validation/Rules/AsyncApiTagRules.cs | 1 - .../Writers/AsyncApiWriterException.cs | 2 +- .../Writers/AsyncApiWriterExtensions.cs | 6 + .../Writers/AsyncApiWriterSettings.cs | 2 +- src/LEGO.AsyncAPI/Writers/StringExtensions.cs | 2 +- .../AsyncApiDocumentBuilder.cs | 22 +- .../AsyncApiDocumentV2Tests.cs | 1111 ++--------------- .../AsyncApiLicenseTests.cs | 26 +- .../AsyncApiReaderTests.cs | 2 + .../Bindings/CustomBinding_Should.cs | 124 ++ .../Bindings/Http/HttpBindings_Should.cs | 23 +- .../Bindings/Kafka/KafkaBindings_Should.cs | 35 +- .../Bindings/Pulsar/PulsarBindings_Should.cs | 53 +- .../WebSockets/WebSocketBindings_Should.cs | 13 +- .../LEGO.AsyncAPI.Tests.csproj | 5 + .../Models/AsyncApiChannel_Should.cs | 12 +- .../Models/AsyncApiContact_Should.cs | 8 +- .../AsyncApiExternalDocumentation_Should.cs | 8 +- .../Models/AsyncApiInfo_Should.cs | 8 +- .../Models/AsyncApiLicense_Should.cs | 8 +- .../Models/AsyncApiMessageExample_Should.cs | 8 +- .../Models/AsyncApiMessage_Should.cs | 20 +- .../Models/AsyncApiOAuthFlow_Should.cs | 8 +- .../Models/AsyncApiOperation_Should.cs | 30 +- .../Models/AsyncApiSchema_Should.cs | 18 +- .../AsyncApiSecurityRequirement_Should.cs | 18 +- .../Models/AsyncApiServer_Should.cs | 16 +- test/LEGO.AsyncAPI.Tests/StringExtensions.cs | 4 +- .../Validation/ValidationRulesetTests.cs | 6 +- test/LEGO.AsyncAPI.Tests/stylecop.json | 15 + 111 files changed, 1341 insertions(+), 2051 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/Binding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Http/HttpOperationBinding.cs (55%) rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/KafkaChannelBinding.cs (52%) rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/KafkaMessageBinding.cs (57%) create mode 100644 src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/KafkaServerBinding.cs (53%) rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Kafka/TopicConfigurationObject.cs (93%) create mode 100644 src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj create mode 100644 src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Pulsar/Persistence.cs (100%) rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Pulsar/PulsarChannelBinding.cs (59%) create mode 100644 src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/Pulsar/RetentionDefinition.cs (100%) create mode 100644 src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs rename src/{LEGO.AsyncAPI/Models/Bindings => LEGO.AsyncAPI.Bindings}/WebSockets/WebSocketsChannelBinding.cs (57%) create mode 100644 src/LEGO.AsyncAPI.Bindings/stylecop.json create mode 100644 src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs create mode 100644 src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs create mode 100644 src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs create mode 100644 src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs create mode 100644 test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs create mode 100644 test/LEGO.AsyncAPI.Tests/stylecop.json diff --git a/.github/labeler.yml b/.github/labeler.yml index 4561e7f9..13e2f191 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -4,6 +4,8 @@ ci/cd: - .github/**/* asyncapi.readers: - src/LEGO.AsyncAPI.Readers/**/* +asyncapi.bindings: +- src/LEGO.AsyncAPI.Bindings/**/* asyncapi.models: - src/LEGO.AsyncAPI/**/* asyncapi.tests: diff --git a/.github/workflows/release-internal.yml b/.github/workflows/release-internal.yml index 8b54315b..66df465a 100644 --- a/.github/workflows/release-internal.yml +++ b/.github/workflows/release-internal.yml @@ -5,31 +5,51 @@ on: paths: - 'src/LEGO.AsyncAPI/**' - 'src/LEGO.AsyncAPI.Readers/**' - - 'src/LEGO.AsyncAPI.Writers/**' - - ".github/workflows/release-package.yml" + - 'src/LEGO.AsyncAPI.Bindings/**' + - ".github/workflows/release-internal.yml" - '!**/*.md' workflow_dispatch: jobs: - release: + check: + runs-on: ubuntu-latest + name: Check release + environment: AsyncAPI + steps: + - name: Checkout repository + uses: actions/checkout@v1 + + - name: Semantic Release + uses: cycjimmy/semantic-release-action@v3 + with: + dry_run: true + ci: false + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + outputs: + trigger_release: ${{ steps.semantic.outputs.new_release_published }} + version: ${{ steps.semantic.outputs.new_release_published == 'true' && steps.semantic.outputs.new_release_version }} + + pre-release: runs-on: ubuntu-latest name: Publish NuGet packages + needs: check + environment: AsyncAPI strategy: matrix: - package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers"] + package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers", "LEGO.AsyncAPI.Bindings" ] steps: - name: Checkout repository uses: actions/checkout@v1 - name: Setup .NET Core @ Latest + if: needs.check.outputs.trigger_release == 'true' uses: actions/setup-dotnet@v1 - with: - source-url: https://nuget.pkg.github.com/LEGO/index.json - env: - NUGET_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Build ${{ matrix.package-name }} project and pack NuGet package - run: dotnet pack src/${{ matrix.package-name }}/${{ matrix.package-name }}.csproj -c Release -o out-${{ matrix.package-name }} -p:PackageVersion=0.2.$GITHUB_RUN_NUMBER.0-prerelease + if: needs.check.outputs.trigger_release == 'true' + run: dotnet pack src/${{ matrix.package-name }}/${{ matrix.package-name }}.csproj -c Release -o out-${{ matrix.package-name }} -p:PackageVersion=${{ needs.check.outputs.version }}-beta - name: Push generated package to GitHub Packages registry - run: dotnet nuget push out-${{ matrix.package-name }}/*.nupkg --skip-duplicate -n --api-key ${{secrets.GITHUB_TOKEN}} + if: needs.check.outputs.trigger_release == 'true' + run: dotnet nuget push out-${{ matrix.package-name }}/*.nupkg -s https://api.nuget.org/v3/index.json --skip-duplicate -n --api-key ${{secrets.NUGET}} diff --git a/.github/workflows/release-package.yml b/.github/workflows/release-package.yml index 0f064176..9e52a9fe 100644 --- a/.github/workflows/release-package.yml +++ b/.github/workflows/release-package.yml @@ -56,7 +56,7 @@ jobs: environment: AsyncAPI strategy: matrix: - package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers" ] + package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers", "LEGO.AsyncAPI.Bindings" ] steps: - name: Checkout repository uses: actions/checkout@v1 diff --git a/AsyncAPI.sln b/AsyncAPI.sln index bf944501..f972fa3d 100644 --- a/AsyncAPI.sln +++ b/AsyncAPI.sln @@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LEGO.AsyncAPI.Bindings", "src\LEGO.AsyncAPI.Bindings\LEGO.AsyncAPI.Bindings.csproj", "{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -32,6 +34,10 @@ Global {7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Release|Any CPU.Build.0 = Release|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/LEGO.AsyncAPI.Bindings/Binding.cs b/src/LEGO.AsyncAPI.Bindings/Binding.cs new file mode 100644 index 00000000..e7c57400 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Binding.cs @@ -0,0 +1,14 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.Interface; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class Binding : AsyncApiBinding, IBindingParser + where T : IBinding, new() + { + public abstract T LoadBinding(PropertyNode node); + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs b/src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs new file mode 100644 index 00000000..424afc9e --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/BindingHelpers.cs @@ -0,0 +1,49 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public static class BindingHelpers + { + public static T ParseMap(this ParseNode node, FixedFieldMap fixedFieldMap) + where T : new() + { + var mapNode = node.CheckMapNode(node.Context.GetLocation()); + if (mapNode == null) + { + return default(T); + } + + var instance = new T(); + + foreach (var propertyNode in mapNode) + { + propertyNode.ParseField(instance, fixedFieldMap, null); + } + + return instance; + } + + public static T ParseMapWithExtensions(this ParseNode node, FixedFieldMap fixedFieldMap) + where T : IAsyncApiExtensible, new() + { + var mapNode = node.CheckMapNode(node.Context.GetLocation()); + if (mapNode == null) + { + return default(T); + } + + var instance = new T(); + + foreach (var propertyNode in mapNode) + { + propertyNode.ParseField(instance, fixedFieldMap, ExtensionHelpers.GetExtensionsFieldMap()); + } + + return instance; + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs new file mode 100644 index 00000000..0a34ec39 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -0,0 +1,71 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Bindings.Http; + using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Bindings.Pulsar; + using LEGO.AsyncAPI.Bindings.WebSockets; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.Interface; + + public static class BindingsCollection + { + public static TCollection Add( + this TCollection destination, + IEnumerable source) + where TCollection : ICollection + { + ArgumentNullException.ThrowIfNull(destination); + ArgumentNullException.ThrowIfNull(source); + + if (destination is List list) + { + list.AddRange(source); + return destination; + } + + foreach (var item in source) + { + destination.Add(item); + } + + return destination; + } + + public static IEnumerable> All => new List> + { + Pulsar, + Kafka, + Http, + }; + + public static IEnumerable> Http => new List> + { + new HttpOperationBinding(), + new HttpMessageBinding() + }; + + public static IEnumerable> Websockets => new List> + { + new WebSocketsChannelBinding(), + }; + + public static IEnumerable> Kafka => new List> + { + new KafkaServerBinding(), + new KafkaChannelBinding(), + new KafkaOperationBinding(), + new KafkaMessageBinding(), + }; + + public static IEnumerable> Pulsar => new List> + { + // Pulsar + new PulsarServerBinding(), + new PulsarChannelBinding(), + }; + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs new file mode 100644 index 00000000..d792809c --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class ChannelBinding : Binding, IChannelBinding + where T : IChannelBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("ChannelBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs new file mode 100644 index 00000000..15a654dd --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs @@ -0,0 +1,50 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Http +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for http messaging channels. + /// + public class HttpMessageBinding : MessageBinding + { + + /// + /// A Schema object containing the definitions for HTTP-specific headers. This schema MUST be of type object and have a properties key. + /// + public AsyncApiSchema Headers { get; set; } + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + + writer.WriteOptionalObject(AsyncApiConstants.Headers, this.Headers, (w, h) => h.SerializeV2(w)); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + + writer.WriteEndObject(); + } + + public override string BindingKey => "http"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, + }; + + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs similarity index 55% rename from src/LEGO.AsyncAPI/Models/Bindings/Http/HttpOperationBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs index 27a91798..b5c92c38 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs @@ -1,21 +1,31 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Http +namespace LEGO.AsyncAPI.Bindings.Http { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for http operations. /// - public class HttpOperationBinding : IOperationBinding + public class HttpOperationBinding : OperationBinding { + public enum HttpOperationType + { + [Display("request")] + Request, + + [Display("response")] + Response, + } /// /// REQUIRED. Type of operation. Its value MUST be either request or response. /// - public string Type { get; set; } + public HttpOperationType? Type { get; set; } /// /// When type is request, this is the HTTP method, otherwise it MUST be ignored. Its value MUST be one of GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, CONNECT, and TRACE. @@ -27,15 +37,10 @@ public class HttpOperationBinding : IOperationBinding /// public AsyncApiSchema Query { get; set; } - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - /// /// Serialize to AsyncAPI V2 document without using reference. /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -44,37 +49,22 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteStartObject(); - writer.WriteRequiredProperty(AsyncApiConstants.Type, this.Type); + writer.WriteRequiredProperty(AsyncApiConstants.Type, this.Type.GetDisplayName()); writer.WriteOptionalProperty(AsyncApiConstants.Method, this.Method); writer.WriteOptionalObject(AsyncApiConstants.Query, this.Query, (w, h) => h.SerializeV2(w)); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - public void SerializeV2(IAsyncApiWriter writer) + protected override FixedFieldMap FixedFieldMap => new() { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, + { "query", (a, n) => { a.Query = JsonSchemaDeserializer.LoadSchema(n); } }, + }; - BindingType IBinding.Type => BindingType.Http; + public override string BindingKey => "http"; } } diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs similarity index 52% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaChannelBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs index 998c47b3..78b745e0 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs @@ -1,16 +1,17 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Kafka +namespace LEGO.AsyncAPI.Bindings.Kafka { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Bindings.Kafka; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for Kafka channel settings. /// - public class KafkaChannelBinding : IChannelBinding + public class KafkaChannelBinding : ChannelBinding { /// /// Kafka topic name if different from channel name. @@ -32,23 +33,30 @@ public class KafkaChannelBinding : IChannelBinding /// public TopicConfigurationObject TopicConfiguration { get; set; } - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Kafka; - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } + public override string BindingKey => "kafka"; - public IDictionary Extensions { get; set; } = new Dictionary(); + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, + { "partitions", (a, n) => { a.Partitions = n.GetIntegerValue(); } }, + { "topicConfiguration", (a, n) => { a.TopicConfiguration = n.ParseMap(kafkaChannelTopicConfigurationObjectFixedFields); } }, + { "replicas", (a, n) => { a.Replicas = n.GetIntegerValue(); } }, + }; + + private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new() + { + { "cleanup.policy", (a, n) => { a.CleanupPolicy = n.CreateSimpleList(s => s.GetScalarValue()); } }, + { "retention.ms", (a, n) => { a.RetentionMiliseconds = n.GetIntegerValue(); } }, + { "retention.bytes", (a, n) => { a.RetentionBytes = n.GetIntegerValue(); } }, + { "delete.retention.ms", (a, n) => { a.DeleteRetentionMiliseconds = n.GetIntegerValue(); } }, + { "max.message.bytes", (a, n) => { a.MaxMessageBytes = n.GetIntegerValue(); } }, + }; /// /// Serialize to AsyncAPI V2 document without using reference. /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -61,24 +69,9 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.Replicas, this.Replicas); writer.WriteOptionalObject(AsyncApiConstants.TopicConfiguration, this.TopicConfiguration, (w, t) => t.Serialize(w)); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } } } diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs similarity index 57% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaMessageBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs index 1fd4bb90..31123de1 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaMessageBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs @@ -1,16 +1,17 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Kafka +namespace LEGO.AsyncAPI.Bindings.Kafka { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for kafka messages. /// - public class KafkaMessageBinding : IMessageBinding + public class KafkaMessageBinding : MessageBinding { /// /// The message key. NOTE: You can also use the reference object way. @@ -35,22 +36,8 @@ public class KafkaMessageBinding : IMessageBinding /// /// The version of this binding. If omitted, "latest" MUST be assumed. /// - public string BindingVersion { get; set; } - - /// - /// Indicates if object is populated with data or is just a reference to the data. - /// - public bool UnresolvedReference { get; set; } - - /// - /// Reference object. - /// - public AsyncApiReference Reference { get; set; } - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -64,6 +51,7 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.SchemaIdPayloadEncoding, this.SchemaIdPayloadEncoding); writer.WriteOptionalProperty(AsyncApiConstants.SchemaLookupStrategy, this.SchemaLookupStrategy); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } @@ -73,28 +61,17 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) /// /// The writer. /// writer - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - this.SerializeV2WithoutReference(writer); - } - /// - /// Gets or sets this object MAY be extended with Specification Extensions. - /// To protect the API from leaking the underlying JSON library, the extension data extraction is handled by a customer resolver. - /// - public IDictionary Extensions { get; set; } = new Dictionary(); + public override string BindingKey => "kafka"; - public BindingType Type => BindingType.Kafka; + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "key", (a, n) => { a.Key = JsonSchemaDeserializer.LoadSchema(n); } }, + { "schemaIdLocation", (a, n) => { a.SchemaIdLocation = n.GetScalarValue(); } }, + { "schemaIdPayloadEncoding", (a, n) => { a.SchemaIdPayloadEncoding = n.GetScalarValue(); } }, + { "schemaLookupStrategy", (a, n) => { a.SchemaLookupStrategy = n.GetScalarValue(); } }, + }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs new file mode 100644 index 00000000..db80e0ce --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs @@ -0,0 +1,54 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Kafka +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for Kafka operations. + /// + public class KafkaOperationBinding : OperationBinding + { + /// + /// Id of the consumer group. + /// + public AsyncApiSchema GroupId { get; set; } + + /// + /// Id of the consumer inside a consumer group. + /// + public AsyncApiSchema ClientId { get; set; } + + public override string BindingKey => "kafka"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "groupId", (a, n) => { a.GroupId = JsonSchemaDeserializer.LoadSchema(n); } }, + { "clientId", (a, n) => { a.ClientId = JsonSchemaDeserializer.LoadSchema(n); } }, + }; + + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalObject(AsyncApiConstants.GroupId, this.GroupId, (w, h) => h.SerializeV2(w)); + writer.WriteOptionalObject(AsyncApiConstants.ClientId, this.ClientId, (w, h) => h.SerializeV2(w)); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs similarity index 53% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaServerBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs index bd9d6b19..1d40a798 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaServerBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs @@ -1,16 +1,16 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Kafka +namespace LEGO.AsyncAPI.Bindings.Kafka { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; /// /// Binding class for Kafka server settings. /// - public class KafkaServerBinding : IServerBinding + public class KafkaServerBinding : ServerBinding { /// /// API URL for the Schema Registry used when producing Kafka messages (if a Schema Registry was used) @@ -22,23 +22,20 @@ public class KafkaServerBinding : IServerBinding /// public string SchemaRegistryVendor { get; set; } - /// - /// The version of this binding. - /// - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Kafka; - public bool UnresolvedReference { get; set; } + public override string BindingKey => "kafka"; - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "schemaRegistryUrl", (a, n) => { a.SchemaRegistryUrl = n.GetScalarValue(); } }, + { "schemaRegistryVendor", (a, n) => { a.SchemaRegistryVendor = n.GetScalarValue(); } }, + }; /// /// Serialize to AsyncAPI V2 document without using reference. /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -49,24 +46,8 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.SchemaRegistryUrl, this.SchemaRegistryUrl); writer.WriteOptionalProperty(AsyncApiConstants.SchemaRegistryVendor, this.SchemaRegistryVendor); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } } } diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/TopicConfigurationObject.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs similarity index 93% rename from src/LEGO.AsyncAPI/Models/Bindings/Kafka/TopicConfigurationObject.cs rename to src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs index 5a1ccffe..da6233c3 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/TopicConfigurationObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs @@ -1,12 +1,12 @@ // Copyright (c) The LEGO Group. All rights reserved. -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; -using System; -using System.Collections.Generic; - namespace LEGO.AsyncAPI.Models.Bindings.Kafka { + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + public class TopicConfigurationObject : IAsyncApiElement { /// diff --git a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj new file mode 100644 index 00000000..e05aacab --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj @@ -0,0 +1,43 @@ + + + + net6.0 + disable + The LEGO Group + https://github.com/LEGO/AsyncAPI.NET + README.md + AsyncAPI.NET Bindings + asyncapi .net openapi documentation + AsyncAPI.NET.Bindings + LEGO.AsyncAPI.Bindings + LEGO.AsyncAPI.Bindings + https://github.com/LEGO/AsyncAPI.NET + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + True + \ + + + + + + + + diff --git a/src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs new file mode 100644 index 00000000..3e3956f9 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/MessageBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class MessageBinding : Binding, IMessageBinding + where T : IMessageBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("MessageBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs new file mode 100644 index 00000000..0e4216ed --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class OperationBinding : Binding , IOperationBinding + where T : IOperationBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("OperationBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/Persistence.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/Persistence.cs similarity index 100% rename from src/LEGO.AsyncAPI/Models/Bindings/Pulsar/Persistence.cs rename to src/LEGO.AsyncAPI.Bindings/Pulsar/Persistence.cs diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs similarity index 59% rename from src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarChannelBinding.cs rename to src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs index 7a4f504f..c673a8e8 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs @@ -1,16 +1,15 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Pulsar +namespace LEGO.AsyncAPI.Bindings.Pulsar { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Bindings.Pulsar; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; - /// - /// Binding class for Pulsar server settings. - /// - public class PulsarChannelBinding : IChannelBinding + public class PulsarChannelBinding : ChannelBinding { /// /// The namespace associated with the topic. @@ -38,7 +37,7 @@ public class PulsarChannelBinding : IChannelBinding public RetentionDefinition Retention { get; set; } /// - /// Message Time-to-live in seconds. + /// Message Time-to-live in seconds. /// public int? TTL { get; set; } @@ -47,22 +46,9 @@ public class PulsarChannelBinding : IChannelBinding /// public bool? Deduplication { get; set; } - /// - /// The version of this binding. - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Pulsar; - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); + public override string BindingKey => "pulsar"; - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -78,24 +64,26 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.TTL, this.TTL); writer.WriteOptionalProperty(AsyncApiConstants.Deduplication, this.Deduplication); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - public void SerializeV2(IAsyncApiWriter writer) + protected override FixedFieldMap FixedFieldMap => new() { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "namespace", (a, n) => { a.Namespace = n.GetScalarValue(); } }, + { "persistence", (a, n) => { a.Persistence = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "compaction", (a, n) => { a.Compaction = n.GetIntegerValue(); } }, + { "retention", (a, n) => { a.Retention = n.ParseMap(this.pulsarServerBindingRetentionFixedFields); } }, + { "geo-replication", (a, n) => { a.GeoReplication = n.CreateSimpleList(s => s.GetScalarValue()); } }, + { "ttl", (a, n) => { a.TTL = n.GetIntegerValue(); } }, + { "deduplication", (a, n) => { a.Deduplication = n.GetBooleanValue(); } }, + }; + + private FixedFieldMap pulsarServerBindingRetentionFixedFields = new() + { + { "time", (a, n) => { a.Time = n.GetIntegerValue(); } }, + { "size", (a, n) => { a.Size = n.GetIntegerValue(); } }, + }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs new file mode 100644 index 00000000..e767443d --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs @@ -0,0 +1,42 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Pulsar +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for Pulsar server settings. + /// + public class PulsarServerBinding : ServerBinding + { + /// + /// The pulsar tenant. If omitted, "public" must be assumed. + /// + public string Tenant { get; set; } + + public override string BindingKey => "pulsar"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "tenant", (a, n) => { a.Tenant = n.GetScalarValue(); } }, + }; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty(AsyncApiConstants.Tenant, this.Tenant); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/RetentionDefinition.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/RetentionDefinition.cs similarity index 100% rename from src/LEGO.AsyncAPI/Models/Bindings/Pulsar/RetentionDefinition.cs rename to src/LEGO.AsyncAPI.Bindings/Pulsar/RetentionDefinition.cs diff --git a/src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs new file mode 100644 index 00000000..6c1c58cf --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/ServerBinding{T}.cs @@ -0,0 +1,16 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public abstract class ServerBinding : Binding, IServerBinding + where T : IServerBinding, new() + { + protected abstract FixedFieldMap FixedFieldMap { get; } + + public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("ServerBinding", node.Value, this.FixedFieldMap); + } +} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/WebSockets/WebSocketsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs similarity index 57% rename from src/LEGO.AsyncAPI/Models/Bindings/WebSockets/WebSocketsChannelBinding.cs rename to src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs index 7aa2e793..94afcb55 100644 --- a/src/LEGO.AsyncAPI/Models/Bindings/WebSockets/WebSocketsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs @@ -1,13 +1,14 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.WebSockets +namespace LEGO.AsyncAPI.Bindings.WebSockets { using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; - public class WebSocketsChannelBinding : IChannelBinding + public class WebSocketsChannelBinding : ChannelBinding { /// /// The HTTP method t use when establishing the connection. Its value MUST be either 'GET' or 'POST'. @@ -24,18 +25,17 @@ public class WebSocketsChannelBinding : IChannelBinding /// public AsyncApiSchema Headers { get; set; } - public string BindingVersion { get; set; } + public override string BindingKey => "websockets"; - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = - new Dictionary(); - - public BindingType Type => BindingType.Websockets; + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, + { "query", (a, n) => { a.Query = JsonSchemaDeserializer.LoadSchema(n); } }, + { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, + }; - public void SerializeV2WithoutReference(IAsyncApiWriter writer) + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) { @@ -48,24 +48,8 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalObject(AsyncApiConstants.Query, this.Query, (w, h) => h.SerializeV2(w)); writer.WriteOptionalObject(AsyncApiConstants.Headers, this.Headers, (w, h) => h.SerializeV2(w)); writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - + writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } } -} \ No newline at end of file +} diff --git a/src/LEGO.AsyncAPI.Bindings/stylecop.json b/src/LEGO.AsyncAPI.Bindings/stylecop.json new file mode 100644 index 00000000..0a8f4661 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/stylecop.json @@ -0,0 +1,15 @@ +{ + // ACTION REQUIRED: This file was automatically added to your project, but it + // will not take effect until additional steps are taken to enable it. See the + // following page for additional information: + // + // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md + + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "The LEGO Group", + "xmlHeader": false + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index 21fe1c73..01576991 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -6,6 +6,7 @@ namespace LEGO.AsyncAPI.Readers using System.Collections.Generic; using System.IO; using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.Interface; using LEGO.AsyncAPI.Validations; public enum ReferenceResolutionSetting @@ -40,6 +41,11 @@ public Dictionary> { get; set; } = new Dictionary>(); + public List> + Bindings + { get; } = + new List>(); + /// /// Rules to use for validating AsyncApi specification. If none are provided a default set of rules are applied. /// diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs index da04716d..ed6c3942 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs @@ -33,7 +33,7 @@ public AsyncApiStreamReader(AsyncApiReaderSettings settings = null) public AsyncApiDocument Read(Stream input, out AsyncApiDiagnostic diagnostic) { var reader = new StreamReader(input); - var result = new AsyncApiTextReaderReader(this.settings).Read(reader, out diagnostic); + var result = new AsyncApiTextReader(this.settings).Read(reader, out diagnostic); if (!this.settings.LeaveStreamOpen) { reader.Dispose(); @@ -65,7 +65,7 @@ public async Task ReadAsync(Stream input) var reader = new StreamReader(bufferedStream); - return await new AsyncApiTextReaderReader(this.settings).ReadAsync(reader); + return await new AsyncApiTextReader(this.settings).ReadAsync(reader); } /// @@ -80,7 +80,7 @@ public T ReadFragment(Stream input, AsyncApiVersion version, out AsyncApiDiag { using (var reader = new StreamReader(input)) { - return new AsyncApiTextReaderReader(this.settings).ReadFragment(reader, version, out diagnostic); + return new AsyncApiTextReader(this.settings).ReadFragment(reader, version, out diagnostic); } } } diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs index c6680620..8d4bd870 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs @@ -30,7 +30,7 @@ public AsyncApiDocument Read(string input, out AsyncApiDiagnostic diagnostic) { using (var reader = new StringReader(input)) { - return new AsyncApiTextReaderReader(this.settings).Read(reader, out diagnostic); + return new AsyncApiTextReader(this.settings).Read(reader, out diagnostic); } } @@ -42,7 +42,7 @@ public T ReadFragment(string input, AsyncApiVersion version, out AsyncApiDiag { using (var reader = new StringReader(input)) { - return new AsyncApiTextReaderReader(this.settings).ReadFragment(reader, version, out diagnostic); + return new AsyncApiTextReader(this.settings).ReadFragment(reader, version, out diagnostic); } } } diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs index 51817948..0eaef44d 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs @@ -14,7 +14,7 @@ namespace LEGO.AsyncAPI.Readers /// /// Service class for converting contents of TextReader into AsyncApiDocument instances /// - public class AsyncApiTextReaderReader : IAsyncApiReader + public class AsyncApiTextReader : IAsyncApiReader { private readonly AsyncApiReaderSettings settings; @@ -22,7 +22,7 @@ public class AsyncApiTextReaderReader : IAsyncApiReader /// - public AsyncApiTextReaderReader(AsyncApiReaderSettings settings = null) + public AsyncApiTextReader(AsyncApiReaderSettings settings = null) { this.settings = settings ?? new AsyncApiReaderSettings(); } diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs index 9dae2141..cc03a892 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs @@ -41,6 +41,10 @@ public AsyncApiDocument Read(YamlDocument input, out AsyncApiDiagnostic diagnost var context = new ParsingContext(diagnostic) { ExtensionParsers = this.settings.ExtensionParsers, + ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + ChannelBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + OperationBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + MessageBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), }; AsyncApiDocument document = null; @@ -144,6 +148,10 @@ public T ReadFragment(YamlDocument input, AsyncApiVersion version, out AsyncA var context = new ParsingContext(diagnostic) { ExtensionParsers = this.settings.ExtensionParsers, + ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + ChannelBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + OperationBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), + MessageBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), }; IAsyncApiElement element = null; diff --git a/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs new file mode 100644 index 00000000..5744985d --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs @@ -0,0 +1,31 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers +{ + using LEGO.AsyncAPI.Extensions; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public class BindingDeserializer + { + public static T LoadBinding(string nodeName, ParseNode node, FixedFieldMap fieldMap) + where T : IBinding, new() + { + var mapNode = node.CheckMapNode(nodeName); + var binding = new T(); + + AsyncApiV2Deserializer.ParseMap(mapNode, binding, fieldMap, BindingPatternExtensionFields()); + + return binding; + } + + private static PatternFieldMap BindingPatternExtensionFields() + where T : IBinding, new() + { + return new() + { + { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) }, + }; + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs deleted file mode 100644 index 72c2579b..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiHttpBindingsDeserializer.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Models.Bindings.Http; - using LEGO.AsyncAPI.Readers.ParseNodes; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap httpMessageBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "headers", (a, n) => { a.Headers = LoadSchema(n); } }, - }; - - private static FixedFieldMap httpOperationBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "type", (a, n) => { a.Type = n.GetScalarValue(); } }, - { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, - { "query", (a, n) => { a.Query = LoadSchema(n); } }, - }; - - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs deleted file mode 100644 index 0d80eb8f..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiKafkaBindingsDeserializer.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Models.Bindings.Kafka; - using LEGO.AsyncAPI.Readers.ParseNodes; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap kafkaServerBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "schemaRegistryUrl", (a, n) => { a.SchemaRegistryUrl = n.GetScalarValue(); } }, - { "schemaRegistryVendor", (a, n) => { a.SchemaRegistryVendor = n.GetScalarValue(); } }, - }; - - private static FixedFieldMap kafkaChannelBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, - { "partitions", (a, n) => { a.Partitions = n.GetIntegerValue(); } }, - { "topicConfiguration", (a, n) => { a.TopicConfiguration = LoadTopicConfiguration(n); } }, - { "replicas", (a, n) => { a.Replicas = n.GetIntegerValue(); } }, - }; - - private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new() - { - { "cleanup.policy", (a, n) => { a.CleanupPolicy = n.CreateSimpleList(s => s.GetScalarValue()); } }, - { "retention.ms", (a, n) => { a.RetentionMiliseconds = n.GetIntegerValue(); } }, - { "retention.bytes", (a, n) => { a.RetentionBytes = n.GetIntegerValue(); } }, - { "delete.retention.ms", (a, n) => { a.DeleteRetentionMiliseconds = n.GetIntegerValue(); } }, - { "max.message.bytes", (a, n) => { a.MaxMessageBytes = n.GetIntegerValue(); } }, - }; - - private static FixedFieldMap kafkaOperationBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "groupId", (a, n) => { a.GroupId = LoadSchema(n); } }, - { "clientId", (a, n) => { a.ClientId = LoadSchema(n); } }, - }; - - private static FixedFieldMap kafkaMessageBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "key", (a, n) => { a.Key = LoadSchema(n); } }, - { "schemaIdLocation", (a, n) => { a.SchemaIdLocation = n.GetScalarValue(); } }, - { "schemaIdPayloadEncoding", (a, n) => { a.SchemaIdPayloadEncoding = n.GetScalarValue(); } }, - { "schemaLookupStrategy", (a, n) => { a.SchemaLookupStrategy = n.GetScalarValue(); } }, - }; - - private static TopicConfigurationObject LoadTopicConfiguration(ParseNode node) - { - var mapNode = node.CheckMapNode("topicConfiguration"); - var retention = new TopicConfigurationObject(); - ParseMap(mapNode, retention, kafkaChannelTopicConfigurationObjectFixedFields, null); - return retention; - } - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs deleted file mode 100644 index 6250a064..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiPulsarBindingsDeserializer.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Models.Bindings.Pulsar; - using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap pulsarServerBindingFixedFields = new () - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "tenant", (a, n) => { a.Tenant = n.GetScalarValue(); } }, - }; - - private static FixedFieldMap pulsarChannelBindingFixedFields = new () - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "namespace", (a, n) => { a.Namespace = n.GetScalarValue(); } }, - { "persistence", (a, n) => { a.Persistence = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "compaction", (a, n) => { a.Compaction = n.GetIntegerValue(); } }, - { "retention", (a, n) => { a.Retention = LoadRetention(n); } }, - { "geo-replication", (a, n) => { a.GeoReplication = n.CreateSimpleList(s => s.GetScalarValue()); } }, - { "ttl", (a, n) => { a.TTL = n.GetIntegerValue(); } }, - { "deduplication", (a, n) => { a.Deduplication = n.GetBooleanValue(); } }, - }; - - private static FixedFieldMap pulsarServerBindingRetentionFixedFields = new () - { - { "time", (a, n) => { a.Time = n.GetIntegerValue(); } }, - { "size", (a, n) => { a.Size = n.GetIntegerValue(); } }, - }; - - private static RetentionDefinition LoadRetention(ParseNode node) - { - var mapNode = node.CheckMapNode("retention"); - var retention = new RetentionDefinition(); - ParseMap(mapNode, retention, pulsarServerBindingRetentionFixedFields, null); - return retention; - } - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs deleted file mode 100644 index 4d4af870..00000000 --- a/src/LEGO.AsyncAPI.Readers/Bindings/AsyncApiWebSocketsBindingsDeserializer.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Readers.ParseNodes; - using Models.Bindings.WebSockets; - - internal static partial class AsyncApiV2Deserializer - { - private static FixedFieldMap webSocketsChannelBindingFixedFields = new() - { - { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, - { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, - { "query", (a, n) => { a.Query = LoadSchema(n); } }, - { "headers", (a, n) => { a.Headers = LoadSchema(n); } }, - }; - } -} diff --git a/src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs b/src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs new file mode 100644 index 00000000..9c83fe4d --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/Interface/IBindingParser{T}.cs @@ -0,0 +1,12 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers.Interface +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public interface IBindingParser : IBinding + { + T LoadBinding(PropertyNode node); + } +} diff --git a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj index d1e8bd74..6fd9d2e7 100644 --- a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj +++ b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj @@ -44,4 +44,8 @@ + + + + diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs index efb046dd..316a927a 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/FixedFieldMap.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System; using System.Collections.Generic; - internal class FixedFieldMap : Dictionary> + public class FixedFieldMap : Dictionary> { } -} \ No newline at end of file +} diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs index 41da8d03..1b944de6 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; - internal class MapNode : ParseNode, IEnumerable + public class MapNode : ParseNode, IEnumerable { private readonly YamlMappingNode node; private readonly List nodes; @@ -89,54 +89,6 @@ public override Dictionary CreateMap(Func map) return nodes.ToDictionary(k => k.key, v => v.value); } - public override Dictionary CreateBindingMapWithReference( - ReferenceType referenceType, - Func map) - { - var yamlMap = this.node; - if (yamlMap == null) - { - throw new AsyncApiReaderException($"Expected map while parsing {typeof(T).Name}", this.Context); - } - - var nodes = yamlMap.Select( - n => - { - var key = n.Key.GetScalarValue(); - (string key, T value) entry; - try - { - this.Context.StartObject(key); - entry = ( - key: key, - value: map(new PropertyNode(this.Context, key, n.Value)) - ); - - if (entry.value == null) - { - return default; - } - - if (entry.value.Reference == null) - { - entry.value.Reference = new AsyncApiReference() - { - Type = referenceType, - Id = entry.key, - }; - } - } - finally - { - this.Context.EndObject(); - } - - return entry; - } - ); - return nodes.Where(n => n != default).ToDictionary(k => k.key, v => v.value); - } - public override Dictionary CreateMapWithReference( ReferenceType referenceType, Func map) diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs index aa106cd2..94fc4268 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using LEGO.AsyncAPI.Readers.Exceptions; using YamlDotNet.RepresentationModel; - internal abstract class ParseNode + public abstract class ParseNode { protected ParseNode(ParsingContext parsingContext) { diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/PatternFieldMap.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/PatternFieldMap.cs index 0c1112ad..040a79e7 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/PatternFieldMap.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/PatternFieldMap.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System; using System.Collections.Generic; - internal class PatternFieldMap : Dictionary, Action> + public class PatternFieldMap : Dictionary, Action> { } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs index 99e1a33e..221f0d9a 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs @@ -11,7 +11,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using LEGO.AsyncAPI.Readers.Exceptions; using YamlDotNet.RepresentationModel; - internal class PropertyNode : ParseNode + public class PropertyNode : ParseNode { public PropertyNode(ParsingContext context, string name, YamlNode node) : base( @@ -84,10 +84,5 @@ public void ParseField( } } } - - public override IAsyncApiAny CreateAny() - { - throw new NotImplementedException(); - } } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs index 63e0238e..385cfca0 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using YamlDotNet.Core; using YamlDotNet.RepresentationModel; - internal class ValueNode : ParseNode + public class ValueNode : ParseNode { private readonly YamlScalarNode node; diff --git a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs index 63e8d1a6..58b0b04b 100644 --- a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs +++ b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs @@ -25,6 +25,14 @@ internal Dictionary> ExtensionPar = new (); + internal Dictionary> ServerBindingParsers { get; set; } = new(); + + internal Dictionary> ChannelBindingParsers { get; set; } + + internal Dictionary> OperationBindingParsers { get; set; } = new(); + + internal Dictionary> MessageBindingParsers { get; set; } = new(); + internal RootNode RootNode { get; set; } internal List Tags { get; private set; } = new (); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs deleted file mode 100644 index 7c997dc0..00000000 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiBindingDeserializer.cs +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using LEGO.AsyncAPI.Extensions; - using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Readers.ParseNodes; - using System; - - internal static partial class AsyncApiV2Deserializer - { - private static Type messageBindingType = typeof(IMessageBinding); - private static Type operationBindingType = typeof(IOperationBinding); - private static Type channelBindingType = typeof(IChannelBinding); - private static Type serverBindingType = typeof(IServerBinding); - - private static PatternFieldMap BindingPatternExtensionFields() - where T : IBinding, new() - { - return new() - { - { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, - }; - } - - internal static T LoadBinding(string nodeName, ParseNode node, FixedFieldMap fieldMap) - where T : IBinding, new() - { - var mapNode = node.CheckMapNode(nodeName); - var pointer = mapNode.GetReferencePointer(); - if (pointer != null) - { - ReferenceType referenceType = ReferenceType.None; - var bindingType = typeof(T); - - if (bindingType.IsAssignableTo(messageBindingType)) - { - referenceType = ReferenceType.MessageBinding; - } - - if (bindingType.IsAssignableTo(operationBindingType)) - { - referenceType = ReferenceType.OperationBinding; - } - - if (bindingType.IsAssignableTo(channelBindingType)) - { - referenceType = ReferenceType.ChannelBinding; - } - - if (bindingType.IsAssignableTo(serverBindingType)) - { - referenceType = ReferenceType.ServerBinding; - } - - if (referenceType == ReferenceType.None) - { - throw new ArgumentException($"ReferenceType not found {typeof(T).Name}"); - } - - return mapNode.GetReferencedObject(referenceType, pointer); - } - - var binding = new T(); - - ParseMap(mapNode, binding, fieldMap, BindingPatternExtensionFields()); - - return binding; - } - } -} diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs index c6e56700..1aab9e85 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs @@ -4,19 +4,21 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { internal static AsyncApiBindings LoadChannelBindings(ParseNode node) { - var mapNode = node.CheckMapNode("channelBinding"); + var mapNode = node.CheckMapNode("channelBindings"); + var pointer = mapNode.GetReferencePointer(); + if (pointer != null) + { + return mapNode.GetReferencedObject>(ReferenceType.ChannelBindings, pointer); + } var channelBindings = new AsyncApiBindings(); - foreach (var property in mapNode) { var channelBinding = LoadChannelBinding(property); @@ -35,21 +37,23 @@ internal static AsyncApiBindings LoadChannelBindings(ParseNode return channelBindings; } - internal static IChannelBinding LoadChannelBinding(ParseNode node) + private static IChannelBinding LoadChannelBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try + { + if (node.Context.ChannelBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } + } + catch (AsyncApiException ex) { - case BindingType.Kafka: - return LoadBinding("ChannelBinding", property.Value, kafkaChannelBindingFixedFields); - case BindingType.Pulsar: - return LoadBinding("ChannelBinding", property.Value, pulsarChannelBindingFixedFields); - case BindingType.Websockets: - return LoadBinding("ChannelBinding", property.Value, webSocketsChannelBindingFixedFields); - default: - throw new AsyncApiException($"ChannelBinding {property.Name} is not supported"); + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs index fa475105..3b63db28 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs @@ -10,7 +10,7 @@ internal static partial class AsyncApiV2Deserializer { private static FixedFieldMap componentsFixedFields = new() { - { "schemas", (a, n) => a.Schemas = n.CreateMapWithReference(ReferenceType.Schema, LoadSchema) }, + { "schemas", (a, n) => a.Schemas = n.CreateMapWithReference(ReferenceType.Schema, JsonSchemaDeserializer.LoadSchema) }, { "servers", (a, n) => a.Servers = n.CreateMapWithReference(ReferenceType.Server, LoadServer) }, { "channels", (a, n) => a.Channels = n.CreateMapWithReference(ReferenceType.Channel, LoadChannel) }, { "messages", (a, n) => a.Messages = n.CreateMapWithReference(ReferenceType.Message, LoadMessage) }, @@ -19,10 +19,10 @@ internal static partial class AsyncApiV2Deserializer { "correlationIds", (a, n) => a.CorrelationIds = n.CreateMapWithReference(ReferenceType.CorrelationId, LoadCorrelationId) }, { "operationTraits", (a, n) => a.OperationTraits = n.CreateMapWithReference(ReferenceType.OperationTrait, LoadOperationTrait) }, { "messageTraits", (a, n) => a.MessageTraits = n.CreateMapWithReference(ReferenceType.MessageTrait, LoadMessageTrait) }, - { "serverBindings", (a, n) => a.ServerBindings = n.CreateMapWithReference(ReferenceType.ServerBinding, LoadServerBinding) }, - { "channelBindings", (a, n) => a.ChannelBindings = n.CreateMapWithReference(ReferenceType.ChannelBinding, LoadChannelBinding) }, - { "operationBindings", (a, n) => a.OperationBindings = n.CreateBindingMapWithReference(ReferenceType.OperationBinding, LoadOperationBinding) }, - { "messageBindings", (a, n) => a.MessageBindings = n.CreateMapWithReference(ReferenceType.MessageBinding, LoadMessageBinding) }, + { "serverBindings", (a, n) => a.ServerBindings = n.CreateMapWithReference(ReferenceType.ServerBindings, LoadServerBindings) }, + { "channelBindings", (a, n) => a.ChannelBindings = n.CreateMapWithReference(ReferenceType.ChannelBindings, LoadChannelBindings) }, + { "operationBindings", (a, n) => a.OperationBindings = n.CreateMapWithReference(ReferenceType.OperationBindings, LoadOperationBindings) }, + { "messageBindings", (a, n) => a.MessageBindings = n.CreateMapWithReference(ReferenceType.MessageBindings, LoadMessageBindings) }, }; private static PatternFieldMap componentsPatternFields = diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs index 0328c4ee..5ebbc49b 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs @@ -12,7 +12,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static void ParseMap( + internal static void ParseMap( MapNode mapNode, T domainObject, FixedFieldMap fixedFieldMap, @@ -29,7 +29,7 @@ private static void ParseMap( } } - private static void ProcessAnyFields( + internal static void ProcessAnyFields( MapNode mapNode, T domainObject, AnyFieldMap anyFieldMap) @@ -58,7 +58,7 @@ private static void ProcessAnyFields( } } - private static void ProcessAnyListFields( + internal static void ProcessAnyListFields( MapNode mapNode, T domainObject, AnyListFieldMap anyListFieldMap) @@ -163,7 +163,7 @@ public static IAsyncApiAny LoadAny(ParseNode node) return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); } - private static IAsyncApiExtension LoadExtension(string name, ParseNode node) + public static IAsyncApiExtension LoadExtension(string name, ParseNode node) { try { diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs index c3198737..9a180e84 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs @@ -4,12 +4,8 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; - using LEGO.AsyncAPI.Models.Bindings.Http; - using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { @@ -40,16 +36,20 @@ internal static AsyncApiBindings LoadMessageBindings(ParseNode internal static IMessageBinding LoadMessageBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try { - case BindingType.Kafka: - return LoadBinding("MessageBinding", property.Value, kafkaMessageBindingFixedFields); - case BindingType.Http: - return LoadBinding("MessageBinding", property.Value, httpMessageBindingFixedFields); - default: - throw new AsyncApiException($"MessageBinding {property.Name} is not supported"); + if (node.Context.MessageBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } } + catch (AsyncApiException ex) + { + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); + } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs index 97e40ba7..63d0512c 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs @@ -2,12 +2,12 @@ namespace LEGO.AsyncAPI.Readers { + using System.Collections.Generic; + using System.Linq; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Extensions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.ParseNodes; - using System.Collections.Generic; - using System.Linq; /// /// Class containing logic to deserialize AsyncApi document into @@ -21,10 +21,10 @@ internal static partial class AsyncApiV2Deserializer "messageId", (a, n) => { a.MessageId = n.GetScalarValue(); } }, { - "headers", (a, n) => { a.Headers = LoadSchema(n); } + "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, { - "payload", (a, n) => { a.Payload = LoadSchema(n); } + "payload", (a, n) => { a.Payload = JsonSchemaDeserializer.LoadSchema(n); } }, { "correlationId", (a, n) => { a.CorrelationId = LoadCorrelationId(n); } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs index 7e142ee2..eca8af64 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs @@ -11,7 +11,7 @@ internal static partial class AsyncApiV2Deserializer private static FixedFieldMap messageTraitFixedFields = new() { { "messageId", (a, n) => { a.MessageId = n.GetScalarValue(); } }, - { "headers", (a, n) => { a.Headers = LoadSchema(n); } }, + { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, { "correlationId", (a, n) => { a.CorrelationId = LoadCorrelationId(n); } }, { "schemaFormat", (a, n) => { a.SchemaFormat = n.GetScalarValue(); } }, { "contentType", (a, n) => { a.ContentType = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs index 03631600..4d4eb85f 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs @@ -4,16 +4,14 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { internal static AsyncApiBindings LoadOperationBindings(ParseNode node) { - var mapNode = node.CheckMapNode("operationBinding"); + var mapNode = node.CheckMapNode("operationBindings"); var operationBindings = new AsyncApiBindings(); @@ -38,16 +36,20 @@ internal static AsyncApiBindings LoadOperationBindings(ParseN internal static IOperationBinding LoadOperationBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try { - case BindingType.Kafka: - return LoadBinding("OperationBinding", property.Value, kafkaOperationBindingFixedFields); - case BindingType.Http: - return LoadBinding("OperationBinding", property.Value, httpOperationBindingFixedFields); - default: - throw new AsyncApiException($"OperationBinding {property.Name} is not supported"); + if (node.Context.OperationBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } } + catch (AsyncApiException ex) + { + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); + } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs index 3841fbf0..bff810f1 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs @@ -11,7 +11,7 @@ internal static partial class AsyncApiV2Deserializer private static FixedFieldMap parameterFixedFields = new() { { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, - { "schema", (a, n) => { a.Schema = LoadSchema(n); } }, + { "schema", (a, n) => { a.Schema = JsonSchemaDeserializer.LoadSchema(n); } }, { "location", (a, n) => { a.Location = n.GetScalarValue(); } }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index e63d3311..88dc8efb 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Readers using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; - internal static partial class AsyncApiV2Deserializer + public class JsonSchemaDeserializer { private static readonly FixedFieldMap schemaFixedFields = new () { @@ -144,7 +144,7 @@ internal static partial class AsyncApiV2Deserializer "discriminator", (a, n) => { a.Discriminator = n.GetScalarValue(); } }, { - "externalDocs", (a, n) => { a.ExternalDocs = LoadExternalDocs(n); } + "externalDocs", (a, n) => { a.ExternalDocs = AsyncApiV2Deserializer.LoadExternalDocs(n); } }, { "deprecated", (a, n) => { a.Deprecated = bool.Parse(n.GetScalarValue()); } @@ -154,7 +154,7 @@ internal static partial class AsyncApiV2Deserializer private static readonly PatternFieldMap schemaPatternFields = new() { - { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) }, + { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) }, }; private static readonly AnyFieldMap schemaAnyFields = new() @@ -201,8 +201,8 @@ public static AsyncApiSchema LoadSchema(ParseNode node) propertyNode.ParseField(schema, schemaFixedFields, schemaPatternFields); } - ProcessAnyFields(mapNode, schema, schemaAnyFields); - ProcessAnyListFields(mapNode, schema, schemaAnyListFields); + AsyncApiV2Deserializer.ProcessAnyFields(mapNode, schema, schemaAnyFields); + AsyncApiV2Deserializer.ProcessAnyListFields(mapNode, schema, schemaAnyListFields); return schema; } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs index 632d85ab..44c821d3 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs @@ -4,19 +4,21 @@ namespace LEGO.AsyncAPI.Readers { using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; - using LEGO.AsyncAPI.Writers; internal static partial class AsyncApiV2Deserializer { internal static AsyncApiBindings LoadServerBindings(ParseNode node) { - var mapNode = node.CheckMapNode("serverBinding"); + var mapNode = node.CheckMapNode("serverBindings"); + var pointer = mapNode.GetReferencePointer(); + if (pointer != null) + { + return mapNode.GetReferencedObject>(ReferenceType.ServerBindings, pointer); + } var serverBindings = new AsyncApiBindings(); - foreach (var property in mapNode) { var serverBinding = LoadServerBinding(property); @@ -38,16 +40,20 @@ internal static AsyncApiBindings LoadServerBindings(ParseNode no internal static IServerBinding LoadServerBinding(ParseNode node) { var property = node as PropertyNode; - var bindingType = property.Name.GetEnumFromDisplayName(); - switch (bindingType) + try + { + if (node.Context.ServerBindingParsers.TryGetValue(property.Name, out var parser)) + { + return parser.LoadBinding(property); + } + } + catch (AsyncApiException ex) { - case BindingType.Kafka: - return LoadBinding("ServerBinding", property.Value, kafkaServerBindingFixedFields); - case BindingType.Pulsar: - return LoadBinding("ServerBinding", property.Value, pulsarServerBindingFixedFields); - default: - throw new AsyncApiException($"ServerBinding {property.Name} is not supported"); + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); } + + return null; } } } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs index b4f34131..c3878072 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs @@ -35,7 +35,7 @@ public AsyncApiV2VersionService(AsyncApiDiagnostic diagnostic) [typeof(AsyncApiOAuthFlows)] = AsyncApiV2Deserializer.LoadOAuthFlows, [typeof(AsyncApiOperation)] = AsyncApiV2Deserializer.LoadOperation, [typeof(AsyncApiParameter)] = AsyncApiV2Deserializer.LoadParameter, - [typeof(AsyncApiSchema)] = AsyncApiV2Deserializer.LoadSchema, + [typeof(AsyncApiSchema)] = JsonSchemaDeserializer.LoadSchema, [typeof(AsyncApiSecurityRequirement)] = AsyncApiV2Deserializer.LoadSecurityRequirement, [typeof(AsyncApiSecurityScheme)] = AsyncApiV2Deserializer.LoadSecurityScheme, [typeof(AsyncApiServer)] = AsyncApiV2Deserializer.LoadServer, diff --git a/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs b/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs new file mode 100644 index 00000000..64e5508e --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs @@ -0,0 +1,43 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers +{ + using LEGO.AsyncAPI.Exceptions; + using LEGO.AsyncAPI.Extensions; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public static class ExtensionHelpers + { + public static PatternFieldMap GetExtensionsFieldMap() where T : IAsyncApiExtensible + { + return new () + { + { + s => s.StartsWith("x-"), + (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) + }, + }; + } + + public static IAsyncApiExtension LoadExtension(string name, ParseNode node) + { + try + { + if (node.Context.ExtensionParsers.TryGetValue(name, out var parser)) + { + return parser( + AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny())); + } + } + catch (AsyncApiException ex) + { + ex.Pointer = node.Context.GetLocation(); + node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); + } + + return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/YamlHelper.cs b/src/LEGO.AsyncAPI.Readers/YamlHelper.cs index f29522f0..0f159c73 100644 --- a/src/LEGO.AsyncAPI.Readers/YamlHelper.cs +++ b/src/LEGO.AsyncAPI.Readers/YamlHelper.cs @@ -6,7 +6,6 @@ namespace LEGO.AsyncAPI.Readers using System.Linq; using LEGO.AsyncAPI.Exceptions; using YamlDotNet.RepresentationModel; - internal static class YamlHelper { public static string GetScalarValue(this YamlNode node) diff --git a/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs b/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs index 150e12a0..b9403b07 100644 --- a/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs @@ -16,12 +16,5 @@ public sealed class MethodExpression : RuntimeExpression /// Gets the expression string. /// public override string Expression { get; } = Method; - - /// - /// Private constructor. - /// - public MethodExpression() - { - } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs b/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs index 81b1eabd..426020ce 100644 --- a/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs +++ b/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs @@ -7,12 +7,12 @@ namespace LEGO.AsyncAPI.Extensions using LEGO.AsyncAPI.Models.Interfaces; /// - /// Extension methods to verify validatity and add an extension to Extensions property. + /// Extension methods to verify validity and add an extension to Extensions property. /// public static class AsyncApiExtensibleExtensions { /// - /// Add extension into the Extensions + /// Add extension into the Extensions. /// /// . /// The extensible AsyncApi element. diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs new file mode 100644 index 00000000..4034b4e9 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs @@ -0,0 +1,41 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public abstract class AsyncApiBinding : IBinding + { + public abstract string BindingKey { get; } + + public bool UnresolvedReference { get; set; } + + public AsyncApiReference Reference { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public string BindingVersion { get; set; } + + public void SerializeV2(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) + { + this.Reference.SerializeV2(writer); + return; + } + + this.SerializeProperties(writer); + } + + public abstract void SerializeProperties(IAsyncApiWriter writer); + } +} diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs index b2784755..d039cddf 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs @@ -4,11 +4,10 @@ namespace LEGO.AsyncAPI.Models { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Bindings; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - public class AsyncApiBindings : Dictionary, IAsyncApiSerializable, IAsyncApiReferenceable + public class AsyncApiBindings : Dictionary, IAsyncApiReferenceable where TBinding : IBinding { public bool UnresolvedReference { get; set; } @@ -17,7 +16,7 @@ public class AsyncApiBindings : Dictionary, IAs public void Add(TBinding binding) { - this[binding.Type] = binding; + this[binding.BindingKey] = binding; } public void SerializeV2(IAsyncApiWriter writer) @@ -51,7 +50,7 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) var bindingType = binding.Key; var bindingValue = binding.Value; - writer.WritePropertyName(bindingType.GetDisplayName()); + writer.WritePropertyName(bindingType); bindingValue.SerializeV2(writer); } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs b/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs index 9e8e6e02..6c8a2a4c 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs @@ -69,22 +69,22 @@ public class AsyncApiComponents : IAsyncApiExtensible, IAsyncApiSerializable /// /// An object to hold reusable Server Bindings Objects. /// - public IDictionary ServerBindings { get; set; } = new Dictionary(); + public IDictionary> ServerBindings { get; set; } = new Dictionary>(); /// /// An object to hold reusable Channel Bindings Objects. /// - public IDictionary ChannelBindings { get; set; } = new Dictionary(); + public IDictionary> ChannelBindings { get; set; } = new Dictionary>(); /// /// An object to hold reusable Operation Bindings Objects. /// - public IDictionary OperationBindings { get; set; } = new Dictionary(); + public IDictionary> OperationBindings { get; set; } = new Dictionary>(); /// /// An object to hold reusable Message Bindings Objects. /// - public IDictionary MessageBindings { get; set; } = new Dictionary(); + public IDictionary> MessageBindings { get; set; } = new Dictionary>(); public IDictionary Extensions { get; set; } = new Dictionary(); @@ -311,7 +311,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.ServerBinding && + component.Reference.Type == ReferenceType.ServerBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); @@ -329,7 +329,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.ChannelBinding && + component.Reference.Type == ReferenceType.ChannelBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); @@ -347,7 +347,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.OperationBinding && + component.Reference.Type == ReferenceType.OperationBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); @@ -365,7 +365,7 @@ public void SerializeV2(IAsyncApiWriter writer) (w, key, component) => { if (component.Reference != null && - component.Reference.Type == ReferenceType.MessageBinding && + component.Reference.Type == ReferenceType.MessageBindings && component.Reference.Id == key) { component.SerializeV2WithoutReference(w); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs index 1558dcc2..b28168e2 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs @@ -7,7 +7,7 @@ namespace LEGO.AsyncAPI.Models using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using Services; + using LEGO.AsyncAPI.Services; /// /// This is the root document object for the API specification. It combines resource listing and API declaration together into one document. @@ -189,13 +189,13 @@ public IAsyncApiReferenceable ResolveReference(AsyncApiReference reference) return this.Components.OperationTraits[reference.Id]; case ReferenceType.MessageTrait: return this.Components.MessageTraits[reference.Id]; - case ReferenceType.ServerBinding: + case ReferenceType.ServerBindings: return this.Components.ServerBindings[reference.Id]; - case ReferenceType.ChannelBinding: + case ReferenceType.ChannelBindings: return this.Components.ChannelBindings[reference.Id]; - case ReferenceType.OperationBinding: + case ReferenceType.OperationBindings: return this.Components.OperationBindings[reference.Id]; - case ReferenceType.MessageBinding: + case ReferenceType.MessageBindings: return this.Components.MessageBindings[reference.Id]; default: throw new AsyncApiException("Invalid reference type."); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs index 45e61cbf..c9a5c309 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiWriterExtensions.cs @@ -4,7 +4,7 @@ namespace LEGO.AsyncAPI.Models { using LEGO.AsyncAPI.Writers; - internal static class AsyncApiWriterExtensions + public static class AsyncApiWriterExtensions { internal static AsyncApiWriterSettings GetSettings(this IAsyncApiWriter asyncApiWriter) { diff --git a/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs b/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs deleted file mode 100644 index b88d3d5d..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/BindingType.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings -{ - using LEGO.AsyncAPI.Attributes; - - public enum BindingType - { - [Display("kafka")] - Kafka, - - [Display("http")] - Http, - - [Display("websockets")] - Websockets, - - [Display("pulsar")] - Pulsar, - } -} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs deleted file mode 100644 index eeb5d05f..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/Http/HttpMessageBinding.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings.Http -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// Binding class for http messaging channels. - /// - public class HttpMessageBinding : IMessageBinding - { - - /// - /// A Schema object containing the definitions for HTTP-specific headers. This schema MUST be of type object and have a properties key. - /// - public AsyncApiSchema Headers { get; set; } - - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - - /// - /// Indicates if object is populated with data or is just a reference to the data - /// - public bool UnresolvedReference { get; set; } - - /// - /// Reference object. - /// - public AsyncApiReference Reference { get; set; } - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - - writer.WriteOptionalObject(AsyncApiConstants.Headers, this.Headers, (w, h) => h.SerializeV2(w)); - writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - - writer.WriteEndObject(); - } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - public BindingType Type => BindingType.Http; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs deleted file mode 100644 index 85ec18d6..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/Kafka/KafkaOperationBinding.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings.Kafka -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// Binding class for Kafka operations. - /// - public class KafkaOperationBinding : IOperationBinding - { - /// - /// Id of the consumer group. - /// - public AsyncApiSchema GroupId { get; set; } - - /// - /// Id of the consumer inside a consumer group. - /// - public AsyncApiSchema ClientId { get; set; } - - /// - /// The version of this binding. If omitted, "latest" MUST be assumed. - /// - public string BindingVersion { get; set; } - - /// - public IDictionary Extensions { get; set; } = new Dictionary(); - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public BindingType Type => BindingType.Kafka; - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - writer.WriteOptionalObject(AsyncApiConstants.GroupId, this.GroupId, (w, h) => h.SerializeV2(w)); - writer.WriteOptionalObject(AsyncApiConstants.ClientId, this.ClientId, (w, h) => h.SerializeV2(w)); - writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - - writer.WriteEndObject(); - } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs b/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs deleted file mode 100644 index 2fc0e15c..00000000 --- a/src/LEGO.AsyncAPI/Models/Bindings/Pulsar/PulsarServerBinding.cs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Bindings.Pulsar -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// Binding class for Pulsar server settings. - /// - public class PulsarServerBinding : IServerBinding - { - /// - /// The pulsar tenant. If omitted, "public" must be assumed. - /// - public string Tenant { get; set; } - - /// - /// The version of this binding. - public string BindingVersion { get; set; } - - public BindingType Type => BindingType.Pulsar; - - public bool UnresolvedReference { get; set; } - - public AsyncApiReference Reference { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); - - /// - /// Serialize to AsyncAPI V2 document without using reference. - /// - public void SerializeV2WithoutReference(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - - writer.WriteOptionalProperty(AsyncApiConstants.Tenant, this.Tenant); - writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); - - writer.WriteEndObject(); - } - - public void SerializeV2(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference)) - { - this.Reference.SerializeV2(writer); - return; - } - - this.SerializeV2WithoutReference(writer); - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs b/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs index c427db45..44e4573d 100644 --- a/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs +++ b/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs @@ -1,15 +1,13 @@ // Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Models.Interfaces { - using LEGO.AsyncAPI.Models.Bindings; - /// /// Describes a protocol-specific binding. /// - public interface IBinding : IAsyncApiReferenceable, IAsyncApiExtensible + public interface IBinding : IAsyncApiSerializable, IAsyncApiExtensible { - public BindingType Type { get; } + public string BindingKey { get; } - public string BindingVersion { get; set; } + public string BindingVersion { get; set; } } } diff --git a/src/LEGO.AsyncAPI/Models/ReferenceType.cs b/src/LEGO.AsyncAPI/Models/ReferenceType.cs index 8195e197..8903dd73 100644 --- a/src/LEGO.AsyncAPI/Models/ReferenceType.cs +++ b/src/LEGO.AsyncAPI/Models/ReferenceType.cs @@ -56,22 +56,22 @@ public enum ReferenceType /// /// ServerBindings item. /// - [Display("serverBindings")] ServerBinding, + [Display("serverBindings")] ServerBindings, /// /// ChannelBindings item. /// - [Display("channelBindings")] ChannelBinding, + [Display("channelBindings")] ChannelBindings, /// /// OperationBindings item. /// - [Display("operationBindings")] OperationBinding, + [Display("operationBindings")] OperationBindings, /// /// MessageBindings item. /// - [Display("messageBindings")] MessageBinding, + [Display("messageBindings")] MessageBindings, /// /// Examples item. @@ -82,6 +82,10 @@ public enum ReferenceType /// Headers item. /// [Display("headers")] Header, - ServerVariable, + + /// + /// The server variable + /// + [Display("serverVariable")] ServerVariable, } -} \ No newline at end of file +} diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs index 85f2d4da..c1e45d6a 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs @@ -5,9 +5,9 @@ namespace LEGO.AsyncAPI.Services using System; using System.Collections.Generic; using System.Linq; - using Exceptions; - using Models; - using Models.Interfaces; + using LEGO.AsyncAPI.Exceptions; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; /// /// This class is used to walk an AsyncApiDocument and convert unresolved references to references to populated objects @@ -64,8 +64,7 @@ public override void Visit(AsyncApiDocument doc) public override void Visit(AsyncApiChannel channel) { this.ResolveMap(channel.Parameters); - var bindingDictionary = channel.Bindings.Select(binding => binding.Value).ToDictionary(x => x.Type.GetDisplayName()); - this.ResolveMap(bindingDictionary); + this.ResolveObject(channel.Bindings, r => channel.Bindings = r); } public override void Visit(AsyncApiMessageTrait trait) @@ -81,8 +80,7 @@ public override void Visit(AsyncApiOperation operation) { this.ResolveList(operation.Message); this.ResolveList(operation.Traits); - var bindingDictionary = operation.Bindings.Select(binding => binding.Value).ToDictionary(x => x.Type.GetDisplayName()); - this.ResolveMap(bindingDictionary); + this.ResolveObject(operation.Bindings, r => operation.Bindings = r); } public override void Visit(AsyncApiMessage message) @@ -91,19 +89,12 @@ public override void Visit(AsyncApiMessage message) this.ResolveObject(message.Payload, r => message.Payload = r); this.ResolveList(message.Traits); this.ResolveObject(message.CorrelationId, r => message.CorrelationId = r); - var bindingDictionary = message.Bindings.Select(binding => binding.Value).ToDictionary(x => x.Type.GetDisplayName()); - this.ResolveMap(bindingDictionary); + this.ResolveObject(message.Bindings, r => message.Bindings = r); } - /// - /// Resolve all references to bindings. - /// - public override void Visit(AsyncApiBindings bindings) + public override void Visit(AsyncApiServer server) { - foreach (var binding in bindings.Values.ToList()) - { - this.ResolveObject(binding, resolvedBinding => bindings[binding.Type] = resolvedBinding); - } + this.ResolveObject(server.Bindings, r => server.Bindings = r); } /// diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs index 9519f3ba..bac5482f 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs @@ -15,7 +15,7 @@ public abstract class AsyncApiVisitorBase private readonly Stack path = new Stack(); /// - /// Properties available to identify context of where an object is within AsyncApi Document + /// Properties available to identify context of where an object is within AsyncApi Document. /// public CurrentKeys CurrentKeys { get; } = new CurrentKeys(); @@ -171,14 +171,6 @@ public virtual void Visit(AsyncApiOAuthFlow asyncApiOAuthFlow) { } - /// - /// Visits - /// - public virtual void Visit(AsyncApiBindings bindings) - where TBinding : class, IBinding - { - } - /// /// Visits /// diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs index c3ea6af8..1e0f7330 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs @@ -73,6 +73,17 @@ internal void Walk(AsyncApiComponents components) } }); + this.Walk(AsyncApiConstants.ServerBindings, () => + { + if (components.ServerBindings != null) + { + foreach (var item in components.ServerBindings) + { + this.Walk(item.Key, () => this.Walk(item.Value, isComponent: true)); + } + } + }); + this.Walk(AsyncApiConstants.Parameters, () => { if (components.Parameters != null) @@ -532,48 +543,44 @@ internal void Walk(AsyncApiMessageTrait trait, bool isComponent = false) this.Walk(trait as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings serverBindings) + internal void Walk(AsyncApiBindings serverBindings, bool isComponent = false) { - if (serverBindings is null) + if (serverBindings == null || this.ProcessAsReference(serverBindings, isComponent)) { return; } this.visitor.Visit(serverBindings); - this.Walk(serverBindings as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings channelBindings) + internal void Walk(AsyncApiBindings channelBindings, bool isComponent = false) { - if (channelBindings is null) + if (channelBindings == null || this.ProcessAsReference(channelBindings, isComponent)) { return; } this.visitor.Visit(channelBindings); - this.Walk(channelBindings as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings operationBindings) + internal void Walk(AsyncApiBindings operationBindings, bool isComponent = false) { - if (operationBindings is null) + if (operationBindings == null || this.ProcessAsReference(operationBindings, isComponent)) { return; } this.visitor.Visit(operationBindings); - this.Walk(operationBindings as IAsyncApiExtensible); } - internal void Walk(AsyncApiBindings messageBindings) + internal void Walk(AsyncApiBindings messageBindings, bool isComponent = false) { - if (messageBindings is null) + if (messageBindings == null || this.ProcessAsReference(messageBindings, isComponent)) { return; } this.visitor.Visit(messageBindings); - this.Walk(messageBindings as IAsyncApiExtensible); } internal void Walk(IList examples) @@ -703,7 +710,6 @@ internal void Walk(AsyncApiServer server, bool isComponent = false) this.visitor.Visit(server); this.Walk(AsyncApiConstants.Variables, () => this.Walk(server.Variables)); this.Walk(AsyncApiConstants.Security, () => this.Walk(server.Security)); - this.Walk(AsyncApiConstants.Bindings, () => this.Walk(server.Bindings)); this.visitor.Visit(server as IAsyncApiExtensible); } diff --git a/src/LEGO.AsyncAPI/Services/CurrentKeys.cs b/src/LEGO.AsyncAPI/Services/CurrentKeys.cs index f070a15c..3544eb21 100644 --- a/src/LEGO.AsyncAPI/Services/CurrentKeys.cs +++ b/src/LEGO.AsyncAPI/Services/CurrentKeys.cs @@ -4,6 +4,8 @@ namespace LEGO.AsyncAPI.Services { public class CurrentKeys { + public string ServerBindings { get; internal set; } + public string Channel { get; internal set; } public string Extension { get; internal set; } diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs index 019c2ef4..79576005 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs @@ -3,7 +3,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Validations; [AsyncApiRule] diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs index 5422eea8..3e6a21c8 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Validations; - using System.Linq; [AsyncApiRule] public static class AsyncApiCorrelationIdRules diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs index 3cba8059..27076369 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { - using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using LEGO.AsyncAPI.Models; diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs index 8ce2a099..710b30e5 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { - using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Validations; diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs index ce735c93..69b8c5ee 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs @@ -3,7 +3,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Validations; [AsyncApiRule] diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs index e1649d4e..073066da 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs @@ -2,9 +2,9 @@ namespace LEGO.AsyncAPI.Validation.Rules { + using System.Linq; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Validations; - using System.Linq; [AsyncApiRule] public static class AsyncApiOAuthFlowRules diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs index bb421471..244686c5 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Validation.Rules { using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Validations; - using System.Linq; [AsyncApiRule] public static class AsyncApiTagRules diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs index 9d89bba3..17229818 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs @@ -2,8 +2,8 @@ namespace LEGO.AsyncAPI.Writers { - using LEGO.AsyncAPI.Exceptions; using System; + using LEGO.AsyncAPI.Exceptions; public class AsyncApiWriterException : AsyncApiException { diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs index 5953e9d1..ec9c96b6 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs @@ -138,6 +138,12 @@ public static void WriteOptionalObject( { if (value != null) { + if (value is IAsyncApiReferenceable refer && refer.Reference != null) + { + writer.WriteRequiredObject(name, value, action); + return; + } + var values = value as IEnumerable; if (values != null && !values.GetEnumerator().MoveNext()) { diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs index b6edd619..b3f061e3 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs @@ -40,7 +40,7 @@ public ReferenceInlineSetting ReferenceInline /// public bool InlineReferences { get; set; } = false; - internal bool ShouldInlineReference(AsyncApiReference reference) + public bool ShouldInlineReference(AsyncApiReference reference) { return this.InlineReferences; } diff --git a/src/LEGO.AsyncAPI/Writers/StringExtensions.cs b/src/LEGO.AsyncAPI/Writers/StringExtensions.cs index 15697bd3..e4efe5f7 100644 --- a/src/LEGO.AsyncAPI/Writers/StringExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/StringExtensions.cs @@ -4,7 +4,7 @@ namespace LEGO.AsyncAPI.Writers { using System; using System.Reflection; - using Attributes; + using LEGO.AsyncAPI.Attributes; public static class StringExtensions { diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs index 49b32d80..f800a1f7 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs @@ -1,8 +1,10 @@ -namespace LEGO.AsyncAPI.Tests +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests { + using System; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; - using System; internal class AsyncApiDocumentBuilder { @@ -154,47 +156,47 @@ public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiMessageTrait me return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IServerBinding serverBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings serverBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.ServerBindings.Add(key, serverBinding); + this.document.Components.ServerBindings.Add(key, serverBindings); return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IChannelBinding channelBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings channelBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.ChannelBindings.Add(key, channelBinding); + this.document.Components.ChannelBindings.Add(key, channelBindings); return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IOperationBinding operationBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings operationBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.OperationBindings.Add(key, operationBinding); + this.document.Components.OperationBindings.Add(key, operationBindings); return this; } - public AsyncApiDocumentBuilder WithComponent(string key, IMessageBinding messageBinding) + public AsyncApiDocumentBuilder WithComponent(string key, AsyncApiBindings messageBindings) { if (this.document.Components == null) { this.document.Components = new AsyncApiComponents(); } - this.document.Components.MessageBindings.Add(key, messageBinding); + this.document.Components.MessageBindings.Add(key, messageBindings); return this; } diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 75db39a9..3c90d791 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -1,15 +1,18 @@ -namespace LEGO.AsyncAPI.Tests +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests { using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; + using LEGO.AsyncAPI.Bindings.Pulsar; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Http; + using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Any; - using LEGO.AsyncAPI.Models.Bindings; - using LEGO.AsyncAPI.Models.Bindings.Http; - using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Writers; @@ -17,86 +20,6 @@ public class AsyncApiDocumentV2Tests { - - [Test] - public void test() - { - var input = @"asyncapi: 2.0.0 -info: - title: Sites - version: 1.0.0 - description: Responsible for emitting the site on/off - x-application-id: APP-02042 - x-audience: component-internal -channels: - site-events: - subscribe: - message: - payload: - properties: - data: - '$ref': '#/components/schemas/DtosSiteUpdatedEvent' - description: The actual payload of the event - datacontenttype: - description: Always application/json - type: string - example: application/json - id: - description: The unique ID of the event - type: string - example: 3489d4b1e21badf3665dae24c6526169 - source: - description: The source of the event - type: string - example: LEGO.OmnichannelFulfilment.DeliveryOrchestration/Sites - specversion: - description: The CloudEvents schema version used - type: string - example: 1.0 - time: - description: The time the event was published - format: date-time - type: string - example: 2022-10-13T11:57:36.268054757Z - type: - description: The type of the event - type: string - example: siteUpdatedV1 - traceparent: - description: The value to propagate context information that enables distributed tracing scenarios - type: string - example: 3489d4b1e21badf3665dae24c6526169 - type: object - summary: Subscriber message - description: All data used for turning on or off a site request - x-classification: green - x-datalakesubscription: false - x-eventarchetype: objectchanged - x-eventdurability: persistent -components: - schemas: - DtosSiteUpdatedEvent: - properties: - enabled: - description: The Enabled shows the current status of the site - type: boolean - examples: - - false - siteId: - description: The SiteCode related to the site that is being turn on or off - type: string - examples: - - 489 - reason: - description: The reason the site is being turned on or off - type: string - examples: - - Workers striking require us to temporary close the site. - type: object -"; - var serialized = new AsyncApiStringReader().Read(input, out var diag); - - } [Test] public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() { @@ -748,7 +671,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Bindings = new AsyncApiBindings() { { - BindingType.Kafka, new KafkaOperationBinding() + "kafka", new KafkaOperationBinding() { ClientId = new AsyncApiSchema() { @@ -770,7 +693,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -1201,937 +1124,95 @@ public void SerializeV2_WithFullSpec_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] - public void tesT() + public void Serialize_WithBindingReferences_SerializesDeserializes() { - var spec = @"asyncapi: '2.6.0' -defaultContentType: 'application/json' -info: - title: '{TITLE}' - version: '{VERSION}' - x-audience: company-internal - x-application-id: APP-01575 - x-eventdeduplication: false - contact: - name: Team Deadlock - email: Deadlock@o365.corp.LEGO.com - url: https://legogroup.atlassian.net/wiki/spaces/TD/pages/37143022928/Consent+Service - description: | - Emits events related to consent changes for both LEGO Account users and anonymous users. - This includes both parental consents and cookie consents. -channels: - userconsents.objectchanged: - x-eventarchetype: objectchanged - x-eventdurability: persistent - x-classification: yellow - description: | - A topic for events regarding changes to user consents. The event archetype is set to 'objectchanged' which will enable tombstoning and compaction. - subscribe: - operationId: UserConsentsObjectChanged - message: - oneOf: - - $ref: '#/components/messages/UserConsentsObjectCreated' - - $ref: '#/components/messages/UserConsentsObjectChanged' - - $ref: '#/components/messages/UserConsentsObjectDeleted' - userconsents.fieldchanged: - x-eventarchetype: fieldchanged - x-eventdurability: persistent - x-classification: yellow - description: | - A topic for deleted user consents events. The event archetype is set to 'fieldchanged' in order to enforce a 28 days retention policy. - subscribe: - operationId: UserConsentsFieldChanged - message: - oneOf: - - $ref: '#/components/messages/UserConsentFieldCreated' - - $ref: '#/components/messages/UserConsentFieldChanged' - - $ref: '#/components/messages/UserConsentFieldDeleted' - anonymousconsent.fieldchange: - x-eventarchetype: fieldchanged - x-eventdurability: persistent - x-classification: green - description: | - A topic for events regarding changes to anonymous consents. The event archetype is set to 'fieldchanged' in order to enforce a 28 days retention policy. - subscribe: - operationId: AnonymousConsentsFieldChanged - message: - oneOf: - - $ref: '#/components/messages/AnonymousConsentFieldChange' - experiences.events: - x-eventarchetype: objectchanged - x-eventdurability: persistent - x-classification: yellow - description: | - A topic for experience events. The event archetype is set to 'objectchanged' in order to store event forever. - subscribe: - operationId: ExperiencesChanged - message: - oneOf: - - $ref: '#/components/messages/ExperienceCreated' - - $ref: '#/components/messages/ExperienceDeleted' - - $ref: '#/components/messages/ExperienceUpdated' - - $ref: '#/components/messages/ExperienceClientAdded' - - $ref: '#/components/messages/ExperienceClientRemoved' - - $ref: '#/components/messages/ExperienceConsentOptionAdded' - - $ref: '#/components/messages/ExperienceConsentOptionRemoved' -components: - schemas: - EnvelopeBase: - type: object - properties: - type: - type: string - description: The type of event. - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - type: object - - EnvelopeOfUserConsentsObjectCreated: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentsObjectCreated] - data: - $ref: '#/components/schemas/UserConsentsObjectCreated' - - EnvelopeOfUserConsentsObjectChanged: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentsObjectChanged] - data: - $ref: '#/components/schemas/UserConsentsObjectChanged' - EnvelopeOfUserConsentsObjectDeleted: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentsObjectDeleted] - data: - $ref: '#/components/schemas/UserConsentsObjectDeleted' - UserConsentsObjectCreated: - type: object - required: - - changeType - - changeTime - - userId - - consents - properties: - changeType: - type: string - enum: ['created'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consents: - type: array - items: - $ref: '#/components/schemas/UserConsent' - UserConsentsObjectChanged: - type: object - required: - - changeType - - changeTime - - userId - - consents - properties: - changeType: - type: string - enum: ['updated'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consents: - type: array - items: - $ref: '#/components/schemas/UserConsent' - UserConsentsObjectDeleted: - type: object - required: - - changeType - - changeTime - - userId - properties: - changeType: - type: string - enum: ['deleted'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - UserConsent: - type: object - required: - - consentId - - consenterUserId - - consentState - - culture - properties: - consentId: - type: string - format: uri - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - consenterUserId: - type: string - format: guid - description: The ID of the user giving the consent. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentState: - type: string - description: The state of the consent for the given consent option - enum: ['granted','denied','undecided'] - culture: - type: string - minLength: 5 - maxLength: 5 - description: The culture where the consent was changed. - examples: - - da-DK - - en-US - - en-GB - submissionSource: - type: string - description: The method used by the user to submit the cookies - enum: ['prebannerAcceptAll', 'prebannerRejectAll', 'savePrefButton', 'cloned' ] - - EnvelopeOfUserConsentFieldCreatedEvent: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentFieldCreatedEvent] - data: - $ref: '#/components/schemas/UserConsentFieldCreatedEvent' - EnvelopeOfUserConsentFieldChangedEvent: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentFieldChangedEvent] - data: - $ref: '#/components/schemas/UserConsentFieldChangedEvent' - EnvelopeOfUserConsentFieldDeletedEvent: - allOf: - - $ref: '#/components/schemas/EnvelopeBase' - - type: object - properties: - type: - enum: [UserConsentFieldDeletedEvent] - data: - $ref: '#/components/schemas/UserConsentFieldDeletedEvent' - - UserConsentFieldCreatedEvent: - type: object - required: - - changeType - - changeTime - - userId - - consentId - - consenterUserId - - consentState - - culture - properties: - changeType: - type: string - enum: ['created','updated','deleted'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentId: - type: string - format: uri - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - consenterUserId: - type: string - format: guid - description: The ID of the user giving the consent. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentState: - type: string - description: The state of the consent for the given consent option - enum: ['granted','denied','undecided'] - culture: - type: string - minLength: 5 - maxLength: 5 - description: The culture where the consent was changed. - examples: - - da-DK - - en-US - - en-GB - submissionSource: - type: string - description: The method used by the user to submit the cookies - enum: ['prebannerAcceptAll', 'prebannerRejectAll', 'savePrefButton', 'cloned' ] - - UserConsentFieldChangedEvent: - type: object - required: - - changeType - - changeTime - - userId - - consentId - - consenterUserId - - consentState - - culture - properties: - changeType: - type: string - enum: ['created','updated','deleted'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentId: - type: string - format: uri - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - consenterUserId: - type: string - format: guid - description: The ID of the user giving the consent. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentState: - type: string - description: The state of the consent for the given consent option - enum: ['granted','denied','undecided'] - culture: - type: string - minLength: 5 - maxLength: 5 - description: The culture where the consent was changed. - examples: - - da-DK - - en-US - - en-GB - submissionSource: - type: string - description: The method used by the user to submit the cookies - enum: ['prebannerAcceptAll', 'prebannerRejectAll', 'savePrefButton', 'cloned' ] - - UserConsentFieldDeletedEvent: - type: object - required: - - changeType - - changeTime - - userId - - consentId - properties: - changeType: - type: string - enum: ['deleted'] - description: The change type of the event. - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - userId: - type: string - format: guid - description: The ID of the user. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentId: - type: string - format: uri - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - - EnvelopeOfAnonymousConsentFieldChangeEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'anonymousconsents.fieldchanged'. - enum: [anonymousconsents.fieldchanged] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/AnonymousConsentFieldChangeEvent' - EnvelopeOfExperienceCreatedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.created'. - enum: [experience.created] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceCreatedEvent' - EnvelopeOfExperienceDeletedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.deleted'. - enum: [experience.deleted] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceDeletedEvent' - EnvelopeOfExperienceUpdatedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.updated'. - enum: [experience.updated] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceUpdatedEvent' - EnvelopeOfExperienceClientAddedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.client.added'. - enum: [experience.client.added] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceClientAddedEvent' - EnvelopeOfExperienceClientRemovedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.client.removed'. - enum: [experience.client.removed] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceClientRemovedEvent' - EnvelopeOfExperienceConsentOptionAddedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.consentoption.added'. - enum: [experience.consentoption.added] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceConsentOptionAddedEvent' - EnvelopeOfExperienceConsentOptionRemovedEvent: - type: object - properties: - type: - type: string - description: The type of event. Will always have the value 'experience.consentoption.removed'. - enum: [experience.consentoption.removed] - correlationId: - type: string - description: | - The correlation ID as defined in https://github.com/LEGO/api-matters/blob/main/docs/practices/sync-apis/restful/readme.md#124-correlation-id-header - data: - $ref: '#/components/schemas/ExperienceConsentOptionRemovedEvent' - - AnonymousConsentFieldChangeEvent: - type: object - required: - - ChangeType - - ChangeTime - - AnonymousUserId - - ConsentId - - ConsentState - - Culture - properties: - changeType: - type: string - description: The change type - examples: - - Created - - Updated - - Deleted - changeTime: - type: string - format: date-time - description: 'The date and time of when the user consent was changed. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - anonymousUserId: - type: string - format: guid - description: The anonymous user ID. The GUID is 32 digits separated by hyphens and is not considered to be PII. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - consentId: - type: string - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - consentState: - type: string - description: The state of the consent for the given consent id - examples: - - granted - - denied - - undecided - culture: - type: string - minLength: 5 - maxLength: 5 - description: The culture where the consent was changed. - examples: - - da-DK - - en-US - - en-GB - ExperienceCreatedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - name - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - name: - type: string - description: name of experience - minLength: 1 - maxLength: 100 - examples: - - LEGO Webshop - ExperienceUpdatedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - name - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - name: - type: string - description: name of experience - minLength: 1 - maxLength: 100 - examples: - - LEGO Webshop - ExperienceDeletedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - ExperienceClientAddedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - clientId - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - clientId: - type: string - format: guid - description: Identity client id. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - ExperienceClientRemovedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - clientId - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - clientId: - type: string - format: guid - description: Identity client id. - minLength: 36 - maxLength: 36 - examples: - - 95b2cb5f-d551-4106-805c-9b800b1a0133 - ExperienceConsentOptionAddedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - consentOption - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - clientId: - type: string - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - ExperienceConsentOptionRemovedEvent: - type: object - required: - - experienceId - - occurredOnTimestamp - - consentOption - properties: - experienceId: - type: string - description: The experience id. - minLength: 1 - maxLength: 40 - examples: - - lego.com - occurredOnTimestamp: - type: string - format: date-time - description: 'The date and time of when experience was created. The format is in RFC 3339.' - examples: - - 2023-01-18T13:19:08.132084 - clientId: - type: string - description: The consent option URI - examples: - - self-consent://global/analytic-cookies - - self-consent://global/necessary-cookies - - self-consent://global/lego-marketing-cookies - messages: - UserConsentsObjectCreated: - messageId: UserConsentsObjectCreated - name: User consents object created - title: The object state of consents for a user - description: An event emitted whenever a user's consent is created. - tags: - - name: user - - name: consents - - name: created - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentsObjectCreated' - UserConsentsObjectChanged: - messageId: UserConsentsObjectChanged - name: User consents object changed - title: The object state of consents for a user - description: An event emitted whenever a user's consent is changed. - tags: - - name: user - - name: consents - - name: changed - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentsObjectChanged' - UserConsentsObjectDeleted: - messageId: UserConsentsObjectDeleted - name: User consents object deleted - title: The object state of consents for a user - description: An event emitted whenever a user's consent is deleted. - tags: - - name: user - - name: consents - - name: deleted - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentsObjectDeleted' - UserConsentFieldCreated: - messageId: UserConsentFieldCreated - name: User consent field created - title: The change of 1 specific consent being created - description: An event emitted whenever a user's consent is created. - tags: - - name: user - - name: consents - - name: created - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentFieldCreatedEvent' - UserConsentFieldChanged: - messageId: UserConsentFieldChanged - name: User consent field changed - title: The change of 1 specific consent being changed - description: An event emitted whenever a user's consent is changed. - tags: - - name: user - - name: consents - - name: changed - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentFieldChangedEvent' - UserConsentFieldDeleted: - messageId: UserConsentFieldDeleted - name: User consent field deleted - title: The change of 1 specific consent being deleted - description: An event emitted whenever a user's consent is deleted. - tags: - - name: user - - name: consents - - name: deleted - payload: - $ref: '#/components/schemas/EnvelopeOfUserConsentFieldDeletedEvent' - AnonymousConsentFieldChange: - messageId: AnonymousConsentFieldChange - name: Anonymous consent field change - title: Anonymous consent field change event - description: An event emitted whenever an anonymous consent is changed (added, updated or removed). - tags: - - name: anonymous - - name: consents - - name: deleted - payload: - $ref: '#/components/schemas/EnvelopeOfAnonymousConsentFieldChangeEvent' - ExperienceCreated: - messageId: ExperienceCreated - name: Experience created - title: Experience created event - description: An event emitted whenever an experience is created. - tags: - - name: experience - - name: created - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceCreatedEvent' - ExperienceDeleted: - messageId: ExperienceDeleted - name: Experience deleted - title: Experience deleted event - description: An event emitted whenever an experience is deleted. - tags: - - name: experience - - name: deleted - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceDeletedEvent' - ExperienceUpdated: - messageId: ExperienceUpdated - name: Experience updated - title: Experience updated event - description: An event emitted whenever an experience is updated. - tags: - - name: experience - - name: updated - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceUpdatedEvent' - ExperienceClientAdded: - messageId: ExperienceClientAdded - name: Experience client added - title: Experience client added event - description: An event emitted whenever a client is added to experience. - tags: - - name: experience - - name: updated - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceClientAddedEvent' - ExperienceClientRemoved: - messageId: ExperienceClientRemoved - name: Experience client removed - title: Experience client removed event - description: An event emitted whenever a client is removed from experience. - tags: - - name: experience - - name: updated - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceClientRemovedEvent' - ExperienceConsentOptionAdded: - messageId: ExperienceConsentOptionAdded - name: Experience consent option added - title: Experience consent option added event - description: An event emitted whenever an consent option is added to experience. - tags: - - name: experience - - name: updated - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceConsentOptionAddedEvent' - ExperienceConsentOptionRemoved: - messageId: ExperienceConsentOptionRemoved - name: Experience consent option removed - title: Experience consent option removed event - description: An event emitted whenever an consent option is removed from experience. - tags: - - name: experience - - name: updated - payload: - $ref: '#/components/schemas/EnvelopeOfExperienceConsentOptionRemovedEvent' -"; + var doc = new AsyncApiDocument(); + doc.Info = new AsyncApiInfo() + { + Description = "test description" + }; + doc.Servers.Add("production", new AsyncApiServer + { + Description = "test description", + Protocol = "pulsar+ssl", + Url = "example.com", + Bindings = new AsyncApiBindings() + { + Reference = new AsyncApiReference() + { + Type = ReferenceType.ServerBindings, + Id = "bindings", + }, + }, + }); + doc.Components = new AsyncApiComponents() + { + Channels = new Dictionary() + { + { "otherchannel", new AsyncApiChannel() + { + Publish = new AsyncApiOperation() + { + Description = "test", + }, + Bindings = new AsyncApiBindings() + { + Reference = new AsyncApiReference() + { + Type = ReferenceType.ChannelBindings, + Id = "bindings", + }, + }, + } + } + }, + ServerBindings = new Dictionary>() + { + { + "bindings", new AsyncApiBindings() + { + new PulsarServerBinding() + { + Tenant = "staging" + }, + } + } + }, + ChannelBindings = new Dictionary>() + { + { + "bindings", new AsyncApiBindings() + { + new PulsarChannelBinding() + { + Namespace = "users", + Persistence = AsyncAPI.Models.Bindings.Pulsar.Persistence.Persistent, + } + } + } + }, + }; + doc.Channels.Add("testChannel", + new AsyncApiChannel + { + Reference = new AsyncApiReference() + { + Type = ReferenceType.Channel, + Id = "otherchannel" + } + }); + var actual = doc.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); - var reader = new AsyncApiStringReader(); - var deserialized = reader.Read(spec, out var diagnostic); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.AddRange(BindingsCollection.Pulsar); + var reader = new AsyncApiStringReader(settings); + var deserialized = reader.Read(actual, out var diagnostic); } - + [Test] public void Serializev2_WithBindings_Serializes() { @@ -2219,18 +1300,20 @@ public void Serializev2_WithBindings_Serializes() }); var actual = doc.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); - var reader = new AsyncApiStringReader(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.AddRange(BindingsCollection.All); + var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); Assert.AreEqual(2, deserialized.Channels.First().Value.Publish.Message.First().Bindings.Count); var binding = deserialized.Channels.First().Value.Publish.Message.First().Bindings.First(); - Assert.AreEqual(BindingType.Http, binding.Key); + Assert.AreEqual("http", binding.Key); var httpBinding = binding.Value as HttpMessageBinding; Assert.AreEqual("this mah binding", httpBinding.Headers.Description); diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs index 7059b6fe..7e66eb06 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs @@ -1,18 +1,20 @@ -using FluentAssertions; -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Any; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Readers; -using LEGO.AsyncAPI.Readers.ParseNodes; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using YamlDotNet.RepresentationModel; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests { + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using FluentAssertions; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Any; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using NUnit.Framework; + using YamlDotNet.RepresentationModel; + public class AsyncApiLicenseTests { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index e2aeac00..a5d6a7a6 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Tests { using System; diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs new file mode 100644 index 00000000..5bf7379c --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs @@ -0,0 +1,124 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings +{ + using System.Collections.Generic; + using Extensions; + using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Any; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + using NUnit.Framework; + + public class NestedConfiguration : IAsyncApiExtensible + { + public string Name { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public static FixedFieldMap fixedFieldMap = new() + { + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + }; + + public void SerializeProperties(IAsyncApiWriter writer) + { + writer.WriteStartObject(); + writer.WriteOptionalProperty("name", this.Name); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } + + public class MyBinding : ChannelBinding + { + public string Custom { get; set; } + + public override string BindingKey => "my"; + + public NestedConfiguration NestedConfiguration { get; set; } + + public IAsyncApiAny Any { get; set; } + + protected override FixedFieldMap FixedFieldMap => new FixedFieldMap() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "custom", (a, n) => { a.Custom = n.GetScalarValue(); } }, + { "any", (a, n) => { a.Any = n.CreateAny(); } }, + { "nestedConfiguration", (a, n) => { a.NestedConfiguration = n.ParseMapWithExtensions(NestedConfiguration.fixedFieldMap); } }, + }; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + writer.WriteStartObject(); + writer.WriteRequiredProperty("custom", this.Custom); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteRequiredObject("any", this.Any, (w, p) => w.WriteAny(p)); + writer.WriteOptionalObject("nestedConfiguration", this.NestedConfiguration, (w, r) => r.SerializeProperties(w)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } + + public class CustomBinding_Should + { + [Test] + public void CustomBinding_SerializesDeserializes() + { + // Arrange + var expected = +@"bindings: + my: + custom: someValue + bindingVersion: 0.1.0 + any: + anyKeyName: anyValue + nestedConfiguration: + name: nested + x-myNestedExtension: nestedValue + x-myextension: someValue"; + + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new MyBinding + { + Custom = "someValue", + Any = new AsyncApiObject() + { + { "anyKeyName", new AsyncApiString("anyValue") }, + }, + BindingVersion = "0.1.0", + NestedConfiguration = new NestedConfiguration() + { + Name = "nested", + Extensions = new Dictionary() + { + { "x-myNestedExtension", new AsyncApiString("nestedValue") }, + }, + }, + Extensions = new Dictionary() + { + { "x-myextension", new AsyncApiString("someValue") }, + }, + }); + + // Act + var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(new MyBinding()); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(channel); + } + } +} diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs index 01ce4885..0b00bbf7 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs @@ -1,8 +1,11 @@ -namespace LEGO.AsyncAPI.Tests.Bindings.Http +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.Http { using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.Http; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -32,11 +35,12 @@ public void HttpMessageBinding_FilledObject_SerializesAndDeserializes() var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Http); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(message); } @@ -56,7 +60,7 @@ public void HttpOperationBinding_FilledObject_SerializesAndDeserializes() operation.Bindings.Add(new HttpOperationBinding { - Type = "request", + Type = HttpOperationBinding.HttpOperationType.Request, Method = "POST", Query = new AsyncApiSchema { @@ -68,11 +72,12 @@ public void HttpOperationBinding_FilledObject_SerializesAndDeserializes() var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Http); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(operation); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index 49e1e515..22bf8d2d 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -1,11 +1,15 @@ -namespace LEGO.AsyncAPI.Tests.Bindings.Kafka +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.Kafka { + using System.Collections.Generic; using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Readers; using NUnit.Framework; - using System.Collections.Generic; internal class KafkaBindings_Should { @@ -50,11 +54,12 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() // Assert actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(channel); } @@ -86,11 +91,12 @@ public void KafkaServerBinding_WithFilledObject_SerializesAndDeserializes() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(server); } @@ -124,11 +130,12 @@ public void KafkaMessageBinding_WithFilledObject_SerializesAndDeserializes() var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(message); } @@ -163,10 +170,12 @@ public void KafkaOperationBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Kafka); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(operation); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs index 05fd4f98..15577763 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs @@ -1,13 +1,14 @@ -using LEGO.AsyncAPI.Models.Bindings; - +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Bindings.Pulsar { + using System.Collections.Generic; using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Pulsar; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Bindings.Pulsar; using LEGO.AsyncAPI.Readers; using NUnit.Framework; - using System.Collections.Generic; internal class PulsarBindings_Should { @@ -59,10 +60,12 @@ public void PulsarChannelBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(channel); } @@ -76,10 +79,12 @@ public void PulsarChannelBindingNamespaceDefaultToNull() persistence: persistent"; // Act - // Assert - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - Assert.AreEqual(null, ((PulsarChannelBinding)binding.Bindings[BindingType.Pulsar]).Namespace); + // Assert + Assert.AreEqual(null, ((PulsarChannelBinding)binding.Bindings["pulsar"]).Namespace); } [Test] @@ -93,9 +98,12 @@ public void PulsarChannelBindingPropertiesExceptNamespaceDefaultToNull() // Act // Assert - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - var pulsarBinding = ((PulsarChannelBinding) binding.Bindings[BindingType.Pulsar]); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var pulsarBinding = ((PulsarChannelBinding)binding.Bindings["pulsar"]); + Assert.AreEqual("staging", pulsarBinding.Namespace); Assert.AreEqual(null, pulsarBinding.Persistence); Assert.AreEqual(null, pulsarBinding.Compaction); Assert.AreEqual(null, pulsarBinding.GeoReplication); @@ -130,11 +138,12 @@ public void PulsarServerBinding_WithFilledObject_SerializesAndDeserializes() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(server); } @@ -165,12 +174,13 @@ public void ServerBindingVersionDefaultsToNull() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); - Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings[BindingType.Pulsar]).BindingVersion); + Assert.AreEqual(expected, actual); + Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings["pulsar"]).BindingVersion); binding.Should().BeEquivalentTo(server); } @@ -201,12 +211,13 @@ public void ServerTenantDefaultsToNull() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Pulsar); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); - Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings[BindingType.Pulsar]).Tenant); + Assert.AreEqual(expected, actual); + Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings["pulsar"]).Tenant); binding.Should().BeEquivalentTo(server); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs index 0f934b91..2a57b997 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs @@ -1,8 +1,11 @@ -namespace LEGO.AsyncAPI.Tests.Bindings.WebSockets +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.WebSockets { using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.WebSockets; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.WebSockets; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -40,10 +43,12 @@ public void WebSocketChannelBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var binding = new AsyncApiStringReader().ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Websockets); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); binding.Should().BeEquivalentTo(channel); } } diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index c55609e5..692c8e06 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -28,6 +28,7 @@ + @@ -41,6 +42,10 @@ + + + + diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs index dfaa6764..05f91943 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs @@ -1,9 +1,11 @@ -namespace LEGO.AsyncAPI.Tests.Models +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Models { using System.Collections.Generic; + using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Bindings.WebSockets; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.Kafka; - using LEGO.AsyncAPI.Models.Bindings.WebSockets; using LEGO.AsyncAPI.Models.Interfaces; using NUnit.Framework; @@ -69,7 +71,7 @@ public void AsyncApiChannel_WithWebSocketsBinding_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -104,7 +106,7 @@ public void AsyncApiChannel_WithKafkaBinding_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs index 817e2bc3..4cdbf307 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiContact_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiContact_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs index 0721426c..e6720030 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiExternalDocumentation_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiExternalDocumentation_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs index 31fdb8d0..7c4e9a73 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiInfo_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiInfo_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs index 5cd8f59a..eb412d21 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiLicense_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiLicense_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs index 3df3266e..b6f40e8c 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessageExample_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiMessageExample_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index d2dabcd3..f86a9ad4 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -1,13 +1,15 @@ -namespace LEGO.AsyncAPI.Tests.Models +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Models { using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Any; - using LEGO.AsyncAPI.Models.Bindings; - using LEGO.AsyncAPI.Models.Bindings.Http; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -90,7 +92,7 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); message.Should().BeEquivalentTo(deserializedMessage); } @@ -131,7 +133,7 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); message.Should().BeEquivalentTo(deserializedMessage); } @@ -275,7 +277,7 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Bindings = new AsyncApiBindings() { { - BindingType.Http, new HttpMessageBinding + "http", new HttpMessageBinding { Headers = new AsyncApiSchema { @@ -370,10 +372,12 @@ public void AsyncApiMessage_WithFilledObject_Serializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.All); + var deserializedMessage = new AsyncApiStringReader(settings).ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); message.Should().BeEquivalentTo(deserializedMessage); } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs index 207fe140..93fc79e1 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOAuthFlow_Should.cs @@ -1,9 +1,11 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiOAuthFlow_Should { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs index f0778cec..8d8bd4f5 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs @@ -1,15 +1,17 @@ -using System; -using System.Globalization; -using System.IO; -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Bindings.Http; -using LEGO.AsyncAPI.Models.Bindings.Kafka; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Writers; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using System.Globalization; + using System.IO; + using LEGO.AsyncAPI.Bindings.Http; + using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + using NUnit.Framework; + public class AsyncApiOperation_Should { [Test] @@ -46,7 +48,7 @@ public void SerializeV2_WithMultipleMessages_SerializesWithOneOf() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -69,7 +71,7 @@ public void SerializeV2_WithSingleMessage_Serializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -78,7 +80,7 @@ public void AsyncApiOperation_WithBindings_Serializes() var expected = @"bindings: http: - type: type + type: request method: PUT query: description: some query @@ -95,7 +97,7 @@ public void AsyncApiOperation_WithBindings_Serializes() { new HttpOperationBinding { - Type = "type", + Type = HttpOperationBinding.HttpOperationType.Request, Method = "PUT", Query = new AsyncApiSchema { @@ -125,7 +127,7 @@ public void AsyncApiOperation_WithBindings_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 57101ed9..9395373d 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -1,13 +1,15 @@ -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Writers; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using System.Collections.Generic; + using System.Globalization; + using System.IO; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Writers; + using NUnit.Framework; + public class AsyncApiSchema_Should { private string NoInlinedReferences => @@ -150,7 +152,7 @@ public void Serialize_WithInliningOptions_ShouldInlineAccordingly(bool shouldInl expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs index f8872233..b92224df 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSecurityRequirement_Should.cs @@ -1,9 +1,12 @@ -using System; -using LEGO.AsyncAPI.Models; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + public class AsyncApiSecurityRequirement_Should { [Test] @@ -16,5 +19,14 @@ public void SerializeV2_WithNullWriter_Throws() // Assert Assert.Throws(() => { asyncApiSecurityRequirement.SerializeV2(null); }); } + + [Test] + public void SerializeV2_Serializes() + { + var asyncApiSecurityRequirement = new AsyncApiSecurityRequirement(); + asyncApiSecurityRequirement.Add(new AsyncApiSecurityScheme { Type = SecuritySchemeType.ApiKey }, new List { "string" }); + + var output = asyncApiSecurityRequirement.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs index 52010434..880694d6 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs @@ -1,11 +1,13 @@ -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Bindings.Kafka; -using LEGO.AsyncAPI.Models.Interfaces; -using NUnit.Framework; -using System.Collections.Generic; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Models { + using System.Collections.Generic; + using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using NUnit.Framework; + internal class AsyncApiServer_Should { [Test] @@ -71,7 +73,7 @@ public void AsyncApiServer_Serializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } [Test] @@ -104,7 +106,7 @@ public void AsyncApiServer_WithKafkaBinding_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); // Assert - Assert.AreEqual(actual, expected); + Assert.AreEqual(expected, actual); } } } diff --git a/test/LEGO.AsyncAPI.Tests/StringExtensions.cs b/test/LEGO.AsyncAPI.Tests/StringExtensions.cs index 6a3c89fd..2f0fddb8 100644 --- a/test/LEGO.AsyncAPI.Tests/StringExtensions.cs +++ b/test/LEGO.AsyncAPI.Tests/StringExtensions.cs @@ -1,4 +1,6 @@ -namespace LEGO.AsyncAPI.Tests +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests { using System; diff --git a/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs b/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs index 70929de1..48446738 100644 --- a/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs +++ b/test/LEGO.AsyncAPI.Tests/Validation/ValidationRulesetTests.cs @@ -1,8 +1,10 @@ -using LEGO.AsyncAPI.Validations; -using NUnit.Framework; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Tests.Validation { + using LEGO.AsyncAPI.Validations; + using NUnit.Framework; + public class ValidationRuleSetTests { [Test] diff --git a/test/LEGO.AsyncAPI.Tests/stylecop.json b/test/LEGO.AsyncAPI.Tests/stylecop.json new file mode 100644 index 00000000..0a8f4661 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/stylecop.json @@ -0,0 +1,15 @@ +{ + // ACTION REQUIRED: This file was automatically added to your project, but it + // will not take effect until additional steps are taken to enable it. See the + // following page for additional information: + // + // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md + + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "The LEGO Group", + "xmlHeader": false + } + } +} From 7b0b8168f998cf08593860a1be91e7e6015883c4 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Mon, 5 Jun 2023 09:26:39 +0200 Subject: [PATCH 04/84] ci: force pre-releases --- .github/workflows/release-internal.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release-internal.yml b/.github/workflows/release-internal.yml index 66df465a..fef5c2e4 100644 --- a/.github/workflows/release-internal.yml +++ b/.github/workflows/release-internal.yml @@ -20,6 +20,7 @@ jobs: uses: actions/checkout@v1 - name: Semantic Release + id: semantic uses: cycjimmy/semantic-release-action@v3 with: dry_run: true @@ -27,8 +28,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} outputs: - trigger_release: ${{ steps.semantic.outputs.new_release_published }} - version: ${{ steps.semantic.outputs.new_release_published == 'true' && steps.semantic.outputs.new_release_version }} + version: ${{ steps.semantic.outputs.new_release_version }} pre-release: runs-on: ubuntu-latest @@ -43,13 +43,10 @@ jobs: uses: actions/checkout@v1 - name: Setup .NET Core @ Latest - if: needs.check.outputs.trigger_release == 'true' uses: actions/setup-dotnet@v1 - name: Build ${{ matrix.package-name }} project and pack NuGet package - if: needs.check.outputs.trigger_release == 'true' run: dotnet pack src/${{ matrix.package-name }}/${{ matrix.package-name }}.csproj -c Release -o out-${{ matrix.package-name }} -p:PackageVersion=${{ needs.check.outputs.version }}-beta - name: Push generated package to GitHub Packages registry - if: needs.check.outputs.trigger_release == 'true' run: dotnet nuget push out-${{ matrix.package-name }}/*.nupkg -s https://api.nuget.org/v3/index.json --skip-duplicate -n --api-key ${{secrets.NUGET}} From 0c9eab7477d895442f1c1f08f151f97b580d5d60 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Mon, 5 Jun 2023 09:47:25 +0200 Subject: [PATCH 05/84] chore: update readme with pre-release shields --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cacd288a..199a055c 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,16 @@ The AsyncAPI.NET SDK contains a useful object model for the AsyncAPI specificati ## Installation Install the NuGet packages: +### AsyncAPI.NET +[![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET?label=AsyncAPI.NET&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) +[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET?label=AsyncAPI.NET&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) +### AsyncAPI.NET.Readers [![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Readers?label=AsyncAPI.NET.Readers&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) -[![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET?label=AsyncAPI.NET&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) +[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET.Readers?label=AsyncAPI.NET.Readers&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) + +### AsyncAPI.NET.Bindings +[![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Bindings?label=AsyncAPI.NET.Bindings&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Bindings/) ## Example Usage From 211646e95b82b3e32563fe75c57656cd6882267b Mon Sep 17 00:00:00 2001 From: "Alex W. Carlsen" Date: Mon, 5 Jun 2023 12:51:23 +0200 Subject: [PATCH 06/84] fix: add setter to BindingParsers collection to be able to set during initialization, a setter was added to the Bindings property. --- README.md | 20 ++++++++++++++----- .../BindingsCollection.cs | 4 ++-- .../AsyncApiReaderSettings.cs | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 199a055c..fda302e2 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,12 @@ Install the NuGet packages: [![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET?label=AsyncAPI.NET&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) [![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET?label=AsyncAPI.NET&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) -### AsyncAPI.NET.Readers -[![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Readers?label=AsyncAPI.NET.Readers&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) -[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET.Readers?label=AsyncAPI.NET.Readers&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) +### AsyncAPI.Readers +[![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Readers?label=AsyncAPI.Readers&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) +[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET.Readers?label=AsyncAPI.Readers&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) -### AsyncAPI.NET.Bindings -[![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Bindings?label=AsyncAPI.NET.Bindings&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Bindings/) +### AsyncAPI.Bindings +[![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Bindings?label=AsyncAPI.Bindings&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Bindings/) ## Example Usage @@ -78,6 +78,16 @@ var stream = await httpClient.GetStreamAsync("master/examples/streetlights-kafka var asyncApiDocument = new AsyncApiStreamReader().Read(stream, out var diagnostic); ``` +### Bindings +To add support for reading bindings, simply add the bindings you wish to support, to the `Bindings` collection of `AsyncApiReaderSettings`. +There is a nifty helper to add different types of bindings, or like in the example `All` of them. + +```csharp +var settings = new AsyncApiReaderSettings(); +settings.Bindings.Add(BindingsCollection.All); +var asyncApiDocument = new AsyncApiStringReader(settings).Read(stream, out var diagnostic); +``` + ## Attribution * [OpenAPI.Net](https://github.com/microsoft/OpenAPI.NET) - [MIT License](https://github.com/microsoft/OpenAPI.NET/blob/vnext/LICENSE) diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs index 0a34ec39..b201a4dc 100644 --- a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -40,12 +40,13 @@ public static TCollection Add( Pulsar, Kafka, Http, + Websockets, }; public static IEnumerable> Http => new List> { new HttpOperationBinding(), - new HttpMessageBinding() + new HttpMessageBinding(), }; public static IEnumerable> Websockets => new List> @@ -63,7 +64,6 @@ public static TCollection Add( public static IEnumerable> Pulsar => new List> { - // Pulsar new PulsarServerBinding(), new PulsarChannelBinding(), }; diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index 01576991..47e7e1da 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -43,7 +43,7 @@ public Dictionary> public List> Bindings - { get; } = + { get; set; } = new List>(); /// From ab009764a916171c8926c129384ce18b3162e71e Mon Sep 17 00:00:00 2001 From: "Alex W. Carlsen" Date: Wed, 7 Jun 2023 08:44:34 +0200 Subject: [PATCH 07/84] feat(JsonSchema)!: changed out decimal for double to allow for bigger numbers BREAKING CHANGE: this changes the type of 3 properties of JsonSchema from `decimal` to `double` --- .../V2/AsyncApiSchemaDeserializer.cs | 6 +- src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs | 6 +- .../Models/AsyncApiSchema_Should.cs | 384 ++++++++++++++++++ 3 files changed, 390 insertions(+), 6 deletions(-) diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index 88dc8efb..074fd443 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -37,14 +37,14 @@ public class JsonSchemaDeserializer "multipleOf", (a, n) => { - a.MultipleOf = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + a.MultipleOf = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); } }, { "maximum", (a, n) => { - a.Maximum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + a.Maximum = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); } }, { @@ -54,7 +54,7 @@ public class JsonSchemaDeserializer "minimum", (a, n) => { - a.Minimum = decimal.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + a.Minimum = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); } }, { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs index b72b452b..dee06728 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs @@ -37,7 +37,7 @@ public class AsyncApiSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyn /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public decimal? Maximum { get; set; } + public double? Maximum { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. @@ -47,7 +47,7 @@ public class AsyncApiSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyn /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public decimal? Minimum { get; set; } + public double? Minimum { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. @@ -73,7 +73,7 @@ public class AsyncApiSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyn /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public decimal? MultipleOf { get; set; } + public double? MultipleOf { get; set; } /// /// Follow JSON Schema definition: https://tools.ietf.org/html/draft-fge-json-schema-validation-00 diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 9395373d..58dfd574 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -6,12 +6,226 @@ namespace LEGO.AsyncAPI.Tests.Models using System.Collections.Generic; using System.Globalization; using System.IO; + using FluentAssertions; using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Writers; using NUnit.Framework; public class AsyncApiSchema_Should { + public static AsyncApiSchema BasicSchema = new AsyncApiSchema(); + + public static AsyncApiSchema AdvancedSchemaNumber = new AsyncApiSchema + { + Title = "title1", + MultipleOf = 3, + Maximum = 42, + ExclusiveMinimum = true, + Minimum = 10, + Default = new AsyncApiInteger(15), + Type = new List { SchemaType.Integer }, + Nullable = true, + ExternalDocs = new AsyncApiExternalDocumentation + { + Url = new Uri("http://example.com/externalDocs"), + }, + }; + + public static AsyncApiSchema AdvancedSchemaBigNumbers = new AsyncApiSchema + { + Title = "title1", + MultipleOf = 3, + Maximum = double.MaxValue, + ExclusiveMinimum = true, + Minimum = double.MinValue, + Default = new AsyncApiInteger(15), + Type = new List { SchemaType.Integer }, + Nullable = true, + ExternalDocs = new AsyncApiExternalDocumentation + { + Url = new Uri("http://example.com/externalDocs"), + }, + }; + + public static AsyncApiSchema AdvancedSchemaObject = new AsyncApiSchema + { + Title = "title1", + Properties = new Dictionary + { + ["property1"] = new AsyncApiSchema + { + Properties = new Dictionary + { + ["property2"] = new AsyncApiSchema + { + Type = new List { SchemaType.Integer }, + }, + ["property3"] = new AsyncApiSchema + { + Type = new List { SchemaType.String }, + MaxLength = 15, + }, + }, + }, + ["property4"] = new AsyncApiSchema + { + Properties = new Dictionary + { + ["property5"] = new AsyncApiSchema + { + Properties = new Dictionary + { + ["property6"] = new AsyncApiSchema + { + Type = new List { SchemaType.Boolean }, + }, + }, + }, + ["property7"] = new AsyncApiSchema + { + Type = new List { SchemaType.String }, + MinLength = 2, + }, + }, + }, + }, + Nullable = true, + ExternalDocs = new AsyncApiExternalDocumentation + { + Url = new Uri("http://example.com/externalDocs"), + }, + }; + + public static AsyncApiSchema AdvancedSchemaWithAllOf = new AsyncApiSchema + { + Title = "title1", + AllOf = new List + { + new AsyncApiSchema + { + Title = "title2", + Properties = new Dictionary + { + ["property1"] = new AsyncApiSchema + { + Type = new List { SchemaType.Integer }, + }, + ["property2"] = new AsyncApiSchema + { + Type = new List { SchemaType.String }, + MaxLength = 15, + }, + }, + }, + new AsyncApiSchema + { + Title = "title3", + Properties = new Dictionary + { + ["property3"] = new AsyncApiSchema + { + Properties = new Dictionary + { + ["property4"] = new AsyncApiSchema + { + Type = new List { SchemaType.Boolean }, + }, + }, + }, + ["property5"] = new AsyncApiSchema + { + Type = new List { SchemaType.String }, + MinLength = 2, + }, + }, + Nullable = true, + }, + }, + Nullable = true, + ExternalDocs = new AsyncApiExternalDocumentation + { + Url = new Uri("http://example.com/externalDocs"), + }, + }; + + public static AsyncApiSchema ReferencedSchema = new AsyncApiSchema + { + Title = "title1", + MultipleOf = 3, + Maximum = 42, + ExclusiveMinimum = true, + Minimum = 10, + Default = new AsyncApiInteger(15), + Type = new List { SchemaType.Integer }, + + Nullable = true, + ExternalDocs = new AsyncApiExternalDocumentation + { + Url = new Uri("http://example.com/externalDocs"), + }, + + Reference = new AsyncApiReference + { + Type = ReferenceType.Schema, + Id = "schemaObject1", + }, + }; + + public static AsyncApiSchema AdvancedSchemaWithRequiredPropertiesObject = new AsyncApiSchema + { + Title = "title1", + Required = new HashSet() { "property1" }, + Properties = new Dictionary + { + ["property1"] = new AsyncApiSchema + { + Required = new HashSet() { "property3" }, + Properties = new Dictionary + { + ["property2"] = new AsyncApiSchema + { + Type = new List { SchemaType.Integer }, + }, + ["property3"] = new AsyncApiSchema + { + Type = new List { SchemaType.String }, + MaxLength = 15, + ReadOnly = true, + }, + }, + ReadOnly = true, + }, + ["property4"] = new AsyncApiSchema + { + Properties = new Dictionary + { + ["property5"] = new AsyncApiSchema + { + Properties = new Dictionary + { + ["property6"] = new AsyncApiSchema + { + Type = new List { SchemaType.Boolean }, + }, + }, + }, + ["property7"] = new AsyncApiSchema + { + Type = new List { SchemaType.String }, + MinLength = 2, + }, + }, + ReadOnly = true, + }, + }, + Nullable = true, + ExternalDocs = new AsyncApiExternalDocumentation + { + Url = new Uri("http://example.com/externalDocs"), + }, + }; + private string NoInlinedReferences => @"asyncapi: '2.6.0' info: @@ -77,6 +291,176 @@ public class AsyncApiSchema_Should description: test components: { }"; + [Test] + public void SerializeAsJson_WithBasicSchema_V2Works() + { + // Arrange + var expected = @"{ }"; + + // Act + var actual = BasicSchema.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Test] + public void SerializeAsJson_WithAdvancedSchemaNumber_V2Works() + { + // Arrange + var expected = @"{ + ""title"": ""title1"", + ""type"": ""integer"", + ""maximum"": 42, + ""minimum"": 10, + ""exclusiveMinimum"": true, + ""multipleOf"": 3, + ""default"": 15, + ""nullable"": true, + ""externalDocs"": { + ""url"": ""http://example.com/externalDocs"" + } +}"; + + // Act + var actual = AdvancedSchemaNumber.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Test] + public void SerializeAsJson_WithAdvancedSchemaBigNumbers_V2Works() + { + // Arrange + var expected = @"{ + ""title"": ""title1"", + ""type"": ""integer"", + ""maximum"": 1.7976931348623157E+308, + ""minimum"": -1.7976931348623157E+308, + ""exclusiveMinimum"": true, + ""multipleOf"": 3, + ""default"": 15, + ""nullable"": true, + ""externalDocs"": { + ""url"": ""http://example.com/externalDocs"" + } +}"; + + // Act + var actual = AdvancedSchemaBigNumbers.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Test] + public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() + { + // Arrange + var expected = @"{ + ""title"": ""title1"", + ""properties"": { + ""property1"": { + ""properties"": { + ""property2"": { + ""type"": ""integer"" + }, + ""property3"": { + ""type"": ""string"", + ""maxLength"": 15 + } + } + }, + ""property4"": { + ""properties"": { + ""property5"": { + ""properties"": { + ""property6"": { + ""type"": ""boolean"" + } + } + }, + ""property7"": { + ""type"": ""string"", + ""minLength"": 2 + } + } + } + }, + ""nullable"": true, + ""externalDocs"": { + ""url"": ""http://example.com/externalDocs"" + } +}"; + + // Act + var actual = AdvancedSchemaObject.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Test] + public void SerializeAsJson_WithAdvancedSchemaWithAllOf_V2Works() + { + // Arrange + var expected = @"{ + ""title"": ""title1"", + ""allOf"": [ + { + ""title"": ""title2"", + ""properties"": { + ""property1"": { + ""type"": ""integer"" + }, + ""property2"": { + ""type"": ""string"", + ""maxLength"": 15 + } + } + }, + { + ""title"": ""title3"", + ""properties"": { + ""property3"": { + ""properties"": { + ""property4"": { + ""type"": ""boolean"" + } + } + }, + ""property5"": { + ""type"": ""string"", + ""minLength"": 2 + } + }, + ""nullable"": true + } + ], + ""nullable"": true, + ""externalDocs"": { + ""url"": ""http://example.com/externalDocs"" + } +}"; + + // Act + var actual = AdvancedSchemaWithAllOf.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + [Theory] [TestCase(true)] [TestCase(false)] From d44efb048402c70377064b87bd962b0e455e08b3 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Wed, 7 Jun 2023 14:28:55 +0200 Subject: [PATCH 08/84] feat(JsonSchema)!: type as flag rather than list (#115) BREAKING CHANGE: this changes the type of Type in JsonSchema to be a Flags enum, rather than a List of enum. --- .../ParseNodes/AsyncApiAnyConverter.cs | 26 +++++------ .../V2/AsyncApiSchemaDeserializer.cs | 20 +++++++-- src/LEGO.AsyncAPI/EnumExtensions.cs | 13 ++++++ src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs | 11 ++--- src/LEGO.AsyncAPI/Models/SchemaType.cs | 31 ++++++++++--- .../Validation/Rules/RuleHelpers.cs | 2 +- .../AsyncApiDocumentV2Tests.cs | 22 +++++----- .../Models/AsyncApiMessage_Should.cs | 28 +++++------- .../Models/AsyncApiSchema_Should.cs | 44 +++++++++---------- 9 files changed, 118 insertions(+), 79 deletions(-) diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs index ae73281e..a9b3ab79 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs @@ -62,7 +62,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn return new AsyncApiDateTime(dateTimeValue); } } - else if (type.Contains(SchemaType.String)) + else if (type.Value.HasFlag(SchemaType.String)) { if (format == "byte") { @@ -143,7 +143,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn } else { - if (type.Contains(SchemaType.Integer) && format == "int32") + if (type.Value.HasFlag(SchemaType.Integer) && format == "int32") { if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) { @@ -151,7 +151,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn } } - if (type.Contains(SchemaType.Integer) && format == "int64") + if (type.Value.HasFlag(SchemaType.Integer) && format == "int64") { if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue)) { @@ -159,7 +159,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn } } - if (type.Contains(SchemaType.Integer)) + if (type.Value.HasFlag(SchemaType.Integer)) { if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) { @@ -167,7 +167,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn } } - if (type.Contains(SchemaType.Number) && format == "float") + if (type.Value.HasFlag(SchemaType.Number) && format == "float") { if (float.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var floatValue)) { @@ -175,7 +175,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn } } - if (type.Contains(SchemaType.Number) && format == "double") + if (type.Value.HasFlag(SchemaType.Number) && format == "double") { if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue)) { @@ -183,7 +183,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn } } - if (type.Contains(SchemaType.Number)) + if (type.Value.HasFlag(SchemaType.Number)) { if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue)) { @@ -191,7 +191,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn } } - if (type.Contains(SchemaType.String) && format == "byte") + if (type.Value.HasFlag(SchemaType.String) && format == "byte") { try { @@ -202,7 +202,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn } // binary - if (type.Contains(SchemaType.String) && format == "binary") + if (type.Value.HasFlag(SchemaType.String) && format == "binary") { try { @@ -212,7 +212,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn { } } - if (type.Contains(SchemaType.String) && format == "date") + if (type.Value.HasFlag(SchemaType.String) && format == "date") { if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateValue)) { @@ -220,7 +220,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn } } - if (type.Contains(SchemaType.String) && format == "date-time") + if (type.Value.HasFlag(SchemaType.String) && format == "date-time") { if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue)) { @@ -228,12 +228,12 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn } } - if (type.Contains(SchemaType.String)) + if (type.Value.HasFlag(SchemaType.String)) { return asyncApiAny; } - if (type.Contains(SchemaType.Boolean)) + if (type.Value.HasFlag(SchemaType.Boolean)) { if (bool.TryParse(value, out var booleanValue)) { diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index 074fd443..8a7af701 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -2,8 +2,10 @@ namespace LEGO.AsyncAPI.Readers { + using System; using System.Collections.Generic; using System.Globalization; + using System.Runtime.CompilerServices; using LEGO.AsyncAPI.Extensions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.ParseNodes; @@ -21,11 +23,23 @@ public class JsonSchemaDeserializer { if (n.GetType() == typeof(ValueNode)) { - a.Type = new List { n.GetScalarValue().GetEnumFromDisplayName() }; + a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } - else + if (n.GetType() == typeof(ListNode)) { - a.Type = new List(n.CreateSimpleList(n2 => n2.GetScalarValue().GetEnumFromDisplayName())); + SchemaType? initialValue = null; + foreach (var node in n as ListNode) + { + if (initialValue == null) + { + initialValue = node.GetScalarValue().GetEnumFromDisplayName(); + continue; + } + + initialValue |= node.GetScalarValue().GetEnumFromDisplayName(); + } + + a.Type = initialValue; } } }, diff --git a/src/LEGO.AsyncAPI/EnumExtensions.cs b/src/LEGO.AsyncAPI/EnumExtensions.cs index d7e2a751..56a46874 100644 --- a/src/LEGO.AsyncAPI/EnumExtensions.cs +++ b/src/LEGO.AsyncAPI/EnumExtensions.cs @@ -3,6 +3,7 @@ namespace LEGO.AsyncAPI { using System; + using System.Collections.Generic; using System.Linq; using System.Reflection; using LEGO.AsyncAPI.Attributes; @@ -38,5 +39,17 @@ public static string GetDisplayName(this Enum enumValue) var attribute = enumValue.GetAttributeOfType(); return attribute == null ? enumValue.ToString() : attribute.Name; } + + public static IEnumerable GetFlags(this Enum input) + where TEnum : Enum + { + foreach (TEnum value in System.Enum.GetValues(input.GetType())) + { + if (input.HasFlag(value)) + { + yield return value; + } + } + } } } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs index dee06728..c9282af3 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs @@ -19,9 +19,9 @@ public class AsyncApiSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyn public string Title { get; set; } /// - /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html + /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public IList Type { get; set; } + public SchemaType? Type { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. @@ -257,13 +257,14 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) // type if (this.Type != null) { - if (this.Type.Count == 1) + var types = EnumExtensions.GetFlags(this.Type.Value); + if (types.Count() == 1) { - writer.WriteOptionalProperty(AsyncApiConstants.Type, this.Type.First().GetDisplayName()); + writer.WriteOptionalProperty(AsyncApiConstants.Type, types.First().GetDisplayName()); } else { - writer.WriteOptionalCollection(AsyncApiConstants.Type, this.Type.Select(t => t.GetDisplayName()), (w, s) => w.WriteValue(s)); + writer.WriteOptionalCollection(AsyncApiConstants.Type, types.Select(t => t.GetDisplayName()), (w, s) => w.WriteValue(s)); } } diff --git a/src/LEGO.AsyncAPI/Models/SchemaType.cs b/src/LEGO.AsyncAPI/Models/SchemaType.cs index 5951d7b5..4fc9ec0c 100644 --- a/src/LEGO.AsyncAPI/Models/SchemaType.cs +++ b/src/LEGO.AsyncAPI/Models/SchemaType.cs @@ -2,29 +2,46 @@ namespace LEGO.AsyncAPI.Models { + using System; + using System.Collections.Generic; using LEGO.AsyncAPI.Attributes; + [Flags] public enum SchemaType { [Display("null")] - Null, + Null = 1, [Display("boolean")] - Boolean, + Boolean = 2, [Display("object")] - Object, + Object = 4, [Display("array")] - Array, + Array = 8, [Display("number")] - Number, + Number = 16, [Display("string")] - String, + String = 32, [Display("integer")] - Integer, + Integer = 64, + } + + public static class SchemaTypeHelpers + { + public static IEnumerable GetFlags(SchemaType input) + { + foreach (SchemaType value in System.Enum.GetValues(input.GetType())) + { + if (input.HasFlag(value)) + { + yield return value; + } + } + } } } diff --git a/src/LEGO.AsyncAPI/Validation/Rules/RuleHelpers.cs b/src/LEGO.AsyncAPI/Validation/Rules/RuleHelpers.cs index 058ff27e..57a31789 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/RuleHelpers.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/RuleHelpers.cs @@ -42,7 +42,7 @@ public static void ValidateDataTypeMismatch( return; } - var types = schema.Type; + var types = EnumExtensions.GetFlags(schema.Type); var format = schema.Format; var nullable = schema.Nullable; diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 3c90d791..706fa8f2 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -545,13 +545,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }) .WithComponent("lightMeasuredPayload", new AsyncApiSchema() { - Type = new List { SchemaType.Object }, + Type = SchemaType.Object, Properties = new Dictionary() { { "lumens", new AsyncApiSchema() { - Type = new List { SchemaType.Integer }, + Type = SchemaType.Integer, Minimum = 0, Description = "Light intensity measured in lumens.", } @@ -570,13 +570,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }) .WithComponent("turnOnOffPayload", new AsyncApiSchema() { - Type = new List { SchemaType.Object }, + Type = SchemaType.Object, Properties = new Dictionary() { { "command", new AsyncApiSchema() { - Type = new List { SchemaType.String }, + Type = SchemaType.String, Enum = new List { new AsyncApiString("on"), @@ -599,13 +599,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }) .WithComponent("dimLightPayload", new AsyncApiSchema() { - Type = new List { SchemaType.Object }, + Type = SchemaType.Object, Properties = new Dictionary() { { "percentage", new AsyncApiSchema() { - Type = new List { SchemaType.Integer }, + Type = SchemaType.Integer, Description = "Percentage to which the light should be dimmed to.", Minimum = 0, Maximum = 100, @@ -625,7 +625,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }) .WithComponent("sentAt", new AsyncApiSchema() { - Type = new List { SchemaType.String }, + Type = SchemaType.String, Format = "date-time", Description = "Date and time when the message was sent.", @@ -645,20 +645,20 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Description = "The ID of the streetlight.", Schema = new AsyncApiSchema() { - Type = new List { SchemaType.String }, + Type = SchemaType.String, }, }) .WithComponent("commonHeaders", new AsyncApiMessageTrait() { Headers = new AsyncApiSchema() { - Type = new List { SchemaType.Object }, + Type = SchemaType.Object, Properties = new Dictionary() { { "my-app-header", new AsyncApiSchema() { - Type = new List { SchemaType.Integer }, + Type = SchemaType.Integer, Minimum = 0, Maximum = 100, } @@ -675,7 +675,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() { ClientId = new AsyncApiSchema() { - Type = new List { SchemaType.String }, + Type = SchemaType.String, Enum = new List { new AsyncApiString("my-app-id"), diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index f86a9ad4..f207a31c 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -25,8 +25,8 @@ public void AsyncApiMessage_WithNoSchemaFormat_DeserializesToDefault() properties: propertyA: type: - - string - - 'null'"; + - 'null' + - string"; // Act var message = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); @@ -45,8 +45,8 @@ public void AsyncApiMessage_WithUnsupportedSchemaFormat_DeserializesWithError() properties: propertyA: type: - - string - 'null' + - string schemaFormat: application/vnd.apache.avro;version=1.9.0"; // Act @@ -66,8 +66,8 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() properties: propertyA: type: - - string - - 'null'"; + - 'null' + - string"; var message = new AsyncApiMessage(); message.Payload = new AsyncApiSchema() @@ -77,7 +77,7 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() { "propertyA", new AsyncApiSchema() { - Type = new List { SchemaType.String, SchemaType.Null }, + Type = SchemaType.String | SchemaType.Null, } }, }, @@ -105,8 +105,8 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() properties: propertyA: type: - - string - 'null' + - string schemaFormat: application/vnd.aai.asyncapi+json;version=2.6.0"; var message = new AsyncApiMessage(); @@ -118,7 +118,7 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() { "propertyA", new AsyncApiSchema() { - Type = new List { SchemaType.String, SchemaType.Null }, + Type = SchemaType.String | SchemaType.Null, } }, }, @@ -137,7 +137,7 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() message.Should().BeEquivalentTo(deserializedMessage); } - [Test] + [Test] public void AsyncApiMessage_WithFilledObject_Serializes() { var expected = @@ -230,19 +230,13 @@ public void AsyncApiMessage_WithFilledObject_Serializes() { "propA", new AsyncApiSchema() { - Type = new List() - { - SchemaType.String, - }, + Type = SchemaType.String, } }, { "propB", new AsyncApiSchema() { - Type = new List() - { - SchemaType.String, - }, + Type =SchemaType.String, } }, }, diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 58dfd574..386773f7 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -24,7 +24,7 @@ public class AsyncApiSchema_Should ExclusiveMinimum = true, Minimum = 10, Default = new AsyncApiInteger(15), - Type = new List { SchemaType.Integer }, + Type = SchemaType.Integer, Nullable = true, ExternalDocs = new AsyncApiExternalDocumentation { @@ -40,7 +40,7 @@ public class AsyncApiSchema_Should ExclusiveMinimum = true, Minimum = double.MinValue, Default = new AsyncApiInteger(15), - Type = new List { SchemaType.Integer }, + Type = SchemaType.Integer, Nullable = true, ExternalDocs = new AsyncApiExternalDocumentation { @@ -59,11 +59,11 @@ public class AsyncApiSchema_Should { ["property2"] = new AsyncApiSchema { - Type = new List { SchemaType.Integer }, + Type = SchemaType.Integer, }, ["property3"] = new AsyncApiSchema { - Type = new List { SchemaType.String }, + Type = SchemaType.String, MaxLength = 15, }, }, @@ -78,13 +78,13 @@ public class AsyncApiSchema_Should { ["property6"] = new AsyncApiSchema { - Type = new List { SchemaType.Boolean }, + Type = SchemaType.Boolean , }, }, }, ["property7"] = new AsyncApiSchema { - Type = new List { SchemaType.String }, + Type = SchemaType.String, MinLength = 2, }, }, @@ -109,11 +109,11 @@ public class AsyncApiSchema_Should { ["property1"] = new AsyncApiSchema { - Type = new List { SchemaType.Integer }, + Type = SchemaType.Integer, }, ["property2"] = new AsyncApiSchema { - Type = new List { SchemaType.String }, + Type = SchemaType.String, MaxLength = 15, }, }, @@ -129,13 +129,13 @@ public class AsyncApiSchema_Should { ["property4"] = new AsyncApiSchema { - Type = new List { SchemaType.Boolean }, + Type = SchemaType.Boolean , }, }, }, ["property5"] = new AsyncApiSchema { - Type = new List { SchemaType.String }, + Type = SchemaType.String, MinLength = 2, }, }, @@ -157,7 +157,7 @@ public class AsyncApiSchema_Should ExclusiveMinimum = true, Minimum = 10, Default = new AsyncApiInteger(15), - Type = new List { SchemaType.Integer }, + Type = SchemaType.Integer, Nullable = true, ExternalDocs = new AsyncApiExternalDocumentation @@ -185,11 +185,11 @@ public class AsyncApiSchema_Should { ["property2"] = new AsyncApiSchema { - Type = new List { SchemaType.Integer }, + Type = SchemaType.Integer, }, ["property3"] = new AsyncApiSchema { - Type = new List { SchemaType.String }, + Type = SchemaType.String, MaxLength = 15, ReadOnly = true, }, @@ -206,13 +206,13 @@ public class AsyncApiSchema_Should { ["property6"] = new AsyncApiSchema { - Type = new List { SchemaType.Boolean }, + Type = SchemaType.Boolean , }, }, }, ["property7"] = new AsyncApiSchema { - Type = new List { SchemaType.String }, + Type = SchemaType.String, MinLength = 2, }, }, @@ -489,7 +489,7 @@ public void Serialize_WithInliningOptions_ShouldInlineAccordingly(bool shouldInl { Payload = new AsyncApiSchema { - Type = new List { SchemaType.Object }, + Type = SchemaType.Object, Required = new HashSet { "testB" }, Properties = new Dictionary { @@ -501,16 +501,16 @@ public void Serialize_WithInliningOptions_ShouldInlineAccordingly(bool shouldInl }, }, }) - .WithComponent("testD", new AsyncApiSchema() { Type = new List { SchemaType.String }, Format = "uuid" }) + .WithComponent("testD", new AsyncApiSchema() { Type = SchemaType.String, Format = "uuid" }) .WithComponent("testC", new AsyncApiSchema() { - Type = new List { SchemaType.Object }, + Type = SchemaType.Object, Properties = new Dictionary { { "testD", new AsyncApiSchema { Reference = new AsyncApiReference { Type = ReferenceType.Schema, Id = "testD" } } }, }, }) - .WithComponent("testB", new AsyncApiSchema() { Description = "test", Type = new List { SchemaType.Boolean } }) + .WithComponent("testB", new AsyncApiSchema() { Description = "test", Type = SchemaType.Boolean }) .Build(); var outputString = new StringWriter(CultureInfo.InvariantCulture); @@ -559,7 +559,7 @@ public void Serialize_WithOneOf_DoesNotWriteThen() { var mainSchema = new AsyncApiSchema(); var subSchema = new AsyncApiSchema(); - subSchema.Properties.Add("title", new AsyncApiSchema() { Type = new List { SchemaType.String } }); + subSchema.Properties.Add("title", new AsyncApiSchema() { Type = SchemaType.String }); mainSchema.OneOf = new List() { subSchema }; var yaml = mainSchema.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); @@ -577,7 +577,7 @@ public void Serialize_WithAnyOf_DoesNotWriteIf() { var mainSchema = new AsyncApiSchema(); var subSchema = new AsyncApiSchema(); - subSchema.Properties.Add("title", new AsyncApiSchema() { Type = new List { SchemaType.String } }); + subSchema.Properties.Add("title", new AsyncApiSchema() { Type = SchemaType.String }); mainSchema.AnyOf = new List() { subSchema }; var yaml = mainSchema.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); @@ -594,7 +594,7 @@ public void Serialize_WithNot_DoesNotWriteElse() { var mainSchema = new AsyncApiSchema(); var subSchema = new AsyncApiSchema(); - subSchema.Properties.Add("title", new AsyncApiSchema() { Type = new List { SchemaType.String } }); + subSchema.Properties.Add("title", new AsyncApiSchema() { Type = SchemaType.String }); mainSchema.Not = subSchema; var yaml = mainSchema.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); From df36b0a3829f05a8e4990625ee407c2d6f6b48f0 Mon Sep 17 00:00:00 2001 From: "Alex W. Carlsen" Date: Fri, 9 Jun 2023 12:56:07 +0200 Subject: [PATCH 09/84] refactor: fix lots of warnings --- .../Http/HttpMessageBinding.cs | 2 +- .../Http/HttpOperationBinding.cs | 3 +- .../Kafka/KafkaChannelBinding.cs | 4 +- .../Kafka/KafkaMessageBinding.cs | 5 +- .../Kafka/KafkaOperationBinding.cs | 3 +- .../Kafka/KafkaServerBinding.cs | 7 +- .../Pulsar/PulsarChannelBinding.cs | 4 +- .../Pulsar/PulsarServerBinding.cs | 2 +- .../WebSockets/WebSocketsChannelBinding.cs | 2 +- .../AsyncApiReaderSettings.cs | 2 +- .../AsyncApiStreamReader.cs | 8 +- .../AsyncApiStringReader.cs | 4 +- .../AsyncApiTextReader.cs | 16 ++-- .../AsyncApiYamlDocumentReader.cs | 11 ++- .../BindingDeserializer.cs | 2 +- .../Exceptions/AsyncApiReaderException.cs | 12 ++- ...erty.cs => AnyListFieldMapParameter{T}.cs} | 3 +- .../ParseNodes/PropertyNode.cs | 1 - src/LEGO.AsyncAPI.Readers/ParsingContext.cs | 6 +- .../Services/DefaultStreamLoader.cs | 1 - .../V2/AsyncApiComponentsDeserializer.cs | 4 +- .../V2/AsyncApiContactDeserializer.cs | 4 +- .../V2/AsyncApiCorrelationIdDeserializer.cs | 4 +- .../V2/AsyncApiDocumentDeserializer.cs | 4 +- .../V2/AsyncApiExampleDeserializer.cs | 4 +- .../V2/AsyncApiExternalDocsDeserializer.cs | 4 +- .../V2/AsyncApiInfoDeserializer.cs | 4 +- .../V2/AsyncApiLicenseDeserializer.cs | 4 +- .../V2/AsyncApiMessageDeserializer.cs | 4 +- .../V2/AsyncApiMessageTraitDeserializer.cs | 4 +- .../V2/AsyncApiOAuthFlowDeserializer.cs | 4 +- .../V2/AsyncApiOAuthFlowsDeserializer.cs | 4 +- .../V2/AsyncApiOperationTraitDeserializer.cs | 4 +- .../V2/AsyncApiParameterDeserializer.cs | 4 +- .../V2/AsyncApiSchemaDeserializer.cs | 8 +- .../V2/AsyncApiSecuritySchemeDeserializer.cs | 2 +- .../V2/AsyncApiServerDeserializer.cs | 4 +- .../V2/AsyncApiServerVariableDeserializer.cs | 4 +- .../V2/AsyncApiTagDeserializer.cs | 4 +- .../V2/AsyncApiV2VersionService.cs | 6 +- .../Exceptions/AsyncApiException.cs | 2 +- .../Expressions/BodyExpression.cs | 4 +- .../Expressions/CompositeExpression.cs | 8 +- .../Expressions/HeaderExpression.cs | 2 +- .../Expressions/MethodExpression.cs | 2 +- .../Expressions/PathExpression.cs | 2 +- .../Expressions/QueryExpression.cs | 2 +- .../Expressions/RequestExpression.cs | 2 +- .../Expressions/ResponseExpression.cs | 2 +- .../Extensions/AsyncApiElementExtensions.cs | 8 +- .../Models/Any/AsyncAPIDouble.cs | 2 +- .../Models/Any/AsyncAPIString.cs | 3 +- src/LEGO.AsyncAPI/Models/Any/AsyncApiByte.cs | 2 +- src/LEGO.AsyncAPI/Models/Any/AsyncApiDate.cs | 2 +- .../Models/Any/AsyncApiDateTime.cs | 2 +- src/LEGO.AsyncAPI/Models/Any/AsyncApiFloat.cs | 2 +- .../Models/Any/AsyncApiInteger.cs | 2 +- src/LEGO.AsyncAPI/Models/AsyncApiOAuthFlow.cs | 2 +- .../Models/AsyncApiOAuthFlows.cs | 4 +- src/LEGO.AsyncAPI/Models/AsyncApiOperation.cs | 2 +- .../Models/AsyncApiSecurityRequirement.cs | 2 +- .../Models/AsyncApiSecurityScheme.cs | 2 +- .../Models/AsyncApiSerializableExtensions.cs | 21 ++--- .../Interfaces/IAsyncApiReferenceable.cs | 2 +- .../Models/RuntimeExpressionAnyWrapper.cs | 8 +- src/LEGO.AsyncAPI/Models/SchemaType.cs | 15 ---- src/LEGO.AsyncAPI/Models/SchemaTypeHelpers.cs | 20 +++++ .../Services/AsyncApiReferenceResolver.cs | 8 +- .../Services/AsyncApiVisitorBase.cs | 80 ++++++++++--------- src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs | 2 +- .../Validation/AsyncApiValidationError.cs | 2 +- .../Validation/AsyncApiValidator.cs | 54 ++++++------- .../Validation/AsyncApiValidatorWarning.cs | 2 +- .../Validation/IValidationContext.cs | 6 +- .../Validation/ValidationExtensions.cs | 6 +- .../Validation/ValidationRule.cs | 48 +---------- .../Validation/ValidationRuleSet.cs | 9 +-- .../Validation/ValidationRule{T}.cs | 51 ++++++++++++ .../Writers/AsyncApiWriterAnyExtensions.cs | 4 +- .../Writers/AsyncApiWriterBase.cs | 2 +- .../Writers/AsyncApiWriterExtensions.cs | 14 ++-- .../Writers/AsyncApiYamlWriter.cs | 2 +- .../Writers/AsyncJsonWriterSettings.cs | 3 +- src/LEGO.AsyncAPI/Writers/WriterConstants.cs | 10 +-- .../AsyncApiDocumentV2Tests.cs | 2 +- .../Bindings/CustomBinding_Should.cs | 5 +- 86 files changed, 315 insertions(+), 304 deletions(-) rename src/LEGO.AsyncAPI.Readers/ParseNodes/{AnyListFieldMapProperty.cs => AnyListFieldMapParameter{T}.cs} (89%) create mode 100644 src/LEGO.AsyncAPI/Models/SchemaTypeHelpers.cs create mode 100644 src/LEGO.AsyncAPI/Validation/ValidationRule{T}.cs diff --git a/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs index 15a654dd..ea9f6f95 100644 --- a/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs @@ -40,7 +40,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) public override string BindingKey => "http"; - protected override FixedFieldMap FixedFieldMap => new() + protected override FixedFieldMap FixedFieldMap => new () { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs index b5c92c38..d41bbafb 100644 --- a/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs @@ -22,6 +22,7 @@ public enum HttpOperationType [Display("response")] Response, } + /// /// REQUIRED. Type of operation. Its value MUST be either request or response. /// @@ -57,7 +58,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteEndObject(); } - protected override FixedFieldMap FixedFieldMap => new() + protected override FixedFieldMap FixedFieldMap => new () { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs index 78b745e0..48fa5f04 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs @@ -35,7 +35,7 @@ public class KafkaChannelBinding : ChannelBinding public override string BindingKey => "kafka"; - protected override FixedFieldMap FixedFieldMap => new() + protected override FixedFieldMap FixedFieldMap => new () { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, @@ -44,7 +44,7 @@ public class KafkaChannelBinding : ChannelBinding { "replicas", (a, n) => { a.Replicas = n.GetIntegerValue(); } }, }; - private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new() + private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new () { { "cleanup.policy", (a, n) => { a.CleanupPolicy = n.CreateSimpleList(s => s.GetScalarValue()); } }, { "retention.ms", (a, n) => { a.RetentionMiliseconds = n.GetIntegerValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs index 31123de1..85062422 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs @@ -60,12 +60,11 @@ public override void SerializeProperties(IAsyncApiWriter writer) /// Serializes the v2. /// /// The writer. - /// writer - + /// writer. public override string BindingKey => "kafka"; - protected override FixedFieldMap FixedFieldMap => new() + protected override FixedFieldMap FixedFieldMap => new () { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "key", (a, n) => { a.Key = JsonSchemaDeserializer.LoadSchema(n); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs index db80e0ce..5ae7ba8f 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs @@ -25,14 +25,13 @@ public class KafkaOperationBinding : OperationBinding public override string BindingKey => "kafka"; - protected override FixedFieldMap FixedFieldMap => new() + protected override FixedFieldMap FixedFieldMap => new () { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "groupId", (a, n) => { a.GroupId = JsonSchemaDeserializer.LoadSchema(n); } }, { "clientId", (a, n) => { a.ClientId = JsonSchemaDeserializer.LoadSchema(n); } }, }; - /// /// Serialize to AsyncAPI V2 document without using reference. /// diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs index 1d40a798..0ee3bc5d 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs @@ -13,19 +13,18 @@ namespace LEGO.AsyncAPI.Bindings.Kafka public class KafkaServerBinding : ServerBinding { /// - /// API URL for the Schema Registry used when producing Kafka messages (if a Schema Registry was used) + /// API URL for the Schema Registry used when producing Kafka messages (if a Schema Registry was used). /// public string SchemaRegistryUrl { get; set; } /// - /// The vendor of Schema Registry and Kafka serdes library that should be used (e.g. apicurio, confluent, ibm, or karapace) + /// The vendor of Schema Registry and Kafka serdes library that should be used (e.g. apicurio, confluent, ibm, or karapace). /// public string SchemaRegistryVendor { get; set; } - public override string BindingKey => "kafka"; - protected override FixedFieldMap FixedFieldMap => new() + protected override FixedFieldMap FixedFieldMap => new () { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "schemaRegistryUrl", (a, n) => { a.SchemaRegistryUrl = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs index c673a8e8..bc30f9a9 100644 --- a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs @@ -68,7 +68,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteEndObject(); } - protected override FixedFieldMap FixedFieldMap => new() + protected override FixedFieldMap FixedFieldMap => new () { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "namespace", (a, n) => { a.Namespace = n.GetScalarValue(); } }, @@ -80,7 +80,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) { "deduplication", (a, n) => { a.Deduplication = n.GetBooleanValue(); } }, }; - private FixedFieldMap pulsarServerBindingRetentionFixedFields = new() + private FixedFieldMap pulsarServerBindingRetentionFixedFields = new () { { "time", (a, n) => { a.Time = n.GetIntegerValue(); } }, { "size", (a, n) => { a.Size = n.GetIntegerValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs index e767443d..1a102d71 100644 --- a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs @@ -19,7 +19,7 @@ public class PulsarServerBinding : ServerBinding public override string BindingKey => "pulsar"; - protected override FixedFieldMap FixedFieldMap => new() + protected override FixedFieldMap FixedFieldMap => new () { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "tenant", (a, n) => { a.Tenant = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs index 94afcb55..a3fb6366 100644 --- a/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs @@ -27,7 +27,7 @@ public class WebSocketsChannelBinding : ChannelBinding public override string BindingKey => "websockets"; - protected override FixedFieldMap FixedFieldMap => new() + protected override FixedFieldMap FixedFieldMap => new () { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index 47e7e1da..0429364a 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -23,7 +23,7 @@ public enum ReferenceResolutionSetting } /// - /// Configuration settings to control how AsyncApi documents are parsed + /// Configuration settings to control how AsyncApi documents are parsed. /// public class AsyncApiReaderSettings { diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs index ed6c3942..aeaf33c0 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Readers using LEGO.AsyncAPI.Readers.Interface; /// - /// Service class for converting streams into AsyncApiDocument instances + /// Service class for converting streams into AsyncApiDocument instances. /// public class AsyncApiStreamReader : IAsyncApiReader { @@ -46,7 +46,7 @@ public AsyncApiDocument Read(Stream input, out AsyncApiDiagnostic diagnostic) /// Reads the stream input and parses it into an AsyncApi document. /// /// Stream containing AsyncApi description to parse. - /// Instance result containing newly created AsyncApiDocument and diagnostics object from the process + /// Instance result containing newly created AsyncApiDocument and diagnostics object from the process. public async Task ReadAsync(Stream input) { MemoryStream bufferedStream; @@ -73,8 +73,8 @@ public async Task ReadAsync(Stream input) /// /// Stream containing AsyncApi description to parse. /// Version of the AsyncApi specification that the fragment conforms to. - /// Returns diagnostic object containing errors detected during parsing - /// Instance of newly created AsyncApiDocument + /// Returns diagnostic object containing errors detected during parsing. + /// Instance of newly created AsyncApiDocument. public T ReadFragment(Stream input, AsyncApiVersion version, out AsyncApiDiagnostic diagnostic) where T : IAsyncApiReferenceable { diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs index 8d4bd870..90d3f84b 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiStringReader.cs @@ -8,14 +8,14 @@ namespace LEGO.AsyncAPI.Readers using LEGO.AsyncAPI.Readers.Interface; /// - /// Service class for converting strings into AsyncApiDocument instances + /// Service class for converting strings into AsyncApiDocument instances. /// public class AsyncApiStringReader : IAsyncApiReader { private readonly AsyncApiReaderSettings settings; /// - /// Constructor tha allows reader to use non-default settings + /// Constructor tha allows reader to use non-default settings. /// /// public AsyncApiStringReader(AsyncApiReaderSettings settings = null) diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs index 0eaef44d..84f8392f 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs @@ -12,7 +12,7 @@ namespace LEGO.AsyncAPI.Readers using YamlDotNet.RepresentationModel; /// - /// Service class for converting contents of TextReader into AsyncApiDocument instances + /// Service class for converting contents of TextReader into AsyncApiDocument instances. /// public class AsyncApiTextReader : IAsyncApiReader { @@ -31,8 +31,8 @@ public AsyncApiTextReader(AsyncApiReaderSettings settings = null) /// Reads the stream input and parses it into an AsyncApi document. /// /// TextReader containing AsyncApi description to parse. - /// Returns diagnostic object containing errors detected during parsing - /// Instance of newly created AsyncApiDocument + /// Returns diagnostic object containing errors detected during parsing. + /// Instance of newly created AsyncApiDocument. public AsyncApiDocument Read(TextReader input, out AsyncApiDiagnostic diagnostic) { YamlDocument yamlDocument; @@ -85,8 +85,8 @@ public async Task ReadAsync(TextReader input) /// /// TextReader containing AsyncApi description to parse. /// Version of the AsyncApi specification that the fragment conforms to. - /// Returns diagnostic object containing errors detected during parsing - /// Instance of newly created AsyncApiDocument + /// Returns diagnostic object containing errors detected during parsing. + /// Instance of newly created AsyncApiDocument. public T ReadFragment(TextReader input, AsyncApiVersion version, out AsyncApiDiagnostic diagnostic) where T : IAsyncApiElement { @@ -109,10 +109,10 @@ public T ReadFragment(TextReader input, AsyncApiVersion version, out AsyncApi } /// - /// Helper method to turn streams into YamlDocument + /// Helper method to turn streams into YamlDocument. /// - /// Stream containing YAML formatted text - /// Instance of a YamlDocument + /// Stream containing YAML formatted text. + /// Instance of a YamlDocument. static YamlDocument LoadYamlDocument(TextReader input) { var yamlStream = new YamlStream(); diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs index cc03a892..5b52ea88 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs @@ -14,7 +14,7 @@ namespace LEGO.AsyncAPI.Readers using YamlDotNet.RepresentationModel; /// - /// Service class for converting contents of TextReader into AsyncApiDocument instances + /// Service class for converting contents of TextReader into AsyncApiDocument instances. /// internal class AsyncApiYamlDocumentReader : IAsyncApiReader { @@ -33,8 +33,8 @@ public AsyncApiYamlDocumentReader(AsyncApiReaderSettings settings = null) /// Reads the stream input and parses it into an AsyncApi document. /// /// TextReader containing AsyncApi description to parse. - /// Returns diagnostic object containing errors detected during parsing - /// Instance of newly created AsyncApiDocument + /// Returns diagnostic object containing errors detected during parsing. + /// Instance of newly created AsyncApiDocument. public AsyncApiDocument Read(YamlDocument input, out AsyncApiDiagnostic diagnostic) { diagnostic = new AsyncApiDiagnostic(); @@ -133,14 +133,13 @@ private void ResolveReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument d } } - /// /// Reads the stream input and parses the fragment of an AsyncApi description into an AsyncApi Element. /// /// TextReader containing AsyncApi description to parse. /// Version of the AsyncApi specification that the fragment conforms to. - /// Returns diagnostic object containing errors detected during parsing - /// Instance of newly created AsyncApiDocument + /// Returns diagnostic object containing errors detected during parsing. + /// Instance of newly created AsyncApiDocument. public T ReadFragment(YamlDocument input, AsyncApiVersion version, out AsyncApiDiagnostic diagnostic) where T : IAsyncApiElement { diff --git a/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs index 5744985d..441e0935 100644 --- a/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs @@ -22,7 +22,7 @@ public static T LoadBinding(string nodeName, ParseNode node, FixedFieldMap private static PatternFieldMap BindingPatternExtensionFields() where T : IBinding, new() { - return new() + return new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiReaderException.cs b/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiReaderException.cs index c58ccf1c..498eb592 100644 --- a/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiReaderException.cs +++ b/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiReaderException.cs @@ -9,10 +9,14 @@ namespace LEGO.AsyncAPI.Readers.Exceptions [Serializable] public class AsyncApiReaderException : AsyncApiException { - public AsyncApiReaderException() { } + public AsyncApiReaderException() + { + } public AsyncApiReaderException(string message) - : base(message) { } + : base(message) + { + } public AsyncApiReaderException(string message, ParsingContext context) : base(message) @@ -29,6 +33,8 @@ public AsyncApiReaderException(string message, YamlNode node) } public AsyncApiReaderException(string message, Exception innerException) - : base(message, innerException) { } + : base(message, innerException) + { + } } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapProperty.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs similarity index 89% rename from src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapProperty.cs rename to src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs index 13c44f33..abd65184 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapProperty.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs @@ -1,5 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Readers.ParseNodes { diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs index 221f0d9a..913edfb1 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs @@ -7,7 +7,6 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System.Linq; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Exceptions; using YamlDotNet.RepresentationModel; diff --git a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs index 58b0b04b..c8b9c8d0 100644 --- a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs +++ b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs @@ -25,13 +25,13 @@ internal Dictionary> ExtensionPar = new (); - internal Dictionary> ServerBindingParsers { get; set; } = new(); + internal Dictionary> ServerBindingParsers { get; set; } = new (); internal Dictionary> ChannelBindingParsers { get; set; } - internal Dictionary> OperationBindingParsers { get; set; } = new(); + internal Dictionary> OperationBindingParsers { get; set; } = new (); - internal Dictionary> MessageBindingParsers { get; set; } = new(); + internal Dictionary> MessageBindingParsers { get; set; } = new (); internal RootNode RootNode { get; set; } diff --git a/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs b/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs index acf94265..6e506730 100644 --- a/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs +++ b/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs @@ -13,7 +13,6 @@ internal class DefaultStreamLoader : IStreamLoader private readonly Uri baseUrl; private HttpClient httpClient = new HttpClient(); - public DefaultStreamLoader(Uri baseUrl) { this.baseUrl = baseUrl; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs index 3b63db28..cda083de 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap componentsFixedFields = new() + private static FixedFieldMap componentsFixedFields = new () { { "schemas", (a, n) => a.Schemas = n.CreateMapWithReference(ReferenceType.Schema, JsonSchemaDeserializer.LoadSchema) }, { "servers", (a, n) => a.Servers = n.CreateMapWithReference(ReferenceType.Server, LoadServer) }, @@ -26,7 +26,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap componentsPatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiContactDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiContactDeserializer.cs index 1a02ffaa..09672719 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiContactDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiContactDeserializer.cs @@ -13,14 +13,14 @@ namespace LEGO.AsyncAPI.Readers /// internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap contactFixedFields = new() + private static FixedFieldMap contactFixedFields = new () { { "name", (o, n) => { o.Name = n.GetScalarValue(); } }, { "email", (o, n) => { o.Email = n.GetScalarValue(); } }, { "url", (o, n) => { o.Url = new Uri(n.GetScalarValue(), UriKind.RelativeOrAbsolute); } }, }; - private static PatternFieldMap contactPatternFields = new() + private static PatternFieldMap contactPatternFields = new () { { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs index 0b8e88f0..aaf30093 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs @@ -13,14 +13,14 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap correlationIdFixedFileds = - new() + new () { { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, { "location", (a, n) => { a.Location = n.GetScalarValue(); } }, }; private static readonly PatternFieldMap correlationIdPatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDocumentDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDocumentDeserializer.cs index e7f0289e..9daf6418 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDocumentDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDocumentDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap asyncApiFixedFields = new() + private static FixedFieldMap asyncApiFixedFields = new () { { "asyncapi", (a, n) => { a.Asyncapi = "2.6.0"; } }, { "id", (a, n) => a.Id = n.GetScalarValue() }, @@ -21,7 +21,7 @@ internal static partial class AsyncApiV2Deserializer { "externalDocs", (a, n) => a.ExternalDocs = LoadExternalDocs(n) }, }; - private static PatternFieldMap asyncApiPatternFields = new() + private static PatternFieldMap asyncApiPatternFields = new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExampleDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExampleDeserializer.cs index 46667a4b..b8f80257 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExampleDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExampleDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap exampleFixedFields = new() + private static FixedFieldMap exampleFixedFields = new () { { "headers", (a, n) => { a.Headers = n.CreateMap(LoadAny); } }, { "payload", (a, n) => { a.Payload = n.CreateAny(); } }, @@ -17,7 +17,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap examplePatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExternalDocsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExternalDocsDeserializer.cs index 2c5e07b6..824fa26a 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExternalDocsDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExternalDocsDeserializer.cs @@ -9,14 +9,14 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap externalDocumentationFixedFields = new() + private static FixedFieldMap externalDocumentationFixedFields = new () { { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, { "url", (a, n) => { a.Url = new Uri(n.GetScalarValue()); } }, }; private static PatternFieldMap externalDocumentationPatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiInfoDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiInfoDeserializer.cs index 60a359f5..c9c745dd 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiInfoDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiInfoDeserializer.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap infoFixedFields = new() + private static FixedFieldMap infoFixedFields = new () { { "title", (a, n) => { a.Title = n.GetScalarValue(); } }, { "version", (a, n) => { a.Version = n.GetScalarValue(); } }, @@ -20,7 +20,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap infoPatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiLicenseDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiLicenseDeserializer.cs index 630d2efc..28c2fb8d 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiLicenseDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiLicenseDeserializer.cs @@ -9,14 +9,14 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap licenseFixedFields = new() + private static FixedFieldMap licenseFixedFields = new () { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, { "url", (a, n) => { a.Url = new Uri(n.GetScalarValue()); } }, }; private static PatternFieldMap licensePatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs index 63d0512c..640d3482 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs @@ -15,7 +15,7 @@ namespace LEGO.AsyncAPI.Readers /// internal static partial class AsyncApiV2Deserializer { - private static readonly FixedFieldMap messageFixedFields = new() + private static readonly FixedFieldMap messageFixedFields = new () { { "messageId", (a, n) => { a.MessageId = n.GetScalarValue(); } @@ -83,7 +83,7 @@ private static string LoadSchemaFormat(string schemaFormat) return schemaFormat; } - private static readonly PatternFieldMap messagePatternFields = new() + private static readonly PatternFieldMap messagePatternFields = new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs index eca8af64..67de4bcd 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap messageTraitFixedFields = new() + private static FixedFieldMap messageTraitFixedFields = new () { { "messageId", (a, n) => { a.MessageId = n.GetScalarValue(); } }, { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, @@ -26,7 +26,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap messageTraitPatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowDeserializer.cs index cddb8126..dfeb3d83 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowDeserializer.cs @@ -14,7 +14,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap oAuthFlowFixedFields = - new() + new () { { "authorizationUrl", (o, n) => @@ -38,7 +38,7 @@ internal static partial class AsyncApiV2Deserializer }; private static readonly PatternFieldMap oAuthFlowPatternFields = - new() + new () { { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowsDeserializer.cs index dba5ab09..8f3a1ce3 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowsDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowsDeserializer.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap oAuthFlowsFixedFileds = - new() + new () { { "implicit", (a, n) => a.Implicit = LoadOAuthFlow(n) }, { "password", (a, n) => a.Password = LoadOAuthFlow(n) }, @@ -22,7 +22,7 @@ internal static partial class AsyncApiV2Deserializer }; private static readonly PatternFieldMap oAuthFlowsPatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs index 699795c8..1cb1629f 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap operationTraitFixedFields = new() + private static FixedFieldMap operationTraitFixedFields = new () { { "operationId", (a, n) => { a.OperationId = n.GetScalarValue(); } }, { "summary", (a, n) => { a.Summary = n.GetScalarValue(); } }, @@ -19,7 +19,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap operationTraitPatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs index bff810f1..e9ef5e51 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap parameterFixedFields = new() + private static FixedFieldMap parameterFixedFields = new () { { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, { "schema", (a, n) => { a.Schema = JsonSchemaDeserializer.LoadSchema(n); } }, @@ -16,7 +16,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap parameterPatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index 8a7af701..911b7f77 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -2,10 +2,8 @@ namespace LEGO.AsyncAPI.Readers { - using System; using System.Collections.Generic; using System.Globalization; - using System.Runtime.CompilerServices; using LEGO.AsyncAPI.Extensions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.ParseNodes; @@ -166,12 +164,12 @@ public class JsonSchemaDeserializer }; private static readonly PatternFieldMap schemaPatternFields = - new() + new () { { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) }, }; - private static readonly AnyFieldMap schemaAnyFields = new() + private static readonly AnyFieldMap schemaAnyFields = new () { { AsyncApiConstants.Default, @@ -182,7 +180,7 @@ public class JsonSchemaDeserializer }, }; - private static readonly AnyListFieldMap schemaAnyListFields = new() + private static readonly AnyListFieldMap schemaAnyListFields = new () { { AsyncApiConstants.Enum, diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs index 75ad46dd..10ce26d7 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs @@ -15,7 +15,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap securitySchemeFixedFields = - new() + new () { { "type", (o, n) => { o.Type = n.GetScalarValue().GetEnumFromDisplayName(); } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs index 6eae00af..2dffe850 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs @@ -12,7 +12,7 @@ namespace LEGO.AsyncAPI.Readers /// internal static partial class AsyncApiV2Deserializer { - private static readonly FixedFieldMap serverFixedFields = new() + private static readonly FixedFieldMap serverFixedFields = new () { { "url", (a, n) => { a.Url = n.GetScalarValue(); } @@ -41,7 +41,7 @@ internal static partial class AsyncApiV2Deserializer }; private static readonly PatternFieldMap serverPatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs index b773d255..4a4c0db5 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap serverVariableFixedFields = - new() + new () { { "enum", (a, n) => { a.Enum = n.CreateSimpleList(s => s.GetScalarValue()); } @@ -30,7 +30,7 @@ internal static partial class AsyncApiV2Deserializer }; private static readonly PatternFieldMap serverVariablePatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiTagDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiTagDeserializer.cs index cc589fa2..9f38c01c 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiTagDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiTagDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap tagsFixedFields = new() + private static FixedFieldMap tagsFixedFields = new () { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, @@ -16,7 +16,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap tagsPatternFields = - new() + new () { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs index c3878072..6ea4cbb2 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs @@ -16,7 +16,7 @@ internal class AsyncApiV2VersionService : IAsyncApiVersionService public AsyncApiDiagnostic Diagnostic { get; } /// - /// Create Parsing Context + /// Create Parsing Context. /// /// Provide instance for diagnostic object for collecting and accessing information about the parsing. public AsyncApiV2VersionService(AsyncApiDiagnostic diagnostic) @@ -48,8 +48,8 @@ public AsyncApiV2VersionService(AsyncApiDiagnostic diagnostic) /// /// Parse the string to a object. /// - /// The URL of the reference - /// The type of object referenced based on the context of the reference + /// The URL of the reference. + /// The type of object referenced based on the context of the reference. public AsyncApiReference ConvertToAsyncApiReference( string reference, ReferenceType? type) diff --git a/src/LEGO.AsyncAPI/Exceptions/AsyncApiException.cs b/src/LEGO.AsyncAPI/Exceptions/AsyncApiException.cs index 73a25acd..d841c002 100644 --- a/src/LEGO.AsyncAPI/Exceptions/AsyncApiException.cs +++ b/src/LEGO.AsyncAPI/Exceptions/AsyncApiException.cs @@ -39,7 +39,7 @@ public AsyncApiException(string message, Exception innerException) /// JSON Pointer as per https://tools.ietf.org/html/rfc6901 /// If the document fails to parse as JSON/YAML then the fragment will be based on /// a text/plain pointer as defined in https://tools.ietf.org/html/rfc5147 - /// Currently only line= is provided because using char= causes tests to break due to CR/LF and LF differences + /// Currently only line= is provided because using char= causes tests to break due to CR/LF and LF differences. /// public string Pointer { get; set; } } diff --git a/src/LEGO.AsyncAPI/Expressions/BodyExpression.cs b/src/LEGO.AsyncAPI/Expressions/BodyExpression.cs index 202812a5..2e8fe5d7 100644 --- a/src/LEGO.AsyncAPI/Expressions/BodyExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/BodyExpression.cs @@ -8,12 +8,12 @@ namespace LEGO.AsyncAPI.Expressions public sealed class BodyExpression : SourceExpression { /// - /// body string + /// body string. /// public const string Body = "body"; /// - /// Prefix for a pointer + /// Prefix for a pointer. /// public const string PointerPrefix = "#"; diff --git a/src/LEGO.AsyncAPI/Expressions/CompositeExpression.cs b/src/LEGO.AsyncAPI/Expressions/CompositeExpression.cs index 22ef38da..62e1a823 100644 --- a/src/LEGO.AsyncAPI/Expressions/CompositeExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/CompositeExpression.cs @@ -7,7 +7,7 @@ namespace LEGO.AsyncAPI.Expressions using System.Text.RegularExpressions; /// - /// String literal with embedded expressions + /// String literal with embedded expressions. /// public class CompositeExpression : RuntimeExpression { @@ -15,12 +15,12 @@ public class CompositeExpression : RuntimeExpression private Regex expressionPattern = new Regex(@"{(?\$[^}]*)"); /// - /// Expressions embedded into string literal + /// Expressions embedded into string literal. /// public List ContainedExpressions = new List(); /// - /// Create a composite expression from a string literal with an embedded expression + /// Create a composite expression from a string literal with an embedded expression. /// /// public CompositeExpression(string expression) @@ -38,7 +38,7 @@ public CompositeExpression(string expression) } /// - /// Return original string literal with embedded expression + /// Return original string literal with embedded expression. /// public override string Expression => this.template; } diff --git a/src/LEGO.AsyncAPI/Expressions/HeaderExpression.cs b/src/LEGO.AsyncAPI/Expressions/HeaderExpression.cs index 753b1218..ebee9cd6 100644 --- a/src/LEGO.AsyncAPI/Expressions/HeaderExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/HeaderExpression.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Expressions public class HeaderExpression : SourceExpression { /// - /// header. string + /// header. string. /// public const string Header = "header."; diff --git a/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs b/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs index b9403b07..95404d68 100644 --- a/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Expressions public sealed class MethodExpression : RuntimeExpression { /// - /// $method. string + /// $method. string. /// public const string Method = "$method"; diff --git a/src/LEGO.AsyncAPI/Expressions/PathExpression.cs b/src/LEGO.AsyncAPI/Expressions/PathExpression.cs index de6eab39..8b89565f 100644 --- a/src/LEGO.AsyncAPI/Expressions/PathExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/PathExpression.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Expressions public sealed class PathExpression : SourceExpression { /// - /// path. string + /// path. string. /// public const string Path = "path."; diff --git a/src/LEGO.AsyncAPI/Expressions/QueryExpression.cs b/src/LEGO.AsyncAPI/Expressions/QueryExpression.cs index 4d415f0c..7aebbb8e 100644 --- a/src/LEGO.AsyncAPI/Expressions/QueryExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/QueryExpression.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Expressions public sealed class QueryExpression : SourceExpression { /// - /// query. string + /// query. string. /// public const string Query = "query."; diff --git a/src/LEGO.AsyncAPI/Expressions/RequestExpression.cs b/src/LEGO.AsyncAPI/Expressions/RequestExpression.cs index 8b18b0ab..47850bf9 100644 --- a/src/LEGO.AsyncAPI/Expressions/RequestExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/RequestExpression.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Expressions public sealed class RequestExpression : RuntimeExpression { /// - /// $request. string + /// $request. string. /// public const string Request = "$request."; diff --git a/src/LEGO.AsyncAPI/Expressions/ResponseExpression.cs b/src/LEGO.AsyncAPI/Expressions/ResponseExpression.cs index d276f4c7..8a335209 100644 --- a/src/LEGO.AsyncAPI/Expressions/ResponseExpression.cs +++ b/src/LEGO.AsyncAPI/Expressions/ResponseExpression.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Expressions public sealed class ResponseExpression : RuntimeExpression { /// - /// $response. string + /// $response. string. /// public const string Response = "$response."; diff --git a/src/LEGO.AsyncAPI/Extensions/AsyncApiElementExtensions.cs b/src/LEGO.AsyncAPI/Extensions/AsyncApiElementExtensions.cs index 309a8b64..1215b86b 100644 --- a/src/LEGO.AsyncAPI/Extensions/AsyncApiElementExtensions.cs +++ b/src/LEGO.AsyncAPI/Extensions/AsyncApiElementExtensions.cs @@ -10,15 +10,15 @@ namespace LEGO.AsyncAPI.Extensions using LEGO.AsyncAPI.Validations; /// - /// Extension methods that apply across all AsyncAPIElements + /// Extension methods that apply across all AsyncAPIElements. /// public static class AsyncApiElementExtensions { /// - /// Validate element and all child elements + /// Validate element and all child elements. /// - /// Element to validate - /// Optional set of rules to use for validation + /// Element to validate. + /// Optional set of rules to use for validation. /// An IEnumerable of errors. This function will never return null. public static IEnumerable Validate(this IAsyncApiElement element, ValidationRuleSet ruleSet) { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIDouble.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIDouble.cs index dc54727e..e200cc83 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIDouble.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIDouble.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Models.Any using LEGO.AsyncAPI.Models.Interfaces; /// - /// AsyncApi Double + /// AsyncApi Double. /// public class AsyncApiDouble : AsyncApiPrimitive { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIString.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIString.cs index 389545e2..772b2b85 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIString.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIString.cs @@ -1,5 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Models.Any { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiByte.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiByte.cs index 511e3d7b..91f96b56 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiByte.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncApiByte.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Models.Any using LEGO.AsyncAPI.Models.Interfaces; /// - /// AsyncApi Byte + /// AsyncApi Byte. /// public class AsyncApiByte : AsyncApiPrimitive { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiDate.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiDate.cs index a4c3a063..ef223535 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiDate.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncApiDate.cs @@ -6,7 +6,7 @@ namespace LEGO.AsyncAPI.Models.Any using LEGO.AsyncAPI.Models.Interfaces; /// - /// AsyncApi Date + /// AsyncApi Date. /// public class AsyncApiDate : AsyncApiPrimitive { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiDateTime.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiDateTime.cs index 1bf213c6..e93fe59e 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiDateTime.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncApiDateTime.cs @@ -6,7 +6,7 @@ namespace LEGO.AsyncAPI.Models.Any using LEGO.AsyncAPI.Models.Interfaces; /// - /// AsyncApi Datetime + /// AsyncApi Datetime. /// public class AsyncApiDateTime : AsyncApiPrimitive { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiFloat.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiFloat.cs index 03ca5cb2..a824e75c 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiFloat.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncApiFloat.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Models.Any using LEGO.AsyncAPI.Models.Interfaces; /// - /// AsyncApi Float + /// AsyncApi Float. /// public class AsyncApiFloat : AsyncApiPrimitive { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiInteger.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiInteger.cs index 33d2b9d7..e9453a89 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiInteger.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncApiInteger.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Models.Any using LEGO.AsyncAPI.Models.Interfaces; /// - /// AsyncApi Integer + /// AsyncApi Integer. /// public class AsyncApiInteger : AsyncApiPrimitive { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiOAuthFlow.cs b/src/LEGO.AsyncAPI/Models/AsyncApiOAuthFlow.cs index 77650dc1..5144fe10 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiOAuthFlow.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiOAuthFlow.cs @@ -37,7 +37,7 @@ public class AsyncApiOAuthFlow : IAsyncApiSerializable, IAsyncApiExtensible public IDictionary Extensions { get; set; } = new Dictionary(); /// - /// Serialize to Async Api v2.4 + /// Serialize to Async Api v2.4. /// public void SerializeV2(IAsyncApiWriter writer) { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiOAuthFlows.cs b/src/LEGO.AsyncAPI/Models/AsyncApiOAuthFlows.cs index 17fabac7..a24f19ad 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiOAuthFlows.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiOAuthFlows.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Models public class AsyncApiOAuthFlows : IAsyncApiSerializable, IAsyncApiExtensible { /// - /// Configuration for the OAuth Implicit flow + /// Configuration for the OAuth Implicit flow. /// public AsyncApiOAuthFlow Implicit { get; set; } @@ -38,7 +38,7 @@ public class AsyncApiOAuthFlows : IAsyncApiSerializable, IAsyncApiExtensible public IDictionary Extensions { get; set; } = new Dictionary(); /// - /// Serialize to Async Api v2.4 + /// Serialize to Async Api v2.4. /// public void SerializeV2(IAsyncApiWriter writer) { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiOperation.cs b/src/LEGO.AsyncAPI/Models/AsyncApiOperation.cs index 5a55f387..ae3a6ff6 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiOperation.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiOperation.cs @@ -29,7 +29,7 @@ public class AsyncApiOperation : IAsyncApiSerializable, IAsyncApiExtensible public string Description { get; set; } /// - /// 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. Only one of the security requirement objects need to be satisfied to authorize a connection or operation + /// 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. Only one of the security requirement objects need to be satisfied to authorize a connection or operation. /// public IList Security { get; set; } = new List(); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSecurityRequirement.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSecurityRequirement.cs index 889ce3bb..475497d2 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSecurityRequirement.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSecurityRequirement.cs @@ -44,7 +44,7 @@ public void SerializeV2(IAsyncApiWriter writer) continue; } - //securityScheme.SerializeV2(writer); + // securityScheme.SerializeV2(writer); writer.WritePropertyName(securityScheme.Reference.Id); writer.WriteStartArray(); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSecurityScheme.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSecurityScheme.cs index 3374fb9f..08fd3d67 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSecurityScheme.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSecurityScheme.cs @@ -58,7 +58,7 @@ public class AsyncApiSecurityScheme : IAsyncApiSerializable, IAsyncApiReferencea public IDictionary Extensions { get; set; } = new Dictionary(); /// - /// Indicates if object is populated with data or is just a reference to the data + /// Indicates if object is populated with data or is just a reference to the data. /// public bool UnresolvedReference { get; set; } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs index 1339fd6c..8d444df0 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs @@ -14,7 +14,7 @@ public static class AsyncApiSerializableExtensions /// /// Serialize the to the AsyncApi document (JSON) using the given stream and specification version. /// - /// the + /// the . /// The AsyncApi element. /// The output stream. /// The AsyncApi specification version. @@ -27,7 +27,7 @@ public static void SerializeAsJson(this T element, Stream stream, AsyncApiVer /// /// Serializes the to the AsyncApi document (YAML) using the given stream and specification version. /// - /// the + /// the . /// The AsyncApi element. /// The output stream. /// The AsyncApi specification version. @@ -41,7 +41,7 @@ public static void SerializeAsYaml(this T element, Stream stream, AsyncApiVer /// Serializes the to the AsyncApi document using /// the given stream, specification version and the format. /// - /// the + /// the . /// The AsyncApi element. /// The given stream. /// The AsyncApi specification version. @@ -60,12 +60,12 @@ public static void Serialize( /// Serializes the to the AsyncApi document using /// the given stream, specification version and the format. /// - /// the + /// the . /// The AsyncApi element. /// The given stream. /// The AsyncApi specification version. /// The output format (JSON or YAML). - /// Provide configuration settings for controlling writing output + /// Provide configuration settings for controlling writing output. public static void Serialize( this T element, Stream stream, @@ -93,14 +93,14 @@ public static void Serialize( /// /// Serializes the to AsyncApi document using the given specification version and writer. /// - /// the + /// the . /// The AsyncApi element. /// The output writer. /// The specification version. /// /// element /// or - /// writer + /// writer. /// /// specification version '{specificationVersion}' is not supported. public static void Serialize(this T element, IAsyncApiWriter writer, AsyncApiVersion specificationVersion) @@ -124,13 +124,14 @@ public static void Serialize(this T element, IAsyncApiWriter writer, AsyncApi default: throw new AsyncApiException($"specification version '{specificationVersion}' is not supported."); } + writer.Flush(); } /// /// Serializes the to the AsyncApi document as a string in JSON format. /// - /// the + /// the . /// The AsyncApi element. /// The AsyncApi specification version. public static string SerializeAsJson( @@ -144,7 +145,7 @@ public static string SerializeAsJson( /// /// Serializes the to the AsyncApi document as a string in YAML format. /// - /// the + /// the . /// The AsyncApi element. /// The AsyncApi specification version. public static string SerializeAsYaml( @@ -158,7 +159,7 @@ public static string SerializeAsYaml( /// /// Serializes the to the AsyncApi document as a string in the given format. /// - /// the + /// the . /// The AsyncApi element. /// The AsyncApi specification version. /// AsyncApi document format. diff --git a/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiReferenceable.cs b/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiReferenceable.cs index c3cdd7fa..9f0bc64c 100644 --- a/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiReferenceable.cs +++ b/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiReferenceable.cs @@ -7,7 +7,7 @@ namespace LEGO.AsyncAPI.Models.Interfaces public interface IAsyncApiReferenceable : IAsyncApiSerializable { /// - /// Indicates if object is populated with data or is just a reference to the data + /// Indicates if object is populated with data or is just a reference to the data. /// bool UnresolvedReference { get; set; } diff --git a/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs b/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs index 1e9817af..6c7e1428 100644 --- a/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs +++ b/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs @@ -7,7 +7,7 @@ namespace LEGO.AsyncAPI.Models using LEGO.AsyncAPI.Writers; /// - /// The wrapper either for or + /// The wrapper either for or . /// public class RuntimeExpressionAnyWrapper : IAsyncApiElement { @@ -15,7 +15,7 @@ public class RuntimeExpressionAnyWrapper : IAsyncApiElement private RuntimeExpression expression; /// - /// Gets/Sets the + /// Gets/Sets the . /// public IAsyncApiAny Any { @@ -32,7 +32,7 @@ public IAsyncApiAny Any } /// - /// Gets/Set the + /// Gets/Set the . /// public RuntimeExpression Expression { @@ -49,7 +49,7 @@ public RuntimeExpression Expression } /// - /// Write + /// Write . /// public void WriteValue(IAsyncApiWriter writer) { diff --git a/src/LEGO.AsyncAPI/Models/SchemaType.cs b/src/LEGO.AsyncAPI/Models/SchemaType.cs index 4fc9ec0c..33e56e22 100644 --- a/src/LEGO.AsyncAPI/Models/SchemaType.cs +++ b/src/LEGO.AsyncAPI/Models/SchemaType.cs @@ -3,7 +3,6 @@ namespace LEGO.AsyncAPI.Models { using System; - using System.Collections.Generic; using LEGO.AsyncAPI.Attributes; [Flags] @@ -30,18 +29,4 @@ public enum SchemaType [Display("integer")] Integer = 64, } - - public static class SchemaTypeHelpers - { - public static IEnumerable GetFlags(SchemaType input) - { - foreach (SchemaType value in System.Enum.GetValues(input.GetType())) - { - if (input.HasFlag(value)) - { - yield return value; - } - } - } - } } diff --git a/src/LEGO.AsyncAPI/Models/SchemaTypeHelpers.cs b/src/LEGO.AsyncAPI/Models/SchemaTypeHelpers.cs new file mode 100644 index 00000000..a24a00fb --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/SchemaTypeHelpers.cs @@ -0,0 +1,20 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System.Collections.Generic; + + public static class SchemaTypeHelpers + { + public static IEnumerable GetFlags(SchemaType input) + { + foreach (SchemaType value in System.Enum.GetValues(input.GetType())) + { + if (input.HasFlag(value)) + { + yield return value; + } + } + } + } +} diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs index c1e45d6a..ca345b3f 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs @@ -10,7 +10,7 @@ namespace LEGO.AsyncAPI.Services using LEGO.AsyncAPI.Models.Interfaces; /// - /// This class is used to walk an AsyncApiDocument and convert unresolved references to references to populated objects + /// This class is used to walk an AsyncApiDocument and convert unresolved references to references to populated objects. /// internal class AsyncApiReferenceResolver : AsyncApiVisitorBase { @@ -119,7 +119,7 @@ public override void Visit(AsyncApiSecurityRequirement securityRequirement) } /// - /// Resolve all references to parameters + /// Resolve all references to parameters. /// public override void Visit(IList parameters) { @@ -127,7 +127,7 @@ public override void Visit(IList parameters) } /// - /// Resolve all references used in a parameter + /// Resolve all references used in a parameter. /// public override void Visit(AsyncApiParameter parameter) { @@ -135,7 +135,7 @@ public override void Visit(AsyncApiParameter parameter) } /// - /// Resolve all references used in a schema + /// Resolve all references used in a schema. /// public override void Visit(AsyncApiSchema schema) { diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs index bac5482f..7a025ddb 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs @@ -7,8 +7,9 @@ namespace LEGO.AsyncAPI.Services using System.Linq; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; + /// - /// AsyncApi visitor base provides common logic for concrete visitors + /// AsyncApi visitor base provides common logic for concrete visitors. /// public abstract class AsyncApiVisitorBase { @@ -22,7 +23,7 @@ public abstract class AsyncApiVisitorBase /// /// Allow Rule to indicate validation error occured at a deeper context level. /// - /// Identifier for context + /// Identifier for context. public void Enter(string segment) { this.path.Push(segment); @@ -37,80 +38,82 @@ public void Exit() } /// - /// Pointer to source of validation error in document + /// Pointer to source of validation error in document. /// public string PathString { get { - return "#/" + String.Join("/", this.path.Reverse()); + return "#/" + string.Join("/", this.path.Reverse()); } } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiDocument doc) { } public virtual void Visit(IDictionary anys) - { } + { + } public virtual void Visit(IList traits) - { } + { + } + /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiInfo info) { } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiContact contact) { } - /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiLicense license) { } /// - /// Visits list of + /// Visits list of . /// public virtual void Visit(IList servers) { } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiServer server) { } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiServerVariable serverVariable) { } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiOperation operation) { } /// - /// Visits list of + /// Visits list of . /// public virtual void Visit(IList parameters) { @@ -121,29 +124,28 @@ public virtual void Visit(IDictionary parameters) } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiParameter parameter) { } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiComponents components) { } - /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiExternalDocumentation externalDocs) { } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiSchema schema) { @@ -158,78 +160,80 @@ public virtual void Visit(IList messages) } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiTag tag) { } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiOAuthFlow asyncApiOAuthFlow) { } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiSecurityRequirement securityRequirement) { } /// - /// Visits + /// Visits . /// public virtual void Visit(AsyncApiSecurityScheme securityScheme) { } /// - /// Visits list of + /// Visits list of . /// - public virtual void Visit(IList AsyncApiTags) + public virtual void Visit(IList asyncApiTags) { } /// - /// Visits list of + /// Visits list of . /// - public virtual void Visit(IList AsyncApiSecurityRequirements) + public virtual void Visit(IList asyncApiSecurityRequirements) { } /// - /// Visits + /// Visits . /// - public virtual void Visit(IAsyncApiExtensible AsyncApiExtensible) + public virtual void Visit(IAsyncApiExtensible asyncApiExtensible) { } public virtual void Visit(AsyncApiCorrelationId correlationId) - { } + { + } public virtual void Visit(AsyncApiMessageTrait trait) - { } + { + } /// - /// Visits + /// Visits . /// - public virtual void Visit(IAsyncApiExtension AsyncApiExtension) + public virtual void Visit(IAsyncApiExtension asyncApiExtension) { } /// - /// Visits a dictionary of server variables + /// Visits a dictionary of server variables. /// public virtual void Visit(IDictionary serverVariables) { } /// - /// Visits IAsyncApiReferenceable instances that are references and not in components + /// Visits IAsyncApiReferenceable instances that are references and not in components. /// - /// referenced object + /// referenced object. public virtual void Visit(IAsyncApiReferenceable referencable) { } diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs index 1e0f7330..9a2184ed 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs @@ -10,7 +10,7 @@ namespace LEGO.AsyncAPI.Services public class AsyncApiWalker { private readonly AsyncApiVisitorBase visitor; - private readonly Stack schemaLoop = new(); + private readonly Stack schemaLoop = new (); public AsyncApiWalker(AsyncApiVisitorBase visitor) { diff --git a/src/LEGO.AsyncAPI/Validation/AsyncApiValidationError.cs b/src/LEGO.AsyncAPI/Validation/AsyncApiValidationError.cs index 863607bf..614bc1fe 100644 --- a/src/LEGO.AsyncAPI/Validation/AsyncApiValidationError.cs +++ b/src/LEGO.AsyncAPI/Validation/AsyncApiValidationError.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Validations using LEGO.AsyncAPI.Models; /// - /// Errors detected when validating an AsyncApi Element + /// Errors detected when validating an AsyncApi Element. /// public class AsyncApiValidatorError : AsyncApiError { diff --git a/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs b/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs index 0e9a567e..4ce96627 100644 --- a/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs +++ b/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs @@ -18,7 +18,7 @@ public class AsyncApiValidator : AsyncApiVisitorBase, IValidationContext private readonly IList warnings = new List(); /// - /// Create a vistor that will validate an AsyncApiDocument + /// Create a vistor that will validate an AsyncApiDocument. /// /// public AsyncApiValidator(ValidationRuleSet ruleSet) @@ -77,81 +77,81 @@ public void AddWarning(AsyncApiValidatorWarning warning) } /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(AsyncApiDocument item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(AsyncApiInfo item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(AsyncApiContact item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(AsyncApiComponents item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(AsyncApiLicense item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(AsyncApiOAuthFlow item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(AsyncApiTag item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(AsyncApiParameter item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(AsyncApiSchema item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(AsyncApiServer item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(IAsyncApiExtensible item) => this.Validate(item); /// - /// Execute validation rules against an + /// Execute validation rules against an . /// - /// The object to be validated + /// The object to be validated. public override void Visit(IAsyncApiExtension item) => this.Validate(item, item.GetType()); /// - /// Execute validation rules against a list of + /// Execute validation rules against a list of . /// - /// The object to be validated + /// The object to be validated. public override void Visit(IList items) => this.Validate(items, items.GetType()); private void Validate(T item) diff --git a/src/LEGO.AsyncAPI/Validation/AsyncApiValidatorWarning.cs b/src/LEGO.AsyncAPI/Validation/AsyncApiValidatorWarning.cs index f0d5eb08..98304250 100644 --- a/src/LEGO.AsyncAPI/Validation/AsyncApiValidatorWarning.cs +++ b/src/LEGO.AsyncAPI/Validation/AsyncApiValidatorWarning.cs @@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI.Validations using LEGO.AsyncAPI.Models; /// - /// Warnings detected when validating an AsyncApi Element + /// Warnings detected when validating an AsyncApi Element. /// public class AsyncApiValidatorWarning : AsyncApiError { diff --git a/src/LEGO.AsyncAPI/Validation/IValidationContext.cs b/src/LEGO.AsyncAPI/Validation/IValidationContext.cs index fdb658a1..51ce0977 100644 --- a/src/LEGO.AsyncAPI/Validation/IValidationContext.cs +++ b/src/LEGO.AsyncAPI/Validation/IValidationContext.cs @@ -3,7 +3,7 @@ namespace LEGO.AsyncAPI.Validations { /// - /// Constrained interface used to provide context to rule implementation + /// Constrained interface used to provide context to rule implementation. /// public interface IValidationContext { @@ -22,7 +22,7 @@ public interface IValidationContext /// /// Allow Rule to indicate validation error occured at a deeper context level. /// - /// Identifier for context + /// Identifier for context. void Enter(string segment); /// @@ -31,7 +31,7 @@ public interface IValidationContext void Exit(); /// - /// Pointer to source of validation error in document + /// Pointer to source of validation error in document. /// string PathString { get; } } diff --git a/src/LEGO.AsyncAPI/Validation/ValidationExtensions.cs b/src/LEGO.AsyncAPI/Validation/ValidationExtensions.cs index 46c2c5d2..eb2e2deb 100644 --- a/src/LEGO.AsyncAPI/Validation/ValidationExtensions.cs +++ b/src/LEGO.AsyncAPI/Validation/ValidationExtensions.cs @@ -3,12 +3,12 @@ namespace LEGO.AsyncAPI.Validations { /// - /// Helper methods to simplify creating validation rules + /// Helper methods to simplify creating validation rules. /// public static class ValidationContextExtensions { /// - /// Helper method to simplify validation rules + /// Helper method to simplify validation rules. /// public static void CreateError(this IValidationContext context, string ruleName, string message) { @@ -17,7 +17,7 @@ public static void CreateError(this IValidationContext context, string ruleName, } /// - /// Helper method to simplify validation rules + /// Helper method to simplify validation rules. /// public static void CreateWarning(this IValidationContext context, string ruleName, string message) { diff --git a/src/LEGO.AsyncAPI/Validation/ValidationRule.cs b/src/LEGO.AsyncAPI/Validation/ValidationRule.cs index d2e48704..a6b1b0b3 100644 --- a/src/LEGO.AsyncAPI/Validation/ValidationRule.cs +++ b/src/LEGO.AsyncAPI/Validation/ValidationRule.cs @@ -1,10 +1,8 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Validations { using System; - using LEGO.AsyncAPI.Models.Interfaces; /// /// Class containing validation rule logic. @@ -23,48 +21,4 @@ public abstract class ValidationRule /// The object item. internal abstract void Evaluate(IValidationContext context, object item); } - - /// - /// Class containing validation rule logic for . - /// - /// - public class ValidationRule : ValidationRule where T : IAsyncApiElement - { - private readonly Action validate; - - /// - /// Initializes a new instance of the class. - /// - /// Action to perform the validation. - public ValidationRule(Action validate) - { - this.validate = validate ?? throw Error.ArgumentNull(nameof(validate)); - } - - internal override Type ElementType - { - get { return typeof(T); } - } - - internal override void Evaluate(IValidationContext context, object item) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (item == null) - { - return; - } - - if (!(item is T)) - { - throw Error.Argument(string.Format("Input type must be of type {0}", typeof(T).FullName)); - } - - T typedItem = (T)item; - this.validate(context, typedItem); - } - } } diff --git a/src/LEGO.AsyncAPI/Validation/ValidationRuleSet.cs b/src/LEGO.AsyncAPI/Validation/ValidationRuleSet.cs index 83490856..2ea5d0f3 100644 --- a/src/LEGO.AsyncAPI/Validation/ValidationRuleSet.cs +++ b/src/LEGO.AsyncAPI/Validation/ValidationRuleSet.cs @@ -1,5 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Validations { @@ -22,9 +21,9 @@ public sealed class ValidationRuleSet : IEnumerable private IList emptyRules = new List(); /// - /// Retrieve the rules that are related to a specific type + /// Retrieve the rules that are related to a specific type. /// - /// The type that is to be validated + /// The type that is to be validated. /// Either the rules related to the type, or an empty list. public IList FindRules(Type type) { @@ -55,7 +54,7 @@ public static ValidationRuleSet GetDefaultRuleSet() } /// - /// Return Ruleset with no rules + /// Return Ruleset with no rules. /// public static ValidationRuleSet GetEmptyRuleSet() { diff --git a/src/LEGO.AsyncAPI/Validation/ValidationRule{T}.cs b/src/LEGO.AsyncAPI/Validation/ValidationRule{T}.cs new file mode 100644 index 00000000..6aecdb7a --- /dev/null +++ b/src/LEGO.AsyncAPI/Validation/ValidationRule{T}.cs @@ -0,0 +1,51 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Validations +{ + using System; + using LEGO.AsyncAPI.Models.Interfaces; + + /// + /// Class containing validation rule logic for . + /// + /// + public class ValidationRule : ValidationRule where T : IAsyncApiElement + { + private readonly Action validate; + + /// + /// Initializes a new instance of the class. + /// + /// Action to perform the validation. + public ValidationRule(Action validate) + { + this.validate = validate ?? throw Error.ArgumentNull(nameof(validate)); + } + + internal override Type ElementType + { + get { return typeof(T); } + } + + internal override void Evaluate(IValidationContext context, object item) + { + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (item == null) + { + return; + } + + if (!(item is T)) + { + throw Error.Argument(string.Format("Input type must be of type {0}", typeof(T).FullName)); + } + + T typedItem = (T)item; + this.validate(context, typedItem); + } + } +} diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs index 15f8c06e..4f31e62d 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs @@ -10,7 +10,7 @@ namespace LEGO.AsyncAPI.Writers public static class AsyncApiWriterAnyExtensions { /// - /// Write the specification extensions + /// Write the specification extensions. /// /// The AsyncApi writer. /// The specification extensions. @@ -37,7 +37,7 @@ public static void WriteExtensions(this IAsyncApiWriter writer, IDictionary /// The AsyncApi Any type. /// The AsyncApi writer. - /// The Any value + /// The Any value. public static void WriteAny(this IAsyncApiWriter writer, T any) where T : IAsyncApiAny { if (writer is null) diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs index 86c94be4..471db021 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs @@ -384,7 +384,7 @@ private bool IsScopeType(ScopeType type) /// Verifies whether a property name can be written based on whether /// the property name is a valid string and whether the current scope is an object scope. /// - /// property name + /// property name. protected void VerifyCanWritePropertyName(string name) { if (string.IsNullOrWhiteSpace(name)) diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs index ec9c96b6..08025c13 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs @@ -124,7 +124,7 @@ public static void WriteRequiredProperty(this IAsyncApiWriter writer, string /// /// Write the optional AsyncApi object/element. /// - /// The AsyncApi element type. + /// The AsyncApi element type. . /// The AsyncApi writer. /// The property name. /// The property value. @@ -157,7 +157,7 @@ public static void WriteOptionalObject( /// /// Write the required AsyncApi object/element. /// - /// The AsyncApi element type. + /// The AsyncApi element type. . /// The AsyncApi writer. /// The property name. /// The property value. @@ -205,7 +205,7 @@ public static void WriteOptionalCollection( /// /// Write the optional AsyncApi object/element collection. /// - /// The AsyncApi element type. + /// The AsyncApi element type. . /// The AsyncApi writer. /// The property name. /// The collection values. @@ -226,7 +226,7 @@ public static void WriteOptionalCollection( /// /// Write the required AsyncApi object/element collection. /// - /// The AsyncApi element type. + /// The AsyncApi element type. . /// The AsyncApi writer. /// The property name. /// The collection values. @@ -291,7 +291,7 @@ public static void WriteRequiredMap( /// /// Write the optional AsyncApi element map. /// - /// The AsyncApi element type. + /// The AsyncApi element type. . /// The AsyncApi writer. /// The property name. /// The map values. @@ -312,7 +312,7 @@ public static void WriteOptionalMap( /// /// Write the optional AsyncApi element map. /// - /// The AsyncApi element type. + /// The AsyncApi element type. . /// The AsyncApi writer. /// The property name. /// The map values. @@ -333,7 +333,7 @@ public static void WriteOptionalMap( /// /// Write the required AsyncApi element map. /// - /// The AsyncApi element type. + /// The AsyncApi element type. . /// The AsyncApi writer. /// The property name. /// The map values. diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs index dc834c48..5749e0d1 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs @@ -26,7 +26,7 @@ public AsyncApiYamlWriter(TextWriter textWriter, AsyncApiWriterSettings settings } /// - /// Allow rendering of multi-line strings using YAML | syntax + /// Allow rendering of multi-line strings using YAML | syntax. /// public bool UseLiteralStyle { get; set; } diff --git a/src/LEGO.AsyncAPI/Writers/AsyncJsonWriterSettings.cs b/src/LEGO.AsyncAPI/Writers/AsyncJsonWriterSettings.cs index d29a6a8b..662cb89a 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncJsonWriterSettings.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncJsonWriterSettings.cs @@ -8,7 +8,8 @@ public class AsyncJsonWriterSettings : AsyncApiWriterSettings /// Initializes a new instance of the class. /// public AsyncJsonWriterSettings() - { } + { + } /// /// Indicates whether or not the produced document will be written in a compact or pretty fashion. diff --git a/src/LEGO.AsyncAPI/Writers/WriterConstants.cs b/src/LEGO.AsyncAPI/Writers/WriterConstants.cs index f6bde088..be76ff29 100644 --- a/src/LEGO.AsyncAPI/Writers/WriterConstants.cs +++ b/src/LEGO.AsyncAPI/Writers/WriterConstants.cs @@ -86,27 +86,27 @@ internal static class WriterConstants internal const string NameValueSeparatorWhiteSpaceSuffix = " "; /// - /// The white space for empty object + /// The white space for empty object. /// internal const string WhiteSpaceForEmptyObject = " "; /// - /// The white space for empty array + /// The white space for empty array. /// internal const string WhiteSpaceForEmptyArray = " "; /// - /// The prefix of array item + /// The prefix of array item. /// internal const string PrefixOfArrayItem = "- "; /// - /// The white space for indent + /// The white space for indent. /// internal const string WhiteSpaceForIndent = " "; /// - /// Empty object + /// Empty object. /// /// To indicate empty object in YAML. internal const string EmptyObject = "{ }"; diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 706fa8f2..592e7f19 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -842,7 +842,7 @@ public void SerializeV2_WithFullSpec_Serializes() string anyKey = "key"; string anyOtherKey = "otherKey"; string anyStringValue = "value"; - long anyLongValue = Int64.MaxValue; + long anyLongValue = long.MaxValue; string exampleSummary = "exampleSummary"; string exampleName = "exampleName"; string traitDescription = "traitDescription"; diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs index 5bf7379c..ab7df131 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs @@ -3,7 +3,6 @@ namespace LEGO.AsyncAPI.Tests.Bindings { using System.Collections.Generic; - using Extensions; using FluentAssertions; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Models; @@ -20,7 +19,7 @@ public class NestedConfiguration : IAsyncApiExtensible public IDictionary Extensions { get; set; } = new Dictionary(); - public static FixedFieldMap fixedFieldMap = new() + public static FixedFieldMap FixedFieldMap = new () { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, }; @@ -49,7 +48,7 @@ public class MyBinding : ChannelBinding { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "custom", (a, n) => { a.Custom = n.GetScalarValue(); } }, { "any", (a, n) => { a.Any = n.CreateAny(); } }, - { "nestedConfiguration", (a, n) => { a.NestedConfiguration = n.ParseMapWithExtensions(NestedConfiguration.fixedFieldMap); } }, + { "nestedConfiguration", (a, n) => { a.NestedConfiguration = n.ParseMapWithExtensions(NestedConfiguration.FixedFieldMap); } }, }; public override void SerializeProperties(IAsyncApiWriter writer) From 4a93c7a26dbc0dd28914ac96575070deb0a6d2c1 Mon Sep 17 00:00:00 2001 From: Dec Kolakowski <51292634+dpwdec@users.noreply.github.com> Date: Fri, 9 Jun 2023 12:25:54 +0100 Subject: [PATCH 10/84] feat(bindings): add SQS AWS Bindings (#113) Co-authored-by: Alex Wichmann Co-authored-by: UlrikSandberg Co-authored-by: Alex W. Carlsen Co-authored-by: Goker Akce Co-authored-by: Goker Akce Co-authored-by: Dec Kolakowski --- .../BindingsCollection.cs | 8 + src/LEGO.AsyncAPI.Bindings/Sqs/Identifier.cs | 30 ++ src/LEGO.AsyncAPI.Bindings/Sqs/Policy.cs | 30 ++ src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs | 82 ++++ .../Sqs/RedrivePolicy.cs | 36 ++ .../Sqs/SqsChannelBinding.cs | 81 ++++ .../Sqs/SqsOperationBinding.cs | 72 +++ src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs | 50 ++ .../StringOrStringList.cs | 48 ++ .../ParseNodes/AsyncApiAnyConverter.cs | 2 +- .../ParseNodes/ListNode.cs | 2 +- .../V2/AsyncApiDeserializer.cs | 9 + .../V2/AsyncApiMessageDeserializer.cs | 4 +- .../V2/ExtensionHelpers.cs | 2 +- .../Rules/AsyncApiOAuthFlowRules.cs | 2 +- .../Writers/AsyncApiWriterException.cs | 2 +- .../AsyncApiDocumentBuilder.cs | 2 +- .../AsyncApiDocumentV2Tests.cs | 2 +- .../Bindings/Kafka/KafkaBindings_Should.cs | 2 +- .../Bindings/Sqs/SqsBindings_should.cs | 450 ++++++++++++++++++ .../Bindings/StringOrStringList_Should.cs | 150 ++++++ 21 files changed, 1056 insertions(+), 10 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/Identifier.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/Policy.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs create mode 100644 test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs create mode 100644 test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs index b201a4dc..9c729597 100644 --- a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -1,5 +1,7 @@ // Copyright (c) The LEGO Group. All rights reserved. +using LEGO.AsyncAPI.Bindings.Sqs; + namespace LEGO.AsyncAPI.Bindings { using System; @@ -67,5 +69,11 @@ public static TCollection Add( new PulsarServerBinding(), new PulsarChannelBinding(), }; + + public static IEnumerable> Sqs => new List> + { + new SqsChannelBinding(), + new SqsOperationBinding(), + }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Identifier.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Identifier.cs new file mode 100644 index 00000000..d7d95dcc --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Identifier.cs @@ -0,0 +1,30 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class Identifier : IAsyncApiExtensible + { + public string Arn { get; set; } + + public string Name { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty("arn", this.Arn); + writer.WriteOptionalProperty("name", this.Name); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Policy.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Policy.cs new file mode 100644 index 00000000..a989c239 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Policy.cs @@ -0,0 +1,30 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class Policy : IAsyncApiExtensible + { + /// + /// An array of statement objects, each of which controls a permission for this topic. + /// + public List Statements { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalCollection("statements", this.Statements, (w, t) => t.Serialize(w)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs new file mode 100644 index 00000000..f79e0ad3 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs @@ -0,0 +1,82 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + using Extensions; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + + public class Queue : IAsyncApiExtensible + { + /// + /// The name of the queue. When an SNS Operation Binding Object references an SQS queue by name, the identifier should be the one in this field. + /// + public string Name { get; set; } + + /// + /// Is this a FIFO queue? + /// + public bool FifoQueue { get; set; } + + /// + /// The number of seconds to delay before a message sent to the queue can be received. used to create a delay queue. + /// + public int? DeliveryDelay { get; set; } + + /// + /// The length of time, in seconds, that a consumer locks a message - hiding it from reads - before it is unlocked and can be read again. + /// + public int? VisibilityTimeout { get; set; } + + /// + /// Determines if the queue uses short polling or long polling. Set to zero the queue reads available messages and returns immediately. Set to a non-zero integer, long polling waits the specified number of seconds for messages to arrive before returning. + /// + public int? ReceiveMessageWaitTime { get; set; } + + /// + /// How long to retain a message on the queue in seconds, unless deleted. + /// + public int? MessageRetentionPeriod { get; set; } + + /// + /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. + /// + public RedrivePolicy RedrivePolicy { get; set; } + + /// + /// The security policy for the SQS Queue + /// + public Policy Policy { get; set; } + + /// + /// Key-value pairs that represent AWS tags on the topic. + /// + public Dictionary Tags { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("name", this.Name); + writer.WriteOptionalProperty("fifoQueue", this.FifoQueue); + writer.WriteOptionalProperty("deliveryDelay", this.DeliveryDelay); + writer.WriteOptionalProperty("visibilityTimeout", this.VisibilityTimeout); + writer.WriteOptionalProperty("receiveMessageWaitTime", this.ReceiveMessageWaitTime); + writer.WriteOptionalProperty("messageRetentionPeriod", this.MessageRetentionPeriod); + writer.WriteOptionalObject("redrivePolicy", this.RedrivePolicy, (w, p) => p.Serialize(w)); + writer.WriteOptionalObject("policy", this.Policy, (w, p) => p.Serialize(w)); + writer.WriteOptionalMap("tags", this.Tags, (w, t) => w.WriteValue(t)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs new file mode 100644 index 00000000..4222ee45 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs @@ -0,0 +1,36 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs +{ + using System; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + using System.Collections.Generic; + + public class RedrivePolicy : IAsyncApiExtensible + { + /// + /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. + /// + public Identifier DeadLetterQueue { get; set; } + + /// + /// The number of times a message is delivered to the source queue before being moved to the dead-letter queue. + /// + public int? MaxReceiveCount { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredObject("deadLetterQueue", this.DeadLetterQueue, (w, q) => q.Serialize(w)); + writer.WriteOptionalProperty("maxReceiveCount", this.MaxReceiveCount); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs new file mode 100644 index 00000000..2142b105 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs @@ -0,0 +1,81 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs +{ + using System; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// This object contains information about the channel representation in SQS. + /// + public class SqsChannelBinding : ChannelBinding + { + /// + /// A definition of the queue that will be used as the channel. + /// + public Queue Queue { get; set; } + + /// + /// A definition of the queue that will be used for un-processable messages. + /// + public Queue DeadLetterQueue { get; set; } + + public override string BindingKey => "sqs"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "queue", (a, n) => { a.Queue = n.ParseMapWithExtensions(this.queueFixedFields); } }, + { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMapWithExtensions(this.queueFixedFields); } }, + }; + + private FixedFieldMap queueFixedFields => new() + { + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + { "fifoQueue", (a, n) => { a.FifoQueue = n.GetBooleanValue(); } }, + { "deliveryDelay", (a, n) => { a.DeliveryDelay = n.GetIntegerValue(); } }, + { "visibilityTimeout", (a, n) => { a.VisibilityTimeout = n.GetIntegerValue(); } }, + { "receiveMessageWaitTime", (a, n) => { a.ReceiveMessageWaitTime = n.GetIntegerValue(); } }, + { "messageRetentionPeriod", (a, n) => { a.MessageRetentionPeriod = n.GetIntegerValue(); } }, + { "redrivePolicy", (a, n) => { a.RedrivePolicy = n.ParseMapWithExtensions(this.redrivePolicyFixedFields); } }, + { "policy", (a, n) => { a.Policy = n.ParseMapWithExtensions(this.policyFixedFields); } }, + { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, + }; + + private FixedFieldMap redrivePolicyFixedFields => new() + { + { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMapWithExtensions(identifierFixFields); } }, + { "maxReceiveCount", (a, n) => { a.MaxReceiveCount = n.GetIntegerValue(); } }, + }; + + private static FixedFieldMap identifierFixFields => new() + { + { "arn", (a, n) => { a.Arn = n.GetScalarValue(); } }, + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + }; + + private FixedFieldMap policyFixedFields = new() + { + { "statements", (a, n) => { a.Statements = n.CreateList(s => s.ParseMapWithExtensions(statementFixedFields)); } }, + }; + + private static FixedFieldMap statementFixedFields = new() + { + { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } }, + { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, + }; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredObject("queue", this.Queue, (w, q) => q.Serialize(w)); + writer.WriteOptionalObject("deadLetterQueue", this.DeadLetterQueue, (w, q) => q.Serialize(w)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs new file mode 100644 index 00000000..9aff5a90 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs @@ -0,0 +1,72 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + public class SqsOperationBinding : OperationBinding + { + /// + /// Queue objects that are either the endpoint for an SNS Operation Binding Object, or the deadLetterQueue of the SQS Operation Binding Object + /// + public List Queues { get; set; } + + public override string BindingKey => "sqs"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "queues", (a, n) => { a.Queues = n.CreateList(s => s.ParseMapWithExtensions(this.queueFixedFields)); } }, + }; + + private FixedFieldMap queueFixedFields => new() + { + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + { "fifoQueue", (a, n) => { a.FifoQueue = n.GetBooleanValue(); } }, + { "deliveryDelay", (a, n) => { a.DeliveryDelay = n.GetIntegerValue(); } }, + { "visibilityTimeout", (a, n) => { a.VisibilityTimeout = n.GetIntegerValue(); } }, + { "receiveMessageWaitTime", (a, n) => { a.ReceiveMessageWaitTime = n.GetIntegerValue(); } }, + { "messageRetentionPeriod", (a, n) => { a.MessageRetentionPeriod = n.GetIntegerValue(); } }, + { "redrivePolicy", (a, n) => { a.RedrivePolicy = n.ParseMapWithExtensions(this.redrivePolicyFixedFields); } }, + { "policy", (a, n) => { a.Policy = n.ParseMapWithExtensions(this.policyFixedFields); } }, + { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, + }; + + private FixedFieldMap redrivePolicyFixedFields => new() + { + { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMapWithExtensions(identifierFixFields); } }, + { "maxReceiveCount", (a, n) => { a.MaxReceiveCount = n.GetIntegerValue(); } }, + }; + + private static FixedFieldMap identifierFixFields => new() + { + { "arn", (a, n) => { a.Arn = n.GetScalarValue(); } }, + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + }; + + private FixedFieldMap policyFixedFields = new() + { + { "statements", (a, n) => { a.Statements = n.CreateList(s => s.ParseMapWithExtensions(statementFixedFields)); } }, + }; + + private static FixedFieldMap statementFixedFields = new() + { + { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } }, + { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, + }; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredCollection("queues", this.Queues, (w, t) => t.Serialize(w)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs new file mode 100644 index 00000000..508e4a33 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs @@ -0,0 +1,50 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class Statement : IAsyncApiExtensible + { + + public Effect Effect { get; set; } + + /// + /// The AWS account or resource ARN that this statement applies to. + /// + // public StringOrStringList Principal { get; set; } + public StringOrStringList Principal { get; set; } + + /// + /// The SNS permission being allowed or denied e.g. sns:Publish + /// + public StringOrStringList Action { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } + + public enum Effect + { + [Display("allow")] + Allow, + [Display("deny")] + Deny, + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs new file mode 100644 index 00000000..31d9c749 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs @@ -0,0 +1,48 @@ +using System; +using System.Linq; +using LEGO.AsyncAPI.Models.Any; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers.ParseNodes; + +namespace LEGO.AsyncAPI.Bindings +{ + public class StringOrStringList : IAsyncApiElement + { + public StringOrStringList(IAsyncApiAny value) + { + this.Value = value switch + { + AsyncApiArray array => IsValidStringList(array) ? array : throw new ArgumentException($"{nameof(StringOrStringList)} value should only contain string items."), + AsyncApiPrimitive => value, + _ => throw new ArgumentException($"{nameof(StringOrStringList)} should be a string value or a string list.") + }; + } + + public IAsyncApiAny Value { get; } + + public static StringOrStringList Parse(ParseNode node) + { + switch (node) + { + case ValueNode: + return new StringOrStringList(new AsyncApiString(node.GetScalarValue())); + case ListNode: + { + var asyncApiArray = new AsyncApiArray(); + asyncApiArray.AddRange(node.CreateSimpleList(s => new AsyncApiString(s.GetScalarValue()))); + + return new StringOrStringList(asyncApiArray); + } + + default: + throw new ArgumentException($"An error occured while parsing a {nameof(StringOrStringList)} node. " + + $"Node should contain a string value or a list of strings."); + } + } + + private static bool IsValidStringList(AsyncApiArray array) + { + return array.All(x => x is AsyncApiPrimitive); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs index a9b3ab79..4bd72a19 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs @@ -245,4 +245,4 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn return asyncApiAny; } } -} +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs index c402936e..17c7a592 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs @@ -11,7 +11,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using LEGO.AsyncAPI.Readers.Exceptions; using YamlDotNet.RepresentationModel; - internal class ListNode : ParseNode, IEnumerable + public class ListNode : ParseNode, IEnumerable { private readonly YamlSequenceNode nodeList; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs index 5ebbc49b..98fed324 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs @@ -1,9 +1,18 @@ // Copyright (c) The LEGO Group. All rights reserved. +using System.Collections.Generic; +using System.Linq; +using LEGO.AsyncAPI.Exceptions; +using LEGO.AsyncAPI.Expressions; +using LEGO.AsyncAPI.Models; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers.ParseNodes; + namespace LEGO.AsyncAPI.Readers { using System.Collections.Generic; using System.Linq; + using Extensions; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Expressions; using LEGO.AsyncAPI.Models; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs index 63d0512c..4c16bf22 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs @@ -2,12 +2,12 @@ namespace LEGO.AsyncAPI.Readers { - using System.Collections.Generic; - using System.Linq; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Extensions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.ParseNodes; + using System.Collections.Generic; + using System.Linq; /// /// Class containing logic to deserialize AsyncApi document into diff --git a/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs b/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs index 64e5508e..08036d04 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs @@ -40,4 +40,4 @@ public static IAsyncApiExtension LoadExtension(string name, ParseNode node) return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); } } -} +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs index 073066da..e1649d4e 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs @@ -2,9 +2,9 @@ namespace LEGO.AsyncAPI.Validation.Rules { - using System.Linq; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Validations; + using System.Linq; [AsyncApiRule] public static class AsyncApiOAuthFlowRules diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs index 17229818..9d89bba3 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterException.cs @@ -2,8 +2,8 @@ namespace LEGO.AsyncAPI.Writers { - using System; using LEGO.AsyncAPI.Exceptions; + using System; public class AsyncApiWriterException : AsyncApiException { diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs index f800a1f7..3f2ee429 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs @@ -2,9 +2,9 @@ namespace LEGO.AsyncAPI.Tests { - using System; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; + using System; internal class AsyncApiDocumentBuilder { diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 706fa8f2..f016a3f4 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -1319,4 +1319,4 @@ public void Serializev2_WithBindings_Serializes() Assert.AreEqual("this mah binding", httpBinding.Headers.Description); } } -} +} \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index 22bf8d2d..537e904c 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Kafka { - using System.Collections.Generic; using FluentAssertions; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Kafka; @@ -10,6 +9,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Kafka using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Readers; using NUnit.Framework; + using System.Collections.Generic; internal class KafkaBindings_Should { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs new file mode 100644 index 00000000..b2cd0eb2 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -0,0 +1,450 @@ +namespace LEGO.AsyncAPI.Tests.Bindings.Sqs +{ + using System.Collections.Generic; + using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Sqs; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Any; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; + using NUnit.Framework; + using BindingsCollection = LEGO.AsyncAPI.Bindings.BindingsCollection; + + internal class SqsBindings_should + { + [Test] + public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = + @"bindings: + sqs: + queue: + name: myQueue + fifoQueue: true + deliveryDelay: 30 + visibilityTimeout: 60 + receiveMessageWaitTime: 0 + messageRetentionPeriod: 86400 + redrivePolicy: + deadLetterQueue: + arn: arn:aws:SQS:eu-west-1:0000000:123456789 + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue + maxReceiveCount: 15 + x-redrivePolicyExtension: + redrivePolicyXPropertyName: redrivePolicyXPropertyValue + policy: + statements: + - effect: deny + principal: arn:aws:iam::123456789012:user/alex.wichmann + action: + - sqs:SendMessage + - sqs:ReceiveMessage + x-statementExtension: + statementXPropertyName: statementXPropertyValue + - effect: allow + principal: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski + action: sqs:CreateQueue + x-policyExtension: + policyXPropertyName: policyXPropertyValue + tags: + owner: AsyncAPI.NET + platform: AsyncAPIOrg + x-queueExtension: + queueXPropertyName: queueXPropertyValue + deadLetterQueue: + name: myQueue_error + deliveryDelay: 0 + visibilityTimeout: 0 + receiveMessageWaitTime: 0 + messageRetentionPeriod: 604800 + policy: + statements: + - effect: allow + principal: arn:aws:iam::123456789012:user/alex.wichmann + action: + - sqs:* + x-internalObject: + myExtensionPropertyName: myExtensionPropertyValue"; + + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new SqsChannelBinding() + { + Queue = new Queue() + { + Name = "myQueue", + FifoQueue = true, + DeliveryDelay = 30, + VisibilityTimeout = 60, + ReceiveMessageWaitTime = 0, + MessageRetentionPeriod = 86400, + RedrivePolicy = new RedrivePolicy() + { + DeadLetterQueue = new Identifier() + { + Arn = "arn:aws:SQS:eu-west-1:0000000:123456789", + Extensions = new Dictionary() + { + { + "x-identifierExtension", + new AsyncApiObject() + { + { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + } + }, + }, + }, + MaxReceiveCount = 15, + Extensions = new Dictionary() + { + { + "x-redrivePolicyExtension", + new AsyncApiObject() + { + { "redrivePolicyXPropertyName", new AsyncApiString("redrivePolicyXPropertyValue") }, + } + }, + }, + }, + Policy = new Policy() + { + Statements = new List() + { + new Statement() + { + Effect = Effect.Deny, + Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Action = new StringOrStringList(new AsyncApiArray() + { + new AsyncApiString("sqs:SendMessage"), + new AsyncApiString("sqs:ReceiveMessage") + }), + Extensions = new Dictionary() + { + { + "x-statementExtension", + new AsyncApiObject() + { + { "statementXPropertyName", new AsyncApiString("statementXPropertyValue") }, + } + }, + }, + }, + new Statement() + { + Effect = Effect.Allow, + Principal = new StringOrStringList(new AsyncApiArray + { + new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann"), + new AsyncApiString("arn:aws:iam::123456789012:user/dec.kolakowski") + }), + Action = new StringOrStringList(new AsyncApiString("sqs:CreateQueue")), + }, + }, + Extensions = new Dictionary() + { + { + "x-policyExtension", + new AsyncApiObject() + { + { "policyXPropertyName", new AsyncApiString("policyXPropertyValue") }, + } + }, + }, + }, + Tags = new Dictionary() + { + { "owner", "AsyncAPI.NET" }, + { "platform", "AsyncAPIOrg" }, + }, + Extensions = new Dictionary() + { + { + "x-queueExtension", + new AsyncApiObject() + { + { "queueXPropertyName", new AsyncApiString("queueXPropertyValue") }, + } + }, + }, + }, + DeadLetterQueue = new Queue() + { + Name = "myQueue_error", + FifoQueue = false, + DeliveryDelay = 0, + VisibilityTimeout = 0, + ReceiveMessageWaitTime = 0, + MessageRetentionPeriod = 604800, + Policy = new Policy() + { + Statements = new List() + { + new Statement() + { + Effect = Effect.Allow, + Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Action = new StringOrStringList(new AsyncApiArray() + { + new AsyncApiString("sqs:*") + }) + }, + }, + }, + }, + Extensions = new Dictionary() + { + { + "x-internalObject", new AsyncApiObject() + { + { "myExtensionPropertyName", new AsyncApiString("myExtensionPropertyValue") }, + } + }, + }, + }); + + // Act + var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Sqs); + var binding = + new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, + out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(channel); + } + + [Test] + public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = + @"bindings: + sqs: + queues: + - name: myQueue + fifoQueue: true + deliveryDelay: 30 + visibilityTimeout: 60 + receiveMessageWaitTime: 0 + messageRetentionPeriod: 86400 + redrivePolicy: + deadLetterQueue: + arn: arn:aws:SQS:eu-west-1:0000000:123456789 + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue + maxReceiveCount: 15 + x-redrivePolicyExtension: + redrivePolicyXPropertyName: redrivePolicyXPropertyValue + policy: + statements: + - effect: deny + principal: arn:aws:iam::123456789012:user/alex.wichmann + action: + - sqs:SendMessage + - sqs:ReceiveMessage + x-statementExtension: + statementXPropertyName: statementXPropertyValue + - effect: allow + principal: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski + action: sqs:CreateQueue + x-policyExtension: + policyXPropertyName: policyXPropertyValue + tags: + owner: AsyncAPI.NET + platform: AsyncAPIOrg + x-queueExtension: + queueXPropertyName: queueXPropertyValue + - name: myQueue_error + deliveryDelay: 0 + visibilityTimeout: 0 + receiveMessageWaitTime: 0 + messageRetentionPeriod: 604800 + policy: + statements: + - effect: allow + principal: arn:aws:iam::123456789012:user/alex.wichmann + action: + - sqs:* + x-queueExtension: + queueXPropertyName: queueXPropertyValue + x-internalObject: + myExtensionPropertyName: myExtensionPropertyValue"; + + var operation = new AsyncApiOperation(); + operation.Bindings.Add(new SqsOperationBinding() + { + Queues = new List() + { + new Queue() + { + Name = "myQueue", + FifoQueue = true, + DeliveryDelay = 30, + VisibilityTimeout = 60, + ReceiveMessageWaitTime = 0, + MessageRetentionPeriod = 86400, + RedrivePolicy = new RedrivePolicy() + { + DeadLetterQueue = new Identifier() + { + Arn = "arn:aws:SQS:eu-west-1:0000000:123456789", + Extensions = new Dictionary() + { + { + "x-identifierExtension", + new AsyncApiObject() + { + { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + } + }, + }, + }, + MaxReceiveCount = 15, + Extensions = new Dictionary() + { + { + "x-redrivePolicyExtension", + new AsyncApiObject() + { + { "redrivePolicyXPropertyName", new AsyncApiString("redrivePolicyXPropertyValue") }, + } + }, + }, + }, + Policy = new Policy() + { + Statements = new List() + { + new Statement() + { + Effect = Effect.Deny, + Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Action = new StringOrStringList(new AsyncApiArray() + { + new AsyncApiString("sqs:SendMessage"), + new AsyncApiString("sqs:ReceiveMessage") + }), + Extensions = new Dictionary() + { + { + "x-statementExtension", + new AsyncApiObject() + { + { "statementXPropertyName", new AsyncApiString("statementXPropertyValue") }, + } + }, + }, + }, + new Statement() + { + Effect = Effect.Allow, + Principal = new StringOrStringList(new AsyncApiArray + { + new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann"), + new AsyncApiString("arn:aws:iam::123456789012:user/dec.kolakowski"), + }), + Action = new StringOrStringList(new AsyncApiString("sqs:CreateQueue")) + }, + }, + Extensions = new Dictionary() + { + { + "x-policyExtension", + new AsyncApiObject() + { + { "policyXPropertyName", new AsyncApiString("policyXPropertyValue") }, + } + }, + }, + }, + Tags = new Dictionary() + { + { "owner", "AsyncAPI.NET" }, + { "platform", "AsyncAPIOrg" }, + }, + Extensions = new Dictionary() + { + { + "x-queueExtension", + new AsyncApiObject() + { + { "queueXPropertyName", new AsyncApiString("queueXPropertyValue") }, + } + }, + }, + }, + new Queue() + { + Name = "myQueue_error", + FifoQueue = false, + DeliveryDelay = 0, + VisibilityTimeout = 0, + ReceiveMessageWaitTime = 0, + MessageRetentionPeriod = 604800, + Policy = new Policy() + { + Statements = new List() + { + new Statement() + { + Effect = Effect.Allow, + Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Action = new StringOrStringList(new AsyncApiArray + { + new AsyncApiString("sqs:*") + }) + }, + }, + }, + Extensions = new Dictionary() + { + { + "x-queueExtension", + new AsyncApiObject() + { + { "queueXPropertyName", new AsyncApiString("queueXPropertyValue") }, + } + }, + }, + }, + }, + Extensions = new Dictionary() + { + { + "x-internalObject", new AsyncApiObject() + { + { "myExtensionPropertyName", new AsyncApiString("myExtensionPropertyValue") }, + } + }, + }, + }); + + // Act + var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Sqs); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(operation); + } + } +} \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs new file mode 100644 index 00000000..9b6958a5 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs @@ -0,0 +1,150 @@ +namespace LEGO.AsyncAPI.Tests.Bindings +{ + using System; + using System.Collections.Generic; + using System.Linq; + using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Any; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + using NUnit.Framework; + + public class StringOrStringList_Should + { + + [Test] + public void StringOrStringList_IsInitialised_WhenPassedStringOrStringList() + { + // Arrange + var stringValue = new StringOrStringList(new AsyncApiString("AsyncApi")); + var listValue = new StringOrStringList( + new AsyncApiArray() + { + new AsyncApiString("Async"), + new AsyncApiString("Api"), + }); + + // Assert + (stringValue.Value as AsyncApiString).Value.Should().Be("AsyncApi"); + (listValue.Value as AsyncApiArray) + .Select(s => (s as AsyncApiString).Value) + .Should().BeEquivalentTo(new List() { "Async", "Api" }); + } + + [Test] + public void StringOrStringList_ThrowsArgumentException_WhenIntialisedWithoutStringOrStringList() + { + // Assert + var ex = Assert.Throws(() => new StringOrStringList(new AsyncApiBoolean(true))); + + // Assert + ex.Message.Should().Be("StringOrStringList should be a string value or a string list."); + } + + [Test] + public void StringOrStringList_ThrowsArgumentException_WhenIntialisedWithListOfNonStrings() + { + // Assert + var ex = Assert.Throws(() => new StringOrStringList( + new AsyncApiArray() + { + new AsyncApiString("x"), + new AsyncApiInteger(1), + new AsyncApiString("y"), + })); + + // Assert + ex.Message.Should().Be("StringOrStringList value should only contain string items."); + } + + [Test] + public void StringOrStringList_WhenValueIsString_SerializesDeserializes() + { + // Arrange + var expected = @"bindings: + testBinding: + testProperty: someValue"; + + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new StringOrStringListTestBinding + { + TestProperty = new StringOrStringList(new AsyncApiString("someValue")), + }); + + // Act + var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(new StringOrStringListTestBinding()); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(channel); + } + + [Test] + public void StringOrStringList_WhenValueIsStringList_SerializesDeserializes() + { + // Arrange + var expected = @"bindings: + testBinding: + testProperty: + - someValue01 + - someValue02 + - someValue03"; + + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new StringOrStringListTestBinding + { + TestProperty = new StringOrStringList(new AsyncApiArray + { + new AsyncApiString("someValue01"), + new AsyncApiString("someValue02"), + new AsyncApiString("someValue03"), + }), + }); + + // Act + var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(new StringOrStringListTestBinding()); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(channel); + } + } + + public class StringOrStringListTestBinding : ChannelBinding + { + public StringOrStringList TestProperty { get; set; } + + public override string BindingKey => "testBinding"; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + writer.WriteStartObject(); + writer.WriteRequiredObject("testProperty", this.TestProperty, (w, t) => t.Value.Write(w)); + writer.WriteEndObject(); + } + + protected override FixedFieldMap FixedFieldMap => new () + { + { "testProperty", (a, n) => { a.TestProperty = new StringOrStringList(n.CreateAny()); } }, + }; + } +} \ No newline at end of file From d48f1669ebfd9ad3f661b2b5928df1d622a4e7ba Mon Sep 17 00:00:00 2001 From: Dec Kolakowski <51292634+dpwdec@users.noreply.github.com> Date: Mon, 12 Jun 2023 12:07:37 +0100 Subject: [PATCH 11/84] feat(bindings): add SNS AWS bindings (#108) Co-authored-by: Dec Kolakowski Co-authored-by: Alex Wichmann Co-authored-by: UlrikSandberg Co-authored-by: Alex W. Carlsen Co-authored-by: Goker Akce Co-authored-by: Goker Akce --- .../BindingsCollection.cs | 13 +- src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs | 80 ++++ .../Sns/DeliveryPolicy.cs | 81 ++++ .../Sns/FilterPolicy.cs | 30 ++ src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs | 39 ++ src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs | 45 ++ src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs | 31 ++ .../Sns/RedrivePolicy.cs | 36 ++ .../Sns/SnsChannelBinding.cs | 79 ++++ .../Sns/SnsOperationBinding.cs | 96 ++++ src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 51 +++ .../Bindings/Sns/SnsBindings_Should.cs | 409 ++++++++++++++++++ .../Bindings/Sqs/SqsBindings_should.cs | 4 +- 13 files changed, 989 insertions(+), 5 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs create mode 100644 test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs index 9c729597..e52392b6 100644 --- a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -1,7 +1,4 @@ // Copyright (c) The LEGO Group. All rights reserved. - -using LEGO.AsyncAPI.Bindings.Sqs; - namespace LEGO.AsyncAPI.Bindings { using System; @@ -9,6 +6,8 @@ namespace LEGO.AsyncAPI.Bindings using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Bindings.Pulsar; + using LEGO.AsyncAPI.Bindings.Sns; + using LEGO.AsyncAPI.Bindings.Sqs; using LEGO.AsyncAPI.Bindings.WebSockets; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Interface; @@ -43,6 +42,8 @@ public static TCollection Add( Kafka, Http, Websockets, + Sqs, + Sns, }; public static IEnumerable> Http => new List> @@ -75,5 +76,11 @@ public static TCollection Add( new SqsChannelBinding(), new SqsOperationBinding(), }; + + public static IEnumerable> Sns => new List> + { + new SnsChannelBinding(), + new SnsOperationBinding(), + }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs new file mode 100644 index 00000000..46548977 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs @@ -0,0 +1,80 @@ +namespace LEGO.AsyncAPI.Bindings.Sns +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class Consumer : IAsyncApiExtensible + { + /// + /// The protocol that this endpoint will receive messages by. + /// + public Protocol Protocol { get; set; } + + /// + /// The endpoint messages are delivered to. + /// + public Identifier Endpoint { get; set; } + + /// + /// Only receive a subset of messages from the channel, determined by this policy. + /// + public FilterPolicy FilterPolicy { get; set; } + + /// + /// If true AWS SNS attributes are removed from the body, and for SQS, SNS message attributes are copied to SQS message attributes. If false the SNS attributes are included in the body. + /// + public bool RawMessageDelivery { get; set; } + + /// + /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. + /// + public RedrivePolicy RedrivePolicy { get; set; } + + /// + /// Policy for retries to HTTP. The parameter is for that SNS Subscription and overrides any policy on the SNS Topic. + /// + public DeliveryPolicy DeliveryPolicy { get; set; } + + /// + /// The display name to use with an SNS subscription. + /// + public string DisplayName { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("protocol", this.Protocol.GetDisplayName()); + writer.WriteRequiredObject("endpoint", this.Endpoint, (w, e) => e.Serialize(w)); + writer.WriteOptionalObject("filterPolicy", this.FilterPolicy, (w, f) => f.Serialize(w)); + writer.WriteRequiredProperty("rawMessageDelivery", this.RawMessageDelivery); + writer.WriteOptionalObject("redrivePolicy", this.RedrivePolicy, (w, p) => p.Serialize(w)); + writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); + writer.WriteOptionalProperty("displayName", this.DisplayName); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } + + public enum Protocol + { + [Display("http")] Http, + [Display("https")] Https, + [Display("email")] Email, + [Display("email-json")] EmailJson, + [Display("sms")] Sms, + [Display("sqs")] Sqs, + [Display("application")] Application, + [Display("lambda")] Lambda, + [Display("firehose")] Firehose, + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs new file mode 100644 index 00000000..f9421ca9 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs @@ -0,0 +1,81 @@ +namespace LEGO.AsyncAPI.Bindings.Sns +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class DeliveryPolicy : IAsyncApiExtensible + { + /// + /// The minimum delay for a retry in seconds. + /// + public int? MinDelayTarget { get; set; } + + /// + /// The maximum delay for a retry in seconds. + /// + public int? MaxDelayTarget { get; set; } + + /// + /// The total number of retries, including immediate, pre-backoff, backoff, and post-backoff retries. + /// + public int? NumRetries { get; set; } + + /// + /// The number of immediate retries (with no delay). + /// + public int? NumNoDelayRetries { get; set; } + + /// + /// The number of immediate retries (with delay). + /// + public int? NumMinDelayRetries { get; set; } + + /// + /// The number of post-backoff phase retries, with the maximum delay between retries. + /// + public int? NumMaxDelayRetries { get; set; } + + /// + /// The algorithm for backoff between retries. + /// + public BackoffFunction BackoffFunction { get; set; } + + /// + /// The maximum number of deliveries per second, per subscription. + /// + public int? MaxReceivesPerSecond { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty("minDelayTarget", this.MinDelayTarget); + writer.WriteOptionalProperty("maxDelayTarget", this.MaxDelayTarget); + writer.WriteOptionalProperty("numRetries", this.NumRetries); + writer.WriteOptionalProperty("numNoDelayRetries", this.NumNoDelayRetries); + writer.WriteOptionalProperty("numMinDelayRetries", this.NumMinDelayRetries); + writer.WriteOptionalProperty("numMaxDelayRetries", this.NumMaxDelayRetries); + writer.WriteOptionalProperty("backoffFunction", this.BackoffFunction.GetDisplayName()); + writer.WriteOptionalProperty("maxReceivesPerSecond", this.MaxReceivesPerSecond); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } + + public enum BackoffFunction + { + [Display("arithmetic")] Arithmetic, + [Display("exponential")] Exponential, + [Display("geometric")] Geometric, + [Display("linear")] Linear, + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs new file mode 100644 index 00000000..47530cc0 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs @@ -0,0 +1,30 @@ +namespace LEGO.AsyncAPI.Bindings.Sns +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class FilterPolicy : IAsyncApiExtensible + { + /// + /// A map of a message attribute to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint. + /// + public IAsyncApiAny Attributes { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredObject("attributes", this.Attributes, (w, a) => w.WriteAny(a)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs new file mode 100644 index 00000000..0e6466b0 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs @@ -0,0 +1,39 @@ +namespace LEGO.AsyncAPI.Bindings.Sns +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class Identifier : IAsyncApiExtensible + { + public string Url { get; set; } + + public string Email { get; set; } + + public string Phone { get; set; } + + public string Arn { get; set; } + + public string Name { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty("url", this.Url); + writer.WriteOptionalProperty("email", this.Email); + writer.WriteOptionalProperty("phone", this.Phone); + writer.WriteOptionalProperty("arn", this.Arn); + writer.WriteOptionalProperty("name", this.Name); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs new file mode 100644 index 00000000..4ed81eec --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs @@ -0,0 +1,45 @@ +namespace LEGO.AsyncAPI.Bindings.Sns +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class Ordering : IAsyncApiExtensible + { + /// + /// What type of SNS Topic is this? + /// + public OrderingType Type { get; set; } + + /// + /// True to turn on de-duplication of messages for a channel. + /// + public bool ContentBasedDeduplication { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("type", this.Type.GetDisplayName()); + writer.WriteOptionalProperty("contentBasedDeduplication", this.ContentBasedDeduplication); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } + + public enum OrderingType + { + [Display("standard")] + Standard, + [Display("FIFO")] + Fifo, + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs new file mode 100644 index 00000000..685b2f8d --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs @@ -0,0 +1,31 @@ +namespace LEGO.AsyncAPI.Bindings.Sns +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class Policy : IAsyncApiExtensible + { + /// + /// An array of statement objects, each of which controls a permission for this topic. + /// + public List Statements { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalCollection("statements", this.Statements, (w, t) => t.Serialize(w)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs new file mode 100644 index 00000000..4e5e6340 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs @@ -0,0 +1,36 @@ +namespace LEGO.AsyncAPI.Bindings.Sns +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + public class RedrivePolicy : IAsyncApiExtensible + { + /// + /// Prevent poison pill messages by moving un-processable messages to an SQS dead letter queue. + /// + public Identifier DeadLetterQueue { get; set; } + + /// + /// The number of times a message is delivered to the source queue before being moved to the dead-letter queue. + /// + public int? MaxReceiveCount { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredObject("deadLetterQueue", this.DeadLetterQueue, (w, q) => q.Serialize(w)); + writer.WriteOptionalProperty("maxReceiveCount", this.MaxReceiveCount); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs new file mode 100644 index 00000000..a0df69a9 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -0,0 +1,79 @@ +namespace LEGO.AsyncAPI.Bindings.Sns +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for SNS channel settings. + /// + public class SnsChannelBinding : ChannelBinding + { + /// + /// The name of the topic. Can be different from the channel name to allow flexibility around AWS resource naming limitations. + /// + public string Name { get; set; } + + /// + /// By default, we assume an unordered SNS topic. This field allows configuration of a FIFO SNS Topic. + /// + public Ordering Ordering { get; set; } + + /// + /// The security policy for the SNS Topic. + /// + public Policy Policy { get; set; } + + /// + /// Key-value pairs that represent AWS tags on the topic. + /// + public Dictionary Tags { get; set; } + + public override string BindingKey => "sns"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + { "type", (a, n) => { a.Ordering = n.ParseMapWithExtensions(this.orderingFixedFields); } }, + { "policy", (a, n) => { a.Policy = n.ParseMapWithExtensions(this.policyFixedFields); } }, + { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, + }; + + private FixedFieldMap orderingFixedFields = new() + { + { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "contentBasedDeduplication", (a, n) => { a.ContentBasedDeduplication = n.GetBooleanValue(); } }, + }; + + private FixedFieldMap policyFixedFields = new() + { + { "statements", (a, n) => { a.Statements = n.CreateList(s => s.ParseMapWithExtensions(statementFixedFields)); } }, + }; + + private static FixedFieldMap statementFixedFields = new() + { + { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } }, + { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, + }; + + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("name", this.Name); + writer.WriteOptionalObject("ordering", this.Ordering, (w, t) => t.Serialize(w)); + writer.WriteOptionalObject("policy", this.Policy, (w, t) => t.Serialize(w)); + writer.WriteOptionalMap("tags", this.Tags, (w, t) => w.WriteValue(t)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs new file mode 100644 index 00000000..d35a46f5 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -0,0 +1,96 @@ +namespace LEGO.AsyncAPI.Bindings.Sns +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models.Any; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// This object contains information about the operation representation in SNS. + /// + public class SnsOperationBinding : OperationBinding + { + /// + /// Often we can assume that the SNS Topic is the channel name-we provide this field in case the you need to supply the ARN, or the Topic name is not the channel name in the AsyncAPI document. + /// + public Identifier Topic { get; set; } + + /// + /// The protocols that listen to this topic and their endpoints. + /// + public List Consumers { get; set; } + + /// + /// Policy for retries to HTTP. The field is the default for HTTP receivers of the SNS Topic which may be overridden by a specific consumer. + /// + public DeliveryPolicy DeliveryPolicy { get; set; } + + public override string BindingKey => "sns"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "topic", (a, n) => { a.Topic = n.ParseMapWithExtensions(this.identifierFixFields); } }, + { "consumers", (a, n) => { a.Consumers = n.CreateList(s => s.ParseMapWithExtensions(this.consumerFixedFields)); } }, + { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMapWithExtensions(this.deliveryPolicyFixedFields); } }, + }; + + private FixedFieldMap identifierFixFields => new() + { + { "url", (a, n) => { a.Url = n.GetScalarValue(); } }, + { "email", (a, n) => { a.Email = n.GetScalarValue(); } }, + { "phone", (a, n) => { a.Phone = n.GetScalarValue(); } }, + { "arn", (a, n) => { a.Arn = n.GetScalarValue(); } }, + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + }; + + private FixedFieldMap consumerFixedFields => new () + { + { "protocol", (a, n) => { a.Protocol = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "endpoint", (a, n) => { a.Endpoint = n.ParseMapWithExtensions(this.identifierFixFields); } }, + { "filterPolicy", (a, n) => { a.FilterPolicy = n.ParseMapWithExtensions(this.filterPolicyFixedFields); } }, + { "rawMessageDelivery", (a, n) => { a.RawMessageDelivery = n.GetBooleanValue(); } }, + { "redrivePolicy", (a, n) => { a.RedrivePolicy = n.ParseMapWithExtensions(this.redrivePolicyFixedFields); } }, + { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMapWithExtensions(this.deliveryPolicyFixedFields); } }, + { "displayName", (a, n) => { a.DisplayName = n.GetScalarValue(); } }, + }; + + private FixedFieldMap filterPolicyFixedFields => new() + { + { "attributes", (a, n) => { a.Attributes = n.CreateAny(); } }, + }; + + private FixedFieldMap redrivePolicyFixedFields => new() + { + { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMapWithExtensions(identifierFixFields); } }, + { "maxReceiveCount", (a, n) => { a.MaxReceiveCount = n.GetIntegerValue(); } }, + }; + + private FixedFieldMap deliveryPolicyFixedFields => new() + { + { "minDelayTarget", (a, n) => { a.MinDelayTarget = n.GetIntegerValue(); } }, + { "maxDelayTarget", (a, n) => { a.MaxDelayTarget = n.GetIntegerValue(); } }, + { "numRetries", (a, n) => { a.NumRetries = n.GetIntegerValue(); } }, + { "numNoDelayRetries", (a, n) => { a.NumNoDelayRetries = n.GetIntegerValue(); } }, + { "numMinDelayRetries", (a, n) => { a.NumMinDelayRetries = n.GetIntegerValue(); } }, + { "numMaxDelayRetries", (a, n) => { a.NumMaxDelayRetries = n.GetIntegerValue(); } }, + { "backoffFunction", (a, n) => { a.BackoffFunction = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "maxReceivesPerSecond", (a, n) => { a.MaxReceivesPerSecond = n.GetIntegerValue(); } }, + }; + + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalObject("topic", this.Topic, (w, t) => t.Serialize(w)); + writer.WriteOptionalCollection("consumers", this.Consumers, (w, c) => c.Serialize(w)); + writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs new file mode 100644 index 00000000..c21ecc80 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -0,0 +1,51 @@ +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; + + public class Statement : IAsyncApiExtensible + { + + public Effect Effect { get; set; } + + /// + /// The AWS account or resource ARN that this statement applies to. + /// + // public StringOrStringList Principal { get; set; } + public StringOrStringList Principal { get; set; } + + /// + /// The SNS permission being allowed or denied e.g. sns:Publish + /// + public StringOrStringList Action { get; set; } + + public IDictionary Extensions { get; set; } = new Dictionary(); + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } + + public enum Effect + { + [Display("Allow")] + Allow, + [Display("Deny")] + Deny, + } +} \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs new file mode 100644 index 00000000..7a2269bb --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -0,0 +1,409 @@ +using System; +using LEGO.AsyncAPI.Models.Any; +using LEGO.AsyncAPI.Models.Interfaces; +using BindingsCollection = LEGO.AsyncAPI.Bindings.BindingsCollection; + +namespace LEGO.AsyncAPI.Tests.Bindings.Sns +{ + using NUnit.Framework; + using System.Collections.Generic; + using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.Sns; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + + internal class SnsBindings_Should + { + [Test] + public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = + @"bindings: + sns: + name: myTopic + ordering: + type: FIFO + contentBasedDeduplication: true + x-orderingExtension: + orderingXPropertyName: orderingXPropertyValue + policy: + statements: + - effect: Deny + principal: arn:aws:iam::123456789012:user/alex.wichmann + action: + - sns:Publish + - sns:Delete + - effect: Allow + principal: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski + action: sns:Create + x-statementExtension: + statementXPropertyName: statementXPropertyValue + x-policyExtension: + policyXPropertyName: policyXPropertyValue + tags: + owner: AsyncAPI.NET + platform: AsyncAPIOrg + x-bindingExtension: + bindingXPropertyName: bindingXPropertyValue"; + + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new SnsChannelBinding() + { + Name = "myTopic", + Ordering = new Ordering() + { + Type = OrderingType.Fifo, + ContentBasedDeduplication = true, + Extensions = new Dictionary() + { + { + "x-orderingExtension", + new AsyncApiObject() + { + { "orderingXPropertyName", new AsyncApiString("orderingXPropertyValue") }, + } + }, + }, + }, + Policy = new Policy() + { + Statements = new List() + { + new Statement() + { + Effect = Effect.Deny, + Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Action = new StringOrStringList(new AsyncApiArray() + { + new AsyncApiString("sns:Publish"), + new AsyncApiString("sns:Delete") + }), + }, + new Statement() + { + Effect = Effect.Allow, + Principal = new StringOrStringList(new AsyncApiArray() + { + new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann"), + new AsyncApiString("arn:aws:iam::123456789012:user/dec.kolakowski") + }), + Action = new StringOrStringList(new AsyncApiString("sns:Create")), + Extensions = new Dictionary() + { + { + "x-statementExtension", + new AsyncApiObject() + { + { "statementXPropertyName", new AsyncApiString("statementXPropertyValue") }, + } + }, + }, + }, + }, + Extensions = new Dictionary() + { + { + "x-policyExtension", + new AsyncApiObject() + { + { "policyXPropertyName", new AsyncApiString("policyXPropertyValue") }, + } + }, + }, + }, + Tags = new Dictionary() + { + { "owner", "AsyncAPI.NET" }, + { "platform", "AsyncAPIOrg" }, + }, + Extensions = new Dictionary() + { + { + "x-bindingExtension", + new AsyncApiObject() + { + { "bindingXPropertyName", new AsyncApiString("bindingXPropertyValue") }, + } + }, + }, + }); + + // Act + var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Sns); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + + // Assert + Assert.AreEqual(actual, expected); + binding.Should().BeEquivalentTo(channel); + + } + + [Test] + public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = + @"bindings: + sns: + topic: + name: someTopic + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue + consumers: + - protocol: sqs + endpoint: + name: someQueue + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue + filterPolicy: + attributes: + store: + - asyncapi_corp + contact: dec.kolakowski + event: + - anything-but: order_cancelled + order_key: + transient: by_area + customer_interests: + - rugby + - football + - baseball + x-filterPolicyExtension: + filterPolicyXPropertyName: filterPolicyXPropertyValue + rawMessageDelivery: false + redrivePolicy: + deadLetterQueue: + arn: arn:aws:SQS:eu-west-1:0000000:123456789 + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue + maxReceiveCount: 25 + x-redrivePolicyExtension: + redrivePolicyXPropertyName: redrivePolicyXPropertyValue + deliveryPolicy: + minDelayTarget: 10 + maxDelayTarget: 100 + numRetries: 5 + numNoDelayRetries: 2 + numMinDelayRetries: 3 + numMaxDelayRetries: 5 + backoffFunction: linear + maxReceivesPerSecond: 2 + x-deliveryPolicyExtension: + deliveryPolicyXPropertyName: deliveryPolicyXPropertyValue + x-consumerExtension: + consumerXPropertyName: consumerXPropertyValue + deliveryPolicy: + minDelayTarget: 10 + maxDelayTarget: 100 + numRetries: 5 + numNoDelayRetries: 2 + numMinDelayRetries: 3 + numMaxDelayRetries: 5 + backoffFunction: geometric + maxReceivesPerSecond: 10 + x-deliveryPolicyExtension: + deliveryPolicyXPropertyName: deliveryPolicyXPropertyValue + x-bindingExtension: + bindingXPropertyName: bindingXPropertyValue"; + + var operation = new AsyncApiOperation(); + operation.Bindings.Add(new SnsOperationBinding() + { + Topic = new Identifier() + { + Name = "someTopic", + Extensions = new Dictionary() + { + { + "x-identifierExtension", + new AsyncApiObject() + { + { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + } + }, + }, + }, + Consumers = new List() + { + new Consumer() + { + Protocol = Protocol.Sqs, + Endpoint = new Identifier() + { + Name = "someQueue", + Extensions = new Dictionary() + { + { + "x-identifierExtension", + new AsyncApiObject() + { + { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + } + }, + }, + }, + FilterPolicy = new FilterPolicy() + { + Attributes = new AsyncApiObject() + { + { "store", new AsyncApiArray() { new AsyncApiString("asyncapi_corp") } }, + { "contact", new AsyncApiString("dec.kolakowski") }, + { + "event", new AsyncApiArray() + { + new AsyncApiObject() + { + { "anything-but", new AsyncApiString("order_cancelled") }, + }, + } + }, + { + "order_key", new AsyncApiObject() + { + { "transient", new AsyncApiString("by_area") }, + } + }, + { + "customer_interests", new AsyncApiArray() + { + new AsyncApiString("rugby"), + new AsyncApiString("football"), + new AsyncApiString("baseball"), + } + }, + }, + Extensions = new Dictionary() + { + { + "x-filterPolicyExtension", + new AsyncApiObject() + { + { "filterPolicyXPropertyName", new AsyncApiString("filterPolicyXPropertyValue") }, + } + }, + }, + }, + RawMessageDelivery = false, + RedrivePolicy = new RedrivePolicy() + { + DeadLetterQueue = new Identifier() + { + Arn = "arn:aws:SQS:eu-west-1:0000000:123456789", + Extensions = new Dictionary() + { + { + "x-identifierExtension", + new AsyncApiObject() + { + { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + } + }, + }, + }, + MaxReceiveCount = 25, + Extensions = new Dictionary() + { + { + "x-redrivePolicyExtension", + new AsyncApiObject() + { + { "redrivePolicyXPropertyName", new AsyncApiString("redrivePolicyXPropertyValue") }, + } + }, + }, + }, + DeliveryPolicy = new DeliveryPolicy() + { + MinDelayTarget = 10, + MaxDelayTarget = 100, + NumRetries = 5, + NumNoDelayRetries = 2, + NumMinDelayRetries = 3, + NumMaxDelayRetries = 5, + BackoffFunction = BackoffFunction.Linear, + MaxReceivesPerSecond = 2, + Extensions = new Dictionary() + { + { + "x-deliveryPolicyExtension", + new AsyncApiObject() + { + { "deliveryPolicyXPropertyName", new AsyncApiString("deliveryPolicyXPropertyValue") }, + } + }, + }, + }, + Extensions = new Dictionary() + { + { + "x-consumerExtension", + new AsyncApiObject() + { + { "consumerXPropertyName", new AsyncApiString("consumerXPropertyValue") }, + } + }, + }, + }, + }, + DeliveryPolicy = new DeliveryPolicy() + { + MinDelayTarget = 10, + MaxDelayTarget = 100, + NumRetries = 5, + NumNoDelayRetries = 2, + NumMinDelayRetries = 3, + NumMaxDelayRetries = 5, + BackoffFunction = BackoffFunction.Geometric, + MaxReceivesPerSecond = 10, + Extensions = new Dictionary() + { + { + "x-deliveryPolicyExtension", + new AsyncApiObject() + { + { "deliveryPolicyXPropertyName", new AsyncApiString("deliveryPolicyXPropertyValue") }, + } + }, + }, + }, + Extensions = new Dictionary() + { + { + "x-bindingExtension", + new AsyncApiObject() + { + { "bindingXPropertyName", new AsyncApiString("bindingXPropertyValue") }, + } + }, + }, + }); + + // Act + var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings.Add(BindingsCollection.Sns); + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + + // Assert + Assert.AreEqual(actual, expected); + binding.Should().BeEquivalentTo(operation); + + } + } +} \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index b2cd0eb2..34c361e1 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -190,8 +190,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), Action = new StringOrStringList(new AsyncApiArray() { - new AsyncApiString("sqs:*") - }) + new AsyncApiString("sqs:*"), + }), }, }, }, From 6033c6d87747e9f4f10862ca3449a8844875d247 Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Mon, 12 Jun 2023 11:08:37 +0000 Subject: [PATCH 12/84] chore: update CHANGELOG.md --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f493bf3f..1a0eda89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +# [4.0.0](https://github.com/LEGO/AsyncAPI.NET/compare/v3.0.2...v4.0.0) (2023-06-12) + + +### Bug Fixes + +* add setter to BindingParsers collection ([211646e](https://github.com/LEGO/AsyncAPI.NET/commit/211646e95b82b3e32563fe75c57656cd6882267b)) + + +* feat(JsonSchema)!: type as flag rather than list (#115) ([d44efb0](https://github.com/LEGO/AsyncAPI.NET/commit/d44efb048402c70377064b87bd962b0e455e08b3)), closes [#115](https://github.com/LEGO/AsyncAPI.NET/issues/115) +* feat(JsonSchema)!: changed out decimal for double to allow for bigger numbers ([ab00976](https://github.com/LEGO/AsyncAPI.NET/commit/ab009764a916171c8926c129384ce18b3162e71e)) +* feat(Bindings)!: separate bindings and allow for custom bindings. (#107) ([d38c33f](https://github.com/LEGO/AsyncAPI.NET/commit/d38c33f14d6de73e2563e29534965b06d423edac)), closes [#107](https://github.com/LEGO/AsyncAPI.NET/issues/107) + + +### Features + +* **bindings:** add SNS AWS bindings ([#108](https://github.com/LEGO/AsyncAPI.NET/issues/108)) ([d48f166](https://github.com/LEGO/AsyncAPI.NET/commit/d48f1669ebfd9ad3f661b2b5928df1d622a4e7ba)) +* **bindings:** add SQS AWS Bindings ([#113](https://github.com/LEGO/AsyncAPI.NET/issues/113)) ([4a93c7a](https://github.com/LEGO/AsyncAPI.NET/commit/4a93c7a26dbc0dd28914ac96575070deb0a6d2c1)) + + +### BREAKING CHANGES + +* this changes the type of Type in JsonSchema to be a Flags enum, rather than a List of enum. +* this changes the type of 3 properties of JsonSchema from `decimal` to `double` +* Bindings have been moved to a separate project + ## [3.0.2](https://github.com/LEGO/AsyncAPI.NET/compare/v3.0.1...v3.0.2) (2023-03-30) From bc1569abf29b9b2748fb8187be11a3143b855b9d Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Mon, 12 Jun 2023 13:11:57 +0200 Subject: [PATCH 13/84] Update CHANGELOG.md --- CHANGELOG.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a0eda89..b4bdb69a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,21 +6,19 @@ * add setter to BindingParsers collection ([211646e](https://github.com/LEGO/AsyncAPI.NET/commit/211646e95b82b3e32563fe75c57656cd6882267b)) -* feat(JsonSchema)!: type as flag rather than list (#115) ([d44efb0](https://github.com/LEGO/AsyncAPI.NET/commit/d44efb048402c70377064b87bd962b0e455e08b3)), closes [#115](https://github.com/LEGO/AsyncAPI.NET/issues/115) -* feat(JsonSchema)!: changed out decimal for double to allow for bigger numbers ([ab00976](https://github.com/LEGO/AsyncAPI.NET/commit/ab009764a916171c8926c129384ce18b3162e71e)) -* feat(Bindings)!: separate bindings and allow for custom bindings. (#107) ([d38c33f](https://github.com/LEGO/AsyncAPI.NET/commit/d38c33f14d6de73e2563e29534965b06d423edac)), closes [#107](https://github.com/LEGO/AsyncAPI.NET/issues/107) - - ### Features +* **jsonschema**!: type as flag rather than list (#115) ([d44efb0](https://github.com/LEGO/AsyncAPI.NET/commit/d44efb048402c70377064b87bd962b0e455e08b3)), closes [#115](https://github.com/LEGO/AsyncAPI.NET/issues/115) +* **jsonschema**!: changed out decimal for double to allow for bigger numbers ([ab00976](https://github.com/LEGO/AsyncAPI.NET/commit/ab009764a916171c8926c129384ce18b3162e71e)) +* **bindings**!: separate bindings and allow for custom bindings. (#107) ([d38c33f](https://github.com/LEGO/AsyncAPI.NET/commit/d38c33f14d6de73e2563e29534965b06d423edac)), closes [#107](https://github.com/LEGO/AsyncAPI.NET/issues/107) * **bindings:** add SNS AWS bindings ([#108](https://github.com/LEGO/AsyncAPI.NET/issues/108)) ([d48f166](https://github.com/LEGO/AsyncAPI.NET/commit/d48f1669ebfd9ad3f661b2b5928df1d622a4e7ba)) * **bindings:** add SQS AWS Bindings ([#113](https://github.com/LEGO/AsyncAPI.NET/issues/113)) ([4a93c7a](https://github.com/LEGO/AsyncAPI.NET/commit/4a93c7a26dbc0dd28914ac96575070deb0a6d2c1)) ### BREAKING CHANGES -* this changes the type of Type in JsonSchema to be a Flags enum, rather than a List of enum. -* this changes the type of 3 properties of JsonSchema from `decimal` to `double` +* The type of `Type` in JsonSchema is now a Flags enum, rather than a List of enum. This provides an easier to use interface, for adding and checking types. +* 3 properties, previously of type `decimal` in the JsonSchema type have been changed to `double`. * Bindings have been moved to a separate project ## [3.0.2](https://github.com/LEGO/AsyncAPI.NET/compare/v3.0.1...v3.0.2) (2023-03-30) From 9e4867fbec9377964489e53c71f38a239e359cdf Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Thu, 29 Jun 2023 18:34:33 +0200 Subject: [PATCH 14/84] fix: add ability to have 'false' as the value for 'additionalproperties' (#118) Co-authored-by: UlrikSandberg --- .../V2/AsyncApiSchemaDeserializer.cs | 12 +++++++++++- src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs | 9 ++++++++- .../Models/JsonSchema/NoAdditionalProperties.cs | 12 ++++++++++++ .../Models/AsyncApiSchema_Should.cs | 6 ++++-- 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 src/LEGO.AsyncAPI/Models/JsonSchema/NoAdditionalProperties.cs diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index 911b7f77..6266ef1b 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -123,7 +123,17 @@ public class JsonSchemaDeserializer "properties", (a, n) => { a.Properties = n.CreateMap(LoadSchema); } }, { - "additionalProperties", (a, n) => { a.AdditionalProperties = LoadSchema(n); } + "additionalProperties", (a, n) => + { + if (n.GetBooleanValueOrDefault(null) == false) + { + a.AdditionalProperties = new NoAdditionalProperties(); + } + else + { + a.AdditionalProperties = LoadSchema(n); + } + } }, { "items", (a, n) => { a.Items = LoadSchema(n); } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs index c9282af3..1de5542b 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs @@ -356,7 +356,14 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.MinProperties, this.MinProperties); // additionalProperties - writer.WriteOptionalObject(AsyncApiConstants.AdditionalProperties, this.AdditionalProperties, (w, s) => s.SerializeV2(w)); + if (this.AdditionalProperties is NoAdditionalProperties) + { + writer.WriteOptionalProperty(AsyncApiConstants.AdditionalProperties, false); + } + else + { + writer.WriteOptionalObject(AsyncApiConstants.AdditionalProperties, this.AdditionalProperties, (w, s) => s.SerializeV2(w)); + } // discriminator writer.WriteOptionalProperty(AsyncApiConstants.Discriminator, this.Discriminator); diff --git a/src/LEGO.AsyncAPI/Models/JsonSchema/NoAdditionalProperties.cs b/src/LEGO.AsyncAPI/Models/JsonSchema/NoAdditionalProperties.cs new file mode 100644 index 00000000..700d3351 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/JsonSchema/NoAdditionalProperties.cs @@ -0,0 +1,12 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + /// + /// An object representing 'false' for the 'additionalProperties' property of AsyncApiSchema. + /// + /// + public class NoAdditionalProperties : AsyncApiSchema + { + } +} diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 386773f7..14763e14 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -67,6 +67,7 @@ public class AsyncApiSchema_Should MaxLength = 15, }, }, + AdditionalProperties = new NoAdditionalProperties(), }, ["property4"] = new AsyncApiSchema { @@ -78,7 +79,7 @@ public class AsyncApiSchema_Should { ["property6"] = new AsyncApiSchema { - Type = SchemaType.Boolean , + Type = SchemaType.Boolean, }, }, }, @@ -376,7 +377,8 @@ public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() ""type"": ""string"", ""maxLength"": 15 } - } + }, + ""additionalProperties"": false }, ""property4"": { ""properties"": { From 3761f521570268febb8b00fde9896379acb7047b Mon Sep 17 00:00:00 2001 From: UlrikSandberg Date: Tue, 11 Jul 2023 15:50:56 +0200 Subject: [PATCH 15/84] fix: async schema deserializer "additionalProperties" not deserializing JsonSchema correctly (#120) --- .../V2/AsyncApiSchemaDeserializer.cs | 5 +- .../Models/AsyncApiSchema_Should.cs | 84 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index 6266ef1b..5f3fb609 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -125,7 +125,7 @@ public class JsonSchemaDeserializer { "additionalProperties", (a, n) => { - if (n.GetBooleanValueOrDefault(null) == false) + if (n is ValueNode && n.GetBooleanValueOrDefault(null) == false) { a.AdditionalProperties = new NoAdditionalProperties(); } @@ -171,6 +171,9 @@ public class JsonSchemaDeserializer { "deprecated", (a, n) => { a.Deprecated = bool.Parse(n.GetScalarValue()); } }, + { + "nullable", (a, n) => { a.Nullable = n.GetBooleanValue(); } + }, }; private static readonly PatternFieldMap schemaPatternFields = diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 14763e14..bb190ad3 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -1,5 +1,9 @@ // Copyright (c) The LEGO Group. All rights reserved. +using System.Linq; +using LEGO.AsyncAPI.Bindings; +using LEGO.AsyncAPI.Readers; + namespace LEGO.AsyncAPI.Tests.Models { using System; @@ -89,6 +93,16 @@ public class AsyncApiSchema_Should MinLength = 2, }, }, + AdditionalProperties = new AsyncApiSchema + { + Properties = new Dictionary + { + ["Property8"] = new AsyncApiSchema + { + Type = SchemaType.String | SchemaType.Null, + }, + }, + }, }, }, Nullable = true, @@ -393,6 +407,16 @@ public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() ""type"": ""string"", ""minLength"": 2 } + }, + ""additionalProperties"": { + ""properties"": { + ""Property8"": { + ""type"": [ + ""null"", + ""string"" + ] + } + } } } }, @@ -411,6 +435,66 @@ public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() actual.Should().Be(expected); } + [Test] + public void Deserialize_WithAdditionalProperties_Works() + { + // Arrange + var json = @"{ + ""title"": ""title1"", + ""properties"": { + ""property1"": { + ""properties"": { + ""property2"": { + ""type"": ""integer"" + }, + ""property3"": { + ""type"": ""string"", + ""maxLength"": 15 + } + }, + ""additionalProperties"": false + }, + ""property4"": { + ""properties"": { + ""property5"": { + ""properties"": { + ""property6"": { + ""type"": ""boolean"" + } + } + }, + ""property7"": { + ""type"": ""string"", + ""minLength"": 2 + } + }, + ""additionalProperties"": { + ""properties"": { + ""Property8"": { + ""type"": [ + ""null"", + ""string"" + ] + } + } + } + } + }, + ""nullable"": true, + ""externalDocs"": { + ""url"": ""http://example.com/externalDocs"" + } +}"; + var expected = AdvancedSchemaObject; + + // Act + var actual = new AsyncApiStringReader().ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out var _diagnostics); + + // Assert + actual.Should().BeEquivalentTo(expected); + _diagnostics.Errors.Should().BeEmpty(); + } + [Test] public void SerializeAsJson_WithAdvancedSchemaWithAllOf_V2Works() { From 3c28796cddefa9db083a70fa3ceb63448082964c Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Tue, 11 Jul 2023 13:51:53 +0000 Subject: [PATCH 16/84] chore: update CHANGELOG.md --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b4bdb69a..b86c37f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [4.0.1](https://github.com/LEGO/AsyncAPI.NET/compare/v4.0.0...v4.0.1) (2023-07-11) + + +### Bug Fixes + +* add ability to have 'false' as the value for 'additionalproperties' ([#118](https://github.com/LEGO/AsyncAPI.NET/issues/118)) ([9e4867f](https://github.com/LEGO/AsyncAPI.NET/commit/9e4867fbec9377964489e53c71f38a239e359cdf)) +* async schema deserializer "additionalProperties" not deserializing JsonSchema correctly ([#120](https://github.com/LEGO/AsyncAPI.NET/issues/120)) ([3761f52](https://github.com/LEGO/AsyncAPI.NET/commit/3761f521570268febb8b00fde9896379acb7047b)) + # [4.0.0](https://github.com/LEGO/AsyncAPI.NET/compare/v3.0.2...v4.0.0) (2023-06-12) From 22b329c6c8068e4ff2090cb6dd11bab2d5a254a5 Mon Sep 17 00:00:00 2001 From: crudbee <129835705+crudbee@users.noreply.github.com> Date: Sun, 30 Jul 2023 14:08:32 +0200 Subject: [PATCH 17/84] fix: parse const keyword in a schema object (#121) --- .../V2/AsyncApiSchemaDeserializer.cs | 3 +++ .../Models/AsyncApiSchema_Should.cs | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index 5f3fb609..582016eb 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -101,6 +101,9 @@ public class JsonSchemaDeserializer { "enum", (a, n) => { a.Enum = n.CreateListOfAny(); } }, + { + "const", (a, n) => { a.Const = n.CreateAny(); } + }, { "examples", (a, n) => { a.Examples = n.CreateListOfAny(); } }, diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index bb190ad3..4b99a465 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -104,6 +104,10 @@ public class AsyncApiSchema_Should }, }, }, + ["property9"] = new AsyncApiSchema + { + Const = new AsyncApiString("aSpecialConstant"), + }, }, Nullable = true, ExternalDocs = new AsyncApiExternalDocumentation @@ -418,6 +422,9 @@ public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() } } } + }, + ""property9"": { + ""const"": ""aSpecialConstant"" } }, ""nullable"": true, @@ -478,6 +485,9 @@ public void Deserialize_WithAdditionalProperties_Works() } } } + }, + ""property9"": { + ""const"": ""aSpecialConstant"" } }, ""nullable"": true, @@ -596,7 +606,7 @@ public void Serialize_WithInliningOptions_ShouldInlineAccordingly(bool shouldInl { "testD", new AsyncApiSchema { Reference = new AsyncApiReference { Type = ReferenceType.Schema, Id = "testD" } } }, }, }) - .WithComponent("testB", new AsyncApiSchema() { Description = "test", Type = SchemaType.Boolean }) + .WithComponent("testB", new AsyncApiSchema() { Description = "test", Type = SchemaType.Boolean }) .Build(); var outputString = new StringWriter(CultureInfo.InvariantCulture); From e53db729813bd76c17a335baf9bf0d0efc34e0bc Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Tue, 1 Aug 2023 23:54:03 +0200 Subject: [PATCH 18/84] fix: nullref if type is not set on jsonschema when using enum. (#123) --- .../ParseNodes/AsyncApiAnyConverter.cs | 4 +-- .../Models/AsyncApiMessage_Should.cs | 27 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs index 4bd72a19..d2f0ec2b 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs @@ -62,7 +62,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn return new AsyncApiDateTime(dateTimeValue); } } - else if (type.Value.HasFlag(SchemaType.String)) + else if (type != null && type.Value.HasFlag(SchemaType.String)) { if (format == "byte") { @@ -109,7 +109,7 @@ public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, Asyn return new AsyncApiNull(); } - if (schema?.Type == null) + if (type == null) { if (value == "true") { diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index f207a31c..edb13eff 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -16,6 +16,33 @@ namespace LEGO.AsyncAPI.Tests.Models internal class AsyncApiMessage_Should { + [Test] + public void AsyncApiMessage_WithNoType_DeserializesToDefault() + { + // Arrange + var expected = + @"{ + ""payload"": { + ""type"": ""object"", + ""properties"": { + ""someProp"": { + ""enum"": [ + ""test"", + ""test2"" + ] + } + } + } + }"; + + // Act + var message = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + + // Assert + diagnostic.Errors.Should().BeEmpty(); + message.Payload.Properties.First().Value.Enum.Should().HaveCount(2); + } + [Test] public void AsyncApiMessage_WithNoSchemaFormat_DeserializesToDefault() { From adcd017b3ff6875eddac9649c2c95c398e49dec0 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Wed, 2 Aug 2023 00:52:50 +0200 Subject: [PATCH 19/84] fix: add missing properties to json schema (#124) --- .../V2/AsyncApiSchemaDeserializer.cs | 33 ++++- src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs | 3 + src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs | 34 ++++- .../Models/JsonSchema/FalseApiSchema.cs | 10 ++ .../JsonSchema/NoAdditionalProperties.cs | 12 -- .../Models/AsyncApiSchema_Should.cs | 118 +++++++++++++++++- 6 files changed, 189 insertions(+), 21 deletions(-) create mode 100644 src/LEGO.AsyncAPI/Models/JsonSchema/FalseApiSchema.cs delete mode 100644 src/LEGO.AsyncAPI/Models/JsonSchema/NoAdditionalProperties.cs diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index 582016eb..2131eb7e 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -130,7 +130,7 @@ public class JsonSchemaDeserializer { if (n is ValueNode && n.GetBooleanValueOrDefault(null) == false) { - a.AdditionalProperties = new NoAdditionalProperties(); + a.AdditionalProperties = new FalseApiSchema(); } else { @@ -139,7 +139,36 @@ public class JsonSchemaDeserializer } }, { - "items", (a, n) => { a.Items = LoadSchema(n); } + "items", (a, n) => + { + if (n is ValueNode && n.GetBooleanValueOrDefault(null) == false) + { + a.Items = new FalseApiSchema(); + } + else + { + a.Items = LoadSchema(n); + } + } + }, + { + "additionalItems", (a, n) => + { + if (n is ValueNode && n.GetBooleanValueOrDefault(null) == false) + { + a.AdditionalItems = new FalseApiSchema(); + } + else + { + a.AdditionalItems = LoadSchema(n); + } + } + }, + { + "patternProperties", (a, n) => { a.PatternProperties = n.CreateMap(LoadSchema); } + }, + { + "propertyNames", (a, n) => { a.PropertyNames = LoadSchema(n); } }, { "contains", (a, n) => { a.Contains = LoadSchema(n); } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs index 2d3ae13c..37b1b45d 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs @@ -138,5 +138,8 @@ public static class AsyncApiConstants public const string MaxMessageBytes = "max.message.bytes"; public const string TopicConfiguration = "topicConfiguration"; public const string GeoReplication = "geo-replication"; + public const string AdditionalItems = "additionalItems"; + public const string PropertyNames = "propertyNames"; + public const string PatternProperties = "patternProperties"; } } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs index 1de5542b..85fcc13c 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs @@ -159,6 +159,13 @@ public class AsyncApiSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyn /// public AsyncApiSchema 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 AsyncApiSchema AdditionalItems { get; set; } + /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// @@ -197,6 +204,8 @@ public class AsyncApiSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyn /// public AsyncApiSchema AdditionalProperties { get; set; } + public IDictionary PatternProperties { get; set; } = new Dictionary(); + /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// @@ -335,7 +344,24 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalCollection(AsyncApiConstants.Required, this.Required, (w, s) => w.WriteValue(s)); // items - writer.WriteOptionalObject(AsyncApiConstants.Items, this.Items, (w, s) => s.SerializeV2(w)); + if (this.Items is FalseApiSchema) + { + writer.WriteOptionalProperty(AsyncApiConstants.Items, false); + } + else + { + writer.WriteOptionalObject(AsyncApiConstants.Items, this.Items, (w, s) => s.SerializeV2(w)); + } + + // additionalItems + if (this.AdditionalItems is FalseApiSchema) + { + writer.WriteOptionalProperty(AsyncApiConstants.AdditionalItems, false); + } + else + { + writer.WriteOptionalObject(AsyncApiConstants.AdditionalItems, this.AdditionalItems, (w, s) => s.SerializeV2(w)); + } // maxItems writer.WriteOptionalProperty(AsyncApiConstants.MaxItems, this.MaxItems); @@ -356,7 +382,7 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.MinProperties, this.MinProperties); // additionalProperties - if (this.AdditionalProperties is NoAdditionalProperties) + if (this.AdditionalProperties is FalseApiSchema) { writer.WriteOptionalProperty(AsyncApiConstants.AdditionalProperties, false); } @@ -365,6 +391,10 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer) writer.WriteOptionalObject(AsyncApiConstants.AdditionalProperties, this.AdditionalProperties, (w, s) => s.SerializeV2(w)); } + writer.WriteOptionalMap(AsyncApiConstants.PatternProperties, this.PatternProperties, (w, s) => s.SerializeV2(w)); + + writer.WriteOptionalObject(AsyncApiConstants.PropertyNames, this.PropertyNames, (w, s) => s.SerializeV2(w)); + // discriminator writer.WriteOptionalProperty(AsyncApiConstants.Discriminator, this.Discriminator); diff --git a/src/LEGO.AsyncAPI/Models/JsonSchema/FalseApiSchema.cs b/src/LEGO.AsyncAPI/Models/JsonSchema/FalseApiSchema.cs new file mode 100644 index 00000000..40746fb3 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/JsonSchema/FalseApiSchema.cs @@ -0,0 +1,10 @@ +namespace LEGO.AsyncAPI.Models +{ + /// + /// An object representing 'false' for properties of AsyncApiSchema that can be false OR a schema. + /// + /// + public class FalseApiSchema : AsyncApiSchema + { + } +} diff --git a/src/LEGO.AsyncAPI/Models/JsonSchema/NoAdditionalProperties.cs b/src/LEGO.AsyncAPI/Models/JsonSchema/NoAdditionalProperties.cs deleted file mode 100644 index 700d3351..00000000 --- a/src/LEGO.AsyncAPI/Models/JsonSchema/NoAdditionalProperties.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models -{ - /// - /// An object representing 'false' for the 'additionalProperties' property of AsyncApiSchema. - /// - /// - public class NoAdditionalProperties : AsyncApiSchema - { - } -} diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 4b99a465..31647d3e 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -71,7 +71,9 @@ public class AsyncApiSchema_Should MaxLength = 15, }, }, - AdditionalProperties = new NoAdditionalProperties(), + AdditionalProperties = new FalseApiSchema(), + Items = new FalseApiSchema(), + AdditionalItems = new FalseApiSchema(), }, ["property4"] = new AsyncApiSchema { @@ -93,6 +95,26 @@ public class AsyncApiSchema_Should MinLength = 2, }, }, + PatternProperties = new Dictionary() + { + { + "^S_", + new AsyncApiSchema() + { + Type = SchemaType.String, + } + }, + { + "^I_", new AsyncApiSchema() + { + Type = SchemaType.Integer, + } + }, + }, + PropertyNames = new AsyncApiSchema() + { + Pattern = "^[A-Za-z_][A-Za-z0-9_]*$", + }, AdditionalProperties = new AsyncApiSchema { Properties = new Dictionary @@ -103,8 +125,28 @@ public class AsyncApiSchema_Should }, }, }, + Items = new AsyncApiSchema + { + Properties = new Dictionary + { + ["Property9"] = new AsyncApiSchema + { + Type = SchemaType.String | SchemaType.Null, + }, + }, + }, + AdditionalItems = new AsyncApiSchema + { + Properties = new Dictionary + { + ["Property10"] = new AsyncApiSchema + { + Type = SchemaType.String | SchemaType.Null, + }, + }, + }, }, - ["property9"] = new AsyncApiSchema + ["property11"] = new AsyncApiSchema { Const = new AsyncApiString("aSpecialConstant"), }, @@ -387,6 +429,8 @@ public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() ""title"": ""title1"", ""properties"": { ""property1"": { + ""items"": false, + ""additionalItems"": false, ""properties"": { ""property2"": { ""type"": ""integer"" @@ -399,6 +443,26 @@ public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() ""additionalProperties"": false }, ""property4"": { + ""items"": { + ""properties"": { + ""Property9"": { + ""type"": [ + ""null"", + ""string"" + ] + } + } + }, + ""additionalItems"": { + ""properties"": { + ""Property10"": { + ""type"": [ + ""null"", + ""string"" + ] + } + } + }, ""properties"": { ""property5"": { ""properties"": { @@ -421,9 +485,20 @@ public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() ] } } + }, + ""patternProperties"": { + ""^S_"": { + ""type"": ""string"" + }, + ""^I_"": { + ""type"": ""integer"" + } + }, + ""propertyNames"": { + ""pattern"": ""^[A-Za-z_][A-Za-z0-9_]*$"" } }, - ""property9"": { + ""property11"": { ""const"": ""aSpecialConstant"" } }, @@ -443,13 +518,15 @@ public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() } [Test] - public void Deserialize_WithAdditionalProperties_Works() + public void Deserialize_WithAdvancedSchema_Works() { // Arrange var json = @"{ ""title"": ""title1"", ""properties"": { ""property1"": { + ""items"": false, + ""additionalItems"": false, ""properties"": { ""property2"": { ""type"": ""integer"" @@ -462,6 +539,26 @@ public void Deserialize_WithAdditionalProperties_Works() ""additionalProperties"": false }, ""property4"": { + ""items"": { + ""properties"": { + ""Property9"": { + ""type"": [ + ""null"", + ""string"" + ] + } + } + }, + ""additionalItems"": { + ""properties"": { + ""Property10"": { + ""type"": [ + ""null"", + ""string"" + ] + } + } + }, ""properties"": { ""property5"": { ""properties"": { @@ -484,9 +581,20 @@ public void Deserialize_WithAdditionalProperties_Works() ] } } + }, + ""patternProperties"": { + ""^S_"": { + ""type"": ""string"" + }, + ""^I_"": { + ""type"": ""integer"" + } + }, + ""propertyNames"": { + ""pattern"": ""^[A-Za-z_][A-Za-z0-9_]*$"" } }, - ""property9"": { + ""property11"": { ""const"": ""aSpecialConstant"" } }, From 4b17fadbc95df6197fb057e4b50287834e2ab486 Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 22:53:28 +0000 Subject: [PATCH 20/84] chore: update CHANGELOG.md --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b86c37f4..efb62f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [4.0.2](https://github.com/LEGO/AsyncAPI.NET/compare/v4.0.1...v4.0.2) (2023-08-01) + + +### Bug Fixes + +* add missing properties to json schema ([#124](https://github.com/LEGO/AsyncAPI.NET/issues/124)) ([adcd017](https://github.com/LEGO/AsyncAPI.NET/commit/adcd017b3ff6875eddac9649c2c95c398e49dec0)) +* nullref if type is not set on jsonschema when using enum. ([#123](https://github.com/LEGO/AsyncAPI.NET/issues/123)) ([e53db72](https://github.com/LEGO/AsyncAPI.NET/commit/e53db729813bd76c17a335baf9bf0d0efc34e0bc)) +* parse const keyword in a schema object ([#121](https://github.com/LEGO/AsyncAPI.NET/issues/121)) ([22b329c](https://github.com/LEGO/AsyncAPI.NET/commit/22b329c6c8068e4ff2090cb6dd11bab2d5a254a5)) + ## [4.0.1](https://github.com/LEGO/AsyncAPI.NET/compare/v4.0.0...v4.0.1) (2023-07-11) From 4e4d3e094654db4bb066a61fbed803a38c48b3f4 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 8 Sep 2023 13:22:37 +0200 Subject: [PATCH 21/84] add ci run to vnext --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 447ff2b5..d43f060d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ name: Build & Test on: push: - branches: [ main ] + branches: [ main, vnext ] paths: - 'src/**' - '!**/*.md' From 81658d468b7ba2706713727c547551f5c3d38b15 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 8 Sep 2023 13:23:45 +0200 Subject: [PATCH 22/84] add ci run to vnext (#126) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 447ff2b5..d43f060d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,7 @@ name: Build & Test on: push: - branches: [ main ] + branches: [ main, vnext ] paths: - 'src/**' - '!**/*.md' From 7f535f72a417409fb3bbaaaef554e47b1acc93d1 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 8 Sep 2023 13:29:33 +0200 Subject: [PATCH 23/84] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d43f060d..4f8d2c69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: - 'src/**' - '!**/*.md' pull_request: - branches: [ main ] + branches: [ main, vnext ] paths: - 'src/**' - '!**/*.md' From 19f62ea701e1c2dadbfc9804c603e3460fc51484 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 8 Sep 2023 13:30:28 +0200 Subject: [PATCH 24/84] missed one (#127) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d43f060d..4f8d2c69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: - 'src/**' - '!**/*.md' pull_request: - branches: [ main ] + branches: [ main, vnext ] paths: - 'src/**' - '!**/*.md' From a0c6d7fb786afbdda93aa3e2ee977cc16784129a Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 15 Sep 2023 12:19:57 +0200 Subject: [PATCH 25/84] feat(serialization)!: migrate from YamlDotnet to System.Text.Json (#125) --- .../Sns/FilterPolicy.cs | 3 +- src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs | 1 - .../Sns/SnsOperationBinding.cs | 1 - src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 1 - src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs | 4 - .../StringOrStringList.cs | 45 +-- .../AsyncApiReaderSettings.cs | 5 +- .../AsyncApiTextReader.cs | 37 +-- .../AsyncApiYamlDocumentReader.cs | 12 +- .../Exceptions/AsyncApiReaderException.cs | 9 - src/LEGO.AsyncAPI.Readers/JsonHelper.cs | 32 +++ .../ParseNodes/AnyFieldMapParameter.cs | 9 +- .../ParseNodes/AnyListFieldMapParameter{T}.cs | 9 +- .../AnyMapFieldMapParameter{T,U}.cs | 9 +- .../ParseNodes/AsyncApiAnyConverter.cs | 248 ---------------- .../ParseNodes/JsonPointerExtensions.cs | 25 +- .../ParseNodes/ListNode.cs | 27 +- .../ParseNodes/MapNode.cs | 87 +++--- .../ParseNodes/ParseNode.cs | 14 +- .../ParseNodes/PropertyNode.cs | 4 +- .../ParseNodes/RootNode.cs | 15 +- .../ParseNodes/ValueNode.cs | 50 ++-- src/LEGO.AsyncAPI.Readers/ParsingContext.cs | 20 +- .../V2/AsyncApiDeserializer.cs | 72 ++--- .../V2/AsyncApiV2VersionService.cs | 2 +- .../V2/ExtensionHelpers.cs | 5 +- src/LEGO.AsyncAPI.Readers/YamlConverter.cs | 71 +++++ src/LEGO.AsyncAPI.Readers/YamlHelper.cs | 32 --- src/LEGO.AsyncAPI/Models/Any/AnyType.cs | 32 --- src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs | 42 ++- .../Models/Any/AsyncAPIBoolean.cs | 26 -- .../Models/Any/AsyncAPIDouble.cs | 25 -- src/LEGO.AsyncAPI/Models/Any/AsyncAPILong.cs | 25 -- src/LEGO.AsyncAPI/Models/Any/AsyncAPINull.cs | 27 -- .../Models/Any/AsyncAPIObject.cs | 20 +- .../Models/Any/AsyncAPIString.cs | 69 ----- src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs | 49 ++++ .../Models/Any/AsyncApiBinary.cs | 26 -- src/LEGO.AsyncAPI/Models/Any/AsyncApiByte.cs | 33 --- src/LEGO.AsyncAPI/Models/Any/AsyncApiDate.cs | 26 -- .../Models/Any/AsyncApiDateTime.cs | 26 -- src/LEGO.AsyncAPI/Models/Any/AsyncApiFloat.cs | 25 -- .../Models/Any/AsyncApiInteger.cs | 25 -- .../Models/Any/AsyncApiPrimitive{T}.cs | 130 --------- .../Models/AsyncApiMessageExample.cs | 4 +- src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs | 8 +- .../Models/Interfaces/IAsyncApiAny.cs | 17 -- .../Models/Interfaces/IAsyncApiPrimitive.cs | 71 ----- .../Models/RuntimeExpressionAnyWrapper.cs | 8 +- .../Services/AsyncApiVisitorBase.cs | 2 +- src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs | 4 +- .../Validation/Rules/AsyncApiContactRules.cs | 15 + .../Validation/Rules/RuleHelpers.cs | 268 ------------------ .../Writers/AsyncApiWriterAnyExtensions.cs | 89 ++++-- .../AsyncApiDocumentV2Tests.cs | 39 ++- .../AsyncApiLicenseTests.cs | 18 +- .../AsyncApiReaderTests.cs | 20 +- .../Bindings/CustomBinding_Should.cs | 9 +- .../Bindings/Sns/SnsBindings_Should.cs | 55 ++-- .../Bindings/Sqs/SqsBindings_should.cs | 59 ++-- .../Bindings/StringOrStringList_Should.cs | 29 +- .../Models/AsyncApiMessage_Should.cs | 37 +-- .../Models/AsyncApiSchema_Should.cs | 15 +- 63 files changed, 624 insertions(+), 1598 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Readers/JsonHelper.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs create mode 100644 src/LEGO.AsyncAPI.Readers/YamlConverter.cs delete mode 100644 src/LEGO.AsyncAPI.Readers/YamlHelper.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AnyType.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncAPIBoolean.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncAPIDouble.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncAPILong.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncAPINull.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncAPIString.cs create mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncApiBinary.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncApiByte.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncApiDate.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncApiDateTime.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncApiFloat.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncApiInteger.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Any/AsyncApiPrimitive{T}.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiAny.cs delete mode 100644 src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiPrimitive.cs delete mode 100644 src/LEGO.AsyncAPI/Validation/Rules/RuleHelpers.cs diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs index 47530cc0..f24ae280 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs @@ -2,6 +2,7 @@ namespace LEGO.AsyncAPI.Bindings.Sns { using System; using System.Collections.Generic; + using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; @@ -10,7 +11,7 @@ public class FilterPolicy : IAsyncApiExtensible /// /// A map of a message attribute to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint. /// - public IAsyncApiAny Attributes { get; set; } + public AsyncApiAny Attributes { get; set; } public IDictionary Extensions { get; set; } = new Dictionary(); diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs index 685b2f8d..eefc6171 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Bindings.Sns { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs index d35a46f5..e474e4a6 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -2,7 +2,6 @@ namespace LEGO.AsyncAPI.Bindings.Sns { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index c21ecc80..38e0b1ee 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -3,7 +3,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/Queue.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs index f79e0ad3..e0b95893 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs @@ -2,12 +2,8 @@ namespace LEGO.AsyncAPI.Bindings.Sqs { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using Extensions; - using LEGO.AsyncAPI.Readers; - using LEGO.AsyncAPI.Readers.ParseNodes; public class Queue : IAsyncApiExtensible { diff --git a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs index 31d9c749..4653d586 100644 --- a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs +++ b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs @@ -1,37 +1,44 @@ -using System; -using System.Linq; -using LEGO.AsyncAPI.Models.Any; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Readers.ParseNodes; +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Bindings { + using System; + using System.Linq; + using System.Text.Json; + using System.Text.Json.Nodes; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers.ParseNodes; + public class StringOrStringList : IAsyncApiElement { - public StringOrStringList(IAsyncApiAny value) + public StringOrStringList(AsyncApiAny value) { - this.Value = value switch + this.Value = value.Node switch { - AsyncApiArray array => IsValidStringList(array) ? array : throw new ArgumentException($"{nameof(StringOrStringList)} value should only contain string items."), - AsyncApiPrimitive => value, + JsonArray array => IsValidStringList(array) ? new AsyncApiAny(array) : throw new ArgumentException($"{nameof(StringOrStringList)} value should only contain string items."), + JsonValue jValue => IsString(jValue) ? new AsyncApiAny(jValue) : throw new ArgumentException($"{nameof(StringOrStringList)} should be a string value or a string list."), _ => throw new ArgumentException($"{nameof(StringOrStringList)} should be a string value or a string list.") }; } - public IAsyncApiAny Value { get; } + public AsyncApiAny Value { get; } public static StringOrStringList Parse(ParseNode node) { switch (node) { case ValueNode: - return new StringOrStringList(new AsyncApiString(node.GetScalarValue())); + return new StringOrStringList(new AsyncApiAny(node.GetScalarValue())); case ListNode: { - var asyncApiArray = new AsyncApiArray(); - asyncApiArray.AddRange(node.CreateSimpleList(s => new AsyncApiString(s.GetScalarValue()))); + var jsonArray = new JsonArray(); + foreach (var item in node as ListNode) + { + jsonArray.Add(item.GetScalarValue()); + } - return new StringOrStringList(asyncApiArray); + return new StringOrStringList(new AsyncApiAny(jsonArray)); } default: @@ -40,9 +47,15 @@ public static StringOrStringList Parse(ParseNode node) } } - private static bool IsValidStringList(AsyncApiArray array) + private static bool IsString(JsonNode value) + { + var element = JsonDocument.Parse(value.ToJsonString()).RootElement; + return element.ValueKind == JsonValueKind.String; + } + + private static bool IsValidStringList(JsonArray array) { - return array.All(x => x is AsyncApiPrimitive); + return array.All(x => IsString(x)); } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index 0429364a..134acc75 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -5,6 +5,7 @@ namespace LEGO.AsyncAPI.Readers using System; using System.Collections.Generic; using System.IO; + using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Interface; using LEGO.AsyncAPI.Validations; @@ -36,10 +37,10 @@ public class AsyncApiReaderSettings /// /// Dictionary of parsers for converting extensions into strongly typed classes. /// - public Dictionary> + public Dictionary> ExtensionParsers { get; set; } = - new Dictionary>(); + new Dictionary>(); public List> Bindings diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs index 84f8392f..d98bf6e7 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs @@ -4,11 +4,12 @@ namespace LEGO.AsyncAPI.Readers { using System.IO; using System.Linq; + using System.Text.Json; + using System.Text.Json.Nodes; using System.Threading.Tasks; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Interface; - using YamlDotNet.Core; using YamlDotNet.RepresentationModel; /// @@ -35,21 +36,21 @@ public AsyncApiTextReader(AsyncApiReaderSettings settings = null) /// Instance of newly created AsyncApiDocument. public AsyncApiDocument Read(TextReader input, out AsyncApiDiagnostic diagnostic) { - YamlDocument yamlDocument; + JsonNode jsonNode; // Parse the YAML/JSON text in the TextReader into the YamlDocument try { - yamlDocument = LoadYamlDocument(input); + jsonNode = LoadYamlDocument(input); } - catch (YamlException ex) + catch (JsonException ex) { diagnostic = new AsyncApiDiagnostic(); - diagnostic.Errors.Add(new AsyncApiError($"#line={ex.Start.Line}", ex.Message)); + diagnostic.Errors.Add(new AsyncApiError($"#line={ex.LineNumber}", ex.Message)); return new AsyncApiDocument(); } - return new AsyncApiYamlDocumentReader(this.settings).Read(yamlDocument, out diagnostic); + return new AsyncApiJsonDocumentReader(this.settings).Read(jsonNode, out diagnostic); } /// @@ -59,17 +60,17 @@ public AsyncApiDocument Read(TextReader input, out AsyncApiDiagnostic diagnostic /// A ReadResult instance that contains the resulting AsyncApiDocument and a diagnostics instance. public async Task ReadAsync(TextReader input) { - YamlDocument yamlDocument; + JsonNode jsonNode; // Parse the YAML/JSON text in the TextReader into the YamlDocument try { - yamlDocument = LoadYamlDocument(input); + jsonNode = LoadYamlDocument(input); } - catch (YamlException ex) + catch (JsonException ex) { var diagnostic = new AsyncApiDiagnostic(); - diagnostic.Errors.Add(new AsyncApiError($"#line={ex.Start.Line}", ex.Message)); + diagnostic.Errors.Add(new AsyncApiError($"#line={ex.LineNumber}", ex.Message)); return new ReadResult { AsyncApiDocument = null, @@ -77,7 +78,7 @@ public async Task ReadAsync(TextReader input) }; } - return await new AsyncApiYamlDocumentReader(this.settings).ReadAsync(yamlDocument); + return await new AsyncApiJsonDocumentReader(this.settings).ReadAsync(jsonNode); } /// @@ -90,21 +91,21 @@ public async Task ReadAsync(TextReader input) public T ReadFragment(TextReader input, AsyncApiVersion version, out AsyncApiDiagnostic diagnostic) where T : IAsyncApiElement { - YamlDocument yamlDocument; + JsonNode jsonNode; // Parse the YAML/JSON try { - yamlDocument = LoadYamlDocument(input); + jsonNode = LoadYamlDocument(input); } - catch (YamlException ex) + catch (JsonException ex) { diagnostic = new AsyncApiDiagnostic(); - diagnostic.Errors.Add(new AsyncApiError($"#line={ex.Start.Line}", ex.Message)); + diagnostic.Errors.Add(new AsyncApiError($"#line={ex.LineNumber}", ex.Message)); return default; } - return new AsyncApiYamlDocumentReader(this.settings).ReadFragment(yamlDocument, version, + return new AsyncApiJsonDocumentReader(this.settings).ReadFragment(jsonNode, version, out diagnostic); } @@ -113,11 +114,11 @@ public T ReadFragment(TextReader input, AsyncApiVersion version, out AsyncApi /// /// Stream containing YAML formatted text. /// Instance of a YamlDocument. - static YamlDocument LoadYamlDocument(TextReader input) + static JsonNode LoadYamlDocument(TextReader input) { var yamlStream = new YamlStream(); yamlStream.Load(input); - return yamlStream.Documents.First(); + return yamlStream.Documents.First().ToJsonNode(); } } } diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs index 5b52ea88..fccdf3e3 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs @@ -4,6 +4,7 @@ namespace LEGO.AsyncAPI.Readers { using System.Collections.Generic; using System.Linq; + using System.Text.Json.Nodes; using System.Threading.Tasks; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Extensions; @@ -11,12 +12,11 @@ namespace LEGO.AsyncAPI.Readers using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Interface; using LEGO.AsyncAPI.Validations; - using YamlDotNet.RepresentationModel; /// /// Service class for converting contents of TextReader into AsyncApiDocument instances. /// - internal class AsyncApiYamlDocumentReader : IAsyncApiReader + internal class AsyncApiJsonDocumentReader : IAsyncApiReader { private readonly AsyncApiReaderSettings settings; @@ -24,7 +24,7 @@ internal class AsyncApiYamlDocumentReader : IAsyncApiReader /// - public AsyncApiYamlDocumentReader(AsyncApiReaderSettings settings = null) + public AsyncApiJsonDocumentReader(AsyncApiReaderSettings settings = null) { this.settings = settings ?? new AsyncApiReaderSettings(); } @@ -35,7 +35,7 @@ public AsyncApiYamlDocumentReader(AsyncApiReaderSettings settings = null) /// TextReader containing AsyncApi description to parse. /// Returns diagnostic object containing errors detected during parsing. /// Instance of newly created AsyncApiDocument. - public AsyncApiDocument Read(YamlDocument input, out AsyncApiDiagnostic diagnostic) + public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic) { diagnostic = new AsyncApiDiagnostic(); var context = new ParsingContext(diagnostic) @@ -76,7 +76,7 @@ public AsyncApiDocument Read(YamlDocument input, out AsyncApiDiagnostic diagnost return document; } - public Task ReadAsync(YamlDocument input) + public Task ReadAsync(JsonNode input) { var diagnostic = new AsyncApiDiagnostic(); var context = new ParsingContext(diagnostic) @@ -140,7 +140,7 @@ private void ResolveReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument d /// Version of the AsyncApi specification that the fragment conforms to. /// Returns diagnostic object containing errors detected during parsing. /// Instance of newly created AsyncApiDocument. - public T ReadFragment(YamlDocument input, AsyncApiVersion version, out AsyncApiDiagnostic diagnostic) + public T ReadFragment(JsonNode input, AsyncApiVersion version, out AsyncApiDiagnostic diagnostic) where T : IAsyncApiElement { diagnostic = new AsyncApiDiagnostic(); diff --git a/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiReaderException.cs b/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiReaderException.cs index 498eb592..61ae4124 100644 --- a/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiReaderException.cs +++ b/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiReaderException.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Readers.Exceptions { using System; using LEGO.AsyncAPI.Exceptions; - using YamlDotNet.RepresentationModel; [Serializable] public class AsyncApiReaderException : AsyncApiException @@ -24,14 +23,6 @@ public AsyncApiReaderException(string message, ParsingContext context) this.Pointer = context.GetLocation(); } - public AsyncApiReaderException(string message, YamlNode node) - : base(message) - { - // This only includes line because using a char range causes tests to break due to CR/LF & LF differences - // See https://tools.ietf.org/html/rfc5147 for syntax - this.Pointer = $"#line={node.Start.Line}"; - } - public AsyncApiReaderException(string message, Exception innerException) : base(message, innerException) { diff --git a/src/LEGO.AsyncAPI.Readers/JsonHelper.cs b/src/LEGO.AsyncAPI.Readers/JsonHelper.cs new file mode 100644 index 00000000..193daed2 --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/JsonHelper.cs @@ -0,0 +1,32 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers +{ + using System; + using System.Globalization; + using System.IO; + using System.Linq; + using System.Text.Json.Nodes; + using LEGO.AsyncAPI.Exceptions; + using YamlDotNet.RepresentationModel; + + internal static class JsonHelper + { + public static string GetScalarValue(this JsonNode node) + { + var scalarNode = node is JsonValue value ? value : throw new AsyncApiException($"Expected scalar value"); + return Convert.ToString(scalarNode.GetValue(), CultureInfo.InvariantCulture); + } + + public static JsonNode ParseJsonString(string jsonString) + { + return JsonNode.Parse(jsonString); + var reader = new StringReader(jsonString); + var yamlStream = new YamlStream(); + yamlStream.Load(reader); + + var yamlDocument = yamlStream.Documents.First(); + return yamlDocument.RootNode.ToJsonNode(); + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyFieldMapParameter.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyFieldMapParameter.cs index 5b2073ed..76e9008a 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyFieldMapParameter.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyFieldMapParameter.cs @@ -4,13 +4,12 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes { using System; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; internal class AnyFieldMapParameter { public AnyFieldMapParameter( - Func propertyGetter, - Action propertySetter, + Func propertyGetter, + Action propertySetter, Func schemaGetter) { this.PropertyGetter = propertyGetter; @@ -18,9 +17,9 @@ public AnyFieldMapParameter( this.SchemaGetter = schemaGetter; } - public Func PropertyGetter { get; } + public Func PropertyGetter { get; } - public Action PropertySetter { get; } + public Action PropertySetter { get; } public Func SchemaGetter { get; } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs index abd65184..ee1af993 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs @@ -5,13 +5,12 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System; using System.Collections.Generic; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; internal class AnyListFieldMapParameter { public AnyListFieldMapParameter( - Func> propertyGetter, - Action> propertySetter, + Func> propertyGetter, + Action> propertySetter, Func schemaGetter) { this.PropertyGetter = propertyGetter; @@ -19,9 +18,9 @@ public AnyListFieldMapParameter( this.SchemaGetter = schemaGetter; } - public Func> PropertyGetter { get; } + public Func> PropertyGetter { get; } - public Action> PropertySetter { get; } + public Action> PropertySetter { get; } public Func SchemaGetter { get; } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyMapFieldMapParameter{T,U}.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyMapFieldMapParameter{T,U}.cs index 8b852453..2399fe31 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyMapFieldMapParameter{T,U}.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyMapFieldMapParameter{T,U}.cs @@ -5,14 +5,13 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System; using System.Collections.Generic; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Interfaces; internal class AnyMapFieldMapParameter { public AnyMapFieldMapParameter( Func> propertyMapGetter, - Func propertyGetter, - Action propertySetter, + Func propertyGetter, + Action propertySetter, Func schemaGetter) { this.PropertyMapGetter = propertyMapGetter; @@ -23,9 +22,9 @@ public AnyMapFieldMapParameter( public Func> PropertyMapGetter { get; } - public Func PropertyGetter { get; } + public Func PropertyGetter { get; } - public Action PropertySetter { get; } + public Action PropertySetter { get; } public Func SchemaGetter { get; } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs deleted file mode 100644 index d2f0ec2b..00000000 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AsyncApiAnyConverter.cs +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers.ParseNodes -{ - using System; - using System.Globalization; - using System.Linq; - using System.Text; - using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; - using LEGO.AsyncAPI.Models.Interfaces; - - internal static class AsyncApiAnyConverter - { - public static IAsyncApiAny GetSpecificAsyncApiAny(IAsyncApiAny asyncApiAny, AsyncApiSchema schema = null) - { - if (asyncApiAny is AsyncApiArray asyncApiArray) - { - var newArray = new AsyncApiArray(); - foreach (var element in asyncApiArray) - { - newArray.Add(GetSpecificAsyncApiAny(element, schema?.Items)); - } - - return newArray; - } - - if (asyncApiAny is AsyncApiObject asyncApiObject) - { - var newObject = new AsyncApiObject(); - - foreach (var key in asyncApiObject.Keys.ToList()) - { - if (schema?.Properties != null && schema.Properties.TryGetValue(key, out var property)) - { - newObject[key] = GetSpecificAsyncApiAny(asyncApiObject[key], property); - } - else - { - newObject[key] = GetSpecificAsyncApiAny(asyncApiObject[key], schema?.AdditionalProperties); - } - } - - return newObject; - } - - if (!(asyncApiAny is AsyncApiString)) - { - return asyncApiAny; - } - - var value = ((AsyncApiString)asyncApiAny).Value; - var type = schema?.Type; - var format = schema?.Format; - - if (((AsyncApiString)asyncApiAny).IsExplicit()) - { - if (schema == null) - { - if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue)) - { - return new AsyncApiDateTime(dateTimeValue); - } - } - else if (type != null && type.Value.HasFlag(SchemaType.String)) - { - if (format == "byte") - { - try - { - return new AsyncApiByte(Convert.FromBase64String(value)); - } - catch (FormatException) - { } - } - - if (format == "binary") - { - try - { - return new AsyncApiBinary(Encoding.UTF8.GetBytes(value)); - } - catch (EncoderFallbackException) - { } - } - - if (format == "date") - { - if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateValue)) - { - return new AsyncApiDate(dateValue.Date); - } - } - - if (format == "date-time") - { - if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue)) - { - return new AsyncApiDateTime(dateTimeValue); - } - } - } - - return asyncApiAny; - } - - if (value == null || value == "null") - { - return new AsyncApiNull(); - } - - if (type == null) - { - if (value == "true") - { - return new AsyncApiBoolean(true); - } - - if (value == "false") - { - return new AsyncApiBoolean(false); - } - - if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) - { - return new AsyncApiInteger(intValue); - } - - if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue)) - { - return new AsyncApiLong(longValue); - } - - if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue)) - { - return new AsyncApiDouble(doubleValue); - } - - if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue)) - { - return new AsyncApiDateTime(dateTimeValue); - } - } - else - { - if (type.Value.HasFlag(SchemaType.Integer) && format == "int32") - { - if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) - { - return new AsyncApiInteger(intValue); - } - } - - if (type.Value.HasFlag(SchemaType.Integer) && format == "int64") - { - if (long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var longValue)) - { - return new AsyncApiLong(longValue); - } - } - - if (type.Value.HasFlag(SchemaType.Integer)) - { - if (int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue)) - { - return new AsyncApiInteger(intValue); - } - } - - if (type.Value.HasFlag(SchemaType.Number) && format == "float") - { - if (float.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var floatValue)) - { - return new AsyncApiFloat(floatValue); - } - } - - if (type.Value.HasFlag(SchemaType.Number) && format == "double") - { - if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue)) - { - return new AsyncApiDouble(doubleValue); - } - } - - if (type.Value.HasFlag(SchemaType.Number)) - { - if (double.TryParse(value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var doubleValue)) - { - return new AsyncApiDouble(doubleValue); - } - } - - if (type.Value.HasFlag(SchemaType.String) && format == "byte") - { - try - { - return new AsyncApiByte(Convert.FromBase64String(value)); - } - catch (FormatException) - { } - } - - // binary - if (type.Value.HasFlag(SchemaType.String) && format == "binary") - { - try - { - return new AsyncApiBinary(Encoding.UTF8.GetBytes(value)); - } - catch (EncoderFallbackException) - { } - } - - if (type.Value.HasFlag(SchemaType.String) && format == "date") - { - if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateValue)) - { - return new AsyncApiDate(dateValue.Date); - } - } - - if (type.Value.HasFlag(SchemaType.String) && format == "date-time") - { - if (DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var dateTimeValue)) - { - return new AsyncApiDateTime(dateTimeValue); - } - } - - if (type.Value.HasFlag(SchemaType.String)) - { - return asyncApiAny; - } - - if (type.Value.HasFlag(SchemaType.Boolean)) - { - if (bool.TryParse(value, out var booleanValue)) - { - return new AsyncApiBoolean(booleanValue); - } - } - } - - return asyncApiAny; - } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/JsonPointerExtensions.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/JsonPointerExtensions.cs index 2cb6302c..acf5857e 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/JsonPointerExtensions.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/JsonPointerExtensions.cs @@ -3,38 +3,31 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes { using System; - using YamlDotNet.RepresentationModel; + using System.Text.Json.Nodes; public static class JsonPointerExtensions { - public static YamlNode Find(this JsonPointer currentPointer, YamlNode baseYamlNode) + public static JsonNode Find(this JsonPointer currentPointer, JsonNode baseJsonNode) { if (currentPointer.Tokens.Length == 0) { - return baseYamlNode; + return baseJsonNode; } try { - var pointer = baseYamlNode; + var pointer = baseJsonNode; foreach (var token in currentPointer.Tokens) { - var sequence = pointer as YamlSequenceNode; + var sequence = pointer as JsonArray; - if (sequence != null) + if (sequence != null && int.TryParse(token, out var tokenValue)) { - pointer = sequence.Children[Convert.ToInt32(token)]; + pointer = sequence[tokenValue]; } - else + else if (pointer is JsonObject map && !map.TryGetPropertyValue(token, out pointer)) { - var map = pointer as YamlMappingNode; - if (map != null) - { - if (!map.Children.TryGetValue(new YamlScalarNode(token), out pointer)) - { - return null; - } - } + return null; } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs index 17c7a592..cce1d406 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ListNode.cs @@ -6,16 +6,15 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System.Collections; using System.Collections.Generic; using System.Linq; - using LEGO.AsyncAPI.Models.Any; - using LEGO.AsyncAPI.Models.Interfaces; + using System.Text.Json.Nodes; + using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.Exceptions; - using YamlDotNet.RepresentationModel; public class ListNode : ParseNode, IEnumerable { - private readonly YamlSequenceNode nodeList; + private readonly JsonArray nodeList; - public ListNode(ParsingContext context, YamlSequenceNode sequenceNode) + public ListNode(ParsingContext context, JsonArray sequenceNode) : base( context) { @@ -27,15 +26,15 @@ public override List CreateList(Func map) if (this.nodeList == null) { throw new AsyncApiReaderException( - $"Expected list at line {this.nodeList.Start.Line} while parsing {typeof(T).Name}", this.nodeList); + $"Expected list while parsing {typeof(T).Name}"); } - return this.nodeList.Select(n => map(new MapNode(this.Context, n as YamlMappingNode))) + return this.nodeList.Select(n => map(new MapNode(this.Context, n as JsonObject))) .Where(i => i != null) .ToList(); } - public override List CreateListOfAny() + public override List CreateListOfAny() { return this.nodeList.Select(n => ParseNode.Create(this.Context, n).CreateAny()) .Where(i => i != null) @@ -47,7 +46,7 @@ public override List CreateSimpleList(Func map) if (this.nodeList == null) { throw new AsyncApiReaderException( - $"Expected list at line {this.nodeList.Start.Line} while parsing {typeof(T).Name}", this.nodeList); + $"Expected list while parsing {typeof(T).Name}"); } return this.nodeList.Select(n => map(new ValueNode(this.Context, n))).ToList(); @@ -63,15 +62,9 @@ IEnumerator IEnumerable.GetEnumerator() return this.GetEnumerator(); } - public override IAsyncApiAny CreateAny() + public override AsyncApiAny CreateAny() { - var array = new AsyncApiArray(); - foreach (var node in this) - { - array.Add(node.CreateAny()); - } - - return array; + return new AsyncApiAny(this.nodeList); } } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs index 1b944de6..d7be2ed3 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs @@ -6,37 +6,35 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System.Collections; using System.Collections.Generic; using System.Linq; + using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Exceptions; - using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; public class MapNode : ParseNode, IEnumerable { - private readonly YamlMappingNode node; + private readonly JsonObject node; private readonly List nodes; - public MapNode(ParsingContext context, string yamlString) - : this(context, (YamlMappingNode)YamlHelper.ParseYamlString(yamlString)) + public MapNode(ParsingContext context, string jsonString) + : this(context, JsonHelper.ParseJsonString(jsonString)) { } - public MapNode(ParsingContext context, YamlNode node) + public MapNode(ParsingContext context, JsonNode node) : base( context) { - if (!(node is YamlMappingNode mapNode)) + if (!(node is JsonObject mapNode)) { throw new AsyncApiReaderException("Expected map.", this.Context); } this.node = mapNode; - this.nodes = this.node.Children - .Select(kvp => new PropertyNode(this.Context, kvp.Key.GetScalarValue(), kvp.Value)) - .Cast() + this.nodes = this.node + .Select(node => new PropertyNode(this.Context, node.Key, node.Value)) .ToList(); } @@ -44,8 +42,7 @@ public PropertyNode this[string key] { get { - YamlNode node; - if (this.node.Children.TryGetValue(new YamlScalarNode(key), out node)) + if (this.node.TryGetPropertyValue(key, out var node)) { return new PropertyNode(this.Context, key, node); } @@ -56,23 +53,23 @@ public PropertyNode this[string key] public override Dictionary CreateMap(Func map) { - var yamlMap = this.node; - if (yamlMap == null) + var jsonMap = this.node; + if (jsonMap == null) { throw new AsyncApiReaderException($"Expected map while parsing {typeof(T).Name}", this.Context); } - var nodes = yamlMap.Select( + var nodes = jsonMap.Select( n => { - var key = n.Key.GetScalarValue(); + var key = n.Key; T value; try { this.Context.StartObject(key); - value = n.Value as YamlMappingNode == null - ? default(T) - : map(new MapNode(this.Context, n.Value as YamlMappingNode)); + value = n.Value is JsonObject + ? map(new MapNode(this.Context, n.Value)) + : default(T); } finally { @@ -81,8 +78,8 @@ public override Dictionary CreateMap(Func map) return new { - key = key, - value = value, + key, + value, }; }); @@ -93,23 +90,23 @@ public override Dictionary CreateMapWithReference( ReferenceType referenceType, Func map) { - var yamlMap = this.node; - if (yamlMap == null) + var jsonMap = this.node; + if (jsonMap == null) { throw new AsyncApiReaderException($"Expected map while parsing {typeof(T).Name}", this.Context); } - var nodes = yamlMap.Select( + var nodes = jsonMap.Select( n => { - var key = n.Key.GetScalarValue(); + var key = n.Key; (string key, T value) entry; try { this.Context.StartObject(key); entry = ( - key: key, - value: map(new MapNode(this.Context, (YamlMappingNode)n.Value)) + key, + value: map(new MapNode(this.Context, n.Value)) ); if (entry.value == null) { @@ -138,26 +135,26 @@ public override Dictionary CreateMapWithReference( public override Dictionary CreateSimpleMap(Func map) { - var yamlMap = this.node; - if (yamlMap == null) + var jsonMap = this.node; + if (jsonMap == null) { throw new AsyncApiReaderException($"Expected map while parsing {typeof(T).Name}", this.Context); } - var nodes = yamlMap.Select( + var nodes = jsonMap.Select( n => { - var key = n.Key.GetScalarValue(); + var key = n.Key; try { this.Context.StartObject(key); - YamlScalarNode scalarNode = n.Value as YamlScalarNode; + JsonValue scalarNode = n.Value as JsonValue; if (scalarNode == null) { throw new AsyncApiReaderException($"Expected scalar while parsing {typeof(T).Name}", this.Context); } - return (key, value: map(new ValueNode(this.Context, (YamlScalarNode)n.Value))); + return (key, value: map(new ValueNode(this.Context, n.Value))); } finally { @@ -195,9 +192,7 @@ public T GetReferencedObject(ReferenceType referenceType, string referenceId) public string GetReferencePointer() { - YamlNode refNode; - - if (!this.node.Children.TryGetValue(new YamlScalarNode("$ref"), out refNode)) + if (!this.node.TryGetPropertyValue("$ref", out JsonNode refNode)) { return null; } @@ -207,24 +202,16 @@ public string GetReferencePointer() public string GetScalarValue(ValueNode key) { - var scalarNode = this.node.Children[new YamlScalarNode(key.GetScalarValue())] as YamlScalarNode; - if (scalarNode == null) - { - throw new AsyncApiReaderException($"Expected scalar at line {this.node.Start.Line} for key {key.GetScalarValue()}", this.Context); - } + var scalarNode = this.node[key.GetScalarValue()] is JsonValue jsonValue + ? jsonValue + : throw new AsyncApiReaderException($"Expected scalar value while parsing {key.GetScalarValue()}", this.Context); - return scalarNode.Value; + return scalarNode.GetScalarValue(); } - public override IAsyncApiAny CreateAny() + public override AsyncApiAny CreateAny() { - var apiObject = new AsyncApiObject(); - foreach (var node in this) - { - apiObject.Add(node.Name, node.Value.CreateAny()); - } - - return apiObject; + return new AsyncApiAny(this.node); } } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs index 94fc4268..838eb358 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs @@ -4,10 +4,10 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes { using System; using System.Collections.Generic; + using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Exceptions; - using YamlDotNet.RepresentationModel; public abstract class ParseNode { @@ -28,19 +28,19 @@ public MapNode CheckMapNode(string nodeName) return mapNode; } - public static ParseNode Create(ParsingContext context, YamlNode node) + public static ParseNode Create(ParsingContext context, JsonNode node) { - if (node is YamlSequenceNode listNode) + if (node is JsonArray listNode) { return new ListNode(context, listNode); } - if (node is YamlMappingNode mapNode) + if (node is JsonObject mapNode) { return new MapNode(context, mapNode); } - return new ValueNode(context, node as YamlScalarNode); + return new ValueNode(context, node as JsonValue); } public virtual List CreateList(Func map) @@ -79,7 +79,7 @@ public virtual Dictionary CreateSimpleMap(Func map) throw new AsyncApiReaderException("Cannot create simple map from this type of node.", this.Context); } - public virtual IAsyncApiAny CreateAny() + public virtual AsyncApiAny CreateAny() { throw new AsyncApiReaderException("Cannot create an Any object this type of node.", this.Context); } @@ -129,7 +129,7 @@ public virtual long GetLongValue() throw new AsyncApiReaderException("Cannot create a scalar value from this type of node.", this.Context); } - public virtual List CreateListOfAny() + public virtual List CreateListOfAny() { throw new AsyncApiReaderException("Cannot create a list from this type of node.", this.Context); } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs index 913edfb1..e0359659 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs @@ -5,14 +5,14 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System; using System.Collections.Generic; using System.Linq; + using System.Text.Json.Nodes; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.Exceptions; - using YamlDotNet.RepresentationModel; public class PropertyNode : ParseNode { - public PropertyNode(ParsingContext context, string name, YamlNode node) + public PropertyNode(ParsingContext context, string name, JsonNode node) : base( context) { diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/RootNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/RootNode.cs index a5034d2d..82e39349 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/RootNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/RootNode.cs @@ -2,34 +2,33 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes { - using YamlDotNet.RepresentationModel; + using System.Text.Json.Nodes; internal class RootNode : ParseNode { - private readonly YamlDocument yamlDocument; + private readonly JsonNode jsonNode; public RootNode( ParsingContext context, - YamlDocument yamlDocument) + JsonNode jsonNode) : base(context) { - this.yamlDocument = yamlDocument; + this.jsonNode = jsonNode; } public ParseNode Find(JsonPointer referencePointer) { - var yamlNode = referencePointer.Find(this.yamlDocument.RootNode); - if (yamlNode == null) + if (referencePointer.Find(this.jsonNode) is not JsonNode jsonNode) { return null; } - return Create(this.Context, yamlNode); + return Create(this.Context, jsonNode); } public MapNode GetMap() { - return new MapNode(this.Context, (YamlMappingNode)this.yamlDocument.RootNode); + return new MapNode(this.Context, this.jsonNode); } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs index 385cfca0..17ec9ac7 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs @@ -2,23 +2,21 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes { - using LEGO.AsyncAPI.Models.Any; - using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.Exceptions; - using YamlDotNet.Core; - using YamlDotNet.RepresentationModel; + using System.Text.Json.Nodes; public class ValueNode : ParseNode { - private readonly YamlScalarNode node; - - public ValueNode(ParsingContext context, YamlNode node) + private readonly JsonNode node; + private string cachedScalarValue; + public ValueNode(ParsingContext context, JsonNode node) : base( context) { - if (!(node is YamlScalarNode scalarNode)) + if (!(node is JsonValue scalarNode)) { - throw new AsyncApiReaderException("Expected a value.", node); + throw new AsyncApiReaderException("Expected a value."); } this.node = scalarNode; @@ -26,14 +24,20 @@ public ValueNode(ParsingContext context, YamlNode node) public override string GetScalarValue() { - return this.node.Value; + if (this.cachedScalarValue == null) + { + this.cachedScalarValue = this.node.GetScalarValue(); + } + + return this.cachedScalarValue; } public override string GetScalarValueOrDefault(string defaultValue) { - if (this.node.Value is not null) + var value = this.GetScalarValue(); + if (value is not null) { - return this.node.Value; + return value; } return defaultValue; @@ -41,17 +45,17 @@ public override string GetScalarValueOrDefault(string defaultValue) public override int GetIntegerValue() { - if (int.TryParse(this.node.Value, out int value)) + if (int.TryParse(this.GetScalarValue(), out int value)) { return value; } - throw new AsyncApiReaderException("Value could not parse to integer", this.node); + throw new AsyncApiReaderException("Value could not parse to integer."); } public override int? GetIntegerValueOrDefault(int? defaultValue) { - if (int.TryParse(this.node.Value, out int value)) + if (int.TryParse(this.GetScalarValue(), out int value)) { return value; } @@ -61,17 +65,17 @@ public override int GetIntegerValue() public override long GetLongValue() { - if (long.TryParse(this.node.Value, out long value)) + if (long.TryParse(this.GetScalarValue(), out long value)) { return value; } - throw new AsyncApiReaderException("Value could not parse to long", this.node); + throw new AsyncApiReaderException("Value could not parse to long."); } public override long? GetLongValueOrDefault(long? defaultValue) { - if (long.TryParse(this.node.Value, out long value)) + if (long.TryParse(this.GetScalarValue(), out long value)) { return value; } @@ -81,17 +85,17 @@ public override long GetLongValue() public override bool GetBooleanValue() { - if (bool.TryParse(this.node.Value, out bool value)) + if (bool.TryParse(this.GetScalarValue(), out bool value)) { return value; } - throw new AsyncApiReaderException("Value could not parse to bool", this.node); + throw new AsyncApiReaderException("Value could not parse to bool."); } public override bool? GetBooleanValueOrDefault(bool? defaultValue) { - if (bool.TryParse(this.node.Value, out bool value)) + if (bool.TryParse(this.GetScalarValue(), out bool value)) { return value; } @@ -99,10 +103,10 @@ public override bool GetBooleanValue() return defaultValue; } - public override IAsyncApiAny CreateAny() + public override AsyncApiAny CreateAny() { var value = this.GetScalarValue(); - return new AsyncApiString(value, this.node.Style == ScalarStyle.SingleQuoted || this.node.Style == ScalarStyle.DoubleQuoted || this.node.Style == ScalarStyle.Literal || this.node.Style == ScalarStyle.Folded); + return new AsyncApiAny(this.node); } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs index c8b9c8d0..91f35d4a 100644 --- a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs +++ b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs @@ -5,19 +5,19 @@ namespace LEGO.AsyncAPI.Readers using System; using System.Collections.Generic; using System.Linq; + using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Exceptions; using LEGO.AsyncAPI.Readers.Interface; using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Readers.V2; - using YamlDotNet.RepresentationModel; public class ParsingContext { private readonly Stack currentLocation = new (); - internal Dictionary> ExtensionParsers + internal Dictionary> ExtensionParsers { get; set; @@ -44,9 +44,9 @@ public ParsingContext(AsyncApiDiagnostic diagnostic) this.Diagnostic = diagnostic; } - internal AsyncApiDocument Parse(YamlDocument yamlDocument) + internal AsyncApiDocument Parse(JsonNode jsonNode) { - this.RootNode = new RootNode(this, yamlDocument); + this.RootNode = new RootNode(this, jsonNode); var inputVersion = GetVersion(this.RootNode); @@ -67,9 +67,9 @@ internal AsyncApiDocument Parse(YamlDocument yamlDocument) return doc; } - internal T ParseFragment(YamlDocument yamlDocument, AsyncApiVersion version) where T : IAsyncApiElement + internal T ParseFragment(JsonNode jsonNode, AsyncApiVersion version) where T : IAsyncApiElement { - var node = ParseNode.Create(this, yamlDocument.RootNode); + var node = ParseNode.Create(this, jsonNode); T element = default(T); @@ -87,13 +87,7 @@ internal T ParseFragment(YamlDocument yamlDocument, AsyncApiVersion version) private static string GetVersion(RootNode rootNode) { var versionNode = rootNode.Find(new JsonPointer("/asyncapi")); - - if (versionNode != null) - { - return versionNode.GetScalarValue(); - } - - return versionNode?.GetScalarValue(); + return versionNode?.GetScalarValue().Replace("\"", string.Empty); } internal IAsyncApiVersionService VersionService { get; set; } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs index 98fed324..fcb0d91e 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDeserializer.cs @@ -1,20 +1,10 @@ // Copyright (c) The LEGO Group. All rights reserved. -using System.Collections.Generic; -using System.Linq; -using LEGO.AsyncAPI.Exceptions; -using LEGO.AsyncAPI.Expressions; -using LEGO.AsyncAPI.Models; -using LEGO.AsyncAPI.Models.Interfaces; -using LEGO.AsyncAPI.Readers.ParseNodes; - namespace LEGO.AsyncAPI.Readers { using System.Collections.Generic; using System.Linq; - using Extensions; using LEGO.AsyncAPI.Exceptions; - using LEGO.AsyncAPI.Expressions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.ParseNodes; @@ -49,11 +39,15 @@ internal static void ProcessAnyFields( { mapNode.Context.StartObject(anyFieldName); - var convertedAsyncApiAny = AsyncApiAnyConverter.GetSpecificAsyncApiAny( - anyFieldMap[anyFieldName].PropertyGetter(domainObject), - anyFieldMap[anyFieldName].SchemaGetter(domainObject)); - - anyFieldMap[anyFieldName].PropertySetter(domainObject, convertedAsyncApiAny); + var anyFieldValue = anyFieldMap[anyFieldName].PropertyGetter(domainObject); + if (anyFieldValue == null) + { + anyFieldMap[anyFieldName].PropertySetter(domainObject, null); + } + else + { + anyFieldMap[anyFieldName].PropertySetter(domainObject, anyFieldValue); + } } catch (AsyncApiException exception) { @@ -76,16 +70,13 @@ internal static void ProcessAnyListFields( { try { - var newProperty = new List(); + var newProperty = new List(); mapNode.Context.StartObject(anyListFieldName); foreach (var propertyElement in anyListFieldMap[anyListFieldName].PropertyGetter(domainObject)) { - newProperty.Add( - AsyncApiAnyConverter.GetSpecificAsyncApiAny( - propertyElement, - anyListFieldMap[anyListFieldName].SchemaGetter(domainObject))); + newProperty.Add(propertyElement); } anyListFieldMap[anyListFieldName].PropertySetter(domainObject, newProperty); @@ -111,7 +102,7 @@ private static void ProcessAnyMapFields( { try { - var newProperty = new List(); + var newProperty = new List(); mapNode.Context.StartObject(anyMapFieldName); @@ -123,11 +114,7 @@ private static void ProcessAnyMapFields( { var any = anyMapFieldMap[anyMapFieldName].PropertyGetter(propertyMapElement.Value); - var newAny = AsyncApiAnyConverter.GetSpecificAsyncApiAny( - any, - anyMapFieldMap[anyMapFieldName].SchemaGetter(domainObject)); - - anyMapFieldMap[anyMapFieldName].PropertySetter(propertyMapElement.Value, newAny); + anyMapFieldMap[anyMapFieldName].PropertySetter(propertyMapElement.Value, any); } } } @@ -143,33 +130,9 @@ private static void ProcessAnyMapFields( } } - private static RuntimeExpression LoadRuntimeExpression(ParseNode node) - { - var value = node.GetScalarValue(); - return RuntimeExpression.Build(value); - } - - private static RuntimeExpressionAnyWrapper LoadRuntimeExpressionAnyWrapper(ParseNode node) - { - var value = node.GetScalarValue(); - - if (value != null && value.StartsWith("$")) - { - return new RuntimeExpressionAnyWrapper - { - Expression = RuntimeExpression.Build(value), - }; - } - - return new RuntimeExpressionAnyWrapper - { - Any = AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()), - }; - } - - public static IAsyncApiAny LoadAny(ParseNode node) + public static AsyncApiAny LoadAny(ParseNode node) { - return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); + return node.CreateAny(); } public static IAsyncApiExtension LoadExtension(string name, ParseNode node) @@ -178,8 +141,7 @@ public static IAsyncApiExtension LoadExtension(string name, ParseNode node) { if (node.Context.ExtensionParsers.TryGetValue(name, out var parser)) { - return parser( - AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny())); + return parser(node.CreateAny()); } } catch (AsyncApiException ex) @@ -188,7 +150,7 @@ public static IAsyncApiExtension LoadExtension(string name, ParseNode node) node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); } - return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); + return node.CreateAny(); } private static string LoadString(ParseNode node) diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs index 6ea4cbb2..92a34c64 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs @@ -26,7 +26,7 @@ public AsyncApiV2VersionService(AsyncApiDiagnostic diagnostic) private IDictionary> loaders = new Dictionary> { - [typeof(IAsyncApiAny)] = AsyncApiV2Deserializer.LoadAny, + [typeof(AsyncApiAny)] = AsyncApiV2Deserializer.LoadAny, [typeof(AsyncApiComponents)] = AsyncApiV2Deserializer.LoadComponents, [typeof(AsyncApiExternalDocumentation)] = AsyncApiV2Deserializer.LoadExternalDocs, [typeof(AsyncApiInfo)] = AsyncApiV2Deserializer.LoadInfo, diff --git a/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs b/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs index 08036d04..a134c46d 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs @@ -27,8 +27,7 @@ public static IAsyncApiExtension LoadExtension(string name, ParseNode node) { if (node.Context.ExtensionParsers.TryGetValue(name, out var parser)) { - return parser( - AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny())); + return parser(node.CreateAny()); } } catch (AsyncApiException ex) @@ -37,7 +36,7 @@ public static IAsyncApiExtension LoadExtension(string name, ParseNode node) node.Context.Diagnostic.Errors.Add(new AsyncApiError(ex)); } - return AsyncApiAnyConverter.GetSpecificAsyncApiAny(node.CreateAny()); + return node.CreateAny(); } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/YamlConverter.cs b/src/LEGO.AsyncAPI.Readers/YamlConverter.cs new file mode 100644 index 00000000..e3dfcd43 --- /dev/null +++ b/src/LEGO.AsyncAPI.Readers/YamlConverter.cs @@ -0,0 +1,71 @@ +namespace LEGO.AsyncAPI.Readers +{ + using System; + using System.Globalization; + using System.Text.Json.Nodes; + using YamlDotNet.Core; + using YamlDotNet.RepresentationModel; + + internal static class YamlConverter + { + public static JsonNode ToJsonNode(this YamlDocument yamlDocument) + { + return yamlDocument.RootNode.ToJsonNode(); + } + + public static JsonObject ToJsonObject(this YamlMappingNode yamlMappingNode) + { + var node = new JsonObject(); + foreach (var keyValuePair in yamlMappingNode) + { + var key = ((YamlScalarNode)keyValuePair.Key).Value!; + node[key] = keyValuePair.Value.ToJsonNode(); + } + + return node; + } + + public static JsonArray ToJsonArray(this YamlSequenceNode yaml) + { + var node = new JsonArray(); + foreach (var value in yaml) + { + node.Add(value.ToJsonNode()); + } + + return node; + } + + public static JsonNode ToJsonNode(this YamlNode yaml) + { + return yaml switch + { + YamlMappingNode map => map.ToJsonObject(), + YamlSequenceNode seq => seq.ToJsonArray(), + YamlScalarNode scalar => scalar.ToJsonValue(), + _ => throw new NotSupportedException("This yaml isn't convertible to JSON") + }; + } + + private static JsonValue ToJsonValue(this YamlScalarNode yaml) + { + switch (yaml.Style) + { + case ScalarStyle.Plain: + return decimal.TryParse(yaml.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var d) + ? JsonValue.Create(d) + : bool.TryParse(yaml.Value, out var b) + ? JsonValue.Create(b) + : JsonValue.Create(yaml.Value)!; + case ScalarStyle.SingleQuoted: + case ScalarStyle.DoubleQuoted: + case ScalarStyle.Literal: + case ScalarStyle.Folded: + case ScalarStyle.Any: + return JsonValue.Create(yaml.Value); + default: + throw new ArgumentOutOfRangeException(); + } + } + } +} diff --git a/src/LEGO.AsyncAPI.Readers/YamlHelper.cs b/src/LEGO.AsyncAPI.Readers/YamlHelper.cs deleted file mode 100644 index 0f159c73..00000000 --- a/src/LEGO.AsyncAPI.Readers/YamlHelper.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using System.IO; - using System.Linq; - using LEGO.AsyncAPI.Exceptions; - using YamlDotNet.RepresentationModel; - internal static class YamlHelper - { - public static string GetScalarValue(this YamlNode node) - { - var scalarNode = node as YamlScalarNode; - if (scalarNode == null) - { - throw new AsyncApiException($"Expected scalar at line {node.Start.Line}"); - } - - return scalarNode.Value; - } - - public static YamlNode ParseYamlString(string yamlString) - { - var reader = new StringReader(yamlString); - var yamlStream = new YamlStream(); - yamlStream.Load(reader); - - var yamlDocument = yamlStream.Documents.First(); - return yamlDocument.RootNode; - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AnyType.cs b/src/LEGO.AsyncAPI/Models/Any/AnyType.cs deleted file mode 100644 index 5e608514..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AnyType.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// Type of an . - /// - public enum AnyType - { - /// - /// Primitive. - /// - Primitive, - - /// - /// Null. - /// - Null, - - /// - /// Array. - /// - Array, - - /// - /// Object. - /// - Object, - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs index 87d11aeb..ec62159a 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs @@ -1,25 +1,43 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Any +namespace LEGO.AsyncAPI.Models { - using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - /// - /// AsyncApi array. - /// - public class AsyncApiArray : List, IAsyncApiAny + public class AsyncApiArray : Collection, IAsyncApiExtension, IAsyncApiElement { - /// - /// The type of . - /// - public AnyType AnyType { get; } = AnyType.Array; + + public static explicit operator AsyncApiArray(AsyncApiAny any) + { + var a = new AsyncApiArray(); + if (any.Node is JsonArray arr) + { + foreach (var item in arr) + { + a.Add(new AsyncApiAny(item)); + } + } + + return a; + } + + public static implicit operator AsyncApiAny(AsyncApiArray arr) + { + var jArray = new JsonArray(); + foreach (var item in arr) + { + jArray.Add(item.Node); + } + + return new AsyncApiAny(jArray); + } /// - /// Write out contents of AsyncApiArray to passed writer. + /// Serialize AsyncApiObject to writer. /// - /// Instance of JSON or YAML writer. public void Write(IAsyncApiWriter writer) { writer.WriteStartArray(); diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIBoolean.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIBoolean.cs deleted file mode 100644 index 7a063be0..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIBoolean.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// AsyncApi boolean. - /// - public class AsyncApiBoolean : AsyncApiPrimitive - { - /// - /// Initializes the class. - /// - /// - public AsyncApiBoolean(bool value) - : base(value) - { - } - - /// - /// Primitive type this object represents. - /// - public override PrimitiveType PrimitiveType { get; } = PrimitiveType.Boolean; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIDouble.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIDouble.cs deleted file mode 100644 index e200cc83..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIDouble.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// AsyncApi Double. - /// - public class AsyncApiDouble : AsyncApiPrimitive - { - /// - /// Initializes the class. - /// - public AsyncApiDouble(double value) - : base(value) - { - } - - /// - /// Primitive type this object represents. - /// - public override PrimitiveType PrimitiveType { get; } = PrimitiveType.Double; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPILong.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPILong.cs deleted file mode 100644 index 3376d853..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPILong.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// AsyncApi long. - /// - public class AsyncApiLong : AsyncApiPrimitive - { - /// - /// Initializes the class. - /// - public AsyncApiLong(long value) - : base(value) - { - } - - /// - /// Primitive type this object represents. - /// - public override PrimitiveType PrimitiveType { get; } = PrimitiveType.Long; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPINull.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPINull.cs deleted file mode 100644 index 18cfe97d..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPINull.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// AsyncApi null. - /// - public class AsyncApiNull : IAsyncApiAny - { - /// - /// The type of . - /// - public AnyType AnyType { get; } = AnyType.Null; - - /// - /// Write out null representation. - /// - /// - public void Write(IAsyncApiWriter writer) - { - writer.WriteAny(this); - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs index fff2e840..f620a4f2 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs @@ -1,20 +1,28 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Any +namespace LEGO.AsyncAPI.Models { using System.Collections.Generic; + using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; /// /// AsyncApi object. /// - public class AsyncApiObject : Dictionary, IAsyncApiAny + public class AsyncApiObject : Dictionary, IAsyncApiExtension, IAsyncApiElement { - /// - /// Type of . - /// - public AnyType AnyType { get; } = AnyType.Object; + + public static implicit operator AsyncApiAny(AsyncApiObject obj) + { + var jObject = new JsonObject(); + foreach (var item in obj) + { + jObject.Add(item.Key, item.Value.Node); + } + + return new AsyncApiAny(jObject); + } /// /// Serialize AsyncApiObject to writer. diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIString.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIString.cs deleted file mode 100644 index 772b2b85..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIString.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// AsyncApi string type. - /// - public class AsyncApiString : AsyncApiPrimitive - { - private bool isExplicit; - private bool isRawString; - - /// - /// Initializes the class. - /// - /// - public AsyncApiString(string value) - : this(value, false) - { - } - - /// - /// Initializes the class. - /// - /// - /// Used to indicate if a string is quoted. - public AsyncApiString(string value, bool isExplicit) - : base(value) - { - this.isExplicit = isExplicit; - } - - /// - /// Initializes the class. - /// - /// - /// Used to indicate if a string is quoted. - /// Used to indicate to the writer that the value should be written without encoding. - public AsyncApiString(string value, bool isExplicit, bool isRawString) - : base(value) - { - this.isExplicit = isExplicit; - this.isRawString = isRawString; - } - - /// - /// The primitive class this object represents. - /// - public override PrimitiveType PrimitiveType { get; } = PrimitiveType.String; - - /// - /// True if string was specified explicitly by the means of double quotes, single quotes, or literal or folded style. - /// - public bool IsExplicit() - { - return this.isExplicit; - } - - /// - /// True if the writer should process the value as supplied without encoding. - /// - public bool IsRawString() - { - return this.isRawString; - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs new file mode 100644 index 00000000..3b866cd6 --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs @@ -0,0 +1,49 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System.Text.Json.Nodes; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// AsyncApiAny. + /// + /// + /// + public class AsyncApiAny : IAsyncApiElement, IAsyncApiExtension + { + private JsonNode node; + + /// + /// Initializes a new instance of the class. + /// + /// The node. + public AsyncApiAny(JsonNode node) + { + this.node = node; + } + + /// + /// Gets the node. + /// + /// + /// The node. + /// + public JsonNode Node => this.node; + + public T GetValue() + { + return this.node.GetValue(); + } + + /// + /// Writes the Any type. + /// + /// The writer. + public void Write(IAsyncApiWriter writer) + { + writer.WriteAny(new AsyncApiAny(this.node)); + } + } +} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiBinary.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiBinary.cs deleted file mode 100644 index d6f62fe1..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiBinary.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// AsyncApi binary. - /// - public class AsyncApiBinary : AsyncApiPrimitive - { - /// - /// Initializes the class. - /// - /// - public AsyncApiBinary(byte[] value) - : base(value) - { - } - - /// - /// Primitive type this object represents. - /// - public override PrimitiveType PrimitiveType { get; } = PrimitiveType.Binary; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiByte.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiByte.cs deleted file mode 100644 index 91f96b56..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiByte.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// AsyncApi Byte. - /// - public class AsyncApiByte : AsyncApiPrimitive - { - /// - /// Initializes the class. - /// - public AsyncApiByte(byte value) - : this(new byte[] { value }) - { - } - - /// - /// Initializes the class. - /// - public AsyncApiByte(byte[] value) - : base(value) - { - } - - /// - /// Primitive type this object represents. - /// - public override PrimitiveType PrimitiveType { get; } = PrimitiveType.Byte; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiDate.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiDate.cs deleted file mode 100644 index ef223535..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiDate.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using System; - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// AsyncApi Date. - /// - public class AsyncApiDate : AsyncApiPrimitive - { - /// - /// Initializes the class. - /// - public AsyncApiDate(DateTime value) - : base(value) - { - } - - /// - /// Primitive type this object represents. - /// - public override PrimitiveType PrimitiveType { get; } = PrimitiveType.Date; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiDateTime.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiDateTime.cs deleted file mode 100644 index e93fe59e..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiDateTime.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using System; - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// AsyncApi Datetime. - /// - public class AsyncApiDateTime : AsyncApiPrimitive - { - /// - /// Initializes the class. - /// - public AsyncApiDateTime(DateTimeOffset value) - : base(value) - { - } - - /// - /// Primitive type this object represents. - /// - public override PrimitiveType PrimitiveType { get; } = PrimitiveType.DateTime; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiFloat.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiFloat.cs deleted file mode 100644 index a824e75c..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiFloat.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// AsyncApi Float. - /// - public class AsyncApiFloat : AsyncApiPrimitive - { - /// - /// Initializes the class. - /// - public AsyncApiFloat(float value) - : base(value) - { - } - - /// - /// Primitive type this object represents. - /// - public override PrimitiveType PrimitiveType { get; } = PrimitiveType.Float; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiInteger.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiInteger.cs deleted file mode 100644 index e9453a89..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiInteger.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using LEGO.AsyncAPI.Models.Interfaces; - - /// - /// AsyncApi Integer. - /// - public class AsyncApiInteger : AsyncApiPrimitive - { - /// - /// Initializes the class. - /// - public AsyncApiInteger(int value) - : base(value) - { - } - - /// - /// Primitive type this object represents. - /// - public override PrimitiveType PrimitiveType { get; } = PrimitiveType.Integer; - } -} diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiPrimitive{T}.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiPrimitive{T}.cs deleted file mode 100644 index 99ac381d..00000000 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiPrimitive{T}.cs +++ /dev/null @@ -1,130 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Any -{ - using System; - using System.Text; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// AsyncApi primitive class. - /// - /// - public abstract class AsyncApiPrimitive : IAsyncApiPrimitive - { - /// - /// Initializes the class with the given value. - /// - /// - public AsyncApiPrimitive(T value) - { - this.Value = value; - } - - /// - /// The kind of . - /// - public AnyType AnyType { get; } = AnyType.Primitive; - - /// - /// The primitive class this object represents. - /// - public abstract PrimitiveType PrimitiveType { get; } - - /// - /// Value of this . - /// - public T Value { get; } - - /// - /// Write out content of primitive element. - /// - /// - public void Write(IAsyncApiWriter writer) - { - switch (this.PrimitiveType) - { - case PrimitiveType.Integer: - var intValue = (AsyncApiInteger)(IAsyncApiPrimitive)this; - writer.WriteValue(intValue.Value); - break; - - case PrimitiveType.Long: - var longValue = (AsyncApiLong)(IAsyncApiPrimitive)this; - writer.WriteValue(longValue.Value); - break; - - case PrimitiveType.Float: - var floatValue = (AsyncApiFloat)(IAsyncApiPrimitive)this; - writer.WriteValue(floatValue.Value); - break; - - case PrimitiveType.Double: - var doubleValue = (AsyncApiDouble)(IAsyncApiPrimitive)this; - writer.WriteValue(doubleValue.Value); - break; - - case PrimitiveType.String: - var stringValue = (AsyncApiString)(IAsyncApiPrimitive)this; - if (stringValue.IsRawString()) - { - writer.WriteRaw(stringValue.Value); - } - else - { - writer.WriteValue(stringValue.Value); - } - - break; - - case PrimitiveType.Byte: - var byteValue = (AsyncApiByte)(IAsyncApiPrimitive)this; - if (byteValue.Value == null) - { - writer.WriteNull(); - } - else - { - writer.WriteValue(Convert.ToBase64String(byteValue.Value)); - } - - break; - - case PrimitiveType.Binary: - var binaryValue = (AsyncApiBinary)(IAsyncApiPrimitive)this; - if (binaryValue.Value == null) - { - writer.WriteNull(); - } - else - { - writer.WriteValue(Encoding.UTF8.GetString(binaryValue.Value)); - } - - break; - - case PrimitiveType.Boolean: - var boolValue = (AsyncApiBoolean)(IAsyncApiPrimitive)this; - writer.WriteValue(boolValue.Value); - break; - - case PrimitiveType.Date: - var dateValue = (AsyncApiDate)(IAsyncApiPrimitive)this; - writer.WriteValue(dateValue.Value); - break; - - case PrimitiveType.DateTime: - var dateTimeValue = (AsyncApiDateTime)(IAsyncApiPrimitive)this; - writer.WriteValue(dateTimeValue.Value); - break; - - default: - throw new AsyncApiWriterException( - string.Format( - "The given primitive type '{0}' is not supported.", - this.PrimitiveType)); - } - } - } -} diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiMessageExample.cs b/src/LEGO.AsyncAPI/Models/AsyncApiMessageExample.cs index f4bfa083..3af20b39 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiMessageExample.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiMessageExample.cs @@ -15,12 +15,12 @@ public class AsyncApiMessageExample : IAsyncApiExtensible, IAsyncApiSerializable /// /// Gets or sets the value of this field MUST validate against the Message Object's headers field. /// - public IDictionary Headers { get; set; } = new Dictionary(); + public IDictionary Headers { get; set; } = new Dictionary(); /// /// Gets or sets the value of this field MUST validate against the Message Object's payload field. /// - public IAsyncApiAny Payload { get; set; } + public AsyncApiAny Payload { get; set; } /// /// a machine-friendly name. diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs index 85fcc13c..3244017b 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs @@ -81,7 +81,7 @@ public class AsyncApiSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyn /// 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 IAsyncApiAny Default { get; set; } + public AsyncApiAny Default { get; set; } /// /// a value indicating whether relevant only for Schema "properties" definitions. Declares the property as "read only". @@ -220,17 +220,17 @@ public class AsyncApiSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyn /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public IList Enum { get; set; } = new List(); + public 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 IList Examples { get; set; } = new List(); /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public IAsyncApiAny Const { get; set; } + public AsyncApiAny Const { get; set; } /// /// a value indicating whether allows sending a null value for the defined schema. Default value is false. diff --git a/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiAny.cs b/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiAny.cs deleted file mode 100644 index dfabf5b3..00000000 --- a/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiAny.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Interfaces -{ - using LEGO.AsyncAPI.Models.Any; - - /// - /// Base interface for all the types that represent AsyncAPI Any. - /// - public interface IAsyncApiAny : IAsyncApiElement, IAsyncApiExtension - { - /// - /// Gets type of an . - /// - AnyType AnyType { get; } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiPrimitive.cs b/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiPrimitive.cs deleted file mode 100644 index 618a66bd..00000000 --- a/src/LEGO.AsyncAPI/Models/Interfaces/IAsyncApiPrimitive.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models.Interfaces -{ - /// - /// Primitive type. - /// - public enum PrimitiveType - { - /// - /// Integer - /// - Integer, - - /// - /// Long - /// - Long, - - /// - /// Float - /// - Float, - - /// - /// Double - /// - Double, - - /// - /// String - /// - String, - - /// - /// Byte - /// - Byte, - - /// - /// Binary - /// - Binary, - - /// - /// Boolean - /// - Boolean, - - /// - /// Date - /// - Date, - - /// - /// DateTime - /// - DateTime, - } - - /// - /// Base interface for the Primitive type. - /// - public interface IAsyncApiPrimitive : IAsyncApiAny - { - /// - /// Primitive type. - /// - PrimitiveType PrimitiveType { get; } - } -} diff --git a/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs b/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs index 6c7e1428..130746ea 100644 --- a/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs +++ b/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs @@ -7,17 +7,17 @@ namespace LEGO.AsyncAPI.Models using LEGO.AsyncAPI.Writers; /// - /// The wrapper either for or . + /// The wrapper either for or . /// public class RuntimeExpressionAnyWrapper : IAsyncApiElement { - private IAsyncApiAny any; + private AsyncApiAny any; private RuntimeExpression expression; /// - /// Gets/Sets the . + /// Gets/Sets the . /// - public IAsyncApiAny Any + public AsyncApiAny Any { get { diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs index 7a025ddb..8e3dc241 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs @@ -55,7 +55,7 @@ public virtual void Visit(AsyncApiDocument doc) { } - public virtual void Visit(IDictionary anys) + public virtual void Visit(IDictionary anys) { } diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs index 9a2184ed..cc174747 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs @@ -619,7 +619,7 @@ internal void Walk(AsyncApiMessageExample example) this.Walk(example as IAsyncApiExtensible); } - internal void Walk(IDictionary anys) + internal void Walk(IDictionary anys) { if (anys == null) { @@ -834,7 +834,7 @@ internal void Walk(AsyncApiContact contact) this.visitor.Visit(contact); } - internal void Walk(IAsyncApiAny any) + internal void Walk(AsyncApiAny any) { if (any == null) { diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs index 79576005..93245c22 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs @@ -2,12 +2,27 @@ namespace LEGO.AsyncAPI.Validation.Rules { + using System.Net.Mail; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Validations; [AsyncApiRule] public static class AsyncApiContactRules { + private static bool IsEmailAddress(this string input) + { + try + { + _ = new MailAddress(input); + } + catch (System.Exception) + { + return false; + } + + return true; + } + public static ValidationRule EmailMustBeEmailFormat => new ValidationRule( (context, contact) => diff --git a/src/LEGO.AsyncAPI/Validation/Rules/RuleHelpers.cs b/src/LEGO.AsyncAPI/Validation/Rules/RuleHelpers.cs deleted file mode 100644 index 57a31789..00000000 --- a/src/LEGO.AsyncAPI/Validation/Rules/RuleHelpers.cs +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Validation.Rules -{ - using System.Net.Mail; - using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Validations; - - internal static class RuleHelpers - { - internal const string DataTypeMismatchedErrorMessage = "Data and type mismatch found."; - - /// - /// Input string must be in the format of an email address. - /// - /// The input string. - /// True if it's an email address. Otherwise False. - public static bool IsEmailAddress(this string input) - { - try - { - _ = new MailAddress(input); - } - catch (System.Exception) - { - return false; - } - - return true; - } - - public static void ValidateDataTypeMismatch( - IValidationContext context, - string ruleName, - IAsyncApiAny value, - AsyncApiSchema schema) - { - if (schema == null) - { - return; - } - - var types = EnumExtensions.GetFlags(schema.Type); - var format = schema.Format; - var nullable = schema.Nullable; - - // Before checking the type, check first if the schema allows null. - // If so and the data given is also null, this is allowed for any type. - if (nullable) - { - if (value is AsyncApiNull) - { - return; - } - } - - foreach (var type in types) - { - if (type == SchemaType.Object) - { - // It is not against the spec to have a string representing an object value. - // To represent examples of media types that cannot naturally be represented in JSON or YAML, - // a string value can contain the example with escaping where necessary - if (value is AsyncApiString) - { - return; - } - - // If value is not a string and also not an object, there is a data mismatch. - if (!(value is AsyncApiObject)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - return; - } - - var anyObject = (AsyncApiObject)value; - - foreach (var key in anyObject.Keys) - { - context.Enter(key); - - if (schema.Properties != null && schema.Properties.ContainsKey(key)) - { - ValidateDataTypeMismatch(context, ruleName, anyObject[key], schema.Properties[key]); - } - else - { - ValidateDataTypeMismatch(context, ruleName, anyObject[key], schema.AdditionalProperties); - } - - context.Exit(); - } - - return; - } - - if (type == SchemaType.Array) - { - // It is not against the spec to have a string representing an array value. - // To represent examples of media types that cannot naturally be represented in JSON or YAML, - // a string value can contain the example with escaping where necessary - if (value is AsyncApiString) - { - return; - } - - // If value is not a string and also not an array, there is a data mismatch. - if (!(value is AsyncApiArray)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - return; - } - - var anyArray = (AsyncApiArray)value; - - for (int i = 0; i < anyArray.Count; i++) - { - context.Enter(i.ToString()); - - ValidateDataTypeMismatch(context, ruleName, anyArray[i], schema.Items); - - context.Exit(); - } - - return; - } - - if (type == SchemaType.Integer && format == "int32") - { - if (!(value is AsyncApiInteger)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - - if (type == SchemaType.Integer && format == "int64") - { - if (!(value is AsyncApiLong)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - - if (type == SchemaType.Integer && !(value is AsyncApiInteger)) - { - if (!(value is AsyncApiInteger)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - - if (type == SchemaType.Number && format == "float") - { - if (!(value is AsyncApiFloat)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - - if (type == SchemaType.Number && format == "double") - { - if (!(value is AsyncApiDouble)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - - if (type == SchemaType.Number) - { - if (!(value is AsyncApiDouble)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - - if (type == SchemaType.String && format == "byte") - { - if (!(value is AsyncApiByte)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - - if (type == SchemaType.String && format == "date") - { - if (!(value is AsyncApiDate)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - - if (type == SchemaType.String && format == "date-time") - { - if (!(value is AsyncApiDateTime)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - - if (type == SchemaType.String) - { - if (!(value is AsyncApiString)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - - if (type == SchemaType.Boolean) - { - if (!(value is AsyncApiBoolean)) - { - context.CreateWarning( - ruleName, - DataTypeMismatchedErrorMessage); - } - - return; - } - } - } - } -} diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs index 4f31e62d..201ce127 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs @@ -4,7 +4,9 @@ namespace LEGO.AsyncAPI.Writers { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Any; + using System.Text.Json; + using System.Text.Json.Nodes; + using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; public static class AsyncApiWriterAnyExtensions @@ -27,45 +29,57 @@ public static void WriteExtensions(this IAsyncApiWriter writer, IDictionary - /// Write the value. + /// Write the value. /// /// The AsyncApi Any type. /// The AsyncApi writer. /// The Any value. - public static void WriteAny(this IAsyncApiWriter writer, T any) where T : IAsyncApiAny + public static void WriteAny(this IAsyncApiWriter writer, AsyncApiAny any) { if (writer is null) { throw new ArgumentNullException(nameof(writer)); } - if (any == null) + if (any.Node == null) { writer.WriteNull(); return; } - switch (any.AnyType) + var node = any.Node; + + var element = JsonDocument.Parse(node.ToJsonString()).RootElement; + switch (element.ValueKind) { - case AnyType.Array: // Array - writer.WriteArray(any as AsyncApiArray); + case JsonValueKind.Array: // Array + writer.WriteArray(node as JsonArray); break; - case AnyType.Object: // Object - writer.WriteObject(any as AsyncApiObject); + case JsonValueKind.Object: // Object + writer.WriteObject(node as JsonObject); break; - case AnyType.Primitive: // Primitive - writer.WritePrimitive(any as IAsyncApiPrimitive); + case JsonValueKind.String: + case JsonValueKind.Number: + case JsonValueKind.False or JsonValueKind.True: + writer.WritePrimitive(element); break; - case AnyType.Null: // null + case JsonValueKind.Null: // null writer.WriteNull(); break; default: @@ -73,7 +87,7 @@ public static void WriteAny(this IAsyncApiWriter writer, T any) where T : IAs } } - private static void WriteArray(this IAsyncApiWriter writer, AsyncApiArray array) + private static void WriteArray(this IAsyncApiWriter writer, JsonArray array) { if (writer is null) { @@ -89,13 +103,13 @@ private static void WriteArray(this IAsyncApiWriter writer, AsyncApiArray array) foreach (var item in array) { - writer.WriteAny(item); + writer.WriteAny(new AsyncApiAny(item)); } writer.WriteEndArray(); } - private static void WriteObject(this IAsyncApiWriter writer, AsyncApiObject entity) + private static void WriteObject(this IAsyncApiWriter writer, JsonObject entity) { if (writer is null) { @@ -112,25 +126,58 @@ private static void WriteObject(this IAsyncApiWriter writer, AsyncApiObject enti foreach (var item in entity) { writer.WritePropertyName(item.Key); - writer.WriteAny(item.Value); + writer.WriteAny(new AsyncApiAny(item.Value)); } writer.WriteEndObject(); } - private static void WritePrimitive(this IAsyncApiWriter writer, IAsyncApiPrimitive primitive) + private static void WritePrimitive(this IAsyncApiWriter writer, JsonElement primitive) { if (writer is null) { throw new ArgumentNullException(nameof(writer)); } - if (primitive is null) + if (primitive.ValueKind == JsonValueKind.String) { - throw new ArgumentNullException(nameof(primitive)); + if (primitive.TryGetDateTime(out var dateTime)) + { + writer.WriteValue(dateTime); + } + else if (primitive.TryGetDateTimeOffset(out var dateTimeOffset)) + { + writer.WriteValue(dateTimeOffset); + } + else + { + writer.WriteValue(primitive.GetString()); + } } - primitive.Write(writer); + if (primitive.ValueKind == JsonValueKind.Number) + { + if (primitive.TryGetDecimal(out var decimalValue)) + { + writer.WriteValue(decimalValue); + } + else if (primitive.TryGetDouble(out var doubleValue)) + { + writer.WriteValue(doubleValue); + } + else if (primitive.TryGetInt64(out var longValue)) + { + writer.WriteValue(longValue); + } + else if (primitive.TryGetInt32(out var intValue)) + { + writer.WriteValue(intValue); + } + } + if (primitive.ValueKind is JsonValueKind.True or JsonValueKind.False) + { + writer.WriteValue(primitive.GetBoolean()); + } } } } diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index f4a26e78..526f164b 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -12,7 +12,6 @@ namespace LEGO.AsyncAPI.Tests using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Writers; @@ -577,10 +576,10 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() "command", new AsyncApiSchema() { Type = SchemaType.String, - Enum = new List + Enum = new List { - new AsyncApiString("on"), - new AsyncApiString("off"), + new AsyncApiAny("on"), + new AsyncApiAny("off"), }, Description = "Whether to turn on or off the light." } @@ -676,9 +675,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() ClientId = new AsyncApiSchema() { Type = SchemaType.String, - Enum = new List + Enum = new List { - new AsyncApiString("my-app-id"), + new AsyncApiAny("my-app-id"), }, }, } @@ -893,7 +892,7 @@ public void SerializeV2_WithFullSpec_Serializes() AuthorizationUrl = new Uri(authorizationUrl), Extensions = new Dictionary { - { extensionKey, new AsyncApiString(extensionString) }, + { extensionKey, new AsyncApiAny(extensionString) }, }, }, }, @@ -948,14 +947,14 @@ public void SerializeV2_WithFullSpec_Serializes() Url = new Uri(licenseUri), Extensions = new Dictionary { - { extensionKey, new AsyncApiString(extensionString) }, + { extensionKey, new AsyncApiAny(extensionString) }, }, }, Version = apiVersion, TermsOfService = new Uri(termsOfServiceUri), Extensions = new Dictionary { - { extensionKey, new AsyncApiString(extensionString) }, + { extensionKey, new AsyncApiAny(extensionString) }, }, }, Channels = new Dictionary @@ -1001,7 +1000,7 @@ public void SerializeV2_WithFullSpec_Serializes() Description = correlationDescription, Extensions = new Dictionary { - { extensionKey, new AsyncApiString(extensionString) }, + { extensionKey, new AsyncApiAny(extensionString) }, }, }, Traits = new List @@ -1015,12 +1014,12 @@ public void SerializeV2_WithFullSpec_Serializes() Title = schemaTitle, WriteOnly = true, Description = schemaDescription, - Examples = new List + Examples = new List { new AsyncApiObject { - { anyKey, new AsyncApiString(anyStringValue) }, - { anyOtherKey, new AsyncApiLong(anyLongValue) }, + { anyKey, new AsyncApiAny(anyStringValue) }, + { anyOtherKey, new AsyncApiAny(anyLongValue) }, }, }, }, @@ -1032,12 +1031,12 @@ public void SerializeV2_WithFullSpec_Serializes() Name = exampleName, Payload = new AsyncApiObject { - { anyKey, new AsyncApiString(anyStringValue) }, - { anyOtherKey, new AsyncApiLong(anyLongValue) }, + { anyKey, new AsyncApiAny(anyStringValue) }, + { anyOtherKey, new AsyncApiAny(anyLongValue) }, }, Extensions = new Dictionary { - { extensionKey, new AsyncApiString(extensionString) }, + { extensionKey, new AsyncApiAny(extensionString) }, }, }, }, @@ -1058,20 +1057,20 @@ public void SerializeV2_WithFullSpec_Serializes() }, Extensions = new Dictionary { - { extensionKey, new AsyncApiString(extensionString) }, + { extensionKey, new AsyncApiAny(extensionString) }, }, }, }, Extensions = new Dictionary { - { extensionKey, new AsyncApiString(extensionString) }, + { extensionKey, new AsyncApiAny(extensionString) }, }, } }, }, Extensions = new Dictionary { - { extensionKey, new AsyncApiString(extensionString) }, + { extensionKey, new AsyncApiAny(extensionString) }, }, Tags = new List { @@ -1103,7 +1102,7 @@ public void SerializeV2_WithFullSpec_Serializes() OperationId = operationId, Extensions = new Dictionary { - { extensionKey, new AsyncApiString(extensionString) }, + { extensionKey, new AsyncApiAny(extensionString) }, }, }, }, diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs index 7e66eb06..bcb25150 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs @@ -5,15 +5,13 @@ namespace LEGO.AsyncAPI.Tests using System; using System.Collections.Generic; using System.IO; - using System.Linq; + using System.Text.Json.Nodes; using FluentAssertions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Readers.ParseNodes; using NUnit.Framework; - using YamlDotNet.RepresentationModel; public class AsyncApiLicenseTests { @@ -31,7 +29,7 @@ public void Serialize_WithAllProperties_Serializes() Url = new Uri("https://example.com/license"), Extensions = new Dictionary { - ["x-extension"] = new AsyncApiString("value"), + ["x-extension"] = new AsyncApiAny("value"), }, }; @@ -64,16 +62,12 @@ public void LoadLicense_WithJson_Deserializes() ""x-extension"": ""value"" }"; - using (var stream = GenerateStreamFromString(input)) - { - var yamlStream = new YamlStream(); - yamlStream.Load(new StreamReader(stream)); - var yamlNode = yamlStream.Documents.First().RootNode; - + using (var stream = GenerateStreamFromString(input)) + { var diagnostic = new AsyncApiDiagnostic(); var context = new ParsingContext(diagnostic); - var node = new MapNode(context, (YamlMappingNode)yamlNode); + var node = new MapNode(context, JsonNode.Parse(stream)); // Act var actual = AsyncApiV2Deserializer.LoadLicense(node); @@ -85,7 +79,7 @@ public void LoadLicense_WithJson_Deserializes() Url = new Uri("https://example.com/license"), Extensions = new Dictionary { - ["x-extension"] = new AsyncApiString("value"), + ["x-extension"] = new AsyncApiAny("value"), }, }; actual.Should().BeEquivalentTo( diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index a5d6a7a6..33a593f0 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -5,9 +5,9 @@ namespace LEGO.AsyncAPI.Tests using System; using System.Collections.Generic; using System.Linq; + using System.Text.Json.Nodes; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -38,22 +38,22 @@ public void Read_WithExtensionParser_Parses() workspace: {extensionName}: onetwothreefour "; - Func valueExtensionParser = (any) => + Func valueExtensionParser = (any) => { - if (any.AnyType == AnyType.Primitive && any is AsyncApiString value) + if (any.Node is JsonValue value) { - if (value.Value == "onetwothreefour") + if (value.GetScalarValue() == "onetwothreefour") { - return new AsyncApiInteger(1234); + return new AsyncApiAny(1234); } } - return new AsyncApiString("No value provided"); + return new AsyncApiAny("No value provided"); }; var settings = new AsyncApiReaderSettings { - ExtensionParsers = new Dictionary> + ExtensionParsers = new Dictionary> { { extensionName, valueExtensionParser }, }, @@ -61,7 +61,7 @@ public void Read_WithExtensionParser_Parses() var reader = new AsyncApiStringReader(settings); var doc = reader.Read(yaml, out var diagnostic); - Assert.AreEqual((doc.Channels["workspace"].Extensions[extensionName] as AsyncApiInteger).Value, 1234); + Assert.AreEqual((doc.Channels["workspace"].Extensions[extensionName] as AsyncApiAny).GetValue(), 1234); } [Test] @@ -80,14 +80,14 @@ public void Read_WithThrowingExtensionParser_AddsToDiagnostics() workspace: {extensionName}: onetwothreefour "; - Func failingExtensionParser = (any) => + Func failingExtensionParser = (any) => { throw new AsyncApiException("Failed to parse"); }; var settings = new AsyncApiReaderSettings { - ExtensionParsers = new Dictionary> + ExtensionParsers = new Dictionary> { { extensionName, failingExtensionParser }, }, diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs index ab7df131..623f6d2b 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs @@ -6,7 +6,6 @@ namespace LEGO.AsyncAPI.Tests.Bindings using FluentAssertions; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Readers.ParseNodes; @@ -41,7 +40,7 @@ public class MyBinding : ChannelBinding public NestedConfiguration NestedConfiguration { get; set; } - public IAsyncApiAny Any { get; set; } + public AsyncApiAny Any { get; set; } protected override FixedFieldMap FixedFieldMap => new FixedFieldMap() { @@ -87,7 +86,7 @@ public void CustomBinding_SerializesDeserializes() Custom = "someValue", Any = new AsyncApiObject() { - { "anyKeyName", new AsyncApiString("anyValue") }, + { "anyKeyName", new AsyncApiAny("anyValue") }, }, BindingVersion = "0.1.0", NestedConfiguration = new NestedConfiguration() @@ -95,12 +94,12 @@ public void CustomBinding_SerializesDeserializes() Name = "nested", Extensions = new Dictionary() { - { "x-myNestedExtension", new AsyncApiString("nestedValue") }, + { "x-myNestedExtension", new AsyncApiAny("nestedValue") }, }, }, Extensions = new Dictionary() { - { "x-myextension", new AsyncApiString("someValue") }, + { "x-myextension", new AsyncApiAny("someValue") }, }, }); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 7a2269bb..0ae28e88 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -1,7 +1,4 @@ -using System; -using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; -using BindingsCollection = LEGO.AsyncAPI.Bindings.BindingsCollection; namespace LEGO.AsyncAPI.Tests.Bindings.Sns { @@ -64,7 +61,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() "x-orderingExtension", new AsyncApiObject() { - { "orderingXPropertyName", new AsyncApiString("orderingXPropertyValue") }, + { "orderingXPropertyName", new AsyncApiAny("orderingXPropertyValue") }, } }, }, @@ -76,11 +73,11 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), Action = new StringOrStringList(new AsyncApiArray() { - new AsyncApiString("sns:Publish"), - new AsyncApiString("sns:Delete") + new AsyncApiAny("sns:Publish"), + new AsyncApiAny("sns:Delete") }), }, new Statement() @@ -88,17 +85,17 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() Effect = Effect.Allow, Principal = new StringOrStringList(new AsyncApiArray() { - new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann"), - new AsyncApiString("arn:aws:iam::123456789012:user/dec.kolakowski") + new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"), + new AsyncApiAny("arn:aws:iam::123456789012:user/dec.kolakowski") }), - Action = new StringOrStringList(new AsyncApiString("sns:Create")), + Action = new StringOrStringList(new AsyncApiAny("sns:Create")), Extensions = new Dictionary() { { "x-statementExtension", new AsyncApiObject() { - { "statementXPropertyName", new AsyncApiString("statementXPropertyValue") }, + { "statementXPropertyName", new AsyncApiAny("statementXPropertyValue") }, } }, }, @@ -110,7 +107,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() "x-policyExtension", new AsyncApiObject() { - { "policyXPropertyName", new AsyncApiString("policyXPropertyValue") }, + { "policyXPropertyName", new AsyncApiAny("policyXPropertyValue") }, } }, }, @@ -126,7 +123,7 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() "x-bindingExtension", new AsyncApiObject() { - { "bindingXPropertyName", new AsyncApiString("bindingXPropertyValue") }, + { "bindingXPropertyName", new AsyncApiAny("bindingXPropertyValue") }, } }, }, @@ -229,7 +226,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-identifierExtension", new AsyncApiObject() { - { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + { "identifierXPropertyName", new AsyncApiAny("identifierXPropertyValue") }, } }, }, @@ -248,7 +245,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-identifierExtension", new AsyncApiObject() { - { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + { "identifierXPropertyName", new AsyncApiAny("identifierXPropertyValue") }, } }, }, @@ -257,29 +254,29 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { Attributes = new AsyncApiObject() { - { "store", new AsyncApiArray() { new AsyncApiString("asyncapi_corp") } }, - { "contact", new AsyncApiString("dec.kolakowski") }, + { "store", new AsyncApiArray() { new AsyncApiAny("asyncapi_corp") } }, + { "contact", new AsyncApiAny("dec.kolakowski") }, { "event", new AsyncApiArray() { new AsyncApiObject() { - { "anything-but", new AsyncApiString("order_cancelled") }, + { "anything-but", new AsyncApiAny("order_cancelled") }, }, } }, { "order_key", new AsyncApiObject() { - { "transient", new AsyncApiString("by_area") }, + { "transient", new AsyncApiAny("by_area") }, } }, { "customer_interests", new AsyncApiArray() { - new AsyncApiString("rugby"), - new AsyncApiString("football"), - new AsyncApiString("baseball"), + new AsyncApiAny("rugby"), + new AsyncApiAny("football"), + new AsyncApiAny("baseball"), } }, }, @@ -289,7 +286,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-filterPolicyExtension", new AsyncApiObject() { - { "filterPolicyXPropertyName", new AsyncApiString("filterPolicyXPropertyValue") }, + { "filterPolicyXPropertyName", new AsyncApiAny("filterPolicyXPropertyValue") }, } }, }, @@ -306,7 +303,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-identifierExtension", new AsyncApiObject() { - { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + { "identifierXPropertyName", new AsyncApiAny("identifierXPropertyValue") }, } }, }, @@ -318,7 +315,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-redrivePolicyExtension", new AsyncApiObject() { - { "redrivePolicyXPropertyName", new AsyncApiString("redrivePolicyXPropertyValue") }, + { "redrivePolicyXPropertyName", new AsyncApiAny("redrivePolicyXPropertyValue") }, } }, }, @@ -339,7 +336,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-deliveryPolicyExtension", new AsyncApiObject() { - { "deliveryPolicyXPropertyName", new AsyncApiString("deliveryPolicyXPropertyValue") }, + { "deliveryPolicyXPropertyName", new AsyncApiAny("deliveryPolicyXPropertyValue") }, } }, }, @@ -350,7 +347,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-consumerExtension", new AsyncApiObject() { - { "consumerXPropertyName", new AsyncApiString("consumerXPropertyValue") }, + { "consumerXPropertyName", new AsyncApiAny("consumerXPropertyValue") }, } }, }, @@ -372,7 +369,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-deliveryPolicyExtension", new AsyncApiObject() { - { "deliveryPolicyXPropertyName", new AsyncApiString("deliveryPolicyXPropertyValue") }, + { "deliveryPolicyXPropertyName", new AsyncApiAny("deliveryPolicyXPropertyValue") }, } }, }, @@ -383,7 +380,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-bindingExtension", new AsyncApiObject() { - { "bindingXPropertyName", new AsyncApiString("bindingXPropertyValue") }, + { "bindingXPropertyName", new AsyncApiAny("bindingXPropertyValue") }, } }, }, diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index 34c361e1..50a6fe53 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -5,7 +5,6 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Sqs using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Sqs; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -93,7 +92,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() "x-identifierExtension", new AsyncApiObject() { - { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + { "identifierXPropertyName", new AsyncApiAny("identifierXPropertyValue") }, } }, }, @@ -105,7 +104,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() "x-redrivePolicyExtension", new AsyncApiObject() { - { "redrivePolicyXPropertyName", new AsyncApiString("redrivePolicyXPropertyValue") }, + { "redrivePolicyXPropertyName", new AsyncApiAny("redrivePolicyXPropertyValue") }, } }, }, @@ -117,11 +116,11 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), Action = new StringOrStringList(new AsyncApiArray() { - new AsyncApiString("sqs:SendMessage"), - new AsyncApiString("sqs:ReceiveMessage") + new AsyncApiAny("sqs:SendMessage"), + new AsyncApiAny("sqs:ReceiveMessage") }), Extensions = new Dictionary() { @@ -129,7 +128,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() "x-statementExtension", new AsyncApiObject() { - { "statementXPropertyName", new AsyncApiString("statementXPropertyValue") }, + { "statementXPropertyName", new AsyncApiAny("statementXPropertyValue") }, } }, }, @@ -139,10 +138,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() Effect = Effect.Allow, Principal = new StringOrStringList(new AsyncApiArray { - new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann"), - new AsyncApiString("arn:aws:iam::123456789012:user/dec.kolakowski") + new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"), + new AsyncApiAny("arn:aws:iam::123456789012:user/dec.kolakowski") }), - Action = new StringOrStringList(new AsyncApiString("sqs:CreateQueue")), + Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), }, }, Extensions = new Dictionary() @@ -151,7 +150,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() "x-policyExtension", new AsyncApiObject() { - { "policyXPropertyName", new AsyncApiString("policyXPropertyValue") }, + { "policyXPropertyName", new AsyncApiAny("policyXPropertyValue") }, } }, }, @@ -167,7 +166,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() "x-queueExtension", new AsyncApiObject() { - { "queueXPropertyName", new AsyncApiString("queueXPropertyValue") }, + { "queueXPropertyName", new AsyncApiAny("queueXPropertyValue") }, } }, }, @@ -187,10 +186,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), Action = new StringOrStringList(new AsyncApiArray() { - new AsyncApiString("sqs:*"), + new AsyncApiAny("sqs:*"), }), }, }, @@ -201,7 +200,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() { "x-internalObject", new AsyncApiObject() { - { "myExtensionPropertyName", new AsyncApiString("myExtensionPropertyValue") }, + { "myExtensionPropertyName", new AsyncApiAny("myExtensionPropertyValue") }, } }, }, @@ -307,7 +306,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-identifierExtension", new AsyncApiObject() { - { "identifierXPropertyName", new AsyncApiString("identifierXPropertyValue") }, + { "identifierXPropertyName", new AsyncApiAny("identifierXPropertyValue") }, } }, }, @@ -319,7 +318,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-redrivePolicyExtension", new AsyncApiObject() { - { "redrivePolicyXPropertyName", new AsyncApiString("redrivePolicyXPropertyValue") }, + { "redrivePolicyXPropertyName", new AsyncApiAny("redrivePolicyXPropertyValue") }, } }, }, @@ -331,11 +330,11 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), Action = new StringOrStringList(new AsyncApiArray() { - new AsyncApiString("sqs:SendMessage"), - new AsyncApiString("sqs:ReceiveMessage") + new AsyncApiAny("sqs:SendMessage"), + new AsyncApiAny("sqs:ReceiveMessage") }), Extensions = new Dictionary() { @@ -343,7 +342,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-statementExtension", new AsyncApiObject() { - { "statementXPropertyName", new AsyncApiString("statementXPropertyValue") }, + { "statementXPropertyName", new AsyncApiAny("statementXPropertyValue") }, } }, }, @@ -353,10 +352,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() Effect = Effect.Allow, Principal = new StringOrStringList(new AsyncApiArray { - new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann"), - new AsyncApiString("arn:aws:iam::123456789012:user/dec.kolakowski"), + new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"), + new AsyncApiAny("arn:aws:iam::123456789012:user/dec.kolakowski"), }), - Action = new StringOrStringList(new AsyncApiString("sqs:CreateQueue")) + Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")) }, }, Extensions = new Dictionary() @@ -365,7 +364,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-policyExtension", new AsyncApiObject() { - { "policyXPropertyName", new AsyncApiString("policyXPropertyValue") }, + { "policyXPropertyName", new AsyncApiAny("policyXPropertyValue") }, } }, }, @@ -381,7 +380,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-queueExtension", new AsyncApiObject() { - { "queueXPropertyName", new AsyncApiString("queueXPropertyValue") }, + { "queueXPropertyName", new AsyncApiAny("queueXPropertyValue") }, } }, }, @@ -401,10 +400,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiString("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), Action = new StringOrStringList(new AsyncApiArray { - new AsyncApiString("sqs:*") + new AsyncApiAny("sqs:*") }) }, }, @@ -415,7 +414,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() "x-queueExtension", new AsyncApiObject() { - { "queueXPropertyName", new AsyncApiString("queueXPropertyValue") }, + { "queueXPropertyName", new AsyncApiAny("queueXPropertyValue") }, } }, }, @@ -426,7 +425,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() { "x-internalObject", new AsyncApiObject() { - { "myExtensionPropertyName", new AsyncApiString("myExtensionPropertyValue") }, + { "myExtensionPropertyName", new AsyncApiAny("myExtensionPropertyValue") }, } }, }, diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs index 9b6958a5..615cf11b 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs @@ -6,7 +6,6 @@ namespace LEGO.AsyncAPI.Tests.Bindings using FluentAssertions; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; @@ -19,18 +18,18 @@ public class StringOrStringList_Should public void StringOrStringList_IsInitialised_WhenPassedStringOrStringList() { // Arrange - var stringValue = new StringOrStringList(new AsyncApiString("AsyncApi")); + var stringValue = new StringOrStringList(new AsyncApiAny("AsyncApi")); var listValue = new StringOrStringList( new AsyncApiArray() { - new AsyncApiString("Async"), - new AsyncApiString("Api"), + new AsyncApiAny("Async"), + new AsyncApiAny("Api"), }); // Assert - (stringValue.Value as AsyncApiString).Value.Should().Be("AsyncApi"); - (listValue.Value as AsyncApiArray) - .Select(s => (s as AsyncApiString).Value) + stringValue.Value.GetValue().Should().Be("AsyncApi"); + ((AsyncApiArray)listValue.Value) + .Select(s => s.GetValue()) .Should().BeEquivalentTo(new List() { "Async", "Api" }); } @@ -38,7 +37,7 @@ public void StringOrStringList_IsInitialised_WhenPassedStringOrStringList() public void StringOrStringList_ThrowsArgumentException_WhenIntialisedWithoutStringOrStringList() { // Assert - var ex = Assert.Throws(() => new StringOrStringList(new AsyncApiBoolean(true))); + var ex = Assert.Throws(() => new StringOrStringList(new AsyncApiAny(true))); // Assert ex.Message.Should().Be("StringOrStringList should be a string value or a string list."); @@ -51,9 +50,9 @@ public void StringOrStringList_ThrowsArgumentException_WhenIntialisedWithListOfN var ex = Assert.Throws(() => new StringOrStringList( new AsyncApiArray() { - new AsyncApiString("x"), - new AsyncApiInteger(1), - new AsyncApiString("y"), + new AsyncApiAny("x"), + new AsyncApiAny(1), + new AsyncApiAny("y"), })); // Assert @@ -71,7 +70,7 @@ public void StringOrStringList_WhenValueIsString_SerializesDeserializes() var channel = new AsyncApiChannel(); channel.Bindings.Add(new StringOrStringListTestBinding { - TestProperty = new StringOrStringList(new AsyncApiString("someValue")), + TestProperty = new StringOrStringList(new AsyncApiAny("someValue")), }); // Act @@ -106,9 +105,9 @@ public void StringOrStringList_WhenValueIsStringList_SerializesDeserializes() { TestProperty = new StringOrStringList(new AsyncApiArray { - new AsyncApiString("someValue01"), - new AsyncApiString("someValue02"), - new AsyncApiString("someValue03"), + new AsyncApiAny("someValue01"), + new AsyncApiAny("someValue02"), + new AsyncApiAny("someValue03"), }), }); diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index edb13eff..a9207f92 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -9,7 +9,6 @@ namespace LEGO.AsyncAPI.Tests.Models using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using NUnit.Framework; @@ -242,11 +241,11 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Title = "HeaderTitle", WriteOnly = true, Description = "HeaderDescription", - Examples = new List + Examples = new List { new AsyncApiObject { - { "x-correlation-id", new AsyncApiString("nil") }, + { "x-correlation-id", new AsyncApiAny("nil") }, }, }, }, @@ -274,7 +273,7 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Description = "CorrelationDescription", Extensions = new Dictionary { - { "x-extension-a", new AsyncApiString("a") }, + { "x-extension-a", new AsyncApiAny("a") }, }, }, ContentType = "MessageContentType", @@ -305,12 +304,12 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Title = "SchemaTitle", WriteOnly = true, Description = "SchemaDescription", - Examples = new List + Examples = new List { new AsyncApiObject { - { "cKey", new AsyncApiString("c") }, - { "dKey", new AsyncApiInteger(1) }, + { "cKey", new AsyncApiAny("c") }, + { "dKey", new AsyncApiAny(1) }, }, }, }, @@ -323,8 +322,8 @@ public void AsyncApiMessage_WithFilledObject_Serializes() { Payload = new AsyncApiObject() { - { "PropA", new AsyncApiString("a") }, - { "PropB", new AsyncApiString("b") }, + { "PropA", new AsyncApiAny("a") }, + { "PropB", new AsyncApiAny("b") }, }, }, }, @@ -339,12 +338,12 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Title = "SchemaTitle", WriteOnly = true, Description = "SchemaDescription", - Examples = new List + Examples = new List { new AsyncApiObject { - { "eKey", new AsyncApiString("e") }, - { "fKey", new AsyncApiInteger(1) }, + { "eKey", new AsyncApiAny("e") }, + { "fKey", new AsyncApiAny(1) }, }, }, }, @@ -356,12 +355,12 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Name = "MessageExampleName", Payload = new AsyncApiObject { - { "gKey", new AsyncApiString("g") }, - { "hKey", new AsyncApiBoolean(true) }, + { "gKey", new AsyncApiAny("g") }, + { "hKey", new AsyncApiAny(true) }, }, Extensions = new Dictionary { - { "x-extension-b", new AsyncApiString("b") }, + { "x-extension-b", new AsyncApiAny("b") }, }, }, }, @@ -382,7 +381,7 @@ public void AsyncApiMessage_WithFilledObject_Serializes() }, Extensions = new Dictionary { - { "x-extension-c", new AsyncApiString("c") }, + { "x-extension-c", new AsyncApiAny("c") }, }, }, }, @@ -399,7 +398,11 @@ public void AsyncApiMessage_WithFilledObject_Serializes() // Assert Assert.AreEqual(expected, actual); - message.Should().BeEquivalentTo(deserializedMessage); + message.Should().BeEquivalentTo(deserializedMessage, options => options.IgnoringCyclicReferences() + .Excluding(message => message.Headers.Examples[0].Node.Parent) + .Excluding(message => message.Traits[0].Headers.Examples[0].Node.Parent) + .Excluding(message => message.Traits[0].Examples[0].Payload.Node.Parent) + .Excluding(message => message.Examples[0].Payload.Node.Parent)); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 31647d3e..07078d80 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -1,7 +1,5 @@ // Copyright (c) The LEGO Group. All rights reserved. -using System.Linq; -using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Readers; namespace LEGO.AsyncAPI.Tests.Models @@ -12,7 +10,6 @@ namespace LEGO.AsyncAPI.Tests.Models using System.IO; using FluentAssertions; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Writers; using NUnit.Framework; @@ -27,7 +24,7 @@ public class AsyncApiSchema_Should Maximum = 42, ExclusiveMinimum = true, Minimum = 10, - Default = new AsyncApiInteger(15), + Default = new AsyncApiAny(15), Type = SchemaType.Integer, Nullable = true, ExternalDocs = new AsyncApiExternalDocumentation @@ -43,7 +40,7 @@ public class AsyncApiSchema_Should Maximum = double.MaxValue, ExclusiveMinimum = true, Minimum = double.MinValue, - Default = new AsyncApiInteger(15), + Default = new AsyncApiAny(15), Type = SchemaType.Integer, Nullable = true, ExternalDocs = new AsyncApiExternalDocumentation @@ -148,7 +145,7 @@ public class AsyncApiSchema_Should }, ["property11"] = new AsyncApiSchema { - Const = new AsyncApiString("aSpecialConstant"), + Const = new AsyncApiAny("aSpecialConstant"), }, }, Nullable = true, @@ -217,7 +214,7 @@ public class AsyncApiSchema_Should Maximum = 42, ExclusiveMinimum = true, Minimum = 10, - Default = new AsyncApiInteger(15), + Default = new AsyncApiAny(15), Type = SchemaType.Integer, Nullable = true, @@ -609,7 +606,9 @@ public void Deserialize_WithAdvancedSchema_Works() var actual = new AsyncApiStringReader().ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out var _diagnostics); // Assert - actual.Should().BeEquivalentTo(expected); + actual.Should().BeEquivalentTo(expected, options => options.IgnoringCyclicReferences() + .Excluding(actual => actual.Properties["property11"].Const.Node.Parent) + .Excluding(actual => actual.Properties["property11"].Const.Node.Root)); _diagnostics.Errors.Should().BeEmpty(); } From 5b6465474ae09d42a27377bf04d58fdbd1dd8a59 Mon Sep 17 00:00:00 2001 From: Gadam8 <44494964+Gadam8@users.noreply.github.com> Date: Mon, 25 Sep 2023 17:38:57 +0100 Subject: [PATCH 26/84] feat(bindings): update FilterPolicy to match AWS API (#128) Co-authored-by: adam.gloyne --- src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs | 18 ++++- .../Sns/FilterPolicy.cs | 30 ------- .../Sns/SnsOperationBinding.cs | 8 +- .../Bindings/Sns/SnsBindings_Should.cs | 78 ++++++++----------- 4 files changed, 50 insertions(+), 84 deletions(-) delete mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs index 46548977..a38174bc 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs @@ -3,6 +3,7 @@ namespace LEGO.AsyncAPI.Bindings.Sns using System; using System.Collections.Generic; using LEGO.AsyncAPI.Attributes; + using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; @@ -20,8 +21,14 @@ public class Consumer : IAsyncApiExtensible /// /// Only receive a subset of messages from the channel, determined by this policy. + /// Depending on the FilterPolicyScope, a map of either a message attribute or message body to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint. /// - public FilterPolicy FilterPolicy { get; set; } + public IAsyncApiAny FilterPolicy { get; set; } + + /// + /// Determines whether the FilterPolicy applies to MessageAttributes or MessageBody. + /// + public FilterPolicyScope FilterPolicyScope { get; set; } /// /// If true AWS SNS attributes are removed from the body, and for SQS, SNS message attributes are copied to SQS message attributes. If false the SNS attributes are included in the body. @@ -55,7 +62,8 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("protocol", this.Protocol.GetDisplayName()); writer.WriteRequiredObject("endpoint", this.Endpoint, (w, e) => e.Serialize(w)); - writer.WriteOptionalObject("filterPolicy", this.FilterPolicy, (w, f) => f.Serialize(w)); + writer.WriteOptionalObject("filterPolicy", this.FilterPolicy, (w, f) => f.Write(w)); + writer.WriteOptionalProperty("filterPolicyScope", this.FilterPolicyScope.GetDisplayName()); writer.WriteRequiredProperty("rawMessageDelivery", this.RawMessageDelivery); writer.WriteOptionalObject("redrivePolicy", this.RedrivePolicy, (w, p) => p.Serialize(w)); writer.WriteOptionalObject("deliveryPolicy", this.DeliveryPolicy, (w, p) => p.Serialize(w)); @@ -77,4 +85,10 @@ public enum Protocol [Display("lambda")] Lambda, [Display("firehose")] Firehose, } + + public enum FilterPolicyScope + { + [Display("MessageAttributes")] MessageAttributes, + [Display("MessageBody")] MessageBody, + } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs deleted file mode 100644 index 47530cc0..00000000 --- a/src/LEGO.AsyncAPI.Bindings/Sns/FilterPolicy.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace LEGO.AsyncAPI.Bindings.Sns -{ - using System; - using System.Collections.Generic; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - public class FilterPolicy : IAsyncApiExtensible - { - /// - /// A map of a message attribute to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint. - /// - public IAsyncApiAny Attributes { get; set; } - - public IDictionary Extensions { get; set; } = new Dictionary(); - - public void Serialize(IAsyncApiWriter writer) - { - if (writer is null) - { - throw new ArgumentNullException(nameof(writer)); - } - - writer.WriteStartObject(); - writer.WriteRequiredObject("attributes", this.Attributes, (w, a) => w.WriteAny(a)); - writer.WriteExtensions(this.Extensions); - writer.WriteEndObject(); - } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs index d35a46f5..da077f2d 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -48,18 +48,14 @@ public class SnsOperationBinding : OperationBinding { { "protocol", (a, n) => { a.Protocol = n.GetScalarValue().GetEnumFromDisplayName(); } }, { "endpoint", (a, n) => { a.Endpoint = n.ParseMapWithExtensions(this.identifierFixFields); } }, - { "filterPolicy", (a, n) => { a.FilterPolicy = n.ParseMapWithExtensions(this.filterPolicyFixedFields); } }, + { "filterPolicy", (a, n) => { a.FilterPolicy = n.CreateAny(); } }, + { "filterPolicyScope", (a, n) => { a.FilterPolicyScope = n.GetScalarValue().GetEnumFromDisplayName(); } }, { "rawMessageDelivery", (a, n) => { a.RawMessageDelivery = n.GetBooleanValue(); } }, { "redrivePolicy", (a, n) => { a.RedrivePolicy = n.ParseMapWithExtensions(this.redrivePolicyFixedFields); } }, { "deliveryPolicy", (a, n) => { a.DeliveryPolicy = n.ParseMapWithExtensions(this.deliveryPolicyFixedFields); } }, { "displayName", (a, n) => { a.DisplayName = n.GetScalarValue(); } }, }; - private FixedFieldMap filterPolicyFixedFields => new() - { - { "attributes", (a, n) => { a.Attributes = n.CreateAny(); } }, - }; - private FixedFieldMap redrivePolicyFixedFields => new() { { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMapWithExtensions(identifierFixFields); } }, diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 7a2269bb..948dbb7e 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -167,20 +167,18 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() x-identifierExtension: identifierXPropertyName: identifierXPropertyValue filterPolicy: - attributes: - store: - - asyncapi_corp - contact: dec.kolakowski - event: - - anything-but: order_cancelled - order_key: - transient: by_area - customer_interests: - - rugby - - football - - baseball - x-filterPolicyExtension: - filterPolicyXPropertyName: filterPolicyXPropertyValue + store: + - asyncapi_corp + contact: dec.kolakowski + event: + - anything-but: order_cancelled + order_key: + transient: by_area + customer_interests: + - rugby + - football + - baseball + filterPolicyScope: MessageAttributes rawMessageDelivery: false redrivePolicy: deadLetterQueue: @@ -253,47 +251,35 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() }, }, }, - FilterPolicy = new FilterPolicy() - { - Attributes = new AsyncApiObject() + FilterPolicy = new AsyncApiObject() + { + { "store", new AsyncApiArray() { new AsyncApiString("asyncapi_corp") } }, + { "contact", new AsyncApiString("dec.kolakowski") }, { - { "store", new AsyncApiArray() { new AsyncApiString("asyncapi_corp") } }, - { "contact", new AsyncApiString("dec.kolakowski") }, - { - "event", new AsyncApiArray() - { - new AsyncApiObject() - { - { "anything-but", new AsyncApiString("order_cancelled") }, - }, - } - }, + "event", new AsyncApiArray() { - "order_key", new AsyncApiObject() + new AsyncApiObject() { - { "transient", new AsyncApiString("by_area") }, - } - }, + { "anything-but", new AsyncApiString("order_cancelled") }, + }, + } + }, + { + "order_key", new AsyncApiObject() { - "customer_interests", new AsyncApiArray() - { - new AsyncApiString("rugby"), - new AsyncApiString("football"), - new AsyncApiString("baseball"), - } - }, + { "transient", new AsyncApiString("by_area") }, + } }, - Extensions = new Dictionary() { + "customer_interests", new AsyncApiArray() { - "x-filterPolicyExtension", - new AsyncApiObject() - { - { "filterPolicyXPropertyName", new AsyncApiString("filterPolicyXPropertyValue") }, - } - }, + new AsyncApiString("rugby"), + new AsyncApiString("football"), + new AsyncApiString("baseball"), + } }, }, + FilterPolicyScope = FilterPolicyScope.MessageAttributes, RawMessageDelivery = false, RedrivePolicy = new RedrivePolicy() { From f923c65a5d80d8c277631581d9529363e83d87b0 Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 11:35:12 +0000 Subject: [PATCH 27/84] chore: update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index efb62f45..4e5bda60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# [4.1.0](https://github.com/LEGO/AsyncAPI.NET/compare/v4.0.2...v4.1.0) (2023-09-27) + + +### Features + +* **bindings:** update FilterPolicy to match AWS API ([#128](https://github.com/LEGO/AsyncAPI.NET/issues/128)) ([5b64654](https://github.com/LEGO/AsyncAPI.NET/commit/5b6465474ae09d42a27377bf04d58fdbd1dd8a59)) + ## [4.0.2](https://github.com/LEGO/AsyncAPI.NET/compare/v4.0.1...v4.0.2) (2023-08-01) From dc544f1c01be3b95ded08ee894453ce8529eafb3 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Wed, 11 Oct 2023 20:41:13 +0200 Subject: [PATCH 28/84] fix: patternProperties should also be walked as a reference (#133) --- src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs index cc174747..e0d3ec38 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs @@ -386,6 +386,17 @@ internal void Walk(AsyncApiSchema schema, bool isComponent = false) this.Walk("additionalProperties", () => this.Walk(schema.AdditionalProperties)); } + if (schema.PatternProperties != null) + { + this.Walk("patternProperties", () => + { + foreach (var item in schema.PatternProperties) + { + this.Walk(item.Key, () => this.Walk(item.Value)); + } + }); + } + if (schema.PropertyNames != null) { this.Walk("propertyNames", () => this.Walk(schema.PropertyNames)); From 71fe571a3db0b4fbc13f4573b4d4b53f4f6b0911 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 20 Oct 2023 14:11:11 +0200 Subject: [PATCH 29/84] feat: allow non-component references (#132) --- ...eader.cs => AsyncApiJsonDocumentReader.cs} | 9 +- .../AsyncApiReaderSettings.cs | 2 +- .../V2/AsyncApiV2VersionService.cs | 28 +- src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs | 2 +- src/LEGO.AsyncAPI/Models/AsyncApiReference.cs | 45 +++- .../Services/AsyncApiReferenceResolver.cs | 15 +- .../Models/AsyncApiReference_Should.cs | 241 ++++++++++++++++++ 7 files changed, 326 insertions(+), 16 deletions(-) rename src/LEGO.AsyncAPI.Readers/{AsyncApiYamlDocumentReader.cs => AsyncApiJsonDocumentReader.cs} (96%) create mode 100644 test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs similarity index 96% rename from src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs rename to src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs index fccdf3e3..5c0826af 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiYamlDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs @@ -1,10 +1,11 @@ -// Copyright (c) The LEGO Group. All rights reserved. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Readers { using System.Collections.Generic; using System.Linq; using System.Text.Json.Nodes; + using System.Threading; using System.Threading.Tasks; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Extensions; @@ -76,7 +77,7 @@ public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic) return document; } - public Task ReadAsync(JsonNode input) + public async Task ReadAsync(JsonNode input, CancellationToken cancellationToken = default) { var diagnostic = new AsyncApiDiagnostic(); var context = new ParsingContext(diagnostic) @@ -106,11 +107,11 @@ public Task ReadAsync(JsonNode input) } } - return Task.FromResult(new ReadResult + return new ReadResult { AsyncApiDocument = document, AsyncApiDiagnostic = diagnostic, - }); + }; } private void ResolveReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument document) diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index 134acc75..7f759107 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -18,7 +18,7 @@ public enum ReferenceResolutionSetting DoNotResolveReferences, /// - /// ResolveAllReferences, effectively inlining them. + /// Resolve internal component references and inline them. /// ResolveReferences, } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs index 92a34c64..42562d9a 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs @@ -67,9 +67,21 @@ public AsyncApiReference ConvertToAsyncApiReference( Id = reference, }; } + + var asyncApiReference = new AsyncApiReference(); + if (reference.StartsWith("/")) + { + asyncApiReference.IsFragment = true; + } + + asyncApiReference.ExternalResource = segments[0]; + + return asyncApiReference; + } else if (segments.Length == 2) { + // Local reference if (reference.StartsWith("#")) { try @@ -84,7 +96,7 @@ public AsyncApiReference ConvertToAsyncApiReference( } var id = segments[1]; - + var asyncApiReference = new AsyncApiReference(); if (id.StartsWith("/components/")) { var localSegments = segments[1].Split('/'); @@ -103,12 +115,16 @@ public AsyncApiReference ConvertToAsyncApiReference( id = localSegments[3]; } - - return new AsyncApiReference + else { - Type = type, - Id = id, - }; + asyncApiReference.IsFragment = true; + } + + asyncApiReference.ExternalResource = segments[0]; + asyncApiReference.Type = type; + asyncApiReference.Id = id; + + return asyncApiReference; } } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs index b28168e2..1a5b7c71 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs @@ -150,7 +150,7 @@ internal T ResolveReference(AsyncApiReference reference) where T : class, IAs return this.ResolveReference(reference) as T; } - public IAsyncApiReferenceable ResolveReference(AsyncApiReference reference) + internal IAsyncApiReferenceable ResolveReference(AsyncApiReference reference) { if (reference == null) { diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiReference.cs b/src/LEGO.AsyncAPI/Models/AsyncApiReference.cs index 2403b4ad..4f9660d1 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiReference.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiReference.cs @@ -11,6 +11,14 @@ namespace LEGO.AsyncAPI.Models /// public class AsyncApiReference : IAsyncApiSerializable { + /// + /// 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; } + /// /// Gets or sets the element type referenced. /// @@ -27,17 +35,37 @@ public class AsyncApiReference : IAsyncApiSerializable public AsyncApiDocument HostDocument { get; set; } = null; /// - /// Gets the full reference string for v2.3. + /// Gets a flag indicating whether a file is a valid OpenAPI document or a fragment + /// + public bool IsFragment { get; set; } = false; + + /// + /// Gets a flag indicating whether this reference is an external reference. + /// + public bool IsExternal => this.ExternalResource != null; + + /// + /// Gets the full reference string for v2. /// 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; } } @@ -67,6 +95,21 @@ public void SerializeV2(IAsyncApiWriter writer) writer.WriteEndObject(); } + 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; + } + public void Write(IAsyncApiWriter writer) { this.SerializeV2(writer); diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs index ca345b3f..88808cb8 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs @@ -154,7 +154,7 @@ public override void Visit(AsyncApiSchema schema) this.ResolveMap(schema.Properties); } - private void ResolveObject(T entity, Action assign) where T : class, IAsyncApiReferenceable + private void ResolveObject(T entity, Action assign) where T : class, IAsyncApiReferenceable, new() { if (entity == null) { @@ -184,7 +184,7 @@ private void ResolveObject(T entity, Action assign) where T : class, IAsyn } } - private void ResolveMap(IDictionary map) where T : class, IAsyncApiReferenceable + private void ResolveMap(IDictionary map) where T : class, IAsyncApiReferenceable, new() { if (map == null) { @@ -201,8 +201,17 @@ private void ResolveMap(IDictionary map) where T : class, IAsyncAp } } - private T ResolveReference(AsyncApiReference reference) where T : class, IAsyncApiReferenceable + private T ResolveReference(AsyncApiReference reference) where T : class, IAsyncApiReferenceable, new() { + if (reference.IsExternal) + { + return new () + { + UnresolvedReference = true, + Reference = reference, + }; + } + try { return this.currentDocument.ResolveReference(reference) as T; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs new file mode 100644 index 00000000..34831e86 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs @@ -0,0 +1,241 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests +{ + using FluentAssertions; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using NUnit.Framework; + using System.Linq; + + public class AsyncApiReference_Should + { + + [Test] + public void AsyncApiReference_WithExternalFragmentUriReference_AllowReference() + { + // Arrange + var actual = @"payload: + $ref: http://example.com/some-resource#/path/to/external/fragment"; + var reader = new AsyncApiStringReader(); + + // Act + var deserialized = reader.ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + + // Assert + diagnostic.Errors.Should().BeEmpty(); + deserialized.Payload.UnresolvedReference.Should().BeTrue(); + + var reference = deserialized.Payload.Reference; + reference.ExternalResource.Should().Be("http://example.com/some-resource"); + reference.Id.Should().Be("/path/to/external/fragment"); + reference.IsFragment.Should().BeTrue(); + reference.IsExternal.Should().BeTrue(); + + var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + var expected = serialized.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Test] + public void AsyncApiReference_WithFragmentReference_AllowReference() + { + // Arrange + var actual = @"payload: + $ref: /fragments/myFragment"; + var reader = new AsyncApiStringReader(); + + // Act + var deserialized = reader.ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + + // Assert + diagnostic.Errors.Should().BeEmpty(); + deserialized.Payload.UnresolvedReference.Should().BeTrue(); + + var reference = deserialized.Payload.Reference; + reference.ExternalResource.Should().Be("/fragments/myFragment"); + reference.Id.Should().BeNull(); + reference.IsFragment.Should().BeTrue(); + reference.IsExternal.Should().BeTrue(); + var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + var expected = serialized.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Test] + public void AsyncApiReference_WithInternalComponentReference_AllowReference() + { + // Arrange + var actual = @"payload: + $ref: '#/components/schemas/test'"; + var reader = new AsyncApiStringReader(); + + // Act + var deserialized = reader.ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + + // Assert + diagnostic.Errors.Should().BeEmpty(); + var reference = deserialized.Payload.Reference; + reference.ExternalResource.Should().BeNull(); + reference.Type.Should().Be(ReferenceType.Schema); + reference.Id.Should().Be("test"); + reference.IsFragment.Should().BeFalse(); + reference.IsExternal.Should().BeFalse(); + + var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + var expected = serialized.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Test] + public void AsyncApiReference_WithExternalFragmentReference_AllowReference() + { + // Arrange + var actual = @"payload: + $ref: ./myjsonfile.json#/fragment"; + var reader = new AsyncApiStringReader(); + + // Act + var deserialized = reader.ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + + // Assert + diagnostic.Errors.Should().BeEmpty(); + var reference = deserialized.Payload.Reference; + reference.ExternalResource.Should().Be("./myjsonfile.json"); + reference.Id.Should().Be("/fragment"); + reference.IsFragment.Should().BeTrue(); + reference.IsExternal.Should().BeTrue(); + + var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + var expected = serialized.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Test] + public void AsyncApiReference_WithExternalComponentReference_AllowReference() + { + // Arrange + var actual = @"payload: + $ref: ./someotherdocument.json#/components/schemas/test"; + var reader = new AsyncApiStringReader(); + + // Act + var deserialized = reader.ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + + // Assert + diagnostic.Errors.Should().BeEmpty(); + var reference = deserialized.Payload.Reference; + reference.ExternalResource.Should().Be("./someotherdocument.json"); + reference.Type.Should().Be(ReferenceType.Schema); + reference.Id.Should().Be("test"); + reference.IsFragment.Should().BeFalse(); + reference.IsExternal.Should().BeTrue(); + + var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + var expected = serialized.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + + [Test] + public void AsyncApiDocument_WithInternalComponentReference_ResolvesReference() + { + // Arrange + var actual = @"asyncapi: 2.6.0 +info: + title: My AsyncAPI Document + version: 1.0.0 +channels: + myChannel: + $ref: '#/components/channels/myChannel' +components: + channels: + myChannel: + description: customDescription"; + + var settings = new AsyncApiReaderSettings() + { + ReferenceResolution = ReferenceResolutionSetting.ResolveReferences, + }; + var reader = new AsyncApiStringReader(settings); + + // Act + var deserialized = reader.Read(actual, out var diagnostic); + + // Assert + diagnostic.Errors.Should().BeEmpty(); + var channel = deserialized.Channels.First().Value; + + channel.UnresolvedReference.Should().BeFalse(); + channel.Description.Should().Be("customDescription"); + channel.Reference.ExternalResource.Should().BeNull(); + channel.Reference.Id.Should().Be("myChannel"); + channel.Reference.IsExternal.Should().BeFalse(); + channel.Reference.Type.Should().Be(ReferenceType.Channel); + } + + [Test] + public void AsyncApiDocument_WithExternalReference_DoesNotResolve() + { + // 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.ResolveReferences, + }; + var reader = new AsyncApiStringReader(settings); + + // Act + var deserialized = reader.Read(actual, out var diagnostic); + + // Assert + diagnostic.Errors.Should().BeEmpty(); + var channel = deserialized.Channels.First().Value; + + channel.UnresolvedReference.Should().BeTrue(); + channel.Description.Should().BeNull(); + channel.Reference.ExternalResource.Should().Be("http://example.com/channel.json"); + channel.Reference.Id.Should().BeNull(); + channel.Reference.IsExternal.Should().BeTrue(); + channel.Reference.IsFragment.Should().BeFalse(); + channel.Reference.Type.Should().BeNull(); + } + + [Test] + public void AsyncApiReference_WithExternalReference_AllowsReferenceDoesNotResolve() + { + // Arrange + var actual = @"payload: + $ref: http://example.com/json.json"; + var reader = new AsyncApiStringReader(); + + // Act + var deserialized = reader.ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + + // Assert + diagnostic.Errors.Should().BeEmpty(); + var reference = deserialized.Payload.Reference; + reference.ExternalResource.Should().Be("http://example.com/json.json"); + reference.Id.Should().BeNull(); + reference.IsExternal.Should().BeTrue(); + reference.IsFragment.Should().BeFalse(); + diagnostic.Errors.Should().BeEmpty(); + + var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + var expected = serialized.MakeLineBreaksEnvironmentNeutral(); + actual.Should().Be(expected); + } + } +} \ No newline at end of file From 1df49c83dbe19664d7f5612b53b6dbde9fc4d892 Mon Sep 17 00:00:00 2001 From: "Alex W. Carlsen" Date: Tue, 24 Oct 2023 10:23:51 +0200 Subject: [PATCH 30/84] chore: remove yamldotnet from parsenodes --- src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs | 2 +- src/LEGO.AsyncAPI.Readers/JsonHelper.cs | 9 --------- src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs | 7 +++---- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs index d98bf6e7..3299a6a6 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs @@ -54,7 +54,7 @@ public AsyncApiDocument Read(TextReader input, out AsyncApiDiagnostic diagnostic } /// - /// Reads the content of the TextReader. If there are references to external documents then they will be read asynchronously. + /// Reads the content of the TextReader. /// /// TextReader containing AsyncApi description to parse. /// A ReadResult instance that contains the resulting AsyncApiDocument and a diagnostics instance. diff --git a/src/LEGO.AsyncAPI.Readers/JsonHelper.cs b/src/LEGO.AsyncAPI.Readers/JsonHelper.cs index 193daed2..5f7fc584 100644 --- a/src/LEGO.AsyncAPI.Readers/JsonHelper.cs +++ b/src/LEGO.AsyncAPI.Readers/JsonHelper.cs @@ -4,11 +4,8 @@ namespace LEGO.AsyncAPI.Readers { using System; using System.Globalization; - using System.IO; - using System.Linq; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Exceptions; - using YamlDotNet.RepresentationModel; internal static class JsonHelper { @@ -21,12 +18,6 @@ public static string GetScalarValue(this JsonNode node) public static JsonNode ParseJsonString(string jsonString) { return JsonNode.Parse(jsonString); - var reader = new StringReader(jsonString); - var yamlStream = new YamlStream(); - yamlStream.Load(reader); - - var yamlDocument = yamlStream.Documents.First(); - return yamlDocument.RootNode.ToJsonNode(); } } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs index d7be2ed3..508a6cdd 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs @@ -6,11 +6,11 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System.Collections; using System.Collections.Generic; using System.Linq; + using System.Text.Json; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Exceptions; - using YamlDotNet.Serialization; public class MapNode : ParseNode, IEnumerable { @@ -176,8 +176,7 @@ IEnumerator IEnumerable.GetEnumerator() public override string GetRaw() { - var x = new SerializerBuilder().JsonCompatible().Build(); - return x.Serialize(this.node); + return JsonSerializer.Serialize(this.node); } public T GetReferencedObject(ReferenceType referenceType, string referenceId) @@ -203,7 +202,7 @@ public string GetReferencePointer() public string GetScalarValue(ValueNode key) { var scalarNode = this.node[key.GetScalarValue()] is JsonValue jsonValue - ? jsonValue + ? jsonValue : throw new AsyncApiReaderException($"Expected scalar value while parsing {key.GetScalarValue()}", this.Context); return scalarNode.GetScalarValue(); From e1f8c8766767ce642546a911810064a5234f04c3 Mon Sep 17 00:00:00 2001 From: "Alex W. Carlsen" Date: Tue, 24 Oct 2023 10:31:15 +0200 Subject: [PATCH 31/84] chore(settings)!: make reader bindings IEnumerable to allow for simpler usage ```csharp new Settings { Bindings = BindingCollection.All, }; ``` BREAKING CHANGE: changes how bindings are applied. --- src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs | 2 +- test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs | 4 ++-- test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs | 2 +- test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs | 5 ++--- test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs | 4 ++-- .../Bindings/StringOrStringList_Should.cs | 4 ++-- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index 7f759107..2e715958 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -42,7 +42,7 @@ public Dictionary> { get; set; } = new Dictionary>(); - public List> + public IEnumerable> Bindings { get; set; } = new List>(); diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 526f164b..b48b1b48 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -1207,7 +1207,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() var actual = doc.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); var settings = new AsyncApiReaderSettings(); - settings.Bindings.AddRange(BindingsCollection.Pulsar); + settings.Bindings = BindingsCollection.Pulsar; var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); } @@ -1300,7 +1300,7 @@ public void Serializev2_WithBindings_Serializes() var actual = doc.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); var settings = new AsyncApiReaderSettings(); - settings.Bindings.AddRange(BindingsCollection.All); + settings.Bindings = BindingsCollection.All; var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs index 623f6d2b..461bbf79 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs @@ -111,7 +111,7 @@ public void CustomBinding_SerializesDeserializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(new MyBinding()); + settings.Bindings = new[] { new MyBinding() }; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 607510d9..33ad822c 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -136,10 +136,9 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Sns); + settings.Bindings = BindingsCollection.Sns; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - // Assert Assert.AreEqual(actual, expected); binding.Should().BeEquivalentTo(channel); @@ -379,7 +378,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Sns); + settings.Bindings = BindingsCollection.Sns; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index 50a6fe53..cb96671c 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -213,7 +213,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Sqs); + settings.Bindings = BindingsCollection.Sqs; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); @@ -438,7 +438,7 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Sqs); + settings.Bindings = BindingsCollection.Sqs; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs index 615cf11b..5e2780e7 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs @@ -81,7 +81,7 @@ public void StringOrStringList_WhenValueIsString_SerializesDeserializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(new StringOrStringListTestBinding()); + settings.Bindings = new[] { new StringOrStringListTestBinding() }; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -119,7 +119,7 @@ public void StringOrStringList_WhenValueIsStringList_SerializesDeserializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(new StringOrStringListTestBinding()); + settings.Bindings = new[] { new StringOrStringListTestBinding() }; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert From 44ffcf4ceaf06a5168597e1eeb9407f09d47ab23 Mon Sep 17 00:00:00 2001 From: Gadam8 <44494964+Gadam8@users.noreply.github.com> Date: Wed, 1 Nov 2023 13:25:14 +0000 Subject: [PATCH 32/84] feat(bindings): add high throughput fifo properties (#135) Co-authored-by: adam.gloyne --- src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs | 29 ++++++++++++++++--- .../Sqs/SqsChannelBinding.cs | 2 ++ .../Sqs/SqsOperationBinding.cs | 2 ++ .../Bindings/Sqs/SqsBindings_should.cs | 9 ++++-- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs index f79e0ad3..4eec17ef 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs @@ -2,12 +2,9 @@ namespace LEGO.AsyncAPI.Bindings.Sqs { using System; using System.Collections.Generic; - using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - using Extensions; - using LEGO.AsyncAPI.Readers; - using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Attributes; public class Queue : IAsyncApiExtensible { @@ -21,6 +18,16 @@ public class Queue : IAsyncApiExtensible /// public bool FifoQueue { get; set; } + /// + /// Specifies whether message deduplication occurs at the message group or queue level. Valid values are messageGroup and queue (default). + /// + public DeduplicationScope? DeduplicationScope { get; set; } + + /// + /// Specifies whether the FIFO queue throughput quota applies to the entire queue or per message group. Valid values are perQueue (default) and perMessageGroupId. + /// + public FifoThroughputLimit? FifoThroughputLimit { get; set; } + /// /// The number of seconds to delay before a message sent to the queue can be received. used to create a delay queue. /// @@ -68,6 +75,8 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("name", this.Name); writer.WriteOptionalProperty("fifoQueue", this.FifoQueue); + writer.WriteOptionalProperty("deduplicationScope", this.DeduplicationScope?.GetDisplayName()); + writer.WriteOptionalProperty("fifoThroughputLimit", this.FifoThroughputLimit?.GetDisplayName()); writer.WriteOptionalProperty("deliveryDelay", this.DeliveryDelay); writer.WriteOptionalProperty("visibilityTimeout", this.VisibilityTimeout); writer.WriteOptionalProperty("receiveMessageWaitTime", this.ReceiveMessageWaitTime); @@ -79,4 +88,16 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteEndObject(); } } + + public enum DeduplicationScope + { + [Display("queue")] Queue, + [Display("messageGroup")] MessageGroup, + } + + public enum FifoThroughputLimit + { + [Display("perQueue")] PerQueue, + [Display("perMessageGroupId")] PerMessageGroupId, + } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs index 2142b105..b64bed31 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs @@ -31,6 +31,8 @@ public class SqsChannelBinding : ChannelBinding { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, { "fifoQueue", (a, n) => { a.FifoQueue = n.GetBooleanValue(); } }, + { "deduplicationScope", (a, n) => { a.DeduplicationScope = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "fifoThroughputLimit", (a, n) => { a.FifoThroughputLimit = n.GetScalarValue().GetEnumFromDisplayName(); } }, { "deliveryDelay", (a, n) => { a.DeliveryDelay = n.GetIntegerValue(); } }, { "visibilityTimeout", (a, n) => { a.VisibilityTimeout = n.GetIntegerValue(); } }, { "receiveMessageWaitTime", (a, n) => { a.ReceiveMessageWaitTime = n.GetIntegerValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs index 9aff5a90..de2372f1 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs @@ -23,6 +23,8 @@ public class SqsOperationBinding : OperationBinding { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, { "fifoQueue", (a, n) => { a.FifoQueue = n.GetBooleanValue(); } }, + { "deduplicationScope", (a, n) => { a.DeduplicationScope = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "fifoThroughputLimit", (a, n) => { a.FifoThroughputLimit = n.GetScalarValue().GetEnumFromDisplayName(); } }, { "deliveryDelay", (a, n) => { a.DeliveryDelay = n.GetIntegerValue(); } }, { "visibilityTimeout", (a, n) => { a.VisibilityTimeout = n.GetIntegerValue(); } }, { "receiveMessageWaitTime", (a, n) => { a.ReceiveMessageWaitTime = n.GetIntegerValue(); } }, diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index 34c361e1..3ce6fb72 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -23,6 +23,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() queue: name: myQueue fifoQueue: true + deduplicationScope: messageGroup + fifoThroughputLimit: perMessageGroupId deliveryDelay: 30 visibilityTimeout: 60 receiveMessageWaitTime: 0 @@ -78,6 +80,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() { Name = "myQueue", FifoQueue = true, + DeduplicationScope = DeduplicationScope.MessageGroup, + FifoThroughputLimit = FifoThroughputLimit.PerMessageGroupId, DeliveryDelay = 30, VisibilityTimeout = 60, ReceiveMessageWaitTime = 0, @@ -233,7 +237,6 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() sqs: queues: - name: myQueue - fifoQueue: true deliveryDelay: 30 visibilityTimeout: 60 receiveMessageWaitTime: 0 @@ -291,7 +294,9 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Queue() { Name = "myQueue", - FifoQueue = true, + FifoQueue = false, + DeduplicationScope = null, + FifoThroughputLimit = null, DeliveryDelay = 30, VisibilityTimeout = 60, ReceiveMessageWaitTime = 0, From 510426e200b4fe97ad1b8e9a6e94a615593c2a3c Mon Sep 17 00:00:00 2001 From: Goker Akce Date: Thu, 14 Dec 2023 13:53:40 +0000 Subject: [PATCH 33/84] fix: added missing mapping for ordering (#138) Co-authored-by: Goker Akce --- .../Sns/SnsChannelBinding.cs | 1 + .../Bindings/Sns/SnsBindings_Should.cs | 15 ++++++++------- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index a0df69a9..913b512b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -37,6 +37,7 @@ public class SnsChannelBinding : ChannelBinding { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, { "type", (a, n) => { a.Ordering = n.ParseMapWithExtensions(this.orderingFixedFields); } }, + { "ordering", (a, n) => { a.Ordering = n.ParseMapWithExtensions(this.orderingFixedFields); } }, { "policy", (a, n) => { a.Policy = n.ParseMapWithExtensions(this.policyFixedFields); } }, { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, }; diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 948dbb7e..851269b2 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -1,17 +1,16 @@ -using System; +using System.Linq; using LEGO.AsyncAPI.Models.Any; using LEGO.AsyncAPI.Models.Interfaces; -using BindingsCollection = LEGO.AsyncAPI.Bindings.BindingsCollection; namespace LEGO.AsyncAPI.Tests.Bindings.Sns { - using NUnit.Framework; using System.Collections.Generic; using FluentAssertions; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Sns; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers; + using NUnit.Framework; internal class SnsBindings_Should { @@ -145,8 +144,9 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() // Assert Assert.AreEqual(actual, expected); - binding.Should().BeEquivalentTo(channel); - + + var expectedSnsBinding = (SnsChannelBinding)channel.Bindings.Values.First(); + expectedSnsBinding.Should().BeEquivalentTo((SnsChannelBinding)binding.Bindings.Values.First()); } [Test] @@ -388,8 +388,9 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() // Assert Assert.AreEqual(actual, expected); - binding.Should().BeEquivalentTo(operation); - + + var expectedSnsBinding = (SnsOperationBinding)operation.Bindings.Values.First(); + expectedSnsBinding.Should().BeEquivalentTo((SnsOperationBinding)binding.Bindings.Values.First()); } } } \ No newline at end of file From 30310232bb3869258486d9f7f85721d4e3fb46eb Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Thu, 14 Dec 2023 14:57:47 +0100 Subject: [PATCH 34/84] fix: add type to references, always. (#139) --- .../AsyncApiJsonDocumentReader.cs | 15 ++- .../AsyncApiStreamReader.cs | 10 +- .../AsyncApiTextReader.cs | 10 +- .../V2/AsyncApiV2VersionService.cs | 111 +++++++++--------- .../Bindings/Http/HttpBindings_Should.cs | 4 +- .../Bindings/Kafka/KafkaBindings_Should.cs | 8 +- .../Bindings/Pulsar/PulsarBindings_Should.cs | 12 +- .../WebSockets/WebSocketBindings_Should.cs | 2 +- .../Models/AsyncApiMessage_Should.cs | 2 +- .../Models/AsyncApiReference_Should.cs | 5 +- 10 files changed, 98 insertions(+), 81 deletions(-) diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs index 5c0826af..fbce6668 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.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 { @@ -63,12 +63,12 @@ public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic) if (this.settings.RuleSet != null && this.settings.RuleSet.Rules.Count > 0) { var asyncApiErrors = document.Validate(this.settings.RuleSet); - foreach (var item in asyncApiErrors.Where(e => e is AsyncApiValidatorError)) + foreach (var item in asyncApiErrors.OfType()) { diagnostic.Errors.Add(item); } - foreach (var item in asyncApiErrors.Where(e => e is AsyncApiValidatorWarning)) + foreach (var item in asyncApiErrors.OfType()) { diagnostic.Warnings.Add(item); } @@ -100,11 +100,16 @@ public async Task ReadAsync(JsonNode input, CancellationToken cancel // Validate the document if (this.settings.RuleSet != null && this.settings.RuleSet.Rules.Count > 0) { - var errors = document.Validate(this.settings.RuleSet); - foreach (var item in errors) + var asyncApiErrors = document.Validate(this.settings.RuleSet); + foreach (var item in asyncApiErrors.OfType()) { diagnostic.Errors.Add(item); } + + foreach (var item in asyncApiErrors.OfType()) + { + diagnostic.Warnings.Add(item); + } } return new ReadResult diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs index aeaf33c0..2cbf47ac 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiStreamReader.cs @@ -3,6 +3,7 @@ namespace LEGO.AsyncAPI.Readers { using System.IO; + using System.Threading; using System.Threading.Tasks; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; @@ -46,8 +47,11 @@ public AsyncApiDocument Read(Stream input, out AsyncApiDiagnostic diagnostic) /// Reads the stream input and parses it into an AsyncApi document. /// /// Stream containing AsyncApi description to parse. - /// Instance result containing newly created AsyncApiDocument and diagnostics object from the process. - public async Task ReadAsync(Stream input) + /// The cancellation token. + /// + /// Instance result containing newly created AsyncApiDocument and diagnostics object from the process. + /// + public async Task ReadAsync(Stream input, CancellationToken cancellationToken) { MemoryStream bufferedStream; if (input is MemoryStream) @@ -65,7 +69,7 @@ public async Task ReadAsync(Stream input) var reader = new StreamReader(bufferedStream); - return await new AsyncApiTextReader(this.settings).ReadAsync(reader); + return await new AsyncApiTextReader(this.settings).ReadAsync(reader, cancellationToken); } /// diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs index 3299a6a6..64587e46 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs @@ -6,6 +6,7 @@ namespace LEGO.AsyncAPI.Readers using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; + using System.Threading; using System.Threading.Tasks; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; @@ -57,8 +58,11 @@ public AsyncApiDocument Read(TextReader input, out AsyncApiDiagnostic diagnostic /// Reads the content of the TextReader. /// /// TextReader containing AsyncApi description to parse. - /// A ReadResult instance that contains the resulting AsyncApiDocument and a diagnostics instance. - public async Task ReadAsync(TextReader input) + /// The cancellation token. + /// + /// A ReadResult instance that contains the resulting AsyncApiDocument and a diagnostics instance. + /// + public async Task ReadAsync(TextReader input, CancellationToken cancellationToken) { JsonNode jsonNode; @@ -78,7 +82,7 @@ public async Task ReadAsync(TextReader input) }; } - return await new AsyncApiJsonDocumentReader(this.settings).ReadAsync(jsonNode); + return await new AsyncApiJsonDocumentReader(this.settings).ReadAsync(jsonNode, cancellationToken); } /// diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs index 42562d9a..3c99409c 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs @@ -54,78 +54,81 @@ public AsyncApiReference ConvertToAsyncApiReference( string reference, ReferenceType? type) { - if (!string.IsNullOrWhiteSpace(reference)) + if (string.IsNullOrWhiteSpace(reference)) { - var segments = reference.Split('#'); - if (segments.Length == 1) + throw new AsyncApiException($"The reference string '{reference}' has invalid format."); + } + + var segments = reference.Split('#'); + if (segments.Length == 1) + { + if (type == ReferenceType.SecurityScheme) { - if (type == ReferenceType.SecurityScheme) + return new AsyncApiReference { - return new AsyncApiReference - { - Type = type, - Id = reference, - }; - } + Type = type, + Id = reference, + }; + } - var asyncApiReference = new AsyncApiReference(); - if (reference.StartsWith("/")) - { - asyncApiReference.IsFragment = true; - } + var asyncApiReference = new AsyncApiReference(); + asyncApiReference.Type = type; + if (reference.StartsWith("/")) + { + asyncApiReference.IsFragment = true; + } - asyncApiReference.ExternalResource = segments[0]; + asyncApiReference.ExternalResource = segments[0]; - return asyncApiReference; + return asyncApiReference; - } - else if (segments.Length == 2) + } + else if (segments.Length == 2) + { + // Local reference + if (reference.StartsWith("#")) { - // Local reference - if (reference.StartsWith("#")) + try { - try - { - return this.ParseReference(segments[1]); - } - catch (AsyncApiException ex) - { - this.Diagnostic.Errors.Add(new AsyncApiError(ex)); - return null; - } + return this.ParseReference(segments[1]); } - - var id = segments[1]; - var asyncApiReference = new AsyncApiReference(); - if (id.StartsWith("/components/")) + catch (AsyncApiException ex) { - 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"); - } - } + this.Diagnostic.Errors.Add(new AsyncApiError(ex)); + return null; + } + } - id = localSegments[3]; + 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 { - asyncApiReference.IsFragment = true; + if (type != referencedType) + { + throw new AsyncApiException("Referenced type mismatch"); + } } - asyncApiReference.ExternalResource = segments[0]; - asyncApiReference.Type = type; - asyncApiReference.Id = id; - - return asyncApiReference; + id = localSegments[3]; } + else + { + asyncApiReference.IsFragment = true; + } + + asyncApiReference.ExternalResource = segments[0]; + asyncApiReference.Type = type; + asyncApiReference.Id = id; + + return asyncApiReference; } throw new AsyncApiException($"The reference string '{reference}' has invalid format."); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs index 0b00bbf7..b207dcd8 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs @@ -36,7 +36,7 @@ public void HttpMessageBinding_FilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Http); + settings.Bindings = BindingsCollection.Http; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -73,7 +73,7 @@ public void HttpOperationBinding_FilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Http); + settings.Bindings = BindingsCollection.Http; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index 537e904c..efeac315 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -55,7 +55,7 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Kafka); + settings.Bindings = BindingsCollection.Kafka; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -92,7 +92,7 @@ public void KafkaServerBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Kafka); + settings.Bindings = BindingsCollection.Kafka; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -131,7 +131,7 @@ public void KafkaMessageBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Kafka); + settings.Bindings = BindingsCollection.Kafka; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -171,7 +171,7 @@ public void KafkaOperationBinding_WithFilledObject_SerializesAndDeserializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Kafka); + settings.Bindings = BindingsCollection.Kafka; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs index 15577763..c99bfa6c 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs @@ -61,7 +61,7 @@ public void PulsarChannelBinding_WithFilledObject_SerializesAndDeserializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Pulsar); + settings.Bindings = BindingsCollection.Pulsar; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -80,7 +80,7 @@ public void PulsarChannelBindingNamespaceDefaultToNull() // Act var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Pulsar); + settings.Bindings = BindingsCollection.Pulsar; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -99,7 +99,7 @@ public void PulsarChannelBindingPropertiesExceptNamespaceDefaultToNull() // Act // Assert var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Pulsar); + settings.Bindings = BindingsCollection.Pulsar; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); var pulsarBinding = ((PulsarChannelBinding)binding.Bindings["pulsar"]); @@ -139,7 +139,7 @@ public void PulsarServerBinding_WithFilledObject_SerializesAndDeserializes() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Pulsar); + settings.Bindings = BindingsCollection.Pulsar; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -175,7 +175,7 @@ public void ServerBindingVersionDefaultsToNull() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Pulsar); + settings.Bindings = BindingsCollection.Pulsar; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -212,7 +212,7 @@ public void ServerTenantDefaultsToNull() actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Pulsar); + settings.Bindings = BindingsCollection.Pulsar; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs index 2a57b997..0e9c5344 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs @@ -44,7 +44,7 @@ public void WebSocketChannelBinding_WithFilledObject_SerializesAndDeserializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.Websockets); + settings.Bindings = BindingsCollection.Websockets; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index a9207f92..cc4b7aa7 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -393,7 +393,7 @@ public void AsyncApiMessage_WithFilledObject_Serializes() expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); - settings.Bindings.Add(BindingsCollection.All); + settings.Bindings = BindingsCollection.All; var deserializedMessage = new AsyncApiStringReader(settings).ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs index 34831e86..273162b3 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs @@ -31,7 +31,7 @@ public void AsyncApiReference_WithExternalFragmentUriReference_AllowReference() reference.Id.Should().Be("/path/to/external/fragment"); reference.IsFragment.Should().BeTrue(); reference.IsExternal.Should().BeTrue(); - + reference.Type.Should().Be(ReferenceType.Schema); var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); actual = actual.MakeLineBreaksEnvironmentNeutral(); var expected = serialized.MakeLineBreaksEnvironmentNeutral(); @@ -54,6 +54,7 @@ public void AsyncApiReference_WithFragmentReference_AllowReference() deserialized.Payload.UnresolvedReference.Should().BeTrue(); var reference = deserialized.Payload.Reference; + reference.Type.Should().Be(ReferenceType.Schema); reference.ExternalResource.Should().Be("/fragments/myFragment"); reference.Id.Should().BeNull(); reference.IsFragment.Should().BeTrue(); @@ -206,10 +207,10 @@ public void AsyncApiDocument_WithExternalReference_DoesNotResolve() 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.IsExternal.Should().BeTrue(); channel.Reference.IsFragment.Should().BeFalse(); - channel.Reference.Type.Should().BeNull(); } [Test] From 273eb9289d6391520de6638967b8ca414d9f8c4c Mon Sep 17 00:00:00 2001 From: VisualBean Date: Thu, 14 Dec 2023 15:02:56 +0100 Subject: [PATCH 35/84] minor fixes --- test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs | 6 ++---- test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 475f16b1..9642d141 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -1,14 +1,12 @@ -using System.Linq; -using LEGO.AsyncAPI.Models.Any; -using LEGO.AsyncAPI.Models.Interfaces; - namespace LEGO.AsyncAPI.Tests.Bindings.Sns { using System.Collections.Generic; + using System.Linq; using FluentAssertions; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Sns; using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using NUnit.Framework; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 07078d80..9041d0b8 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -1,7 +1,5 @@ // Copyright (c) The LEGO Group. All rights reserved. -using LEGO.AsyncAPI.Readers; - namespace LEGO.AsyncAPI.Tests.Models { using System; @@ -10,6 +8,7 @@ namespace LEGO.AsyncAPI.Tests.Models using System.IO; using FluentAssertions; using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Writers; using NUnit.Framework; From 67bc6c211d12bf5100864c0021c634b1183d48e6 Mon Sep 17 00:00:00 2001 From: VisualBean Date: Thu, 14 Dec 2023 15:47:18 +0100 Subject: [PATCH 36/84] cyclic reference fixes --- src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs | 4 ++-- src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs | 1 - src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs | 2 +- src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs | 4 ++-- src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs | 2 +- src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs | 2 +- .../Writers/AsyncApiWriterAnyExtensions.cs | 4 ++-- test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs | 2 +- .../Bindings/Sns/SnsBindings_Should.cs | 11 +++++------ .../Models/AsyncApiMessage_Should.cs | 6 +----- .../Models/AsyncApiSchema_Should.cs | 5 +---- 11 files changed, 17 insertions(+), 26 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs index 4ed81eec..f80c953b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs @@ -12,14 +12,14 @@ public class Ordering : IAsyncApiExtensible /// What type of SNS Topic is this? /// public OrderingType Type { get; set; } - + /// /// True to turn on de-duplication of messages for a channel. /// public bool ContentBasedDeduplication { get; set; } public IDictionary Extensions { get; set; } = new Dictionary(); - + public void Serialize(IAsyncApiWriter writer) { if (writer is null) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 913b512b..65043449 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -36,7 +36,6 @@ public class SnsChannelBinding : ChannelBinding protected override FixedFieldMap FixedFieldMap => new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, - { "type", (a, n) => { a.Ordering = n.ParseMapWithExtensions(this.orderingFixedFields); } }, { "ordering", (a, n) => { a.Ordering = n.ParseMapWithExtensions(this.orderingFixedFields); } }, { "policy", (a, n) => { a.Policy = n.ParseMapWithExtensions(this.policyFixedFields); } }, { "tags", (a, n) => { a.Tags = n.CreateSimpleMap(s => s.GetScalarValue()); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs index 4653d586..c2f323cc 100644 --- a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs +++ b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs @@ -14,7 +14,7 @@ public class StringOrStringList : IAsyncApiElement { public StringOrStringList(AsyncApiAny value) { - this.Value = value.Node switch + this.Value = value.GetNode() switch { JsonArray array => IsValidStringList(array) ? new AsyncApiAny(array) : throw new ArgumentException($"{nameof(StringOrStringList)} value should only contain string items."), JsonValue jValue => IsString(jValue) ? new AsyncApiAny(jValue) : throw new ArgumentException($"{nameof(StringOrStringList)} should be a string value or a string list."), diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs index ec62159a..042b1f68 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs @@ -13,7 +13,7 @@ public class AsyncApiArray : Collection, IAsyncApiExtension, IAsync public static explicit operator AsyncApiArray(AsyncApiAny any) { var a = new AsyncApiArray(); - if (any.Node is JsonArray arr) + if (any.GetNode() is JsonArray arr) { foreach (var item in arr) { @@ -29,7 +29,7 @@ public static implicit operator AsyncApiAny(AsyncApiArray arr) var jArray = new JsonArray(); foreach (var item in arr) { - jArray.Add(item.Node); + jArray.Add(item.GetNode()); } return new AsyncApiAny(jArray); diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs index f620a4f2..90e8e124 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs @@ -18,7 +18,7 @@ public static implicit operator AsyncApiAny(AsyncApiObject obj) var jObject = new JsonObject(); foreach (var item in obj) { - jObject.Add(item.Key, item.Value.Node); + jObject.Add(item.Key, item.Value.GetNode()); } return new AsyncApiAny(jObject); diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs index 3b866cd6..098c88d4 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs @@ -30,7 +30,7 @@ public AsyncApiAny(JsonNode node) /// /// The node. /// - public JsonNode Node => this.node; + public JsonNode GetNode() => this.node; public T GetValue() { diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs index 201ce127..fe2a34a1 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs @@ -54,13 +54,13 @@ public static void WriteAny(this IAsyncApiWriter writer, AsyncApiAny any) throw new ArgumentNullException(nameof(writer)); } - if (any.Node == null) + if (any.GetNode() == null) { writer.WriteNull(); return; } - var node = any.Node; + var node = any.GetNode(); var element = JsonDocument.Parse(node.ToJsonString()).RootElement; switch (element.ValueKind) diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index 33a593f0..1214da3c 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -40,7 +40,7 @@ public void Read_WithExtensionParser_Parses() "; Func valueExtensionParser = (any) => { - if (any.Node is JsonValue value) + if (any.GetNode() is JsonValue value) { if (value.GetScalarValue() == "onetwothreefour") { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 9642d141..d4d5cf05 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -141,11 +141,11 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() // Assert Assert.AreEqual(actual, expected); - + var expectedSnsBinding = (SnsChannelBinding)channel.Bindings.Values.First(); - expectedSnsBinding.Should().BeEquivalentTo((SnsChannelBinding)binding.Bindings.Values.First()); + expectedSnsBinding.Should().BeEquivalentTo((SnsChannelBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); } - + [Test] public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { @@ -382,12 +382,11 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() settings.Bindings = BindingsCollection.Sns; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - // Assert Assert.AreEqual(actual, expected); - + var expectedSnsBinding = (SnsOperationBinding)operation.Bindings.Values.First(); - expectedSnsBinding.Should().BeEquivalentTo((SnsOperationBinding)binding.Bindings.Values.First()); + expectedSnsBinding.Should().BeEquivalentTo((SnsOperationBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); } } } \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index cc4b7aa7..0849aa85 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -398,11 +398,7 @@ public void AsyncApiMessage_WithFilledObject_Serializes() // Assert Assert.AreEqual(expected, actual); - message.Should().BeEquivalentTo(deserializedMessage, options => options.IgnoringCyclicReferences() - .Excluding(message => message.Headers.Examples[0].Node.Parent) - .Excluding(message => message.Traits[0].Headers.Examples[0].Node.Parent) - .Excluding(message => message.Traits[0].Examples[0].Payload.Node.Parent) - .Excluding(message => message.Examples[0].Payload.Node.Parent)); + message.Should().BeEquivalentTo(deserializedMessage); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 9041d0b8..686ed9b6 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -605,10 +605,7 @@ public void Deserialize_WithAdvancedSchema_Works() var actual = new AsyncApiStringReader().ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out var _diagnostics); // Assert - actual.Should().BeEquivalentTo(expected, options => options.IgnoringCyclicReferences() - .Excluding(actual => actual.Properties["property11"].Const.Node.Parent) - .Excluding(actual => actual.Properties["property11"].Const.Node.Root)); - _diagnostics.Errors.Should().BeEmpty(); + actual.Should().BeEquivalentTo(expected); } [Test] From 4f1edc356c5d0e64ac577cadabac7097a1585443 Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 15:02:38 +0000 Subject: [PATCH 37/84] chore: update CHANGELOG.md --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e5bda60..e8d96662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +# [5.0.0](https://github.com/LEGO/AsyncAPI.NET/compare/v4.1.0...v5.0.0) (2023-12-14) + + +### Bug Fixes + +* add type to references, always. ([#139](https://github.com/LEGO/AsyncAPI.NET/issues/139)) ([3031023](https://github.com/LEGO/AsyncAPI.NET/commit/30310232bb3869258486d9f7f85721d4e3fb46eb)) +* added missing mapping for ordering ([#138](https://github.com/LEGO/AsyncAPI.NET/issues/138)) ([510426e](https://github.com/LEGO/AsyncAPI.NET/commit/510426e200b4fe97ad1b8e9a6e94a615593c2a3c)) +* patternProperties should also be walked as a reference ([#133](https://github.com/LEGO/AsyncAPI.NET/issues/133)) ([dc544f1](https://github.com/LEGO/AsyncAPI.NET/commit/dc544f1c01be3b95ded08ee894453ce8529eafb3)) + + +* chore(settings)!: make reader bindings IEnumerable to allow for simpler usage ([e1f8c87](https://github.com/LEGO/AsyncAPI.NET/commit/e1f8c8766767ce642546a911810064a5234f04c3)) + + +### Features + +* allow non-component references ([#132](https://github.com/LEGO/AsyncAPI.NET/issues/132)) ([71fe571](https://github.com/LEGO/AsyncAPI.NET/commit/71fe571a3db0b4fbc13f4573b4d4b53f4f6b0911)) +* **bindings:** add high throughput fifo properties ([#135](https://github.com/LEGO/AsyncAPI.NET/issues/135)) ([44ffcf4](https://github.com/LEGO/AsyncAPI.NET/commit/44ffcf4ceaf06a5168597e1eeb9407f09d47ab23)) + + +### BREAKING CHANGES + +* changes how bindings are applied. + # [4.1.0](https://github.com/LEGO/AsyncAPI.NET/compare/v4.0.2...v4.1.0) (2023-09-27) From c336ed8aaff012479be6eaf1b4c05d133a47bb7f Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 12 Jan 2024 15:04:07 +0100 Subject: [PATCH 38/84] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fda302e2..fe9189f5 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ There is a nifty helper to add different types of bindings, or like in the examp ```csharp var settings = new AsyncApiReaderSettings(); -settings.Bindings.Add(BindingsCollection.All); +settings.Bindings = BindingsCollection.All; var asyncApiDocument = new AsyncApiStringReader(settings).Read(stream, out var diagnostic); ``` From 4a6c6a8fed3a970153bd511daad5e614dbcdf2df Mon Sep 17 00:00:00 2001 From: gokerakc Date: Mon, 12 Feb 2024 13:09:14 +0000 Subject: [PATCH 39/84] feat: added new topic configuration properties --- .../Kafka/KafkaChannelBinding.cs | 4 ++++ .../Kafka/TopicConfigurationObject.cs | 24 +++++++++++++++++++ src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs | 4 ++++ .../Bindings/Kafka/KafkaBindings_Should.cs | 10 +++++++- 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs index 48fa5f04..d8c43556 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs @@ -51,6 +51,10 @@ public class KafkaChannelBinding : ChannelBinding { "retention.bytes", (a, n) => { a.RetentionBytes = n.GetIntegerValue(); } }, { "delete.retention.ms", (a, n) => { a.DeleteRetentionMiliseconds = n.GetIntegerValue(); } }, { "max.message.bytes", (a, n) => { a.MaxMessageBytes = n.GetIntegerValue(); } }, + { "confluent.key.schema.validation", (a, n) => { a.ConfluentKeySchemaValidation = n.GetBooleanValue(); } }, + { "confluent.key.subject.name.strategy", (a, n) => { a.ConfluentKeySubjectName = n.GetScalarValue(); } }, + { "confluent.value.schema.validation", (a, n) => { a.ConfluentValueSchemaValidation = n.GetBooleanValue(); } }, + { "confluent.value.subject.name.strategy", (a, n) => { a.ConfluentValueSubjectName = n.GetScalarValue(); } }, }; /// diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs index da6233c3..2cbf6b4c 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs @@ -33,6 +33,26 @@ public class TopicConfigurationObject : IAsyncApiElement /// The max.message.bytes configuration option. /// public int? MaxMessageBytes { get; set; } + + /// + /// The confluent.key.schema.validation configuration option. + /// + public bool? ConfluentKeySchemaValidation { get; set; } + + /// + /// The confluent.key.subject.name.strategy configuration option. + /// + public string ConfluentKeySubjectName { get; set; } + + /// + /// The confluent.value.schema.validation configuration option. + /// + public bool? ConfluentValueSchemaValidation { get; set; } + + /// + /// The confluent.value.subject.name.strategy configuration option. + /// + public string ConfluentValueSubjectName { get; set; } public void Serialize(IAsyncApiWriter writer) { @@ -47,6 +67,10 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteOptionalProperty(AsyncApiConstants.RetentionBytes, this.RetentionBytes); writer.WriteOptionalProperty(AsyncApiConstants.DeleteRetentionMiliseconds, this.DeleteRetentionMiliseconds); writer.WriteOptionalProperty(AsyncApiConstants.MaxMessageBytes, this.MaxMessageBytes); + writer.WriteOptionalProperty(AsyncApiConstants.ConfluentKeySchemaValidation, this.ConfluentKeySchemaValidation); + writer.WriteOptionalProperty(AsyncApiConstants.ConfluentKeySubjectName, this.ConfluentKeySubjectName); + writer.WriteOptionalProperty(AsyncApiConstants.ConfluentValueSchemaValidation, this.ConfluentValueSchemaValidation); + writer.WriteOptionalProperty(AsyncApiConstants.ConfluentValueSubjectName, this.ConfluentValueSubjectName); writer.WriteEndObject(); } } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs index 37b1b45d..d83915bf 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs @@ -136,6 +136,10 @@ public static class AsyncApiConstants public const string RetentionBytes = "retention.bytes"; public const string DeleteRetentionMiliseconds = "delete.retention.ms"; public const string MaxMessageBytes = "max.message.bytes"; + public const string ConfluentKeySchemaValidation = "confluent.key.schema.validation"; + public const string ConfluentKeySubjectName = "confluent.key.subject.name.strategy"; + public const string ConfluentValueSchemaValidation = "confluent.value.schema.validation"; + public const string ConfluentValueSubjectName = "confluent.value.subject.name.strategy"; public const string TopicConfiguration = "topicConfiguration"; public const string GeoReplication = "geo-replication"; public const string AdditionalItems = "additionalItems"; diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index efeac315..4a791556 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -30,7 +30,11 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() retention.ms: 1 retention.bytes: 2 delete.retention.ms: 3 - max.message.bytes: 4"; + max.message.bytes: 4 + confluent.key.schema.validation: true + confluent.key.subject.name.strategy: TopicNameStrategy + confluent.value.schema.validation: true + confluent.value.subject.name.strategy: TopicNameStrategy"; var channel = new AsyncApiChannel(); channel.Bindings.Add(new KafkaChannelBinding @@ -45,6 +49,10 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() RetentionBytes = 2, DeleteRetentionMiliseconds = 3, MaxMessageBytes = 4, + ConfluentKeySchemaValidation = true, + ConfluentKeySubjectName = "TopicNameStrategy", + ConfluentValueSchemaValidation = true, + ConfluentValueSubjectName = "TopicNameStrategy", }, }); From eace86dde4fc704d4652d19e7073be3b37ade6c7 Mon Sep 17 00:00:00 2001 From: gokerakc Date: Wed, 14 Feb 2024 09:53:50 +0000 Subject: [PATCH 40/84] fix: updated topic configuration data types --- .../Kafka/KafkaChannelBinding.cs | 4 ++-- .../Kafka/TopicConfigurationObject.cs | 14 +++++++------- src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs | 4 ++-- .../Bindings/Kafka/KafkaBindings_Should.cs | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs index d8c43556..82a18968 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs @@ -47,9 +47,9 @@ public class KafkaChannelBinding : ChannelBinding private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new () { { "cleanup.policy", (a, n) => { a.CleanupPolicy = n.CreateSimpleList(s => s.GetScalarValue()); } }, - { "retention.ms", (a, n) => { a.RetentionMiliseconds = n.GetIntegerValue(); } }, + { "retention.ms", (a, n) => { a.RetentionMilliseconds = n.GetIntegerValue(); } }, { "retention.bytes", (a, n) => { a.RetentionBytes = n.GetIntegerValue(); } }, - { "delete.retention.ms", (a, n) => { a.DeleteRetentionMiliseconds = n.GetIntegerValue(); } }, + { "delete.retention.ms", (a, n) => { a.DeleteRetentionMilliseconds = n.GetIntegerValue(); } }, { "max.message.bytes", (a, n) => { a.MaxMessageBytes = n.GetIntegerValue(); } }, { "confluent.key.schema.validation", (a, n) => { a.ConfluentKeySchemaValidation = n.GetBooleanValue(); } }, { "confluent.key.subject.name.strategy", (a, n) => { a.ConfluentKeySubjectName = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs index 2cbf6b4c..da0027c8 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs @@ -17,17 +17,17 @@ public class TopicConfigurationObject : IAsyncApiElement /// /// The retention.ms configuration option. /// - public int? RetentionMiliseconds { get; set; } + public long? RetentionMilliseconds { get; set; } /// /// The retention.bytes configuration option. /// - public int? RetentionBytes { get; set; } + public long? RetentionBytes { get; set; } /// /// The delete.retention.ms configuration option. /// - public int? DeleteRetentionMiliseconds { get; set; } + public long? DeleteRetentionMilliseconds { get; set; } /// /// The max.message.bytes configuration option. @@ -63,10 +63,10 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteOptionalCollection(AsyncApiConstants.CleanupPolicy, this.CleanupPolicy, (w, s) => w.WriteValue(s)); - writer.WriteOptionalProperty(AsyncApiConstants.RetentionMiliseconds, this.RetentionMiliseconds); - writer.WriteOptionalProperty(AsyncApiConstants.RetentionBytes, this.RetentionBytes); - writer.WriteOptionalProperty(AsyncApiConstants.DeleteRetentionMiliseconds, this.DeleteRetentionMiliseconds); - writer.WriteOptionalProperty(AsyncApiConstants.MaxMessageBytes, this.MaxMessageBytes); + writer.WriteOptionalProperty(AsyncApiConstants.RetentionMilliseconds, this.RetentionMilliseconds); + writer.WriteOptionalProperty(AsyncApiConstants.RetentionBytes, this.RetentionBytes); + writer.WriteOptionalProperty(AsyncApiConstants.DeleteRetentionMilliseconds, this.DeleteRetentionMilliseconds); + writer.WriteOptionalProperty(AsyncApiConstants.MaxMessageBytes, this.MaxMessageBytes); writer.WriteOptionalProperty(AsyncApiConstants.ConfluentKeySchemaValidation, this.ConfluentKeySchemaValidation); writer.WriteOptionalProperty(AsyncApiConstants.ConfluentKeySubjectName, this.ConfluentKeySubjectName); writer.WriteOptionalProperty(AsyncApiConstants.ConfluentValueSchemaValidation, this.ConfluentValueSchemaValidation); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs index d83915bf..5806e8f9 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiConstants.cs @@ -132,9 +132,9 @@ public static class AsyncApiConstants public const string ServerVariables = "serverVariables"; public const string MessageId = "messageId"; public const string CleanupPolicy = "cleanup.policy"; - public const string RetentionMiliseconds = "retention.ms"; + public const string RetentionMilliseconds = "retention.ms"; public const string RetentionBytes = "retention.bytes"; - public const string DeleteRetentionMiliseconds = "delete.retention.ms"; + public const string DeleteRetentionMilliseconds = "delete.retention.ms"; public const string MaxMessageBytes = "max.message.bytes"; public const string ConfluentKeySchemaValidation = "confluent.key.schema.validation"; public const string ConfluentKeySubjectName = "confluent.key.subject.name.strategy"; diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index 4a791556..32f83748 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -27,7 +27,7 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() cleanup.policy: - delete - compact - retention.ms: 1 + retention.ms: 2592000000 retention.bytes: 2 delete.retention.ms: 3 max.message.bytes: 4 @@ -45,9 +45,9 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() TopicConfiguration = new TopicConfigurationObject() { CleanupPolicy = new List { "delete", "compact" }, - RetentionMiliseconds = 1, + RetentionMilliseconds = 2592000000, RetentionBytes = 2, - DeleteRetentionMiliseconds = 3, + DeleteRetentionMilliseconds = 3, MaxMessageBytes = 4, ConfluentKeySchemaValidation = true, ConfluentKeySubjectName = "TopicNameStrategy", From 1e463fc5c902d4fef0f9958858330f6eee841a5c Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Thu, 15 Feb 2024 05:28:22 +0000 Subject: [PATCH 41/84] chore: update CHANGELOG.md --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8d96662..77559496 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# [5.1.0](https://github.com/LEGO/AsyncAPI.NET/compare/v5.0.0...v5.1.0) (2024-02-15) + + +### Bug Fixes + +* updated topic configuration data types ([eace86d](https://github.com/LEGO/AsyncAPI.NET/commit/eace86dde4fc704d4652d19e7073be3b37ade6c7)) + + +### Features + +* added new topic configuration properties ([4a6c6a8](https://github.com/LEGO/AsyncAPI.NET/commit/4a6c6a8fed3a970153bd511daad5e614dbcdf2df)) + # [5.0.0](https://github.com/LEGO/AsyncAPI.NET/compare/v4.1.0...v5.0.0) (2023-12-14) From 0d516ce1326a122e2b34424b21bd90582de49853 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Thu, 15 Feb 2024 06:35:44 +0100 Subject: [PATCH 42/84] ci: add run number to beta releases. --- .github/workflows/release-internal.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-internal.yml b/.github/workflows/release-internal.yml index fef5c2e4..971be8e7 100644 --- a/.github/workflows/release-internal.yml +++ b/.github/workflows/release-internal.yml @@ -1,4 +1,4 @@ -name: Publish internal NuGet package +name: Publish beta NuGet package on: push: branches: [ main ] @@ -46,7 +46,7 @@ jobs: uses: actions/setup-dotnet@v1 - name: Build ${{ matrix.package-name }} project and pack NuGet package - run: dotnet pack src/${{ matrix.package-name }}/${{ matrix.package-name }}.csproj -c Release -o out-${{ matrix.package-name }} -p:PackageVersion=${{ needs.check.outputs.version }}-beta + run: dotnet pack src/${{ matrix.package-name }}/${{ matrix.package-name }}.csproj -c Release -o out-${{ matrix.package-name }} -p:PackageVersion=${{ needs.check.outputs.version }}-beta.${{github.run_number}} - name: Push generated package to GitHub Packages registry run: dotnet nuget push out-${{ matrix.package-name }}/*.nupkg -s https://api.nuget.org/v3/index.json --skip-duplicate -n --api-key ${{secrets.NUGET}} From 93ba4755babd05a0d21f3530aab417eeed3b7073 Mon Sep 17 00:00:00 2001 From: VisualBean Date: Fri, 16 Feb 2024 12:16:06 +0100 Subject: [PATCH 43/84] fix: long values for missing retention properties --- src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs | 6 +++--- .../Bindings/Kafka/KafkaBindings_Should.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs index 82a18968..b6185cd4 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs @@ -47,9 +47,9 @@ public class KafkaChannelBinding : ChannelBinding private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new () { { "cleanup.policy", (a, n) => { a.CleanupPolicy = n.CreateSimpleList(s => s.GetScalarValue()); } }, - { "retention.ms", (a, n) => { a.RetentionMilliseconds = n.GetIntegerValue(); } }, - { "retention.bytes", (a, n) => { a.RetentionBytes = n.GetIntegerValue(); } }, - { "delete.retention.ms", (a, n) => { a.DeleteRetentionMilliseconds = n.GetIntegerValue(); } }, + { "retention.ms", (a, n) => { a.RetentionMilliseconds = n.GetLongValue(); } }, + { "retention.bytes", (a, n) => { a.RetentionBytes = n.GetLongValue(); } }, + { "delete.retention.ms", (a, n) => { a.DeleteRetentionMilliseconds = n.GetLongValue(); } }, { "max.message.bytes", (a, n) => { a.MaxMessageBytes = n.GetIntegerValue(); } }, { "confluent.key.schema.validation", (a, n) => { a.ConfluentKeySchemaValidation = n.GetBooleanValue(); } }, { "confluent.key.subject.name.strategy", (a, n) => { a.ConfluentKeySubjectName = n.GetScalarValue(); } }, diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index 32f83748..26525652 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -27,7 +27,7 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() cleanup.policy: - delete - compact - retention.ms: 2592000000 + retention.ms: 15552000000 retention.bytes: 2 delete.retention.ms: 3 max.message.bytes: 4 @@ -45,7 +45,7 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() TopicConfiguration = new TopicConfigurationObject() { CleanupPolicy = new List { "delete", "compact" }, - RetentionMilliseconds = 2592000000, + RetentionMilliseconds = 15552000000, RetentionBytes = 2, DeleteRetentionMilliseconds = 3, MaxMessageBytes = 4, From b58f0425bf7e443df8c7cb470ac65c6f46a984f2 Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Fri, 16 Feb 2024 11:17:23 +0000 Subject: [PATCH 44/84] chore: update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77559496..7e4adc04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [5.1.1](https://github.com/LEGO/AsyncAPI.NET/compare/v5.1.0...v5.1.1) (2024-02-16) + + +### Bug Fixes + +* long values for missing retention properties ([93ba475](https://github.com/LEGO/AsyncAPI.NET/commit/93ba4755babd05a0d21f3530aab417eeed3b7073)) + # [5.1.0](https://github.com/LEGO/AsyncAPI.NET/compare/v5.0.0...v5.1.0) (2024-02-15) From 9063f4e4f19929f8ccbdee5bd46dd9e27a3e0c08 Mon Sep 17 00:00:00 2001 From: VisualBean Date: Mon, 26 Feb 2024 12:49:45 +0100 Subject: [PATCH 45/84] feat: improve AsyncApiAny api surface. ## About the PR Fixes some issues with working with AsyncApiAny, figuring out the correct types etc. Added new GetValue type methods for extracting expected values. These use `system.text.json` to deserialize to `T` from the `JsonNode` type. Added a static FromExtension method, to remove redundant type casting. So instead of ```csharp if (TryGetValue(key, out IAsyncApiExtension extension)) { var myType = (extension as AsyncApiAny).GetValue(); } ``` You do ```csharp if (TryGetValue(key, out IAsyncApiExtension extension)) { var myType = AsyncApiAny.FromExtensionOrDefault(extension); } ``` Added new constructor allowing for much simpler `AsyncApiAny` initialization, utlizing `system.json.text` to figure out the JsonNode type. ### Changelog - Added: `GetValue()` - Added: `GetValueOrDefault()` - Added: `TryGetValue()` - Added: static `FromExtensionOrDefault(IAsyncApiExtension extension)` - Added: new constructor to allow for easier object creation. - Obsoleted: `AsyncApiArray` - Obsoleted: `AsyncApiObject` --- .../AsyncApiExtensibleExtensions.cs | 21 ++++ src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs | 2 + .../Models/Any/AsyncAPIObject.cs | 2 + src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs | 109 +++++++++++++++++- .../AsyncApiDocumentV2Tests.cs | 27 +++-- .../AsyncApiReaderTests.cs | 4 +- .../Bindings/Sns/SnsBindings_Should.cs | 7 ++ .../LEGO.AsyncAPI.Tests.csproj | 1 - .../Models/AsyncApiAnyTests.cs | 54 +++++++++ 9 files changed, 211 insertions(+), 16 deletions(-) create mode 100644 test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs diff --git a/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs b/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs index 426020ce..8fdb50e5 100644 --- a/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs +++ b/src/LEGO.AsyncAPI/Extensions/AsyncApiExtensibleExtensions.cs @@ -2,6 +2,7 @@ namespace LEGO.AsyncAPI.Extensions { + using System.Collections.Generic; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; @@ -38,5 +39,25 @@ public static void AddExtension(this T element, string name, IAsyncApiExtensi element.Extensions[name] = any ?? throw Error.ArgumentNull(nameof(any)); } + + /// + /// Tries the get value or default. + /// + /// + /// The dictionary. + /// The key. + /// The value. + /// + public static bool TryGetValueOrDefault(this IDictionary dictionary, string key, out T value) + { + if (dictionary.TryGetValue(key, out var extension)) + { + value = AsyncApiAny.FromExtensionOrDefault(extension); + return true; + } + + value = default(T); + return false; + } } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs index 042b1f68..01970b19 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs @@ -2,11 +2,13 @@ namespace LEGO.AsyncAPI.Models { + using System; using System.Collections.ObjectModel; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; + [Obsolete("Please use AsyncApiAny instead")] public class AsyncApiArray : Collection, IAsyncApiExtension, IAsyncApiElement { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs index 90e8e124..e93f595e 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs @@ -2,6 +2,7 @@ namespace LEGO.AsyncAPI.Models { + using System; using System.Collections.Generic; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models.Interfaces; @@ -10,6 +11,7 @@ namespace LEGO.AsyncAPI.Models /// /// AsyncApi object. /// + [Obsolete("Please use AsyncApiAny instead")] public class AsyncApiObject : Dictionary, IAsyncApiExtension, IAsyncApiElement { diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs index 098c88d4..5c5f2b31 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs @@ -2,6 +2,8 @@ namespace LEGO.AsyncAPI.Models { + using System.Collections.Generic; + using System.Text.Json; using System.Text.Json.Nodes; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; @@ -13,10 +15,15 @@ namespace LEGO.AsyncAPI.Models /// public class AsyncApiAny : IAsyncApiElement, IAsyncApiExtension { + private JsonSerializerOptions options = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }; + private JsonNode node; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The node. public AsyncApiAny(JsonNode node) @@ -24,17 +31,115 @@ public AsyncApiAny(JsonNode node) this.node = node; } + /// + /// Initializes a new instance of the class. + /// + /// The object. + public AsyncApiAny(object obj) + { + this.node = JsonNode.Parse(JsonSerializer.Serialize(obj, this.options)); + } + + /// + /// Initializes a new instance of the class. + /// + /// The node. + public AsyncApiAny(JsonArray node) + { + this.node = node; + } + + /// + /// Initializes a new instance of the class. + /// + /// The node. + public AsyncApiAny(JsonObject node) + { + this.node = node; + } + + /// + /// Converts to from an Extension. + /// + /// T. + /// The extension. + /// . + public static T FromExtensionOrDefault(IAsyncApiExtension extension) + { + if (extension is AsyncApiAny any) + { + return any.GetValueOrDefault(); + } + else + { + return default(T); + } + } + /// /// Gets the node. /// + /// . /// /// The node. /// public JsonNode GetNode() => this.node; + /// + /// Gets the value. + /// + /// . + /// . public T GetValue() { - return this.node.GetValue(); + if (this.node == null) + { + return default(T); + } + + if (this.node is JsonValue) + { + return this.node.GetValue(); + } + + return JsonSerializer.Deserialize(this.node.ToJsonString()); + } + + /// + /// Gets the value or default. + /// + /// . + /// or default. + public T GetValueOrDefault() + { + try + { + return this.GetValue(); + } + catch (System.Exception) + { + return default(T); + } + } + + /// + /// Tries the get value. + /// + /// . + /// The value. + /// true if the value could be converted, otherwise false. + public bool TryGetValue(out T value) + { + try + { + value = this.GetValue(); + return true; + } + catch (System.Exception) + { + value = default(T); + return false; + } } /// diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index b48b1b48..a598ac18 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -7,16 +7,21 @@ namespace LEGO.AsyncAPI.Tests using System.Globalization; using System.IO; using System.Linq; - using LEGO.AsyncAPI.Bindings.Pulsar; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Bindings.Pulsar; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Writers; using NUnit.Framework; + public class ExtensionClass + { + public string Key { get; set; } + public long OtherKey { get; set; } + } public class AsyncApiDocumentV2Tests { [Test] @@ -838,8 +843,6 @@ public void SerializeV2_WithFullSpec_Serializes() string traitTitle = "traitTitle"; string schemaTitle = "schemaTitle"; string schemaDescription = "schemaDescription"; - string anyKey = "key"; - string anyOtherKey = "otherKey"; string anyStringValue = "value"; long anyLongValue = long.MaxValue; string exampleSummary = "exampleSummary"; @@ -864,6 +867,8 @@ public void SerializeV2_WithFullSpec_Serializes() string refreshUrl = "https://example.com/refresh"; string authorizationUrl = "https://example.com/authorization"; string requirementString = "requirementItem"; + + var document = new AsyncApiDocument() { Id = documentId, @@ -1016,11 +1021,11 @@ public void SerializeV2_WithFullSpec_Serializes() Description = schemaDescription, Examples = new List { - new AsyncApiObject + new AsyncApiAny(new ExtensionClass { - { anyKey, new AsyncApiAny(anyStringValue) }, - { anyOtherKey, new AsyncApiAny(anyLongValue) }, - }, + Key = anyStringValue, + OtherKey = anyLongValue, + }), }, }, Examples = new List @@ -1029,11 +1034,11 @@ public void SerializeV2_WithFullSpec_Serializes() { Summary = exampleSummary, Name = exampleName, - Payload = new AsyncApiObject + Payload =new AsyncApiAny(new ExtensionClass { - { anyKey, new AsyncApiAny(anyStringValue) }, - { anyOtherKey, new AsyncApiAny(anyLongValue) }, - }, + Key = anyStringValue, + OtherKey = anyLongValue, + }), Extensions = new Dictionary { { extensionKey, new AsyncApiAny(extensionString) }, diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index 1214da3c..8461aece 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -40,9 +40,9 @@ public void Read_WithExtensionParser_Parses() "; Func valueExtensionParser = (any) => { - if (any.GetNode() is JsonValue value) + if (any.TryGetValue(out var value)) { - if (value.GetScalarValue() == "onetwothreefour") + if (value == "onetwothreefour") { return new AsyncApiAny(1234); } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index d4d5cf05..dbca5966 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -381,6 +381,9 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Sns; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + var binding2 = new AsyncApiStringReader(settings).ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); + binding2.Bindings.First().Value.Extensions.TryGetValue("x-bindingExtension", out IAsyncApiExtension any); + var val = AsyncApiAny.FromExtensionOrDefault(any); // Assert Assert.AreEqual(actual, expected); @@ -388,5 +391,9 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() var expectedSnsBinding = (SnsOperationBinding)operation.Bindings.Values.First(); expectedSnsBinding.Should().BeEquivalentTo((SnsOperationBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); } + class ExtensionClass + { + public string bindingXPropertyName { get; set; } + } } } \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index 692c8e06..1be4a264 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -24,7 +24,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs new file mode 100644 index 00000000..598ef31f --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs @@ -0,0 +1,54 @@ +using LEGO.AsyncAPI.Models; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace LEGO.AsyncAPI.Tests +{ + + public class AsyncApiAnyTests + { + [Test] + public void GetValue_ReturnsCorrectConversions() + { + // Arrange + // Act + var a = new AsyncApiAny("string"); + var b = new AsyncApiAny(1); + var c = new AsyncApiAny(1.1); + var d = new AsyncApiAny(true); + var e = new AsyncApiAny(new MyType("test")); + var f = new AsyncApiAny(new List() { "test", "test2"}); + var g = new AsyncApiAny(new List() { "test", "test2"}.AsEnumerable()); + var h = new AsyncApiAny(new List() { new MyType("test") }); + var i = new AsyncApiAny(new Dictionary() { { "t", 2 } }); + var j = new AsyncApiAny(new Dictionary() { { "t", new MyType("test") } }); + + // Assert + Assert.AreEqual("string", a.GetValue()); + Assert.AreEqual(1, b.GetValue()); + Assert.AreEqual(1.1, c.GetValue()); + Assert.AreEqual(true, d.GetValue()); + Assert.NotNull(e.GetValue()); + Assert.IsNotEmpty(f.GetValue>()); + Assert.IsNotEmpty(f.GetValue>()); + Assert.IsNotEmpty(g.GetValue>()); + Assert.IsNotEmpty(g.GetValue>()); + Assert.IsNotEmpty(h.GetValue>()); + Assert.IsNotEmpty(h.GetValue>()); + Assert.IsNotEmpty(i.GetValue>()); + Assert.IsNotEmpty(j.GetValue>()); + } + + class MyType + { + public MyType(string value) + { + this.Value = value; + } + + public string Value { get; set; } + } + } +} From 72588e7276099751060daa6cc042a5e6d9acd634 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Thu, 29 Feb 2024 09:22:37 +0100 Subject: [PATCH 46/84] ci: run test for test changes --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f8d2c69..69ea5318 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,13 @@ on: branches: [ main, vnext ] paths: - 'src/**' + - 'test/**' - '!**/*.md' pull_request: branches: [ main, vnext ] paths: - 'src/**' + - 'test/**' - '!**/*.md' workflow_dispatch: jobs: From 9291da603335fd202b59f421945629952f136296 Mon Sep 17 00:00:00 2001 From: Byron Mayne Date: Thu, 14 Mar 2024 07:26:45 -0400 Subject: [PATCH 47/84] feat: targetframework to netstandard2.0 (#150) --- AsyncAPI.sln | 3 +- Common.Build.props | 13 +++ .../BindingsCollection.cs | 11 ++- .../LEGO.AsyncAPI.Bindings.csproj | 21 ++-- .../LEGO.AsyncAPI.Readers.csproj | 17 +--- src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj | 19 ++-- .../LEGO.AsyncAPI.Tests.csproj | 98 +++++++++---------- 7 files changed, 90 insertions(+), 92 deletions(-) create mode 100644 Common.Build.props diff --git a/AsyncAPI.sln b/AsyncAPI.sln index f972fa3d..db79f153 100644 --- a/AsyncAPI.sln +++ b/AsyncAPI.sln @@ -12,9 +12,10 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DE167614-5BCB-4046-BD4C-ABB70E9F3462}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig + Common.Build.props = Common.Build.props EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LEGO.AsyncAPI.Bindings", "src\LEGO.AsyncAPI.Bindings\LEGO.AsyncAPI.Bindings.csproj", "{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LEGO.AsyncAPI.Bindings", "src\LEGO.AsyncAPI.Bindings\LEGO.AsyncAPI.Bindings.csproj", "{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Common.Build.props b/Common.Build.props new file mode 100644 index 00000000..f4f9797f --- /dev/null +++ b/Common.Build.props @@ -0,0 +1,13 @@ + + + + 10 + netstandard2.0 + disable + The LEGO Group + https://github.com/LEGO/AsyncAPI.NET + README.md + https://github.com/LEGO/AsyncAPI.NET + asyncapi .net openapi documentation + + \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs index e52392b6..ccfad8a4 100644 --- a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -19,8 +19,15 @@ public static TCollection Add( IEnumerable source) where TCollection : ICollection { - ArgumentNullException.ThrowIfNull(destination); - ArgumentNullException.ThrowIfNull(source); + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } if (destination is List list) { diff --git a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj index e05aacab..751ea5c6 100644 --- a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj +++ b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj @@ -1,20 +1,11 @@ - - + + - net6.0 - disable - The LEGO Group - https://github.com/LEGO/AsyncAPI.NET - README.md - AsyncAPI.NET Bindings - asyncapi .net openapi documentation - AsyncAPI.NET.Bindings - LEGO.AsyncAPI.Bindings - LEGO.AsyncAPI.Bindings - https://github.com/LEGO/AsyncAPI.NET + AsyncAPI.NET Bindings + AsyncAPI.NET.Bindings + LEGO.AsyncAPI.Bindings + LEGO.AsyncAPI.Bindings - - diff --git a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj index 6fd9d2e7..b0164afd 100644 --- a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj +++ b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj @@ -1,18 +1,11 @@  - + - net6.0 - disable disable - The LEGO Group - https://github.com/LEGO/AsyncAPI.NET - README.md - AsyncAPI.NET Readers for JSON and YAML documents - asyncapi .net openapi documentation - AsyncAPI.NET.Readers - LEGO.AsyncAPI.Readers - LEGO.AsyncAPI.Readers - https://github.com/LEGO/AsyncAPI.NET + AsyncAPI.NET Readers for JSON and YAML documents + AsyncAPI.NET.Readers + LEGO.AsyncAPI.Readers + LEGO.AsyncAPI.Readers diff --git a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj index 8d4b9501..c2dd1598 100644 --- a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj +++ b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj @@ -1,19 +1,11 @@  - + - net6.0 - disable - The LEGO Group - https://github.com/LEGO/AsyncAPI.NET - README.md - AsyncAPI.NET models - asyncapi .net openapi documentation - AsyncAPI.NET - LEGO.AsyncAPI - LEGO.AsyncAPI - https://github.com/LEGO/AsyncAPI.NET + AsyncAPI.NET models + AsyncAPI.NET + LEGO.AsyncAPI + LEGO.AsyncAPI - @@ -27,6 +19,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + <_Parameter1>$(MSBuildProjectName).Tests diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index 1be4a264..f10a92bc 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -1,55 +1,55 @@  - - net6.0 - disable - enable + + net6.0 + disable + enable + false + $(NoWarn);SA1600 + - false - + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + From 8d128db869d8164cfaad156d4f29a7130a00827e Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Mon, 25 Mar 2024 23:51:30 +0100 Subject: [PATCH 48/84] feat(bindings): add amqp bindings (#153) --- .../AMQP/AMQPChannelBinding.cs | 86 +++++++++ .../AMQP/AMQPMessageBinding.cs | 51 +++++ .../AMQP/AMQPOperationBinding.cs | 103 ++++++++++ .../AMQP/ChannelType.cs | 15 ++ .../AMQP/DeliveryMode.cs | 15 ++ src/LEGO.AsyncAPI.Bindings/AMQP/Exchange.cs | 50 +++++ .../AMQP/ExchangeType.cs | 24 +++ src/LEGO.AsyncAPI.Bindings/AMQP/Queue.cs | 50 +++++ .../BindingsCollection.cs | 9 + .../Kafka/TopicConfigurationObject.cs | 8 +- .../ParseNodes/ParseNode.cs | 8 +- .../ParseNodes/ValueNode.cs | 8 +- .../Bindings/AMQP/AMQPBindings_Should.cs | 181 ++++++++++++++++++ 13 files changed, 596 insertions(+), 12 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/AMQP/AMQPChannelBinding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/AMQP/AMQPMessageBinding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/AMQP/AMQPOperationBinding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/AMQP/ChannelType.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/AMQP/DeliveryMode.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/AMQP/Exchange.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/AMQP/ExchangeType.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/AMQP/Queue.cs create mode 100644 test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPChannelBinding.cs new file mode 100644 index 00000000..f77dc37f --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPChannelBinding.cs @@ -0,0 +1,86 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.AMQP +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for AMQP channel settings. + /// + public class AMQPChannelBinding : ChannelBinding + { + /// + /// Defines what type of channel is it. Can be either queue or routingKey. + /// + public ChannelType Is { get; set; } + + /// + /// When is=routingKey, this object defines the exchange properties. + /// + public Exchange Exchange { get; set; } + + /// + /// When is=queue, this object defines the queue properties. + /// + public Queue Queue { get; set; } + + public override string BindingKey => "amqp"; + + protected override FixedFieldMap FixedFieldMap => new () + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "is", (a, n) => { a.Is = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "exchange", (a, n) => { a.Exchange = n.ParseMap(ExchangeFixedFields); } }, + { "queue", (a, n) => { a.Queue = n.ParseMap(QueueFixedFields); } }, + }; + + private static FixedFieldMap ExchangeFixedFields = new () + { + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + { "durable", (a, n) => { a.Durable = n.GetBooleanValue(); } }, + { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, + { "autoDelete", (a, n) => { a.AutoDelete = n.GetBooleanValue(); } }, + { "vhost", (a, n) => { a.Vhost = n.GetScalarValue(); } }, + }; + + private static FixedFieldMap QueueFixedFields = new() + { + { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, + { "durable", (a, n) => { a.Durable = n.GetBooleanValue(); } }, + { "exclusive", (a, n) => { a.Exclusive = n.GetBooleanValue(); } }, + { "autoDelete", (a, n) => { a.AutoDelete = n.GetBooleanValue(); } }, + { "vhost", (a, n) => { a.Vhost = n.GetScalarValue(); } }, + }; + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("is", this.Is.GetDisplayName()); + switch (this.Is) + { + case ChannelType.RoutingKey: + writer.WriteOptionalObject("exchange", this.Exchange, (w, t) => t.Serialize(w)); + break; + case ChannelType.Queue: + writer.WriteOptionalObject("queue", this.Queue, (w, t) => t.Serialize(w)); + break; + } + + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPMessageBinding.cs new file mode 100644 index 00000000..17642020 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPMessageBinding.cs @@ -0,0 +1,51 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.AMQP +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for AMQP messages. + /// + public class AMQPMessageBinding : MessageBinding + { + /// + /// A MIME encoding for the message content. + /// + public string ContentEncoding { get; set; } + + /// + /// Application-specific message type. + /// + public string MessageType { get; set; } + + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + + writer.WriteOptionalProperty("contentEncoding", this.ContentEncoding); + writer.WriteOptionalProperty("messageType", this.MessageType); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + + writer.WriteEndObject(); + } + + public override string BindingKey => "amqp"; + + protected override FixedFieldMap FixedFieldMap => new () + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "contentEncoding", (a, n) => { a.ContentEncoding = n.GetScalarValue(); } }, + { "messageType", (a, n) => { a.MessageType = n.GetScalarValue(); } }, + }; + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPOperationBinding.cs new file mode 100644 index 00000000..86dc74ef --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPOperationBinding.cs @@ -0,0 +1,103 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.AMQP +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for AMQP operations. + /// + public class AMQPOperationBinding : OperationBinding + { + /// + /// TTL (Time-To-Live) for the message. It MUST be greater than or equal to zero. + /// + public uint? Expiration { get; set; } + + /// + /// Identifies the user who has sent the message. + /// + public string UserId { get; set; } + + /// + /// The routing keys the message should be routed to at the time of publishing. + /// + public List Cc { get; set; } = new List(); + + /// + /// A priority for the message. + /// + public int? Priority { get; set; } + + /// + /// Delivery mode of the message. Its value MUST be either 1 (transient) or 2 (persistent). + /// + public DeliveryMode? DeliveryMode { get; set; } + + /// + /// Whether the message is mandatory or not. + /// + public bool? Mandatory { get; set; } + + /// + /// Like cc but consumers will not receive this information. + /// + public List Bcc { get; set; } = new List(); + + /// + /// Whether the message should include a timestamp or not. + /// + public bool? Timestamp { get; set; } + + /// + /// Whether the consumer should ack the message or not. + /// + public bool? Ack { get; set; } + + public override string BindingKey => "amqp"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "expiration", (a, n) => { a.Expiration = (uint?)n.GetIntegerValueOrDefault(); } }, + { "userId", (a, n) => { a.UserId = n.GetScalarValueOrDefault(); } }, + { "cc", (a, n) => { a.Cc = n.CreateSimpleList(s => s.GetScalarValue()); } }, + { "priority", (a, n) => { a.Priority = n.GetIntegerValueOrDefault(); } }, + { "deliveryMode", (a, n) => { a.DeliveryMode = (DeliveryMode?)n.GetIntegerValueOrDefault(); } }, + { "mandatory", (a, n) => { a.Mandatory = n.GetBooleanValueOrDefault(); } }, + { "bcc", (a, n) => { a.Bcc = n.CreateSimpleList(s => s.GetScalarValue()); } }, + { "timestamp", (a, n) => { a.Timestamp = n.GetBooleanValueOrDefault(); } }, + { "ack", (a, n) => { a.Ack = n.GetBooleanValueOrDefault(); } }, + }; + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty("expiration", (int)this.Expiration); + writer.WriteOptionalProperty("userId", this.UserId); + writer.WriteOptionalCollection("cc", this.Cc, (w, s) => w.WriteValue(s)); + writer.WriteOptionalProperty("priority", this.Priority); + writer.WriteOptionalProperty("deliveryMode", (int?)this.DeliveryMode); + writer.WriteOptionalProperty("mandatory", this.Mandatory); + writer.WriteOptionalCollection("bcc", this.Bcc, (w, s) => w.WriteValue(s)); + writer.WriteOptionalProperty("timestamp", this.Timestamp); + writer.WriteOptionalProperty("ack", this.Ack); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/ChannelType.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/ChannelType.cs new file mode 100644 index 00000000..a817ea49 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/AMQP/ChannelType.cs @@ -0,0 +1,15 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.AMQP +{ + using LEGO.AsyncAPI.Attributes; + + public enum ChannelType + { + [Display("routingKey")] + RoutingKey = 0, + + [Display("queue")] + Queue, + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/DeliveryMode.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/DeliveryMode.cs new file mode 100644 index 00000000..77b855b5 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/AMQP/DeliveryMode.cs @@ -0,0 +1,15 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.AMQP +{ + using LEGO.AsyncAPI.Attributes; + + public enum DeliveryMode + { + [Display("transient")] + Transient = 1, + + [Display("persistent")] + Persistent = 2, + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/Exchange.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/Exchange.cs new file mode 100644 index 00000000..66815b3c --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/AMQP/Exchange.cs @@ -0,0 +1,50 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.AMQP +{ + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// Represents an exchange configuration. + /// + public class Exchange : IAsyncApiElement + { + /// + /// The name of the exchange. It MUST NOT exceed 255 characters long. + /// + public string Name { get; set; } + + /// + /// The type of the exchange. Can be either topic, direct, fanout, default, or headers. + /// + public ExchangeType Type { get; set; } + + /// + /// Whether the exchange should survive broker restarts or not. + /// + public bool Durable { get; set; } + + /// + /// Whether the exchange should be deleted when the last queue is unbound from it. + /// + public bool AutoDelete { get; set; } + + /// + /// The virtual host of the exchange. Defaults to /. + /// + public string Vhost { get; set; } = "/"; + + public void Serialize(IAsyncApiWriter writer) + { + writer.WriteStartObject(); + writer.WriteRequiredProperty(AsyncApiConstants.Name, this.Name); + writer.WriteRequiredProperty(AsyncApiConstants.Type, this.Type.GetDisplayName()); + writer.WriteRequiredProperty("durable", this.Durable); + writer.WriteRequiredProperty("autoDelete", this.AutoDelete); + writer.WriteRequiredProperty("vhost", this.Vhost); + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/ExchangeType.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/ExchangeType.cs new file mode 100644 index 00000000..1cfde013 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/AMQP/ExchangeType.cs @@ -0,0 +1,24 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.AMQP +{ + using LEGO.AsyncAPI.Attributes; + + public enum ExchangeType + { + [Display("default")] + Default = 0, + + [Display("topic")] + Topic, + + [Display("direct")] + Direct, + + [Display("fanout")] + Fanout, + + [Display("headers")] + Headers, + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/Queue.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/Queue.cs new file mode 100644 index 00000000..a3cf25d3 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/AMQP/Queue.cs @@ -0,0 +1,50 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.AMQP +{ + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + + /// + /// Represents a queue configuration. + /// + public class Queue : IAsyncApiElement + { + /// + /// The name of the queue. It MUST NOT exceed 255 characters long. + /// + public string Name { get; set; } + + /// + /// Whether the queue should survive broker restarts or not. + /// + public bool Durable { get; set; } + + /// + /// Whether the queue should be used only by one connection or not. + /// + public bool Exclusive { get; set; } + + /// + /// Whether the queue should be deleted when the last consumer unsubscribes. + /// + public bool AutoDelete { get; set; } + + /// + /// The virtual host of the queue. Defaults to /. + /// + public string Vhost { get; set; } = "/"; + + public void Serialize(IAsyncApiWriter writer) + { + writer.WriteStartObject(); + writer.WriteRequiredProperty(AsyncApiConstants.Name, this.Name); + writer.WriteRequiredProperty("durable", this.Durable); + writer.WriteRequiredProperty("exclusive", this.Exclusive); + writer.WriteRequiredProperty("autoDelete", this.AutoDelete); + writer.WriteRequiredProperty("vhost", this.Vhost); + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs index ccfad8a4..0fb7876f 100644 --- a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -3,6 +3,7 @@ namespace LEGO.AsyncAPI.Bindings { using System; using System.Collections.Generic; + using LEGO.AsyncAPI.Bindings.AMQP; using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Bindings.Pulsar; @@ -51,6 +52,7 @@ public static TCollection Add( Websockets, Sqs, Sns, + AMQP, }; public static IEnumerable> Http => new List> @@ -89,5 +91,12 @@ public static TCollection Add( new SnsChannelBinding(), new SnsOperationBinding(), }; + + public static IEnumerable> AMQP => new List> + { + new AMQPChannelBinding(), + new AMQPOperationBinding(), + new AMQPMessageBinding(), + }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs index da0027c8..a3fd1e7a 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs @@ -33,22 +33,22 @@ public class TopicConfigurationObject : IAsyncApiElement /// The max.message.bytes configuration option. /// public int? MaxMessageBytes { get; set; } - + /// /// The confluent.key.schema.validation configuration option. /// public bool? ConfluentKeySchemaValidation { get; set; } - + /// /// The confluent.key.subject.name.strategy configuration option. /// public string ConfluentKeySubjectName { get; set; } - + /// /// The confluent.value.schema.validation configuration option. /// public bool? ConfluentValueSchemaValidation { get; set; } - + /// /// The confluent.value.subject.name.strategy configuration option. /// diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs index 838eb358..da3e8a1a 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ParseNode.cs @@ -94,7 +94,7 @@ public virtual string GetScalarValue() throw new AsyncApiReaderException("Cannot create a scalar value from this type of node.", this.Context); } - public virtual string GetScalarValueOrDefault(string defaultValue) + public virtual string GetScalarValueOrDefault(string defaultValue = null) { throw new AsyncApiReaderException("Cannot create a scalar value from this type of node.", this.Context); } @@ -104,7 +104,7 @@ public virtual bool GetBooleanValue() throw new AsyncApiReaderException("Cannot create a scalar value from this type of node.", this.Context); } - public virtual bool? GetBooleanValueOrDefault(bool? defaultValue) + public virtual bool? GetBooleanValueOrDefault(bool? defaultValue = null) { throw new AsyncApiReaderException("Cannot create a scalar value from this type of node.", this.Context); } @@ -114,7 +114,7 @@ public virtual int GetIntegerValue() throw new AsyncApiReaderException("Cannot create a scalar value from this type of node.", this.Context); } - public virtual int? GetIntegerValueOrDefault(int? defaultValue) + public virtual int? GetIntegerValueOrDefault(int? defaultValue = null) { throw new AsyncApiReaderException("Cannot create a scalar value from this type of node.", this.Context); } @@ -124,7 +124,7 @@ public virtual long GetLongValue() throw new AsyncApiReaderException("Cannot create a scalar value from this type of node.", this.Context); } - public virtual long? GetLongValueOrDefault(long? defaultValue) + public virtual long? GetLongValueOrDefault(long? defaultValue = null) { throw new AsyncApiReaderException("Cannot create a scalar value from this type of node.", this.Context); } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs index 17ec9ac7..e580afe6 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs @@ -32,7 +32,7 @@ public override string GetScalarValue() return this.cachedScalarValue; } - public override string GetScalarValueOrDefault(string defaultValue) + public override string GetScalarValueOrDefault(string defaultValue = null) { var value = this.GetScalarValue(); if (value is not null) @@ -53,7 +53,7 @@ public override int GetIntegerValue() throw new AsyncApiReaderException("Value could not parse to integer."); } - public override int? GetIntegerValueOrDefault(int? defaultValue) + public override int? GetIntegerValueOrDefault(int? defaultValue = null) { if (int.TryParse(this.GetScalarValue(), out int value)) { @@ -73,7 +73,7 @@ public override long GetLongValue() throw new AsyncApiReaderException("Value could not parse to long."); } - public override long? GetLongValueOrDefault(long? defaultValue) + public override long? GetLongValueOrDefault(long? defaultValue = null) { if (long.TryParse(this.GetScalarValue(), out long value)) { @@ -93,7 +93,7 @@ public override bool GetBooleanValue() throw new AsyncApiReaderException("Value could not parse to bool."); } - public override bool? GetBooleanValueOrDefault(bool? defaultValue) + public override bool? GetBooleanValueOrDefault(bool? defaultValue = null) { if (bool.TryParse(this.GetScalarValue(), out bool value)) { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs new file mode 100644 index 00000000..09317d0c --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs @@ -0,0 +1,181 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.AMQP +{ + using System.Collections.Generic; + using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.AMQP; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using NUnit.Framework; + + public class AMQPBindings_Should + { + [Test] + public void AMQPChannelBinding_WithRoutingKey_SerializesAndDeserializes() + { + // Arrange + var expected = +@"bindings: + amqp: + is: routingKey + exchange: + name: myExchange + type: topic + durable: true + autoDelete: false + vhost: /"; + + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new AMQPChannelBinding + { + Is = ChannelType.RoutingKey, + Exchange = new Exchange + { + Name = "myExchange", + Type = ExchangeType.Topic, + Durable = true, + AutoDelete = false, + Vhost = "/", + }, + }); + + // Act + var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings = BindingsCollection.AMQP; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(channel); + } + + [Test] + public void AMQPChannelBinding_WithQueue_SerializesAndDeserializes() + { + // Arrange + var expected = +@"bindings: + amqp: + is: queue + queue: + name: my-queue-name + durable: true + exclusive: true + autoDelete: false + vhost: /"; + + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new AMQPChannelBinding + { + Is = ChannelType.Queue, + Queue = new Queue + { + Name = "my-queue-name", + Durable = true, + Exclusive = true, + AutoDelete = false, + Vhost = "/", + }, + }); + + // Act + var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings = BindingsCollection.AMQP; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(channel); + } + + [Test] + public void AMQPMessageBinding_WithFilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = +@"bindings: + amqp: + contentEncoding: gzip + messageType: user.signup"; + + var message = new AsyncApiMessage(); + + message.Bindings.Add(new AMQPMessageBinding + { + ContentEncoding = "gzip", + MessageType = "user.signup", + }); + + // Act + var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings = BindingsCollection.AMQP; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(message); + } + + [Test] + public void AMQPOperationBinding_WithFilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = +@"bindings: + amqp: + expiration: 100000 + userId: guest + cc: + - user.logs + priority: 10 + deliveryMode: 2 + mandatory: false + bcc: + - external.audit + timestamp: true + ack: false"; + + var operation = new AsyncApiOperation(); + operation.Bindings.Add(new AMQPOperationBinding + { + Expiration = 100000, + UserId = "guest", + Cc = new List { "user.logs" }, + Priority = 10, + DeliveryMode = DeliveryMode.Persistent, + Mandatory = false, + Bcc = new List { "external.audit" }, + Timestamp = true, + Ack = false, + });; + + // Act + var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + + var settings = new AsyncApiReaderSettings(); + settings.Bindings = BindingsCollection.AMQP; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(operation); + } + } +} From f5529e0e96d139e0cb1958d6b0620ed826e21cb5 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Tue, 26 Mar 2024 01:13:46 +0100 Subject: [PATCH 49/84] feat(bindings): add mqtt bindings (#154) --- .../BindingsCollection.cs | 9 ++ src/LEGO.AsyncAPI.Bindings/MQTT/LastWill.cs | 48 ++++++ .../MQTT/MQTTMessageBinding.cs | 65 +++++++++ .../MQTT/MQTTOperationBinding.cs | 61 ++++++++ .../MQTT/MQTTServerBinding.cs | 93 ++++++++++++ .../MQTT/MQTTBindings_Should.cs | 138 ++++++++++++++++++ 6 files changed, 414 insertions(+) create mode 100644 src/LEGO.AsyncAPI.Bindings/MQTT/LastWill.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/MQTT/MQTTOperationBinding.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/MQTT/MQTTServerBinding.cs create mode 100644 test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs index 0fb7876f..4fd5560f 100644 --- a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs +++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs @@ -6,6 +6,7 @@ namespace LEGO.AsyncAPI.Bindings using LEGO.AsyncAPI.Bindings.AMQP; using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Bindings.Kafka; + using LEGO.AsyncAPI.Bindings.MQTT; using LEGO.AsyncAPI.Bindings.Pulsar; using LEGO.AsyncAPI.Bindings.Sns; using LEGO.AsyncAPI.Bindings.Sqs; @@ -53,6 +54,7 @@ public static TCollection Add( Sqs, Sns, AMQP, + MQTT, }; public static IEnumerable> Http => new List> @@ -98,5 +100,12 @@ public static TCollection Add( new AMQPOperationBinding(), new AMQPMessageBinding(), }; + + public static IEnumerable> MQTT => new List> + { + new MQTTServerBinding(), + new MQTTOperationBinding(), + new MQTTMessageBinding(), + }; } } diff --git a/src/LEGO.AsyncAPI.Bindings/MQTT/LastWill.cs b/src/LEGO.AsyncAPI.Bindings/MQTT/LastWill.cs new file mode 100644 index 00000000..23118bfa --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/MQTT/LastWill.cs @@ -0,0 +1,48 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.MQTT +{ + using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Writers; + using System; + + public class LastWill : IAsyncApiElement + { + /// + /// The topic where the Last Will and Testament message will be sent. + /// + public string Topic { get; set; } + + /// + /// Defines how hard the broker/client will try to ensure that + /// the Last Will and Testament message is received. + /// Its value MUST be either 0, 1 or 2. + /// + public uint? QoS { get; set; } + + /// + /// Last Will message. + /// + public string Message { get; set; } + + /// + /// Whether the broker should retain the Last Will and Testament message or not. + /// + public bool Retain { get; set; } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("topic", this.Topic); + writer.WriteOptionalProperty("qos", (int?)this.QoS); + writer.WriteOptionalProperty("message", this.Message); + writer.WriteRequiredProperty("retain", this.Retain); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs new file mode 100644 index 00000000..fdee2114 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs @@ -0,0 +1,65 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.MQTT +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for MQTT messages. + /// + public class MQTTMessageBinding : MessageBinding + { + /// + /// Indicates the format of the payload. + /// Either: 0 (zero) for unspecified bytes, or 1 for UTF-8 encoded character data. + /// + public int? PayloadFormatIndicator { get; set; } + + /// + /// Correlation Data is used to identify the request the response message is for. + /// + public AsyncApiSchema CorrelationData { get; set; } + + /// + /// String describing the content type of the message payload. + /// This should not conflict with the contentType field of the associated AsyncAPI Message object. + /// + public string ContentType { get; set; } + + /// + /// The topic (channel URI) for a response message. + /// + public string ResponseTopic { get; set; } + + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteOptionalProperty("payloadFormatIndicator", this.PayloadFormatIndicator); + writer.WriteOptionalObject("correlationData", this.CorrelationData, (w, h) => h.SerializeV2(w)); + writer.WriteOptionalProperty("contentType", this.ContentType); + writer.WriteOptionalProperty("responseTopic", this.ResponseTopic); + writer.WriteOptionalProperty("bindingVersion", this.BindingVersion); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + + public override string BindingKey => "mqtt"; + + protected override FixedFieldMap FixedFieldMap => new () + { + { "payloadFormatIndicator", (a, n) => { a.PayloadFormatIndicator = n.GetIntegerValueOrDefault(); } }, + { "correlationData", (a, n) => { a.CorrelationData = JsonSchemaDeserializer.LoadSchema(n); } }, + { "contentType", (a, n) => { a.ContentType = n.GetScalarValue(); } }, + { "responseTopic", (a, n) => { a.ResponseTopic = n.GetScalarValue(); } }, + }; + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTOperationBinding.cs new file mode 100644 index 00000000..d3155ecd --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTOperationBinding.cs @@ -0,0 +1,61 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.MQTT +{ + using System; + using System.Collections.Generic; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for MQTT operations. + /// + public class MQTTOperationBinding : OperationBinding + { + /// + /// Defines the Quality of Service (QoS) levels for the message flow between client and server. + /// Its value MUST be either 0 (At most once delivery), 1 (At least once delivery), or 2 (Exactly once delivery). + /// + public int QoS { get; set; } + + /// + /// Whether the broker should retain the message or not. + /// + public bool Retain { get; set; } + + /// + /// Interval in seconds or a Schema Object containing the definition of the lifetime of the message. + /// + public int? MessageExpiryInterval { get; set; } + + public override string BindingKey => "mqtt"; + + protected override FixedFieldMap FixedFieldMap => new() + { + { "qos", (a, n) => { a.QoS = n.GetIntegerValue(); } }, + { "retain", (a, n) => { a.Retain = n.GetBooleanValue(); } }, + { "messageExpiryInterval", (a, n) => { a.MessageExpiryInterval = n.GetIntegerValueOrDefault(); } }, + }; + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("qos", this.QoS); + writer.WriteRequiredProperty("retain", this.Retain); + writer.WriteOptionalProperty("messageExpiryInterval", this.MessageExpiryInterval); + writer.WriteOptionalProperty("bindingVersion", this.BindingVersion); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} diff --git a/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTServerBinding.cs new file mode 100644 index 00000000..c37b12e3 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTServerBinding.cs @@ -0,0 +1,93 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.MQTT +{ + using System; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers.ParseNodes; + using LEGO.AsyncAPI.Writers; + + /// + /// Binding class for MQTT channel settings. + /// + public class MQTTServerBinding : ServerBinding + { + /// + /// The client identifier. + /// + public string ClientId { get; set; } + + /// + /// Whether to create a persistent connection or not. + /// When false, the connection will be persistent. + /// This is called clean start in MQTTv5. + /// + public bool? CleanSession { get; set; } + + /// + /// Last Will and Testament configuration. + /// + public LastWill LastWill { get; set; } + + /// + /// Interval in seconds of the longest period of time + /// the broker and the client can endure without sending a message. + /// + public int? KeepAlive { get; set; } + + /// + /// Interval in seconds the broker maintains a session + /// for a disconnected client until this interval expires. + /// + public int? SessionExpiryInterval { get; set; } + + /// + /// Number of bytes representing the maximum packet size + /// the client is willing to accept. + /// + public int? MaximumPacketSize { get; set; } + + public override string BindingKey => "mqtt"; + + protected override FixedFieldMap FixedFieldMap => new () + { + { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, + { "clientId", (a, n) => { a.ClientId = n.GetScalarValue(); } }, + { "cleanSession", (a, n) => { a.CleanSession = n.GetBooleanValueOrDefault(); } }, + { "lastWill", (a, n) => { a.LastWill = n.ParseMap(LastWillFixedFields); } }, + { "keepAlive", (a, n) => { a.KeepAlive = n.GetIntegerValueOrDefault(); } }, + { "sessionExpiryInterval", (a, n) => { a.SessionExpiryInterval = n.GetIntegerValueOrDefault(); } }, + { "maximumPacketSize", (a, n) => { a.MaximumPacketSize = n.GetIntegerValueOrDefault(); } }, + }; + + private static FixedFieldMap LastWillFixedFields = new () + { + { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, + { "qos", (a, n) => { a.QoS = (uint?)n.GetIntegerValueOrDefault(); } }, + { "message", (a, n) => { a.Message = n.GetScalarValue(); } }, + { "retain", (a, n) => { a.Retain = n.GetBooleanValue(); } }, + }; + + /// + /// Serialize to AsyncAPI V2 document without using reference. + /// + public override void SerializeProperties(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredProperty("clientId", this.ClientId); + writer.WriteOptionalProperty("cleanSession", this.CleanSession); + writer.WriteOptionalObject("lastWill", this.LastWill, (w, l) => l.Serialize(w)); + writer.WriteOptionalProperty("keepAlive", this.KeepAlive); + writer.WriteOptionalProperty("sessionExpiryInterval", this.SessionExpiryInterval); + writer.WriteOptionalProperty("maximumPacketSize", this.MaximumPacketSize); + writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion); + writer.WriteExtensions(this.Extensions); + writer.WriteEndObject(); + } + } +} diff --git a/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs new file mode 100644 index 00000000..3ee233d3 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs @@ -0,0 +1,138 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.MQTT +{ + using FluentAssertions; + using LEGO.AsyncAPI.Bindings; + using LEGO.AsyncAPI.Bindings.MQTT; + using LEGO.AsyncAPI.Models; + using LEGO.AsyncAPI.Readers; + using NUnit.Framework; + + public class MQTTBindings_Should + { + [Test] + public void MQTTServerBinding_FilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = +@"url: https://example.com +protocol: mqtt +bindings: + mqtt: + clientId: guest + cleanSession: true + lastWill: + topic: /last-wills + qos: 2 + message: Guest gone offline. + retain: false + keepAlive: 60 + sessionExpiryInterval: 600 + maximumPacketSize: 1200"; + + var server = new AsyncApiServer(); + server.Url = "https://example.com"; + server.Protocol = "mqtt"; + server.Bindings.Add(new MQTTServerBinding + { + ClientId = "guest", + CleanSession = true, + LastWill = new LastWill + { + Topic = "/last-wills", + QoS = 2, + Message = "Guest gone offline.", + Retain = false, + }, + KeepAlive = 60, + SessionExpiryInterval = 600, + MaximumPacketSize = 1200, + }); + + // Act + var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + // Assert + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings = BindingsCollection.MQTT; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(server); + } + + [Test] + public void MQTTOperationBinding_WithFilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = +@"bindings: + mqtt: + qos: 2 + retain: true + messageExpiryInterval: 60"; + + var operation = new AsyncApiOperation(); + operation.Bindings.Add(new MQTTOperationBinding + { + QoS = 2, + Retain = true, + MessageExpiryInterval = 60, + }); + + // Act + var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + + var settings = new AsyncApiReaderSettings(); + settings.Bindings = BindingsCollection.MQTT; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(operation); + } + + [Test] + public void MQTTMessageBinding_WithFilledObject_SerializesAndDeserializes() + { + // Arrange + var expected = +@"bindings: + mqtt: + correlationData: + type: string + format: uuid + contentType: application/json"; + + var message = new AsyncApiMessage(); + + message.Bindings.Add(new MQTTMessageBinding + { + ContentType = "application/json", + CorrelationData = new AsyncApiSchema + { + Type = SchemaType.String, + Format = "uuid", + }, + }); + + // Act + var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); + var settings = new AsyncApiReaderSettings(); + settings.Bindings = BindingsCollection.MQTT; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); + + // Assert + Assert.AreEqual(expected, actual); + binding.Should().BeEquivalentTo(message); + } + } +} From 581f6806d1b18602d92be72e4e7f302afcba4a75 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Tue, 26 Mar 2024 01:38:14 +0100 Subject: [PATCH 50/84] chore: add TryGetValue extensions for bindings (#156) --- .../Kafka/KafkaChannelBinding.cs | 1 - .../Kafka/TopicConfigurationObject.cs | 3 ++- src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs | 27 +++++++++++++++++++ .../Bindings/Kafka/KafkaBindings_Should.cs | 3 +-- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs index b6185cd4..80e07891 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs @@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Bindings.Kafka { using System; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Readers.ParseNodes; using LEGO.AsyncAPI.Writers; diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs index a3fd1e7a..cbf7c955 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/TopicConfigurationObject.cs @@ -1,9 +1,10 @@ // Copyright (c) The LEGO Group. All rights reserved. -namespace LEGO.AsyncAPI.Models.Bindings.Kafka +namespace LEGO.AsyncAPI.Bindings.Kafka { using System; using System.Collections.Generic; + using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs index d039cddf..cf382b63 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs @@ -7,6 +7,33 @@ namespace LEGO.AsyncAPI.Models using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; + public static class BindingExtensions + { + public static bool TryGetValue(this AsyncApiBindings bindings, out IServerBinding binding) + where TBinding : IServerBinding + { + return bindings.TryGetValue(Activator.CreateInstance().BindingKey, out binding); + } + + public static bool TryGetValue(this AsyncApiBindings bindings, out IChannelBinding binding) + where TBinding : IChannelBinding + { + return bindings.TryGetValue(Activator.CreateInstance().BindingKey, out binding); + } + + public static bool TryGetValue(this AsyncApiBindings bindings, out IOperationBinding binding) + where TBinding : IOperationBinding + { + return bindings.TryGetValue(Activator.CreateInstance().BindingKey, out binding); + } + + public static bool TryGetValue(this AsyncApiBindings bindings, out IMessageBinding binding) + where TBinding : IMessageBinding + { + return bindings.TryGetValue(Activator.CreateInstance().BindingKey, out binding); + } + } + public class AsyncApiBindings : Dictionary, IAsyncApiReferenceable where TBinding : IBinding { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index 26525652..842f3c1d 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -2,14 +2,13 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Kafka { + using System.Collections.Generic; using FluentAssertions; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Models; - using LEGO.AsyncAPI.Models.Bindings.Kafka; using LEGO.AsyncAPI.Readers; using NUnit.Framework; - using System.Collections.Generic; internal class KafkaBindings_Should { From ee91a56e3f086f088ca9ae686337613d589eac8d Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Tue, 26 Mar 2024 02:25:26 +0100 Subject: [PATCH 51/84] refactor: remove unused expressions (#155) --- .../Expressions/BodyExpression.cs | 68 ----------- .../Expressions/CompositeExpression.cs | 45 -------- .../Expressions/HeaderExpression.cs | 50 -------- .../Expressions/MethodExpression.cs | 20 ---- .../Expressions/PathExpression.cs | 50 -------- .../Expressions/QueryExpression.cs | 50 -------- .../Expressions/RequestExpression.cs | 34 ------ .../Expressions/ResponseExpression.cs | 34 ------ .../Expressions/RuntimeExpressions.cs | 107 ------------------ .../Expressions/SourceExpression.cs | 73 ------------ .../Expressions/StatusCodeExpression.cs | 27 ----- .../Expressions/UrlExpression.cs | 27 ----- .../Models/RuntimeExpressionAnyWrapper.cs | 71 ------------ 13 files changed, 656 deletions(-) delete mode 100644 src/LEGO.AsyncAPI/Expressions/BodyExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/CompositeExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/HeaderExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/MethodExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/PathExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/QueryExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/RequestExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/ResponseExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/RuntimeExpressions.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/SourceExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/StatusCodeExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Expressions/UrlExpression.cs delete mode 100644 src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs diff --git a/src/LEGO.AsyncAPI/Expressions/BodyExpression.cs b/src/LEGO.AsyncAPI/Expressions/BodyExpression.cs deleted file mode 100644 index 2e8fe5d7..00000000 --- a/src/LEGO.AsyncAPI/Expressions/BodyExpression.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - /// - /// Body expression. - /// - public sealed class BodyExpression : SourceExpression - { - /// - /// body string. - /// - public const string Body = "body"; - - /// - /// Prefix for a pointer. - /// - public const string PointerPrefix = "#"; - - /// - /// Initializes a new instance of the class. - /// - public BodyExpression() - : base(null) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// a JSON Pointer [RFC 6901](https://tools.ietf.org/html/rfc6901). - public BodyExpression(JsonPointer pointer) - : base(pointer?.ToString()) - { - if (pointer == null) - { - throw Error.ArgumentNull(nameof(pointer)); - } - } - - /// - /// Gets the expression string. - /// - public override string Expression - { - get - { - if (string.IsNullOrWhiteSpace(this.Value)) - { - return Body; - } - - return Body + PointerPrefix + this.Value; - } - } - - /// - /// Gets the fragment string. - /// - public string Fragment - { - get - { - return this.Value; - } - } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Expressions/CompositeExpression.cs b/src/LEGO.AsyncAPI/Expressions/CompositeExpression.cs deleted file mode 100644 index 62e1a823..00000000 --- a/src/LEGO.AsyncAPI/Expressions/CompositeExpression.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - using System.Collections.Generic; - using System.Linq; - using System.Text.RegularExpressions; - - /// - /// String literal with embedded expressions. - /// - public class CompositeExpression : RuntimeExpression - { - private readonly string template; - private Regex expressionPattern = new Regex(@"{(?\$[^}]*)"); - - /// - /// Expressions embedded into string literal. - /// - public List ContainedExpressions = new List(); - - /// - /// Create a composite expression from a string literal with an embedded expression. - /// - /// - public CompositeExpression(string expression) - { - this.template = expression; - - // Extract subexpressions and convert to RuntimeExpressions - var matches = this.expressionPattern.Matches(expression); - - foreach (var item in matches.Cast()) - { - var value = item.Groups["exp"].Captures.Cast().First().Value; - this.ContainedExpressions.Add(RuntimeExpression.Build(value)); - } - } - - /// - /// Return original string literal with embedded expression. - /// - public override string Expression => this.template; - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Expressions/HeaderExpression.cs b/src/LEGO.AsyncAPI/Expressions/HeaderExpression.cs deleted file mode 100644 index ebee9cd6..00000000 --- a/src/LEGO.AsyncAPI/Expressions/HeaderExpression.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - /// - /// Header expression, The token identifier in header is case-insensitive. - /// - public class HeaderExpression : SourceExpression - { - /// - /// header. string. - /// - public const string Header = "header."; - - /// - /// Initializes a new instance of the class. - /// - /// The token string, it's case-insensitive. - public HeaderExpression(string token) - : base(token) - { - if (string.IsNullOrWhiteSpace(token)) - { - throw Error.ArgumentNullOrWhiteSpace(nameof(token)); - } - } - - /// - /// Gets the expression string. - /// - public override string Expression - { - get - { - return Header + this.Value; - } - } - - /// - /// Gets the token string. - /// - public string Token - { - get - { - return this.Value; - } - } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs b/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs deleted file mode 100644 index 95404d68..00000000 --- a/src/LEGO.AsyncAPI/Expressions/MethodExpression.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - /// - /// Method expression. - /// - public sealed class MethodExpression : RuntimeExpression - { - /// - /// $method. string. - /// - public const string Method = "$method"; - - /// - /// Gets the expression string. - /// - public override string Expression { get; } = Method; - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Expressions/PathExpression.cs b/src/LEGO.AsyncAPI/Expressions/PathExpression.cs deleted file mode 100644 index 8b89565f..00000000 --- a/src/LEGO.AsyncAPI/Expressions/PathExpression.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - /// - /// Path expression, the name in path is case-sensitive. - /// - public sealed class PathExpression : SourceExpression - { - /// - /// path. string. - /// - public const string Path = "path."; - - /// - /// Initializes a new instance of the class. - /// - /// The name string, it's case-insensitive. - public PathExpression(string name) - : base(name) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw Error.ArgumentNullOrWhiteSpace(nameof(name)); - } - } - - /// - /// Gets the expression string. - /// - public override string Expression - { - get - { - return Path + this.Value; - } - } - - /// - /// Gets the name string. - /// - public string Name - { - get - { - return this.Value; - } - } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Expressions/QueryExpression.cs b/src/LEGO.AsyncAPI/Expressions/QueryExpression.cs deleted file mode 100644 index 7aebbb8e..00000000 --- a/src/LEGO.AsyncAPI/Expressions/QueryExpression.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - /// - /// Query expression, the name in query is case-sensitive. - /// - public sealed class QueryExpression : SourceExpression - { - /// - /// query. string. - /// - public const string Query = "query."; - - /// - /// Initializes a new instance of the class. - /// - /// The name string, it's case-insensitive. - public QueryExpression(string name) - : base(name) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw Error.ArgumentNullOrWhiteSpace(nameof(name)); - } - } - - /// - /// Gets the expression string. - /// - public override string Expression - { - get - { - return Query + this.Value; - } - } - - /// - /// Gets the name string. - /// - public string Name - { - get - { - return this.Value; - } - } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Expressions/RequestExpression.cs b/src/LEGO.AsyncAPI/Expressions/RequestExpression.cs deleted file mode 100644 index 47850bf9..00000000 --- a/src/LEGO.AsyncAPI/Expressions/RequestExpression.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - /// - /// $request. expression. - /// - public sealed class RequestExpression : RuntimeExpression - { - /// - /// $request. string. - /// - public const string Request = "$request."; - - /// - /// Initializes a new instance of the class. - /// - /// The source of the request. - public RequestExpression(SourceExpression source) - { - this.Source = source ?? throw Error.ArgumentNull(nameof(source)); - } - - /// - /// Gets the expression string. - /// - public override string Expression => Request + this.Source.Expression; - - /// - /// The expression. - /// - public SourceExpression Source { get; } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Expressions/ResponseExpression.cs b/src/LEGO.AsyncAPI/Expressions/ResponseExpression.cs deleted file mode 100644 index 8a335209..00000000 --- a/src/LEGO.AsyncAPI/Expressions/ResponseExpression.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - /// - /// $response. expression. - /// - public sealed class ResponseExpression : RuntimeExpression - { - /// - /// $response. string. - /// - public const string Response = "$response."; - - /// - /// Initializes a new instance of the class. - /// - /// The source of the response. - public ResponseExpression(SourceExpression source) - { - this.Source = source ?? throw Error.ArgumentNull(nameof(source)); - } - - /// - /// Gets the expression string. - /// - public override string Expression => Response + this.Source.Expression; - - /// - /// The expression. - /// - public SourceExpression Source { get; } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Expressions/RuntimeExpressions.cs b/src/LEGO.AsyncAPI/Expressions/RuntimeExpressions.cs deleted file mode 100644 index 2c88373a..00000000 --- a/src/LEGO.AsyncAPI/Expressions/RuntimeExpressions.cs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - using System; - using LEGO.AsyncAPI.Exceptions; - - /// - /// Base class for the AsyncApi runtime expression. - /// - public abstract class RuntimeExpression : IEquatable - { - /// - /// The dollar sign prefix for a runtime expression. - /// - public const string Prefix = "$"; - - /// - /// The expression string. - /// - public abstract string Expression { get; } - - /// - /// Build the runtime expression from input string. - /// - /// The runtime expression. - /// The built runtime expression object. - public static RuntimeExpression Build(string expression) - { - if (string.IsNullOrWhiteSpace(expression)) - { - throw Error.ArgumentNullOrWhiteSpace(nameof(expression)); - } - - if (!expression.StartsWith(Prefix)) - { - return new CompositeExpression(expression); - } - - // $url - if (expression == UrlExpression.Url) - { - return new UrlExpression(); - } - - // $method - if (expression == MethodExpression.Method) - { - return new MethodExpression(); - } - - // $statusCode - if (expression == StatusCodeExpression.StatusCode) - { - return new StatusCodeExpression(); - } - - // $request. - if (expression.StartsWith(RequestExpression.Request)) - { - var subString = expression.Substring(RequestExpression.Request.Length); - var source = SourceExpression.Build(subString); - return new RequestExpression(source); - } - - // $response. - if (expression.StartsWith(ResponseExpression.Response)) - { - var subString = expression.Substring(ResponseExpression.Response.Length); - var source = SourceExpression.Build(subString); - return new ResponseExpression(source); - } - - throw new AsyncApiException(string.Format("The runtime expression '{0}' has invalid format.", expression)); - } - - /// - /// GetHashCode implementation for IEquatable. - /// - public override int GetHashCode() - { - return this.Expression.GetHashCode(); - } - - /// - /// Equals implementation for IEquatable. - /// - public override bool Equals(object obj) - { - return this.Equals(obj as RuntimeExpression); - } - - /// - /// Equals implementation for object of the same type. - /// - public bool Equals(RuntimeExpression obj) - { - return obj != null && obj.Expression == this.Expression; - } - - /// - public override string ToString() - { - return this.Expression; - } - } -} diff --git a/src/LEGO.AsyncAPI/Expressions/SourceExpression.cs b/src/LEGO.AsyncAPI/Expressions/SourceExpression.cs deleted file mode 100644 index c5bbde98..00000000 --- a/src/LEGO.AsyncAPI/Expressions/SourceExpression.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - using LEGO.AsyncAPI.Exceptions; - - /// - /// Source expression. - /// - public abstract class SourceExpression : RuntimeExpression - { - /// - /// Initializes a new instance of the class. - /// - /// The value string. - protected SourceExpression(string value) - { - this.Value = value; - } - - /// - /// Gets the expression string. - /// - protected string Value { get; } - - /// - /// Build the source expression from input string. - /// - /// The source expression. - /// The built source expression. - public new static SourceExpression Build(string expression) - { - if (!string.IsNullOrWhiteSpace(expression)) - { - var expressions = expression.Split('.'); - if (expressions.Length == 2) - { - if (expression.StartsWith(HeaderExpression.Header)) - { - // header. - return new HeaderExpression(expressions[1]); - } - - if (expression.StartsWith(QueryExpression.Query)) - { - // query. - return new QueryExpression(expressions[1]); - } - - if (expression.StartsWith(PathExpression.Path)) - { - // path. - return new PathExpression(expressions[1]); - } - } - - // body - if (expression.StartsWith(BodyExpression.Body)) - { - var subString = expression.Substring(BodyExpression.Body.Length); - if (string.IsNullOrEmpty(subString)) - { - return new BodyExpression(); - } - - return new BodyExpression(new JsonPointer(subString)); - } - } - - throw new AsyncApiException(string.Format("The source expression '{0}' has invalid format.", expression)); - } - } -} diff --git a/src/LEGO.AsyncAPI/Expressions/StatusCodeExpression.cs b/src/LEGO.AsyncAPI/Expressions/StatusCodeExpression.cs deleted file mode 100644 index c8ef862e..00000000 --- a/src/LEGO.AsyncAPI/Expressions/StatusCodeExpression.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - /// - /// StatusCode expression. - /// - public sealed class StatusCodeExpression : RuntimeExpression - { - /// - /// $statusCode string. - /// - public const string StatusCode = "$statusCode"; - - /// - /// Gets the expression string. - /// - public override string Expression { get; } = StatusCode; - - /// - /// Private constructor. - /// - public StatusCodeExpression() - { - } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Expressions/UrlExpression.cs b/src/LEGO.AsyncAPI/Expressions/UrlExpression.cs deleted file mode 100644 index 83e01aa4..00000000 --- a/src/LEGO.AsyncAPI/Expressions/UrlExpression.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Expressions -{ - /// - /// Url expression. - /// - public sealed class UrlExpression : RuntimeExpression - { - /// - /// $url string. - /// - public const string Url = "$url"; - - /// - /// Gets the expression string. - /// - public override string Expression { get; } = Url; - - /// - /// Private constructor. - /// - public UrlExpression() - { - } - } -} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs b/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs deleted file mode 100644 index 130746ea..00000000 --- a/src/LEGO.AsyncAPI/Models/RuntimeExpressionAnyWrapper.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Models -{ - using LEGO.AsyncAPI.Expressions; - using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; - - /// - /// The wrapper either for or . - /// - public class RuntimeExpressionAnyWrapper : IAsyncApiElement - { - private AsyncApiAny any; - private RuntimeExpression expression; - - /// - /// Gets/Sets the . - /// - public AsyncApiAny Any - { - get - { - return this.any; - } - - set - { - this.expression = null; - this.any = value; - } - } - - /// - /// Gets/Set the . - /// - public RuntimeExpression Expression - { - get - { - return this.expression; - } - - set - { - this.any = null; - this.expression = value; - } - } - - /// - /// Write . - /// - public void WriteValue(IAsyncApiWriter writer) - { - if (writer == null) - { - throw Error.ArgumentNull(nameof(writer)); - } - - if (this.any != null) - { - writer.WriteAny(this.any); - } - else if (this.expression != null) - { - writer.WriteValue(this.expression.Expression); - } - } - } -} \ No newline at end of file From 18f2cb3d4b5b610cb410737f17765c16c9511eaa Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Tue, 26 Mar 2024 02:32:07 +0100 Subject: [PATCH 52/84] test: remove stylecop from tests (#158) --- test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index f10a92bc..5580582b 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -20,10 +20,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - From 61695545b79e73ecf2ee1ea91c4c6c6cfe4719f8 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Tue, 26 Mar 2024 03:49:19 +0100 Subject: [PATCH 53/84] chore: fix warnings (#157) --- .../AMQP/AMQPChannelBinding.cs | 4 +- .../AMQP/AMQPMessageBinding.cs | 2 +- .../Http/HttpMessageBinding.cs | 4 +- .../Http/HttpOperationBinding.cs | 2 +- .../Kafka/KafkaChannelBinding.cs | 4 +- .../Kafka/KafkaMessageBinding.cs | 6 +- .../Kafka/KafkaOperationBinding.cs | 2 +- .../Kafka/KafkaServerBinding.cs | 2 +- .../LEGO.AsyncAPI.Bindings.csproj | 4 +- .../MQTT/MQTTMessageBinding.cs | 2 +- .../MQTT/MQTTServerBinding.cs | 4 +- .../OperationBinding{T}.cs | 2 +- .../Pulsar/PulsarChannelBinding.cs | 4 +- .../Pulsar/PulsarServerBinding.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs | 6 +- .../Sns/DeliveryPolicy.cs | 2 + src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs | 2 + src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs | 2 + src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs | 2 + .../Sns/RedrivePolicy.cs | 2 + .../Sns/SnsChannelBinding.cs | 2 + .../Sns/SnsOperationBinding.cs | 4 +- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 3 +- src/LEGO.AsyncAPI.Bindings/Sqs/Identifier.cs | 2 + src/LEGO.AsyncAPI.Bindings/Sqs/Policy.cs | 2 + src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs | 4 +- .../Sqs/RedrivePolicy.cs | 2 + .../Sqs/SqsChannelBinding.cs | 2 + .../Sqs/SqsOperationBinding.cs | 2 + src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs | 3 +- .../StringOrStringList.cs | 16 +- .../WebSockets/WebSocketsChannelBinding.cs | 4 +- .../AsyncApiJsonDocumentReader.cs | 43 +- .../BindingDeserializer.cs | 2 +- .../Interface/IAsyncApiReader.cs | 3 +- .../Interface/IAsyncApiVersionService.cs | 3 +- .../LEGO.AsyncAPI.Readers.csproj | 4 +- .../ParseNodes/MapNode.cs | 2 +- src/LEGO.AsyncAPI.Readers/ParsingContext.cs | 24 +- .../V2/AsyncApiChannelDeserializer.cs | 4 +- .../V2/AsyncApiComponentsDeserializer.cs | 4 +- .../V2/AsyncApiContactDeserializer.cs | 4 +- .../V2/AsyncApiCorrelationIdDeserializer.cs | 4 +- .../V2/AsyncApiDocumentDeserializer.cs | 4 +- .../V2/AsyncApiExampleDeserializer.cs | 4 +- .../V2/AsyncApiExternalDocsDeserializer.cs | 4 +- .../V2/AsyncApiInfoDeserializer.cs | 4 +- .../V2/AsyncApiLicenseDeserializer.cs | 4 +- .../V2/AsyncApiMessageDeserializer.cs | 4 +- .../V2/AsyncApiMessageTraitDeserializer.cs | 4 +- .../V2/AsyncApiOAuthFlowDeserializer.cs | 4 +- .../V2/AsyncApiOAuthFlowsDeserializer.cs | 4 +- .../V2/AsyncApiOperationDeserializer.cs | 4 +- .../V2/AsyncApiOperationTraitDeserializer.cs | 5 +- .../V2/AsyncApiParameterDeserializer.cs | 4 +- .../V2/AsyncApiSchemaDeserializer.cs | 9 +- .../V2/AsyncApiSecuritySchemeDeserializer.cs | 4 +- .../V2/AsyncApiServerDeserializer.cs | 4 +- .../V2/AsyncApiServerVariableDeserializer.cs | 4 +- .../V2/AsyncApiTagDeserializer.cs | 4 +- .../V2/AsyncApiV2VersionService.cs | 4 +- .../V2/ExtensionHelpers.cs | 5 +- src/LEGO.AsyncAPI.Readers/YamlConverter.cs | 6 +- src/LEGO.AsyncAPI/EnumExtensions.cs | 3 +- src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj | 4 +- src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs | 1 - .../Models/Any/AsyncAPIObject.cs | 1 - src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs | 1 - src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs | 3 +- .../Models/AsyncApiSerializableExtensions.cs | 6 +- .../Models/Interfaces/IBinding.cs | 4 +- .../Models/JsonSchema/FalseApiSchema.cs | 2 + .../Models/SecuritySchemeType.cs | 20 +- .../Services/AsyncApiReferenceResolver.cs | 14 +- src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs | 2 +- .../Validation/Rules/AsyncApiContactRules.cs | 6 +- .../Rules/AsyncApiCorrelationIdRules.cs | 1 - .../Rules/AsyncApiExtensionRules.cs | 1 + .../AsyncApiExternalDocumentationRules.cs | 2 - .../Validation/Rules/AsyncApiInfoRules.cs | 1 - .../Validation/Rules/AsyncApiLicenseRules.cs | 2 - .../Rules/AsyncApiOAuthFlowRules.cs | 3 - .../Validation/Rules/AsyncApiTagRules.cs | 1 - .../Validation/ValidationRule{T}.cs | 3 +- .../Writers/AsyncApiWriterAnyExtensions.cs | 1 + .../Writers/AsyncApiWriterExtensions.cs | 5 +- .../AsyncApiDocumentV2Tests.cs | 426 +++++++++--------- .../AsyncApiLicenseTests.cs | 6 +- .../AsyncApiReaderTests.cs | 178 ++++---- .../Bindings/AMQP/AMQPBindings_Should.cs | 2 +- .../Bindings/CustomBinding_Should.cs | 8 +- .../Bindings/Sns/SnsBindings_Should.cs | 119 ++--- .../Bindings/Sqs/SqsBindings_should.cs | 129 +++--- .../Bindings/StringOrStringList_Should.cs | 36 +- .../Models/AsyncApiAnyTests.cs | 9 +- .../Models/AsyncApiChannel_Should.cs | 1 - .../Models/AsyncApiMessage_Should.cs | 192 ++++---- .../Models/AsyncApiReference_Should.cs | 1 - .../Models/AsyncApiSchema_Should.cs | 2 +- .../Models/AsyncApiServer_Should.cs | 4 +- 100 files changed, 768 insertions(+), 724 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPChannelBinding.cs index f77dc37f..ebfed688 100644 --- a/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPChannelBinding.cs @@ -29,7 +29,7 @@ public class AMQPChannelBinding : ChannelBinding public override string BindingKey => "amqp"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "is", (a, n) => { a.Is = n.GetScalarValue().GetEnumFromDisplayName(); } }, @@ -37,7 +37,7 @@ public class AMQPChannelBinding : ChannelBinding { "queue", (a, n) => { a.Queue = n.ParseMap(QueueFixedFields); } }, }; - private static FixedFieldMap ExchangeFixedFields = new () + private static FixedFieldMap ExchangeFixedFields = new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, { "durable", (a, n) => { a.Durable = n.GetBooleanValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPMessageBinding.cs index 17642020..73a68049 100644 --- a/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPMessageBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPMessageBinding.cs @@ -41,7 +41,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) public override string BindingKey => "amqp"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "contentEncoding", (a, n) => { a.ContentEncoding = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs index ea9f6f95..7a5731ef 100644 --- a/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs @@ -13,7 +13,6 @@ namespace LEGO.AsyncAPI.Bindings.Http /// public class HttpMessageBinding : MessageBinding { - /// /// A Schema object containing the definitions for HTTP-specific headers. This schema MUST be of type object and have a properties key. /// @@ -40,11 +39,10 @@ public override void SerializeProperties(IAsyncApiWriter writer) public override string BindingKey => "http"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, }; - } } diff --git a/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs index d41bbafb..f70858c2 100644 --- a/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs @@ -58,7 +58,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteEndObject(); } - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs index 80e07891..90b7c6ff 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaChannelBinding.cs @@ -34,7 +34,7 @@ public class KafkaChannelBinding : ChannelBinding public override string BindingKey => "kafka"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, @@ -43,7 +43,7 @@ public class KafkaChannelBinding : ChannelBinding { "replicas", (a, n) => { a.Replicas = n.GetIntegerValue(); } }, }; - private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new () + private static FixedFieldMap kafkaChannelTopicConfigurationObjectFixedFields = new() { { "cleanup.policy", (a, n) => { a.CleanupPolicy = n.CreateSimpleList(s => s.GetScalarValue()); } }, { "retention.ms", (a, n) => { a.RetentionMilliseconds = n.GetLongValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs index 85062422..2f665560 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs @@ -36,7 +36,7 @@ public class KafkaMessageBinding : MessageBinding /// /// The version of this binding. If omitted, "latest" MUST be assumed. /// - + public override void SerializeProperties(IAsyncApiWriter writer) { if (writer is null) @@ -62,9 +62,9 @@ public override void SerializeProperties(IAsyncApiWriter writer) /// The writer. /// writer. - public override string BindingKey => "kafka"; + public override string BindingKey => "kafka"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "key", (a, n) => { a.Key = JsonSchemaDeserializer.LoadSchema(n); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs index 5ae7ba8f..53db7ae0 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs @@ -25,7 +25,7 @@ public class KafkaOperationBinding : OperationBinding public override string BindingKey => "kafka"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "groupId", (a, n) => { a.GroupId = JsonSchemaDeserializer.LoadSchema(n); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs index 0ee3bc5d..c679d46b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaServerBinding.cs @@ -24,7 +24,7 @@ public class KafkaServerBinding : ServerBinding public override string BindingKey => "kafka"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "schemaRegistryUrl", (a, n) => { a.SchemaRegistryUrl = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj index 751ea5c6..dc76c2ba 100644 --- a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj +++ b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj @@ -1,5 +1,5 @@  - + AsyncAPI.NET Bindings AsyncAPI.NET.Bindings @@ -15,7 +15,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs index fdee2114..b48e5ae9 100644 --- a/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs @@ -54,7 +54,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) public override string BindingKey => "mqtt"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "payloadFormatIndicator", (a, n) => { a.PayloadFormatIndicator = n.GetIntegerValueOrDefault(); } }, { "correlationData", (a, n) => { a.CorrelationData = JsonSchemaDeserializer.LoadSchema(n); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTServerBinding.cs index c37b12e3..03b88b23 100644 --- a/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTServerBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTServerBinding.cs @@ -49,7 +49,7 @@ public class MQTTServerBinding : ServerBinding public override string BindingKey => "mqtt"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "clientId", (a, n) => { a.ClientId = n.GetScalarValue(); } }, @@ -60,7 +60,7 @@ public class MQTTServerBinding : ServerBinding { "maximumPacketSize", (a, n) => { a.MaximumPacketSize = n.GetIntegerValueOrDefault(); } }, }; - private static FixedFieldMap LastWillFixedFields = new () + private static FixedFieldMap LastWillFixedFields = new() { { "topic", (a, n) => { a.Topic = n.GetScalarValue(); } }, { "qos", (a, n) => { a.QoS = (uint?)n.GetIntegerValueOrDefault(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs b/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs index 0e4216ed..626250ee 100644 --- a/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs +++ b/src/LEGO.AsyncAPI.Bindings/OperationBinding{T}.cs @@ -6,7 +6,7 @@ namespace LEGO.AsyncAPI.Bindings using LEGO.AsyncAPI.Readers; using LEGO.AsyncAPI.Readers.ParseNodes; - public abstract class OperationBinding : Binding , IOperationBinding + public abstract class OperationBinding : Binding, IOperationBinding where T : IOperationBinding, new() { protected abstract FixedFieldMap FixedFieldMap { get; } diff --git a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs index bc30f9a9..c673a8e8 100644 --- a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs @@ -68,7 +68,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteEndObject(); } - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "namespace", (a, n) => { a.Namespace = n.GetScalarValue(); } }, @@ -80,7 +80,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) { "deduplication", (a, n) => { a.Deduplication = n.GetBooleanValue(); } }, }; - private FixedFieldMap pulsarServerBindingRetentionFixedFields = new () + private FixedFieldMap pulsarServerBindingRetentionFixedFields = new() { { "time", (a, n) => { a.Time = n.GetIntegerValue(); } }, { "size", (a, n) => { a.Size = n.GetIntegerValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs index 1a102d71..e767443d 100644 --- a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarServerBinding.cs @@ -19,7 +19,7 @@ public class PulsarServerBinding : ServerBinding public override string BindingKey => "pulsar"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "tenant", (a, n) => { a.Tenant = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs index 262521d0..688c186f 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Consumer.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sns { using System; @@ -24,7 +26,7 @@ public class Consumer : IAsyncApiExtensible /// Depending on the FilterPolicyScope, a map of either a message attribute or message body to an array of possible matches. The match may be a simple string for an exact match, but it may also be an object that represents a constraint and values for that constraint. /// public AsyncApiAny FilterPolicy { get; set; } - + /// /// Determines whether the FilterPolicy applies to MessageAttributes or MessageBody. /// @@ -85,7 +87,7 @@ public enum Protocol [Display("lambda")] Lambda, [Display("firehose")] Firehose, } - + public enum FilterPolicyScope { [Display("MessageAttributes")] MessageAttributes, diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs index f9421ca9..9ed78b19 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/DeliveryPolicy.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/Identifier.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs index 0e6466b0..960c1c54 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Identifier.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/Ordering.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs index f80c953b..23f69f52 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.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/Policy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs index eefc6171..a4232e5e 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Policy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Policy.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/RedrivePolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs index 4e5e6340..01e6d7e2 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/RedrivePolicy.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/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 65043449..13676168 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.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 ffab9afb..350b7a59 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sns { using System; @@ -43,7 +45,7 @@ public class SnsOperationBinding : OperationBinding { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, }; - private FixedFieldMap consumerFixedFields => new () + private FixedFieldMap consumerFixedFields => new() { { "protocol", (a, n) => { a.Protocol = n.GetScalarValue().GetEnumFromDisplayName(); } }, { "endpoint", (a, n) => { a.Endpoint = n.ParseMapWithExtensions(this.identifierFixFields); } }, diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 38e0b1ee..7f3771f0 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sns { using System; @@ -8,7 +10,6 @@ namespace LEGO.AsyncAPI.Bindings.Sns public class Statement : IAsyncApiExtensible { - public Effect Effect { get; set; } /// diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Identifier.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Identifier.cs index d7d95dcc..6e7aff23 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Identifier.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Identifier.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/Policy.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Policy.cs index a989c239..2fd9f68b 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Policy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Policy.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/Queue.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs index 4eec17ef..33166af5 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sqs { using System; @@ -26,7 +28,7 @@ public class Queue : IAsyncApiExtensible /// /// Specifies whether the FIFO queue throughput quota applies to the entire queue or per message group. Valid values are perQueue (default) and perMessageGroupId. /// - public FifoThroughputLimit? FifoThroughputLimit { get; set; } + public FifoThroughputLimit? FifoThroughputLimit { get; set; } /// /// The number of seconds to delay before a message sent to the queue can be received. used to create a delay queue. diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs index 4222ee45..923d668c 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.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/SqsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs index b64bed31..6f98da99 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.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/SqsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs index de2372f1..d8eb43dd 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.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 508e4a33..9518d2d4 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Bindings.Sqs { using System; @@ -8,7 +10,6 @@ namespace LEGO.AsyncAPI.Bindings.Sqs public class Statement : IAsyncApiExtensible { - public Effect Effect { get; set; } /// diff --git a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs index c2f323cc..6be69094 100644 --- a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs +++ b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs @@ -18,7 +18,7 @@ public StringOrStringList(AsyncApiAny value) { JsonArray array => IsValidStringList(array) ? new AsyncApiAny(array) : throw new ArgumentException($"{nameof(StringOrStringList)} value should only contain string items."), JsonValue jValue => IsString(jValue) ? new AsyncApiAny(jValue) : throw new ArgumentException($"{nameof(StringOrStringList)} should be a string value or a string list."), - _ => throw new ArgumentException($"{nameof(StringOrStringList)} should be a string value or a string list.") + _ => throw new ArgumentException($"{nameof(StringOrStringList)} should be a string value or a string list."), }; } @@ -31,15 +31,15 @@ public static StringOrStringList Parse(ParseNode node) case ValueNode: return new StringOrStringList(new AsyncApiAny(node.GetScalarValue())); case ListNode: - { - var jsonArray = new JsonArray(); - foreach (var item in node as ListNode) { - jsonArray.Add(item.GetScalarValue()); - } + var jsonArray = new JsonArray(); + foreach (var item in node as ListNode) + { + jsonArray.Add(item.GetScalarValue()); + } - return new StringOrStringList(new AsyncApiAny(jsonArray)); - } + return new StringOrStringList(new AsyncApiAny(jsonArray)); + } default: throw new ArgumentException($"An error occured while parsing a {nameof(StringOrStringList)} node. " + diff --git a/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs index a3fb6366..c87393bb 100644 --- a/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs @@ -25,9 +25,9 @@ public class WebSocketsChannelBinding : ChannelBinding /// public AsyncApiSchema Headers { get; set; } - public override string BindingKey => "websockets"; + public override string BindingKey => "websockets"; - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } }, { "method", (a, n) => { a.Method = n.GetScalarValue(); } }, diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs index fbce6668..aca50c17 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs @@ -119,26 +119,6 @@ public async Task ReadAsync(JsonNode input, CancellationToken cancel }; } - private void ResolveReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument document) - { - var errors = new List(); - - // Resolve References if requested - switch (this.settings.ReferenceResolution) - { - case ReferenceResolutionSetting.ResolveReferences: - errors.AddRange(document.ResolveReferences()); - break; - case ReferenceResolutionSetting.DoNotResolveReferences: - break; - } - - foreach (var item in errors) - { - diagnostic.Errors.Add(item); - } - } - /// /// Reads the stream input and parses the fragment of an AsyncApi description into an AsyncApi Element. /// @@ -182,5 +162,26 @@ public T ReadFragment(JsonNode input, AsyncApiVersion version, out AsyncApiDi return (T)element; } + + private void ResolveReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument document) + { + var errors = new List(); + + // Resolve References if requested + switch (this.settings.ReferenceResolution) + { + case ReferenceResolutionSetting.ResolveReferences: + errors.AddRange(document.ResolveReferences()); + break; + + case ReferenceResolutionSetting.DoNotResolveReferences: + break; + } + + foreach (var item in errors) + { + diagnostic.Errors.Add(item); + } + } } -} +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs index 441e0935..5744985d 100644 --- a/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/BindingDeserializer.cs @@ -22,7 +22,7 @@ public static T LoadBinding(string nodeName, ParseNode node, FixedFieldMap private static PatternFieldMap BindingPatternExtensionFields() where T : IBinding, new() { - return new () + return new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiReader.cs b/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiReader.cs index 6938ea88..ef2fb45f 100644 --- a/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiReader.cs +++ b/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiReader.cs @@ -4,7 +4,8 @@ namespace LEGO.AsyncAPI.Readers.Interface { using LEGO.AsyncAPI.Models; - public interface IAsyncApiReader where TDiagnostic : IDiagnostic + public interface IAsyncApiReader + where TDiagnostic : IDiagnostic { AsyncApiDocument Read(TInput input, out TDiagnostic diagnostic); } diff --git a/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiVersionService.cs b/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiVersionService.cs index 61922eef..c2345ac1 100644 --- a/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiVersionService.cs +++ b/src/LEGO.AsyncAPI.Readers/Interface/IAsyncApiVersionService.cs @@ -10,7 +10,8 @@ internal interface IAsyncApiVersionService { AsyncApiReference ConvertToAsyncApiReference(string reference, ReferenceType? type); - T LoadElement(ParseNode node) where T : IAsyncApiElement; + T LoadElement(ParseNode node) + where T : IAsyncApiElement; AsyncApiDocument LoadDocument(RootNode rootNode); } diff --git a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj index b0164afd..c47530c0 100644 --- a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj +++ b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj @@ -1,5 +1,5 @@  - + disable AsyncAPI.NET Readers for JSON and YAML documents @@ -17,7 +17,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 508a6cdd..a9bf1c48 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs @@ -201,7 +201,7 @@ public string GetReferencePointer() public string GetScalarValue(ValueNode key) { - var scalarNode = this.node[key.GetScalarValue()] is JsonValue jsonValue + var scalarNode = this.node[key.GetScalarValue()] is JsonValue jsonValue ? jsonValue : throw new AsyncApiReaderException($"Expected scalar value while parsing {key.GetScalarValue()}", this.Context); diff --git a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs index 91f35d4a..812a2fe2 100644 --- a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs +++ b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs @@ -15,7 +15,7 @@ namespace LEGO.AsyncAPI.Readers public class ParsingContext { - private readonly Stack currentLocation = new (); + private readonly Stack currentLocation = new(); internal Dictionary> ExtensionParsers { @@ -23,19 +23,19 @@ internal Dictionary> ExtensionPars set; } - = new (); + = new(); - internal Dictionary> ServerBindingParsers { get; set; } = new (); + internal Dictionary> ServerBindingParsers { get; set; } = new(); - internal Dictionary> ChannelBindingParsers { get; set; } - - internal Dictionary> OperationBindingParsers { get; set; } = new (); - - internal Dictionary> MessageBindingParsers { get; set; } = new (); + internal Dictionary> ChannelBindingParsers { get; set; } = new(); + + internal Dictionary> OperationBindingParsers { get; set; } = new(); + + internal Dictionary> MessageBindingParsers { get; set; } = new(); internal RootNode RootNode { get; set; } - internal List Tags { get; private set; } = new (); + internal List Tags { get; private set; } = new(); public AsyncApiDiagnostic Diagnostic { get; } @@ -67,7 +67,8 @@ internal AsyncApiDocument Parse(JsonNode jsonNode) return doc; } - internal T ParseFragment(JsonNode jsonNode, AsyncApiVersion version) where T : IAsyncApiElement + internal T ParseFragment(JsonNode jsonNode, AsyncApiVersion version) + where T : IAsyncApiElement { var node = ParseNode.Create(this, jsonNode); @@ -99,7 +100,8 @@ public void EndObject() public string GetLocation() { - return "#/" + string.Join("/", + return "#/" + string.Join( + "/", this.currentLocation.Reverse().Select(s => s.Replace("~", "~0").Replace("/", "~1")).ToArray()); } diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs index 8211da1d..cc74e0c0 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static readonly FixedFieldMap ChannelFixedFields = new () + private static readonly FixedFieldMap ChannelFixedFields = new() { { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, { "servers", (a, n) => { a.Servers = n.CreateSimpleList(s => s.GetScalarValue()); } }, @@ -19,7 +19,7 @@ internal static partial class AsyncApiV2Deserializer }; private static readonly PatternFieldMap ChannelPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs index cda083de..3b63db28 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap componentsFixedFields = new () + private static FixedFieldMap componentsFixedFields = new() { { "schemas", (a, n) => a.Schemas = n.CreateMapWithReference(ReferenceType.Schema, JsonSchemaDeserializer.LoadSchema) }, { "servers", (a, n) => a.Servers = n.CreateMapWithReference(ReferenceType.Server, LoadServer) }, @@ -26,7 +26,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap componentsPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiContactDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiContactDeserializer.cs index 09672719..1a02ffaa 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiContactDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiContactDeserializer.cs @@ -13,14 +13,14 @@ namespace LEGO.AsyncAPI.Readers /// internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap contactFixedFields = new () + private static FixedFieldMap contactFixedFields = new() { { "name", (o, n) => { o.Name = n.GetScalarValue(); } }, { "email", (o, n) => { o.Email = n.GetScalarValue(); } }, { "url", (o, n) => { o.Url = new Uri(n.GetScalarValue(), UriKind.RelativeOrAbsolute); } }, }; - private static PatternFieldMap contactPatternFields = new () + private static PatternFieldMap contactPatternFields = new() { { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs index aaf30093..0b8e88f0 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs @@ -13,14 +13,14 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap correlationIdFixedFileds = - new () + new() { { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, { "location", (a, n) => { a.Location = n.GetScalarValue(); } }, }; private static readonly PatternFieldMap correlationIdPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDocumentDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDocumentDeserializer.cs index 9daf6418..e7f0289e 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDocumentDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiDocumentDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap asyncApiFixedFields = new () + private static FixedFieldMap asyncApiFixedFields = new() { { "asyncapi", (a, n) => { a.Asyncapi = "2.6.0"; } }, { "id", (a, n) => a.Id = n.GetScalarValue() }, @@ -21,7 +21,7 @@ internal static partial class AsyncApiV2Deserializer { "externalDocs", (a, n) => a.ExternalDocs = LoadExternalDocs(n) }, }; - private static PatternFieldMap asyncApiPatternFields = new () + private static PatternFieldMap asyncApiPatternFields = new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExampleDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExampleDeserializer.cs index b8f80257..46667a4b 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExampleDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExampleDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap exampleFixedFields = new () + private static FixedFieldMap exampleFixedFields = new() { { "headers", (a, n) => { a.Headers = n.CreateMap(LoadAny); } }, { "payload", (a, n) => { a.Payload = n.CreateAny(); } }, @@ -17,7 +17,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap examplePatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExternalDocsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExternalDocsDeserializer.cs index 824fa26a..2c5e07b6 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExternalDocsDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiExternalDocsDeserializer.cs @@ -9,14 +9,14 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap externalDocumentationFixedFields = new () + private static FixedFieldMap externalDocumentationFixedFields = new() { { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, { "url", (a, n) => { a.Url = new Uri(n.GetScalarValue()); } }, }; private static PatternFieldMap externalDocumentationPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiInfoDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiInfoDeserializer.cs index c9c745dd..60a359f5 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiInfoDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiInfoDeserializer.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap infoFixedFields = new () + private static FixedFieldMap infoFixedFields = new() { { "title", (a, n) => { a.Title = n.GetScalarValue(); } }, { "version", (a, n) => { a.Version = n.GetScalarValue(); } }, @@ -20,7 +20,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap infoPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiLicenseDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiLicenseDeserializer.cs index 28c2fb8d..630d2efc 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiLicenseDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiLicenseDeserializer.cs @@ -9,14 +9,14 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap licenseFixedFields = new () + private static FixedFieldMap licenseFixedFields = new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, { "url", (a, n) => { a.Url = new Uri(n.GetScalarValue()); } }, }; private static PatternFieldMap licensePatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs index 4c65a019..4c16bf22 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs @@ -15,7 +15,7 @@ namespace LEGO.AsyncAPI.Readers /// internal static partial class AsyncApiV2Deserializer { - private static readonly FixedFieldMap messageFixedFields = new () + private static readonly FixedFieldMap messageFixedFields = new() { { "messageId", (a, n) => { a.MessageId = n.GetScalarValue(); } @@ -83,7 +83,7 @@ private static string LoadSchemaFormat(string schemaFormat) return schemaFormat; } - private static readonly PatternFieldMap messagePatternFields = new () + private static readonly PatternFieldMap messagePatternFields = new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs index 67de4bcd..eca8af64 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap messageTraitFixedFields = new () + private static FixedFieldMap messageTraitFixedFields = new() { { "messageId", (a, n) => { a.MessageId = n.GetScalarValue(); } }, { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } }, @@ -26,7 +26,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap messageTraitPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowDeserializer.cs index dfeb3d83..cddb8126 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowDeserializer.cs @@ -14,7 +14,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap oAuthFlowFixedFields = - new () + new() { { "authorizationUrl", (o, n) => @@ -38,7 +38,7 @@ internal static partial class AsyncApiV2Deserializer }; private static readonly PatternFieldMap oAuthFlowPatternFields = - new () + new() { { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p,n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowsDeserializer.cs index 8f3a1ce3..dba5ab09 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowsDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOAuthFlowsDeserializer.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap oAuthFlowsFixedFileds = - new () + new() { { "implicit", (a, n) => a.Implicit = LoadOAuthFlow(n) }, { "password", (a, n) => a.Password = LoadOAuthFlow(n) }, @@ -22,7 +22,7 @@ internal static partial class AsyncApiV2Deserializer }; private static readonly PatternFieldMap oAuthFlowsPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationDeserializer.cs index 723a20fa..8a9a0505 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationDeserializer.cs @@ -10,7 +10,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap operationFixedFields = - new () + new() { { "operationId", (a, n) => { a.OperationId = n.GetScalarValue(); } @@ -54,7 +54,7 @@ private static IList LoadMessages(ParseNode n) } private static readonly PatternFieldMap operationPatternFields = - new () + new() { { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs index 1cb1629f..561456e9 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap operationTraitFixedFields = new () + private static FixedFieldMap operationTraitFixedFields = new() { { "operationId", (a, n) => { a.OperationId = n.GetScalarValue(); } }, { "summary", (a, n) => { a.Summary = n.GetScalarValue(); } }, @@ -19,7 +19,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap operationTraitPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; @@ -33,6 +33,7 @@ public static AsyncApiOperationTrait LoadOperationTrait(ParseNode node) { return mapNode.GetReferencedObject(ReferenceType.OperationTrait, pointer); } + var operationTrait = new AsyncApiOperationTrait(); ParseMap(mapNode, operationTrait, operationTraitFixedFields, operationTraitPatternFields); diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs index e9ef5e51..bff810f1 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap parameterFixedFields = new () + private static FixedFieldMap parameterFixedFields = new() { { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, { "schema", (a, n) => { a.Schema = JsonSchemaDeserializer.LoadSchema(n); } }, @@ -16,7 +16,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap parameterPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index 2131eb7e..04c51f52 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -11,7 +11,7 @@ namespace LEGO.AsyncAPI.Readers public class JsonSchemaDeserializer { - private static readonly FixedFieldMap schemaFixedFields = new () + private static readonly FixedFieldMap schemaFixedFields = new() { { "title", (a, n) => { a.Title = n.GetScalarValue(); } @@ -23,6 +23,7 @@ public class JsonSchemaDeserializer { a.Type = n.GetScalarValue().GetEnumFromDisplayName(); } + if (n.GetType() == typeof(ListNode)) { SchemaType? initialValue = null; @@ -209,12 +210,12 @@ public class JsonSchemaDeserializer }; private static readonly PatternFieldMap schemaPatternFields = - new () + new() { { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) }, }; - private static readonly AnyFieldMap schemaAnyFields = new () + private static readonly AnyFieldMap schemaAnyFields = new() { { AsyncApiConstants.Default, @@ -225,7 +226,7 @@ public class JsonSchemaDeserializer }, }; - private static readonly AnyListFieldMap schemaAnyListFields = new () + private static readonly AnyListFieldMap schemaAnyListFields = new() { { AsyncApiConstants.Enum, diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs index 10ce26d7..92708116 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs @@ -15,7 +15,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap securitySchemeFixedFields = - new () + new() { { "type", (o, n) => { o.Type = n.GetScalarValue().GetEnumFromDisplayName(); } @@ -45,7 +45,7 @@ internal static partial class AsyncApiV2Deserializer }; private static readonly PatternFieldMap securitySchemePatternFields = - new () + new() { { s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs index 2dffe850..6eae00af 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs @@ -12,7 +12,7 @@ namespace LEGO.AsyncAPI.Readers /// internal static partial class AsyncApiV2Deserializer { - private static readonly FixedFieldMap serverFixedFields = new () + private static readonly FixedFieldMap serverFixedFields = new() { { "url", (a, n) => { a.Url = n.GetScalarValue(); } @@ -41,7 +41,7 @@ internal static partial class AsyncApiV2Deserializer }; private static readonly PatternFieldMap serverPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs index 4a4c0db5..b773d255 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { private static readonly FixedFieldMap serverVariableFixedFields = - new () + new() { { "enum", (a, n) => { a.Enum = n.CreateSimpleList(s => s.GetScalarValue()); } @@ -30,7 +30,7 @@ internal static partial class AsyncApiV2Deserializer }; private static readonly PatternFieldMap serverVariablePatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiTagDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiTagDeserializer.cs index 9f38c01c..cc589fa2 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiTagDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiTagDeserializer.cs @@ -8,7 +8,7 @@ namespace LEGO.AsyncAPI.Readers internal static partial class AsyncApiV2Deserializer { - private static FixedFieldMap tagsFixedFields = new () + private static FixedFieldMap tagsFixedFields = new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, { "description", (a, n) => { a.Description = n.GetScalarValue(); } }, @@ -16,7 +16,7 @@ internal static partial class AsyncApiV2Deserializer }; private static PatternFieldMap tagsPatternFields = - new () + new() { { s => s.StartsWith("x-"), (a, p, n) => a.AddExtension(p, LoadExtension(p, n)) }, }; diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs index 3c99409c..10edd3ba 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs @@ -81,7 +81,6 @@ public AsyncApiReference ConvertToAsyncApiReference( asyncApiReference.ExternalResource = segments[0]; return asyncApiReference; - } else if (segments.Length == 2) { @@ -139,7 +138,8 @@ public AsyncApiDocument LoadDocument(RootNode rootNode) return AsyncApiV2Deserializer.LoadAsyncApi(rootNode); } - public T LoadElement(ParseNode node) where T : IAsyncApiElement + public T LoadElement(ParseNode node) + where T : IAsyncApiElement { return (T)this.loaders[typeof(T)](node); } diff --git a/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs b/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs index a134c46d..60689b31 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/ExtensionHelpers.cs @@ -10,9 +10,10 @@ namespace LEGO.AsyncAPI.Readers public static class ExtensionHelpers { - public static PatternFieldMap GetExtensionsFieldMap() where T : IAsyncApiExtensible + public static PatternFieldMap GetExtensionsFieldMap() + where T : IAsyncApiExtensible { - return new () + return new() { { s => s.StartsWith("x-"), diff --git a/src/LEGO.AsyncAPI.Readers/YamlConverter.cs b/src/LEGO.AsyncAPI.Readers/YamlConverter.cs index e3dfcd43..295fb573 100644 --- a/src/LEGO.AsyncAPI.Readers/YamlConverter.cs +++ b/src/LEGO.AsyncAPI.Readers/YamlConverter.cs @@ -1,4 +1,6 @@ -namespace LEGO.AsyncAPI.Readers +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Readers { using System; using System.Globalization; @@ -43,7 +45,7 @@ public static JsonNode ToJsonNode(this YamlNode yaml) YamlMappingNode map => map.ToJsonObject(), YamlSequenceNode seq => seq.ToJsonArray(), YamlScalarNode scalar => scalar.ToJsonValue(), - _ => throw new NotSupportedException("This yaml isn't convertible to JSON") + _ => throw new NotSupportedException("This yaml isn't convertible to JSON"), }; } diff --git a/src/LEGO.AsyncAPI/EnumExtensions.cs b/src/LEGO.AsyncAPI/EnumExtensions.cs index 56a46874..3cca149d 100644 --- a/src/LEGO.AsyncAPI/EnumExtensions.cs +++ b/src/LEGO.AsyncAPI/EnumExtensions.cs @@ -18,7 +18,8 @@ public static class EnumExtensions /// /// The attribute of the specified type or null. /// - public static T GetAttributeOfType(this Enum enumValue) where T : Attribute + public static T GetAttributeOfType(this Enum enumValue) + where T : Attribute { var type = enumValue.GetType(); var memInfo = type.GetMember(enumValue.ToString()).First(); diff --git a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj index c2dd1598..c26d0242 100644 --- a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj +++ b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj @@ -1,5 +1,5 @@  - + AsyncAPI.NET models AsyncAPI.NET @@ -15,7 +15,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs index 01970b19..d40208bd 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIArray.cs @@ -11,7 +11,6 @@ namespace LEGO.AsyncAPI.Models [Obsolete("Please use AsyncApiAny instead")] public class AsyncApiArray : Collection, IAsyncApiExtension, IAsyncApiElement { - public static explicit operator AsyncApiArray(AsyncApiAny any) { var a = new AsyncApiArray(); diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs index e93f595e..7f87ac2e 100644 --- a/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs +++ b/src/LEGO.AsyncAPI/Models/Any/AsyncAPIObject.cs @@ -14,7 +14,6 @@ namespace LEGO.AsyncAPI.Models [Obsolete("Please use AsyncApiAny instead")] public class AsyncApiObject : Dictionary, IAsyncApiExtension, IAsyncApiElement { - public static implicit operator AsyncApiAny(AsyncApiObject obj) { var jObject = new JsonObject(); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs index cf382b63..6aaa389a 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs @@ -48,7 +48,6 @@ public void Add(TBinding binding) public void SerializeV2(IAsyncApiWriter writer) { - if (writer is null) { throw new ArgumentNullException(nameof(writer)); diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs index 1a5b7c71..5be55202 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs @@ -145,7 +145,8 @@ public IEnumerable ResolveReferences() return resolver.Errors; } - internal T ResolveReference(AsyncApiReference reference) where T : class, IAsyncApiReferenceable + internal T ResolveReference(AsyncApiReference reference) + where T : class, IAsyncApiReferenceable { return this.ResolveReference(reference) as T; } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs index 8d444df0..eea342c4 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs @@ -119,10 +119,10 @@ public static void Serialize(this T element, IAsyncApiWriter writer, AsyncApi switch (specificationVersion) { case AsyncApiVersion.AsyncApi2_0: - element.SerializeV2(writer); - break; + element.SerializeV2(writer); + break; default: - throw new AsyncApiException($"specification version '{specificationVersion}' is not supported."); + throw new AsyncApiException($"specification version '{specificationVersion}' is not supported."); } writer.Flush(); diff --git a/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs b/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs index 44e4573d..f39bc06f 100644 --- a/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs +++ b/src/LEGO.AsyncAPI/Models/Interfaces/IBinding.cs @@ -6,8 +6,8 @@ namespace LEGO.AsyncAPI.Models.Interfaces /// public interface IBinding : IAsyncApiSerializable, IAsyncApiExtensible { - public string BindingKey { get; } + public string BindingKey { get; } - public string BindingVersion { get; set; } + public string BindingVersion { get; set; } } } diff --git a/src/LEGO.AsyncAPI/Models/JsonSchema/FalseApiSchema.cs b/src/LEGO.AsyncAPI/Models/JsonSchema/FalseApiSchema.cs index 40746fb3..01f313e5 100644 --- a/src/LEGO.AsyncAPI/Models/JsonSchema/FalseApiSchema.cs +++ b/src/LEGO.AsyncAPI/Models/JsonSchema/FalseApiSchema.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Models { /// diff --git a/src/LEGO.AsyncAPI/Models/SecuritySchemeType.cs b/src/LEGO.AsyncAPI/Models/SecuritySchemeType.cs index 02fec71b..d910832c 100644 --- a/src/LEGO.AsyncAPI/Models/SecuritySchemeType.cs +++ b/src/LEGO.AsyncAPI/Models/SecuritySchemeType.cs @@ -27,51 +27,51 @@ public enum SecuritySchemeType /// /// Symmetric Encryption. /// - [Display("symmetricEncryption")]SymmetricEncryption, + [Display("symmetricEncryption")] SymmetricEncryption, /// /// Asymmetric Encryption. /// - [Display("asymmetricEncryption")]AsymmetricEncryption, + [Display("asymmetricEncryption")] AsymmetricEncryption, /// /// Api Key. /// - [Display("httpApiKey")]HttpApiKey, + [Display("httpApiKey")] HttpApiKey, /// /// Basic or Bearer token authorization header. /// - [Display("http")]Http, + [Display("http")] Http, /// /// OAuth2. /// - [Display("oauth2")]OAuth2, + [Display("oauth2")] OAuth2, /// /// OIDC. /// - [Display("openIdConnect")]OpenIdConnect, + [Display("openIdConnect")] OpenIdConnect, /// /// Plain. /// - [Display("plain")]Plain, + [Display("plain")] Plain, /// /// Sha256. /// - [Display("scramSha256")]ScramSha256, + [Display("scramSha256")] ScramSha256, /// /// Sha512. /// - [Display("scramSha512")]ScramSha512, + [Display("scramSha512")] ScramSha512, /// /// GssApi. /// - [Display("gssapi")]Gssapi, + [Display("gssapi")] Gssapi, } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs index 88808cb8..da6195c6 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs @@ -154,7 +154,8 @@ public override void Visit(AsyncApiSchema schema) this.ResolveMap(schema.Properties); } - private void ResolveObject(T entity, Action assign) where T : class, IAsyncApiReferenceable, new() + private void ResolveObject(T entity, Action assign) + where T : class, IAsyncApiReferenceable, new() { if (entity == null) { @@ -167,7 +168,8 @@ public override void Visit(AsyncApiSchema schema) } } - private void ResolveList(IList list) where T : class, IAsyncApiReferenceable, new() + private void ResolveList(IList list) + where T : class, IAsyncApiReferenceable, new() { if (list == null) { @@ -184,7 +186,8 @@ public override void Visit(AsyncApiSchema schema) } } - private void ResolveMap(IDictionary map) where T : class, IAsyncApiReferenceable, new() + private void ResolveMap(IDictionary map) + where T : class, IAsyncApiReferenceable, new() { if (map == null) { @@ -201,11 +204,12 @@ public override void Visit(AsyncApiSchema schema) } } - private T ResolveReference(AsyncApiReference reference) where T : class, IAsyncApiReferenceable, new() + private T ResolveReference(AsyncApiReference reference) + where T : class, IAsyncApiReferenceable, new() { if (reference.IsExternal) { - return new () + return new() { UnresolvedReference = true, Reference = reference, diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs index e0d3ec38..fc5c5186 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs @@ -10,7 +10,7 @@ namespace LEGO.AsyncAPI.Services public class AsyncApiWalker { private readonly AsyncApiVisitorBase visitor; - private readonly Stack schemaLoop = new (); + private readonly Stack schemaLoop = new(); public AsyncApiWalker(AsyncApiVisitorBase visitor) { diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs index 93245c22..e42f2ec2 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiContactRules.cs @@ -48,9 +48,9 @@ private static bool IsEmailAddress(this string input) context.Enter("url"); if (contact != null && contact.Url != null && !contact.Url.IsAbsoluteUri) { - context.CreateError( - nameof(ContactUrlMustBeAbsolute), - string.Format(Resource.Validation_MustBeAbsoluteUrl, "url", "contact")); + context.CreateError( + nameof(ContactUrlMustBeAbsolute), + string.Format(Resource.Validation_MustBeAbsoluteUrl, "url", "contact")); } context.Exit(); diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs index 3e6a21c8..b401088e 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiCorrelationIdRules.cs @@ -21,7 +21,6 @@ public static class AsyncApiCorrelationIdRules } context.Exit(); - }); } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs index 710b30e5..a2d27039 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExtensionRules.cs @@ -25,6 +25,7 @@ public static class AsyncApiExtensionRules string.Format(Resource.Validation_ExtensionNameMustBeginWithXDash, extensible.Key, context.PathString)); } } + context.Exit(); }); } diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExternalDocumentationRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExternalDocumentationRules.cs index e080e7b4..f5fd53ba 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExternalDocumentationRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiExternalDocumentationRules.cs @@ -21,7 +21,6 @@ public static class AsyncApiExternalDocumentationRules } context.Exit(); - }); public static ValidationRule ExternalDocumentationUrlMustBeAbsolute => @@ -34,7 +33,6 @@ public static class AsyncApiExternalDocumentationRules context.CreateError( nameof(ExternalDocumentationUrlMustBeAbsolute), string.Format(Resource.Validation_MustBeAbsoluteUrl, "url", "externalDocumentation")); - } context.Exit(); diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiInfoRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiInfoRules.cs index 88b9187c..342a1739 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiInfoRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiInfoRules.cs @@ -43,7 +43,6 @@ public static class AsyncApiInfoRules context.CreateError( nameof(TermsOfServiceUrlMustBeAbsolute), string.Format(Resource.Validation_MustBeAbsoluteUrl, "termsOfService", "info")); - } context.Exit(); diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs index 69b8c5ee..0708c13b 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiLicenseRules.cs @@ -27,14 +27,12 @@ public static class AsyncApiLicenseRules new ValidationRule( (context, license) => { - context.Enter("url"); if (license.Url != null && !license.Url.IsAbsoluteUri) { context.CreateError( nameof(LicenseUrlMustBeAbsolute), string.Format(Resource.Validation_MustBeAbsoluteUrl, "url", "license")); - } context.Exit(); diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs index e1649d4e..457a03ec 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiOAuthFlowRules.cs @@ -42,7 +42,6 @@ public static class AsyncApiOAuthFlowRules } context.Exit(); - }); public static ValidationRule OAuthFlowUrlMustBeAbsolute => @@ -55,7 +54,6 @@ public static class AsyncApiOAuthFlowRules context.CreateError( nameof(OAuthFlowUrlMustBeAbsolute), string.Format(Resource.Validation_MustBeAbsoluteUrl, "authorizationUrl", "flow")); - } context.Exit(); @@ -66,7 +64,6 @@ public static class AsyncApiOAuthFlowRules context.CreateError( nameof(OAuthFlowUrlMustBeAbsolute), string.Format(Resource.Validation_MustBeAbsoluteUrl, "tokenUrl", "flow")); - } context.Exit(); diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs index 244686c5..6e676edb 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiTagRules.cs @@ -21,7 +21,6 @@ public static class AsyncApiTagRules } context.Exit(); - }); } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI/Validation/ValidationRule{T}.cs b/src/LEGO.AsyncAPI/Validation/ValidationRule{T}.cs index 6aecdb7a..658183f3 100644 --- a/src/LEGO.AsyncAPI/Validation/ValidationRule{T}.cs +++ b/src/LEGO.AsyncAPI/Validation/ValidationRule{T}.cs @@ -9,7 +9,8 @@ namespace LEGO.AsyncAPI.Validations /// Class containing validation rule logic for . /// /// - public class ValidationRule : ValidationRule where T : IAsyncApiElement + public class ValidationRule : ValidationRule + where T : IAsyncApiElement { private readonly Action validate; diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs index fe2a34a1..9a317cd1 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterAnyExtensions.cs @@ -174,6 +174,7 @@ private static void WritePrimitive(this IAsyncApiWriter writer, JsonElement prim writer.WriteValue(intValue); } } + if (primitive.ValueKind is JsonValueKind.True or JsonValueKind.False) { writer.WriteValue(primitive.GetBoolean()); diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs index 08025c13..67c4737a 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs @@ -93,6 +93,7 @@ public static void WriteOptionalProperty( /// /// Write a primitive property. /// + /// . /// The writer. /// The property name. /// The property value. @@ -110,6 +111,7 @@ public static void WriteOptionalProperty(this IAsyncApiWriter writer, string /// /// Write a string/number property. /// + /// . /// The writer. /// The property name. /// The property value. @@ -280,7 +282,8 @@ public static void WriteRequiredMap( this IAsyncApiWriter writer, string name, IDictionary elements, - Action action) where T : IAsyncApiElement + Action action) + where T : IAsyncApiElement { if (elements != null && elements.Any()) { diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index a598ac18..d73125fb 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -20,8 +20,10 @@ namespace LEGO.AsyncAPI.Tests public class ExtensionClass { public string Key { get; set; } + public long OtherKey { get; set; } } + public class AsyncApiDocumentV2Tests { [Test] @@ -200,25 +202,25 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() maximum: 100 minimum: 0"; - var asyncApiDocument = new AsyncApiDocumentBuilder() - .WithInfo(new AsyncApiInfo - { - Title = "Streetlights Kafka API", - Version = "1.0.0", - Description = "The Smartylighting Streetlights API allows you to remotely manage the city lights.", - License = new AsyncApiLicense + var asyncApiDocument = new AsyncApiDocumentBuilder() + .WithInfo(new AsyncApiInfo { - Name = "Apache 2.0", - Url = new Uri("https://www.apache.org/licenses/LICENSE-2.0"), - }, - }) - .WithServer("scram-connections", new AsyncApiServer - { - Url = "test.mykafkacluster.org:18092", - Protocol = "kafka-secure", - Description = "Test broker secured with scramSha256", - Security = new List + Title = "Streetlights Kafka API", + Version = "1.0.0", + Description = "The Smartylighting Streetlights API allows you to remotely manage the city lights.", + License = new AsyncApiLicense + { + Name = "Apache 2.0", + Url = new Uri("https://www.apache.org/licenses/LICENSE-2.0"), + }, + }) + .WithServer("scram-connections", new AsyncApiServer { + Url = "test.mykafkacluster.org:18092", + Protocol = "kafka-secure", + Description = "Test broker secured with scramSha256", + Security = new List + { new AsyncApiSecurityRequirement { { @@ -232,9 +234,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, new List() }, }, - }, - Tags = new List - { + }, + Tags = new List + { new AsyncApiTag { Name = "env:test-scram", @@ -250,15 +252,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Name = "visibility:private", Description = "This resource is private and only available to certain users", }, - }, - }) - .WithServer("mtls-connections", new AsyncApiServer - { - Url = "test.mykafkacluster.org:28092", - Protocol = "kafka-secure", - Description = "Test broker secured with X509", - Security = new List + }, + }) + .WithServer("mtls-connections", new AsyncApiServer { + Url = "test.mykafkacluster.org:28092", + Protocol = "kafka-secure", + Description = "Test broker secured with X509", + Security = new List + { new AsyncApiSecurityRequirement { { @@ -272,9 +274,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, new List() }, }, - }, - Tags = new List - { + }, + Tags = new List + { new AsyncApiTag { Name = "env:test-mtls", @@ -290,16 +292,16 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Name = "visibility:private", Description = "This resource is private and only available to certain users", }, - }, - }) - .WithDefaultContentType() - .WithChannel( - "smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured", - new AsyncApiChannel() - { - Description = "The topic on which measured values may be produced and consumed.", - Parameters = new Dictionary + }, + }) + .WithDefaultContentType() + .WithChannel( + "smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured", + new AsyncApiChannel() { + Description = "The topic on which measured values may be produced and consumed.", + Parameters = new Dictionary + { { "streetlightId", new AsyncApiParameter() { @@ -310,13 +312,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Publish = new AsyncApiOperation() - { - Summary = "Inform about environmental lighting conditions of a particular streetlight.", - OperationId = "receiveLightMeasurement", - Traits = new List + }, + Publish = new AsyncApiOperation() { + Summary = "Inform about environmental lighting conditions of a particular streetlight.", + OperationId = "receiveLightMeasurement", + Traits = new List + { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -325,9 +327,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -336,15 +338,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, + }, }, - }, - }) - .WithChannel( - "smartylighting.streetlights.1.0.action.{streetlightId}.turn.on", - new AsyncApiChannel() - { - Parameters = new Dictionary + }) + .WithChannel( + "smartylighting.streetlights.1.0.action.{streetlightId}.turn.on", + new AsyncApiChannel() { + Parameters = new Dictionary + { { "streetlightId", new AsyncApiParameter() { @@ -355,12 +357,12 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Subscribe = new AsyncApiOperation() - { - OperationId = "turnOn", - Traits = new List + }, + Subscribe = new AsyncApiOperation() { + OperationId = "turnOn", + Traits = new List + { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -369,9 +371,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -380,15 +382,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, + }, }, - }, - }) - .WithChannel( - "smartylighting.streetlights.1.0.action.{streetlightId}.turn.off", - new AsyncApiChannel() - { - Parameters = new Dictionary + }) + .WithChannel( + "smartylighting.streetlights.1.0.action.{streetlightId}.turn.off", + new AsyncApiChannel() { + Parameters = new Dictionary + { { "streetlightId", new AsyncApiParameter() { @@ -399,12 +401,12 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Subscribe = new AsyncApiOperation() - { - OperationId = "turnOff", - Traits = new List + }, + Subscribe = new AsyncApiOperation() { + OperationId = "turnOff", + Traits = new List + { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -413,9 +415,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -424,15 +426,15 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, + }, }, - }, - }) - .WithChannel( - "smartylighting.streetlights.1.0.action.{streetlightId}.dim", - new AsyncApiChannel() - { - Parameters = new Dictionary + }) + .WithChannel( + "smartylighting.streetlights.1.0.action.{streetlightId}.dim", + new AsyncApiChannel() { + Parameters = new Dictionary + { { "streetlightId", new AsyncApiParameter() { @@ -443,12 +445,12 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - Subscribe = new AsyncApiOperation() - { - OperationId = "dimLight", - Traits = new List + }, + Subscribe = new AsyncApiOperation() { + OperationId = "dimLight", + Traits = new List + { new AsyncApiOperationTrait() { Reference = new AsyncApiReference() @@ -457,9 +459,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.OperationTrait, }, }, - }, - Message = new List - { + }, + Message = new List + { new AsyncApiMessage() { Reference = new AsyncApiReference() @@ -468,17 +470,17 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Type = ReferenceType.Message, }, }, + }, }, - }, - }) - .WithComponent("lightMeasured", new AsyncApiMessage() - { - Name = "lightMeasured", - Title = "Light measured", - Summary = "Inform about environmental lighting conditions of a particular streetlight.", - ContentType = "application/json", - Traits = new List() + }) + .WithComponent("lightMeasured", new AsyncApiMessage() { + Name = "lightMeasured", + Title = "Light measured", + Summary = "Inform about environmental lighting conditions of a particular streetlight.", + ContentType = "application/json", + Traits = new List() + { new AsyncApiMessageTrait() { Reference = new AsyncApiReference() @@ -487,23 +489,23 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Id = "commonHeaders", }, }, - }, - Payload = new AsyncApiSchema() - { - Reference = new AsyncApiReference() + }, + Payload = new AsyncApiSchema() { - Type = ReferenceType.Schema, - Id = "lightMeasuredPayload", + Reference = new AsyncApiReference() + { + Type = ReferenceType.Schema, + Id = "lightMeasuredPayload", + }, }, - }, - }) - .WithComponent("turnOnOff", new AsyncApiMessage() - { - Name = "turnOnOff", - Title = "Turn on/off", - Summary = "Command a particular streetlight to turn the lights on or off.", - Traits = new List() + }) + .WithComponent("turnOnOff", new AsyncApiMessage() { + Name = "turnOnOff", + Title = "Turn on/off", + Summary = "Command a particular streetlight to turn the lights on or off.", + Traits = new List() + { new AsyncApiMessageTrait() { Reference = new AsyncApiReference() @@ -512,23 +514,23 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Id = "commonHeaders", }, }, - }, - Payload = new AsyncApiSchema() - { - Reference = new AsyncApiReference() + }, + Payload = new AsyncApiSchema() { - Type = ReferenceType.Schema, - Id = "turnOnOffPayload", + Reference = new AsyncApiReference() + { + Type = ReferenceType.Schema, + Id = "turnOnOffPayload", + }, }, - }, - }) - .WithComponent("dimLight", new AsyncApiMessage() - { - Name = "dimLight", - Title = "Dim light", - Summary = "Command a particular streetlight to dim the lights.", - Traits = new List() + }) + .WithComponent("dimLight", new AsyncApiMessage() { + Name = "dimLight", + Title = "Dim light", + Summary = "Command a particular streetlight to dim the lights.", + Traits = new List() + { new AsyncApiMessageTrait() { Reference = new AsyncApiReference() @@ -537,21 +539,21 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Id = "commonHeaders", }, }, - }, - Payload = new AsyncApiSchema() - { - Reference = new AsyncApiReference() + }, + Payload = new AsyncApiSchema() { - Type = ReferenceType.Schema, - Id = "dimLightPayload", + Reference = new AsyncApiReference() + { + Type = ReferenceType.Schema, + Id = "dimLightPayload", + }, }, - }, - }) - .WithComponent("lightMeasuredPayload", new AsyncApiSchema() - { - Type = SchemaType.Object, - Properties = new Dictionary() + }) + .WithComponent("lightMeasuredPayload", new AsyncApiSchema() { + Type = SchemaType.Object, + Properties = new Dictionary() + { { "lumens", new AsyncApiSchema() { @@ -570,13 +572,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .WithComponent("turnOnOffPayload", new AsyncApiSchema() - { - Type = SchemaType.Object, - Properties = new Dictionary() + }, + }) + .WithComponent("turnOnOffPayload", new AsyncApiSchema() { + Type = SchemaType.Object, + Properties = new Dictionary() + { { "command", new AsyncApiSchema() { @@ -586,7 +588,7 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() new AsyncApiAny("on"), new AsyncApiAny("off"), }, - Description = "Whether to turn on or off the light." + Description = "Whether to turn on or off the light.", } }, { @@ -599,13 +601,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .WithComponent("dimLightPayload", new AsyncApiSchema() - { - Type = SchemaType.Object, - Properties = new Dictionary() + }, + }) + .WithComponent("dimLightPayload", new AsyncApiSchema() { + Type = SchemaType.Object, + Properties = new Dictionary() + { { "percentage", new AsyncApiSchema() { @@ -625,40 +627,39 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .WithComponent("sentAt", new AsyncApiSchema() - { - Type = SchemaType.String, - Format = "date-time", - Description = "Date and time when the message was sent.", - - }) - .WithComponent("saslScram", new AsyncApiSecurityScheme - { - Type = SecuritySchemeType.ScramSha256, - Description = "Provide your username and password for SASL/SCRAM authentication", - }) - .WithComponent("certs", new AsyncApiSecurityScheme - { - Type = SecuritySchemeType.X509, - Description = "Download the certificate files from service provider", - }) - .WithComponent("streetlightId", new AsyncApiParameter() - { - Description = "The ID of the streetlight.", - Schema = new AsyncApiSchema() + }, + }) + .WithComponent("sentAt", new AsyncApiSchema() { Type = SchemaType.String, - }, - }) - .WithComponent("commonHeaders", new AsyncApiMessageTrait() - { - Headers = new AsyncApiSchema() + Format = "date-time", + Description = "Date and time when the message was sent.", + }) + .WithComponent("saslScram", new AsyncApiSecurityScheme { - Type = SchemaType.Object, - Properties = new Dictionary() + Type = SecuritySchemeType.ScramSha256, + Description = "Provide your username and password for SASL/SCRAM authentication", + }) + .WithComponent("certs", new AsyncApiSecurityScheme + { + Type = SecuritySchemeType.X509, + Description = "Download the certificate files from service provider", + }) + .WithComponent("streetlightId", new AsyncApiParameter() + { + Description = "The ID of the streetlight.", + Schema = new AsyncApiSchema() + { + Type = SchemaType.String, + }, + }) + .WithComponent("commonHeaders", new AsyncApiMessageTrait() + { + Headers = new AsyncApiSchema() { + Type = SchemaType.Object, + Properties = new Dictionary() + { { "my-app-header", new AsyncApiSchema() { @@ -667,13 +668,13 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() Maximum = 100, } }, + }, }, - }, - }) - .WithComponent("kafka", new AsyncApiOperationTrait() - { - Bindings = new AsyncApiBindings() + }) + .WithComponent("kafka", new AsyncApiOperationTrait() { + Bindings = new AsyncApiBindings() + { { "kafka", new KafkaOperationBinding() { @@ -687,9 +688,9 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() }, } }, - }, - }) - .Build(); + }, + }) + .Build(); // Act var actual = asyncApiDocument.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); @@ -868,7 +869,6 @@ public void SerializeV2_WithFullSpec_Serializes() string authorizationUrl = "https://example.com/authorization"; string requirementString = "requirementItem"; - var document = new AsyncApiDocument() { Id = documentId, @@ -1034,7 +1034,7 @@ public void SerializeV2_WithFullSpec_Serializes() { Summary = exampleSummary, Name = exampleName, - Payload =new AsyncApiAny(new ExtensionClass + Payload = new AsyncApiAny(new ExtensionClass { Key = anyStringValue, OtherKey = anyLongValue, @@ -1137,7 +1137,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() var doc = new AsyncApiDocument(); doc.Info = new AsyncApiInfo() { - Description = "test description" + Description = "test description", }; doc.Servers.Add("production", new AsyncApiServer { @@ -1157,7 +1157,8 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() { Channels = new Dictionary() { - { "otherchannel", new AsyncApiChannel() + { + "otherchannel", new AsyncApiChannel() { Publish = new AsyncApiOperation() { @@ -1171,8 +1172,8 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() Id = "bindings", }, }, - } - } + } + }, }, ServerBindings = new Dictionary>() { @@ -1181,10 +1182,10 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() { new PulsarServerBinding() { - Tenant = "staging" + Tenant = "staging", }, } - } + }, }, ChannelBindings = new Dictionary>() { @@ -1193,21 +1194,22 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() { new PulsarChannelBinding() { - Namespace = "users", + Namespace = "users", Persistence = AsyncAPI.Models.Bindings.Pulsar.Persistence.Persistent, - } + }, } - } + }, }, }; - doc.Channels.Add("testChannel", + doc.Channels.Add( + "testChannel", new AsyncApiChannel { Reference = new AsyncApiReference() { Type = ReferenceType.Channel, - Id = "otherchannel" - } + Id = "otherchannel", + }, }); var actual = doc.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); @@ -1216,7 +1218,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); } - + [Test] public void Serializev2_WithBindings_Serializes() { @@ -1247,7 +1249,7 @@ public void Serializev2_WithBindings_Serializes() var doc = new AsyncApiDocument(); doc.Info = new AsyncApiInfo() { - Description = "test description" + Description = "test description", }; doc.Servers.Add("production", new AsyncApiServer { @@ -1255,7 +1257,8 @@ public void Serializev2_WithBindings_Serializes() Protocol = "pulsar+ssl", Url = "example.com", }); - doc.Channels.Add("testChannel", + doc.Channels.Add( + "testChannel", new AsyncApiChannel { Bindings = new AsyncApiBindings @@ -1295,7 +1298,6 @@ public void Serializev2_WithBindings_Serializes() }, } }, - }, } }, diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs index bcb25150..44fbad93 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs @@ -56,14 +56,14 @@ public static Stream GenerateStreamFromString(string s) public void LoadLicense_WithJson_Deserializes() { // Arrange - var input = @"{ + var input = @"{ ""name"": ""test"", ""url"": ""https://example.com/license"", ""x-extension"": ""value"" }"; - using (var stream = GenerateStreamFromString(input)) - { + using (var stream = GenerateStreamFromString(input)) + { var diagnostic = new AsyncApiDiagnostic(); var context = new ParsingContext(diagnostic); diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index 8461aece..40a2ddbb 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -103,10 +103,10 @@ public void Read_WithThrowingExtensionParser_AddsToDiagnostics() Assert.AreEqual("Failed to parse", error.Message); } - [Test] - public void Read_WithBasicPlusContact_Deserializes() - { - var yaml = @"asyncapi: 2.3.0 + [Test] + public void Read_WithBasicPlusContact_Deserializes() + { + var yaml = @"asyncapi: 2.3.0 info: title: test version: 1.0.0 @@ -118,17 +118,17 @@ public void Read_WithBasicPlusContact_Deserializes() workspace: x-eventarchetype: objectchanged "; - var reader = new AsyncApiStringReader(); - var doc = reader.Read(yaml, out var diagnostic); - Assert.AreEqual("support@example.com", doc.Info.Contact.Email); - Assert.AreEqual(new Uri("https://www.example.com/support"), doc.Info.Contact.Url); - Assert.AreEqual("API Support", doc.Info.Contact.Name); - } + var reader = new AsyncApiStringReader(); + var doc = reader.Read(yaml, out var diagnostic); + Assert.AreEqual("support@example.com", doc.Info.Contact.Email); + Assert.AreEqual(new Uri("https://www.example.com/support"), doc.Info.Contact.Url); + Assert.AreEqual("API Support", doc.Info.Contact.Name); + } - [Test] - public void Read_WithBasicPlusExternalDocs_Deserializes() - { - var yaml = @"asyncapi: 2.3.0 + [Test] + public void Read_WithBasicPlusExternalDocs_Deserializes() + { + var yaml = @"asyncapi: 2.3.0 info: title: test version: 1.0.0 @@ -148,17 +148,17 @@ public void Read_WithBasicPlusExternalDocs_Deserializes() description: Find more info here url: https://example.com "; - var reader = new AsyncApiStringReader(); - var doc = reader.Read(yaml, out var diagnostic); - var message = doc.Channels["workspace"].Publish.Message; - Assert.AreEqual(new Uri("https://example.com"), message.First().ExternalDocs.Url); - Assert.AreEqual("Find more info here", message.First().ExternalDocs.Description); - } + var reader = new AsyncApiStringReader(); + var doc = reader.Read(yaml, out var diagnostic); + var message = doc.Channels["workspace"].Publish.Message; + Assert.AreEqual(new Uri("https://example.com"), message.First().ExternalDocs.Url); + Assert.AreEqual("Find more info here", message.First().ExternalDocs.Description); + } - [Test] - public void Read_WithBasicPlusTag_Deserializes() - { - var yaml = @"asyncapi: 2.3.0 + [Test] + public void Read_WithBasicPlusTag_Deserializes() + { + var yaml = @"asyncapi: 2.3.0 info: title: test version: 1.0.0 @@ -169,17 +169,17 @@ public void Read_WithBasicPlusTag_Deserializes() - name: user description: User-related messages "; - var reader = new AsyncApiStringReader(); - var doc = reader.Read(yaml, out var diagnostic); - var tag = doc.Tags.First(); - Assert.AreEqual("user", tag.Name); - Assert.AreEqual("User-related messages", tag.Description); - } + var reader = new AsyncApiStringReader(); + var doc = reader.Read(yaml, out var diagnostic); + var tag = doc.Tags.First(); + Assert.AreEqual("user", tag.Name); + Assert.AreEqual("User-related messages", tag.Description); + } - [Test] - public void Read_WithBasicPlusServerDeserializes() - { - var yaml = @"asyncapi: 2.3.0 + [Test] + public void Read_WithBasicPlusServerDeserializes() + { + var yaml = @"asyncapi: 2.3.0 info: title: test version: 1.0.0 @@ -192,19 +192,19 @@ public void Read_WithBasicPlusServerDeserializes() protocol: pulsar+ssl description: Pulsar broker "; - var reader = new AsyncApiStringReader(); - var doc = reader.Read(yaml, out var diagnostic); - var server = doc.Servers.First(); - Assert.AreEqual("production", server.Key); - Assert.AreEqual("pulsar+ssl://prod.events.managed.io:1234", server.Value.Url); - Assert.AreEqual("pulsar+ssl", server.Value.Protocol); - Assert.AreEqual("Pulsar broker", server.Value.Description); - } + var reader = new AsyncApiStringReader(); + var doc = reader.Read(yaml, out var diagnostic); + var server = doc.Servers.First(); + Assert.AreEqual("production", server.Key); + Assert.AreEqual("pulsar+ssl://prod.events.managed.io:1234", server.Value.Url); + Assert.AreEqual("pulsar+ssl", server.Value.Protocol); + Assert.AreEqual("Pulsar broker", server.Value.Description); + } - [Test] - public void Read_WithBasicPlusServerVariablesDeserializes() - { - var yaml = @"asyncapi: 2.3.0 + [Test] + public void Read_WithBasicPlusServerVariablesDeserializes() + { + var yaml = @"asyncapi: 2.3.0 info: title: test version: 1.0.0 @@ -224,19 +224,19 @@ public void Read_WithBasicPlusServerVariablesDeserializes() - '1883' - '8883' "; - var reader = new AsyncApiStringReader(); - var doc = reader.Read(yaml, out var diagnostic); - var server = doc.Servers.First(); - var variable = server.Value.Variables.First(); - Assert.AreEqual("production", server.Key); - Assert.AreEqual("port", variable.Key); - Assert.AreEqual("Secure connection (TLS) is available through port 8883.", variable.Value.Description); - } + var reader = new AsyncApiStringReader(); + var doc = reader.Read(yaml, out var diagnostic); + var server = doc.Servers.First(); + var variable = server.Value.Variables.First(); + Assert.AreEqual("production", server.Key); + Assert.AreEqual("port", variable.Key); + Assert.AreEqual("Secure connection (TLS) is available through port 8883.", variable.Value.Description); + } - [Test] - public void Read_WithBasicPlusCorrelationIDDeserializes() - { - var yaml = @"asyncapi: 2.3.0 + [Test] + public void Read_WithBasicPlusCorrelationIDDeserializes() + { + var yaml = @"asyncapi: 2.3.0 info: title: test version: 1.0.0 @@ -256,12 +256,12 @@ public void Read_WithBasicPlusCorrelationIDDeserializes() description: Default Correlation ID location: $message.header#/correlationId "; - var reader = new AsyncApiStringReader(); - var doc = reader.Read(yaml, out var diagnostic); - var message = doc.Channels["workspace"].Publish.Message; - Assert.AreEqual("Default Correlation ID", message.First().CorrelationId.Description); - Assert.AreEqual("$message.header#/correlationId", message.First().CorrelationId.Location); - } + var reader = new AsyncApiStringReader(); + var doc = reader.Read(yaml, out var diagnostic); + var message = doc.Channels["workspace"].Publish.Message; + Assert.AreEqual("Default Correlation ID", message.First().CorrelationId.Description); + Assert.AreEqual("$message.header#/correlationId", message.First().CorrelationId.Location); + } [Test] public void Read_WithOneOfMessage_Reads() @@ -295,9 +295,9 @@ public void Read_WithOneOfMessage_Reads() } [Test] - public void Read_WithBasicPlusSecuritySchemeDeserializes() - { - var yaml = @"asyncapi: 2.3.0 + public void Read_WithBasicPlusSecuritySchemeDeserializes() + { + var yaml = @"asyncapi: 2.3.0 info: title: test version: 1.0.0 @@ -318,18 +318,18 @@ public void Read_WithBasicPlusSecuritySchemeDeserializes() type: scramSha256 description: Provide your username and password for SASL/SCRAM authentication "; - var reader = new AsyncApiStringReader(); - var doc = reader.Read(yaml, out var diagnostic); - var scheme = doc.Components.SecuritySchemes.First(); - Assert.AreEqual("saslScram", scheme.Key); - Assert.AreEqual(SecuritySchemeType.ScramSha256, scheme.Value.Type); - Assert.AreEqual("Provide your username and password for SASL/SCRAM authentication", scheme.Value.Description); - } + var reader = new AsyncApiStringReader(); + var doc = reader.Read(yaml, out var diagnostic); + var scheme = doc.Components.SecuritySchemes.First(); + Assert.AreEqual("saslScram", scheme.Key); + Assert.AreEqual(SecuritySchemeType.ScramSha256, scheme.Value.Type); + Assert.AreEqual("Provide your username and password for SASL/SCRAM authentication", scheme.Value.Description); + } - [Test] - public void Read_WithBasicPlusOAuthFlowDeserializes() - { - var yaml = @"asyncapi: 2.3.0 + [Test] + public void Read_WithBasicPlusOAuthFlowDeserializes() + { + var yaml = @"asyncapi: 2.3.0 info: title: test version: 1.0.0 @@ -347,16 +347,16 @@ public void Read_WithBasicPlusOAuthFlowDeserializes() write:pets: modify pets in your account read:pets: read your pets "; - var reader = new AsyncApiStringReader(); - var doc = reader.Read(yaml, out var diagnostic); - var scheme = doc.Components.SecuritySchemes.First(); - var flow = scheme.Value.Flows; - Assert.AreEqual("oauth2", scheme.Key); - Assert.AreEqual(SecuritySchemeType.OAuth2, scheme.Value.Type); - Assert.AreEqual(new Uri("https://example.com/api/oauth/dialog"), flow.Implicit.AuthorizationUrl); - Assert.IsTrue(flow.Implicit.Scopes.ContainsKey("write:pets")); - Assert.IsTrue(flow.Implicit.Scopes.ContainsKey("read:pets")); - } + var reader = new AsyncApiStringReader(); + var doc = reader.Read(yaml, out var diagnostic); + var scheme = doc.Components.SecuritySchemes.First(); + var flow = scheme.Value.Flows; + Assert.AreEqual("oauth2", scheme.Key); + Assert.AreEqual(SecuritySchemeType.OAuth2, scheme.Value.Type); + Assert.AreEqual(new Uri("https://example.com/api/oauth/dialog"), flow.Implicit.AuthorizationUrl); + Assert.IsTrue(flow.Implicit.Scopes.ContainsKey("write:pets")); + Assert.IsTrue(flow.Implicit.Scopes.ContainsKey("read:pets")); + } [Test] public void Read_WithServerReference_ResolvesReference() @@ -573,6 +573,6 @@ public void Read_WithBasicPlusSecurityRequirementsDeserializes() Assert.AreEqual(SecuritySchemeType.OAuth2, requirement.Key.Type); Assert.IsTrue(requirement.Value.Contains("write:pets")); Assert.IsTrue(requirement.Value.Contains("read:pets")); - } + } } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs index 09317d0c..e9178311 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs @@ -162,7 +162,7 @@ public void AMQPOperationBinding_WithFilledObject_SerializesAndDeserializes() Bcc = new List { "external.audit" }, Timestamp = true, Ack = false, - });; + }); ; // Act var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs index 461bbf79..5dafed7b 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs @@ -18,7 +18,7 @@ public class NestedConfiguration : IAsyncApiExtensible public IDictionary Extensions { get; set; } = new Dictionary(); - public static FixedFieldMap FixedFieldMap = new () + public static FixedFieldMap FixedFieldMap = new() { { "name", (a, n) => { a.Name = n.GetScalarValue(); } }, }; @@ -84,10 +84,10 @@ public void CustomBinding_SerializesDeserializes() channel.Bindings.Add(new MyBinding { Custom = "someValue", - Any = new AsyncApiObject() + Any = new AsyncApiAny(new Dictionary() { - { "anyKeyName", new AsyncApiAny("anyValue") }, - }, + { "anyKeyName", "anyValue" }, + }), BindingVersion = "0.1.0", NestedConfiguration = new NestedConfiguration() { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index dbca5966..385ccfd5 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Tests.Bindings.Sns { using System.Collections.Generic; @@ -59,10 +61,10 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { { "x-orderingExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "orderingXPropertyName", new AsyncApiAny("orderingXPropertyValue") }, - } + { "orderingXPropertyName", "orderingXPropertyValue" }, + }) }, }, }, @@ -74,29 +76,29 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { Effect = Effect.Deny, Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), - Action = new StringOrStringList(new AsyncApiArray() + Action = new StringOrStringList(new AsyncApiAny(new List() { - new AsyncApiAny("sns:Publish"), - new AsyncApiAny("sns:Delete") - }), + "sns:Publish", + "sns:Delete", + })), }, new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiArray() + Principal = new StringOrStringList(new AsyncApiAny(new List() { - new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"), - new AsyncApiAny("arn:aws:iam::123456789012:user/dec.kolakowski") - }), + "arn:aws:iam::123456789012:user/alex.wichmann", + "arn:aws:iam::123456789012:user/dec.kolakowski", + })), Action = new StringOrStringList(new AsyncApiAny("sns:Create")), Extensions = new Dictionary() { { "x-statementExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "statementXPropertyName", new AsyncApiAny("statementXPropertyValue") }, - } + { "statementXPropertyName", "statementXPropertyValue" }, + }) }, }, }, @@ -105,10 +107,10 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { { "x-policyExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "policyXPropertyName", new AsyncApiAny("policyXPropertyValue") }, - } + { "policyXPropertyName", "policyXPropertyValue" }, + }) }, }, }, @@ -121,10 +123,10 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { { "x-bindingExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "bindingXPropertyName", new AsyncApiAny("bindingXPropertyValue") }, - } + { "bindingXPropertyName", "bindingXPropertyValue" }, + }) }, }, }); @@ -222,10 +224,10 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-identifierExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "identifierXPropertyName", new AsyncApiAny("identifierXPropertyValue") }, - } + { "identifierXPropertyName", "identifierXPropertyValue" }, + }) }, }, }, @@ -241,41 +243,41 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-identifierExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "identifierXPropertyName", new AsyncApiAny("identifierXPropertyValue") }, - } + { "identifierXPropertyName", "identifierXPropertyValue" }, + }) }, }, }, - FilterPolicy = new AsyncApiObject() - { - { "store", new AsyncApiArray() { new AsyncApiAny("asyncapi_corp") } }, - { "contact", new AsyncApiAny("dec.kolakowski") }, + FilterPolicy = new AsyncApiAny(new Dictionary() + { + { "store", new List() { "asyncapi_corp" } }, + { "contact", "dec.kolakowski" }, { - "event", new AsyncApiArray() + "event", new List>() { - new AsyncApiObject() + new Dictionary() { - { "anything-but", new AsyncApiAny("order_cancelled") }, + { "anything-but", "order_cancelled" }, }, } }, { - "order_key", new AsyncApiObject() + "order_key", new Dictionary() { - { "transient", new AsyncApiAny("by_area") }, + { "transient", "by_area" }, } }, { - "customer_interests", new AsyncApiArray() + "customer_interests", new List() { - new AsyncApiAny("rugby"), - new AsyncApiAny("football"), - new AsyncApiAny("baseball"), + "rugby", + "football", + "baseball", } }, - }, + }), FilterPolicyScope = FilterPolicyScope.MessageAttributes, RawMessageDelivery = false, RedrivePolicy = new RedrivePolicy() @@ -287,10 +289,10 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-identifierExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "identifierXPropertyName", new AsyncApiAny("identifierXPropertyValue") }, - } + { "identifierXPropertyName", "identifierXPropertyValue" }, + }) }, }, }, @@ -299,10 +301,10 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-redrivePolicyExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "redrivePolicyXPropertyName", new AsyncApiAny("redrivePolicyXPropertyValue") }, - } + { "redrivePolicyXPropertyName", "redrivePolicyXPropertyValue" }, + }) }, }, }, @@ -320,10 +322,10 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-deliveryPolicyExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "deliveryPolicyXPropertyName", new AsyncApiAny("deliveryPolicyXPropertyValue") }, - } + { "deliveryPolicyXPropertyName", "deliveryPolicyXPropertyValue" }, + }) }, }, }, @@ -331,10 +333,10 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-consumerExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "consumerXPropertyName", new AsyncApiAny("consumerXPropertyValue") }, - } + { "consumerXPropertyName", "consumerXPropertyValue" }, + }) }, }, }, @@ -353,10 +355,10 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-deliveryPolicyExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "deliveryPolicyXPropertyName", new AsyncApiAny("deliveryPolicyXPropertyValue") }, - } + { "deliveryPolicyXPropertyName", "deliveryPolicyXPropertyValue" }, + }) }, }, }, @@ -364,10 +366,10 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-bindingExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "bindingXPropertyName", new AsyncApiAny("bindingXPropertyValue") }, - } + { "bindingXPropertyName", "bindingXPropertyValue" }, + }) }, }, }); @@ -391,6 +393,7 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() var expectedSnsBinding = (SnsOperationBinding)operation.Bindings.Values.First(); expectedSnsBinding.Should().BeEquivalentTo((SnsOperationBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); } + class ExtensionClass { public string bindingXPropertyName { get; set; } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index d72177ce..63eeff51 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Tests.Bindings.Sqs { using System.Collections.Generic; @@ -94,10 +96,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() { { "x-identifierExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary { - { "identifierXPropertyName", new AsyncApiAny("identifierXPropertyValue") }, - } + { "identifierXPropertyName", "identifierXPropertyValue" }, + }) }, }, }, @@ -106,10 +108,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() { { "x-redrivePolicyExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary { - { "redrivePolicyXPropertyName", new AsyncApiAny("redrivePolicyXPropertyValue") }, - } + { "redrivePolicyXPropertyName", "redrivePolicyXPropertyValue" }, + }) }, }, }, @@ -121,30 +123,30 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() { Effect = Effect.Deny, Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), - Action = new StringOrStringList(new AsyncApiArray() + Action = new StringOrStringList(new AsyncApiAny(new List { - new AsyncApiAny("sqs:SendMessage"), - new AsyncApiAny("sqs:ReceiveMessage") - }), + "sqs:SendMessage", + "sqs:ReceiveMessage", + })), Extensions = new Dictionary() { { "x-statementExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary { - { "statementXPropertyName", new AsyncApiAny("statementXPropertyValue") }, - } + { "statementXPropertyName", "statementXPropertyValue" }, + }) }, }, }, new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiArray + Principal = new StringOrStringList(new AsyncApiAny(new List { - new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"), - new AsyncApiAny("arn:aws:iam::123456789012:user/dec.kolakowski") - }), + "arn:aws:iam::123456789012:user/alex.wichmann", + "arn:aws:iam::123456789012:user/dec.kolakowski", + })), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), }, }, @@ -152,10 +154,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() { { "x-policyExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary { - { "policyXPropertyName", new AsyncApiAny("policyXPropertyValue") }, - } + { "policyXPropertyName", "policyXPropertyValue" }, + }) }, }, }, @@ -168,10 +170,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() { { "x-queueExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary { - { "queueXPropertyName", new AsyncApiAny("queueXPropertyValue") }, - } + { "queueXPropertyName", "queueXPropertyValue" }, + }) }, }, }, @@ -191,10 +193,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() { Effect = Effect.Allow, Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), - Action = new StringOrStringList(new AsyncApiArray() + Action = new StringOrStringList(new AsyncApiAny(new List { - new AsyncApiAny("sqs:*"), - }), + "sqs:*", + })), }, }, }, @@ -202,10 +204,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() Extensions = new Dictionary() { { - "x-internalObject", new AsyncApiObject() + "x-internalObject", new AsyncApiAny(new Dictionary { - { "myExtensionPropertyName", new AsyncApiAny("myExtensionPropertyValue") }, - } + { "myExtensionPropertyName", "myExtensionPropertyValue" }, + }) }, }, }); @@ -219,8 +221,7 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Sqs; var binding = - new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, - out _); + new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert Assert.AreEqual(expected, actual); @@ -309,10 +310,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-identifierExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary { - { "identifierXPropertyName", new AsyncApiAny("identifierXPropertyValue") }, - } + { "identifierXPropertyName", "identifierXPropertyValue" }, + }) }, }, }, @@ -321,10 +322,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-redrivePolicyExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary { - { "redrivePolicyXPropertyName", new AsyncApiAny("redrivePolicyXPropertyValue") }, - } + { "redrivePolicyXPropertyName", "redrivePolicyXPropertyValue" }, + }) }, }, }, @@ -336,41 +337,41 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() { Effect = Effect.Deny, Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), - Action = new StringOrStringList(new AsyncApiArray() + Action = new StringOrStringList(new AsyncApiAny(new List() { - new AsyncApiAny("sqs:SendMessage"), - new AsyncApiAny("sqs:ReceiveMessage") - }), + "sqs:SendMessage", + "sqs:ReceiveMessage", + })), Extensions = new Dictionary() { { "x-statementExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "statementXPropertyName", new AsyncApiAny("statementXPropertyValue") }, - } + { "statementXPropertyName", "statementXPropertyValue" }, + }) }, }, }, new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiArray + Principal = new StringOrStringList(new AsyncApiAny(new List { - new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann"), - new AsyncApiAny("arn:aws:iam::123456789012:user/dec.kolakowski"), - }), - Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")) + "arn:aws:iam::123456789012:user/alex.wichmann", + "arn:aws:iam::123456789012:user/dec.kolakowski", + })), + Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), }, }, Extensions = new Dictionary() { { "x-policyExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary { - { "policyXPropertyName", new AsyncApiAny("policyXPropertyValue") }, - } + { "policyXPropertyName", "policyXPropertyValue" }, + }) }, }, }, @@ -383,10 +384,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-queueExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "queueXPropertyName", new AsyncApiAny("queueXPropertyValue") }, - } + { "queueXPropertyName", "queueXPropertyValue" }, + }) }, }, }, @@ -406,10 +407,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() { Effect = Effect.Allow, Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), - Action = new StringOrStringList(new AsyncApiArray + Action = new StringOrStringList(new AsyncApiAny(new List { - new AsyncApiAny("sqs:*") - }) + "sqs:*", + })), }, }, }, @@ -417,10 +418,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() { { "x-queueExtension", - new AsyncApiObject() + new AsyncApiAny(new Dictionary() { - { "queueXPropertyName", new AsyncApiAny("queueXPropertyValue") }, - } + { "queueXPropertyName", "queueXPropertyValue" }, + }) }, }, }, @@ -428,10 +429,10 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() Extensions = new Dictionary() { { - "x-internalObject", new AsyncApiObject() + "x-internalObject", new AsyncApiAny(new Dictionary() { - { "myExtensionPropertyName", new AsyncApiAny("myExtensionPropertyValue") }, - } + { "myExtensionPropertyName", "myExtensionPropertyValue" }, + }) }, }, }); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs index 5e2780e7..21b64f5e 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs @@ -1,3 +1,5 @@ +// Copyright (c) The LEGO Group. All rights reserved. + namespace LEGO.AsyncAPI.Tests.Bindings { using System; @@ -13,23 +15,21 @@ namespace LEGO.AsyncAPI.Tests.Bindings public class StringOrStringList_Should { - [Test] public void StringOrStringList_IsInitialised_WhenPassedStringOrStringList() { // Arrange var stringValue = new StringOrStringList(new AsyncApiAny("AsyncApi")); var listValue = new StringOrStringList( - new AsyncApiArray() + new AsyncApiAny(new List() { - new AsyncApiAny("Async"), - new AsyncApiAny("Api"), - }); + "Async", + "Api", + })); // Assert stringValue.Value.GetValue().Should().Be("AsyncApi"); - ((AsyncApiArray)listValue.Value) - .Select(s => s.GetValue()) + listValue.Value.GetValue>() .Should().BeEquivalentTo(new List() { "Async", "Api" }); } @@ -48,12 +48,12 @@ public void StringOrStringList_ThrowsArgumentException_WhenIntialisedWithListOfN { // Assert var ex = Assert.Throws(() => new StringOrStringList( - new AsyncApiArray() + new AsyncApiAny(new List() { - new AsyncApiAny("x"), - new AsyncApiAny(1), - new AsyncApiAny("y"), - })); + "x", + 1, + "y", + }))); // Assert ex.Message.Should().Be("StringOrStringList value should only contain string items."); @@ -103,12 +103,12 @@ public void StringOrStringList_WhenValueIsStringList_SerializesDeserializes() var channel = new AsyncApiChannel(); channel.Bindings.Add(new StringOrStringListTestBinding { - TestProperty = new StringOrStringList(new AsyncApiArray + TestProperty = new StringOrStringList(new AsyncApiAny(new List { - new AsyncApiAny("someValue01"), - new AsyncApiAny("someValue02"), - new AsyncApiAny("someValue03"), - }), + "someValue01", + "someValue02", + "someValue03", + })), }); // Act @@ -141,7 +141,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) writer.WriteEndObject(); } - protected override FixedFieldMap FixedFieldMap => new () + protected override FixedFieldMap FixedFieldMap => new() { { "testProperty", (a, n) => { a.TestProperty = new StringOrStringList(n.CreateAny()); } }, }; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs index 598ef31f..6c93e483 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiAnyTests.cs @@ -1,4 +1,6 @@ -using LEGO.AsyncAPI.Models; +// Copyright (c) The LEGO Group. All rights reserved. + +using LEGO.AsyncAPI.Models; using NUnit.Framework; using System; using System.Collections.Generic; @@ -6,7 +8,6 @@ namespace LEGO.AsyncAPI.Tests { - public class AsyncApiAnyTests { [Test] @@ -19,8 +20,8 @@ public void GetValue_ReturnsCorrectConversions() var c = new AsyncApiAny(1.1); var d = new AsyncApiAny(true); var e = new AsyncApiAny(new MyType("test")); - var f = new AsyncApiAny(new List() { "test", "test2"}); - var g = new AsyncApiAny(new List() { "test", "test2"}.AsEnumerable()); + var f = new AsyncApiAny(new List() { "test", "test2" }); + var g = new AsyncApiAny(new List() { "test", "test2" }.AsEnumerable()); var h = new AsyncApiAny(new List() { new MyType("test") }); var i = new AsyncApiAny(new Dictionary() { { "t", 2 } }); var j = new AsyncApiAny(new Dictionary() { { "t", new MyType("test") } }); diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs index 05f91943..18e6b4fd 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs @@ -96,7 +96,6 @@ public void AsyncApiChannel_WithKafkaBinding_Serializes() Replicas = 2, } }, - }, }; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index 0849aa85..67156b1d 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -15,12 +15,12 @@ namespace LEGO.AsyncAPI.Tests.Models internal class AsyncApiMessage_Should { - [Test] - public void AsyncApiMessage_WithNoType_DeserializesToDefault() - { - // Arrange - var expected = - @"{ + [Test] + public void AsyncApiMessage_WithNoType_DeserializesToDefault() + { + // Arrange + var expected = + @"{ ""payload"": { ""type"": ""object"", ""properties"": { @@ -34,19 +34,19 @@ public void AsyncApiMessage_WithNoType_DeserializesToDefault() } }"; - // Act - var message = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + // Act + var message = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); - // Assert - diagnostic.Errors.Should().BeEmpty(); - message.Payload.Properties.First().Value.Enum.Should().HaveCount(2); - } + // Assert + diagnostic.Errors.Should().BeEmpty(); + message.Payload.Properties.First().Value.Enum.Should().HaveCount(2); + } - [Test] - public void AsyncApiMessage_WithNoSchemaFormat_DeserializesToDefault() - { - // Arrange - var expected = + [Test] + public void AsyncApiMessage_WithNoSchemaFormat_DeserializesToDefault() + { + // Arrange + var expected = @"payload: properties: propertyA: @@ -54,20 +54,20 @@ public void AsyncApiMessage_WithNoSchemaFormat_DeserializesToDefault() - 'null' - string"; - // Act - var message = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + // Act + var message = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); - // Assert - diagnostic.Errors.Should().BeEmpty(); - message.SchemaFormat.Should().BeNull(); - } + // Assert + diagnostic.Errors.Should().BeEmpty(); + message.SchemaFormat.Should().BeNull(); + } - [Test] - public void AsyncApiMessage_WithUnsupportedSchemaFormat_DeserializesWithError() - { - // Arrange - var expected = - @"payload: + [Test] + public void AsyncApiMessage_WithUnsupportedSchemaFormat_DeserializesWithError() + { + // Arrange + var expected = +@"payload: properties: propertyA: type: @@ -75,30 +75,30 @@ public void AsyncApiMessage_WithUnsupportedSchemaFormat_DeserializesWithError() - string schemaFormat: application/vnd.apache.avro;version=1.9.0"; - // Act - new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); + // Act + new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); - // Assert - diagnostic.Errors.Should().HaveCount(1); - diagnostic.Errors.First().Message.Should().StartWith("'application/vnd.apache.avro;version=1.9.0' is not a supported format"); - } + // Assert + diagnostic.Errors.Should().HaveCount(1); + diagnostic.Errors.First().Message.Should().StartWith("'application/vnd.apache.avro;version=1.9.0' is not a supported format"); + } - [Test] - public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() - { - // Arrange - var expected = - @"payload: + [Test] + public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() + { + // Arrange + var expected = +@"payload: properties: propertyA: type: - 'null' - string"; - var message = new AsyncApiMessage(); - message.Payload = new AsyncApiSchema() - { - Properties = new Dictionary() + var message = new AsyncApiMessage(); + message.Payload = new AsyncApiSchema() + { + Properties = new Dictionary() { { "propertyA", new AsyncApiSchema() @@ -107,27 +107,27 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() } }, }, - }; + }; - // Act - var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + // Act + var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); - var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); + var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); - // Assert - Assert.AreEqual(expected, actual); - message.Should().BeEquivalentTo(deserializedMessage); - } + // Assert + Assert.AreEqual(expected, actual); + message.Should().BeEquivalentTo(deserializedMessage); + } - [Test] - public void AsyncApiMessage_WithSchemaFormat_Serializes() - { - // Arrange - var expected = - @"payload: + [Test] + public void AsyncApiMessage_WithSchemaFormat_Serializes() + { + // Arrange + var expected = +@"payload: properties: propertyA: type: @@ -135,11 +135,11 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() - string schemaFormat: application/vnd.aai.asyncapi+json;version=2.6.0"; - var message = new AsyncApiMessage(); - message.SchemaFormat = "application/vnd.aai.asyncapi+json;version=2.6.0"; - message.Payload = new AsyncApiSchema() - { - Properties = new Dictionary() + var message = new AsyncApiMessage(); + message.SchemaFormat = "application/vnd.aai.asyncapi+json;version=2.6.0"; + message.Payload = new AsyncApiSchema() + { + Properties = new Dictionary() { { "propertyA", new AsyncApiSchema() @@ -148,20 +148,20 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() } }, }, - }; + }; - // Act - var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + // Act + var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); + actual = actual.MakeLineBreaksEnvironmentNeutral(); + expected = expected.MakeLineBreaksEnvironmentNeutral(); - var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); + var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); - // Assert - Assert.AreEqual(expected, actual); - message.Should().BeEquivalentTo(deserializedMessage); - } + // Assert + Assert.AreEqual(expected, actual); + message.Should().BeEquivalentTo(deserializedMessage); + } [Test] public void AsyncApiMessage_WithFilledObject_Serializes() @@ -243,10 +243,10 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Description = "HeaderDescription", Examples = new List { - new AsyncApiObject + new AsyncApiAny(new Dictionary { - { "x-correlation-id", new AsyncApiAny("nil") }, - }, + { "x-correlation-id", "nil" }, + }), }, }, Payload = new AsyncApiSchema() @@ -262,7 +262,7 @@ public void AsyncApiMessage_WithFilledObject_Serializes() { "propB", new AsyncApiSchema() { - Type =SchemaType.String, + Type = SchemaType.String, } }, }, @@ -306,11 +306,11 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Description = "SchemaDescription", Examples = new List { - new AsyncApiObject + new AsyncApiAny(new Dictionary { - { "cKey", new AsyncApiAny("c") }, - { "dKey", new AsyncApiAny(1) }, - }, + { "cKey", "c" }, + { "dKey", 1 }, + }), }, }, } @@ -320,11 +320,11 @@ public void AsyncApiMessage_WithFilledObject_Serializes() { new AsyncApiMessageExample { - Payload = new AsyncApiObject() + Payload = new AsyncApiAny(new Dictionary() { - { "PropA", new AsyncApiAny("a") }, - { "PropB", new AsyncApiAny("b") }, - }, + { "PropA", "a" }, + { "PropB", "b" }, + }), }, }, Traits = new List @@ -340,11 +340,11 @@ public void AsyncApiMessage_WithFilledObject_Serializes() Description = "SchemaDescription", Examples = new List { - new AsyncApiObject + new AsyncApiAny(new Dictionary { - { "eKey", new AsyncApiAny("e") }, - { "fKey", new AsyncApiAny(1) }, - }, + { "eKey", "e" }, + { "fKey", 1 }, + }), }, }, Examples = new List @@ -353,11 +353,11 @@ public void AsyncApiMessage_WithFilledObject_Serializes() { Summary = "MessageExampleSummary", Name = "MessageExampleName", - Payload = new AsyncApiObject + Payload = new AsyncApiAny(new Dictionary { - { "gKey", new AsyncApiAny("g") }, - { "hKey", new AsyncApiAny(true) }, - }, + { "gKey", "g" }, + { "hKey", true }, + }), Extensions = new Dictionary { { "x-extension-b", new AsyncApiAny("b") }, diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs index 273162b3..eeaa4047 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs @@ -10,7 +10,6 @@ namespace LEGO.AsyncAPI.Tests public class AsyncApiReference_Should { - [Test] public void AsyncApiReference_WithExternalFragmentUriReference_AllowReference() { diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 686ed9b6..e7d7725e 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -263,7 +263,7 @@ public class AsyncApiSchema_Should { ["property6"] = new AsyncApiSchema { - Type = SchemaType.Boolean , + Type = SchemaType.Boolean, }, }, }, diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs index 880694d6..a503b169 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs @@ -59,7 +59,7 @@ public void AsyncApiServer_Serializes() } }, }); - server.Tags.Add(new AsyncApiTag { Name = "mytag1", Description ="description of tag1" }); + server.Tags.Add(new AsyncApiTag { Name = "mytag1", Description = "description of tag1" }); server.Bindings.Add(new KafkaServerBinding { SchemaRegistryUrl = "http://example.com", @@ -68,7 +68,7 @@ public void AsyncApiServer_Serializes() // Act var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - + // Assert actual = actual.MakeLineBreaksEnvironmentNeutral(); expected = expected.MakeLineBreaksEnvironmentNeutral(); From efcf8f0b5cef3821cd3551b413d02269f6f40ea8 Mon Sep 17 00:00:00 2001 From: James Thompson Date: Sat, 30 Mar 2024 21:59:58 +1100 Subject: [PATCH 54/84] chore: add net 6 as TFM to make STJ conditional (#163) --- Common.Build.props | 2 +- src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Common.Build.props b/Common.Build.props index f4f9797f..78aa4707 100644 --- a/Common.Build.props +++ b/Common.Build.props @@ -2,7 +2,7 @@ 10 - netstandard2.0 + netstandard2.0;net6 disable The LEGO Group https://github.com/LEGO/AsyncAPI.NET diff --git a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj index c26d0242..3bfe1c9c 100644 --- a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj +++ b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj @@ -19,24 +19,21 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + <_Parameter1>$(MSBuildProjectName).Tests - + - - True - \ - + - + - + True @@ -44,7 +41,7 @@ Resource.resx - + ResXFileCodeGenerator From 01994205ecde4e17317762374b03ec23aad17022 Mon Sep 17 00:00:00 2001 From: Byron Mayne Date: Sat, 30 Mar 2024 16:11:37 -0400 Subject: [PATCH 55/84] feat: add cultureinfo to reader/writer settings. (#152) Co-authored-by: Alex Wichmann --- .../AsyncApiJsonDocumentReader.cs | 10 +- .../AsyncApiReaderSettings.cs | 2 +- .../AsyncApiTextReader.cs | 11 +- ...AsyncApiUnsupportedSpecVersionException.cs | 8 +- src/LEGO.AsyncAPI.Readers/JsonHelper.cs | 23 -- .../ParseNodes/MapNode.cs | 13 +- .../ParseNodes/ValueNode.cs | 6 +- src/LEGO.AsyncAPI.Readers/ParsingContext.cs | 21 ++ .../V2/AsyncApiSchemaDeserializer.cs | 18 +- src/LEGO.AsyncAPI.Readers/YamlConverter.cs | 26 +- src/LEGO.AsyncAPI/AsyncApiSettings.cs | 31 ++ .../Models/AsyncApiSerializableExtensions.cs | 42 ++- .../Writers/AsyncApiWriterBase.cs | 4 +- .../Writers/AsyncApiWriterSettings.cs | 36 +- .../Writers/AsyncApiYamlWriter.cs | 145 +++++++- .../SpecialCharacterStringExtensions.cs | 190 +---------- .../AsyncApiDocumentV2Tests.cs | 29 +- .../AsyncApiLicenseTests.cs | 11 +- .../Bindings/AMQP/AMQPBindings_Should.cs | 20 +- .../Bindings/CustomBinding_Should.cs | 8 +- .../Bindings/Http/HttpBindings_Should.cs | 12 +- .../Bindings/Kafka/KafkaBindings_Should.cs | 22 +- .../Bindings/Pulsar/PulsarBindings_Should.cs | 24 +- .../Bindings/Sns/SnsBindings_Should.cs | 12 +- .../Bindings/Sqs/SqsBindings_should.cs | 10 +- .../Bindings/StringOrStringList_Should.cs | 14 +- .../WebSockets/WebSocketBindings_Should.cs | 8 +- .../FluentAssertionExtensions.cs | 91 +++++ .../LEGO.AsyncAPI.Tests.csproj | 27 +- .../MQTT/MQTTBindings_Should.cs | 15 +- .../Models/AsyncApiChannel_Should.cs | 15 +- .../Models/AsyncApiMessage_Should.cs | 22 +- .../Models/AsyncApiOperation_Should.cs | 29 +- .../Models/AsyncApiReference_Should.cs | 47 ++- .../Models/AsyncApiSchema_Should.cs | 318 ++---------------- .../Models/AsyncApiServer_Should.cs | 15 +- .../Serialization/AsyncApiYamlWriterTests.cs | 153 +++++++++ test/LEGO.AsyncAPI.Tests/StringExtensions.cs | 16 - test/LEGO.AsyncAPI.Tests/TestBase.cs | 81 +++++ .../AsyncApiSchema_InlinedReferences.yml | 27 ++ .../AsyncApiSchema_NoInlinedReferences.yml | 34 ++ .../Deserialize_WithAdvancedSchema_Works.json | 82 +++++ ...Json_WithAdvancedSchemaObject_V2Works.json | 82 +++++ ...n_WithAdvancedSchemaWithAllOf_V2Works.json | 38 +++ 44 files changed, 1061 insertions(+), 787 deletions(-) delete mode 100644 src/LEGO.AsyncAPI.Readers/JsonHelper.cs create mode 100644 src/LEGO.AsyncAPI/AsyncApiSettings.cs create mode 100644 test/LEGO.AsyncAPI.Tests/FluentAssertionExtensions.cs create mode 100644 test/LEGO.AsyncAPI.Tests/Serialization/AsyncApiYamlWriterTests.cs delete mode 100644 test/LEGO.AsyncAPI.Tests/StringExtensions.cs create mode 100644 test/LEGO.AsyncAPI.Tests/TestBase.cs create mode 100644 test/LEGO.AsyncAPI.Tests/TestData/AsyncApiSchema_InlinedReferences.yml create mode 100644 test/LEGO.AsyncAPI.Tests/TestData/AsyncApiSchema_NoInlinedReferences.yml create mode 100644 test/LEGO.AsyncAPI.Tests/TestData/Deserialize_WithAdvancedSchema_Works.json create mode 100644 test/LEGO.AsyncAPI.Tests/TestData/SerializeAsJson_WithAdvancedSchemaObject_V2Works.json create mode 100644 test/LEGO.AsyncAPI.Tests/TestData/SerializeAsJson_WithAdvancedSchemaWithAllOf_V2Works.json diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs index aca50c17..3a973654 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs @@ -22,9 +22,9 @@ internal class AsyncApiJsonDocumentReader : IAsyncApiReader - /// Create stream reader with custom settings if desired. + /// Initializes a new instance of the class. /// - /// + /// The settings used to read json. public AsyncApiJsonDocumentReader(AsyncApiReaderSettings settings = null) { this.settings = settings ?? new AsyncApiReaderSettings(); @@ -39,7 +39,7 @@ public AsyncApiJsonDocumentReader(AsyncApiReaderSettings settings = null) public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic) { diagnostic = new AsyncApiDiagnostic(); - var context = new ParsingContext(diagnostic) + var context = new ParsingContext(diagnostic, this.settings) { ExtensionParsers = this.settings.ExtensionParsers, ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), @@ -80,7 +80,7 @@ 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) + var context = new ParsingContext(diagnostic, this.settings) { ExtensionParsers = this.settings.ExtensionParsers, }; @@ -130,7 +130,7 @@ public T ReadFragment(JsonNode input, AsyncApiVersion version, out AsyncApiDi where T : IAsyncApiElement { diagnostic = new AsyncApiDiagnostic(); - var context = new ParsingContext(diagnostic) + var context = new ParsingContext(diagnostic, this.settings) { ExtensionParsers = this.settings.ExtensionParsers, ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b), diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs index 2e715958..6ca2e129 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs @@ -26,7 +26,7 @@ public enum ReferenceResolutionSetting /// /// Configuration settings to control how AsyncApi documents are parsed. /// - public class AsyncApiReaderSettings + public class AsyncApiReaderSettings : AsyncApiSettings { /// /// Indicates how references in the source document should be handled. diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs index 64587e46..eeece6a7 100644 --- a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs +++ b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs @@ -4,6 +4,7 @@ namespace LEGO.AsyncAPI.Readers { using System.IO; using System.Linq; + using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Nodes; using System.Threading; @@ -42,7 +43,7 @@ public AsyncApiDocument Read(TextReader input, out AsyncApiDiagnostic diagnostic // Parse the YAML/JSON text in the TextReader into the YamlDocument try { - jsonNode = LoadYamlDocument(input); + jsonNode = LoadYamlDocument(input, this.settings); } catch (JsonException ex) { @@ -69,7 +70,7 @@ public async Task ReadAsync(TextReader input, CancellationToken canc // Parse the YAML/JSON text in the TextReader into the YamlDocument try { - jsonNode = LoadYamlDocument(input); + jsonNode = LoadYamlDocument(input, this.settings); } catch (JsonException ex) { @@ -100,7 +101,7 @@ public T ReadFragment(TextReader input, AsyncApiVersion version, out AsyncApi // Parse the YAML/JSON try { - jsonNode = LoadYamlDocument(input); + jsonNode = LoadYamlDocument(input, this.settings); } catch (JsonException ex) { @@ -118,11 +119,11 @@ public T ReadFragment(TextReader input, AsyncApiVersion version, out AsyncApi /// /// Stream containing YAML formatted text. /// Instance of a YamlDocument. - static JsonNode LoadYamlDocument(TextReader input) + static JsonNode LoadYamlDocument(TextReader input, AsyncApiReaderSettings settings) { var yamlStream = new YamlStream(); yamlStream.Load(input); - return yamlStream.Documents.First().ToJsonNode(); + return yamlStream.Documents.First().ToJsonNode(settings); } } } diff --git a/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiUnsupportedSpecVersionException.cs b/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiUnsupportedSpecVersionException.cs index ac183421..d68da4bf 100644 --- a/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiUnsupportedSpecVersionException.cs +++ b/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiUnsupportedSpecVersionException.cs @@ -11,12 +11,13 @@ namespace LEGO.AsyncAPI.Readers.Exceptions [Serializable] public class AsyncApiUnsupportedSpecVersionException : Exception { - const string MessagePattern = "AsyncApi specification version '{0}' is not supported."; + private const string MessagePattern = "AsyncApi specification version '{0}' is not supported."; /// - /// Initializes the class with a specification version. + /// Initializes a new instance of the class. /// /// Version that caused this exception to be thrown. + /// The settings used for reading and writing. public AsyncApiUnsupportedSpecVersionException(string specificationVersion) : base(string.Format(CultureInfo.InvariantCulture, MessagePattern, specificationVersion)) { @@ -24,10 +25,11 @@ public AsyncApiUnsupportedSpecVersionException(string specificationVersion) } /// - /// Initializes the class with a specification version and + /// Initializes a new instance of the class. /// inner exception. /// /// Version that caused this exception to be thrown. + /// The setting used for reading and writing /// Inner exception that caused this exception to be thrown. public AsyncApiUnsupportedSpecVersionException(string specificationVersion, Exception innerException) : base(string.Format(CultureInfo.InvariantCulture, MessagePattern, specificationVersion), innerException) diff --git a/src/LEGO.AsyncAPI.Readers/JsonHelper.cs b/src/LEGO.AsyncAPI.Readers/JsonHelper.cs deleted file mode 100644 index 5f7fc584..00000000 --- a/src/LEGO.AsyncAPI.Readers/JsonHelper.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Readers -{ - using System; - using System.Globalization; - using System.Text.Json.Nodes; - using LEGO.AsyncAPI.Exceptions; - - internal static class JsonHelper - { - public static string GetScalarValue(this JsonNode node) - { - var scalarNode = node is JsonValue value ? value : throw new AsyncApiException($"Expected scalar value"); - return Convert.ToString(scalarNode.GetValue(), CultureInfo.InvariantCulture); - } - - public static JsonNode ParseJsonString(string jsonString) - { - return JsonNode.Parse(jsonString); - } - } -} diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs index a9bf1c48..b087c875 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs @@ -8,6 +8,7 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes using System.Linq; using System.Text.Json; using System.Text.Json.Nodes; + using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Readers.Exceptions; @@ -18,7 +19,7 @@ public class MapNode : ParseNode, IEnumerable private readonly List nodes; public MapNode(ParsingContext context, string jsonString) - : this(context, JsonHelper.ParseJsonString(jsonString)) + : this(context, JsonNode.Parse(jsonString)) { } @@ -196,7 +197,7 @@ public string GetReferencePointer() return null; } - return refNode.GetScalarValue(); + return this.ToScalarValue(refNode); } public string GetScalarValue(ValueNode key) @@ -205,12 +206,18 @@ public string GetScalarValue(ValueNode key) ? jsonValue : throw new AsyncApiReaderException($"Expected scalar value while parsing {key.GetScalarValue()}", this.Context); - return scalarNode.GetScalarValue(); + return this.ToScalarValue(scalarNode); } public override AsyncApiAny CreateAny() { return new AsyncApiAny(this.node); } + + private string ToScalarValue(JsonNode node) + { + var scalarNode = node is JsonValue value ? value : throw new AsyncApiException($"Expected scalar value"); + return Convert.ToString(scalarNode.GetValue(), this.Context.Settings.CultureInfo); + } } } diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs index e580afe6..201ab6f7 100644 --- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs +++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs @@ -2,8 +2,10 @@ namespace LEGO.AsyncAPI.Readers.ParseNodes { + using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers.Exceptions; + using System; using System.Text.Json.Nodes; public class ValueNode : ParseNode @@ -26,7 +28,9 @@ public override string GetScalarValue() { if (this.cachedScalarValue == null) { - this.cachedScalarValue = this.node.GetScalarValue(); + // TODO: Update this property to use the .ToString() or JsonReader. + var scalarNode = this.node is JsonValue value ? value : throw new AsyncApiException($"Expected scalar value"); + this.cachedScalarValue = Convert.ToString(scalarNode.GetValue(), this.Context.Settings.CultureInfo); } return this.cachedScalarValue; diff --git a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs index 812a2fe2..9bcc051b 100644 --- a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs +++ b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs @@ -39,9 +39,30 @@ internal Dictionary> ExtensionPars public AsyncApiDiagnostic Diagnostic { get; } + /// + /// Gets the settings used fore reading json. + /// + public AsyncApiReaderSettings Settings { get; } + + ///// + ///// Initializes a new instance of the class. + ///// + /// The diagnostics. + [Obsolete($"Please use the overloaded version that takes in an instance of {nameof(AsyncApiReaderSettings)} isntead.")] public ParsingContext(AsyncApiDiagnostic diagnostic) + : this(diagnostic, new AsyncApiReaderSettings()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The diagnostics. + /// The settings used to read json. + public ParsingContext(AsyncApiDiagnostic diagnostic, AsyncApiReaderSettings settings) { this.Diagnostic = diagnostic; + this.Settings = settings; } internal AsyncApiDocument Parse(JsonNode jsonNode) diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index 04c51f52..b99fdef8 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -50,14 +50,14 @@ public class JsonSchemaDeserializer "multipleOf", (a, n) => { - a.MultipleOf = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + a.MultipleOf = double.Parse(n.GetScalarValue(), NumberStyles.Float, n.Context.Settings.CultureInfo); } }, { "maximum", (a, n) => { - a.Maximum = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + a.Maximum = double.Parse(n.GetScalarValue(), NumberStyles.Float, n.Context.Settings.CultureInfo); } }, { @@ -67,37 +67,37 @@ public class JsonSchemaDeserializer "minimum", (a, n) => { - a.Minimum = double.Parse(n.GetScalarValue(), NumberStyles.Float, CultureInfo.InvariantCulture); + a.Minimum = double.Parse(n.GetScalarValue(), NumberStyles.Float, n.Context.Settings.CultureInfo); } }, { "exclusiveMinimum", (a, n) => { a.ExclusiveMinimum = bool.Parse(n.GetScalarValue()); } }, { - "maxLength", (a, n) => { a.MaxLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } + "maxLength", (a, n) => { a.MaxLength = int.Parse(n.GetScalarValue(), n.Context.Settings.CultureInfo); } }, { - "minLength", (a, n) => { a.MinLength = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } + "minLength", (a, n) => { a.MinLength = int.Parse(n.GetScalarValue(), n.Context.Settings.CultureInfo); } }, { "pattern", (a, n) => { a.Pattern = n.GetScalarValue(); } }, { - "maxItems", (a, n) => { a.MaxItems = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } + "maxItems", (a, n) => { a.MaxItems = int.Parse(n.GetScalarValue(), n.Context.Settings.CultureInfo); } }, { - "minItems", (a, n) => { a.MinItems = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } + "minItems", (a, n) => { a.MinItems = int.Parse(n.GetScalarValue(), n.Context.Settings.CultureInfo); } }, { "uniqueItems", (a, n) => { a.UniqueItems = bool.Parse(n.GetScalarValue()); } }, { "maxProperties", - (a, n) => { a.MaxProperties = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } + (a, n) => { a.MaxProperties = int.Parse(n.GetScalarValue(), n.Context.Settings.CultureInfo); } }, { "minProperties", - (a, n) => { a.MinProperties = int.Parse(n.GetScalarValue(), CultureInfo.InvariantCulture); } + (a, n) => { a.MinProperties = int.Parse(n.GetScalarValue(), n.Context.Settings.CultureInfo); } }, { "enum", (a, n) => { a.Enum = n.CreateListOfAny(); } diff --git a/src/LEGO.AsyncAPI.Readers/YamlConverter.cs b/src/LEGO.AsyncAPI.Readers/YamlConverter.cs index 295fb573..648ee50a 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) + public static JsonNode ToJsonNode(this YamlDocument yamlDocument, AsyncApiReaderSettings settings) { - return yamlDocument.RootNode.ToJsonNode(); + return yamlDocument.RootNode.ToJsonNode(settings); } - public static JsonObject ToJsonObject(this YamlMappingNode yamlMappingNode) + public static JsonObject ToJsonObject(this YamlMappingNode yamlMappingNode, AsyncApiReaderSettings settings) { var node = new JsonObject(); foreach (var keyValuePair in yamlMappingNode) { var key = ((YamlScalarNode)keyValuePair.Key).Value!; - node[key] = keyValuePair.Value.ToJsonNode(); + node[key] = keyValuePair.Value.ToJsonNode(settings); } return node; } - public static JsonArray ToJsonArray(this YamlSequenceNode yaml) + public static JsonArray ToJsonArray(this YamlSequenceNode yaml, AsyncApiReaderSettings settings) { var node = new JsonArray(); foreach (var value in yaml) { - node.Add(value.ToJsonNode()); + node.Add(value.ToJsonNode(settings)); } return node; } - public static JsonNode ToJsonNode(this YamlNode yaml) + public static JsonNode ToJsonNode(this YamlNode yaml, AsyncApiReaderSettings settings) { return yaml switch { - YamlMappingNode map => map.ToJsonObject(), - YamlSequenceNode seq => seq.ToJsonArray(), - YamlScalarNode scalar => scalar.ToJsonValue(), + YamlMappingNode map => map.ToJsonObject(settings), + YamlSequenceNode seq => seq.ToJsonArray(settings), + YamlScalarNode scalar => scalar.ToJsonValue(settings), _ => throw new NotSupportedException("This yaml isn't convertible to JSON"), }; } - private static JsonValue ToJsonValue(this YamlScalarNode yaml) + private static JsonValue ToJsonValue(this YamlScalarNode yaml, AsyncApiReaderSettings settings) { switch (yaml.Style) { case ScalarStyle.Plain: - return decimal.TryParse(yaml.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var d) + return decimal.TryParse(yaml.Value, NumberStyles.Float, settings.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/AsyncApiSettings.cs b/src/LEGO.AsyncAPI/AsyncApiSettings.cs new file mode 100644 index 00000000..ba9e0189 --- /dev/null +++ b/src/LEGO.AsyncAPI/AsyncApiSettings.cs @@ -0,0 +1,31 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI +{ + using System.Globalization; + + /// + /// Base class for setting common acorss the various projects in the solution. + /// + public abstract class AsyncApiSettings + { + /// + /// Initializes a new instance of the class. + /// + protected AsyncApiSettings() + { + this.DateTimeFormat = "yyyy-MM-ddTHH:mm:ss.fffzzz"; + this.CultureInfo = CultureInfo.InvariantCulture; + } + + /// + /// Gets the format used for reading and writing date time structures. + /// + public string DateTimeFormat { get; } + + /// + /// Gets the culture info used for strings. + /// + public CultureInfo CultureInfo { get; } + } +} diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs index eea342c4..624cfdba 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs @@ -3,12 +3,14 @@ namespace LEGO.AsyncAPI.Models { using System; - using System.Globalization; using System.IO; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; + /// + /// Contains extension methods for working with async api elements. + /// public static class AsyncApiSerializableExtensions { /// @@ -21,7 +23,21 @@ public static class AsyncApiSerializableExtensions public static void SerializeAsJson(this T element, Stream stream, AsyncApiVersion specificationVersion) where T : IAsyncApiSerializable { - element.Serialize(stream, specificationVersion, AsyncApiFormat.Json); + element.SerializeAsJson(stream, specificationVersion, AsyncApiWriterSettings.Default); + } + + /// + /// Serialize the to the AsyncApi document (JSON) using the given stream and specification version. + /// + /// the . + /// The AsyncApi element. + /// The output stream. + /// The AsyncApi specification version. + /// The settings used for writing + public static void SerializeAsJson(this T element, Stream stream, AsyncApiVersion specificationVersion, AsyncApiWriterSettings settings) + where T : IAsyncApiSerializable + { + element.Serialize(stream, specificationVersion, AsyncApiFormat.Json, settings); } /// @@ -34,7 +50,21 @@ public static void SerializeAsJson(this T element, Stream stream, AsyncApiVer public static void SerializeAsYaml(this T element, Stream stream, AsyncApiVersion specificationVersion) where T : IAsyncApiSerializable { - element.Serialize(stream, specificationVersion, AsyncApiFormat.Yaml); + element.SerializeAsYaml(stream, specificationVersion, AsyncApiWriterSettings.Default); + } + + /// + /// Serializes the to the AsyncApi document (YAML) using the given stream and specification version. + /// + /// the . + /// The AsyncApi element. + /// The output stream. + /// The AsyncApi specification version. + /// The settings used for writing + public static void SerializeAsYaml(this T element, Stream stream, AsyncApiVersion specificationVersion, AsyncApiWriterSettings settings) + where T : IAsyncApiSerializable + { + element.Serialize(stream, specificationVersion, AsyncApiFormat.Yaml, settings); } /// @@ -44,7 +74,7 @@ public static void SerializeAsYaml(this T element, Stream stream, AsyncApiVer /// the . /// The AsyncApi element. /// The given stream. - /// The AsyncApi specification version. + /// The AsyncApi specification version. /// The output format (JSON or YAML). public static void Serialize( this T element, @@ -53,7 +83,7 @@ public static void Serialize( AsyncApiFormat format) where T : IAsyncApiSerializable { - element.Serialize(stream, specificationVersion, format, null); + element.Serialize(stream, specificationVersion, format, new AsyncApiWriterSettings()); } /// @@ -79,7 +109,7 @@ public static void Serialize( throw new ArgumentNullException(nameof(stream)); } - var streamWriter = new FormattingStreamWriter(stream, CultureInfo.InvariantCulture); + var streamWriter = new FormattingStreamWriter(stream, settings.CultureInfo); IAsyncApiWriter writer = format switch { diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs index 471db021..7cc6ee02 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterBase.cs @@ -177,7 +177,7 @@ public virtual void WriteValue(long value) /// The DateTime value. public virtual void WriteValue(DateTime value) { - this.WriteValue(value.ToString("o")); + this.WriteValue(value.ToString(this.Settings.DateTimeFormat, this.Settings.CultureInfo)); } /// @@ -186,7 +186,7 @@ public virtual void WriteValue(DateTime value) /// The DateTimeOffset value. public virtual void WriteValue(DateTimeOffset value) { - this.WriteValue(value.ToString("o")); + this.WriteValue(value.ToString(this.Settings.DateTimeFormat, this.Settings.CultureInfo)); } /// diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs index b3f061e3..2b663bb2 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterSettings.cs @@ -4,11 +4,31 @@ namespace LEGO.AsyncAPI.Writers { using LEGO.AsyncAPI.Models; - public class AsyncApiWriterSettings + /// + /// Contains settings for writing async api. + /// + public class AsyncApiWriterSettings : AsyncApiSettings { private ReferenceInlineSetting referenceInline = ReferenceInlineSetting.DoNotInlineReferences; - internal LoopDetector LoopDetector { get; } = new LoopDetector(); + static AsyncApiWriterSettings() + { + Default = new AsyncApiWriterSettings(); + } + + /// + /// Initializes a new instance of the class. + /// + public AsyncApiWriterSettings() + { + this.InlineReferences = false; + this.LoopDetector = new LoopDetector(); + } + + /// + /// Gets the default settings to use for writing async api. + /// + public static AsyncApiWriterSettings Default { get; } /// /// Gets or sets indicates how references in the source document should be handled. @@ -38,8 +58,18 @@ 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; } = false; + public bool InlineReferences { get; set; } + /// + /// Figures out if a loop exists. + /// + internal LoopDetector LoopDetector { get; } + + /// + /// Returns back if the refernece should be inlined or not. + /// + /// The refernece. + /// True if it should be inlined otherwise false. public bool ShouldInlineReference(AsyncApiReference reference) { return this.InlineReferences; diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs index 5749e0d1..d0fab76e 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiYamlWriter.cs @@ -1,17 +1,40 @@ // Copyright (c) The LEGO Group. All rights reserved. +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; + namespace LEGO.AsyncAPI.Writers { - using System.IO; - + /// + /// Used to conver an AsyncApi schema into a yaml document. + /// public class AsyncApiYamlWriter : AsyncApiWriterBase { + private static readonly Regex YamlNumberRegex; + private static readonly char[] YamlIndicators; + private static readonly string[] YamlPlainStringForbiddenCobinations; + private static readonly string[] YamlPlainStringForbiddenTerminals; + private static readonly char[] YamlControlCharacters; + + static AsyncApiYamlWriter() + { + YamlNumberRegex = new Regex("^[+-]?[0-9]*\\.?[0-9]*$", RegexOptions.Compiled); + YamlIndicators = new char[] { '-', '?', ':', ',', '{', '}', '[', ']', '&', '*', '#', '?', '|', '-', '>', '!', '%', '@', '`', '\'', '"', }; + YamlPlainStringForbiddenCobinations = new string[] { ": ", " #", "[", "]", "{", "}", ",", }; + YamlPlainStringForbiddenTerminals = new string[] { ":" }; + 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, null) + : this(textWriter, new AsyncApiWriterSettings()) { } @@ -19,7 +42,7 @@ public AsyncApiYamlWriter(TextWriter textWriter) /// Initializes a new instance of the class. /// /// The text writer. - /// + /// The settings used to read and write yaml public AsyncApiYamlWriter(TextWriter textWriter, AsyncApiWriterSettings settings) : base(textWriter, settings) { @@ -153,7 +176,7 @@ public override void WritePropertyName(string name) this.WriteIndentation(); } - name = name.GetYamlCompatibleString(); + name = this.GetYamlCompatibleString(name); this.Writer.Write(name); this.Writer.Write(":"); @@ -171,7 +194,7 @@ public override void WriteValue(string value) { this.WriteValueSeparator(); - value = value.GetYamlCompatibleString(); + value = this.GetYamlCompatibleString(value); this.Writer.Write(value); } @@ -196,7 +219,7 @@ public override void WriteValue(string value) this.IncreaseIndentation(); - using (var reader = new StringReader(value)) + using (StringReader reader = new(value)) { bool firstLine = true; while (reader.ReadLine() is var line && line != null) @@ -316,5 +339,113 @@ public override void WriteRaw(string value) this.WriteValueSeparator(); this.Writer.Write(value); } + + /// + /// Escapes all special characters and put the string in quotes if necessary to + /// get a YAML-compatible string. + /// + /// The string to turn into yaml. + /// The string as yaml. + internal string GetYamlCompatibleString(string input) + { + if (input == null) + { + return "null"; + } + + switch (input.ToLower()) + { + case "": + return "''"; + + case "~": + // Example 2.20. Floating Point + case "-.inf": + case ".inf": + case ".nan": + // Example 2.21. Miscellaneous + case "null": + + // Booleans + case "true": + case "false": + return $"'{input}'"; + } + + // If string includes a control character, wrapping in double quote is required. + if (input.Any(c => YamlControlCharacters.Contains(c))) + { + // Replace the backslash first, so that the new backslashes created by other Replaces are not duplicated. + input = input.Replace("\\", "\\\\"); + + // Escape the double quotes. + input = input.Replace("\"", "\\\""); + + // Escape all the control characters. + input = input.Replace("\0", "\\0"); + input = input.Replace("\x01", "\\x01"); + input = input.Replace("\x02", "\\x02"); + input = input.Replace("\x03", "\\x03"); + input = input.Replace("\x04", "\\x04"); + input = input.Replace("\x05", "\\x05"); + input = input.Replace("\x06", "\\x06"); + input = input.Replace("\a", "\\a"); + input = input.Replace("\b", "\\b"); + input = input.Replace("\t", "\\t"); + input = input.Replace("\n", "\\n"); + input = input.Replace("\v", "\\v"); + input = input.Replace("\f", "\\f"); + input = input.Replace("\r", "\\r"); + input = input.Replace("\x0e", "\\x0e"); + input = input.Replace("\x0f", "\\x0f"); + input = input.Replace("\x10", "\\x10"); + input = input.Replace("\x11", "\\x11"); + input = input.Replace("\x12", "\\x12"); + input = input.Replace("\x13", "\\x13"); + input = input.Replace("\x14", "\\x14"); + input = input.Replace("\x15", "\\x15"); + input = input.Replace("\x16", "\\x16"); + input = input.Replace("\x17", "\\x17"); + input = input.Replace("\x18", "\\x18"); + input = input.Replace("\x19", "\\x19"); + input = input.Replace("\x1a", "\\x1a"); + input = input.Replace("\x1b", "\\x1b"); + input = input.Replace("\x1c", "\\x1c"); + input = input.Replace("\x1d", "\\x1d"); + input = input.Replace("\x1e", "\\x1e"); + input = input.Replace("\x1f", "\\x1f"); + + return $"\"{input}\""; + } + + // If string + // 1) includes a character forbidden in plain string, + // 2) starts with an indicator, OR + // 3) has trailing/leading white spaces, + // wrap the string in single quote. + // http://www.yaml.org/spec/1.2/spec.html#style/flow/plain + if (YamlPlainStringForbiddenCobinations.Any(fc => input.Contains(fc)) || + YamlIndicators.Any(i => input.StartsWith(i.ToString())) || + YamlPlainStringForbiddenTerminals.Any(i => input.EndsWith(i.ToString())) || + input.Trim() != input) + { + // Escape single quotes with two single quotes. + input = input.Replace("'", "''"); + + return $"'{input}'"; + } + + // If string can be mistaken as a number, a boolean, or a timestamp, + // wrap it in quot number, a boolean, or a timestamp + if (decimal.TryParse(input, NumberStyles.Float, this.Settings.CultureInfo, out decimal _) || + bool.TryParse(input, out bool _) || + DateTime.TryParseExact(input, this.Settings.DateTimeFormat, this.Settings.CultureInfo, DateTimeStyles.RoundtripKind, out DateTime _)) + { + return $"'{input}'"; + } + + // Handle numbers + return YamlNumberRegex.IsMatch(input) ? $"'{input}'" : input; + } } } diff --git a/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs b/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs index 1a091066..f2f6fbe2 100644 --- a/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/SpecialCharacterStringExtensions.cs @@ -1,200 +1,14 @@ -// Copyright (c) The LEGO Group. All rights reserved. +// Copyright (c) The LEGO Group. All rights reserved. namespace LEGO.AsyncAPI.Writers { using System; using System.Globalization; using System.Linq; + using System.Text.RegularExpressions; public static class SpecialCharacterStringExtensions { - // Plain style strings cannot start with indicators. - // http://www.yaml.org/spec/1.2/spec.html#indicator// - private static readonly char[] yamlIndicators = - { - '-', - '?', - ':', - ',', - '{', - '}', - '[', - ']', - '&', - '*', - '#', - '?', - '|', - '-', - '>', - '!', - '%', - '@', - '`', - '\'', - '"', - }; - - // Plain style strings cannot contain these character combinations. - // http://www.yaml.org/spec/1.2/spec.html#style/flow/plain - private static readonly string[] yamlPlainStringForbiddenCombinations = - { - ": ", - " #", - - // These are technically forbidden only inside flow collections, but - // for the sake of simplicity, we will never allow them in our generated plain string. - "[", - "]", - "{", - "}", - ",", - }; - - // Plain style strings cannot end with these characters. - // http://www.yaml.org/spec/1.2/spec.html#style/flow/plain - private static readonly string[] yamlPlainStringForbiddenTerminals = - { - ":", - }; - - // Double-quoted strings are needed for these non-printable control characters. - // http://www.yaml.org/spec/1.2/spec.html#style/flow/double-quoted - private static readonly char[] yamlControlCharacters = - { - '\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', - }; - - /// - /// Escapes all special characters and put the string in quotes if necessary to - /// get a YAML-compatible string. - /// - internal static string GetYamlCompatibleString(this string input) - { - // If string is an empty string, wrap it in quote to ensure it is not recognized as null. - if (input == "") - { - return "''"; - } - - // If string is the word null, wrap it in quote to ensure it is not recognized as empty scalar null. - if (input == "null") - { - return "'null'"; - } - - // If string is the letter ~, wrap it in quote to ensure it is not recognized as empty scalar null. - if (input == "~") - { - return "'~'"; - } - - // If string includes a control character, wrapping in double quote is required. - if (input.Any(c => yamlControlCharacters.Contains(c))) - { - // Replace the backslash first, so that the new backslashes created by other Replaces are not duplicated. - input = input.Replace("\\", "\\\\"); - - // Escape the double quotes. - input = input.Replace("\"", "\\\""); - - // Escape all the control characters. - input = input.Replace("\0", "\\0"); - input = input.Replace("\x01", "\\x01"); - input = input.Replace("\x02", "\\x02"); - input = input.Replace("\x03", "\\x03"); - input = input.Replace("\x04", "\\x04"); - input = input.Replace("\x05", "\\x05"); - input = input.Replace("\x06", "\\x06"); - input = input.Replace("\a", "\\a"); - input = input.Replace("\b", "\\b"); - input = input.Replace("\t", "\\t"); - input = input.Replace("\n", "\\n"); - input = input.Replace("\v", "\\v"); - input = input.Replace("\f", "\\f"); - input = input.Replace("\r", "\\r"); - input = input.Replace("\x0e", "\\x0e"); - input = input.Replace("\x0f", "\\x0f"); - input = input.Replace("\x10", "\\x10"); - input = input.Replace("\x11", "\\x11"); - input = input.Replace("\x12", "\\x12"); - input = input.Replace("\x13", "\\x13"); - input = input.Replace("\x14", "\\x14"); - input = input.Replace("\x15", "\\x15"); - input = input.Replace("\x16", "\\x16"); - input = input.Replace("\x17", "\\x17"); - input = input.Replace("\x18", "\\x18"); - input = input.Replace("\x19", "\\x19"); - input = input.Replace("\x1a", "\\x1a"); - input = input.Replace("\x1b", "\\x1b"); - input = input.Replace("\x1c", "\\x1c"); - input = input.Replace("\x1d", "\\x1d"); - input = input.Replace("\x1e", "\\x1e"); - input = input.Replace("\x1f", "\\x1f"); - - return $"\"{input}\""; - } - - // If string - // 1) includes a character forbidden in plain string, - // 2) starts with an indicator, OR - // 3) has trailing/leading white spaces, - // wrap the string in single quote. - // http://www.yaml.org/spec/1.2/spec.html#style/flow/plain - if (yamlPlainStringForbiddenCombinations.Any(fc => input.Contains(fc)) || - yamlIndicators.Any(i => input.StartsWith(i.ToString())) || - yamlPlainStringForbiddenTerminals.Any(i => input.EndsWith(i.ToString())) || - input.Trim() != input) - { - // Escape single quotes with two single quotes. - input = input.Replace("'", "''"); - - return $"'{input}'"; - } - - // If string can be mistaken as a number, a boolean, or a timestamp, - // wrap it in quote to indicate that this is indeed a string, not a number, a boolean, or a timestamp - if (decimal.TryParse(input, NumberStyles.Float, CultureInfo.InvariantCulture, out var _) || - bool.TryParse(input, out var _) || - DateTime.TryParse(input, out var _)) - { - return $"'{input}'"; - } - - return input; - } - /// /// Handles control characters and backslashes and adds double quotes /// to get JSON-compatible string. diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index d73125fb..6368db66 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -7,6 +7,7 @@ namespace LEGO.AsyncAPI.Tests using System.Globalization; using System.IO; using System.Linq; + using FluentAssertions; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Bindings.Kafka; @@ -24,14 +25,14 @@ public class ExtensionClass public long OtherKey { get; set; } } - public class AsyncApiDocumentV2Tests + public class AsyncApiDocumentV2Tests : TestBase { [Test] public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() { // Arrange var expected = -@"asyncapi: '2.6.0' +@"asyncapi: 2.6.0 info: title: Streetlights Kafka API version: 1.0.0 @@ -694,18 +695,16 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() // Act var actual = asyncApiDocument.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] public void SerializeV2_WithFullSpec_Serializes() { var expected = - @"asyncapi: '2.6.0' + @"asyncapi: 2.6.0 info: title: apiTitle version: apiVersion @@ -1117,18 +1116,16 @@ public void SerializeV2_WithFullSpec_Serializes() }, }; - var outputString = new StringWriter(CultureInfo.InvariantCulture); + var outputString = new StringWriter(); var writer = new AsyncApiYamlWriter(outputString); // Act document.SerializeV2(writer); var actual = outputString.GetStringBuilder().ToString(); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -1222,7 +1219,7 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() [Test] public void Serializev2_WithBindings_Serializes() { - var expected = @"asyncapi: '2.6.0' + var expected = @"asyncapi: 2.6.0 info: description: test description servers: @@ -1311,11 +1308,9 @@ public void Serializev2_WithBindings_Serializes() var reader = new AsyncApiStringReader(settings); var deserialized = reader.Read(actual, out var diagnostic); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); Assert.AreEqual(2, deserialized.Channels.First().Value.Publish.Message.First().Bindings.Count); var binding = deserialized.Channels.First().Value.Publish.Message.First().Bindings.First(); diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs index 44fbad93..d436dc1e 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Tests using LEGO.AsyncAPI.Readers.ParseNodes; using NUnit.Framework; - public class AsyncApiLicenseTests + public class AsyncApiLicenseTests : TestBase { [Test] public void Serialize_WithAllProperties_Serializes() @@ -36,10 +36,8 @@ public void Serialize_WithAllProperties_Serializes() var actual = license.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } public static Stream GenerateStreamFromString(string s) @@ -65,7 +63,8 @@ public void LoadLicense_WithJson_Deserializes() using (var stream = GenerateStreamFromString(input)) { var diagnostic = new AsyncApiDiagnostic(); - var context = new ParsingContext(diagnostic); + var settings = new AsyncApiReaderSettings(); + var context = new ParsingContext(diagnostic, settings); var node = new MapNode(context, JsonNode.Parse(stream)); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs index e9178311..7dce6ed3 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs @@ -45,14 +45,13 @@ public void AMQPChannelBinding_WithRoutingKey_SerializesAndDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.AMQP; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(channel); } @@ -89,14 +88,13 @@ public void AMQPChannelBinding_WithQueue_SerializesAndDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.AMQP; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(channel); } @@ -120,14 +118,13 @@ public void AMQPMessageBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.AMQP; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(message); } @@ -166,15 +163,14 @@ public void AMQPOperationBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.AMQP; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(operation); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs index 5dafed7b..8bd56063 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs @@ -62,7 +62,7 @@ public override void SerializeProperties(IAsyncApiWriter writer) } } - public class CustomBinding_Should + public class CustomBinding_Should : TestBase { [Test] public void CustomBinding_SerializesDeserializes() @@ -107,15 +107,13 @@ public void CustomBinding_SerializesDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - var settings = new AsyncApiReaderSettings(); settings.Bindings = new[] { new MyBinding() }; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(channel); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs index b207dcd8..d31c9174 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Http using LEGO.AsyncAPI.Readers; using NUnit.Framework; - internal class HttpBindings_Should + internal class HttpBindings_Should : TestBase { [Test] public void HttpMessageBinding_FilledObject_SerializesAndDeserializes() @@ -33,14 +33,13 @@ public void HttpMessageBinding_FilledObject_SerializesAndDeserializes() // Act var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Http; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(message); } @@ -70,14 +69,13 @@ public void HttpOperationBinding_FilledObject_SerializesAndDeserializes() // Act var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Http; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(operation); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index 842f3c1d..ec41a06a 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -10,7 +10,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Kafka using LEGO.AsyncAPI.Readers; using NUnit.Framework; - internal class KafkaBindings_Should + internal class KafkaBindings_Should : TestBase { [Test] public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() @@ -59,14 +59,13 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Kafka; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(channel); } @@ -96,14 +95,13 @@ public void KafkaServerBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Kafka; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(server); } @@ -135,14 +133,13 @@ public void KafkaMessageBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Kafka; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(message); } @@ -174,15 +171,14 @@ public void KafkaOperationBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Kafka; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(operation); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs index c99bfa6c..26e6ba68 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs @@ -10,7 +10,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Pulsar using LEGO.AsyncAPI.Readers; using NUnit.Framework; - internal class PulsarBindings_Should + internal class PulsarBindings_Should : TestBase { [Test] public void PulsarChannelBinding_WithFilledObject_SerializesAndDeserializes() @@ -56,16 +56,13 @@ public void PulsarChannelBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Pulsar; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(channel); } @@ -136,14 +133,13 @@ public void PulsarServerBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Pulsar; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(server); } @@ -172,14 +168,13 @@ public void ServerBindingVersionDefaultsToNull() // Act var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Pulsar; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings["pulsar"]).BindingVersion); binding.Should().BeEquivalentTo(server); } @@ -209,14 +204,13 @@ public void ServerTenantDefaultsToNull() // Act var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Pulsar; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); Assert.AreEqual(null, ((PulsarServerBinding)binding.Bindings["pulsar"]).Tenant); binding.Should().BeEquivalentTo(server); } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 385ccfd5..e7aaae0b 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -12,7 +12,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Sns using LEGO.AsyncAPI.Readers; using NUnit.Framework; - internal class SnsBindings_Should + internal class SnsBindings_Should : TestBase { [Test] public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() @@ -135,14 +135,13 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Sns; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(actual, expected); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); var expectedSnsBinding = (SnsChannelBinding)channel.Bindings.Values.First(); expectedSnsBinding.Should().BeEquivalentTo((SnsChannelBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); @@ -378,8 +377,6 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Sns; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); @@ -388,7 +385,8 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() var val = AsyncApiAny.FromExtensionOrDefault(any); // Assert - Assert.AreEqual(actual, expected); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); var expectedSnsBinding = (SnsOperationBinding)operation.Bindings.Values.First(); expectedSnsBinding.Should().BeEquivalentTo((SnsOperationBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index 63eeff51..6f2d7ba6 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -216,15 +216,14 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Sqs; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(channel); } @@ -441,14 +440,13 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Sqs; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(operation); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs index 21b64f5e..f9c9c6f7 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings using LEGO.AsyncAPI.Writers; using NUnit.Framework; - public class StringOrStringList_Should + public class StringOrStringList_Should : TestBase { [Test] public void StringOrStringList_IsInitialised_WhenPassedStringOrStringList() @@ -77,15 +77,13 @@ public void StringOrStringList_WhenValueIsString_SerializesDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - var settings = new AsyncApiReaderSettings(); settings.Bindings = new[] { new StringOrStringListTestBinding() }; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(channel); } @@ -115,15 +113,13 @@ public void StringOrStringList_WhenValueIsStringList_SerializesDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - var settings = new AsyncApiReaderSettings(); settings.Bindings = new[] { new StringOrStringListTestBinding() }; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(channel); } } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs index 0e9c5344..67d71ff5 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs @@ -9,7 +9,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings.WebSockets using LEGO.AsyncAPI.Readers; using NUnit.Framework; - internal class WebSocketBindings_Should + internal class WebSocketBindings_Should : TestBase { [Test] public void WebSocketChannelBinding_WithFilledObject_SerializesAndDeserializes() @@ -40,15 +40,15 @@ public void WebSocketChannelBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.Websockets; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); + binding.Should().BeEquivalentTo(channel); } } diff --git a/test/LEGO.AsyncAPI.Tests/FluentAssertionExtensions.cs b/test/LEGO.AsyncAPI.Tests/FluentAssertionExtensions.cs new file mode 100644 index 00000000..31fec68a --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/FluentAssertionExtensions.cs @@ -0,0 +1,91 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests +{ + using System; + using System.IO; + using FluentAssertions; + using FluentAssertions.Primitives; + using NUnit.Framework; + + /// + /// Contains extension methods for working with fluent assertions. + /// + internal static class FluentAssertionExtensions + { + private static readonly char[] SeperatorChars; + + static FluentAssertionExtensions() + { + SeperatorChars = new[] + { + '\r', + '\n', + }; + } + + /// + /// Checks if the string is equal to other be ingores platform spesefic features + /// line new line breaks. This also checks to validate strings that are multiple lines + /// are the same number of lines. + /// + /// The assertion object. + /// The actaul value. + public static void BePlatformAgnosticEquivalentTo( + this StringAssertions assertions, + string input) + { + TestContext context = TestContext.CurrentContext; + StringSplitOptions splitOptions = StringSplitOptions.RemoveEmptyEntries; + string[] expected = assertions.Subject.Split(SeperatorChars, splitOptions); + string[] actual = input.Split(SeperatorChars, splitOptions); + + // So we don't go out of range + int minLength = Math.Min(expected.Length, actual.Length); + const int previewSize = 3; + + for (int i = 0; i < minLength; i++) + { + string actaulLine = actual[i]; + string expectedLine = expected[i]; + + if (!string.Equals(actaulLine, expectedLine)) + { + TestContext.WriteLine($"The line {i} does not match"); + TestContext.WriteLine("-----------------"); + + // Show lines above + for (int x = previewSize - 1; x >= 1; x--) + { + int index = i - x; + if (index >= 0) + { + TestContext.WriteLine($" {index:00}|{actual[index]}"); + } + } + + TestContext.WriteLine($"- {i:00}|{expectedLine}"); + TestContext.WriteLine($"+ {i:00}|{actaulLine}"); + + for (int x = 1; x < previewSize + 1; x++) + { + int index = i + x; + if (index < actual.Length) + { + TestContext.WriteLine($" {index:00}|{actual[index]}"); + } + else + { + TestContext.WriteLine("\\ end of file \\"); + break; + } + } + + Assert.Fail(); + } + } + + actual.Length.Should().Be(expected.Length); + } + } +} diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index 5580582b..3249426d 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -20,32 +20,15 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - - - - - - - - - - - - - - - - - - - + diff --git a/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs index 3ee233d3..c91187ca 100644 --- a/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs @@ -54,14 +54,13 @@ public void MQTTServerBinding_FilledObject_SerializesAndDeserializes() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.MQTT; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(server); } @@ -86,15 +85,14 @@ public void MQTTOperationBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.MQTT; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(operation); } @@ -124,14 +122,13 @@ public void MQTTMessageBinding_WithFilledObject_SerializesAndDeserializes() // Act var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.MQTT; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(message); } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs index 18e6b4fd..641c763e 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs @@ -3,13 +3,14 @@ namespace LEGO.AsyncAPI.Tests.Models { using System.Collections.Generic; + using FluentAssertions; using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Bindings.WebSockets; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using NUnit.Framework; - internal class AsyncApiChannel_Should + internal class AsyncApiChannel_Should : TestBase { [Test] public void AsyncApiChannel_WithWebSocketsBinding_Serializes() @@ -67,11 +68,9 @@ public void AsyncApiChannel_WithWebSocketsBinding_Serializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -101,11 +100,9 @@ public void AsyncApiChannel_WithKafkaBinding_Serializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index 67156b1d..974fbfb2 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -13,7 +13,7 @@ namespace LEGO.AsyncAPI.Tests.Models using LEGO.AsyncAPI.Readers; using NUnit.Framework; - internal class AsyncApiMessage_Should + internal class AsyncApiMessage_Should : TestBase { [Test] public void AsyncApiMessage_WithNoType_DeserializesToDefault() @@ -112,15 +112,14 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() // Act var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); message.Should().BeEquivalentTo(deserializedMessage); - } + } [Test] public void AsyncApiMessage_WithSchemaFormat_Serializes() @@ -152,14 +151,11 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() // Act var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - var deserializedMessage = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); message.Should().BeEquivalentTo(deserializedMessage); } @@ -389,15 +385,13 @@ public void AsyncApiMessage_WithFilledObject_Serializes() var actual = message.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - var settings = new AsyncApiReaderSettings(); settings.Bindings = BindingsCollection.All; var deserializedMessage = new AsyncApiStringReader(settings).ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); message.Should().BeEquivalentTo(deserializedMessage); } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs index 8d8bd4f5..29d59488 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs @@ -5,6 +5,7 @@ namespace LEGO.AsyncAPI.Tests.Models using System; using System.Globalization; using System.IO; + using FluentAssertions; using LEGO.AsyncAPI.Bindings.Http; using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Models; @@ -12,7 +13,7 @@ namespace LEGO.AsyncAPI.Tests.Models using LEGO.AsyncAPI.Writers; using NUnit.Framework; - public class AsyncApiOperation_Should + public class AsyncApiOperation_Should : TestBase { [Test] public void SerializeV2_WithNullWriter_Throws() @@ -37,18 +38,18 @@ public void SerializeV2_WithMultipleMessages_SerializesWithOneOf() var asyncApiOperation = new AsyncApiOperation(); asyncApiOperation.Message.Add(new AsyncApiMessage { Name = "First Message" }); asyncApiOperation.Message.Add(new AsyncApiMessage { Name = "Second Message" }); - var outputString = new StringWriter(CultureInfo.InvariantCulture); - var writer = new AsyncApiYamlWriter(outputString); + var outputString = new StringWriter(); + var settings = new AsyncApiWriterSettings(); + var writer = new AsyncApiYamlWriter(outputString, settings); // Act asyncApiOperation.SerializeV2(writer); // Assert var actual = outputString.GetStringBuilder().ToString(); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -60,18 +61,18 @@ public void SerializeV2_WithSingleMessage_Serializes() var asyncApiOperation = new AsyncApiOperation(); asyncApiOperation.Message.Add(new AsyncApiMessage { Name = "First Message" }); - var outputString = new StringWriter(CultureInfo.InvariantCulture); - var writer = new AsyncApiYamlWriter(outputString); + var settings = new AsyncApiWriterSettings(); + var outputString = new StringWriter(); + var writer = new AsyncApiYamlWriter(outputString, settings); // Act asyncApiOperation.SerializeV2(writer); // Assert var actual = outputString.GetStringBuilder().ToString(); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -123,11 +124,9 @@ public void AsyncApiOperation_WithBindings_Serializes() var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs index eeaa4047..895d063f 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs @@ -3,12 +3,13 @@ namespace LEGO.AsyncAPI.Tests { using FluentAssertions; + using FluentAssertions.Primitives; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers; using NUnit.Framework; using System.Linq; - public class AsyncApiReference_Should + public class AsyncApiReference_Should : TestBase { [Test] public void AsyncApiReference_WithExternalFragmentUriReference_AllowReference() @@ -31,10 +32,9 @@ public void AsyncApiReference_WithExternalFragmentUriReference_AllowReference() reference.IsFragment.Should().BeTrue(); reference.IsExternal.Should().BeTrue(); reference.Type.Should().Be(ReferenceType.Schema); - var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - var expected = serialized.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + var expected = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -58,10 +58,9 @@ public void AsyncApiReference_WithFragmentReference_AllowReference() reference.Id.Should().BeNull(); reference.IsFragment.Should().BeTrue(); reference.IsExternal.Should().BeTrue(); - var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - var expected = serialized.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + var expected = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -84,10 +83,9 @@ public void AsyncApiReference_WithInternalComponentReference_AllowReference() reference.IsFragment.Should().BeFalse(); reference.IsExternal.Should().BeFalse(); - var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - var expected = serialized.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + var expected = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -109,10 +107,9 @@ public void AsyncApiReference_WithExternalFragmentReference_AllowReference() reference.IsFragment.Should().BeTrue(); reference.IsExternal.Should().BeTrue(); - var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - var expected = serialized.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + var expected = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -135,10 +132,9 @@ public void AsyncApiReference_WithExternalComponentReference_AllowReference() reference.IsFragment.Should().BeFalse(); reference.IsExternal.Should().BeTrue(); - var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - var expected = serialized.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + var expected = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -232,10 +228,11 @@ public void AsyncApiReference_WithExternalReference_AllowsReferenceDoesNotResolv reference.IsFragment.Should().BeFalse(); diagnostic.Errors.Should().BeEmpty(); - var serialized = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - var expected = serialized.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + var expected = deserialized.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); + + expected + .Should() + .BePlatformAgnosticEquivalentTo(actual); } } } \ 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 e7d7725e..f6c56063 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -12,7 +12,7 @@ namespace LEGO.AsyncAPI.Tests.Models using LEGO.AsyncAPI.Writers; using NUnit.Framework; - public class AsyncApiSchema_Should + public class AsyncApiSchema_Should : TestBase { public static AsyncApiSchema BasicSchema = new AsyncApiSchema(); @@ -283,71 +283,6 @@ public class AsyncApiSchema_Should }, }; - private string NoInlinedReferences => - @"asyncapi: '2.6.0' -info: - title: Streetlights Kafka API - version: 1.0.0 - description: The Smartylighting Streetlights API allows you to remotely manage the city lights. - license: - name: Apache 2.0 - url: https://www.apache.org/licenses/LICENSE-2.0 -channels: - mychannel: - publish: - message: - payload: - type: object - required: - - testB - properties: - testC: - $ref: '#/components/schemas/testC' - testB: - $ref: '#/components/schemas/testB' -components: - schemas: - testD: - type: string - format: uuid - testC: - type: object - properties: - testD: - $ref: '#/components/schemas/testD' - testB: - type: boolean - description: test"; - - private string InlinedReferences => - @"asyncapi: '2.6.0' -info: - title: Streetlights Kafka API - version: 1.0.0 - description: The Smartylighting Streetlights API allows you to remotely manage the city lights. - license: - name: Apache 2.0 - url: https://www.apache.org/licenses/LICENSE-2.0 -channels: - mychannel: - publish: - message: - payload: - type: object - required: - - testB - properties: - testC: - type: object - properties: - testD: - type: string - format: uuid - testB: - type: boolean - description: test -components: { }"; - [Test] public void SerializeAsJson_WithBasicSchema_V2Works() { @@ -358,9 +293,8 @@ public void SerializeAsJson_WithBasicSchema_V2Works() var actual = BasicSchema.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -385,9 +319,8 @@ public void SerializeAsJson_WithAdvancedSchemaNumber_V2Works() var actual = AdvancedSchemaNumber.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -412,193 +345,29 @@ public void SerializeAsJson_WithAdvancedSchemaBigNumbers_V2Works() var actual = AdvancedSchemaBigNumbers.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] public void SerializeAsJson_WithAdvancedSchemaObject_V2Works() { // Arrange - var expected = @"{ - ""title"": ""title1"", - ""properties"": { - ""property1"": { - ""items"": false, - ""additionalItems"": false, - ""properties"": { - ""property2"": { - ""type"": ""integer"" - }, - ""property3"": { - ""type"": ""string"", - ""maxLength"": 15 - } - }, - ""additionalProperties"": false - }, - ""property4"": { - ""items"": { - ""properties"": { - ""Property9"": { - ""type"": [ - ""null"", - ""string"" - ] - } - } - }, - ""additionalItems"": { - ""properties"": { - ""Property10"": { - ""type"": [ - ""null"", - ""string"" - ] - } - } - }, - ""properties"": { - ""property5"": { - ""properties"": { - ""property6"": { - ""type"": ""boolean"" - } - } - }, - ""property7"": { - ""type"": ""string"", - ""minLength"": 2 - } - }, - ""additionalProperties"": { - ""properties"": { - ""Property8"": { - ""type"": [ - ""null"", - ""string"" - ] - } - } - }, - ""patternProperties"": { - ""^S_"": { - ""type"": ""string"" - }, - ""^I_"": { - ""type"": ""integer"" - } - }, - ""propertyNames"": { - ""pattern"": ""^[A-Za-z_][A-Za-z0-9_]*$"" - } - }, - ""property11"": { - ""const"": ""aSpecialConstant"" - } - }, - ""nullable"": true, - ""externalDocs"": { - ""url"": ""http://example.com/externalDocs"" - } -}"; + string expected = this.GetTestData(); // Act var actual = AdvancedSchemaObject.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] public void Deserialize_WithAdvancedSchema_Works() { // Arrange - var json = @"{ - ""title"": ""title1"", - ""properties"": { - ""property1"": { - ""items"": false, - ""additionalItems"": false, - ""properties"": { - ""property2"": { - ""type"": ""integer"" - }, - ""property3"": { - ""type"": ""string"", - ""maxLength"": 15 - } - }, - ""additionalProperties"": false - }, - ""property4"": { - ""items"": { - ""properties"": { - ""Property9"": { - ""type"": [ - ""null"", - ""string"" - ] - } - } - }, - ""additionalItems"": { - ""properties"": { - ""Property10"": { - ""type"": [ - ""null"", - ""string"" - ] - } - } - }, - ""properties"": { - ""property5"": { - ""properties"": { - ""property6"": { - ""type"": ""boolean"" - } - } - }, - ""property7"": { - ""type"": ""string"", - ""minLength"": 2 - } - }, - ""additionalProperties"": { - ""properties"": { - ""Property8"": { - ""type"": [ - ""null"", - ""string"" - ] - } - } - }, - ""patternProperties"": { - ""^S_"": { - ""type"": ""string"" - }, - ""^I_"": { - ""type"": ""integer"" - } - }, - ""propertyNames"": { - ""pattern"": ""^[A-Za-z_][A-Za-z0-9_]*$"" - } - }, - ""property11"": { - ""const"": ""aSpecialConstant"" - } - }, - ""nullable"": true, - ""externalDocs"": { - ""url"": ""http://example.com/externalDocs"" - } -}"; + var json = GetTestData(); var expected = AdvancedSchemaObject; // Act @@ -612,52 +381,14 @@ public void Deserialize_WithAdvancedSchema_Works() public void SerializeAsJson_WithAdvancedSchemaWithAllOf_V2Works() { // Arrange - var expected = @"{ - ""title"": ""title1"", - ""allOf"": [ - { - ""title"": ""title2"", - ""properties"": { - ""property1"": { - ""type"": ""integer"" - }, - ""property2"": { - ""type"": ""string"", - ""maxLength"": 15 - } - } - }, - { - ""title"": ""title3"", - ""properties"": { - ""property3"": { - ""properties"": { - ""property4"": { - ""type"": ""boolean"" - } - } - }, - ""property5"": { - ""type"": ""string"", - ""minLength"": 2 - } - }, - ""nullable"": true - } - ], - ""nullable"": true, - ""externalDocs"": { - ""url"": ""http://example.com/externalDocs"" - } -}"; + var expected = this.GetTestData(); // Act var actual = AdvancedSchemaWithAllOf.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - actual.Should().Be(expected); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Theory] @@ -712,30 +443,21 @@ public void Serialize_WithInliningOptions_ShouldInlineAccordingly(bool shouldInl .WithComponent("testB", new AsyncApiSchema() { Description = "test", Type = SchemaType.Boolean }) .Build(); - var outputString = new StringWriter(CultureInfo.InvariantCulture); + var outputString = new StringWriter(); var writer = new AsyncApiYamlWriter(outputString, new AsyncApiWriterSettings { InlineReferences = shouldInline }); // Act asyncApiDocument.SerializeV2(writer); var actual = outputString.ToString(); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - - string expected = string.Empty; // Assert - if (shouldInline) - { - expected = this.InlinedReferences; - } - else - { - expected = this.NoInlinedReferences; - } - - expected = expected.MakeLineBreaksEnvironmentNeutral(); + string expected = this.GetTestData(shouldInline + ? "AsyncApiSchema_InlinedReferences" + : "AsyncApiSchema_NoInlinedReferences.yml"); - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs index a503b169..5c0a464e 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs @@ -3,12 +3,13 @@ namespace LEGO.AsyncAPI.Tests.Models { using System.Collections.Generic; + using FluentAssertions; using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; using NUnit.Framework; - internal class AsyncApiServer_Should + internal class AsyncApiServer_Should : TestBase { [Test] public void AsyncApiServer_Serializes() @@ -70,10 +71,8 @@ public void AsyncApiServer_Serializes() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } [Test] @@ -102,11 +101,9 @@ public void AsyncApiServer_WithKafkaBinding_Serializes() var actual = server.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); - actual = actual.MakeLineBreaksEnvironmentNeutral(); - expected = expected.MakeLineBreaksEnvironmentNeutral(); - // Assert - Assert.AreEqual(expected, actual); + actual.Should() + .BePlatformAgnosticEquivalentTo(expected); } } } diff --git a/test/LEGO.AsyncAPI.Tests/Serialization/AsyncApiYamlWriterTests.cs b/test/LEGO.AsyncAPI.Tests/Serialization/AsyncApiYamlWriterTests.cs new file mode 100644 index 00000000..c364d345 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Serialization/AsyncApiYamlWriterTests.cs @@ -0,0 +1,153 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Writers +{ + using LEGO.AsyncAPI.Writers; + using NUnit.Framework; + using System; + using System.IO; + + internal class AsyncApiYamlWriterTests : TestBase + { + [Test] + public void Write_NullValue_ReturnsNull() + => this.Compose(null, "null"); + + [Test] + public void Write_EmptyValue_ReturnsNull() + => this.Compose(string.Empty, "''"); + + [Test] + public void Write_NullWordString_ReturnsWrappedValue() + => this.Compose("null", "'null'"); + + [Test] + public void Write_TildaWordString_ReturnsWrappedValue() + => this.Compose("~", "'~'"); + + [Test] + public void Write_IntegerWithTwoPeriods_RendersPlainStyle() + => this.Compose("1.2.3", "1.2.3"); + + [Test] + public void Write_Float_WrappedWithQuotes() + => this.Compose("1.2", "'1.2'"); + + [Test] + public void Write_PositiveFloat_WrappedWithQuotes() + => this.Compose("+1.2", "'+1.2'"); + + [Test] + public void Write_NegativeFloat_WrappedWithQuotes() + => this.Compose("-1.2", "'-1.2'"); + + [Test] + public void Write_PositiveInfinityFloat_WrappedWithQuotes() + => this.Compose(".inf", "'.inf'"); + + [Test] + public void Write_NegativeInfinityFloat_WrappedWithQuotes() + => this.Compose("-.inf", "'-.inf'"); + + [Test] + public void Write_NanFloat_WrappedWithQuotes() + => this.Compose(".nan", "'.nan'"); + + [Test] + public void Write_TrueString_WrappedWithQuotes() + => this.Compose("true", "'true'"); + + [Test] + public void Write_FalseString_WrappedWithQuotes() + => this.Compose("false", "'false'"); + + [Test] + public void Write_DateTimeSlashString_NotWrappedWithQuotes() + => this.Compose("12/31/2022 23:59:59", "12/31/2022 23:59:59"); + + [Test] + public void Write_DateTimeDashString_NotWrappedWithQuotes() + => this.Compose("2022-12-31 23:59:59", "2022-12-31 23:59:59"); + + [Test] + public void Write_DateTimeISOString_NotWrappedWithQuotes() + => this.Compose("2022-12-31T23:59:59Z", "2022-12-31T23:59:59Z"); + + [Test] + public void Write_DateTimeCanonicalString_NotWrappedWithQuotes() + => this.Compose("2001-12-15T02:59:43.1Z", "2001-12-15T02:59:43.1Z"); + + [Test] + public void Write_DateTimeSpacedString_NotWrappedWithQuotes() + => this.Compose("2001-12-14 21:59:43.10 -5", "2001-12-14 21:59:43.10 -5"); + + [Test] + public void Write_DateString_NotWrappedWithQuotes() + => this.Compose("2002-12-14", "2002-12-14"); + + [Test] + [TestCase("\0", "\"\\0\"")] + [TestCase("\x01", "\"\\x01\"")] + [TestCase("\x02", "\"\\x02\"")] + [TestCase("\x03", "\"\\x03\"")] + [TestCase("\x04", "\"\\x04\"")] + [TestCase("\x05", "\"\\x05\"")] + [TestCase("\x06", "\"\\x06\"")] + [TestCase("\a", "\"\\a\"")] + [TestCase("\b", "\"\\b\"")] + [TestCase("\t", "\"\\t\"")] + [TestCase("\n", "\"\\n\"")] + [TestCase("\v", "\"\\v\"")] + [TestCase("\f", "\"\\f\"")] + [TestCase("\r", "\"\\r\"")] + [TestCase("\x0e", "\"\\x0e\"")] + [TestCase("\x0f", "\"\\x0f\"")] + [TestCase("\x10", "\"\\x10\"")] + [TestCase("\x11", "\"\\x11\"")] + [TestCase("\x12", "\"\\x12\"")] + [TestCase("\x13", "\"\\x13\"")] + [TestCase("\x14", "\"\\x14\"")] + [TestCase("\x15", "\"\\x15\"")] + [TestCase("\x16", "\"\\x16\"")] + [TestCase("\x17", "\"\\x17\"")] + [TestCase("\x18", "\"\\x18\"")] + [TestCase("\x19", "\"\\x19\"")] + [TestCase("\x1a", "\"\\x1a\"")] + [TestCase("\x1b", "\"\\x1b\"")] + [TestCase("\x1c", "\"\\x1c\"")] + [TestCase("\x1d", "\"\\x1d\"")] + [TestCase("\x1e", "\"\\x1e\"")] + [TestCase("\x1f", "\"\\x1f\"")] + public void Write_ControlCharacters_AreEscaped(string input, string expected) + => this.Compose(input, expected); + + private void Compose( + string? input, + string expected) + { + // It's a property + expected = $"Value: {expected}"; + + using (MemoryStream stream = new MemoryStream()) + using (StreamWriter writer = new StreamWriter(stream)) + { + AsyncApiWriterSettings settings = new AsyncApiWriterSettings(); + AsyncApiYamlWriter yamlWriter = new AsyncApiYamlWriter(writer, settings); + yamlWriter.WriteStartObject(); + yamlWriter.WritePropertyName("Value"); + yamlWriter.WriteValue(input); + yamlWriter.WriteEndObject(); + yamlWriter.Flush(); + stream.Position = 0; + + using (StreamReader reader = new StreamReader(stream)) + { + string actual = reader.ReadToEnd(); + this.Log($"Expected: <{expected}>"); + this.Log($"Actual: <{actual}>"); + Assert.AreEqual(expected, actual); + } + } + } + } +} diff --git a/test/LEGO.AsyncAPI.Tests/StringExtensions.cs b/test/LEGO.AsyncAPI.Tests/StringExtensions.cs deleted file mode 100644 index 2f0fddb8..00000000 --- a/test/LEGO.AsyncAPI.Tests/StringExtensions.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) The LEGO Group. All rights reserved. - -namespace LEGO.AsyncAPI.Tests -{ - using System; - - public static class StringExtensions - { - public static string MakeLineBreaksEnvironmentNeutral(this string input) - { - return input.Replace("\r\n", "\n") - .Replace("\r", "\n") - .Replace("\n", Environment.NewLine); - } - } -} diff --git a/test/LEGO.AsyncAPI.Tests/TestBase.cs b/test/LEGO.AsyncAPI.Tests/TestBase.cs new file mode 100644 index 00000000..17309617 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/TestBase.cs @@ -0,0 +1,81 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests +{ + using System; + using System.Diagnostics; + using System.IO; + using System.Linq; + using System.Runtime.CompilerServices; + using NUnit.Framework; + + /// + /// Base class for unit tests across the project. Can contain + /// helper methods for working with unit tests. + /// + public abstract class TestBase + { + /// + /// Initializes a new instance of the class. + /// + protected TestBase() + { + this.TestContext = TestContext.CurrentContext; + } + + /// + /// Gets the current context of the running text. + /// + protected TestContext TestContext { get; } + + /// + /// Writes information to the console which will only be + /// printed when running in debug mode. + /// + /// The message to print. + [Conditional("DEBUG")] + public void Log(string message) + { + TestContext.WriteLine(message); + } + + /// + /// Attempts to find the first file that matches the name of the active unit test + /// and returns it as an expected type. + /// + /// The type to return + /// The name of the resource file with an optional extension. + /// The result + protected T GetTestData([CallerMemberName] string resourceName = "") + { + string searchPattern = string.IsNullOrWhiteSpace(Path.GetExtension(resourceName)) + ? $"{resourceName}.*" + : resourceName; + + string testDataDirectory = Path.Combine(Environment.CurrentDirectory, "TestData"); + + string? testDataPath = Directory.GetFiles(testDataDirectory, searchPattern) + .FirstOrDefault(); + + Assume.That(File.Exists(testDataPath), $"No test data file named '{resourceName}' exists in directory '{testDataDirectory}'"); + + object? result = null; + Type resultType = typeof(T); + + if (typeof(string) == resultType) + { + result = File.ReadAllText(testDataPath); + } + else if (typeof(string[]) == resultType) + { + result = File.ReadAllLines(testDataPath); + } + else + { + throw new NotImplementedException($"No case has been defined to convering a resource into '{resultType.FullName}'. You can add a new one."); + } + + return (T)result!; + } + } +} diff --git a/test/LEGO.AsyncAPI.Tests/TestData/AsyncApiSchema_InlinedReferences.yml b/test/LEGO.AsyncAPI.Tests/TestData/AsyncApiSchema_InlinedReferences.yml new file mode 100644 index 00000000..54f78b6d --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/TestData/AsyncApiSchema_InlinedReferences.yml @@ -0,0 +1,27 @@ +asyncapi: 2.6.0 +info: + title: Streetlights Kafka API + version: 1.0.0 + description: The Smartylighting Streetlights API allows you to remotely manage the city lights. + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 +channels: + mychannel: + publish: + message: + payload: + type: object + required: + - testB + properties: + testC: + type: object + properties: + testD: + type: string + format: uuid + testB: + type: boolean + description: test +components: { } \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/TestData/AsyncApiSchema_NoInlinedReferences.yml b/test/LEGO.AsyncAPI.Tests/TestData/AsyncApiSchema_NoInlinedReferences.yml new file mode 100644 index 00000000..8308baf0 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/TestData/AsyncApiSchema_NoInlinedReferences.yml @@ -0,0 +1,34 @@ +asyncapi: 2.6.0 +info: + title: Streetlights Kafka API + version: 1.0.0 + description: The Smartylighting Streetlights API allows you to remotely manage the city lights. + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 +channels: + mychannel: + publish: + message: + payload: + type: object + required: + - testB + properties: + testC: + $ref: '#/components/schemas/testC' + testB: + $ref: '#/components/schemas/testB' +components: + schemas: + testD: + type: string + format: uuid + testC: + type: object + properties: + testD: + $ref: '#/components/schemas/testD' + testB: + type: boolean + description: test \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/TestData/Deserialize_WithAdvancedSchema_Works.json b/test/LEGO.AsyncAPI.Tests/TestData/Deserialize_WithAdvancedSchema_Works.json new file mode 100644 index 00000000..f6aa8ca2 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/TestData/Deserialize_WithAdvancedSchema_Works.json @@ -0,0 +1,82 @@ +{ + "title": "title1", + "properties": { + "property1": { + "items": false, + "additionalItems": false, + "properties": { + "property2": { + "type": "integer" + }, + "property3": { + "type": "string", + "maxLength": 15 + } + }, + "additionalProperties": false + }, + "property4": { + "items": { + "properties": { + "Property9": { + "type": [ + "null", + "string" + ] + } + } + }, + "additionalItems": { + "properties": { + "Property10": { + "type": [ + "null", + "string" + ] + } + } + }, + "properties": { + "property5": { + "properties": { + "property6": { + "type": "boolean" + } + } + }, + "property7": { + "type": "string", + "minLength": 2 + } + }, + "additionalProperties": { + "properties": { + "Property8": { + "type": [ + "null", + "string" + ] + } + } + }, + "patternProperties": { + "^S_": { + "type": "string" + }, + "^I_": { + "type": "integer" + } + }, + "propertyNames": { + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" + } + }, + "property11": { + "const": "aSpecialConstant" + } + }, + "nullable": true, + "externalDocs": { + "url": "http://example.com/externalDocs" + } +} \ No newline at end of file diff --git a/test/LEGO.AsyncAPI.Tests/TestData/SerializeAsJson_WithAdvancedSchemaObject_V2Works.json b/test/LEGO.AsyncAPI.Tests/TestData/SerializeAsJson_WithAdvancedSchemaObject_V2Works.json new file mode 100644 index 00000000..7c7e7c17 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/TestData/SerializeAsJson_WithAdvancedSchemaObject_V2Works.json @@ -0,0 +1,82 @@ +{ + "title": "title1", + "properties": { + "property1": { + "items": false, + "additionalItems": false, + "properties": { + "property2": { + "type": "integer" + }, + "property3": { + "type": "string", + "maxLength": 15 + } + }, + "additionalProperties": false + }, + "property4": { + "items": { + "properties": { + "Property9": { + "type": [ + "null", + "string" + ] + } + } + }, + "additionalItems": { + "properties": { + "Property10": { + "type": [ + "null", + "string" + ] + } + } + }, + "properties": { + "property5": { + "properties": { + "property6": { + "type": "boolean" + } + } + }, + "property7": { + "type": "string", + "minLength": 2 + } + }, + "additionalProperties": { + "properties": { + "Property8": { + "type": [ + "null", + "string" + ] + } + } + }, + "patternProperties": { + "^S_": { + "type": "string" + }, + "^I_": { + "type": "integer" + } + }, + "propertyNames": { + "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" + } + }, + "property11": { + "const": "aSpecialConstant" + } + }, + "nullable": true, + "externalDocs": { + "url": "http://example.com/externalDocs" + } +} diff --git a/test/LEGO.AsyncAPI.Tests/TestData/SerializeAsJson_WithAdvancedSchemaWithAllOf_V2Works.json b/test/LEGO.AsyncAPI.Tests/TestData/SerializeAsJson_WithAdvancedSchemaWithAllOf_V2Works.json new file mode 100644 index 00000000..b0539186 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/TestData/SerializeAsJson_WithAdvancedSchemaWithAllOf_V2Works.json @@ -0,0 +1,38 @@ +{ + "title": "title1", + "allOf": [ + { + "title": "title2", + "properties": { + "property1": { + "type": "integer" + }, + "property2": { + "type": "string", + "maxLength": 15 + } + } + }, + { + "title": "title3", + "properties": { + "property3": { + "properties": { + "property4": { + "type": "boolean" + } + } + }, + "property5": { + "type": "string", + "minLength": 2 + } + }, + "nullable": true + } + ], + "nullable": true, + "externalDocs": { + "url": "http://example.com/externalDocs" + } +} \ No newline at end of file From f3bff4a8b19811bfe58ee17088a0d95413518cae Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Sat, 30 Mar 2024 21:37:29 +0100 Subject: [PATCH 56/84] chore: null check for Serialize (#165) --- src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs index 624cfdba..1d1bd35a 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSerializableExtensions.cs @@ -109,6 +109,11 @@ public static void Serialize( throw new ArgumentNullException(nameof(stream)); } + if (settings is null) + { + throw new ArgumentNullException(nameof(settings)); + } + var streamWriter = new FormattingStreamWriter(stream, settings.CultureInfo); IAsyncApiWriter writer = format switch From 5cd3a6bb06c445eb62e6f1f05cd1d5b92d745aee Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Sat, 30 Mar 2024 21:45:36 +0100 Subject: [PATCH 57/84] test: use raw strings (#164) --- .github/workflows/ci.yml | 2 +- .../AsyncApiDocumentV2Tests.cs | 604 +++++++-------- .../AsyncApiLicenseTests.cs | 24 +- .../AsyncApiReaderTests.cs | 707 +++++++++--------- .../Bindings/AMQP/AMQPBindings_Should.cs | 78 +- .../Bindings/CustomBinding_Should.cs | 22 +- .../Bindings/Http/HttpBindings_Should.cs | 24 +- .../Bindings/Kafka/KafkaBindings_Should.cs | 80 +- .../Bindings/Pulsar/PulsarBindings_Should.cs | 82 +- .../Bindings/Sns/SnsBindings_Should.cs | 182 ++--- .../Bindings/Sqs/SqsBindings_should.cs | 216 +++--- .../Bindings/StringOrStringList_Should.cs | 22 +- .../WebSockets/WebSocketBindings_Should.cs | 16 +- .../LEGO.AsyncAPI.Tests.csproj | 53 +- .../MQTT/MQTTBindings_Should.cs | 56 +- .../Models/AsyncApiChannel_Should.cs | 38 +- .../Models/AsyncApiMessage_Should.cs | 222 +++--- .../Models/AsyncApiOperation_Should.cs | 40 +- .../Models/AsyncApiReference_Should.cs | 76 +- .../Models/AsyncApiSchema_Should.cs | 56 +- .../Models/AsyncApiServer_Should.cs | 50 +- 21 files changed, 1389 insertions(+), 1261 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 69ea5318..3d1d849d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: '6.0.x' + dotnet-version: '8.0.x' include-prerelease: true - name: Restore dependencies run: dotnet restore diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 6368db66..63e818b4 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -32,176 +32,178 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() { // Arrange var expected = -@"asyncapi: 2.6.0 -info: - title: Streetlights Kafka API - version: 1.0.0 - description: The Smartylighting Streetlights API allows you to remotely manage the city lights. - license: - name: Apache 2.0 - url: https://www.apache.org/licenses/LICENSE-2.0 -servers: - scram-connections: - url: test.mykafkacluster.org:18092 - protocol: kafka-secure - description: Test broker secured with scramSha256 - security: - - saslScram: [] - tags: - - name: env:test-scram - description: This environment is meant for running internal tests through scramSha256 - - name: kind:remote - description: This server is a remote server. Not exposed by the application - - name: visibility:private - description: This resource is private and only available to certain users - mtls-connections: - url: test.mykafkacluster.org:28092 - protocol: kafka-secure - description: Test broker secured with X509 - security: - - certs: [] - tags: - - name: env:test-mtls - description: This environment is meant for running internal tests through mtls - - name: kind:remote - description: This server is a remote server. Not exposed by the application - - name: visibility:private - description: This resource is private and only available to certain users -defaultContentType: application/json -channels: - 'smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured': - description: The topic on which measured values may be produced and consumed. - publish: - operationId: receiveLightMeasurement - summary: Inform about environmental lighting conditions of a particular streetlight. - traits: - - $ref: '#/components/operationTraits/kafka' - message: - $ref: '#/components/messages/lightMeasured' - parameters: - streetlightId: - $ref: '#/components/parameters/streetlightId' - 'smartylighting.streetlights.1.0.action.{streetlightId}.turn.on': - subscribe: - operationId: turnOn - traits: - - $ref: '#/components/operationTraits/kafka' - message: - $ref: '#/components/messages/turnOnOff' - parameters: - streetlightId: - $ref: '#/components/parameters/streetlightId' - 'smartylighting.streetlights.1.0.action.{streetlightId}.turn.off': - subscribe: - operationId: turnOff - traits: - - $ref: '#/components/operationTraits/kafka' - message: - $ref: '#/components/messages/turnOnOff' - parameters: - streetlightId: - $ref: '#/components/parameters/streetlightId' - 'smartylighting.streetlights.1.0.action.{streetlightId}.dim': - subscribe: - operationId: dimLight - traits: - - $ref: '#/components/operationTraits/kafka' - message: - $ref: '#/components/messages/dimLight' - parameters: - streetlightId: - $ref: '#/components/parameters/streetlightId' -components: - schemas: - lightMeasuredPayload: - type: object - properties: - lumens: - type: integer - description: Light intensity measured in lumens. - minimum: 0 - sentAt: - $ref: '#/components/schemas/sentAt' - turnOnOffPayload: - type: object - properties: - command: - type: string - description: Whether to turn on or off the light. - enum: - - on - - off - sentAt: - $ref: '#/components/schemas/sentAt' - dimLightPayload: - type: object - properties: - percentage: - type: integer - description: Percentage to which the light should be dimmed to. - maximum: 100 - minimum: 0 - sentAt: - $ref: '#/components/schemas/sentAt' - sentAt: - type: string - format: date-time - description: Date and time when the message was sent. - messages: - lightMeasured: - payload: - $ref: '#/components/schemas/lightMeasuredPayload' - contentType: application/json - name: lightMeasured - title: Light measured - summary: Inform about environmental lighting conditions of a particular streetlight. - traits: - - $ref: '#/components/messageTraits/commonHeaders' - turnOnOff: - payload: - $ref: '#/components/schemas/turnOnOffPayload' - name: turnOnOff - title: Turn on/off - summary: Command a particular streetlight to turn the lights on or off. - traits: - - $ref: '#/components/messageTraits/commonHeaders' - dimLight: - payload: - $ref: '#/components/schemas/dimLightPayload' - name: dimLight - title: Dim light - summary: Command a particular streetlight to dim the lights. - traits: - - $ref: '#/components/messageTraits/commonHeaders' - securitySchemes: - saslScram: - type: scramSha256 - description: Provide your username and password for SASL/SCRAM authentication - certs: - type: X509 - description: Download the certificate files from service provider - parameters: - streetlightId: - description: The ID of the streetlight. - schema: - type: string - operationTraits: - kafka: - bindings: - kafka: - clientId: - type: string - enum: - - my-app-id - messageTraits: - commonHeaders: - headers: - type: object - properties: - my-app-header: - type: integer - maximum: 100 - minimum: 0"; + """ + asyncapi: 2.6.0 + info: + title: Streetlights Kafka API + version: 1.0.0 + description: The Smartylighting Streetlights API allows you to remotely manage the city lights. + license: + name: Apache 2.0 + url: https://www.apache.org/licenses/LICENSE-2.0 + servers: + scram-connections: + url: test.mykafkacluster.org:18092 + protocol: kafka-secure + description: Test broker secured with scramSha256 + security: + - saslScram: [] + tags: + - name: env:test-scram + description: This environment is meant for running internal tests through scramSha256 + - name: kind:remote + description: This server is a remote server. Not exposed by the application + - name: visibility:private + description: This resource is private and only available to certain users + mtls-connections: + url: test.mykafkacluster.org:28092 + protocol: kafka-secure + description: Test broker secured with X509 + security: + - certs: [] + tags: + - name: env:test-mtls + description: This environment is meant for running internal tests through mtls + - name: kind:remote + description: This server is a remote server. Not exposed by the application + - name: visibility:private + description: This resource is private and only available to certain users + defaultContentType: application/json + channels: + 'smartylighting.streetlights.1.0.event.{streetlightId}.lighting.measured': + description: The topic on which measured values may be produced and consumed. + publish: + operationId: receiveLightMeasurement + summary: Inform about environmental lighting conditions of a particular streetlight. + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/lightMeasured' + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + 'smartylighting.streetlights.1.0.action.{streetlightId}.turn.on': + subscribe: + operationId: turnOn + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/turnOnOff' + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + 'smartylighting.streetlights.1.0.action.{streetlightId}.turn.off': + subscribe: + operationId: turnOff + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/turnOnOff' + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + 'smartylighting.streetlights.1.0.action.{streetlightId}.dim': + subscribe: + operationId: dimLight + traits: + - $ref: '#/components/operationTraits/kafka' + message: + $ref: '#/components/messages/dimLight' + parameters: + streetlightId: + $ref: '#/components/parameters/streetlightId' + components: + schemas: + lightMeasuredPayload: + type: object + properties: + lumens: + type: integer + description: Light intensity measured in lumens. + minimum: 0 + sentAt: + $ref: '#/components/schemas/sentAt' + turnOnOffPayload: + type: object + properties: + command: + type: string + description: Whether to turn on or off the light. + enum: + - on + - off + sentAt: + $ref: '#/components/schemas/sentAt' + dimLightPayload: + type: object + properties: + percentage: + type: integer + description: Percentage to which the light should be dimmed to. + maximum: 100 + minimum: 0 + sentAt: + $ref: '#/components/schemas/sentAt' + sentAt: + type: string + format: date-time + description: Date and time when the message was sent. + messages: + lightMeasured: + payload: + $ref: '#/components/schemas/lightMeasuredPayload' + contentType: application/json + name: lightMeasured + title: Light measured + summary: Inform about environmental lighting conditions of a particular streetlight. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + turnOnOff: + payload: + $ref: '#/components/schemas/turnOnOffPayload' + name: turnOnOff + title: Turn on/off + summary: Command a particular streetlight to turn the lights on or off. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + dimLight: + payload: + $ref: '#/components/schemas/dimLightPayload' + name: dimLight + title: Dim light + summary: Command a particular streetlight to dim the lights. + traits: + - $ref: '#/components/messageTraits/commonHeaders' + securitySchemes: + saslScram: + type: scramSha256 + description: Provide your username and password for SASL/SCRAM authentication + certs: + type: X509 + description: Download the certificate files from service provider + parameters: + streetlightId: + description: The ID of the streetlight. + schema: + type: string + operationTraits: + kafka: + bindings: + kafka: + clientId: + type: string + enum: + - my-app-id + messageTraits: + commonHeaders: + headers: + type: object + properties: + my-app-header: + type: integer + maximum: 100 + minimum: 0 + """; var asyncApiDocument = new AsyncApiDocumentBuilder() .WithInfo(new AsyncApiInfo @@ -704,113 +706,115 @@ public void AsyncApiDocument_WithStreetLightsExample_SerializesAndDeserializes() public void SerializeV2_WithFullSpec_Serializes() { var expected = - @"asyncapi: 2.6.0 -info: - title: apiTitle - version: apiVersion - description: description - termsOfService: https://example.com/termsOfService - contact: - name: contactName - url: https://example.com/contact - email: contactEmail - license: - name: licenseName - url: https://example.com/license - x-extension: value - x-extension: value -id: documentId -servers: - myServer: - url: https://example.com/server - protocol: KafkaProtocol - protocolVersion: protocolVersion - description: serverDescription - security: - - securitySchemeName: - - requirementItem -channels: - channel1: - description: channelDescription - subscribe: - operationId: myOperation - summary: operationSummary - description: operationDescription - tags: - - name: tagName - description: tagDescription - externalDocs: - description: externalDocsDescription - url: https://example.com/externalDocs - traits: - - operationId: myOperation - summary: traitSummary - description: traitDescription - tags: - - name: tagName - description: tagDescription - externalDocs: - description: externalDocsDescription - url: https://example.com/externalDocs - x-extension: value - message: - oneOf: - - contentType: contentType - name: messageName - title: messageTitle - summary: messageSummary - description: messageDescription - - correlationId: - description: correlationDescription - location: correlationLocation - x-extension: value - schemaFormat: schemaFormat - contentType: contentType - name: messageName - title: messageTitle - summary: messageSummary - description: messageDescription - traits: - - headers: - title: schemaTitle - description: schemaDescription - writeOnly: true - examples: - - key: value - otherKey: 9223372036854775807 - name: traitName - title: traitTitle - summary: traitSummary - description: traitDescription - tags: - - name: tagName - description: tagDescription - externalDocs: - description: externalDocsDescription - url: https://example.com/externalDocs - examples: - - name: exampleName - summary: exampleSummary - payload: - key: value - otherKey: 9223372036854775807 + """ + asyncapi: 2.6.0 + info: + title: apiTitle + version: apiVersion + description: description + termsOfService: https://example.com/termsOfService + contact: + name: contactName + url: https://example.com/contact + email: contactEmail + license: + name: licenseName + url: https://example.com/license x-extension: value - x-extension: value - x-extension: value - x-extension: value -components: - securitySchemes: - securitySchemeName: - type: oauth2 - description: securitySchemeDescription - flows: - implicit: - authorizationUrl: https://example.com/authorization - tokenUrl: https://example.com/tokenUrl - refreshUrl: https://example.com/refresh - scopes: - securitySchemeScopeKey: securitySchemeScopeValue - x-extension: value"; + x-extension: value + id: documentId + servers: + myServer: + url: https://example.com/server + protocol: KafkaProtocol + protocolVersion: protocolVersion + description: serverDescription + security: + - securitySchemeName: + - requirementItem + channels: + channel1: + description: channelDescription + subscribe: + operationId: myOperation + summary: operationSummary + description: operationDescription + tags: + - name: tagName + description: tagDescription + externalDocs: + description: externalDocsDescription + url: https://example.com/externalDocs + traits: + - operationId: myOperation + summary: traitSummary + description: traitDescription + tags: + - name: tagName + description: tagDescription + externalDocs: + description: externalDocsDescription + url: https://example.com/externalDocs + x-extension: value + message: + oneOf: + - contentType: contentType + name: messageName + title: messageTitle + summary: messageSummary + description: messageDescription + - correlationId: + description: correlationDescription + location: correlationLocation + x-extension: value + schemaFormat: schemaFormat + contentType: contentType + name: messageName + title: messageTitle + summary: messageSummary + description: messageDescription + traits: + - headers: + title: schemaTitle + description: schemaDescription + writeOnly: true + examples: + - key: value + otherKey: 9223372036854775807 + name: traitName + title: traitTitle + summary: traitSummary + description: traitDescription + tags: + - name: tagName + description: tagDescription + externalDocs: + description: externalDocsDescription + url: https://example.com/externalDocs + examples: + - name: exampleName + summary: exampleSummary + payload: + key: value + otherKey: 9223372036854775807 + x-extension: value + x-extension: value + x-extension: value + x-extension: value + components: + securitySchemes: + securitySchemeName: + type: oauth2 + description: securitySchemeDescription + flows: + implicit: + authorizationUrl: https://example.com/authorization + tokenUrl: https://example.com/tokenUrl + refreshUrl: https://example.com/refresh + scopes: + securitySchemeScopeKey: securitySchemeScopeValue + x-extension: value + """; // Arrange var title = "apiTitle"; @@ -1219,29 +1223,31 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() [Test] public void Serializev2_WithBindings_Serializes() { - var expected = @"asyncapi: 2.6.0 -info: - description: test description -servers: - production: - url: example.com - protocol: pulsar+ssl - description: test description -channels: - testChannel: - publish: - message: - bindings: - http: - headers: - description: this mah binding - kafka: - key: - description: this mah other binding - bindings: - kafka: - partitions: 2 - replicas: 1"; + var expected = """ + asyncapi: 2.6.0 + info: + description: test description + servers: + production: + url: example.com + protocol: pulsar+ssl + description: test description + channels: + testChannel: + publish: + message: + bindings: + http: + headers: + description: this mah binding + kafka: + key: + description: this mah other binding + bindings: + kafka: + partitions: 2 + replicas: 1 + """; var doc = new AsyncApiDocument(); doc.Info = new AsyncApiInfo() diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs index d436dc1e..1edcbe37 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiLicenseTests.cs @@ -18,11 +18,13 @@ public class AsyncApiLicenseTests : TestBase [Test] public void Serialize_WithAllProperties_Serializes() { - var expected = @"{ - ""name"": ""test"", - ""url"": ""https://example.com/license"", - ""x-extension"": ""value"" -}"; + var expected = """ + { + "name": "test", + "url": "https://example.com/license", + "x-extension": "value" + } + """; var license = new AsyncApiLicense() { Name = "test", @@ -54,11 +56,13 @@ public static Stream GenerateStreamFromString(string s) public void LoadLicense_WithJson_Deserializes() { // Arrange - var input = @"{ - ""name"": ""test"", - ""url"": ""https://example.com/license"", - ""x-extension"": ""value"" -}"; + var input = """ + { + "name": "test", + "url": "https://example.com/license", + "x-extension": "value" + } + """; using (var stream = GenerateStreamFromString(input)) { diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index 40a2ddbb..bf19a944 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -26,18 +26,19 @@ public void Read_WithMissingEverything_DeserializesWithErrors() public void Read_WithExtensionParser_Parses() { var extensionName = "x-someValue"; - var yaml = @$"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 - contact: - name: API Support - url: https://www.example.com/support - email: support@example.com -channels: - workspace: - {extensionName}: onetwothreefour -"; + var yaml = $""" + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + contact: + name: API Support + url: https://www.example.com/support + email: support@example.com + channels: + workspace: + {extensionName}: onetwothreefour + """; Func valueExtensionParser = (any) => { if (any.TryGetValue(out var value)) @@ -68,18 +69,19 @@ public void Read_WithExtensionParser_Parses() public void Read_WithThrowingExtensionParser_AddsToDiagnostics() { var extensionName = "x-fail"; - var yaml = @$"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 - contact: - name: API Support - url: https://www.example.com/support - email: support@example.com -channels: - workspace: - {extensionName}: onetwothreefour -"; + var yaml = $""" + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + contact: + name: API Support + url: https://www.example.com/support + email: support@example.com + channels: + workspace: + {extensionName}: onetwothreefour + """; Func failingExtensionParser = (any) => { throw new AsyncApiException("Failed to parse"); @@ -106,18 +108,19 @@ public void Read_WithThrowingExtensionParser_AddsToDiagnostics() [Test] public void Read_WithBasicPlusContact_Deserializes() { - var yaml = @"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 - contact: - name: API Support - url: https://www.example.com/support - email: support@example.com -channels: - workspace: - x-eventarchetype: objectchanged -"; + var yaml = """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + contact: + name: API Support + url: https://www.example.com/support + email: support@example.com + channels: + workspace: + x-eventarchetype: objectchanged + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); Assert.AreEqual("support@example.com", doc.Info.Contact.Email); @@ -128,26 +131,27 @@ public void Read_WithBasicPlusContact_Deserializes() [Test] public void Read_WithBasicPlusExternalDocs_Deserializes() { - var yaml = @"asyncapi: 2.3.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 - externalDocs: - description: Find more info here - url: https://example.com -"; + var yaml = """ + asyncapi: 2.3.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 + externalDocs: + description: Find more info here + url: https://example.com + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); var message = doc.Channels["workspace"].Publish.Message; @@ -158,17 +162,18 @@ public void Read_WithBasicPlusExternalDocs_Deserializes() [Test] public void Read_WithBasicPlusTag_Deserializes() { - var yaml = @"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 -channels: - workspace: - x-eventarchetype: objectchanged -tags: - - name: user - description: User-related messages -"; + var yaml = """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + channels: + workspace: + x-eventarchetype: objectchanged + tags: + - name: user + description: User-related messages + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); var tag = doc.Tags.First(); @@ -179,19 +184,20 @@ public void Read_WithBasicPlusTag_Deserializes() [Test] public void Read_WithBasicPlusServerDeserializes() { - var yaml = @"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 -channels: - workspace: - x-eventarchetype: objectchanged -servers: - production: - url: 'pulsar+ssl://prod.events.managed.io:1234' - protocol: pulsar+ssl - description: Pulsar broker -"; + var yaml = """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + channels: + workspace: + x-eventarchetype: objectchanged + servers: + production: + url: 'pulsar+ssl://prod.events.managed.io:1234' + protocol: pulsar+ssl + description: Pulsar broker + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); var server = doc.Servers.First(); @@ -204,26 +210,27 @@ public void Read_WithBasicPlusServerDeserializes() [Test] public void Read_WithBasicPlusServerVariablesDeserializes() { - var yaml = @"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 -channels: - workspace: - x-eventarchetype: objectchanged -servers: - production: - url: 'pulsar+ssl://prod.events.managed.io:{port}' - protocol: pulsar+ssl - description: Pulsar broker - variables: - port: - description: Secure connection (TLS) is available through port 8883. - default: '1883' - enum: - - '1883' - - '8883' -"; + var yaml = """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + channels: + workspace: + x-eventarchetype: objectchanged + servers: + production: + url: 'pulsar+ssl://prod.events.managed.io:{port}' + protocol: pulsar+ssl + description: Pulsar broker + variables: + port: + description: Secure connection (TLS) is available through port 8883. + default: '1883' + enum: + - '1883' + - '8883' + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); var server = doc.Servers.First(); @@ -236,26 +243,27 @@ public void Read_WithBasicPlusServerVariablesDeserializes() [Test] public void Read_WithBasicPlusCorrelationIDDeserializes() { - var yaml = @"asyncapi: 2.3.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 - correlationId: - description: Default Correlation ID - location: $message.header#/correlationId -"; + var yaml = """ + asyncapi: 2.3.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 + correlationId: + description: Default Correlation ID + location: $message.header#/correlationId + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); var message = doc.Channels["workspace"].Publish.Message; @@ -266,27 +274,28 @@ public void Read_WithBasicPlusCorrelationIDDeserializes() [Test] public void Read_WithOneOfMessage_Reads() { - var yaml = @"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 -channels: - workspace: - publish: - bindings: - http: - type: response - message: - oneOf: - - $ref: '#/components/messages/WorkspaceEventPayload' -components: - messages: - WorkspaceEventPayload: - schemaFormat: application/schema+yaml;version=draft-07 - correlationId: - description: Default Correlation ID - location: $message.header#/correlationId -"; + var yaml = """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + channels: + workspace: + publish: + bindings: + http: + type: response + message: + oneOf: + - $ref: '#/components/messages/WorkspaceEventPayload' + components: + messages: + WorkspaceEventPayload: + schemaFormat: application/schema+yaml;version=draft-07 + correlationId: + description: Default Correlation ID + location: $message.header#/correlationId + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); var message = doc.Channels["workspace"].Publish.Message.First(); @@ -297,27 +306,28 @@ public void Read_WithOneOfMessage_Reads() [Test] public void Read_WithBasicPlusSecuritySchemeDeserializes() { - var yaml = @"asyncapi: 2.3.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 - securitySchemes: - saslScram: - type: scramSha256 - description: Provide your username and password for SASL/SCRAM authentication -"; + var yaml = """ + asyncapi: 2.3.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 + securitySchemes: + saslScram: + type: scramSha256 + description: Provide your username and password for SASL/SCRAM authentication + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); var scheme = doc.Components.SecuritySchemes.First(); @@ -329,24 +339,25 @@ public void Read_WithBasicPlusSecuritySchemeDeserializes() [Test] public void Read_WithBasicPlusOAuthFlowDeserializes() { - var yaml = @"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 -channels: - workspace: - x-something: yes -components: - securitySchemes: - oauth2: - type: oauth2 - flows: - implicit: - authorizationUrl: https://example.com/api/oauth/dialog - scopes: - write:pets: modify pets in your account - read:pets: read your pets -"; + var yaml = """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + channels: + workspace: + x-something: yes + components: + securitySchemes: + oauth2: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + write:pets: modify pets in your account + read:pets: read your pets + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); var scheme = doc.Components.SecuritySchemes.First(); @@ -361,36 +372,37 @@ public void Read_WithBasicPlusOAuthFlowDeserializes() [Test] public void Read_WithServerReference_ResolvesReference() { - var yaml = @"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 -servers: - production: - $ref: '#/components/servers/production' -channels: - workspace: - x-something: yes -components: - servers: - production: - url: 'pulsar+ssl://prod.events.managed.io:1234' - protocol: pulsar+ssl - description: Pulsar broker - security: - - petstore_auth: - - write:pets - - read:pets - securitySchemes: - petstore_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: https://example.com/api/oauth/dialog - scopes: - write:pets: modify pets in your account - read:pets: read your pets -"; + var yaml = """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + servers: + production: + $ref: '#/components/servers/production' + channels: + workspace: + x-something: yes + components: + servers: + production: + url: 'pulsar+ssl://prod.events.managed.io:1234' + protocol: pulsar+ssl + description: Pulsar broker + security: + - petstore_auth: + - write:pets + - read:pets + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + write:pets: modify pets in your account + read:pets: read your pets + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); Assert.AreEqual("pulsar+ssl://prod.events.managed.io:1234", doc.Servers.First().Value.Url); @@ -399,44 +411,45 @@ public void Read_WithServerReference_ResolvesReference() [Test] public void Read_WithChannelReference_ResolvesReference() { - var yaml = @"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 -servers: - production: - $ref: '#/components/servers/production' -channels: - workspace: - $ref: '#/components/channels/workspace' -components: - channels: - workspace: - publish: - message: - $ref: '#/components/messages/WorkspaceEventPayload' - servers: - production: - url: 'pulsar+ssl://prod.events.managed.io:1234' - protocol: pulsar+ssl - description: Pulsar broker - security: - - petstore_auth: - - write:pets - - read:pets - messages: - WorkspaceEventPayload: - schemaFormat: 'application/schema+yaml;version=draft-07' - securitySchemes: - petstore_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: https://example.com/api/oauth/dialog - scopes: - write:pets: modify pets in your account - read:pets: read your pets -"; + var yaml = """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + servers: + production: + $ref: '#/components/servers/production' + channels: + workspace: + $ref: '#/components/channels/workspace' + components: + channels: + workspace: + publish: + message: + $ref: '#/components/messages/WorkspaceEventPayload' + servers: + production: + url: 'pulsar+ssl://prod.events.managed.io:1234' + protocol: pulsar+ssl + description: Pulsar broker + security: + - petstore_auth: + - write:pets + - read:pets + messages: + WorkspaceEventPayload: + schemaFormat: 'application/schema+yaml;version=draft-07' + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + write:pets: modify pets in your account + read:pets: read your pets + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); Assert.AreEqual("application/schema+yaml;version=draft-07", doc.Channels.First().Value.Publish.Message.First().SchemaFormat); @@ -445,38 +458,39 @@ public void Read_WithChannelReference_ResolvesReference() [Test] public void Read_WithBasicPlusMessageTraitsDeserializes() { - var yaml = @"asyncapi: 2.3.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 - externalDocs: - description: Find more info here - url: https://example.com - traits: - - $ref: '#/components/messageTraits/commonHeaders' - messageTraits: - commonHeaders: - description: a common headers for common things - headers: - type: object - properties: - my-app-header: - type: integer - minimum: 0 - maximum: 100 -"; + var yaml = """ + asyncapi: 2.3.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 + externalDocs: + description: Find more info here + url: https://example.com + traits: + - $ref: '#/components/messageTraits/commonHeaders' + messageTraits: + commonHeaders: + description: a common headers for common things + headers: + type: object + properties: + my-app-header: + type: integer + minimum: 0 + maximum: 100 + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); @@ -491,44 +505,46 @@ public void Read_WithBasicPlusMessageTraitsDeserializes() [Test] public void Serialize_withOneOfSchema_DoesNotWriteThen() { - var yaml = @"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 -defaultContentType: application/json -channels: - channel1: - publish: - operationId: channel1 - summary: tthe first channel - description: a channel of great importance - message: - $ref: '#/components/messages/item1' -components: - schemas: - item2: - type: object - properties: - icon: - description: Theme icon - oneOf: - - type: 'null' - - $ref: '#/components/schemas/item3' - item3: - type: object - properties: - title: - type: string - description: The title. - format: string - messages: - item1: - payload: - $ref: '#/components/schemas/item2' - name: item1 - title: item 1 - summary: the first item - description: a first item for firsting the items"; + var yaml = """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + defaultContentType: application/json + channels: + channel1: + publish: + operationId: channel1 + summary: tthe first channel + description: a channel of great importance + message: + $ref: '#/components/messages/item1' + components: + schemas: + item2: + type: object + properties: + icon: + description: Theme icon + oneOf: + - type: 'null' + - $ref: '#/components/schemas/item3' + item3: + type: object + properties: + title: + type: string + description: The title. + format: string + messages: + item1: + payload: + $ref: '#/components/schemas/item2' + name: item1 + title: item 1 + summary: the first item + description: a first item for firsting the items + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); @@ -540,33 +556,34 @@ public void Serialize_withOneOfSchema_DoesNotWriteThen() [Test] public void Read_WithBasicPlusSecurityRequirementsDeserializes() { - var yaml = @"asyncapi: 2.3.0 -info: - title: test - version: 1.0.0 -servers: - production: - url: 'pulsar+ssl://prod.events.managed.io:1234' - protocol: pulsar+ssl - description: Pulsar broker - security: - - petstore_auth: - - write:pets - - read:pets -channels: - workspace: - x-something: yes -components: - securitySchemes: - petstore_auth: - type: oauth2 - flows: - implicit: - authorizationUrl: https://example.com/api/oauth/dialog - scopes: - write:pets: modify pets in your account - read:pets: read your pets -"; + var yaml = """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + servers: + production: + url: 'pulsar+ssl://prod.events.managed.io:1234' + protocol: pulsar+ssl + description: Pulsar broker + security: + - petstore_auth: + - write:pets + - read:pets + channels: + workspace: + x-something: yes + components: + securitySchemes: + petstore_auth: + type: oauth2 + flows: + implicit: + authorizationUrl: https://example.com/api/oauth/dialog + scopes: + write:pets: modify pets in your account + read:pets: read your pets + """; var reader = new AsyncApiStringReader(); var doc = reader.Read(yaml, out var diagnostic); var requirement = doc.Servers.First().Value.Security.First().First(); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs index 7dce6ed3..5ef0d8d1 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/AMQP/AMQPBindings_Should.cs @@ -17,15 +17,17 @@ public void AMQPChannelBinding_WithRoutingKey_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - amqp: - is: routingKey - exchange: - name: myExchange - type: topic - durable: true - autoDelete: false - vhost: /"; + """ + bindings: + amqp: + is: routingKey + exchange: + name: myExchange + type: topic + durable: true + autoDelete: false + vhost: / + """; var channel = new AsyncApiChannel(); channel.Bindings.Add(new AMQPChannelBinding @@ -60,15 +62,17 @@ public void AMQPChannelBinding_WithQueue_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - amqp: - is: queue - queue: - name: my-queue-name - durable: true - exclusive: true - autoDelete: false - vhost: /"; + """ + bindings: + amqp: + is: queue + queue: + name: my-queue-name + durable: true + exclusive: true + autoDelete: false + vhost: / + """; var channel = new AsyncApiChannel(); channel.Bindings.Add(new AMQPChannelBinding @@ -103,10 +107,12 @@ public void AMQPMessageBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - amqp: - contentEncoding: gzip - messageType: user.signup"; + """ + bindings: + amqp: + contentEncoding: gzip + messageType: user.signup + """; var message = new AsyncApiMessage(); @@ -133,19 +139,21 @@ public void AMQPOperationBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - amqp: - expiration: 100000 - userId: guest - cc: - - user.logs - priority: 10 - deliveryMode: 2 - mandatory: false - bcc: - - external.audit - timestamp: true - ack: false"; + """ + bindings: + amqp: + expiration: 100000 + userId: guest + cc: + - user.logs + priority: 10 + deliveryMode: 2 + mandatory: false + bcc: + - external.audit + timestamp: true + ack: false + """; var operation = new AsyncApiOperation(); operation.Bindings.Add(new AMQPOperationBinding diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs index 8bd56063..448e588b 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/CustomBinding_Should.cs @@ -69,16 +69,18 @@ public void CustomBinding_SerializesDeserializes() { // Arrange var expected = -@"bindings: - my: - custom: someValue - bindingVersion: 0.1.0 - any: - anyKeyName: anyValue - nestedConfiguration: - name: nested - x-myNestedExtension: nestedValue - x-myextension: someValue"; + """ + bindings: + my: + custom: someValue + bindingVersion: 0.1.0 + any: + anyKeyName: anyValue + nestedConfiguration: + name: nested + x-myNestedExtension: nestedValue + x-myextension: someValue + """; var channel = new AsyncApiChannel(); channel.Bindings.Add(new MyBinding diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs index d31c9174..f2c17c6c 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Http/HttpBindings_Should.cs @@ -16,10 +16,12 @@ public void HttpMessageBinding_FilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - http: - headers: - description: this mah binding"; + """ + bindings: + http: + headers: + description: this mah binding + """; var message = new AsyncApiMessage(); @@ -48,12 +50,14 @@ public void HttpOperationBinding_FilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - http: - type: request - method: POST - query: - description: this mah query"; + """ + bindings: + http: + type: request + method: POST + query: + description: this mah query + """; var operation = new AsyncApiOperation(); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs index ec41a06a..2c5c6f3f 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Kafka/KafkaBindings_Should.cs @@ -17,23 +17,25 @@ public void KafkaChannelBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - kafka: - topic: myTopic - partitions: 5 - replicas: 4 - topicConfiguration: - cleanup.policy: - - delete - - compact - retention.ms: 15552000000 - retention.bytes: 2 - delete.retention.ms: 3 - max.message.bytes: 4 - confluent.key.schema.validation: true - confluent.key.subject.name.strategy: TopicNameStrategy - confluent.value.schema.validation: true - confluent.value.subject.name.strategy: TopicNameStrategy"; + """ + bindings: + kafka: + topic: myTopic + partitions: 5 + replicas: 4 + topicConfiguration: + cleanup.policy: + - delete + - compact + retention.ms: 15552000000 + retention.bytes: 2 + delete.retention.ms: 3 + max.message.bytes: 4 + confluent.key.schema.validation: true + confluent.key.subject.name.strategy: TopicNameStrategy + confluent.value.schema.validation: true + confluent.value.subject.name.strategy: TopicNameStrategy + """; var channel = new AsyncApiChannel(); channel.Bindings.Add(new KafkaChannelBinding @@ -74,12 +76,14 @@ public void KafkaServerBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"url: https://example.com -protocol: kafka -bindings: - kafka: - schemaRegistryUrl: https://example.com/schemaregistry - schemaRegistryVendor: confluent"; + """ + url: https://example.com + protocol: kafka + bindings: + kafka: + schemaRegistryUrl: https://example.com/schemaregistry + schemaRegistryVendor: confluent + """; var server = new AsyncApiServer() { @@ -110,13 +114,15 @@ public void KafkaMessageBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - kafka: - key: - description: this mah other binding - SchemaIdLocation: test - schemaIdPayloadEncoding: test - schemaLookupStrategy: header"; + """ + bindings: + kafka: + key: + description: this mah other binding + SchemaIdLocation: test + schemaIdPayloadEncoding: test + schemaLookupStrategy: header + """; var message = new AsyncApiMessage(); @@ -148,12 +154,14 @@ public void KafkaOperationBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - kafka: - groupId: - description: this mah groupId - clientId: - description: this mah clientId"; + """ + bindings: + kafka: + groupId: + description: this mah groupId + clientId: + description: this mah clientId + """; var operation = new AsyncApiOperation(); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs index 26e6ba68..e8a80ac1 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs @@ -17,20 +17,22 @@ public void PulsarChannelBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - pulsar: - namespace: staging - persistence: persistent - compaction: 1000 - geo-replication: - - us-east1 - - us-west1 - retention: - time: 7 - size: 1000 - ttl: 360 - deduplication: true - bindingVersion: 0.1.0"; + """ + bindings: + pulsar: + namespace: staging + persistence: persistent + compaction: 1000 + geo-replication: + - us-east1 + - us-west1 + retention: + time: 7 + size: 1000 + ttl: 360 + deduplication: true + bindingVersion: 0.1.0 + """; var channel = new AsyncApiChannel(); channel.Bindings.Add(new PulsarChannelBinding @@ -71,9 +73,11 @@ public void PulsarChannelBindingNamespaceDefaultToNull() { // Arrange var actual = - @"bindings: - pulsar: - persistence: persistent"; + """ + bindings: + pulsar: + persistence: persistent + """; // Act var settings = new AsyncApiReaderSettings(); @@ -89,9 +93,11 @@ public void PulsarChannelBindingPropertiesExceptNamespaceDefaultToNull() { // Arrange var actual = - @"bindings: - pulsar: - namespace: staging"; + """ + bindings: + pulsar: + namespace: staging + """; // Act // Assert @@ -114,11 +120,13 @@ public void PulsarServerBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"url: https://example.com -protocol: pulsar -bindings: - pulsar: - tenant: contoso"; + """ + url: https://example.com + protocol: pulsar + bindings: + pulsar: + tenant: contoso + """; var server = new AsyncApiServer() { @@ -148,11 +156,13 @@ public void ServerBindingVersionDefaultsToNull() { // Arrange var expected = - @"url: https://example.com -protocol: pulsar -bindings: - pulsar: - tenant: contoso"; + """ + url: https://example.com + protocol: pulsar + bindings: + pulsar: + tenant: contoso + """; var server = new AsyncApiServer() { @@ -184,11 +194,13 @@ public void ServerTenantDefaultsToNull() { // Arrange var expected = - @"url: https://example.com -protocol: pulsar -bindings: - pulsar: - bindingVersion: latest"; + """ + url: https://example.com + protocol: pulsar + bindings: + pulsar: + bindingVersion: latest + """; var server = new AsyncApiServer() { diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index e7aaae0b..6d4f5779 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -19,35 +19,37 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = - @"bindings: - sns: - name: myTopic - ordering: - type: FIFO - contentBasedDeduplication: true - x-orderingExtension: - orderingXPropertyName: orderingXPropertyValue - policy: - statements: - - effect: Deny - principal: arn:aws:iam::123456789012:user/alex.wichmann - action: - - sns:Publish - - sns:Delete - - effect: Allow - principal: - - arn:aws:iam::123456789012:user/alex.wichmann - - arn:aws:iam::123456789012:user/dec.kolakowski - action: sns:Create - x-statementExtension: - statementXPropertyName: statementXPropertyValue - x-policyExtension: - policyXPropertyName: policyXPropertyValue - tags: - owner: AsyncAPI.NET - platform: AsyncAPIOrg - x-bindingExtension: - bindingXPropertyName: bindingXPropertyValue"; + """ + bindings: + sns: + name: myTopic + ordering: + type: FIFO + contentBasedDeduplication: true + x-orderingExtension: + orderingXPropertyName: orderingXPropertyValue + policy: + statements: + - effect: Deny + principal: arn:aws:iam::123456789012:user/alex.wichmann + action: + - sns:Publish + - sns:Delete + - effect: Allow + principal: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski + action: sns:Create + x-statementExtension: + statementXPropertyName: statementXPropertyValue + x-policyExtension: + policyXPropertyName: policyXPropertyValue + tags: + owner: AsyncAPI.NET + platform: AsyncAPIOrg + x-bindingExtension: + bindingXPropertyName: bindingXPropertyValue + """; var channel = new AsyncApiChannel(); channel.Bindings.Add(new SnsChannelBinding() @@ -152,66 +154,68 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = - @"bindings: - sns: - topic: - name: someTopic - x-identifierExtension: - identifierXPropertyName: identifierXPropertyValue - consumers: - - protocol: sqs - endpoint: - name: someQueue - x-identifierExtension: - identifierXPropertyName: identifierXPropertyValue - filterPolicy: - store: - - asyncapi_corp - contact: dec.kolakowski - event: - - anything-but: order_cancelled - order_key: - transient: by_area - customer_interests: - - rugby - - football - - baseball - filterPolicyScope: MessageAttributes - rawMessageDelivery: false - redrivePolicy: - deadLetterQueue: - arn: arn:aws:SQS:eu-west-1:0000000:123456789 - x-identifierExtension: - identifierXPropertyName: identifierXPropertyValue - maxReceiveCount: 25 - x-redrivePolicyExtension: - redrivePolicyXPropertyName: redrivePolicyXPropertyValue - deliveryPolicy: - minDelayTarget: 10 - maxDelayTarget: 100 - numRetries: 5 - numNoDelayRetries: 2 - numMinDelayRetries: 3 - numMaxDelayRetries: 5 - backoffFunction: linear - maxReceivesPerSecond: 2 - x-deliveryPolicyExtension: - deliveryPolicyXPropertyName: deliveryPolicyXPropertyValue - x-consumerExtension: - consumerXPropertyName: consumerXPropertyValue - deliveryPolicy: - minDelayTarget: 10 - maxDelayTarget: 100 - numRetries: 5 - numNoDelayRetries: 2 - numMinDelayRetries: 3 - numMaxDelayRetries: 5 - backoffFunction: geometric - maxReceivesPerSecond: 10 - x-deliveryPolicyExtension: - deliveryPolicyXPropertyName: deliveryPolicyXPropertyValue - x-bindingExtension: - bindingXPropertyName: bindingXPropertyValue"; + """ + bindings: + sns: + topic: + name: someTopic + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue + consumers: + - protocol: sqs + endpoint: + name: someQueue + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue + filterPolicy: + store: + - asyncapi_corp + contact: dec.kolakowski + event: + - anything-but: order_cancelled + order_key: + transient: by_area + customer_interests: + - rugby + - football + - baseball + filterPolicyScope: MessageAttributes + rawMessageDelivery: false + redrivePolicy: + deadLetterQueue: + arn: arn:aws:SQS:eu-west-1:0000000:123456789 + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue + maxReceiveCount: 25 + x-redrivePolicyExtension: + redrivePolicyXPropertyName: redrivePolicyXPropertyValue + deliveryPolicy: + minDelayTarget: 10 + maxDelayTarget: 100 + numRetries: 5 + numNoDelayRetries: 2 + numMinDelayRetries: 3 + numMaxDelayRetries: 5 + backoffFunction: linear + maxReceivesPerSecond: 2 + x-deliveryPolicyExtension: + deliveryPolicyXPropertyName: deliveryPolicyXPropertyValue + x-consumerExtension: + consumerXPropertyName: consumerXPropertyValue + deliveryPolicy: + minDelayTarget: 10 + maxDelayTarget: 100 + numRetries: 5 + numNoDelayRetries: 2 + numMinDelayRetries: 3 + numMaxDelayRetries: 5 + backoffFunction: geometric + maxReceivesPerSecond: 10 + x-deliveryPolicyExtension: + deliveryPolicyXPropertyName: deliveryPolicyXPropertyValue + x-bindingExtension: + bindingXPropertyName: bindingXPropertyValue + """; var operation = new AsyncApiOperation(); operation.Bindings.Add(new SnsOperationBinding() diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index 6f2d7ba6..c031621c 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -19,60 +19,62 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = - @"bindings: - sqs: - queue: - name: myQueue - fifoQueue: true - deduplicationScope: messageGroup - fifoThroughputLimit: perMessageGroupId - deliveryDelay: 30 - visibilityTimeout: 60 - receiveMessageWaitTime: 0 - messageRetentionPeriod: 86400 - redrivePolicy: - deadLetterQueue: - arn: arn:aws:SQS:eu-west-1:0000000:123456789 - x-identifierExtension: - identifierXPropertyName: identifierXPropertyValue - maxReceiveCount: 15 - x-redrivePolicyExtension: - redrivePolicyXPropertyName: redrivePolicyXPropertyValue - policy: - statements: - - effect: deny - principal: arn:aws:iam::123456789012:user/alex.wichmann - action: - - sqs:SendMessage - - sqs:ReceiveMessage - x-statementExtension: - statementXPropertyName: statementXPropertyValue - - effect: allow - principal: - - arn:aws:iam::123456789012:user/alex.wichmann - - arn:aws:iam::123456789012:user/dec.kolakowski - action: sqs:CreateQueue - x-policyExtension: - policyXPropertyName: policyXPropertyValue - tags: - owner: AsyncAPI.NET - platform: AsyncAPIOrg - x-queueExtension: - queueXPropertyName: queueXPropertyValue - deadLetterQueue: - name: myQueue_error - deliveryDelay: 0 - visibilityTimeout: 0 - receiveMessageWaitTime: 0 - messageRetentionPeriod: 604800 - policy: - statements: - - effect: allow - principal: arn:aws:iam::123456789012:user/alex.wichmann - action: - - sqs:* - x-internalObject: - myExtensionPropertyName: myExtensionPropertyValue"; + """ + bindings: + sqs: + queue: + name: myQueue + fifoQueue: true + deduplicationScope: messageGroup + fifoThroughputLimit: perMessageGroupId + deliveryDelay: 30 + visibilityTimeout: 60 + receiveMessageWaitTime: 0 + messageRetentionPeriod: 86400 + redrivePolicy: + deadLetterQueue: + arn: arn:aws:SQS:eu-west-1:0000000:123456789 + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue + maxReceiveCount: 15 + x-redrivePolicyExtension: + redrivePolicyXPropertyName: redrivePolicyXPropertyValue + policy: + statements: + - effect: deny + principal: arn:aws:iam::123456789012:user/alex.wichmann + action: + - sqs:SendMessage + - sqs:ReceiveMessage + x-statementExtension: + statementXPropertyName: statementXPropertyValue + - effect: allow + principal: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski + action: sqs:CreateQueue + x-policyExtension: + policyXPropertyName: policyXPropertyValue + tags: + owner: AsyncAPI.NET + platform: AsyncAPIOrg + x-queueExtension: + queueXPropertyName: queueXPropertyValue + deadLetterQueue: + name: myQueue_error + deliveryDelay: 0 + visibilityTimeout: 0 + receiveMessageWaitTime: 0 + messageRetentionPeriod: 604800 + policy: + statements: + - effect: allow + principal: arn:aws:iam::123456789012:user/alex.wichmann + action: + - sqs:* + x-internalObject: + myExtensionPropertyName: myExtensionPropertyValue + """; var channel = new AsyncApiChannel(); channel.Bindings.Add(new SqsChannelBinding() @@ -232,58 +234,60 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = - @"bindings: - sqs: - queues: - - name: myQueue - deliveryDelay: 30 - visibilityTimeout: 60 - receiveMessageWaitTime: 0 - messageRetentionPeriod: 86400 - redrivePolicy: - deadLetterQueue: - arn: arn:aws:SQS:eu-west-1:0000000:123456789 - x-identifierExtension: - identifierXPropertyName: identifierXPropertyValue - maxReceiveCount: 15 - x-redrivePolicyExtension: - redrivePolicyXPropertyName: redrivePolicyXPropertyValue - policy: - statements: - - effect: deny - principal: arn:aws:iam::123456789012:user/alex.wichmann - action: - - sqs:SendMessage - - sqs:ReceiveMessage - x-statementExtension: - statementXPropertyName: statementXPropertyValue - - effect: allow - principal: - - arn:aws:iam::123456789012:user/alex.wichmann - - arn:aws:iam::123456789012:user/dec.kolakowski - action: sqs:CreateQueue - x-policyExtension: - policyXPropertyName: policyXPropertyValue - tags: - owner: AsyncAPI.NET - platform: AsyncAPIOrg - x-queueExtension: - queueXPropertyName: queueXPropertyValue - - name: myQueue_error - deliveryDelay: 0 - visibilityTimeout: 0 - receiveMessageWaitTime: 0 - messageRetentionPeriod: 604800 - policy: - statements: - - effect: allow - principal: arn:aws:iam::123456789012:user/alex.wichmann - action: - - sqs:* - x-queueExtension: - queueXPropertyName: queueXPropertyValue - x-internalObject: - myExtensionPropertyName: myExtensionPropertyValue"; + """ + bindings: + sqs: + queues: + - name: myQueue + deliveryDelay: 30 + visibilityTimeout: 60 + receiveMessageWaitTime: 0 + messageRetentionPeriod: 86400 + redrivePolicy: + deadLetterQueue: + arn: arn:aws:SQS:eu-west-1:0000000:123456789 + x-identifierExtension: + identifierXPropertyName: identifierXPropertyValue + maxReceiveCount: 15 + x-redrivePolicyExtension: + redrivePolicyXPropertyName: redrivePolicyXPropertyValue + policy: + statements: + - effect: deny + principal: arn:aws:iam::123456789012:user/alex.wichmann + action: + - sqs:SendMessage + - sqs:ReceiveMessage + x-statementExtension: + statementXPropertyName: statementXPropertyValue + - effect: allow + principal: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski + action: sqs:CreateQueue + x-policyExtension: + policyXPropertyName: policyXPropertyValue + tags: + owner: AsyncAPI.NET + platform: AsyncAPIOrg + x-queueExtension: + queueXPropertyName: queueXPropertyValue + - name: myQueue_error + deliveryDelay: 0 + visibilityTimeout: 0 + receiveMessageWaitTime: 0 + messageRetentionPeriod: 604800 + policy: + statements: + - effect: allow + principal: arn:aws:iam::123456789012:user/alex.wichmann + action: + - sqs:* + x-queueExtension: + queueXPropertyName: queueXPropertyValue + x-internalObject: + myExtensionPropertyName: myExtensionPropertyValue + """; var operation = new AsyncApiOperation(); operation.Bindings.Add(new SqsOperationBinding() diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs index f9c9c6f7..c437f15c 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/StringOrStringList_Should.cs @@ -63,9 +63,11 @@ public void StringOrStringList_ThrowsArgumentException_WhenIntialisedWithListOfN public void StringOrStringList_WhenValueIsString_SerializesDeserializes() { // Arrange - var expected = @"bindings: - testBinding: - testProperty: someValue"; + var expected = """ + bindings: + testBinding: + testProperty: someValue + """; var channel = new AsyncApiChannel(); channel.Bindings.Add(new StringOrStringListTestBinding @@ -91,12 +93,14 @@ public void StringOrStringList_WhenValueIsString_SerializesDeserializes() public void StringOrStringList_WhenValueIsStringList_SerializesDeserializes() { // Arrange - var expected = @"bindings: - testBinding: - testProperty: - - someValue01 - - someValue02 - - someValue03"; + var expected = """ + bindings: + testBinding: + testProperty: + - someValue01 + - someValue02 + - someValue03 + """; var channel = new AsyncApiChannel(); channel.Bindings.Add(new StringOrStringListTestBinding diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs index 67d71ff5..b8aef976 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs @@ -16,13 +16,15 @@ public void WebSocketChannelBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - websockets: - method: POST - query: - description: this mah query - headers: - description: this mah binding"; + """ + bindings: + websockets: + method: POST + query: + description: this mah query + headers: + description: this mah binding + """; var channel = new AsyncApiChannel(); channel.Bindings.Add(new WebSocketsChannelBinding diff --git a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj index 3249426d..bd35d2ae 100644 --- a/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj +++ b/test/LEGO.AsyncAPI.Tests/LEGO.AsyncAPI.Tests.csproj @@ -1,34 +1,35 @@  - net6.0 - disable - enable - false - $(NoWarn);SA1600 + 11 + net8.0 + disable + enable + false + $(NoWarn);SA1600 - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + diff --git a/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs index c91187ca..bbccf86e 100644 --- a/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/MQTT/MQTTBindings_Should.cs @@ -16,20 +16,22 @@ public void MQTTServerBinding_FilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"url: https://example.com -protocol: mqtt -bindings: - mqtt: - clientId: guest - cleanSession: true - lastWill: - topic: /last-wills - qos: 2 - message: Guest gone offline. - retain: false - keepAlive: 60 - sessionExpiryInterval: 600 - maximumPacketSize: 1200"; + """ + url: https://example.com + protocol: mqtt + bindings: + mqtt: + clientId: guest + cleanSession: true + lastWill: + topic: /last-wills + qos: 2 + message: Guest gone offline. + retain: false + keepAlive: 60 + sessionExpiryInterval: 600 + maximumPacketSize: 1200 + """; var server = new AsyncApiServer(); server.Url = "https://example.com"; @@ -69,11 +71,13 @@ public void MQTTOperationBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - mqtt: - qos: 2 - retain: true - messageExpiryInterval: 60"; + """ + bindings: + mqtt: + qos: 2 + retain: true + messageExpiryInterval: 60 + """; var operation = new AsyncApiOperation(); operation.Bindings.Add(new MQTTOperationBinding @@ -101,12 +105,14 @@ public void MQTTMessageBinding_WithFilledObject_SerializesAndDeserializes() { // Arrange var expected = -@"bindings: - mqtt: - correlationData: - type: string - format: uuid - contentType: application/json"; + """ + bindings: + mqtt: + correlationData: + type: string + format: uuid + contentType: application/json + """; var message = new AsyncApiMessage(); diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs index 641c763e..ef24e07b 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs @@ -15,18 +15,20 @@ internal class AsyncApiChannel_Should : TestBase [Test] public void AsyncApiChannel_WithWebSocketsBinding_Serializes() { - var expected = @"bindings: - websockets: - method: POST - query: - properties: - index: - description: the index - headers: - properties: - x-correlation-id: - description: the correlationid - bindingVersion: 0.1.0"; + var expected = """ + bindings: + websockets: + method: POST + query: + properties: + index: + description: the index + headers: + properties: + x-correlation-id: + description: the correlationid + bindingVersion: 0.1.0 + """; var channel = new AsyncApiChannel { @@ -77,11 +79,13 @@ public void AsyncApiChannel_WithWebSocketsBinding_Serializes() public void AsyncApiChannel_WithKafkaBinding_Serializes() { var expected = -@"bindings: - kafka: - topic: topic - partitions: 5 - replicas: 2"; + """ + bindings: + kafka: + topic: topic + partitions: 5 + replicas: 2 + """; var channel = new AsyncApiChannel { diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs index 974fbfb2..a37f1503 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiMessage_Should.cs @@ -20,19 +20,21 @@ public void AsyncApiMessage_WithNoType_DeserializesToDefault() { // Arrange var expected = - @"{ - ""payload"": { - ""type"": ""object"", - ""properties"": { - ""someProp"": { - ""enum"": [ - ""test"", - ""test2"" - ] - } - } - } - }"; + """ + { + "payload": { + "type": "object", + "properties": { + "someProp": { + "enum": [ + "test", + "test2" + ] + } + } + } + } + """; // Act var message = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); @@ -47,12 +49,14 @@ public void AsyncApiMessage_WithNoSchemaFormat_DeserializesToDefault() { // Arrange var expected = -@"payload: - properties: - propertyA: - type: - - 'null' - - string"; + """ + payload: + properties: + propertyA: + type: + - 'null' + - string + """; // Act var message = new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); @@ -67,13 +71,15 @@ public void AsyncApiMessage_WithUnsupportedSchemaFormat_DeserializesWithError() { // Arrange var expected = -@"payload: - properties: - propertyA: - type: - - 'null' - - string -schemaFormat: application/vnd.apache.avro;version=1.9.0"; + """ + payload: + properties: + propertyA: + type: + - 'null' + - string + schemaFormat: application/vnd.apache.avro;version=1.9.0 + """; // Act new AsyncApiStringReader().ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out var diagnostic); @@ -88,12 +94,14 @@ public void AsyncApiMessage_WithNoSchemaFormat_DoesNotSerializeSchemaFormat() { // Arrange var expected = -@"payload: - properties: - propertyA: - type: - - 'null' - - string"; + """ + payload: + properties: + propertyA: + type: + - 'null' + - string + """; var message = new AsyncApiMessage(); message.Payload = new AsyncApiSchema() @@ -126,13 +134,15 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() { // Arrange var expected = -@"payload: - properties: - propertyA: - type: - - 'null' - - string -schemaFormat: application/vnd.aai.asyncapi+json;version=2.6.0"; + """ + payload: + properties: + propertyA: + type: + - 'null' + - string + schemaFormat: application/vnd.aai.asyncapi+json;version=2.6.0 + """; var message = new AsyncApiMessage(); message.SchemaFormat = "application/vnd.aai.asyncapi+json;version=2.6.0"; @@ -163,72 +173,74 @@ public void AsyncApiMessage_WithSchemaFormat_Serializes() public void AsyncApiMessage_WithFilledObject_Serializes() { var expected = -@"headers: - title: HeaderTitle - description: HeaderDescription - writeOnly: true - examples: - - x-correlation-id: nil -payload: - properties: - propA: - type: string - propB: - type: string -correlationId: - description: CorrelationDescription - location: Header - x-extension-a: a -contentType: MessageContentType -name: MessageName -title: MessageTitle -summary: MessageSummary -description: MessageDescription -tags: - - name: tagA - description: a -externalDocs: - description: example docs description - url: https://example.com/docs -bindings: - http: - headers: - title: SchemaTitle - description: SchemaDescription - writeOnly: true - examples: - - cKey: c - dKey: 1 -examples: - - payload: - PropA: a - PropB: b -traits: - - headers: - title: SchemaTitle - description: SchemaDescription - writeOnly: true - examples: - - eKey: e - fKey: 1 - name: MessageTraitName - title: MessageTraitTitle - summary: MessageTraitSummary - description: MessageTraitDescription - tags: - - name: tagB - description: b - externalDocs: - description: example docs description - url: https://example.com/docs - examples: - - name: MessageExampleName - summary: MessageExampleSummary - payload: - gKey: g - hKey: true - x-extension-b: b - x-extension-c: c"; + """ + headers: + title: HeaderTitle + description: HeaderDescription + writeOnly: true + examples: + - x-correlation-id: nil + payload: + properties: + propA: + type: string + propB: + type: string + correlationId: + description: CorrelationDescription + location: Header + x-extension-a: a + contentType: MessageContentType + name: MessageName + title: MessageTitle + summary: MessageSummary + description: MessageDescription + tags: + - name: tagA + description: a + externalDocs: + description: example docs description + url: https://example.com/docs + bindings: + http: + headers: + title: SchemaTitle + description: SchemaDescription + writeOnly: true + examples: + - cKey: c + dKey: 1 + examples: + - payload: + PropA: a + PropB: b + traits: + - headers: + title: SchemaTitle + description: SchemaDescription + writeOnly: true + examples: + - eKey: e + fKey: 1 + name: MessageTraitName + title: MessageTraitTitle + summary: MessageTraitSummary + description: MessageTraitDescription + tags: + - name: tagB + description: b + externalDocs: + description: example docs description + url: https://example.com/docs + examples: + - name: MessageExampleName + summary: MessageExampleSummary + payload: + gKey: g + hKey: true + x-extension-b: b + x-extension-c: c + """; var message = new AsyncApiMessage { diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs index 29d59488..5597b211 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiOperation_Should.cs @@ -30,10 +30,12 @@ public void SerializeV2_WithNullWriter_Throws() public void SerializeV2_WithMultipleMessages_SerializesWithOneOf() { // Arrange - var expected = @"message: - oneOf: - - name: First Message - - name: Second Message"; + var expected = """ + message: + oneOf: + - name: First Message + - name: Second Message + """; var asyncApiOperation = new AsyncApiOperation(); asyncApiOperation.Message.Add(new AsyncApiMessage { Name = "First Message" }); @@ -56,8 +58,10 @@ public void SerializeV2_WithMultipleMessages_SerializesWithOneOf() public void SerializeV2_WithSingleMessage_Serializes() { // Arrange - var expected = @"message: - name: First Message"; + var expected = """ + message: + name: First Message + """; var asyncApiOperation = new AsyncApiOperation(); asyncApiOperation.Message.Add(new AsyncApiMessage { Name = "First Message" }); @@ -79,17 +83,19 @@ public void SerializeV2_WithSingleMessage_Serializes() public void AsyncApiOperation_WithBindings_Serializes() { var expected = -@"bindings: - http: - type: request - method: PUT - query: - description: some query - kafka: - groupId: - description: some Id - clientId: - description: some Id"; + """ + bindings: + http: + type: request + method: PUT + query: + description: some query + kafka: + groupId: + description: some Id + clientId: + description: some Id + """; var operation = new AsyncApiOperation { diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs index 895d063f..c4491e40 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiReference_Should.cs @@ -15,8 +15,10 @@ public class AsyncApiReference_Should : TestBase public void AsyncApiReference_WithExternalFragmentUriReference_AllowReference() { // Arrange - var actual = @"payload: - $ref: http://example.com/some-resource#/path/to/external/fragment"; + var actual = """ + payload: + $ref: http://example.com/some-resource#/path/to/external/fragment + """; var reader = new AsyncApiStringReader(); // Act @@ -41,8 +43,10 @@ public void AsyncApiReference_WithExternalFragmentUriReference_AllowReference() public void AsyncApiReference_WithFragmentReference_AllowReference() { // Arrange - var actual = @"payload: - $ref: /fragments/myFragment"; + var actual = """ + payload: + $ref: /fragments/myFragment + """; var reader = new AsyncApiStringReader(); // Act @@ -67,8 +71,10 @@ public void AsyncApiReference_WithFragmentReference_AllowReference() public void AsyncApiReference_WithInternalComponentReference_AllowReference() { // Arrange - var actual = @"payload: - $ref: '#/components/schemas/test'"; + var actual = """ + payload: + $ref: '#/components/schemas/test' + """; var reader = new AsyncApiStringReader(); // Act @@ -92,8 +98,10 @@ public void AsyncApiReference_WithInternalComponentReference_AllowReference() public void AsyncApiReference_WithExternalFragmentReference_AllowReference() { // Arrange - var actual = @"payload: - $ref: ./myjsonfile.json#/fragment"; + var actual = """ + payload: + $ref: ./myjsonfile.json#/fragment + """; var reader = new AsyncApiStringReader(); // Act @@ -116,8 +124,10 @@ public void AsyncApiReference_WithExternalFragmentReference_AllowReference() public void AsyncApiReference_WithExternalComponentReference_AllowReference() { // Arrange - var actual = @"payload: - $ref: ./someotherdocument.json#/components/schemas/test"; + var actual = """ + payload: + $ref: ./someotherdocument.json#/components/schemas/test + """; var reader = new AsyncApiStringReader(); // Act @@ -141,17 +151,19 @@ public void AsyncApiReference_WithExternalComponentReference_AllowReference() public void AsyncApiDocument_WithInternalComponentReference_ResolvesReference() { // Arrange - var actual = @"asyncapi: 2.6.0 -info: - title: My AsyncAPI Document - version: 1.0.0 -channels: - myChannel: - $ref: '#/components/channels/myChannel' -components: - channels: - myChannel: - description: customDescription"; + var actual = """ + asyncapi: 2.6.0 + info: + title: My AsyncAPI Document + version: 1.0.0 + channels: + myChannel: + $ref: '#/components/channels/myChannel' + components: + channels: + myChannel: + description: customDescription + """; var settings = new AsyncApiReaderSettings() { @@ -178,13 +190,15 @@ public void AsyncApiDocument_WithInternalComponentReference_ResolvesReference() public void AsyncApiDocument_WithExternalReference_DoesNotResolve() { // 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 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() { @@ -212,8 +226,10 @@ public void AsyncApiDocument_WithExternalReference_DoesNotResolve() public void AsyncApiReference_WithExternalReference_AllowsReferenceDoesNotResolve() { // Arrange - var actual = @"payload: - $ref: http://example.com/json.json"; + var actual = """ + payload: + $ref: http://example.com/json.json + """; var reader = new AsyncApiStringReader(); // Act diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index f6c56063..830a44bc 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -301,19 +301,21 @@ public void SerializeAsJson_WithBasicSchema_V2Works() public void SerializeAsJson_WithAdvancedSchemaNumber_V2Works() { // Arrange - var expected = @"{ - ""title"": ""title1"", - ""type"": ""integer"", - ""maximum"": 42, - ""minimum"": 10, - ""exclusiveMinimum"": true, - ""multipleOf"": 3, - ""default"": 15, - ""nullable"": true, - ""externalDocs"": { - ""url"": ""http://example.com/externalDocs"" - } -}"; + var expected = """ + { + "title": "title1", + "type": "integer", + "maximum": 42, + "minimum": 10, + "exclusiveMinimum": true, + "multipleOf": 3, + "default": 15, + "nullable": true, + "externalDocs": { + "url": "http://example.com/externalDocs" + } + } + """; // Act var actual = AdvancedSchemaNumber.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); @@ -327,19 +329,21 @@ public void SerializeAsJson_WithAdvancedSchemaNumber_V2Works() public void SerializeAsJson_WithAdvancedSchemaBigNumbers_V2Works() { // Arrange - var expected = @"{ - ""title"": ""title1"", - ""type"": ""integer"", - ""maximum"": 1.7976931348623157E+308, - ""minimum"": -1.7976931348623157E+308, - ""exclusiveMinimum"": true, - ""multipleOf"": 3, - ""default"": 15, - ""nullable"": true, - ""externalDocs"": { - ""url"": ""http://example.com/externalDocs"" - } -}"; + var expected = """ + { + "title": "title1", + "type": "integer", + "maximum": 1.7976931348623157E+308, + "minimum": -1.7976931348623157E+308, + "exclusiveMinimum": true, + "multipleOf": 3, + "default": 15, + "nullable": true, + "externalDocs": { + "url": "http://example.com/externalDocs" + } + } + """; // Act var actual = AdvancedSchemaBigNumbers.SerializeAsJson(AsyncApiVersion.AsyncApi2_0); diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs index 5c0a464e..8b14560b 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiServer_Should.cs @@ -16,23 +16,25 @@ public void AsyncApiServer_Serializes() { // Arrange var expected = -@"url: 'https://example.com/{channelkey}' -protocol: test -protocolVersion: 0.1.0 -description: some description -variables: - channelkey: - description: some description -security: - - schem1: - - requirement -tags: - - name: mytag1 - description: description of tag1 -bindings: - kafka: - schemaRegistryUrl: http://example.com - schemaRegistryVendor: kafka"; + """ + url: 'https://example.com/{channelkey}' + protocol: test + protocolVersion: 0.1.0 + description: some description + variables: + channelkey: + description: some description + security: + - schem1: + - requirement + tags: + - name: mytag1 + description: description of tag1 + bindings: + kafka: + schemaRegistryUrl: http://example.com + schemaRegistryVendor: kafka + """; var server = new AsyncApiServer { @@ -79,12 +81,14 @@ public void AsyncApiServer_Serializes() public void AsyncApiServer_WithKafkaBinding_Serializes() { var expected = -@"url: -protocol: -bindings: - kafka: - schemaRegistryUrl: http://example.com - schemaRegistryVendor: kafka"; + """ + url: + protocol: + bindings: + kafka: + schemaRegistryUrl: http://example.com + schemaRegistryVendor: kafka + """; var server = new AsyncApiServer { Bindings = new AsyncApiBindings From cf6cf6d31575bbab096c08ef08c26a10af3eb2a2 Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Sat, 30 Mar 2024 20:49:42 +0000 Subject: [PATCH 58/84] chore: update CHANGELOG.md --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e4adc04..90780c52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +# [5.2.0](https://github.com/LEGO/AsyncAPI.NET/compare/v5.1.1...v5.2.0) (2024-03-30) + + +### Features + +* add cultureinfo to reader/writer settings. ([#152](https://github.com/LEGO/AsyncAPI.NET/issues/152)) ([0199420](https://github.com/LEGO/AsyncAPI.NET/commit/01994205ecde4e17317762374b03ec23aad17022)) +* **bindings:** add amqp bindings ([#153](https://github.com/LEGO/AsyncAPI.NET/issues/153)) ([8d128db](https://github.com/LEGO/AsyncAPI.NET/commit/8d128db869d8164cfaad156d4f29a7130a00827e)) +* **bindings:** add mqtt bindings ([#154](https://github.com/LEGO/AsyncAPI.NET/issues/154)) ([f5529e0](https://github.com/LEGO/AsyncAPI.NET/commit/f5529e0e96d139e0cb1958d6b0620ed826e21cb5)) +* improve AsyncApiAny api surface. ([9063f4e](https://github.com/LEGO/AsyncAPI.NET/commit/9063f4e4f19929f8ccbdee5bd46dd9e27a3e0c08)) +* targetframework to netstandard2.0 ([#150](https://github.com/LEGO/AsyncAPI.NET/issues/150)) ([9291da6](https://github.com/LEGO/AsyncAPI.NET/commit/9291da603335fd202b59f421945629952f136296)) + ## [5.1.1](https://github.com/LEGO/AsyncAPI.NET/compare/v5.1.0...v5.1.1) (2024-02-16) From 7fd3af0e6669d18e805e0bab9cafac819ea64c1d Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Tue, 21 May 2024 13:34:27 +0200 Subject: [PATCH 59/84] fix: inline channel parameters should not deserialize as references (#172) --- .../V2/AsyncApiChannelDeserializer.cs | 2 +- .../Models/AsyncApiChannel_Should.cs | 21 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs index cc74e0c0..1d6acae6 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs @@ -14,7 +14,7 @@ internal static partial class AsyncApiV2Deserializer { "servers", (a, n) => { a.Servers = n.CreateSimpleList(s => s.GetScalarValue()); } }, { "subscribe", (a, n) => { a.Subscribe = LoadOperation(n); } }, { "publish", (a, n) => { a.Publish = LoadOperation(n); } }, - { "parameters", (a, n) => { a.Parameters = n.CreateMapWithReference(ReferenceType.Parameter, LoadParameter); } }, + { "parameters", (a, n) => { a.Parameters = n.CreateMap(LoadParameter); } }, { "bindings", (a, n) => { a.Bindings = LoadChannelBindings(n); } }, }; diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs index ef24e07b..50c6768b 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiChannel_Should.cs @@ -3,15 +3,36 @@ namespace LEGO.AsyncAPI.Tests.Models { using System.Collections.Generic; + using System.Linq; using FluentAssertions; using LEGO.AsyncAPI.Bindings.Kafka; using LEGO.AsyncAPI.Bindings.WebSockets; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; + using LEGO.AsyncAPI.Readers; using NUnit.Framework; internal class AsyncApiChannel_Should : TestBase { + [Test] + public void AsyncApiChannel_WithInlineParameter_DoesNotCreateReference() + { + var input = + """ + parameters: + id: + description: ids + schema: + type: string + enum: + - 08735ae0-6a1a-4578-8b4a-35aa26d15993 + - 97845c62-329c-4d87-ad24-4f611b909a10 + """; + + var channel = new AsyncApiStringReader().ReadFragment(input, AsyncApiVersion.AsyncApi2_0, out var _ ); + channel.Parameters.First().Value.Reference.Should().BeNull(); + } + [Test] public void AsyncApiChannel_WithWebSocketsBinding_Serializes() { From c27cdd5453b69d44112a3373a6bd7f36d20e7255 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Tue, 28 May 2024 09:19:47 +0200 Subject: [PATCH 60/84] ci: add logo to NuGet package (#173) --- Common.Build.props | 5 +++++ media/logo.png | Bin 0 -> 7975 bytes .../LEGO.AsyncAPI.Bindings.csproj | 7 +------ .../LEGO.AsyncAPI.Readers.csproj | 6 ------ src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj | 4 ---- 5 files changed, 6 insertions(+), 16 deletions(-) create mode 100644 media/logo.png diff --git a/Common.Build.props b/Common.Build.props index 78aa4707..277a2772 100644 --- a/Common.Build.props +++ b/Common.Build.props @@ -9,5 +9,10 @@ README.md https://github.com/LEGO/AsyncAPI.NET asyncapi .net openapi documentation + logo.png + + + + \ No newline at end of file diff --git a/media/logo.png b/media/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1a25d72c6ad62640da96f1686e414483e4cce8ea GIT binary patch literal 7975 zcmbW6bx<5nxA%iff(Lg`kRXe@F7AuFI|K=C0TLuEu(&PmvVp~dCn3RIf=h6BSe(o6 z-luLo^}P45x29(L)T!!k_ssP4IdeWSn(7KT*c8|=UcA6jQk2tvZX5nNG0~r|;=|~? z&keejih|sWr+<$8juX4B}g-zuYvaFDBYd*Z?v>4;g?g-M{m4rR;A`$BaZ^ zUL!)dcL5>RwemFcYc}w(@#)TRG{8+|k`GK4*G1q*NES+&Ld)c>GOW?jsEWScg}Vw< zl9i-?v?|hz`EmUf<(u=wn5qLhQPvL&835U{6@fMUZx(j}`70K_e|-P4eni@FXN>*h z`%|3{`E^FMVWxB4yrk8vJDBNiF!Kb)9w7eJFs0|Yjkados1 zs`EFViOT3`4l^g1$`}|9+s@K#dl01s*vs*>dbmT;=%)BXs)I@g$NbeOKCvWau_^6c zg-ZbXq4`!3igPe1w~D|OP!v=Tyi2=w7=0~y7{ssm$yBD{#s*=tVxkaNq7&_NfvvsU zhBObD(+J$Q-$DHJ^S<9I3jTr?GL$c_^8mUH35zvK}n9t)wmb3q-J8__ z*cI2MR=+3`n*(iWPX0qjloNermF?J67iPQJs{iS^raY1tqM82dOSDN&8Ng_*TS7asEMOGMHyZEe>REsqttO)7fY(LZUH^Ou&`J=%1A;{6(E)G{Ob zfYZPm*PpFvoGVE=9*0l-ypq=zHr585@8M&8FB@H26&*HNkS|doc?2dx328*9?KTF*!ZsH~Z$%X(a(TZxh+-jP&&g;arj_h!D zZPCuNONseYKbB3K-|^PPW51w{VUOo6;qB+BF}=}}&L^ahQBu_2yb|b`@t)myC+z{N zOBdZ;o(jLwEfgp@GUYHXNb%6AShRLIqL^aLY}|a~b1}p;;3e^cGR>LKx*b^b*@>?< z52$fDRvst_Ze8-hOpwSQuh($kyEO%qj7*ff)DM3+=V$Xbo$a2?2?dEh#+lri(>h-C zs8C$)7ioQs!b5s`(tGM#;mUw%jnj_s`}BVgbKIOY5&hzm;3d(CvX=Sh-5-Y$8*phz z;kZ4m6Q7I4Mi-HS^TpC@@E@)(_qj!Q<_8!}QR5 z1piXx@;rko#$#<$x72cHg5%VN7#LpP5c{#``}X9{)I7zKHxxK20me7?;dG(1ROo@q zNnae7IZc(70ZDVdYHc!ZAR175=1X-qKDn8fl~!QSIPqPHgWX!BivSvvZx|U*J*Hq4 zMN{DY#aoIR zV+O=|H}PH6Dt`36PPJSXaQ%pDY$&6QrifC<*Kri$5r>hh)Gc)61m-FY41Z+xG!75ojJrdm-qtu^& zYtIy(MqW7A#3d|OxE?Uaivo+7y_jMZQJX#CctwizRA=yEGy(Bbnv)Fc_yW=6_TtrkdT&vjkiz zHa9mRM03sVw82jgh>M+~ggv?XR*8dkJ214vq-#Zk)Z{(V<<5A7#LWhg(YN>Xu1l@c z-p42yxSG<^NStQv89j02vKkUMOiA{}W9#zj7^oZ+GvDASJ1UN# zQp@1}n8smPnBd+UPksAsU|_&uFpcwK|7+LY@xk-M8CxNL9SL>ioSgbSii(FP!>Ik4 zyT&6-zp*}~oJd{hBQTDvfQUIa=;3lE&1mmY6wh&Ma{pJTumWu`pZMu7q6VKsBTsfw z-=pGO@xVRWsMxgdo?eWXyx$-=lmcTaTph0wC@<0ZZxQ+sf3~^uac0uFgyRVnu97&Tvh~@{;I_w)hK??Y_UZ4Z z8(pbGgE);E^3h(B_zA!Y3mICsa|1b!5f??Cq%KBSU_~fT1`^nDlck%m$$m69lErQg zZtJP0F9;88TBM@>km9Dtq;u5u6ryl-vf$pOc856D*fWrLxX`gYLoK$BZVtCZ`xT2c z$wGd1yZ=>1vZ=Zj*up;ixU0qwpHy(LF(>ch%|M37h#odfdb=i-za}9uYK^EZHp(Qc z6;9^tdU^~r;mr%V-EFY?jjf#8em)|Kl_~jj-7C2x`E;W((fcVl{NPdi2;jV1sK7fZ z>wv0h1EVuseW7yR?th-bf|8vWDMrWdv|}VrjC_ui%{nLayvDK;9XF@_-kY9eC!J7MoUOvq3^aGcdFcctBp$Z zm!N4~aPiqNr%O z;^Kk6l4_VWvk_MFH_U= zvb>Nd~SAXfe^fsr1;b#j8x8s?1&_k<(jtZtr2J-_x<7TTomptUplTg z%#py&dYuLGY+=E0QWVJw>H-2gwNyH)xV{n|!`%ox3C&JBb#+|RkekiY?eLD>RCh5^ zV?P`LHxSKwBE77Yghk|nKFlQZNq{(}6;(y9X06q%%gwtwJyURCfU1P6$|{(4D@#M$sgc2Mdqd!d zcQK1(#GmNe{@j0^l-e;Z8k%n@l1HA{880#=!cr-wF2KO0Di3;~4ZVj1T<#G^^wI>z zwSL$KHZKQMsh5Aop;Uet8qeUhjh%Qlz-P_aNK zi+w#9$2x`KiggD@US;GiJ3LA(d|8Ew1(=?j_lmKF9`75Srs2bd^+d%g^P9+wLNBz- zF?)PRgtxzq9J?;Jx10fFx_`F`AS=gUGbH!)ZMrrum5TL<9)0Wf3{w1|2EtEBG)rawuewEwHaFH(;RA2RKkhoe4T<#=be}#uFbOA!1brwMrsO zo${H>s}F1qXH%OK=`ylHy_0rDqHQW$XVwm>YazN2!DYX0?;0JaOceG77#?%NSl|=X zl5JhuHKSU93zBZZoNnS7!Fs#cF{9y!GXDp^+EmgNxonpEspv>1bnu z)iZmt8y&WmU}9Pegkk0&-Na87HOA%#6!zW@7eEoDs4dm*F8a5BY}B1)(XlECrRQ=imQ5> z7mf({iMH++K_Wb>rIbWd<)Ys@*yHOG%v7{w-%8hG4KAw^ zQOtB*lu)lFTC|esqS+mV=c~pbw{~DQ)qz;fhI1pf^FrTIqjCN(5E)x>yNPO8#E z<}&3@I?G#CzyzxKS}01P@txAgSm1TsoF*12tY9|ex8q3Sit;dZ_M1O>Gg1RH|Yi=q#4z88kMta5s|>~##12tku+MaLTI)K z4aS^_H;rl2QerfQ&pOXpkyRR7Uh1*D_!`B6$T^iFY+jr=Q!+GULY3TRs~;8>GTHMu zAJFBlfkj8)wjB$px>;0;Sy63mOty%)q|2gKH*Y}WWb_&&P$sHJ0g~eC^FT1 z+*0AdXg?md$36!SZO^=TC%4(g@gHaoWop?1Z%wOwkC%ByJtx%!{ds}KkrdlXmqp3J zRKRi)o-jtZ9LF0a#)3NE^OP|m1Wb(eipAlIHSqg$X13f&iu8`{dD+>IfUp@p_N-}t zEp)3CfDHj`8U}T%v~6fGty#6FB^YUgFCZ6LgWDAMqg~f)xyvPT4u->YxD%(mbsaJ` znfen~RaU+^)t%0~KYFcSX&{bQ%y*5-y-(S~?O)>$&A!r>Po#DvjrUE~3EoWA7g1J} ziG^<;dV2&^odO%x(-0r@`e(o-4&tnlKFNmZ<@rg*f96$6LU0-Oceixg&{Evx&6_py zB$NPmozD@q#JQWD2_X1h0NignBXA&IfOy#OdcD_!y8xcueCY>4j=?EQ<`!;{oL~I; zg}zO)(7Vz{$cY9#Bg?AIg_^oOJ|i#dL;O*V*8OqZSiTXBo1ss=vfXlP;7rhjfFeqd zqAQCuV33w@>1ruD`SmzpTD(`QpGTEU|z(kQW4Q@9dmeT=R9uas`br-yjU-PXn) zrYiZfNNc`nYLItG>%S!dzWP4WcKZBY18#Fig$Jvl9uu(bsF@;g#VHU>IdG1%2r?Qx zP-drNC}mSWk3Y=c%Zf|aiNoiW`K#D1HxVCep>xy}&{Mu?+qr$YKf`WHIm*0WC%9@D z)8gSj8z_?=yepV$*dJ@*kr7YX@|?X;Co6pbAm7mlvzXx<@ssa95AEX}oO&Rh@nRf| z=W!Gp>wUZ|{xV zaR(yhC}Po$t1qs^#PMImA=}o%+2)`%?BkFWaa~oA0!2;bww&ES{t`S{!Uu*0 z4UL&IjgqAr{yPwsYgNwnW=Sbhv(oh1c3v9odEe=rcz_V^G(>bN4`R$Vy1mqc@RMq^ z(1lm+y=$zj>}}aAwNQ@Wc-{~ai+Dt$VTtgg)JnM9lE@%HYNI#lCgsje2&+u=qm!gi znJXdaVc|Ksc2gab{p6>I)5ji`qNfX)c3oS963QaNI$rWpl>u1gcEpM9OIdi$d~dux zzsH~Up>o~VyM+O6L8uazZO)A0G5r{huCh#%dOGn`F`i6 z%l^7e(>N+>)+rw$&3Ry!@qCrNG$BDH)2z+zv%{U&sp&)mBU*NI$?G1!4Aw3RItMzrelt+|; zdSLIJu3*}UZBh4I&ta{wu#hP?Twgf$gp@GFO{0CGJL3}Hil{Q0{<(7JIr8~La!Ip8XJ5vlSi z-)*?+U8$SwfEhDk3o&0`ort~|w9H61nkV$$B2@(t0zl0{r&M;(UDM{x1jgeZT`5LslYc7 zbS&0{fXfpILTkIH{*g5*1RY@9d%|l{9t`Qz^W+3!Y4871RF#uOd$8kNt0rS>?Vh_S{K)G;Y&T^(Yc($5KTWy^VC*~_dW{Zw#^Q6cN z?XhVrmv}5!7qA%NZ4_6dm=w)!f3dstFR#ay=PKwgu0)~CS4BB*GJE%oQupa%Gq53OoQr?< zD=^zd^|Edv*M9{Hnb8D$Pohc?NJHEY=NmV-M#l+tWb;>JYxfyjsEI_r*0o&hwU{km z*}Y#&gNq;|xw?*5GiQb;uq>b98qZfsXbWy_K3>^X=@0e-_Upm$7$*5N>PMJ;JEbsU*my4l4YaqQ|es z2cyf01)vIE+?#vL!YA-qb{KlQ(A;>G{8xUR^qLEDy4etprX*|--!VJh{Z65rgj$VA ziS-kS!mlaz$(Lxac1S3i?Y&E6ZD23@tS|2{t=A)wzoPQzkKfEp@GY3x@uq+29QEI} z^_SU-!~)A2#i7W&L@|u$!Oq613w6OIByfS1YUIHL&wTGoi zs}CbiQ&?t$3?+U#8U}yM#x_AYNY{O|nQs0^>EL24|7Etr#kRJv;YD)cFU!=jnYu1q zfpf**zr`#iEO;5Pu)I+7gUOV8PGGl}``dnTLgpvk&70Qj{(#lDnW>l^mp^MmEWT(F zEzZ8Zu@9GXX1bW@=0T%gC^_mJ7iUZy)IPm`xa*mjIwic;?F|#C4-Rl@;2O;u!qE6~ z%g?(78r2z2W;j+7V39+Cf;?EEV*!N|QuUlTo=brWXRNye0u-*d%ztQ?5j@WIy((dH zcq@z}h*_Hu>2+ZA@Cl@XexQWZxxp$I?c*m;;ghn_143hK_Ygm{_AeO6u`a5%){76B zbY6Xdyj8xm#1U`rSY$|R)J6a2QZ5kR$jJQp6T(u?rxpwBSC?4Q+oh)(} z7klhU3a>LrlY0zSx`HASuwhucOEtLFmr9`+#M1_UlXxVmwKK|WSBw+tybZ!Ud6Q-B z^dAZYp^0jDB!jH3y?M`NZRv)bA zcMNxd3hPni1g@CiBcE3ax&&Zvw-kLZIg}_t`E9B1d>rOnp^|;Jv`k`jADjXK8bUnZ zdV@Xmz+Lf>G0IeXri3BbsKK1Kw!k4Y&+-PJTXt3M`0_ndr9n}^{jls|76A?hk<}LJ z&BW1bXHtcEr5|!gZ*%5@pdxZERxa0ZQ6<^;Wrg~6r*S2FD#`tGAngI2%vvk z8Ed2|#o4krCdsxZX3;;bm)<4BRqJq%88s>*l^c1Ib(aCf2~jg*E;fBACKrakJ3P34 zs0vC6brhvK_P)ca$E4kLm_K(mPcYfSl` z@pA$E?SvbvydE+uyOuX$kXiS&#NuL6{iW^lE@=za^A(A9!K_0Sj$ZV$7Bq; zXl@@L)G3i8WwMkygd3yjPkm*El**|@iU<-MtnC<*~h7WUvM3TA~#ku{dc2HiBUe3u>emV#;NqQ9o z$?=z&!lMwTmQ%+rz>^_+94i#XK)n=D2PIzrN*Nv+)6ugGWL{hQVb{3xs2VK%PQUTT zCnYTb(TG@c%spD=LQ3+3wU;817bVNHIjjUyJSn&&!N^}P2c z;ohYdR^M%3RvxQF|ewM^x%6N6hJ)^ZhsYq8C*JO2q6WcG>i2_N6 z%umm);(TrQjoMLtRrex8f09`8|lCf_fVR literal 0 HcmV?d00001 diff --git a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj index dc76c2ba..9c0b025e 100644 --- a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj +++ b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj @@ -20,12 +20,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - - True - \ - - + diff --git a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj index c47530c0..53071ca0 100644 --- a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj +++ b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj @@ -27,12 +27,6 @@ - - - True - \ - - diff --git a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj index 3bfe1c9c..560c0f98 100644 --- a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj +++ b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj @@ -26,10 +26,6 @@ - - - - From bfec063158e4b5d8d35a20d8caafbe255f95d96e Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Tue, 28 May 2024 09:32:52 +0200 Subject: [PATCH 61/84] ci: include license in package (#174) --- Common.Build.props | 2 ++ LICENSE => LICENSE.txt | 0 2 files changed, 2 insertions(+) rename LICENSE => LICENSE.txt (100%) diff --git a/Common.Build.props b/Common.Build.props index 277a2772..ba84a0f8 100644 --- a/Common.Build.props +++ b/Common.Build.props @@ -10,9 +10,11 @@ https://github.com/LEGO/AsyncAPI.NET asyncapi .net openapi documentation logo.png + LICENSE.txt + \ No newline at end of file diff --git a/LICENSE b/LICENSE.txt similarity index 100% rename from LICENSE rename to LICENSE.txt From fc386d9e5c4bb7c62f6f7cc9b4a63a8a03d95690 Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Wed, 12 Jun 2024 13:40:50 +0000 Subject: [PATCH 62/84] chore: update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90780c52..505c9b09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [5.2.1](https://github.com/LEGO/AsyncAPI.NET/compare/v5.2.0...v5.2.1) (2024-06-12) + + +### Bug Fixes + +* inline channel parameters should not deserialize as references ([#172](https://github.com/LEGO/AsyncAPI.NET/issues/172)) ([7fd3af0](https://github.com/LEGO/AsyncAPI.NET/commit/7fd3af0e6669d18e805e0bab9cafac819ea64c1d)) + # [5.2.0](https://github.com/LEGO/AsyncAPI.NET/compare/v5.1.1...v5.2.0) (2024-03-30) From b1638bd43c82b1c9ac4a01013e337a17ae9a1e9f Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Thu, 13 Jun 2024 09:01:35 +0200 Subject: [PATCH 63/84] chore: update readme (#179) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fe9189f5..593dbd88 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,11 @@ The AsyncAPI.NET SDK contains a useful object model for the AsyncAPI specificati Install the NuGet packages: ### AsyncAPI.NET [![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET?label=AsyncAPI.NET&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) -[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET?label=AsyncAPI.NET&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) +[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET?label=AsyncAPI.NET-Preview&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET/) ### AsyncAPI.Readers [![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Readers?label=AsyncAPI.Readers&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) -[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET.Readers?label=AsyncAPI.Readers&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) +[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET.Readers?label=AsyncAPI.Readers-Preview&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Readers/) ### AsyncAPI.Bindings [![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Bindings?label=AsyncAPI.Bindings&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Bindings/) From 47685cd19c7e58391625be043b1e5d82c49eedc8 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 14 Jun 2024 12:35:18 +0200 Subject: [PATCH 64/84] fix: resolving wrong reference (#180) --- .../Services/AsyncApiReferenceResolver.cs | 8 +++++- .../AsyncApiReaderTests.cs | 27 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs index da6195c6..b3c693e0 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiReferenceResolver.cs @@ -218,7 +218,13 @@ private T ResolveReference(AsyncApiReference reference) try { - return this.currentDocument.ResolveReference(reference) as T; + 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) { diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs index bf19a944..8953f2cd 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiReaderTests.cs @@ -6,6 +6,7 @@ namespace LEGO.AsyncAPI.Tests using System.Collections.Generic; using System.Linq; using System.Text.Json.Nodes; + using FluentAssertions; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; @@ -336,6 +337,32 @@ public void Read_WithBasicPlusSecuritySchemeDeserializes() Assert.AreEqual("Provide your username and password for SASL/SCRAM authentication", scheme.Value.Description); } + [Test] + public void Read_WithWrongReference_AddsError() + { + var yaml = + """ + asyncapi: 2.3.0 + info: + title: test + version: 1.0.0 + channels: + workspace: + publish: + message: + $ref: '#/components/securitySchemes/saslScram' + components: + securitySchemes: + saslScram: + type: scramSha256 + description: Provide your username and password for SASL/SCRAM authentication + """; + 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(); + } + [Test] public void Read_WithBasicPlusOAuthFlowDeserializes() { From 883515cec7e4b73005c547b2c18670080d29cd72 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 14 Jun 2024 18:18:49 +0200 Subject: [PATCH 65/84] ci: remove auto preview release --- .github/workflows/release-internal.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/release-internal.yml b/.github/workflows/release-internal.yml index 971be8e7..1231b9e6 100644 --- a/.github/workflows/release-internal.yml +++ b/.github/workflows/release-internal.yml @@ -1,13 +1,5 @@ name: Publish beta NuGet package on: - push: - branches: [ main ] - paths: - - 'src/LEGO.AsyncAPI/**' - - 'src/LEGO.AsyncAPI.Readers/**' - - 'src/LEGO.AsyncAPI.Bindings/**' - - ".github/workflows/release-internal.yml" - - '!**/*.md' workflow_dispatch: jobs: From b586dd0a492742e203513487be78f8e9b32567eb Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 14 Jun 2024 18:30:50 +0200 Subject: [PATCH 66/84] ci: update release to release from vnext as well --- release.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.config.js b/release.config.js index 7c6ba358..7cf37e87 100644 --- a/release.config.js +++ b/release.config.js @@ -1,5 +1,5 @@ module.exports = { - branches: "main", + branches: ["main", "vnext"], plugins: [ "@semantic-release/commit-analyzer", "@semantic-release/release-notes-generator", From fb50d00192896f9ac26e65df1e991854b33aa17c Mon Sep 17 00:00:00 2001 From: Dec Kolakowski <51292634+dpwdec@users.noreply.github.com> Date: Tue, 9 Jul 2024 14:44:14 +0100 Subject: [PATCH 67/84] =?UTF-8?q?fix:=20correct=20typing=20of=20exclusive?= =?UTF-8?q?=20maximums=20and=20minimums=20for=20draft7=20jso=E2=80=A6=20(#?= =?UTF-8?q?188)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../V2/AsyncApiSchemaDeserializer.cs | 10 ++++++++-- src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs | 4 ++-- .../Models/AsyncApiSchema_Should.cs | 10 +++++----- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs index b99fdef8..1934cb0d 100644 --- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs +++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs @@ -61,7 +61,10 @@ public class JsonSchemaDeserializer } }, { - "exclusiveMaximum", (a, n) => { a.ExclusiveMaximum = bool.Parse(n.GetScalarValue()); } + "exclusiveMaximum", (a, n) => + { + a.ExclusiveMaximum = double.Parse(n.GetScalarValue(), NumberStyles.Float, n.Context.Settings.CultureInfo); + } }, { "minimum", @@ -71,7 +74,10 @@ public class JsonSchemaDeserializer } }, { - "exclusiveMinimum", (a, n) => { a.ExclusiveMinimum = bool.Parse(n.GetScalarValue()); } + "exclusiveMinimum", (a, n) => + { + a.ExclusiveMinimum = double.Parse(n.GetScalarValue(), NumberStyles.Float, n.Context.Settings.CultureInfo); + } }, { "maxLength", (a, n) => { a.MaxLength = int.Parse(n.GetScalarValue(), n.Context.Settings.CultureInfo); } diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs index 3244017b..0931c953 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiSchema.cs @@ -42,7 +42,7 @@ public class AsyncApiSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyn /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public bool? ExclusiveMaximum { get; set; } + public double? ExclusiveMaximum { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. @@ -52,7 +52,7 @@ public class AsyncApiSchema : IAsyncApiReferenceable, IAsyncApiExtensible, IAsyn /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. /// - public bool? ExclusiveMinimum { get; set; } + public double? ExclusiveMinimum { get; set; } /// /// follow JSON Schema definition: https://json-schema.org/draft-07/json-schema-release-notes.html. diff --git a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs index 830a44bc..ec7c023f 100644 --- a/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Models/AsyncApiSchema_Should.cs @@ -21,7 +21,7 @@ public class AsyncApiSchema_Should : TestBase Title = "title1", MultipleOf = 3, Maximum = 42, - ExclusiveMinimum = true, + ExclusiveMinimum = 42, Minimum = 10, Default = new AsyncApiAny(15), Type = SchemaType.Integer, @@ -37,7 +37,7 @@ public class AsyncApiSchema_Should : TestBase Title = "title1", MultipleOf = 3, Maximum = double.MaxValue, - ExclusiveMinimum = true, + ExclusiveMinimum = double.MinValue, Minimum = double.MinValue, Default = new AsyncApiAny(15), Type = SchemaType.Integer, @@ -211,7 +211,7 @@ public class AsyncApiSchema_Should : TestBase Title = "title1", MultipleOf = 3, Maximum = 42, - ExclusiveMinimum = true, + ExclusiveMinimum = 42, Minimum = 10, Default = new AsyncApiAny(15), Type = SchemaType.Integer, @@ -307,7 +307,7 @@ public void SerializeAsJson_WithAdvancedSchemaNumber_V2Works() "type": "integer", "maximum": 42, "minimum": 10, - "exclusiveMinimum": true, + "exclusiveMinimum": 42, "multipleOf": 3, "default": 15, "nullable": true, @@ -335,7 +335,7 @@ public void SerializeAsJson_WithAdvancedSchemaBigNumbers_V2Works() "type": "integer", "maximum": 1.7976931348623157E+308, "minimum": -1.7976931348623157E+308, - "exclusiveMinimum": true, + "exclusiveMinimum": -1.7976931348623157E+308, "multipleOf": 3, "default": 15, "nullable": true, From a554e419149b261ab8b5768743921614131a9270 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Mon, 29 Jul 2024 11:49:38 +0200 Subject: [PATCH 68/84] refactor: improve bindings TryGetValue (#190) --- ...dings.cs => AsyncApiBindings{TBinding}.cs} | 27 ------ src/LEGO.AsyncAPI/Models/BindingExtensions.cs | 62 +++++++++++++ .../Bindings/BindingExtensions_Should.cs | 89 +++++++++++++++++++ .../WebSockets/WebSocketBindings_Should.cs | 4 +- 4 files changed, 153 insertions(+), 29 deletions(-) rename src/LEGO.AsyncAPI/Models/{AsyncApiBindings.cs => AsyncApiBindings{TBinding}.cs} (57%) create mode 100644 src/LEGO.AsyncAPI/Models/BindingExtensions.cs create mode 100644 test/LEGO.AsyncAPI.Tests/Bindings/BindingExtensions_Should.cs diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs similarity index 57% rename from src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs rename to src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs index 6aaa389a..f11858aa 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiBindings.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs @@ -7,33 +7,6 @@ namespace LEGO.AsyncAPI.Models using LEGO.AsyncAPI.Models.Interfaces; using LEGO.AsyncAPI.Writers; - public static class BindingExtensions - { - public static bool TryGetValue(this AsyncApiBindings bindings, out IServerBinding binding) - where TBinding : IServerBinding - { - return bindings.TryGetValue(Activator.CreateInstance().BindingKey, out binding); - } - - public static bool TryGetValue(this AsyncApiBindings bindings, out IChannelBinding binding) - where TBinding : IChannelBinding - { - return bindings.TryGetValue(Activator.CreateInstance().BindingKey, out binding); - } - - public static bool TryGetValue(this AsyncApiBindings bindings, out IOperationBinding binding) - where TBinding : IOperationBinding - { - return bindings.TryGetValue(Activator.CreateInstance().BindingKey, out binding); - } - - public static bool TryGetValue(this AsyncApiBindings bindings, out IMessageBinding binding) - where TBinding : IMessageBinding - { - return bindings.TryGetValue(Activator.CreateInstance().BindingKey, out binding); - } - } - public class AsyncApiBindings : Dictionary, IAsyncApiReferenceable where TBinding : IBinding { diff --git a/src/LEGO.AsyncAPI/Models/BindingExtensions.cs b/src/LEGO.AsyncAPI/Models/BindingExtensions.cs new file mode 100644 index 00000000..3bcab20f --- /dev/null +++ b/src/LEGO.AsyncAPI/Models/BindingExtensions.cs @@ -0,0 +1,62 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Models +{ + using System; + using LEGO.AsyncAPI.Models.Interfaces; + + public static class BindingExtensions + { + public static bool TryGetValue(this AsyncApiBindings bindings, out TBinding binding) + where TBinding : class, IServerBinding + { + if (bindings.TryGetValue(Activator.CreateInstance().BindingKey, out var serverBinding)) + { + binding = serverBinding as TBinding; + return true; + } + + binding = default; + return false; + } + + public static bool TryGetValue(this AsyncApiBindings bindings, out TBinding binding) + where TBinding : class, IChannelBinding + { + if (bindings.TryGetValue(Activator.CreateInstance().BindingKey, out var channelBinding)) + { + binding = channelBinding as TBinding; + return true; + } + + binding = default; + return false; + } + + public static bool TryGetValue(this AsyncApiBindings bindings, out TBinding binding) + where TBinding : class, IOperationBinding + { + if (bindings.TryGetValue(Activator.CreateInstance().BindingKey, out var operationBinding)) + { + binding = operationBinding as TBinding; + return true; + } + + binding = default; + return false; + } + + public static bool TryGetValue(this AsyncApiBindings bindings, out TBinding binding) + where TBinding : class, IMessageBinding + { + if (bindings.TryGetValue(Activator.CreateInstance().BindingKey, out var messageBinding)) + { + binding = messageBinding as TBinding; + return true; + } + + binding = default; + return false; + } + } +} diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/BindingExtensions_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/BindingExtensions_Should.cs new file mode 100644 index 00000000..3806fc03 --- /dev/null +++ b/test/LEGO.AsyncAPI.Tests/Bindings/BindingExtensions_Should.cs @@ -0,0 +1,89 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Tests.Bindings.WebSockets +{ + using System.Linq; + using FluentAssertions; + using LEGO.AsyncAPI.Bindings.MQTT; + using LEGO.AsyncAPI.Bindings.Pulsar; + using LEGO.AsyncAPI.Bindings.WebSockets; + using LEGO.AsyncAPI.Models; + using NUnit.Framework; + + public class BindingExtensions_Should + { + [Test] + public void TryGetValue_WithChannelBinding_ReturnsBinding() + { + var channel = new AsyncApiChannel(); + channel.Bindings.Add(new WebSocketsChannelBinding + { + Method = "POST", + Query = new AsyncApiSchema + { + Description = "this mah query", + }, + Headers = new AsyncApiSchema + { + Description = "this mah binding", + }, + }); + + var result = channel.Bindings.TryGetValue(out var channelBinding); + result.Should().BeTrue(); + channelBinding.Should().NotBeNull(); + channelBinding.Should().BeEquivalentTo(channel.Bindings.First().Value); + } + + [Test] + public void TryGetValue_WithServerBinding_ReturnsBinding() + { + var server = new AsyncApiServer(); + server.Bindings.Add(new PulsarServerBinding + { + Tenant = "test tenant", + }); + + var result = server.Bindings.TryGetValue(out var serverBinding); + result.Should().BeTrue(); + serverBinding.Should().NotBeNull(); + serverBinding.Should().BeEquivalentTo(server.Bindings.First().Value); + } + + [Test] + public void TryGetValue_WithOperationBinding_ReturnsBinding() + { + var operation = new AsyncApiOperation(); + operation.Bindings.Add(new MQTTOperationBinding + { + QoS = 23, + MessageExpiryInterval = 1, + Retain = true, + }); + + var result = operation.Bindings.TryGetValue(out var operationBinding); + result.Should().BeTrue(); + operationBinding.Should().NotBeNull(); + operationBinding.Should().BeEquivalentTo(operation.Bindings.First().Value); + } + + [Test] + public void TryGetValue_WithMessageBinding_ReturnsBinding() + { + var message = new AsyncApiMessage(); + message.Bindings.Add(new MQTTMessageBinding + { + PayloadFormatIndicator = 2, + CorrelationData = new AsyncApiSchema + { + Description = "Test", + }, + }); + + var result = message.Bindings.TryGetValue(out var messageBinding); + result.Should().BeTrue(); + messageBinding.Should().NotBeNull(); + messageBinding.Should().BeEquivalentTo(message.Bindings.First().Value); + } + } +} diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs index b8aef976..c37ada58 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/WebSockets/WebSocketBindings_Should.cs @@ -8,8 +8,8 @@ namespace LEGO.AsyncAPI.Tests.Bindings.WebSockets using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Readers; using NUnit.Framework; - - internal class WebSocketBindings_Should : TestBase + + public class WebSocketBindings_Should : TestBase { [Test] public void WebSocketChannelBinding_WithFilledObject_SerializesAndDeserializes() From e1830b99d41076b236759354b086d5872d5d70ce Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 09:50:34 +0000 Subject: [PATCH 69/84] chore: update CHANGELOG.md --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 505c9b09..c2b85fba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## [5.2.2](https://github.com/LEGO/AsyncAPI.NET/compare/v5.2.1...v5.2.2) (2024-07-29) + + +### Bug Fixes + +* correct typing of exclusive maximums and minimums for draft7 jso… ([#188](https://github.com/LEGO/AsyncAPI.NET/issues/188)) ([fb50d00](https://github.com/LEGO/AsyncAPI.NET/commit/fb50d00192896f9ac26e65df1e991854b33aa17c)) +* resolving wrong reference ([#180](https://github.com/LEGO/AsyncAPI.NET/issues/180)) ([47685cd](https://github.com/LEGO/AsyncAPI.NET/commit/47685cd19c7e58391625be043b1e5d82c49eedc8)) + ## [5.2.1](https://github.com/LEGO/AsyncAPI.NET/compare/v5.2.0...v5.2.1) (2024-06-12) From b8307c57a6f9bc7c546702c24dffdfb1833aa5d3 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Mon, 29 Jul 2024 13:58:02 +0200 Subject: [PATCH 70/84] fix: add missing walk and visit methods for bindings. (#191) --- .../Services/AsyncApiVisitorBase.cs | 32 +++++ src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs | 127 ++++++++++++++++-- src/LEGO.AsyncAPI/Services/CurrentKeys.cs | 8 +- .../Validation/AsyncApiValidator.cs | 8 ++ 4 files changed, 165 insertions(+), 10 deletions(-) diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs index 8e3dc241..899731d0 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiVisitorBase.cs @@ -246,6 +246,38 @@ public virtual void Visit(IDictionary channels) { } + public virtual void Visit(AsyncApiBindings bindings) + { + } + + public virtual void Visit(IServerBinding binding) + { + } + + public virtual void Visit(AsyncApiBindings bindings) + { + } + + public virtual void Visit(IChannelBinding binding) + { + } + + public virtual void Visit(AsyncApiBindings bindings) + { + } + + public virtual void Visit(IOperationBinding binding) + { + } + + public virtual void Visit(AsyncApiBindings bindings) + { + } + + public virtual void Visit(IMessageBinding binding) + { + } + public virtual void Visit(AsyncApiChannel channel) { } diff --git a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs index fc5c5186..844ab7e9 100644 --- a/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs +++ b/src/LEGO.AsyncAPI/Services/AsyncApiWalker.cs @@ -74,15 +74,48 @@ internal void Walk(AsyncApiComponents components) }); this.Walk(AsyncApiConstants.ServerBindings, () => - { - if (components.ServerBindings != null) - { - foreach (var item in components.ServerBindings) - { - this.Walk(item.Key, () => this.Walk(item.Value, isComponent: true)); - } - } - }); + { + if (components.ServerBindings != null) + { + foreach (var item in components.ServerBindings) + { + this.Walk(item.Key, () => this.Walk(item.Value, isComponent: true)); + } + } + }); + + this.Walk(AsyncApiConstants.ChannelBindings, () => + { + if (components.ChannelBindings != null) + { + foreach (var item in components.ChannelBindings) + { + this.Walk(item.Key, () => this.Walk(item.Value, isComponent: true)); + } + } + }); + + this.Walk(AsyncApiConstants.OperationBindings, () => + { + if (components.OperationBindings != null) + { + foreach (var item in components.OperationBindings) + { + this.Walk(item.Key, () => this.Walk(item.Value, isComponent: true)); + } + } + }); + + this.Walk(AsyncApiConstants.MessageBindings, () => + { + if (components.MessageBindings != null) + { + foreach (var item in components.MessageBindings) + { + this.Walk(item.Key, () => this.Walk(item.Value, isComponent: true)); + } + } + }); this.Walk(AsyncApiConstants.Parameters, () => { @@ -562,6 +595,25 @@ internal void Walk(AsyncApiBindings serverBindings, bool isCompo } this.visitor.Visit(serverBindings); + if (serverBindings != null) + { + foreach (var binding in serverBindings) + { + this.visitor.CurrentKeys.ServerBinding = binding.Key; + this.Walk(binding.Key, () => this.Walk(binding.Value)); + this.visitor.CurrentKeys.ServerBinding = null; + } + } + } + + internal void Walk(IServerBinding binding) + { + if (binding == null) + { + return; + } + + this.visitor.Visit(binding); } internal void Walk(AsyncApiBindings channelBindings, bool isComponent = false) @@ -572,6 +624,25 @@ internal void Walk(AsyncApiBindings channelBindings, bool isCom } this.visitor.Visit(channelBindings); + if (channelBindings != null) + { + foreach (var binding in channelBindings) + { + this.visitor.CurrentKeys.ChannelBinding = binding.Key; + this.Walk(binding.Key, () => this.Walk(binding.Value)); + this.visitor.CurrentKeys.ChannelBinding = null; + } + } + } + + internal void Walk(IChannelBinding binding) + { + if (binding == null) + { + return; + } + + this.visitor.Visit(binding); } internal void Walk(AsyncApiBindings operationBindings, bool isComponent = false) @@ -582,6 +653,25 @@ internal void Walk(AsyncApiBindings operationBindings, bool i } this.visitor.Visit(operationBindings); + if (operationBindings != null) + { + foreach (var binding in operationBindings) + { + this.visitor.CurrentKeys.OperationBinding = binding.Key; + this.Walk(binding.Key, () => this.Walk(binding.Value)); + this.visitor.CurrentKeys.OperationBinding = null; + } + } + } + + internal void Walk(IOperationBinding binding) + { + if (binding == null) + { + return; + } + + this.visitor.Visit(binding); } internal void Walk(AsyncApiBindings messageBindings, bool isComponent = false) @@ -592,6 +682,25 @@ internal void Walk(AsyncApiBindings messageBindings, bool isCom } this.visitor.Visit(messageBindings); + if (messageBindings != null) + { + foreach (var binding in messageBindings) + { + this.visitor.CurrentKeys.MessageBinding = binding.Key; + this.Walk(binding.Key, () => this.Walk(binding.Value)); + this.visitor.CurrentKeys.MessageBinding = null; + } + } + } + + internal void Walk(IMessageBinding binding) + { + if (binding == null) + { + return; + } + + this.visitor.Visit(binding); } internal void Walk(IList examples) diff --git a/src/LEGO.AsyncAPI/Services/CurrentKeys.cs b/src/LEGO.AsyncAPI/Services/CurrentKeys.cs index 3544eb21..f610aabb 100644 --- a/src/LEGO.AsyncAPI/Services/CurrentKeys.cs +++ b/src/LEGO.AsyncAPI/Services/CurrentKeys.cs @@ -4,7 +4,13 @@ namespace LEGO.AsyncAPI.Services { public class CurrentKeys { - public string ServerBindings { get; internal set; } + public string ServerBinding { get; internal set; } + + public string ChannelBinding { get; internal set; } + + public string OperationBinding { get; internal set; } + + public string MessageBinding { get; internal set; } public string Channel { get; internal set; } diff --git a/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs b/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs index 4ce96627..3f8bf392 100644 --- a/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs +++ b/src/LEGO.AsyncAPI/Validation/AsyncApiValidator.cs @@ -136,6 +136,14 @@ public void AddWarning(AsyncApiValidatorWarning warning) /// The object to be validated. public override void Visit(AsyncApiServer item) => this.Validate(item); + public override void Visit(IServerBinding item) => this.Validate(item); + + public override void Visit(IChannelBinding item) => this.Validate(item); + + public override void Visit(IOperationBinding item) => this.Validate(item); + + public override void Visit(IMessageBinding item) => this.Validate(item); + /// /// Execute validation rules against an . /// From ba16297b2e4ba96b4259a2e85033894360593405 Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:59:34 +0000 Subject: [PATCH 71/84] chore: update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2b85fba..e8d4454c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [5.2.3](https://github.com/LEGO/AsyncAPI.NET/compare/v5.2.2...v5.2.3) (2024-07-29) + + +### Bug Fixes + +* add missing walk and visit methods for bindings. ([#191](https://github.com/LEGO/AsyncAPI.NET/issues/191)) ([b8307c5](https://github.com/LEGO/AsyncAPI.NET/commit/b8307c57a6f9bc7c546702c24dffdfb1833aa5d3)) + ## [5.2.2](https://github.com/LEGO/AsyncAPI.NET/compare/v5.2.1...v5.2.2) (2024-07-29) From 52165f213502d9436e25a4e761804b5796b5de8c Mon Sep 17 00:00:00 2001 From: VisualBean Date: Mon, 29 Jul 2024 14:54:58 +0200 Subject: [PATCH 72/84] fix: remove persistence nullability --- .../Pulsar/PulsarChannelBinding.cs | 2 +- .../Bindings/Pulsar/PulsarBindings_Should.cs | 27 ------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs index c673a8e8..88435b74 100644 --- a/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Pulsar/PulsarChannelBinding.cs @@ -19,7 +19,7 @@ public class PulsarChannelBinding : ChannelBinding /// /// persistence of the topic in Pulsar persistent or non-persistent. /// - public Persistence? Persistence { get; set; } + public Persistence Persistence { get; set; } /// /// Topic compaction threshold given in bytes. diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs index e8a80ac1..7e69a0b7 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Pulsar/PulsarBindings_Should.cs @@ -88,33 +88,6 @@ public void PulsarChannelBindingNamespaceDefaultToNull() Assert.AreEqual(null, ((PulsarChannelBinding)binding.Bindings["pulsar"]).Namespace); } - [Test] - public void PulsarChannelBindingPropertiesExceptNamespaceDefaultToNull() - { - // Arrange - var actual = - """ - bindings: - pulsar: - namespace: staging - """; - - // Act - // Assert - var settings = new AsyncApiReaderSettings(); - settings.Bindings = BindingsCollection.Pulsar; - var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); - var pulsarBinding = ((PulsarChannelBinding)binding.Bindings["pulsar"]); - - Assert.AreEqual("staging", pulsarBinding.Namespace); - Assert.AreEqual(null, pulsarBinding.Persistence); - Assert.AreEqual(null, pulsarBinding.Compaction); - Assert.AreEqual(null, pulsarBinding.GeoReplication); - Assert.AreEqual(null, pulsarBinding.Retention); - Assert.AreEqual(null, pulsarBinding.TTL); - Assert.AreEqual(null, pulsarBinding.Deduplication); - } - [Test] public void PulsarServerBinding_WithFilledObject_SerializesAndDeserializes() { From c90f41ddee9cb3b358469c17abe932caa6cce0db Mon Sep 17 00:00:00 2001 From: "lego-10-01-06[bot]" <119427331+lego-10-01-06[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:02:21 +0000 Subject: [PATCH 73/84] chore: update CHANGELOG.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8d4454c..7dea98ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [5.2.4](https://github.com/LEGO/AsyncAPI.NET/compare/v5.2.3...v5.2.4) (2024-07-29) + + +### Bug Fixes + +* remove persistence nullability ([52165f2](https://github.com/LEGO/AsyncAPI.NET/commit/52165f213502d9436e25a4e761804b5796b5de8c)) + ## [5.2.3](https://github.com/LEGO/AsyncAPI.NET/compare/v5.2.2...v5.2.3) (2024-07-29) From 129622a33c0428e172bdeebce6819b0802d871f1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 11:09:32 +0200 Subject: [PATCH 74/84] chore(deps): bump System.Text.Json from 8.0.2 to 8.0.4 in /src/LEGO.AsyncAPI (#189) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Alex Wichmann --- src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj index 560c0f98..99e69016 100644 --- a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj +++ b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj @@ -19,7 +19,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + <_Parameter1>$(MSBuildProjectName).Tests From 9cc759fde50fcc02185ddd9200ce8ab0b3f80584 Mon Sep 17 00:00:00 2001 From: Adam Gloyne <44494964+Gadam8@users.noreply.github.com> Date: Thu, 15 Aug 2024 16:44:45 +0100 Subject: [PATCH 75/84] feat: extend AWS policy (#187) --- src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs | 63 ++++++++++++ .../Sns/PrincipalObject.cs | 27 ++++++ .../Sns/PrincipalStar.cs | 24 +++++ .../Sns/SnsChannelBinding.cs | 4 +- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 26 +++-- src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs | 65 +++++++++++++ .../Sqs/PrincipalObject.cs | 27 ++++++ .../Sqs/PrincipalStar.cs | 24 +++++ .../Sqs/SqsChannelBinding.cs | 4 +- .../Sqs/SqsOperationBinding.cs | 4 +- src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs | 25 ++++- .../StringOrStringList.cs | 4 +- .../Bindings/Sns/SnsBindings_Should.cs | 57 ++++++++--- .../Bindings/Sqs/SqsBindings_should.cs | 96 ++++++++++++++----- 14 files changed, 395 insertions(+), 55 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs new file mode 100644 index 00000000..a803f6c2 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs @@ -0,0 +1,63 @@ +namespace LEGO.AsyncAPI.Bindings.Sns; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers.ParseNodes; +using LEGO.AsyncAPI.Writers; + +public abstract class Principal : IAsyncApiElement +{ + public abstract void Serialize(IAsyncApiWriter writer); + + public static Principal Parse(ParseNode node) + { + switch (node) + { + case ValueNode: + var nodeValue = node.GetScalarValue(); + if (!IsStarString(nodeValue)) + { + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Principal value without a property name can only be a string value of '*'."); + } + + 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 principalValue = new KeyValuePair( + propertyNode.Name, + StringOrStringList.Parse(propertyNode.Value)); + + return new PrincipalObject(principalValue); + } + + default: + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Node should contain a string value of '*' or a valid AWS principal property."); + } + } + + private static bool IsStarString(JsonNode value) + { + var element = JsonDocument.Parse(value.ToJsonString()).RootElement; + + return element.ValueKind == JsonValueKind.String && element.ValueEquals("*"); + } + + private static bool IsValidPrincipalProperty(string property) + { + return new[] { "AWS", "Service" }.Contains(property); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs new file mode 100644 index 00000000..a25c198f --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs @@ -0,0 +1,27 @@ +namespace LEGO.AsyncAPI.Bindings.Sns; + +using System; +using System.Collections.Generic; +using LEGO.AsyncAPI.Writers; + +public class PrincipalObject : Principal +{ + private KeyValuePair PrincipalValue; + + public PrincipalObject(KeyValuePair principalValue) + { + this.PrincipalValue = principalValue; + } + + public override void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredObject(this.PrincipalValue.Key, this.PrincipalValue.Value, (w, t) => t.Value.Write(w)); + writer.WriteEndObject(); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs new file mode 100644 index 00000000..533e9fb7 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs @@ -0,0 +1,24 @@ +namespace LEGO.AsyncAPI.Bindings.Sns; + +using System; +using LEGO.AsyncAPI.Writers; + +public class PrincipalStar : Principal +{ + private string PrincipalValue; + + public PrincipalStar() + { + this.PrincipalValue = "*"; + } + + public override void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteValue(this.PrincipalValue); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 13676168..4d8668c9 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -57,8 +57,10 @@ public class SnsChannelBinding : ChannelBinding private static FixedFieldMap statementFixedFields = new() { { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } }, + { "principal", (a, n) => { a.Principal = Principal.Parse(n); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, + { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, + { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, }; /// diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 7f3771f0..170fe371 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -1,28 +1,40 @@ // Copyright (c) The LEGO Group. All rights reserved. - 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; public class Statement : IAsyncApiExtensible { + /// + /// Indicates whether the policy allows or denies access. + /// public Effect Effect { get; set; } /// - /// The AWS account or resource ARN that this statement applies to. + /// The AWS account(s) or resource ARN(s) that this statement applies to. /// - // public StringOrStringList Principal { get; set; } - public StringOrStringList Principal { get; set; } + public Principal Principal { get; set; } /// - /// The SNS permission being allowed or denied e.g. sns:Publish + /// The SNS permission being allowed or denied e.g. sns:Publish. /// public StringOrStringList Action { get; set; } + /// + /// The resource(s) that this policy applies to. + /// + public StringOrStringList? Resource { get; set; } + + /// + /// Specific circumstances under which the policy grants permission. + /// + public AsyncApiAny? Condition { get; set; } + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) @@ -34,8 +46,10 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); + writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); + writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs new file mode 100644 index 00000000..2821f952 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs @@ -0,0 +1,65 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Sqs; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Nodes; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers.ParseNodes; +using LEGO.AsyncAPI.Writers; + +public abstract class Principal : IAsyncApiElement +{ + public abstract void Serialize(IAsyncApiWriter writer); + + public static Principal Parse(ParseNode node) + { + switch (node) + { + case ValueNode: + var nodeValue = node.GetScalarValue(); + if (!IsStarString(nodeValue)) + { + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Principal value without a property name can only be a string value of '*'."); + } + + 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 principalValue = new KeyValuePair( + propertyNode.Name, + StringOrStringList.Parse(propertyNode.Value)); + + return new PrincipalObject(principalValue); + } + + default: + throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " + + $"Node should contain a string value of '*' or a valid AWS principal property."); + } + } + + private static bool IsStarString(JsonNode value) + { + var element = JsonDocument.Parse(value.ToJsonString()).RootElement; + + return element.ValueKind == JsonValueKind.String && element.ValueEquals("*"); + } + + private static bool IsValidPrincipalProperty(string property) + { + return new[] { "AWS", "Service" }.Contains(property); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs new file mode 100644 index 00000000..2652060d --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs @@ -0,0 +1,27 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs; + +using System; +using System.Collections.Generic; +using LEGO.AsyncAPI.Writers; + +public class PrincipalObject : Principal +{ + private KeyValuePair PrincipalValue; + + public PrincipalObject(KeyValuePair principalValue) + { + this.PrincipalValue = principalValue; + } + + public override void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + writer.WriteRequiredObject(this.PrincipalValue.Key, this.PrincipalValue.Value, (w, t) => t.Value.Write(w)); + writer.WriteEndObject(); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs new file mode 100644 index 00000000..9e54bc5a --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs @@ -0,0 +1,24 @@ +namespace LEGO.AsyncAPI.Bindings.Sqs; + +using System; +using LEGO.AsyncAPI.Writers; + +public class PrincipalStar : Principal +{ + private string PrincipalValue; + + public PrincipalStar() + { + this.PrincipalValue = "*"; + } + + public override void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteValue(this.PrincipalValue); + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs index 6f98da99..f0b24be7 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs @@ -64,8 +64,10 @@ public class SqsChannelBinding : ChannelBinding private static FixedFieldMap statementFixedFields = new() { { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } }, + { "principal", (a, n) => { a.Principal = Principal.Parse(n); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, + { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, + { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, }; public override void SerializeProperties(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs index d8eb43dd..ed278013 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs @@ -56,8 +56,10 @@ public class SqsOperationBinding : OperationBinding private static FixedFieldMap statementFixedFields = new() { { "effect", (a, n) => { a.Effect = n.GetScalarValue().GetEnumFromDisplayName(); } }, - { "principal", (a, n) => { a.Principal = StringOrStringList.Parse(n); } }, + { "principal", (a, n) => { a.Principal = Principal.Parse(n); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, + { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, + { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, }; public override void SerializeProperties(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs index 9518d2d4..4abc05a6 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs @@ -5,24 +5,37 @@ 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; public class Statement : IAsyncApiExtensible { + /// + /// Indicates whether the policy allows or denies access. + /// public Effect Effect { get; set; } /// - /// The AWS account or resource ARN that this statement applies to. + /// The AWS account(s) or resource ARN(s) that this statement applies to. /// - // public StringOrStringList Principal { get; set; } - public StringOrStringList Principal { get; set; } + public Principal Principal { get; set; } /// - /// The SNS permission being allowed or denied e.g. sns:Publish + /// The SNS permission being allowed or denied e.g. sns:Publish. /// public StringOrStringList Action { get; set; } + /// + /// The resource(s) that this policy applies to. + /// + public StringOrStringList? Resource { get; set; } + + /// + /// Specific circumstances under which the policy grants permission. + /// + public AsyncApiAny? Condition { get; set; } + public IDictionary Extensions { get; set; } = new Dictionary(); public void Serialize(IAsyncApiWriter writer) @@ -34,8 +47,10 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteStartObject(); writer.WriteRequiredProperty("effect", this.Effect.GetDisplayName()); - writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); + writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); + writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } diff --git a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs index 6be69094..b9946f08 100644 --- a/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs +++ b/src/LEGO.AsyncAPI.Bindings/StringOrStringList.cs @@ -30,10 +30,10 @@ public static StringOrStringList Parse(ParseNode node) { case ValueNode: return new StringOrStringList(new AsyncApiAny(node.GetScalarValue())); - case ListNode: + case ListNode listNode: { var jsonArray = new JsonArray(); - foreach (var item in node as ListNode) + foreach (var item in listNode) { jsonArray.Add(item.GetScalarValue()); } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index 6d4f5779..fbb3622e 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -31,15 +31,24 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: Deny - principal: arn:aws:iam::123456789012:user/alex.wichmann + principal: '*' action: - sns:Publish - sns:Delete + condition: + StringEquals: + aws:username: + - johndoe + - mrsmith - effect: Allow principal: - - arn:aws:iam::123456789012:user/alex.wichmann - - arn:aws:iam::123456789012:user/dec.kolakowski + AWS: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski action: sns:Create + condition: + NumericLessThanEquals: + aws:MultiFactorAuthAge: '3600' x-statementExtension: statementXPropertyName: statementXPropertyValue x-policyExtension: @@ -77,22 +86,38 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new PrincipalStar(), Action = new StringOrStringList(new AsyncApiAny(new List() { "sns:Publish", "sns:Delete", })), + Condition = new AsyncApiAny(new Dictionary() + { + { + "StringEquals", new Dictionary>() + { + { "aws:username", new List() { "johndoe", "mrsmith" } }, + } + }, + }), }, new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiAny(new List() - { - "arn:aws:iam::123456789012:user/alex.wichmann", - "arn:aws:iam::123456789012:user/dec.kolakowski", - })), + Principal = new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny(new List + { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sns:Create")), + Condition = new AsyncApiAny(new Dictionary() + { + { + "NumericLessThanEquals", new Dictionary() + { + { "aws:MultiFactorAuthAge", "3600" }, + } + }, + }), Extensions = new Dictionary() { { @@ -137,8 +162,11 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - var settings = new AsyncApiReaderSettings(); - settings.Bindings = BindingsCollection.Sns; + var settings = new AsyncApiReaderSettings + { + Bindings = BindingsCollection.Sns, + }; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert @@ -381,8 +409,11 @@ public void SnsOperationBinding_WithFilledObject_SerializesAndDeserializes() var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - var settings = new AsyncApiReaderSettings(); - settings.Bindings = BindingsCollection.Sns; + var settings = new AsyncApiReaderSettings + { + Bindings = BindingsCollection.Sns, + }; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); var binding2 = new AsyncApiStringReader(settings).ReadFragment(expected, AsyncApiVersion.AsyncApi2_0, out _); binding2.Bindings.First().Value.Extensions.TryGetValue("x-bindingExtension", out IAsyncApiExtension any); diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index c031621c..3a0337a3 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -3,6 +3,7 @@ namespace LEGO.AsyncAPI.Tests.Bindings.Sqs { using System.Collections.Generic; + using System.Linq; using FluentAssertions; using LEGO.AsyncAPI.Bindings; using LEGO.AsyncAPI.Bindings.Sqs; @@ -42,17 +43,27 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: deny - principal: arn:aws:iam::123456789012:user/alex.wichmann + principal: + AWS: arn:aws:iam::123456789012:user/alex.wichmann action: - sqs:SendMessage - sqs:ReceiveMessage + condition: + StringEquals: + aws:username: + - johndoe + - mrsmith x-statementExtension: statementXPropertyName: statementXPropertyValue - effect: allow principal: - - arn:aws:iam::123456789012:user/alex.wichmann - - arn:aws:iam::123456789012:user/dec.kolakowski + AWS: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski action: sqs:CreateQueue + condition: + NumericLessThanEquals: + aws:MultiFactorAuthAge: '3600' x-policyExtension: policyXPropertyName: policyXPropertyValue tags: @@ -69,7 +80,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: allow - principal: arn:aws:iam::123456789012:user/alex.wichmann + principal: + Service: s3.amazonaws.com action: - sqs:* x-internalObject: @@ -124,12 +136,22 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")))), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:SendMessage", "sqs:ReceiveMessage", })), + Condition = new AsyncApiAny(new Dictionary() + { + { + "StringEquals", new Dictionary>() + { + { "aws:username", new List() { "johndoe", "mrsmith" } }, + } + }, + }), Extensions = new Dictionary() { { @@ -144,12 +166,19 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiAny(new List - { - "arn:aws:iam::123456789012:user/alex.wichmann", - "arn:aws:iam::123456789012:user/dec.kolakowski", - })), + Principal = new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny(new List + { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), + Condition = new AsyncApiAny(new Dictionary() + { + { + "NumericLessThanEquals", new Dictionary() + { + { "aws:MultiFactorAuthAge", "3600" }, + } + }, + }), }, }, Extensions = new Dictionary() @@ -194,7 +223,8 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new PrincipalObject(new KeyValuePair( + "Service", new StringOrStringList(new AsyncApiAny("s3.amazonaws.com")))), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:*", @@ -218,8 +248,10 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() var actual = channel.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - var settings = new AsyncApiReaderSettings(); - settings.Bindings = BindingsCollection.Sqs; + var settings = new AsyncApiReaderSettings + { + Bindings = BindingsCollection.Sqs, + }; var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); @@ -227,6 +259,9 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() actual.Should() .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(channel); + + var expectedSqsBinding = (SqsChannelBinding)channel.Bindings.Values.First(); + expectedSqsBinding.Should().BeEquivalentTo((SqsChannelBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); } [Test] @@ -254,7 +289,8 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: deny - principal: arn:aws:iam::123456789012:user/alex.wichmann + principal: + AWS: arn:aws:iam::123456789012:user/alex.wichmann action: - sqs:SendMessage - sqs:ReceiveMessage @@ -262,8 +298,9 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() statementXPropertyName: statementXPropertyValue - effect: allow principal: - - arn:aws:iam::123456789012:user/alex.wichmann - - arn:aws:iam::123456789012:user/dec.kolakowski + AWS: + - arn:aws:iam::123456789012:user/alex.wichmann + - arn:aws:iam::123456789012:user/dec.kolakowski action: sqs:CreateQueue x-policyExtension: policyXPropertyName: policyXPropertyValue @@ -280,7 +317,8 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() policy: statements: - effect: allow - principal: arn:aws:iam::123456789012:user/alex.wichmann + principal: + AWS: arn:aws:iam::123456789012:user/alex.wichmann action: - sqs:* x-queueExtension: @@ -339,7 +377,8 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Deny, - Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")))), Action = new StringOrStringList(new AsyncApiAny(new List() { "sqs:SendMessage", @@ -359,11 +398,9 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiAny(new List - { - "arn:aws:iam::123456789012:user/alex.wichmann", - "arn:aws:iam::123456789012:user/dec.kolakowski", - })), + Principal = new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny(new List + { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), }, }, @@ -409,7 +446,8 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() new Statement() { Effect = Effect.Allow, - Principal = new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")), + Principal = new PrincipalObject(new KeyValuePair( + "AWS", new StringOrStringList(new AsyncApiAny("arn:aws:iam::123456789012:user/alex.wichmann")))), Action = new StringOrStringList(new AsyncApiAny(new List { "sqs:*", @@ -444,14 +482,20 @@ public void SqsOperationBinding_WithFilledObject_SerializesAndDeserializes() var actual = operation.SerializeAsYaml(AsyncApiVersion.AsyncApi2_0); // Assert - var settings = new AsyncApiReaderSettings(); - settings.Bindings = BindingsCollection.Sqs; + var settings = new AsyncApiReaderSettings + { + Bindings = BindingsCollection.Sqs, + }; + var binding = new AsyncApiStringReader(settings).ReadFragment(actual, AsyncApiVersion.AsyncApi2_0, out _); // Assert actual.Should() .BePlatformAgnosticEquivalentTo(expected); binding.Should().BeEquivalentTo(operation); + + var expectedSqsBinding = (SqsOperationBinding)operation.Bindings.Values.First(); + expectedSqsBinding.Should().BeEquivalentTo((SqsOperationBinding)binding.Bindings.Values.First(), options => options.IgnoringCyclicReferences()); } } } \ No newline at end of file From 38390d87aefbf1707ec17514e721a034bff2f561 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Fri, 16 Aug 2024 13:14:18 +0200 Subject: [PATCH 76/84] Add beta link to bindings. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 593dbd88..ce65fd33 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Install the NuGet packages: ### AsyncAPI.Bindings [![Nuget](https://img.shields.io/nuget/v/AsyncAPI.NET.Bindings?label=AsyncAPI.Bindings&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Bindings/) - +[![Nuget](https://img.shields.io/nuget/vpre/AsyncAPI.NET.Bindings?label=AsyncAPI.Bindings-Preview&style=for-the-badge)](https://www.nuget.org/packages/AsyncAPI.NET.Bindings/) ## Example Usage Main classes to know: From 57c0c3365f4d215860d8b599fd9d18e6e5185d54 Mon Sep 17 00:00:00 2001 From: Adam Gloyne <44494964+Gadam8@users.noreply.github.com> Date: Tue, 20 Aug 2024 13:01:50 +0100 Subject: [PATCH 77/84] chore: make principal values public (#192) Co-authored-by: adam.gloyne --- src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs | 8 ++++---- src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs | 6 +++--- src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs | 8 ++++---- src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs | 6 +++--- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs index a25c198f..209be8bf 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs @@ -6,11 +6,11 @@ namespace LEGO.AsyncAPI.Bindings.Sns; public class PrincipalObject : Principal { - private KeyValuePair PrincipalValue; + public KeyValuePair Value { get; private set; } - public PrincipalObject(KeyValuePair principalValue) + public PrincipalObject(KeyValuePair value) { - this.PrincipalValue = principalValue; + this.Value = value; } public override void Serialize(IAsyncApiWriter writer) @@ -21,7 +21,7 @@ public override void Serialize(IAsyncApiWriter writer) } writer.WriteStartObject(); - writer.WriteRequiredObject(this.PrincipalValue.Key, this.PrincipalValue.Value, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject(this.Value.Key, this.Value.Value, (w, t) => t.Value.Write(w)); writer.WriteEndObject(); } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs index 533e9fb7..c885d252 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs @@ -5,11 +5,11 @@ namespace LEGO.AsyncAPI.Bindings.Sns; public class PrincipalStar : Principal { - private string PrincipalValue; + public string Value { get; private set; } public PrincipalStar() { - this.PrincipalValue = "*"; + this.Value = "*"; } public override void Serialize(IAsyncApiWriter writer) @@ -19,6 +19,6 @@ public override void Serialize(IAsyncApiWriter writer) throw new ArgumentNullException(nameof(writer)); } - writer.WriteValue(this.PrincipalValue); + writer.WriteValue(this.Value); } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs index 2652060d..61e6e546 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs @@ -6,11 +6,11 @@ namespace LEGO.AsyncAPI.Bindings.Sqs; public class PrincipalObject : Principal { - private KeyValuePair PrincipalValue; + public KeyValuePair Value { get; private set; } - public PrincipalObject(KeyValuePair principalValue) + public PrincipalObject(KeyValuePair value) { - this.PrincipalValue = principalValue; + this.Value = value; } public override void Serialize(IAsyncApiWriter writer) @@ -21,7 +21,7 @@ public override void Serialize(IAsyncApiWriter writer) } writer.WriteStartObject(); - writer.WriteRequiredObject(this.PrincipalValue.Key, this.PrincipalValue.Value, (w, t) => t.Value.Write(w)); + writer.WriteRequiredObject(this.Value.Key, this.Value.Value, (w, t) => t.Value.Write(w)); writer.WriteEndObject(); } } \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs index 9e54bc5a..1705b966 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs @@ -5,11 +5,11 @@ namespace LEGO.AsyncAPI.Bindings.Sqs; public class PrincipalStar : Principal { - private string PrincipalValue; + public string Value { get; private set; } public PrincipalStar() { - this.PrincipalValue = "*"; + this.Value = "*"; } public override void Serialize(IAsyncApiWriter writer) @@ -19,6 +19,6 @@ public override void Serialize(IAsyncApiWriter writer) throw new ArgumentNullException(nameof(writer)); } - writer.WriteValue(this.PrincipalValue); + writer.WriteValue(this.Value); } } \ No newline at end of file From 030d603673bbde25a3e6cdd99c63f45159f4e9fd Mon Sep 17 00:00:00 2001 From: Adam Gloyne <44494964+Gadam8@users.noreply.github.com> Date: Wed, 4 Sep 2024 17:36:37 +0100 Subject: [PATCH 78/84] chore: make aws condition value explicit (#193) Co-authored-by: adam.gloyne --- src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs | 67 +++++++++++++++++++ .../Sns/SnsChannelBinding.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs | 4 +- src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs | 67 +++++++++++++++++++ .../Sqs/SqsChannelBinding.cs | 2 +- .../Sqs/SqsOperationBinding.cs | 2 +- src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs | 4 +- .../Bindings/Sns/SnsBindings_Should.cs | 16 +++-- .../Bindings/Sqs/SqsBindings_should.cs | 16 +++-- 9 files changed, 161 insertions(+), 19 deletions(-) create mode 100644 src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs create mode 100644 src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs new file mode 100644 index 00000000..38b21da9 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs @@ -0,0 +1,67 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Sns; + +using System; +using System.Collections.Generic; +using System.Linq; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers.ParseNodes; +using LEGO.AsyncAPI.Writers; + +public class Condition : IAsyncApiElement +{ + public Dictionary> Value { get; private set; } + + public Condition(Dictionary> value) + { + this.Value = value; + } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + foreach (var conditionValue in this.Value) + { + writer.WriteRequiredMap(conditionValue.Key, conditionValue.Value, (w, t) => t.Value.Write(w)); + } + + writer.WriteEndObject(); + } + + public static Condition Parse(ParseNode node) + { + switch (node) + { + case MapNode mapNode: + { + var conditionValues = new Dictionary>(); + foreach (var conditionNode in mapNode) + { + 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); + } + + default: + throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " + + $"Node should contain a collection of condition types."); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs index 4d8668c9..4394cdd1 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsChannelBinding.cs @@ -60,7 +60,7 @@ public class SnsChannelBinding : ChannelBinding { "principal", (a, n) => { a.Principal = Principal.Parse(n); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, - { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, + { "condition", (a, n) => { a.Condition = Condition.Parse(n); } }, }; /// diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs index 170fe371..da93cfbf 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs @@ -33,7 +33,7 @@ public class Statement : IAsyncApiExtensible /// /// Specific circumstances under which the policy grants permission. /// - public AsyncApiAny? Condition { get; set; } + public Condition Condition { get; set; } public IDictionary Extensions { get; set; } = new Dictionary(); @@ -49,7 +49,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); - writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); + writer.WriteOptionalObject("condition", this.Condition, (w, t) => t.Serialize(w)); writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs new file mode 100644 index 00000000..93bdf733 --- /dev/null +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs @@ -0,0 +1,67 @@ +// Copyright (c) The LEGO Group. All rights reserved. + +namespace LEGO.AsyncAPI.Bindings.Sqs; + +using System; +using System.Collections.Generic; +using System.Linq; +using LEGO.AsyncAPI.Models.Interfaces; +using LEGO.AsyncAPI.Readers.ParseNodes; +using LEGO.AsyncAPI.Writers; + +public class Condition : IAsyncApiElement +{ + public Dictionary> Value { get; private set; } + + public Condition(Dictionary> value) + { + this.Value = value; + } + + public void Serialize(IAsyncApiWriter writer) + { + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + writer.WriteStartObject(); + foreach (var conditionValue in this.Value) + { + writer.WriteRequiredMap(conditionValue.Key, conditionValue.Value, (w, t) => t.Value.Write(w)); + } + + writer.WriteEndObject(); + } + + public static Condition Parse(ParseNode node) + { + switch (node) + { + case MapNode mapNode: + { + var conditionValues = new Dictionary>(); + foreach (var conditionNode in mapNode) + { + 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); + } + + default: + throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " + + $"Node should contain a collection of AWS condition types."); + } + } +} \ No newline at end of file diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs index f0b24be7..bd806071 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsChannelBinding.cs @@ -67,7 +67,7 @@ public class SqsChannelBinding : ChannelBinding { "principal", (a, n) => { a.Principal = Principal.Parse(n); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, - { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, + { "condition", (a, n) => { a.Condition = Condition.Parse(n); } }, }; public override void SerializeProperties(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs index ed278013..0beb89b8 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs @@ -59,7 +59,7 @@ public class SqsOperationBinding : OperationBinding { "principal", (a, n) => { a.Principal = Principal.Parse(n); } }, { "action", (a, n) => { a.Action = StringOrStringList.Parse(n); } }, { "resource", (a, n) => { a.Resource = StringOrStringList.Parse(n); } }, - { "condition", (a, n) => { a.Condition = n.CreateAny(); } }, + { "condition", (a, n) => { a.Condition = Condition.Parse(n); } }, }; public override void SerializeProperties(IAsyncApiWriter writer) diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs index 4abc05a6..4a9c5303 100644 --- a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs +++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs @@ -34,7 +34,7 @@ public class Statement : IAsyncApiExtensible /// /// Specific circumstances under which the policy grants permission. /// - public AsyncApiAny? Condition { get; set; } + public Condition Condition { get; set; } public IDictionary Extensions { get; set; } = new Dictionary(); @@ -50,7 +50,7 @@ public void Serialize(IAsyncApiWriter writer) writer.WriteRequiredObject("principal", this.Principal, (w, t) => t.Serialize(w)); writer.WriteRequiredObject("action", this.Action, (w, t) => t.Value.Write(w)); writer.WriteOptionalObject("resource", this.Resource, (w, t) => t?.Value.Write(w)); - writer.WriteOptionalObject("condition", this.Condition, (w, t) => t?.Write(w)); + writer.WriteOptionalObject("condition", this.Condition, (w, t) => t.Serialize(w)); writer.WriteExtensions(this.Extensions); writer.WriteEndObject(); } diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs index fbb3622e..daed9e88 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sns/SnsBindings_Should.cs @@ -92,12 +92,14 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() "sns:Publish", "sns:Delete", })), - Condition = new AsyncApiAny(new Dictionary() + Condition = new Condition(new Dictionary> { { - "StringEquals", new Dictionary>() + "StringEquals", new Dictionary { - { "aws:username", new List() { "johndoe", "mrsmith" } }, + { + "aws:username", new StringOrStringList(new AsyncApiAny(new List() { "johndoe", "mrsmith" })) + }, } }, }), @@ -109,12 +111,14 @@ public void SnsChannelBinding_WithFilledObject_SerializesAndDeserializes() "AWS", new StringOrStringList(new AsyncApiAny(new List { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sns:Create")), - Condition = new AsyncApiAny(new Dictionary() + Condition = new Condition(new Dictionary> { { - "NumericLessThanEquals", new Dictionary() + "NumericLessThanEquals", new Dictionary { - { "aws:MultiFactorAuthAge", "3600" }, + { + "aws:MultiFactorAuthAge", new StringOrStringList(new AsyncApiAny("3600")) + }, } }, }), diff --git a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs index 3a0337a3..c3f7ff9d 100644 --- a/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs +++ b/test/LEGO.AsyncAPI.Tests/Bindings/Sqs/SqsBindings_should.cs @@ -143,12 +143,14 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() "sqs:SendMessage", "sqs:ReceiveMessage", })), - Condition = new AsyncApiAny(new Dictionary() + Condition = new Condition(new Dictionary> { { - "StringEquals", new Dictionary>() + "StringEquals", new Dictionary { - { "aws:username", new List() { "johndoe", "mrsmith" } }, + { + "aws:username", new StringOrStringList(new AsyncApiAny(new List { "johndoe", "mrsmith" })) + }, } }, }), @@ -170,12 +172,14 @@ public void SqsChannelBinding_WithFilledObject_SerializesAndDeserializes() "AWS", new StringOrStringList(new AsyncApiAny(new List { "arn:aws:iam::123456789012:user/alex.wichmann", "arn:aws:iam::123456789012:user/dec.kolakowski" })))), Action = new StringOrStringList(new AsyncApiAny("sqs:CreateQueue")), - Condition = new AsyncApiAny(new Dictionary() + Condition = new Condition(new Dictionary> { { - "NumericLessThanEquals", new Dictionary() + "NumericLessThanEquals", new Dictionary { - { "aws:MultiFactorAuthAge", "3600" }, + { + "aws:MultiFactorAuthAge", new StringOrStringList(new AsyncApiAny("3600")) + }, } }, }), From 68657dd2d035a1fd5468844c37c3ee85f019cfe6 Mon Sep 17 00:00:00 2001 From: Alex Wichmann Date: Wed, 8 Jan 2025 10:05:05 +0100 Subject: [PATCH 79/84] Update and rename release-internal.yml to release-beta.yml --- .github/workflows/{release-internal.yml => release-beta.yml} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename .github/workflows/{release-internal.yml => release-beta.yml} (97%) diff --git a/.github/workflows/release-internal.yml b/.github/workflows/release-beta.yml similarity index 97% rename from .github/workflows/release-internal.yml rename to .github/workflows/release-beta.yml index 1231b9e6..9cbebb05 100644 --- a/.github/workflows/release-internal.yml +++ b/.github/workflows/release-beta.yml @@ -1,7 +1,9 @@ name: Publish beta NuGet package on: workflow_dispatch: - + push: + branches: + - vnext jobs: check: runs-on: ubuntu-latest From 57c40958d8d57e7055d13282243dc2c04434011d Mon Sep 17 00:00:00 2001 From: VisualBean Date: Fri, 24 Jan 2025 12:47:38 +0100 Subject: [PATCH 80/84] chore: add Ulrik as codeowner chore: add Ulrik as codeowner --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index f1759cb1..2e87be63 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @VisualBean +* @VisualBean @UlrikSandberg From e6b9bf4b8f6631fa8862848cc9288dd93c8035e1 Mon Sep 17 00:00:00 2001 From: UlrikSandberg Date: Thu, 20 Feb 2025 10:19:02 +0100 Subject: [PATCH 81/84] fix: dont return early for required map (#206) --- .../Writers/AsyncApiWriterExtensions.cs | 5 +-- .../AsyncApiDocumentV2Tests.cs | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs index 67c4737a..25777d51 100644 --- a/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs +++ b/src/LEGO.AsyncAPI/Writers/AsyncApiWriterExtensions.cs @@ -285,10 +285,7 @@ public static void WriteRequiredMap( Action action) where T : IAsyncApiElement { - if (elements != null && elements.Any()) - { - writer.WriteMapInternal(name, elements, action); - } + writer.WriteMapInternal(name, elements, action); } /// diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 63e818b4..6e1a9c70 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -1325,5 +1325,36 @@ public void Serializev2_WithBindings_Serializes() Assert.AreEqual("this mah binding", httpBinding.Headers.Description); } + + + + [Test] + public void SerializeV2_EmptyChannelObject_DeserializeAndSerializePreserveChannelObject() + { + // Arrange + var spec = """ + asyncapi: 2.6.0 + info: + title: Spec with missing channel info + description: test description + servers: + production: + url: example.com + protocol: pulsar+ssl + description: test description + channels: { } + """; + + var settings = new AsyncApiReaderSettings(); + var reader = new AsyncApiStringReader(settings); + + // Act + var deserialized = reader.Read(spec, out var diagnostic); + var actual = deserialized.Serialize(AsyncApiVersion.AsyncApi2_0, AsyncApiFormat.Yaml); + + // Assert + actual.Should() + .BePlatformAgnosticEquivalentTo(spec); + } } } \ No newline at end of file From 24cf1a976986d095a20d0094130184dcdd542be8 Mon Sep 17 00:00:00 2001 From: DominikKaloc Date: Mon, 8 Sep 2025 09:41:41 +0200 Subject: [PATCH 82/84] fix: empty channels should be allowed (#208) --- src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs | 4 ++-- .../Validation/Rules/AsyncApiDocumentRules.cs | 2 +- test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs | 7 ++++++- test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs | 8 ++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs index 5be55202..b6488883 100644 --- a/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs +++ b/src/LEGO.AsyncAPI/Models/AsyncApiDocument.cs @@ -6,8 +6,8 @@ namespace LEGO.AsyncAPI.Models using System.Collections.Generic; using LEGO.AsyncAPI.Exceptions; using LEGO.AsyncAPI.Models.Interfaces; - using LEGO.AsyncAPI.Writers; using LEGO.AsyncAPI.Services; + using LEGO.AsyncAPI.Writers; /// /// This is the root document object for the API specification. It combines resource listing and API declaration together into one document. @@ -46,7 +46,7 @@ public class AsyncApiDocument : IAsyncApiExtensible, IAsyncApiSerializable /// /// REQUIRED. The available channels and messages for the API. /// - public IDictionary Channels { get; set; } = new Dictionary(); + public IDictionary Channels { get; set; } /// /// an element to hold various schemas for the specification. diff --git a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs index 27076369..264c1611 100644 --- a/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs +++ b/src/LEGO.AsyncAPI/Validation/Rules/AsyncApiDocumentRules.cs @@ -30,7 +30,7 @@ public static class AsyncApiDocumentRules context.Exit(); context.Enter("channels"); - if (document.Channels == null || !document.Channels.Keys.Any()) + if (document.Channels == null) { context.CreateError( nameof(DocumentRequiredFields), diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs index 3f2ee429..886fb7bf 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentBuilder.cs @@ -2,9 +2,10 @@ namespace LEGO.AsyncAPI.Tests { + using System; + using System.Collections.Generic; using LEGO.AsyncAPI.Models; using LEGO.AsyncAPI.Models.Interfaces; - using System; internal class AsyncApiDocumentBuilder { @@ -42,6 +43,10 @@ public AsyncApiDocumentBuilder WithDefaultContentType(string contentType = "appl public AsyncApiDocumentBuilder WithChannel(string key, AsyncApiChannel channel) { + if (this.document.Channels == null) + { + this.document.Channels = new Dictionary(); + } this.document.Channels.Add(key, channel); return this; } diff --git a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs index 6e1a9c70..13ae85e5 100644 --- a/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs +++ b/test/LEGO.AsyncAPI.Tests/AsyncApiDocumentV2Tests.cs @@ -1202,6 +1202,10 @@ public void Serialize_WithBindingReferences_SerializesDeserializes() }, }, }; + if (doc.Channels == null) + { + doc.Channels = new Dictionary(); + } doc.Channels.Add( "testChannel", new AsyncApiChannel @@ -1260,6 +1264,10 @@ public void Serializev2_WithBindings_Serializes() Protocol = "pulsar+ssl", Url = "example.com", }); + if (doc.Channels == null) + { + doc.Channels = new Dictionary(); + } doc.Channels.Add( "testChannel", new AsyncApiChannel From 47fee3ffbc9017abdf8304aa78f5cb452027f280 Mon Sep 17 00:00:00 2001 From: DominikKaloc Date: Thu, 25 Sep 2025 13:22:55 +0200 Subject: [PATCH 83/84] chore: manual release - custom version (#210) --- .github/workflows/release-manual.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/release-manual.yml diff --git a/.github/workflows/release-manual.yml b/.github/workflows/release-manual.yml new file mode 100644 index 00000000..0eeca64b --- /dev/null +++ b/.github/workflows/release-manual.yml @@ -0,0 +1,28 @@ +name: Publish custom NuGet package version +on: + workflow_dispatch: + inputs: + package_version: + description: 'NuGet package version (e.g. 6.0.0-beta.1041)' + required: true + +jobs: + pre-release: + runs-on: ubuntu-latest + name: Publish NuGet packages + environment: AsyncAPI + strategy: + matrix: + package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers", "LEGO.AsyncAPI.Bindings" ] + steps: + - name: Checkout repository + uses: actions/checkout@v1 + + - name: Setup .NET Core @ Latest + uses: actions/setup-dotnet@v1 + + - name: Build ${{ matrix.package-name }} project and pack NuGet package + run: dotnet pack src/${{ matrix.package-name }}/${{ matrix.package-name }}.csproj -c Release -o out-${{ matrix.package-name }} -p:PackageVersion=${{ github.event.inputs.package_version }} + + - name: Push generated package to NuGet + run: dotnet nuget push out-${{ matrix.package-name }}/*.nupkg -s https://api.nuget.org/v3/index.json --skip-duplicate -n --api-key ${{secrets.NUGET}} From f5becdf3d24a5138dcc54bfbe6a961b806f22c0f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 13:11:46 +0200 Subject: [PATCH 84/84] fix: bump System.Text.Json from 8.0.4 to 8.0.5 (#200) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj index 99e69016..a321a003 100644 --- a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj +++ b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj @@ -19,7 +19,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + <_Parameter1>$(MSBuildProjectName).Tests