diff --git a/.github/workflows/release-beta.yml b/.github/workflows/release-beta.yml
index 9cbebb05..7a5e69c6 100644
--- a/.github/workflows/release-beta.yml
+++ b/.github/workflows/release-beta.yml
@@ -38,9 +38,15 @@ jobs:
- name: Setup .NET Core @ Latest
uses: actions/setup-dotnet@v1
-
+
+ - name: run number with offset
+ env:
+ NUM: ${{ github.run_number }}
+ run: |
+ echo GITHUB_RUN_NUMBER_WITH_OFFSET=$(($NUM+100)) >> "$GITHUB_ENV"
+ - run: echo ${{env.GITHUB_RUN_NUMBER_WITH_OFFSET}}
- 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.${{github.run_number}}
+ 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.${{env.GITHUB_RUN_NUMBER_WITH_OFFSET}}
- 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}}
diff --git a/Common.Build.props b/Common.Build.props
index ba84a0f8..0f367727 100644
--- a/Common.Build.props
+++ b/Common.Build.props
@@ -2,7 +2,7 @@
10
- netstandard2.0;net6
+ netstandard2.0;net8
disable
The LEGO Group
https://github.com/LEGO/AsyncAPI.NET
diff --git a/README.md b/README.md
index ce65fd33..19f27895 100644
--- a/README.md
+++ b/README.md
@@ -35,39 +35,72 @@ Main classes to know:
### Writing
```csharp
-var myFirstAsyncApi = new AsyncApiDocument
-{
- Info = new AsyncApiInfo
- {
- Title = "my first asyncapi"
- },
- Channels = new Dictionary
- {
- {
- "users", new AsyncApiChannel
- {
- Subscribe = new AsyncApiOperation
- {
- OperationId = "users",
- Description = "my users channel"
- }
- }
- }
- }
-};
-var yaml = myFirstAsyncApi.SerializeAsYaml();
-//asyncapi: '2.5.0'
+ var myFirstAsyncApi = new AsyncApiDocument
+ {
+ Info = new AsyncApiInfo
+ {
+ Title = "my first asyncapi",
+ },
+ Channels = new Dictionary
+ {
+ {
+ "users", new AsyncApiChannel
+ {
+ Subscribe = new AsyncApiOperation
+ {
+ OperationId = "users",
+ Description = "my users channel",
+ Message = new List
+ {
+ new AsyncApiMessageReference("#/components/messages/MyMessage"),
+ },
+ },
+ }
+ },
+ },
+ Components = new AsyncApiComponents
+ {
+ Messages = new Dictionary
+ {
+ {
+ "MyMessage", new AsyncApiMessage
+ {
+ Name = "Hello!",
+ }
+ },
+ },
+ },
+ };
+
+var yaml = myFirstAsyncApi.SerializeAsYaml(AsyncApi);
+
+//asyncapi: 2.6.0
// info:
// title: my first asyncapi
-// channels:
-// users:
-// subscribe:
-// operationId: users
-// description: my users channel
+//channels:
+// users:
+// subscribe:
+// operationId: users
+// description: my users channel
+// message:
+// $ref: '#/components/messages/MyMessage'
+//components:
+// messages:
+// MyMessage:
+// name: Hello!
```
+
### Reading
+There are 3 reader types
+1. AsyncApiStringReader
+2. AsyncApiTextReader
+3. AsyncApiStreamReader
+
+All 3 supports both json and yaml.
+
+#### StreamReader
```csharp
var httpClient = new HttpClient
{
@@ -78,6 +111,46 @@ var stream = await httpClient.GetStreamAsync("master/examples/streetlights-kafka
var asyncApiDocument = new AsyncApiStreamReader().Read(stream, out var diagnostic);
```
+#### StringReader
+```csharp
+var yaml =
+ """
+ asyncapi: 2.6.0
+ info:
+ title: my first asyncapi
+ channels:
+ users:
+ subscribe:
+ operationId: users
+ description: my users channel
+ message:
+ $ref: '#/components/messages/MyMessage'
+ components:
+ messages:
+ MyMessage:
+ name: Hello!
+ """;
+
+var asyncApiDocument = new AsyncApiStringReader().Read(yaml, out var diagnostic);
+```
+All readers will write warnings and errors to the diagnostics.
+
+
+### Reference Resolution
+Internal references are resolved by default. This includes component and non-component references e.g `#/components/messages/MyMessage` and `#/servers/0`.
+External references can be resolved by setting `ReferenceResolution` to `ResolveAllReferences`.
+The default implementation will resolve anything prefixed with `file://`, `http://` & `https://`, however a custom implementation can be made, by inhereting from the `IStreamLoader` interface and setting the `ExternalReferenceLoader` in the `AsyncApiReaderSettings`.
+External references are always force converted to Json during resolution. This means that both yaml and json is supported - but not other serialization languages.
+
+```csharp
+var settings = new AsyncApiReaderSettings { ReferenceResolution = ReferenceResolution.ResolveAllReferences };
+var document = new AsyncApiStringReader(settings).Read(json, out var diagnostics);
+```
+
+
+
+Reference resolution can be disabled by setting `ReferenceResolution` to `DoNotResolveReferences`.
+
### 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.
@@ -85,7 +158,7 @@ There is a nifty helper to add different types of bindings, or like in the examp
```csharp
var settings = new AsyncApiReaderSettings();
settings.Bindings = BindingsCollection.All;
-var asyncApiDocument = new AsyncApiStringReader(settings).Read(stream, out var diagnostic);
+var asyncApiDocument = new AsyncApiStringReader(settings).Read(yaml, out var diagnostic);
```
## Attribution
@@ -99,4 +172,4 @@ This project welcomes contributions and suggestions.
Do you want to contribute to the project? Find out how [here](CONTRIBUTING.md).
## License
-[Modified Apache 2.0 (Section 6)](https://github.com/LEGO/AsyncAPI.NET/blob/main/LICENSE)
+[Modified Apache 2.0 (Section 6)](https://github.com/LEGO/AsyncAPI.NET/blob/main/LICENSE.txt)
diff --git a/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPOperationBinding.cs
index 86dc74ef..3a2e6a35 100644
--- a/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPOperationBinding.cs
+++ b/src/LEGO.AsyncAPI.Bindings/AMQP/AMQPOperationBinding.cs
@@ -5,7 +5,6 @@ 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;
diff --git a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs
index 4fd5560f..83faf695 100644
--- a/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs
+++ b/src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs
@@ -45,7 +45,7 @@ public static TCollection Add(
return destination;
}
- public static IEnumerable> All => new List>
+ public static ICollection> All => new List>
{
Pulsar,
Kafka,
@@ -57,18 +57,18 @@ public static TCollection Add(
MQTT,
};
- public static IEnumerable> Http => new List>
+ public static ICollection> Http => new List>
{
new HttpOperationBinding(),
new HttpMessageBinding(),
};
- public static IEnumerable> Websockets => new List>
+ public static ICollection> Websockets => new List>
{
new WebSocketsChannelBinding(),
};
- public static IEnumerable> Kafka => new List>
+ public static ICollection> Kafka => new List>
{
new KafkaServerBinding(),
new KafkaChannelBinding(),
@@ -76,32 +76,32 @@ public static TCollection Add(
new KafkaMessageBinding(),
};
- public static IEnumerable> Pulsar => new List>
+ public static ICollection> Pulsar => new List>
{
new PulsarServerBinding(),
new PulsarChannelBinding(),
};
- public static IEnumerable> Sqs => new List>
+ public static ICollection> Sqs => new List>
{
new SqsChannelBinding(),
new SqsOperationBinding(),
};
- public static IEnumerable> Sns => new List>
+ public static ICollection> Sns => new List>
{
new SnsChannelBinding(),
new SnsOperationBinding(),
};
- public static IEnumerable> AMQP => new List>
+ public static ICollection> AMQP => new List>
{
new AMQPChannelBinding(),
new AMQPOperationBinding(),
new AMQPMessageBinding(),
};
- public static IEnumerable> MQTT => new List>
+ public static ICollection> MQTT => new List>
{
new MQTTServerBinding(),
new MQTTOperationBinding(),
diff --git a/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs
index 7a5731ef..b6c7bf78 100644
--- a/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs
@@ -16,7 +16,7 @@ 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; }
+ public AsyncApiJsonSchema Headers { get; set; }
///
/// Serialize to AsyncAPI V2 document without using reference.
@@ -42,7 +42,7 @@ public override void SerializeProperties(IAsyncApiWriter writer)
protected override FixedFieldMap FixedFieldMap => new()
{
{ "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } },
- { "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } },
+ { "headers", (a, n) => { a.Headers = AsyncApiSchemaDeserializer.LoadSchema(n); } },
};
}
}
diff --git a/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs
index f70858c2..b7f1215e 100644
--- a/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Http/HttpOperationBinding.cs
@@ -36,7 +36,7 @@ public enum HttpOperationType
///
/// A Schema object containing the definitions for each query parameter. This schema MUST be of type object and have a properties key.
///
- public AsyncApiSchema Query { get; set; }
+ public AsyncApiJsonSchema Query { get; set; }
///
/// Serialize to AsyncAPI V2 document without using reference.
@@ -63,7 +63,7 @@ public override void SerializeProperties(IAsyncApiWriter writer)
{ "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); } },
+ { "query", (a, n) => { a.Query = AsyncApiSchemaDeserializer.LoadSchema(n); } },
};
public override string BindingKey => "http";
diff --git a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs
index 2f665560..e63c95e1 100644
--- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaMessageBinding.cs
@@ -16,7 +16,7 @@ public class KafkaMessageBinding : MessageBinding
///
/// The message key. NOTE: You can also use the reference object way.
///
- public AsyncApiSchema Key { get; set; }
+ public AsyncApiJsonSchema Key { get; set; }
///
/// If a Schema Registry is used when performing this operation, tells where the id of schema is stored (e.g. header or payload).
@@ -67,7 +67,7 @@ public override void SerializeProperties(IAsyncApiWriter writer)
protected override FixedFieldMap FixedFieldMap => new()
{
{ "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } },
- { "key", (a, n) => { a.Key = JsonSchemaDeserializer.LoadSchema(n); } },
+ { "key", (a, n) => { a.Key = AsyncApiSchemaDeserializer.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
index 53db7ae0..358a929b 100644
--- a/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Kafka/KafkaOperationBinding.cs
@@ -16,20 +16,20 @@ public class KafkaOperationBinding : OperationBinding
///
/// Id of the consumer group.
///
- public AsyncApiSchema GroupId { get; set; }
+ public AsyncApiJsonSchema GroupId { get; set; }
///
/// Id of the consumer inside a consumer group.
///
- public AsyncApiSchema ClientId { get; set; }
+ public AsyncApiJsonSchema 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); } },
+ { "groupId", (a, n) => { a.GroupId = AsyncApiSchemaDeserializer.LoadSchema(n); } },
+ { "clientId", (a, n) => { a.ClientId = AsyncApiSchemaDeserializer.LoadSchema(n); } },
};
///
diff --git a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj
index 9c0b025e..2c678678 100644
--- a/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj
+++ b/src/LEGO.AsyncAPI.Bindings/LEGO.AsyncAPI.Bindings.csproj
@@ -19,6 +19,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/src/LEGO.AsyncAPI.Bindings/MQTT/LastWill.cs b/src/LEGO.AsyncAPI.Bindings/MQTT/LastWill.cs
index 23118bfa..955ad90f 100644
--- a/src/LEGO.AsyncAPI.Bindings/MQTT/LastWill.cs
+++ b/src/LEGO.AsyncAPI.Bindings/MQTT/LastWill.cs
@@ -2,9 +2,9 @@
namespace LEGO.AsyncAPI.Bindings.MQTT
{
+ using System;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;
- using System;
public class LastWill : IAsyncApiElement
{
diff --git a/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs
index b48e5ae9..338230ac 100644
--- a/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs
+++ b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTMessageBinding.cs
@@ -22,7 +22,7 @@ public class MQTTMessageBinding : MessageBinding
///
/// Correlation Data is used to identify the request the response message is for.
///
- public AsyncApiSchema CorrelationData { get; set; }
+ public AsyncApiJsonSchema CorrelationData { get; set; }
///
/// String describing the content type of the message payload.
@@ -57,7 +57,7 @@ public override void SerializeProperties(IAsyncApiWriter writer)
protected override FixedFieldMap FixedFieldMap => new()
{
{ "payloadFormatIndicator", (a, n) => { a.PayloadFormatIndicator = n.GetIntegerValueOrDefault(); } },
- { "correlationData", (a, n) => { a.CorrelationData = JsonSchemaDeserializer.LoadSchema(n); } },
+ { "correlationData", (a, n) => { a.CorrelationData = AsyncApiSchemaDeserializer.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
index d3155ecd..f58eede9 100644
--- a/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTOperationBinding.cs
+++ b/src/LEGO.AsyncAPI.Bindings/MQTT/MQTTOperationBinding.cs
@@ -3,9 +3,6 @@
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;
diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs
index 38b21da9..fca3b30c 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sns/Condition.cs
@@ -39,25 +39,25 @@ public static Condition Parse(ParseNode node)
switch (node)
{
case MapNode mapNode:
- {
- var conditionValues = new Dictionary>();
- foreach (var conditionNode in mapNode)
{
- switch (conditionNode.Value)
+ var conditionValues = new Dictionary>();
+ foreach (var conditionNode in mapNode)
{
- case MapNode conditionValueNode:
- conditionValues.Add(conditionNode.Name, new Dictionary(conditionValueNode.Select(x =>
- new KeyValuePair(x.Name, StringOrStringList.Parse(x.Value)))
- .ToDictionary(x => x.Key, x => x.Value)));
- break;
- default:
- throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " +
- $"AWS condition values should be one or more key value pairs.");
+ switch (conditionNode.Value)
+ {
+ case MapNode conditionValueNode:
+ conditionValues.Add(conditionNode.Name, new Dictionary(conditionValueNode.Select(x =>
+ new KeyValuePair(x.Name, StringOrStringList.Parse(x.Value)))
+ .ToDictionary(x => x.Key, x => x.Value)));
+ break;
+ default:
+ throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " +
+ $"AWS condition values should be one or more key value pairs.");
+ }
}
- }
- return new Condition(conditionValues);
- }
+ return new Condition(conditionValues);
+ }
default:
throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " +
diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs
index 23f69f52..d44a0c9e 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sns/Ordering.cs
@@ -11,7 +11,7 @@ namespace LEGO.AsyncAPI.Bindings.Sns
public class Ordering : IAsyncApiExtensible
{
///
- /// What type of SNS Topic is this?
+ /// What type of SNS Topic is this?.
///
public OrderingType Type { get; set; }
diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs
index a803f6c2..2d630641 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sns/Principal.cs
@@ -1,3 +1,5 @@
+// Copyright (c) The LEGO Group. All rights reserved.
+
namespace LEGO.AsyncAPI.Bindings.Sns;
using System;
@@ -28,20 +30,20 @@ public static Principal Parse(ParseNode node)
return new PrincipalStar();
case MapNode mapNode:
- {
- var propertyNode = mapNode.First();
- if (!IsValidPrincipalProperty(propertyNode.Name))
{
- throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
- $"Node should contain a valid AWS principal property name.");
- }
+ var propertyNode = mapNode.First();
+ if (!IsValidPrincipalProperty(propertyNode.Name))
+ {
+ throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
+ $"Node should contain a valid AWS principal property name.");
+ }
- var principalValue = new KeyValuePair(
- propertyNode.Name,
- StringOrStringList.Parse(propertyNode.Value));
+ var principalValue = new KeyValuePair(
+ propertyNode.Name,
+ StringOrStringList.Parse(propertyNode.Value));
- return new PrincipalObject(principalValue);
- }
+ return new PrincipalObject(principalValue);
+ }
default:
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs
index 209be8bf..946fa4d6 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalObject.cs
@@ -1,3 +1,5 @@
+// Copyright (c) The LEGO Group. All rights reserved.
+
namespace LEGO.AsyncAPI.Bindings.Sns;
using System;
diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs
index c885d252..bd0e37bf 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sns/PrincipalStar.cs
@@ -1,3 +1,5 @@
+// Copyright (c) The LEGO Group. All rights reserved.
+
namespace LEGO.AsyncAPI.Bindings.Sns;
using System;
diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs
index 350b7a59..6f2dbba9 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sns/SnsOperationBinding.cs
@@ -59,7 +59,7 @@ public class SnsOperationBinding : OperationBinding
private FixedFieldMap redrivePolicyFixedFields => new()
{
- { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMapWithExtensions(identifierFixFields); } },
+ { "deadLetterQueue", (a, n) => { a.DeadLetterQueue = n.ParseMapWithExtensions(this.identifierFixFields); } },
{ "maxReceiveCount", (a, n) => { a.MaxReceiveCount = n.GetIntegerValue(); } },
};
diff --git a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs
index da93cfbf..5a7a248c 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sns/Statement.cs
@@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Bindings.Sns
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Attributes;
- using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;
diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs
index 93bdf733..9172f3e3 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Condition.cs
@@ -39,25 +39,25 @@ public static Condition Parse(ParseNode node)
switch (node)
{
case MapNode mapNode:
- {
- var conditionValues = new Dictionary>();
- foreach (var conditionNode in mapNode)
{
- switch (conditionNode.Value)
+ var conditionValues = new Dictionary>();
+ foreach (var conditionNode in mapNode)
{
- case MapNode conditionValueNode:
- conditionValues.Add(conditionNode.Name, new Dictionary(conditionValueNode.Select(x =>
- new KeyValuePair(x.Name, StringOrStringList.Parse(x.Value)))
- .ToDictionary(x => x.Key, x => x.Value)));
- break;
- default:
- throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " +
- $"AWS condition values should be one or more key value pairs.");
+ switch (conditionNode.Value)
+ {
+ case MapNode conditionValueNode:
+ conditionValues.Add(conditionNode.Name, new Dictionary(conditionValueNode.Select(x =>
+ new KeyValuePair(x.Name, StringOrStringList.Parse(x.Value)))
+ .ToDictionary(x => x.Key, x => x.Value)));
+ break;
+ default:
+ throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " +
+ $"AWS condition values should be one or more key value pairs.");
+ }
}
- }
- return new Condition(conditionValues);
- }
+ return new Condition(conditionValues);
+ }
default:
throw new ArgumentException($"An error occured while parsing a {nameof(Condition)} node. " +
diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs
index 2821f952..eee2b4fe 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Principal.cs
@@ -30,20 +30,20 @@ public static Principal Parse(ParseNode node)
return new PrincipalStar();
case MapNode mapNode:
- {
- var propertyNode = mapNode.First();
- if (!IsValidPrincipalProperty(propertyNode.Name))
{
- throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
- $"Node should contain a valid AWS principal property name.");
- }
+ var propertyNode = mapNode.First();
+ if (!IsValidPrincipalProperty(propertyNode.Name))
+ {
+ throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
+ $"Node should contain a valid AWS principal property name.");
+ }
- var principalValue = new KeyValuePair(
- propertyNode.Name,
- StringOrStringList.Parse(propertyNode.Value));
+ var principalValue = new KeyValuePair(
+ propertyNode.Name,
+ StringOrStringList.Parse(propertyNode.Value));
- return new PrincipalObject(principalValue);
- }
+ return new PrincipalObject(principalValue);
+ }
default:
throw new ArgumentException($"An error occured while parsing a {nameof(Principal)} node. " +
diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs
index 61e6e546..a1613247 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalObject.cs
@@ -1,3 +1,5 @@
+// Copyright (c) The LEGO Group. All rights reserved.
+
namespace LEGO.AsyncAPI.Bindings.Sqs;
using System;
diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs
index 1705b966..a49b02c6 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sqs/PrincipalStar.cs
@@ -1,3 +1,5 @@
+// Copyright (c) The LEGO Group. All rights reserved.
+
namespace LEGO.AsyncAPI.Bindings.Sqs;
using System;
diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs
index 33166af5..eb3d256a 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Queue.cs
@@ -16,7 +16,7 @@ public class Queue : IAsyncApiExtensible
public string Name { get; set; }
///
- /// Is this a FIFO queue?
+ /// Is this a FIFO queue?.
///
public bool FifoQueue { get; set; }
@@ -56,7 +56,7 @@ public class Queue : IAsyncApiExtensible
public RedrivePolicy RedrivePolicy { get; set; }
///
- /// The security policy for the SQS Queue
+ /// The security policy for the SQS Queue.
///
public Policy Policy { get; set; }
diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs
index 923d668c..538b1edf 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sqs/RedrivePolicy.cs
@@ -3,9 +3,9 @@
namespace LEGO.AsyncAPI.Bindings.Sqs
{
using System;
+ using System.Collections.Generic;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;
- using System.Collections.Generic;
public class RedrivePolicy : IAsyncApiExtensible
{
diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs
index 0beb89b8..66f15314 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sqs/SqsOperationBinding.cs
@@ -10,7 +10,7 @@ namespace LEGO.AsyncAPI.Bindings.Sqs
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
+ /// 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; }
diff --git a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs
index 4a9c5303..0c2873d2 100644
--- a/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs
+++ b/src/LEGO.AsyncAPI.Bindings/Sqs/Statement.cs
@@ -5,7 +5,6 @@ namespace LEGO.AsyncAPI.Bindings.Sqs
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Attributes;
- using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;
diff --git a/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs
index c87393bb..4321c879 100644
--- a/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs
+++ b/src/LEGO.AsyncAPI.Bindings/WebSockets/WebSocketsChannelBinding.cs
@@ -18,12 +18,12 @@ public class WebSocketsChannelBinding : ChannelBinding
///
/// A Schema object containing the definitions for each query parameter. This schema MUST be of type 'object' and have a 'properties' key.
///
- public AsyncApiSchema Query { get; set; }
+ public AsyncApiJsonSchema Query { get; set; }
///
/// A Schema object containing the definitions of the HTTP headers to use when establishing the connection. This schma MUST be of type 'object' and have a 'properties' key.
///
- public AsyncApiSchema Headers { get; set; }
+ public AsyncApiJsonSchema Headers { get; set; }
public override string BindingKey => "websockets";
@@ -31,8 +31,8 @@ public class WebSocketsChannelBinding : ChannelBinding
{
{ "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); } },
+ { "query", (a, n) => { a.Query = AsyncApiSchemaDeserializer.LoadSchema(n); } },
+ { "headers", (a, n) => { a.Headers = AsyncApiSchemaDeserializer.LoadSchema(n); } },
};
public override void SerializeProperties(IAsyncApiWriter writer)
diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiDiagnostics.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiDiagnostics.cs
index faceb39e..f1412e25 100644
--- a/src/LEGO.AsyncAPI.Readers/AsyncApiDiagnostics.cs
+++ b/src/LEGO.AsyncAPI.Readers/AsyncApiDiagnostics.cs
@@ -13,5 +13,18 @@ public class AsyncApiDiagnostic : IDiagnostic
public IList Warnings { get; set; } = new List();
public AsyncApiVersion SpecificationVersion { get; set; }
+
+ public void Append(AsyncApiDiagnostic diagnosticToAdd)
+ {
+ foreach (var error in diagnosticToAdd.Errors)
+ {
+ this.Errors.Add(new(error.Pointer, error.Message));
+ }
+
+ foreach (var warning in diagnosticToAdd.Warnings)
+ {
+ this.Warnings.Add(new(warning.Pointer, warning.Message));
+ }
+ }
}
}
diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs
index 3a973654..4512fb44 100644
--- a/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs
+++ b/src/LEGO.AsyncAPI.Readers/AsyncApiJsonDocumentReader.cs
@@ -2,17 +2,22 @@
namespace LEGO.AsyncAPI.Readers
{
- using System.Collections.Generic;
+ using System;
+ using System.IO;
using System.Linq;
using System.Text.Json.Nodes;
using System.Threading;
using System.Threading.Tasks;
+ using Json.Pointer;
using LEGO.AsyncAPI.Exceptions;
using LEGO.AsyncAPI.Extensions;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.Interface;
+ using LEGO.AsyncAPI.Readers.Services;
+ using LEGO.AsyncAPI.Services;
using LEGO.AsyncAPI.Validations;
+ using YamlDotNet.RepresentationModel;
///
/// Service class for converting contents of TextReader into AsyncApiDocument instances.
@@ -20,6 +25,7 @@ namespace LEGO.AsyncAPI.Readers
internal class AsyncApiJsonDocumentReader : IAsyncApiReader
{
private readonly AsyncApiReaderSettings settings;
+ private ParsingContext context;
///
/// Initializes a new instance of the class.
@@ -39,7 +45,7 @@ public AsyncApiJsonDocumentReader(AsyncApiReaderSettings settings = null)
public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic)
{
diagnostic = new AsyncApiDiagnostic();
- var context = new ParsingContext(diagnostic, this.settings)
+ this.context ??= new ParsingContext(diagnostic, this.settings)
{
ExtensionParsers = this.settings.ExtensionParsers,
ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b),
@@ -51,8 +57,7 @@ public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic)
AsyncApiDocument document = null;
try
{
- document = context.Parse(input);
-
+ document = this.context.Parse(input);
this.ResolveReferences(diagnostic, document);
}
catch (AsyncApiException ex)
@@ -80,17 +85,21 @@ public AsyncApiDocument Read(JsonNode input, out AsyncApiDiagnostic diagnostic)
public async Task ReadAsync(JsonNode input, CancellationToken cancellationToken = default)
{
var diagnostic = new AsyncApiDiagnostic();
- var context = new ParsingContext(diagnostic, this.settings)
+ this.context ??= new ParsingContext(diagnostic, this.settings)
{
ExtensionParsers = this.settings.ExtensionParsers,
+ ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b),
+ ChannelBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b),
+ OperationBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b),
+ MessageBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b),
};
AsyncApiDocument document = null;
try
{
// Parse the AsyncApi Document
- document = context.Parse(input);
- this.ResolveReferences(diagnostic, document);
+ document = this.context.Parse(input);
+ await this.ResolveReferencesAsync(diagnostic, document);
}
catch (AsyncApiException ex)
{
@@ -130,7 +139,7 @@ public T ReadFragment(JsonNode input, AsyncApiVersion version, out AsyncApiDi
where T : IAsyncApiElement
{
diagnostic = new AsyncApiDiagnostic();
- var context = new ParsingContext(diagnostic, this.settings)
+ this.context ??= new ParsingContext(diagnostic, this.settings)
{
ExtensionParsers = this.settings.ExtensionParsers,
ServerBindingParsers = this.settings.Bindings.OfType>().ToDictionary(b => b.BindingKey, b => b),
@@ -143,7 +152,7 @@ public T ReadFragment(JsonNode input, AsyncApiVersion version, out AsyncApiDi
try
{
// Parse the AsyncApi element
- element = context.ParseFragment(input, version);
+ element = this.context.ParseFragment(input, version);
}
catch (AsyncApiException ex)
{
@@ -163,25 +172,256 @@ public T ReadFragment(JsonNode input, AsyncApiVersion version, out AsyncApiDi
return (T)element;
}
- private void ResolveReferences(AsyncApiDiagnostic diagnostic, AsyncApiDocument document)
+ private async Task ResolveReferencesAsync(AsyncApiDiagnostic diagnostic, IAsyncApiSerializable serializable)
{
- var errors = new List();
+ if (this.settings.ReferenceResolution == ReferenceResolutionSetting.DoNotResolveReferences)
+ {
+ return;
+ }
- // Resolve References if requested
- switch (this.settings.ReferenceResolution)
+ var collector = new AsyncApiReferenceCollector(this.context.Workspace);
+ var walker = new AsyncApiWalker(collector);
+ walker.Walk(serializable);
+
+ foreach (var reference in collector.References)
{
- case ReferenceResolutionSetting.ResolveReferences:
- errors.AddRange(document.ResolveReferences());
- break;
+ if (this.context.Workspace.Contains(reference.Reference.Reference))
+ {
+ continue;
+ }
- case ReferenceResolutionSetting.DoNotResolveReferences:
- break;
+ IAsyncApiSerializable component = null;
+ if (reference.Reference.IsExternal)
+ {
+ if (this.settings.ReferenceResolution != ReferenceResolutionSetting.ResolveAllReferences)
+ {
+ continue;
+ }
+
+ component = await this.ResolveExternalReferenceAsync(diagnostic, reference);
+ }
+ else
+ {
+ var stream = this.context.Workspace.ResolveReference(string.Empty); // get whole document.
+ component = this.ResolveStreamReference(stream, reference, diagnostic);
+ }
+
+ if (component == null)
+ {
+ diagnostic.Errors.Add(new AsyncApiError(string.Empty, $"Unable to deserialize reference '{reference.Reference.Reference}'"));
+ continue;
+ }
+
+ this.context.Workspace.RegisterComponent(reference.Reference.Reference, component);
+ await this.ResolveReferencesAsync(diagnostic, component);
+ }
+ }
+
+ private void ResolveReferences(AsyncApiDiagnostic diagnostic, IAsyncApiSerializable serializable)
+ {
+ if (this.settings.ReferenceResolution == ReferenceResolutionSetting.DoNotResolveReferences)
+ {
+ return;
+ }
+
+ var collector = new AsyncApiReferenceCollector(this.context.Workspace);
+ var walker = new AsyncApiWalker(collector);
+ walker.Walk(serializable);
+
+ foreach (var reference in collector.References)
+ {
+ if (this.context.Workspace.Contains(reference.Reference.Reference))
+ {
+ continue;
+ }
+
+ IAsyncApiSerializable component = null;
+ if (reference.Reference.IsExternal)
+ {
+ if (this.settings.ReferenceResolution != ReferenceResolutionSetting.ResolveAllReferences)
+ {
+ continue;
+ }
+
+ component = this.ResolveExternalReference(diagnostic, reference);
+ }
+ else
+ {
+ var stream = this.context.Workspace.ResolveReference(string.Empty); // get whole document.
+ component = this.ResolveStreamReference(stream, reference, diagnostic);
+ }
+
+ if (component == null)
+ {
+ diagnostic.Errors.Add(new AsyncApiError(string.Empty, $"Unable to deserialize reference '{reference.Reference.Reference}'"));
+ continue;
+ }
+
+ this.context.Workspace.RegisterComponent(reference.Reference.Reference, component);
+ this.ResolveReferences(diagnostic, component);
+ }
+ }
+
+ private IAsyncApiSerializable ResolveExternalReference(AsyncApiDiagnostic diagnostic, IAsyncApiReferenceable reference)
+ {
+ if (reference is null)
+ {
+ throw new ArgumentNullException(nameof(reference));
+ }
+
+ var loader = this.settings.ExternalReferenceLoader ??= new DefaultStreamLoader();
+ try
+ {
+ Stream stream;
+ if (this.context.Workspace.Contains(reference.Reference.ExternalResource))
+ {
+ stream = this.context.Workspace.ResolveReference(reference.Reference.ExternalResource);
+ }
+ else
+ {
+ stream = loader.Load(new Uri(reference.Reference.ExternalResource, UriKind.RelativeOrAbsolute));
+ this.context.Workspace.RegisterComponent(reference.Reference.ExternalResource, stream);
+ }
+
+ return this.ResolveStreamReference(stream, reference, diagnostic);
+ }
+ catch (AsyncApiException ex)
+ {
+ diagnostic.Errors.Add(new AsyncApiError(ex));
+ return null;
+ }
+ }
+
+ private async Task ResolveExternalReferenceAsync(AsyncApiDiagnostic diagnostic, IAsyncApiReferenceable reference)
+ {
+ if (reference is null)
+ {
+ throw new ArgumentNullException(nameof(reference));
+ }
+
+ var loader = this.settings.ExternalReferenceLoader ??= new DefaultStreamLoader();
+ try
+ {
+ Stream stream;
+ if (this.context.Workspace.Contains(reference.Reference.ExternalResource))
+ {
+ stream = this.context.Workspace.ResolveReference(reference.Reference.ExternalResource);
+ }
+ else
+ {
+ stream = await loader.LoadAsync(new Uri(reference.Reference.ExternalResource, UriKind.RelativeOrAbsolute));
+ this.context.Workspace.RegisterComponent(reference.Reference.ExternalResource, stream);
+ }
+
+ return this.ResolveStreamReference(stream, reference, diagnostic);
+ }
+ catch (AsyncApiException ex)
+ {
+ diagnostic.Errors.Add(new AsyncApiError(ex));
+ return null;
}
+ }
- foreach (var item in errors)
+ private JsonNode ReadToJson(Stream stream)
+ {
+ if (stream != null)
{
- diagnostic.Errors.Add(item);
+ var reader = new StreamReader(stream);
+ var yamlStream = new YamlStream();
+ yamlStream.Load(reader);
+ return yamlStream.Documents.First().ToJsonNode(this.settings.CultureInfo);
}
+
+ return default;
+ }
+
+ private IAsyncApiSerializable ResolveStreamReference(Stream stream, IAsyncApiReferenceable reference, AsyncApiDiagnostic diagnostic)
+ {
+ JsonNode json = null;
+ try
+ {
+ json = this.ReadToJson(stream);
+ }
+ catch
+ {
+ diagnostic.Errors.Add(new AsyncApiError(string.Empty, $"Unable to deserialize reference: '{reference.Reference.Reference}'"));
+ return null;
+ }
+
+ if (reference.Reference.IsFragment)
+ {
+ var pointer = JsonPointer.Parse(reference.Reference.FragmentId);
+ if (pointer.TryEvaluate(json, out var pointerResult))
+ {
+ json = pointerResult;
+ }
+ else
+ {
+ diagnostic.Errors.Add(new AsyncApiError(reference.Reference.Reference, "Could not resolve reference fragment."));
+ return null;
+ }
+ }
+
+ AsyncApiDiagnostic fragmentDiagnostic = new AsyncApiDiagnostic();
+ IAsyncApiSerializable result = null;
+ switch (reference.Reference.Type)
+ {
+ case ReferenceType.Schema:
+ if (reference is AsyncApiJsonSchemaReference)
+ {
+ result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ }
+
+ if (reference is AsyncApiAvroSchemaReference)
+ {
+ result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ }
+
+ break;
+
+ case ReferenceType.Server:
+ result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.Channel:
+ result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.Message:
+ result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.SecurityScheme:
+ result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.Parameter:
+ result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.CorrelationId:
+ result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.OperationTrait:
+ result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.MessageTrait:
+ result = this.ReadFragment(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.ServerBindings:
+ result = this.ReadFragment>(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.ChannelBindings:
+ result = this.ReadFragment>(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.OperationBindings:
+ result = this.ReadFragment>(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ case ReferenceType.MessageBindings:
+ result = this.ReadFragment>(json, AsyncApiVersion.AsyncApi2_0, out fragmentDiagnostic);
+ break;
+ default:
+ diagnostic.Errors.Add(new AsyncApiError(reference.Reference.Reference, "Could not resolve reference."));
+ break;
+ }
+
+ diagnostic.Append(fragmentDiagnostic);
+ return result;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs
index 6ca2e129..890b83e6 100644
--- a/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs
+++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReaderSettings.cs
@@ -18,9 +18,14 @@ public enum ReferenceResolutionSetting
DoNotResolveReferences,
///
- /// Resolve internal component references and inline them.
+ /// Resolve all references and inline them.
///
- ResolveReferences,
+ ResolveInternalReferences,
+
+ ///
+ /// Resolve internal component references and inline them while leaving external references as placeholder objects with an AsyncApiReference instance and UnresolvedReference set to true.
+ ///
+ ResolveAllReferences,
}
///
@@ -32,7 +37,13 @@ public class AsyncApiReaderSettings : AsyncApiSettings
/// Indicates how references in the source document should be handled.
///
public ReferenceResolutionSetting ReferenceResolution { get; set; } =
- ReferenceResolutionSetting.ResolveReferences;
+ ReferenceResolutionSetting.ResolveInternalReferences;
+
+ ///
+ /// Indicates what should happen when unmapped members are encountered during deserialization.
+ /// Error and Warning will add an error or warning to the diagnostics object.
+ ///
+ public UnmappedMemberHandling UnmappedMemberHandling { get; set; } = UnmappedMemberHandling.Error;
///
/// Dictionary of parsers for converting extensions into strongly typed classes.
@@ -42,7 +53,7 @@ public Dictionary>
{ get; set; } =
new Dictionary>();
- public IEnumerable>
+ public ICollection>
Bindings
{ get; set; } =
new List>();
@@ -57,5 +68,15 @@ public IEnumerable>
/// from an object.
///
public bool LeaveStreamOpen { get; set; }
+
+ ///
+ /// External reference reader implementation provided by users for reading external resources.
+ ///
+ public IStreamLoader ExternalReferenceLoader { get; set; } = null;
+
+ ///
+ /// URL where relative references should be resolved from if.
+ ///
+ public Uri BaseUrl { get; set; }
}
}
\ No newline at end of file
diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiReferenceHostDocumentResolver.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiReferenceHostDocumentResolver.cs
new file mode 100644
index 00000000..362b7011
--- /dev/null
+++ b/src/LEGO.AsyncAPI.Readers/AsyncApiReferenceHostDocumentResolver.cs
@@ -0,0 +1,28 @@
+// Copyright (c) The LEGO Group. All rights reserved.
+
+namespace LEGO.AsyncAPI.Readers
+{
+ using System.Collections.Generic;
+ using LEGO.AsyncAPI.Models;
+ using LEGO.AsyncAPI.Models.Interfaces;
+ using LEGO.AsyncAPI.Services;
+
+ internal class AsyncApiReferenceWorkspaceResolver : AsyncApiVisitorBase
+ {
+ private AsyncApiWorkspace workspace;
+
+ public AsyncApiReferenceWorkspaceResolver(
+ AsyncApiWorkspace workspace)
+ {
+ this.workspace = workspace;
+ }
+
+ public override void Visit(IAsyncApiReferenceable referenceable)
+ {
+ if (referenceable.Reference != null)
+ {
+ referenceable.Reference.Workspace = this.workspace;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs
index eeece6a7..78b6973a 100644
--- a/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs
+++ b/src/LEGO.AsyncAPI.Readers/AsyncApiTextReader.cs
@@ -4,7 +4,6 @@ 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;
@@ -123,7 +122,7 @@ static JsonNode LoadYamlDocument(TextReader input, AsyncApiReaderSettings settin
{
var yamlStream = new YamlStream();
yamlStream.Load(input);
- return yamlStream.Documents.First().ToJsonNode(settings);
+ return yamlStream.Documents.First().ToJsonNode(settings.CultureInfo);
}
}
}
diff --git a/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiUnsupportedSpecVersionException.cs b/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiUnsupportedSpecVersionException.cs
index d68da4bf..590b3d73 100644
--- a/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiUnsupportedSpecVersionException.cs
+++ b/src/LEGO.AsyncAPI.Readers/Exceptions/AsyncApiUnsupportedSpecVersionException.cs
@@ -29,7 +29,7 @@ public AsyncApiUnsupportedSpecVersionException(string specificationVersion)
/// inner exception.
///
/// Version that caused this exception to be thrown.
- /// The setting used for reading and writing
+ /// 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/LEGO.AsyncAPI.Readers.csproj b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj
index 53071ca0..90a0d82f 100644
--- a/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj
+++ b/src/LEGO.AsyncAPI.Readers/LEGO.AsyncAPI.Readers.csproj
@@ -6,6 +6,7 @@
AsyncAPI.NET.Readers
LEGO.AsyncAPI.Readers
LEGO.AsyncAPI.Readers
+ netstandard2.0;net8
@@ -17,10 +18,12 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyFieldMapParameter.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyFieldMapParameter.cs
index 76e9008a..98ded2f6 100644
--- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyFieldMapParameter.cs
+++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyFieldMapParameter.cs
@@ -10,7 +10,7 @@ internal class AnyFieldMapParameter
public AnyFieldMapParameter(
Func propertyGetter,
Action propertySetter,
- Func schemaGetter)
+ Func schemaGetter)
{
this.PropertyGetter = propertyGetter;
this.PropertySetter = propertySetter;
@@ -21,6 +21,6 @@ public AnyFieldMapParameter(
public Action PropertySetter { get; }
- public Func SchemaGetter { get; }
+ public Func SchemaGetter { get; }
}
}
\ No newline at end of file
diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs
index ee1af993..15aafb4f 100644
--- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs
+++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyListFieldMapParameter{T}.cs
@@ -11,7 +11,7 @@ internal class AnyListFieldMapParameter
public AnyListFieldMapParameter(
Func> propertyGetter,
Action> propertySetter,
- Func schemaGetter)
+ Func schemaGetter)
{
this.PropertyGetter = propertyGetter;
this.PropertySetter = propertySetter;
@@ -22,6 +22,6 @@ public AnyListFieldMapParameter(
public Action> PropertySetter { get; }
- public Func SchemaGetter { get; }
+ public Func SchemaGetter { get; }
}
}
\ No newline at end of file
diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyMapFieldMapParameter{T,U}.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyMapFieldMapParameter{T,U}.cs
index 2399fe31..9b36f6fa 100644
--- a/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyMapFieldMapParameter{T,U}.cs
+++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/AnyMapFieldMapParameter{T,U}.cs
@@ -12,7 +12,7 @@ public AnyMapFieldMapParameter(
Func> propertyMapGetter,
Func propertyGetter,
Action propertySetter,
- Func schemaGetter)
+ Func schemaGetter)
{
this.PropertyMapGetter = propertyMapGetter;
this.PropertyGetter = propertyGetter;
@@ -26,6 +26,6 @@ public AnyMapFieldMapParameter(
public Action PropertySetter { get; }
- public Func SchemaGetter { get; }
+ public Func SchemaGetter { get; }
}
}
\ 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 b087c875..8f392f35 100644
--- a/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs
+++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/MapNode.cs
@@ -116,11 +116,7 @@ public override Dictionary CreateMapWithReference(
if (entry.value.Reference == null)
{
- entry.value.Reference = new AsyncApiReference()
- {
- Type = referenceType,
- Id = entry.key,
- };
+ entry.value.Reference = new AsyncApiReference(entry.key, referenceType);
}
}
finally
@@ -185,7 +181,6 @@ public T GetReferencedObject(ReferenceType referenceType, string referenceId)
{
return new T()
{
- UnresolvedReference = true,
Reference = this.Context.VersionService.ConvertToAsyncApiReference(referenceId, referenceType),
};
}
@@ -214,6 +209,14 @@ public override AsyncApiAny CreateAny()
return new AsyncApiAny(this.node);
}
+ public void ParseFields(ref T parentInstance, IDictionary> fixedFields, IDictionary, Action> patternFields)
+ {
+ foreach (var propertyNode in this)
+ {
+ propertyNode.ParseField(parentInstance, fixedFields, patternFields);
+ }
+ }
+
private string ToScalarValue(JsonNode node)
{
var scalarNode = node is JsonValue value ? value : throw new AsyncApiException($"Expected scalar value");
diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs
index e0359659..2313f30d 100644
--- a/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs
+++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/PropertyNode.cs
@@ -29,8 +29,7 @@ public void ParseField(
IDictionary> fixedFields,
IDictionary, Action> patternFields)
{
- var found = fixedFields.TryGetValue(this.Name, out var fixedFieldMap);
-
+ var _ = fixedFields.TryGetValue(this.Name, out var fixedFieldMap);
if (fixedFieldMap != null)
{
try
@@ -78,8 +77,12 @@ public void ParseField(
}
else
{
- this.Context.Diagnostic.Errors.Add(
- new AsyncApiError("", $"{this.Name} is not a valid property at {this.Context.GetLocation()}"));
+ switch (this.Context.Settings.UnmappedMemberHandling)
+ {
+ case UnmappedMemberHandling.Error:
+ this.Context.Diagnostic.Errors.Add(new AsyncApiError(string.Empty, $"{this.Name} is not a valid property at {this.Context.GetLocation()}"));
+ break;
+ }
}
}
}
diff --git a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs
index 201ab6f7..246018b4 100644
--- a/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs
+++ b/src/LEGO.AsyncAPI.Readers/ParseNodes/ValueNode.cs
@@ -2,16 +2,17 @@
namespace LEGO.AsyncAPI.Readers.ParseNodes
{
+ using System;
+ using System.Text.Json.Nodes;
using LEGO.AsyncAPI.Exceptions;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Readers.Exceptions;
- using System;
- using System.Text.Json.Nodes;
public class ValueNode : ParseNode
{
private readonly JsonNode node;
private string cachedScalarValue;
+
public ValueNode(ParsingContext context, JsonNode node)
: base(
context)
diff --git a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs
index 9bcc051b..5fb0c2b3 100644
--- a/src/LEGO.AsyncAPI.Readers/ParsingContext.cs
+++ b/src/LEGO.AsyncAPI.Readers/ParsingContext.cs
@@ -4,7 +4,9 @@ namespace LEGO.AsyncAPI.Readers
{
using System;
using System.Collections.Generic;
+ using System.IO;
using System.Linq;
+ using System.Text.Json;
using System.Text.Json.Nodes;
using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
@@ -44,6 +46,8 @@ internal Dictionary> ExtensionPars
///
public AsyncApiReaderSettings Settings { get; }
+ public AsyncApiWorkspace Workspace { get; }
+
/////
///// Initializes a new instance of the class.
/////
@@ -63,6 +67,7 @@ public ParsingContext(AsyncApiDiagnostic diagnostic, AsyncApiReaderSettings sett
{
this.Diagnostic = diagnostic;
this.Settings = settings;
+ this.Workspace = new AsyncApiWorkspace();
}
internal AsyncApiDocument Parse(JsonNode jsonNode)
@@ -78,6 +83,10 @@ internal AsyncApiDocument Parse(JsonNode jsonNode)
case string version when version.StartsWith("2"):
this.VersionService = new AsyncApiV2VersionService(this.Diagnostic);
doc = this.VersionService.LoadDocument(this.RootNode);
+
+ // Register components
+ this.Workspace.RegisterComponents(doc); // pre-register components.
+ this.Workspace.RegisterComponent(string.Empty, this.ParseToStream(jsonNode)); // register root document.
this.Diagnostic.SpecificationVersion = AsyncApiVersion.AsyncApi2_0;
break;
@@ -88,6 +97,18 @@ internal AsyncApiDocument Parse(JsonNode jsonNode)
return doc;
}
+ private Stream ParseToStream(JsonNode node)
+ {
+ var stream = new MemoryStream();
+ using (var writer = new Utf8JsonWriter(stream))
+ {
+ node.WriteTo(writer);
+ }
+
+ stream.Position = 0;
+ return stream;
+ }
+
internal T ParseFragment(JsonNode jsonNode, AsyncApiVersion version)
where T : IAsyncApiElement
{
diff --git a/src/LEGO.AsyncAPI.Readers/Services/AsyncApiRemoteReferenceCollector.cs b/src/LEGO.AsyncAPI.Readers/Services/AsyncApiRemoteReferenceCollector.cs
new file mode 100644
index 00000000..4094fc6a
--- /dev/null
+++ b/src/LEGO.AsyncAPI.Readers/Services/AsyncApiRemoteReferenceCollector.cs
@@ -0,0 +1,47 @@
+// Copyright (c) The LEGO Group. All rights reserved.
+
+namespace LEGO.AsyncAPI.Readers.Services
+{
+ using System.Collections.Generic;
+ using LEGO.AsyncAPI.Models.Interfaces;
+ using LEGO.AsyncAPI.Services;
+
+ internal class AsyncApiReferenceCollector : AsyncApiVisitorBase
+ {
+ private readonly List references = new();
+ private AsyncApiWorkspace workspace;
+
+ public AsyncApiReferenceCollector(
+ AsyncApiWorkspace workspace)
+ {
+ this.workspace = workspace;
+ }
+ ///
+ /// List of all external references collected from AsyncApiDocument.
+ ///
+ public IEnumerable References
+ {
+ get
+ {
+ return this.references;
+ }
+ }
+
+ ///
+ /// Collect reference for each reference.
+ ///
+ ///
+ public override void Visit(IAsyncApiReferenceable referenceable)
+ {
+ if (referenceable.Reference != null)
+ {
+ if (referenceable.Reference.Workspace == null)
+ {
+ referenceable.Reference.Workspace = this.workspace;
+ }
+
+ this.references.Add(referenceable);
+ }
+ }
+ }
+}
diff --git a/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs b/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs
index 6e506730..3d949a23 100644
--- a/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs
+++ b/src/LEGO.AsyncAPI.Readers/Services/DefaultStreamLoader.cs
@@ -1,4 +1,4 @@
-// Copyright (c) The LEGO Group. All rights reserved.
+// Copyright (c) The LEGO Group. All rights reserved.
namespace LEGO.AsyncAPI.Readers.Services
{
@@ -6,46 +6,54 @@ namespace LEGO.AsyncAPI.Readers.Services
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
+ using LEGO.AsyncAPI.Readers.Exceptions;
using LEGO.AsyncAPI.Readers.Interface;
internal class DefaultStreamLoader : IStreamLoader
{
- private readonly Uri baseUrl;
- private HttpClient httpClient = new HttpClient();
-
- public DefaultStreamLoader(Uri baseUrl)
- {
- this.baseUrl = baseUrl;
- }
+ private static readonly HttpClient HttpClient = new HttpClient();
public Stream Load(Uri uri)
{
- var absoluteUri = new Uri(this.baseUrl, uri);
- switch (uri.Scheme)
+ try
+ {
+ switch (uri.Scheme)
+ {
+ case "file":
+ return File.OpenRead(uri.AbsolutePath);
+ case "http":
+ case "https":
+ return HttpClient.GetStreamAsync(uri).GetAwaiter().GetResult();
+ default:
+ throw new ArgumentException("Unsupported scheme");
+ }
+ }
+ catch (Exception ex)
{
- case "file":
- return File.OpenRead(absoluteUri.AbsolutePath);
- case "http":
- case "https":
- return this.httpClient.GetStreamAsync(absoluteUri).GetAwaiter().GetResult();
- default:
- throw new ArgumentException("Unsupported scheme");
+
+ throw new AsyncApiReaderException($"Something went wrong trying to fetch '{uri.OriginalString}. {ex.Message}'", ex);
}
}
public async Task LoadAsync(Uri uri)
{
- var absoluteUri = new Uri(this.baseUrl, uri);
-
- switch (absoluteUri.Scheme)
+ try
{
- case "file":
- return File.OpenRead(absoluteUri.AbsolutePath);
- case "http":
- case "https":
- return await this.httpClient.GetStreamAsync(absoluteUri);
- default:
- throw new ArgumentException("Unsupported scheme");
+ switch (uri.Scheme)
+ {
+ case "file":
+ return File.OpenRead(uri.AbsolutePath);
+ case "http":
+ case "https":
+ return await HttpClient.GetStreamAsync(uri);
+ default:
+ throw new ArgumentException("Unsupported scheme");
+ }
+ }
+ catch (Exception ex)
+ {
+
+ throw new AsyncApiReaderException($"Something went wrong trying to fetch '{uri.OriginalString}'. {ex.Message}", ex);
}
}
}
diff --git a/src/LEGO.AsyncAPI.Readers/UnmappedMemberHandling.cs b/src/LEGO.AsyncAPI.Readers/UnmappedMemberHandling.cs
new file mode 100644
index 00000000..b58c34c4
--- /dev/null
+++ b/src/LEGO.AsyncAPI.Readers/UnmappedMemberHandling.cs
@@ -0,0 +1,20 @@
+// Copyright (c) The LEGO Group. All rights reserved.
+
+namespace LEGO.AsyncAPI.Readers
+{
+ ///
+ /// Unmapped member handling.
+ ///
+ public enum UnmappedMemberHandling
+ {
+ ///
+ /// Add error to diagnostics for unmapped members.
+ ///
+ Error,
+
+ ///
+ /// Ignore unmapped members.
+ ///
+ Ignore,
+ }
+}
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs
new file mode 100644
index 00000000..348005e0
--- /dev/null
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiAvroSchemaDeserializer.cs
@@ -0,0 +1,336 @@
+// Copyright (c) The LEGO Group. All rights reserved.
+
+namespace LEGO.AsyncAPI.Readers
+{
+ using LEGO.AsyncAPI.Exceptions;
+ using LEGO.AsyncAPI.Models;
+ using LEGO.AsyncAPI.Models.Avro.LogicalTypes;
+ using LEGO.AsyncAPI.Readers.Exceptions;
+ using LEGO.AsyncAPI.Readers.ParseNodes;
+ using LEGO.AsyncAPI.Writers;
+
+ public class AsyncApiAvroSchemaDeserializer
+ {
+ private static readonly FixedFieldMap FieldFixedFields = new()
+ {
+ { "name", (a, n) => a.Name = n.GetScalarValue() },
+ { "type", (a, n) => a.Type = LoadSchema(n) },
+ { "doc", (a, n) => a.Doc = n.GetScalarValue() },
+ { "default", (a, n) => a.Default = n.CreateAny() },
+ { "aliases", (a, n) => a.Aliases = n.CreateSimpleList(n2 => n2.GetScalarValue()) },
+ { "order", (a, n) => a.Order = n.GetScalarValue().GetEnumFromDisplayName() },
+ };
+
+ private static readonly FixedFieldMap RecordFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "name", (a, n) => a.Name = n.GetScalarValue() },
+ { "doc", (a, n) => a.Doc = n.GetScalarValue() },
+ { "namespace", (a, n) => a.Namespace = n.GetScalarValue() },
+ { "aliases", (a, n) => a.Aliases = n.CreateSimpleList(n2 => n2.GetScalarValue()) },
+ { "fields", (a, n) => a.Fields = n.CreateList(LoadField) },
+ };
+
+ private static readonly FixedFieldMap EnumFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "name", (a, n) => a.Name = n.GetScalarValue() },
+ { "doc", (a, n) => a.Doc = n.GetScalarValue() },
+ { "namespace", (a, n) => a.Namespace = n.GetScalarValue() },
+ { "aliases", (a, n) => a.Aliases = n.CreateSimpleList(n2 => n2.GetScalarValue()) },
+ { "symbols", (a, n) => a.Symbols = n.CreateSimpleList(n2 => n2.GetScalarValue()) },
+ { "default", (a, n) => a.Default = n.GetScalarValue() },
+ };
+
+ private static readonly FixedFieldMap FixedFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "name", (a, n) => a.Name = n.GetScalarValue() },
+ { "namespace", (a, n) => a.Namespace = n.GetScalarValue() },
+ { "aliases", (a, n) => a.Aliases = n.CreateSimpleList(n2 => n2.GetScalarValue()) },
+ { "size", (a, n) => a.Size = int.Parse(n.GetScalarValue(), n.Context.Settings.CultureInfo) },
+ };
+
+ private static readonly FixedFieldMap ArrayFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "items", (a, n) => a.Items = LoadSchema(n) },
+ };
+
+ private static readonly FixedFieldMap MapFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "values", (a, n) => a.Values = n.GetScalarValue().GetEnumFromDisplayName() },
+ };
+
+ private static readonly FixedFieldMap UnionFixedFields = new()
+ {
+ { "types", (a, n) => a.Types = n.CreateList(LoadSchema) },
+ };
+
+ private static readonly FixedFieldMap DecimalFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "logicalType", (a, n) => { } },
+ { "precision", (a, n) => a.Precision = int.Parse(n.GetScalarValue()) },
+ { "scale", (a, n) => a.Scale = int.Parse(n.GetScalarValue()) },
+ };
+
+ private static readonly FixedFieldMap UUIDFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "logicalType", (a, n) => { } },
+ };
+
+ private static readonly FixedFieldMap DateFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "logicalType", (a, n) => { } },
+ };
+
+ private static readonly FixedFieldMap TimeMillisFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "logicalType", (a, n) => { } },
+ };
+
+ private static readonly FixedFieldMap TimeMicrosFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "logicalType", (a, n) => { } },
+ };
+
+ private static readonly FixedFieldMap TimestampMillisFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "logicalType", (a, n) => { } },
+ };
+
+ private static readonly FixedFieldMap TimestampMicrosFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "logicalType", (a, n) => { } },
+ };
+
+ private static readonly FixedFieldMap DurationFixedFields = new()
+ {
+ { "type", (a, n) => { } },
+ { "logicalType", (a, n) => { } },
+ { "name", (a, n) => a.Name = n.GetScalarValue() },
+ { "namespace", (a, n) => a.Namespace = n.GetScalarValue() },
+ { "aliases", (a, n) => a.Aliases = n.CreateSimpleList(n2 => n2.GetScalarValue()) },
+ { "size", (a, n) => { } },
+ };
+
+ private static readonly PatternFieldMap RecordMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap FieldMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap EnumMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap FixedMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap ArrayMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap MapMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap UnionMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap DecimalMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap UUIDMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap DateMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap TimeMillisMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap TimeMicrosMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap TimestampMillisMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap TimestampMicrosMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ private static readonly PatternFieldMap DurationMetadataPatternFields =
+ new()
+ {
+ { s => s.StartsWith(string.Empty), (a, p, n) => a.Metadata[p] = n.CreateAny() },
+ };
+
+ public static AsyncApiAvroSchema LoadSchema(ParseNode node)
+ {
+ if (node is ValueNode valueNode)
+ {
+ return new AvroPrimitive(valueNode.GetScalarValue().GetEnumFromDisplayName());
+ }
+
+ if (node is ListNode)
+ {
+ var union = new AvroUnion();
+ foreach (var item in node as ListNode)
+ {
+ union.Types.Add(LoadSchema(item));
+ }
+
+ return union;
+ }
+
+ if (node is MapNode mapNode)
+ {
+ var pointer = mapNode.GetReferencePointer();
+
+ if (pointer != null)
+ {
+ return new AsyncApiAvroSchemaReference(pointer);
+ }
+
+ var isLogicalType = mapNode["logicalType"] != null;
+ if (isLogicalType)
+ {
+ return LoadLogicalType(mapNode);
+ }
+
+ var type = mapNode["type"]?.Value.GetScalarValue();
+ switch (type)
+ {
+ case "record":
+ var record = new AvroRecord();
+ mapNode.ParseFields(ref record, RecordFixedFields, RecordMetadataPatternFields);
+ return record;
+ case "enum":
+ var @enum = new AvroEnum();
+ mapNode.ParseFields(ref @enum, EnumFixedFields, EnumMetadataPatternFields);
+ return @enum;
+ case "fixed":
+ var @fixed = new AvroFixed();
+ mapNode.ParseFields(ref @fixed, FixedFixedFields, FixedMetadataPatternFields);
+ return @fixed;
+ case "array":
+ var array = new AvroArray();
+ mapNode.ParseFields(ref array, ArrayFixedFields, ArrayMetadataPatternFields);
+ return array;
+ case "map":
+ var map = new AvroMap();
+ mapNode.ParseFields(ref map, MapFixedFields, MapMetadataPatternFields);
+ return map;
+ case "union":
+ var union = new AvroUnion();
+ mapNode.ParseFields(ref union, UnionFixedFields, UnionMetadataPatternFields);
+ return union;
+ default:
+ throw new AsyncApiException($"Unsupported type: {type}");
+ }
+ }
+
+ throw new AsyncApiReaderException("Invalid node type");
+ }
+
+ private static AsyncApiAvroSchema LoadLogicalType(MapNode mapNode)
+ {
+ var type = mapNode["logicalType"]?.Value.GetScalarValue();
+ switch (type)
+ {
+ case "decimal":
+ var @decimal = new AvroDecimal();
+ mapNode.ParseFields(ref @decimal, DecimalFixedFields, DecimalMetadataPatternFields);
+ return @decimal;
+ case "uuid":
+ var uuid = new AvroUUID();
+ mapNode.ParseFields(ref uuid, UUIDFixedFields, UUIDMetadataPatternFields);
+ return uuid;
+ case "date":
+ var date = new AvroDate();
+ mapNode.ParseFields(ref date, DateFixedFields, DateMetadataPatternFields);
+ return date;
+ case "time-millis":
+ var timeMillis = new AvroTimeMillis();
+ mapNode.ParseFields(ref timeMillis, TimeMillisFixedFields, TimeMillisMetadataPatternFields);
+ return timeMillis;
+ case "time-micros":
+ var timeMicros = new AvroTimeMicros();
+ mapNode.ParseFields(ref timeMicros, TimeMicrosFixedFields, TimeMicrosMetadataPatternFields);
+ return timeMicros;
+ case "timestamp-millis":
+ var timestampMillis = new AvroTimestampMillis();
+ mapNode.ParseFields(ref timestampMillis, TimestampMillisFixedFields, TimestampMillisMetadataPatternFields);
+ return timestampMillis;
+ case "timestamp-micros":
+ var timestampMicros = new AvroTimestampMicros();
+ mapNode.ParseFields(ref timestampMicros, TimestampMicrosFixedFields, TimestampMicrosMetadataPatternFields);
+ return timestampMicros;
+ case "duration":
+ var duration = new AvroDuration();
+ mapNode.ParseFields(ref duration, DurationFixedFields, DurationMetadataPatternFields);
+ return duration;
+ default:
+ throw new AsyncApiException($"Unsupported type: {type}");
+ }
+ }
+
+ private static AvroField LoadField(ParseNode node)
+ {
+ var mapNode = node.CheckMapNode("field");
+ var field = new AvroField();
+
+ mapNode.ParseFields(ref field, FieldFixedFields, FieldMetadataPatternFields);
+
+ return field;
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs
index 1aab9e85..aa91b942 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelBindingDeserializer.cs
@@ -15,7 +15,7 @@ internal static AsyncApiBindings LoadChannelBindings(ParseNode
var pointer = mapNode.GetReferencePointer();
if (pointer != null)
{
- return mapNode.GetReferencedObject>(ReferenceType.ChannelBindings, pointer);
+ return new AsyncApiBindingsReference(pointer);
}
var channelBindings = new AsyncApiBindings();
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs
index 1d6acae6..7b9e3e63 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiChannelDeserializer.cs
@@ -30,7 +30,7 @@ public static AsyncApiChannel LoadChannel(ParseNode node)
var pointer = mapNode.GetReferencePointer();
if (pointer != null)
{
- return mapNode.GetReferencedObject(ReferenceType.Channel, pointer);
+ return new AsyncApiChannelReference(pointer);
}
var pathItem = new AsyncApiChannel();
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs
index 3b63db28..75c50873 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiComponentsDeserializer.cs
@@ -10,19 +10,19 @@ internal static partial class AsyncApiV2Deserializer
{
private static FixedFieldMap componentsFixedFields = new()
{
- { "schemas", (a, n) => a.Schemas = n.CreateMapWithReference(ReferenceType.Schema, 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) },
- { "securitySchemes", (a, n) => a.SecuritySchemes = n.CreateMapWithReference(ReferenceType.SecurityScheme, LoadSecurityScheme) },
- { "parameters", (a, n) => a.Parameters = n.CreateMapWithReference(ReferenceType.Parameter, LoadParameter) },
- { "correlationIds", (a, n) => a.CorrelationIds = n.CreateMapWithReference(ReferenceType.CorrelationId, LoadCorrelationId) },
- { "operationTraits", (a, n) => a.OperationTraits = n.CreateMapWithReference(ReferenceType.OperationTrait, LoadOperationTrait) },
- { "messageTraits", (a, n) => a.MessageTraits = n.CreateMapWithReference(ReferenceType.MessageTrait, LoadMessageTrait) },
- { "serverBindings", (a, n) => a.ServerBindings = n.CreateMapWithReference(ReferenceType.ServerBindings, LoadServerBindings) },
- { "channelBindings", (a, n) => a.ChannelBindings = n.CreateMapWithReference(ReferenceType.ChannelBindings, LoadChannelBindings) },
- { "operationBindings", (a, n) => a.OperationBindings = n.CreateMapWithReference(ReferenceType.OperationBindings, LoadOperationBindings) },
- { "messageBindings", (a, n) => a.MessageBindings = n.CreateMapWithReference(ReferenceType.MessageBindings, LoadMessageBindings) },
+ { "schemas", (a, n) => a.Schemas = n.CreateMap(AsyncApiSchemaDeserializer.LoadSchema) },
+ { "servers", (a, n) => a.Servers = n.CreateMap(LoadServer) },
+ { "channels", (a, n) => a.Channels = n.CreateMap(LoadChannel) },
+ { "messages", (a, n) => a.Messages = n.CreateMap(LoadMessage) },
+ { "securitySchemes", (a, n) => a.SecuritySchemes = n.CreateMap(LoadSecurityScheme) },
+ { "parameters", (a, n) => a.Parameters = n.CreateMap(LoadParameter) },
+ { "correlationIds", (a, n) => a.CorrelationIds = n.CreateMap(LoadCorrelationId) },
+ { "operationTraits", (a, n) => a.OperationTraits = n.CreateMap(LoadOperationTrait) },
+ { "messageTraits", (a, n) => a.MessageTraits = n.CreateMap(LoadMessageTrait) },
+ { "serverBindings", (a, n) => a.ServerBindings = n.CreateMap(LoadServerBindings) },
+ { "channelBindings", (a, n) => a.ChannelBindings = n.CreateMap(LoadChannelBindings) },
+ { "operationBindings", (a, n) => a.OperationBindings = n.CreateMap(LoadOperationBindings) },
+ { "messageBindings", (a, n) => a.MessageBindings = n.CreateMap(LoadMessageBindings) },
};
private static PatternFieldMap componentsPatternFields =
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs
index 0b8e88f0..ee908181 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiCorrelationIdDeserializer.cs
@@ -31,7 +31,7 @@ public static AsyncApiCorrelationId LoadCorrelationId(ParseNode node)
var pointer = mapNode.GetReferencePointer();
if (pointer != null)
{
- return mapNode.GetReferencedObject(ReferenceType.CorrelationId, pointer);
+ return new AsyncApiCorrelationIdReference(pointer);
}
var correlationId = new AsyncApiCorrelationId();
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs
index 9a180e84..eb58ca1f 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageBindingDeserializer.cs
@@ -12,9 +12,13 @@ internal static partial class AsyncApiV2Deserializer
internal static AsyncApiBindings LoadMessageBindings(ParseNode node)
{
var mapNode = node.CheckMapNode("messageBindings");
+ var pointer = mapNode.GetReferencePointer();
+ if (pointer != null)
+ {
+ return new AsyncApiBindingsReference(pointer);
+ }
var messageBindings = new AsyncApiBindings();
-
foreach (var property in mapNode)
{
var messageBinding = LoadMessageBinding(property);
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs
index 4c16bf22..fe7b8d4a 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageDeserializer.cs
@@ -2,12 +2,13 @@
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.Models.Interfaces;
using LEGO.AsyncAPI.Readers.ParseNodes;
- using System.Collections.Generic;
- using System.Linq;
///
/// Class containing logic to deserialize AsyncApi document into
@@ -21,10 +22,10 @@ internal static partial class AsyncApiV2Deserializer
"messageId", (a, n) => { a.MessageId = n.GetScalarValue(); }
},
{
- "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); }
+ "headers", (a, n) => { a.Headers = AsyncApiSchemaDeserializer.LoadSchema(n); }
},
{
- "payload", (a, n) => { a.Payload = JsonSchemaDeserializer.LoadSchema(n); }
+ "payload", (a, n) => { a.Payload = null; /* resolved after the initial run */ }
},
{
"correlationId", (a, n) => { a.CorrelationId = LoadCorrelationId(n); }
@@ -64,7 +65,39 @@ internal static partial class AsyncApiV2Deserializer
},
};
- static readonly IEnumerable SupportedSchemaFormats = new List
+ public static IAsyncApiMessagePayload LoadJsonSchemaPayload(ParseNode n)
+ {
+ return LoadPayload(n, null);
+ }
+
+ public static IAsyncApiMessagePayload LoadAvroPayload(ParseNode n)
+ {
+ return LoadPayload(n, "application/vnd.apache.avro");
+ }
+
+ private static IAsyncApiMessagePayload LoadPayload(ParseNode n, string format)
+ {
+
+ if (n == null)
+ {
+ return null;
+ }
+
+ switch (format)
+ {
+ case null:
+ case "":
+ case var _ when SupportedJsonSchemaFormats.Where(s => format.StartsWith(s)).Any():
+ return AsyncApiSchemaDeserializer.LoadSchema(n);
+ case var _ when SupportedAvroSchemaFormats.Where(s => format.StartsWith(s)).Any():
+ return AsyncApiAvroSchemaDeserializer.LoadSchema(n);
+ default:
+ var supportedFormats = SupportedJsonSchemaFormats.Concat(SupportedAvroSchemaFormats);
+ throw new AsyncApiException($"'Could not deserialize Payload. Supported formats are {string.Join(", ", supportedFormats)}");
+ }
+ }
+
+ static readonly IEnumerable SupportedJsonSchemaFormats = new List
{
"application/vnd.aai.asyncapi+json",
"application/vnd.aai.asyncapi+yaml",
@@ -73,11 +106,21 @@ internal static partial class AsyncApiV2Deserializer
"application/schema+yaml;version=draft-07",
};
+ static readonly IEnumerable SupportedAvroSchemaFormats = new List
+ {
+ "application/vnd.apache.avro",
+ "application/vnd.apache.avro+json",
+ "application/vnd.apache.avro+yaml",
+ "application/vnd.apache.avro+json;version=1.9.0",
+ "application/vnd.apache.avro+yaml;version=1.9.0",
+ };
+
private static string LoadSchemaFormat(string schemaFormat)
{
- if (!SupportedSchemaFormats.Where(s => schemaFormat.StartsWith(s)).Any())
+ var supportedFormats = SupportedJsonSchemaFormats.Concat(SupportedAvroSchemaFormats);
+ if (!supportedFormats.Where(s => schemaFormat.StartsWith(s)).Any())
{
- throw new AsyncApiException($"'{schemaFormat}' is not a supported format. Supported formats are {string.Join(", ", SupportedSchemaFormats)}");
+ throw new AsyncApiException($"'{schemaFormat}' is not a supported format. Supported formats are {string.Join(", ", supportedFormats)}");
}
return schemaFormat;
@@ -94,12 +137,13 @@ public static AsyncApiMessage LoadMessage(ParseNode node)
var pointer = mapNode.GetReferencePointer();
if (pointer != null)
{
- return mapNode.GetReferencedObject(ReferenceType.Message, pointer);
+ return new AsyncApiMessageReference(pointer);
}
var message = new AsyncApiMessage();
ParseMap(mapNode, message, messageFixedFields, messagePatternFields);
+ message.Payload = LoadPayload(mapNode["payload"]?.Value, message.SchemaFormat);
return message;
}
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiMessageTraitDeserializer.cs
index eca8af64..40e8a945 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 = JsonSchemaDeserializer.LoadSchema(n); } },
+ { "headers", (a, n) => { a.Headers = AsyncApiSchemaDeserializer.LoadSchema(n); } },
{ "correlationId", (a, n) => { a.CorrelationId = LoadCorrelationId(n); } },
{ "schemaFormat", (a, n) => { a.SchemaFormat = n.GetScalarValue(); } },
{ "contentType", (a, n) => { a.ContentType = n.GetScalarValue(); } },
@@ -38,7 +38,7 @@ public static AsyncApiMessageTrait LoadMessageTrait(ParseNode node)
if (pointer != null)
{
- return mapNode.GetReferencedObject(ReferenceType.MessageTrait, pointer);
+ return new AsyncApiMessageTraitReference(pointer);
}
var messageTrait = new AsyncApiMessageTrait();
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs
index 4d4eb85f..0fb5db0a 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationBindingDeserializer.cs
@@ -12,9 +12,13 @@ internal static partial class AsyncApiV2Deserializer
internal static AsyncApiBindings LoadOperationBindings(ParseNode node)
{
var mapNode = node.CheckMapNode("operationBindings");
+ var pointer = mapNode.GetReferencePointer();
+ if (pointer != null)
+ {
+ return new AsyncApiBindingsReference(pointer);
+ }
var operationBindings = new AsyncApiBindings();
-
foreach (var property in mapNode)
{
var operationBinding = LoadOperationBinding(property);
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs
index 561456e9..ee503d91 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiOperationTraitDeserializer.cs
@@ -31,7 +31,7 @@ public static AsyncApiOperationTrait LoadOperationTrait(ParseNode node)
var pointer = mapNode.GetReferencePointer();
if (pointer != null)
{
- return mapNode.GetReferencedObject(ReferenceType.OperationTrait, pointer);
+ return new AsyncApiOperationTraitReference(pointer);
}
var operationTrait = new AsyncApiOperationTrait();
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiParameterDeserializer.cs
index bff810f1..e7628c12 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 = JsonSchemaDeserializer.LoadSchema(n); } },
+ { "schema", (a, n) => { a.Schema = AsyncApiSchemaDeserializer.LoadSchema(n); } },
{ "location", (a, n) => { a.Location = n.GetScalarValue(); } },
};
@@ -28,7 +28,7 @@ public static AsyncApiParameter LoadParameter(ParseNode node)
var pointer = mapNode.GetReferencePointer();
if (pointer != null)
{
- return mapNode.GetReferencedObject(ReferenceType.Parameter, pointer);
+ return new AsyncApiParameterReference(pointer);
}
var parameter = new AsyncApiParameter();
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs
index 1934cb0d..45b74674 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSchemaDeserializer.cs
@@ -9,9 +9,9 @@ namespace LEGO.AsyncAPI.Readers
using LEGO.AsyncAPI.Readers.ParseNodes;
using LEGO.AsyncAPI.Writers;
- public class JsonSchemaDeserializer
+ public class AsyncApiSchemaDeserializer
{
- private static readonly FixedFieldMap schemaFixedFields = new()
+ private static readonly FixedFieldMap schemaFixedFields = new()
{
{
"title", (a, n) => { a.Title = n.GetScalarValue(); }
@@ -215,35 +215,13 @@ public class JsonSchemaDeserializer
},
};
- private static readonly PatternFieldMap schemaPatternFields =
+ private static readonly PatternFieldMap schemaPatternFields =
new()
{
{ s => s.StartsWith("x-"), (o, p, n) => o.AddExtension(p, AsyncApiV2Deserializer.LoadExtension(p, n)) },
};
- private static readonly AnyFieldMap schemaAnyFields = new()
- {
- {
- AsyncApiConstants.Default,
- new AnyFieldMapParameter(
- s => s.Default,
- (s, v) => s.Default = v,
- s => s)
- },
- };
-
- private static readonly AnyListFieldMap schemaAnyListFields = new()
- {
- {
- AsyncApiConstants.Enum,
- new AnyListFieldMapParameter(
- s => s.Enum,
- (s, v) => s.Enum = v,
- s => s)
- },
- };
-
- public static AsyncApiSchema LoadSchema(ParseNode node)
+ public static AsyncApiJsonSchema LoadSchema(ParseNode node)
{
var mapNode = node.CheckMapNode(AsyncApiConstants.Schema);
@@ -251,23 +229,16 @@ public static AsyncApiSchema LoadSchema(ParseNode node)
if (pointer != null)
{
- return new AsyncApiSchema
- {
- UnresolvedReference = true,
- Reference = node.Context.VersionService.ConvertToAsyncApiReference(pointer, ReferenceType.Schema),
- };
+ return new AsyncApiJsonSchemaReference(pointer);
}
- var schema = new AsyncApiSchema();
+ var schema = new AsyncApiJsonSchema();
foreach (var propertyNode in mapNode)
{
propertyNode.ParseField(schema, schemaFixedFields, schemaPatternFields);
}
- AsyncApiV2Deserializer.ProcessAnyFields(mapNode, schema, schemaAnyFields);
- AsyncApiV2Deserializer.ProcessAnyListFields(mapNode, schema, schemaAnyListFields);
-
return schema;
}
}
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecurityRequirementDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecurityRequirementDeserializer.cs
index e3574f6a..f9f02907 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecurityRequirementDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecurityRequirementDeserializer.cs
@@ -36,19 +36,11 @@ public static AsyncApiSecurityRequirement LoadSecurityRequirement(ParseNode node
return securityRequirement;
}
- private static AsyncApiSecurityScheme LoadSecuritySchemeByReference(
+ private static AsyncApiSecuritySchemeReference LoadSecuritySchemeByReference(
ParsingContext context,
string schemeName)
{
- var securitySchemeObject = new AsyncApiSecurityScheme
- {
- UnresolvedReference = true,
- Reference = new AsyncApiReference
- {
- Id = schemeName,
- Type = ReferenceType.SecurityScheme,
- },
- };
+ var securitySchemeObject = new AsyncApiSecuritySchemeReference(schemeName);
return securitySchemeObject;
}
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs
index 92708116..37481197 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiSecuritySchemeDeserializer.cs
@@ -56,7 +56,7 @@ public static AsyncApiSecurityScheme LoadSecurityScheme(ParseNode node)
var pointer = mapNode.GetReferencePointer();
if (pointer != null)
{
- return mapNode.GetReferencedObject(ReferenceType.SecurityScheme, pointer);
+ return new AsyncApiSecuritySchemeReference(pointer);
}
var securityScheme = new AsyncApiSecurityScheme();
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs
index 44c821d3..18a8ea84 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerBindingDeserializer.cs
@@ -15,7 +15,7 @@ internal static AsyncApiBindings LoadServerBindings(ParseNode no
var pointer = mapNode.GetReferencePointer();
if (pointer != null)
{
- return mapNode.GetReferencedObject>(ReferenceType.ServerBindings, pointer);
+ return new AsyncApiBindingsReference(pointer);
}
var serverBindings = new AsyncApiBindings();
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs
index 6eae00af..9d72d588 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerDeserializer.cs
@@ -52,7 +52,7 @@ public static AsyncApiServer LoadServer(ParseNode node)
var pointer = mapNode.GetReferencePointer();
if (pointer != null)
{
- return mapNode.GetReferencedObject(ReferenceType.Server, pointer);
+ return new AsyncApiServerReference(pointer);
}
var server = new AsyncApiServer();
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs
index b773d255..a47ec6a6 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiServerVariableDeserializer.cs
@@ -41,7 +41,7 @@ public static AsyncApiServerVariable LoadServerVariable(ParseNode node)
var pointer = mapNode.GetReferencePointer();
if (pointer != null)
{
- return mapNode.GetReferencedObject(ReferenceType.ServerVariable, pointer);
+ return new AsyncApiServerVariableReference(pointer);
}
var serverVariable = new AsyncApiServerVariable();
diff --git a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs
index 10edd3ba..6cbca7ed 100644
--- a/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs
+++ b/src/LEGO.AsyncAPI.Readers/V2/AsyncApiV2VersionService.cs
@@ -9,7 +9,6 @@ namespace LEGO.AsyncAPI.Readers.V2
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.Interface;
using LEGO.AsyncAPI.Readers.ParseNodes;
- using LEGO.AsyncAPI.Writers;
internal class AsyncApiV2VersionService : IAsyncApiVersionService
{
@@ -35,14 +34,22 @@ public AsyncApiV2VersionService(AsyncApiDiagnostic diagnostic)
[typeof(AsyncApiOAuthFlows)] = AsyncApiV2Deserializer.LoadOAuthFlows,
[typeof(AsyncApiOperation)] = AsyncApiV2Deserializer.LoadOperation,
[typeof(AsyncApiParameter)] = AsyncApiV2Deserializer.LoadParameter,
- [typeof(AsyncApiSchema)] = JsonSchemaDeserializer.LoadSchema,
+ [typeof(AsyncApiJsonSchema)] = AsyncApiSchemaDeserializer.LoadSchema,
+ [typeof(AsyncApiAvroSchema)] = AsyncApiAvroSchemaDeserializer.LoadSchema,
+ [typeof(AsyncApiJsonSchema)] = AsyncApiV2Deserializer.LoadJsonSchemaPayload,
+ [typeof(AsyncApiAvroSchema)] = AsyncApiV2Deserializer.LoadAvroPayload,
[typeof(AsyncApiSecurityRequirement)] = AsyncApiV2Deserializer.LoadSecurityRequirement,
[typeof(AsyncApiSecurityScheme)] = AsyncApiV2Deserializer.LoadSecurityScheme,
[typeof(AsyncApiServer)] = AsyncApiV2Deserializer.LoadServer,
[typeof(AsyncApiServerVariable)] = AsyncApiV2Deserializer.LoadServerVariable,
[typeof(AsyncApiTag)] = AsyncApiV2Deserializer.LoadTag,
[typeof(AsyncApiMessage)] = AsyncApiV2Deserializer.LoadMessage,
+ [typeof(AsyncApiMessageTrait)] = AsyncApiV2Deserializer.LoadMessageTrait,
[typeof(AsyncApiChannel)] = AsyncApiV2Deserializer.LoadChannel,
+ [typeof(AsyncApiBindings)] = AsyncApiV2Deserializer.LoadServerBindings,
+ [typeof(AsyncApiBindings)] = AsyncApiV2Deserializer.LoadChannelBindings,
+ [typeof(AsyncApiBindings)] = AsyncApiV2Deserializer.LoadMessageBindings,
+ [typeof(AsyncApiBindings)] = AsyncApiV2Deserializer.LoadOperationBindings,
};
///
@@ -59,78 +66,15 @@ public AsyncApiReference ConvertToAsyncApiReference(
throw new AsyncApiException($"The reference string '{reference}' has invalid format.");
}
- var segments = reference.Split('#');
- if (segments.Length == 1)
+ try
{
- if (type == ReferenceType.SecurityScheme)
- {
- return new AsyncApiReference
- {
- Type = type,
- Id = reference,
- };
- }
-
- var asyncApiReference = new AsyncApiReference();
- asyncApiReference.Type = type;
- if (reference.StartsWith("/"))
- {
- asyncApiReference.IsFragment = true;
- }
-
- asyncApiReference.ExternalResource = segments[0];
-
- return asyncApiReference;
+ return new AsyncApiReference(reference, type);
}
- else if (segments.Length == 2)
+ catch (AsyncApiException ex)
{
- // Local reference
- if (reference.StartsWith("#"))
- {
- try
- {
- return this.ParseReference(segments[1]);
- }
- catch (AsyncApiException ex)
- {
- this.Diagnostic.Errors.Add(new AsyncApiError(ex));
- return null;
- }
- }
-
- var id = segments[1];
- var asyncApiReference = new AsyncApiReference();
- if (id.StartsWith("/components/"))
- {
- var localSegments = segments[1].Split('/');
- var referencedType = localSegments[2].GetEnumFromDisplayName();
- if (type == null)
- {
- type = referencedType;
- }
- else
- {
- if (type != referencedType)
- {
- throw new AsyncApiException("Referenced type mismatch");
- }
- }
-
- id = localSegments[3];
- }
- else
- {
- asyncApiReference.IsFragment = true;
- }
-
- asyncApiReference.ExternalResource = segments[0];
- asyncApiReference.Type = type;
- asyncApiReference.Id = id;
-
- return asyncApiReference;
+ this.Diagnostic.Errors.Add(new AsyncApiError(ex));
+ return null;
}
-
- throw new AsyncApiException($"The reference string '{reference}' has invalid format.");
}
public AsyncApiDocument LoadDocument(RootNode rootNode)
@@ -143,27 +87,5 @@ public T LoadElement(ParseNode node)
{
return (T)this.loaders[typeof(T)](node);
}
-
- private AsyncApiReference ParseReference(string localReference)
- {
- if (string.IsNullOrWhiteSpace(localReference))
- {
- throw new ArgumentException(
- $"The argument '{nameof(localReference)}' is null, empty or consists only of white-space.");
- }
-
- var segments = localReference.Split('/');
-
- if (segments.Length == 4)
- {
- if (segments[1] == "components")
- {
- var referenceType = segments[2].GetEnumFromDisplayName();
- return new AsyncApiReference { Type = referenceType, Id = segments[3] };
- }
- }
-
- throw new AsyncApiException($"The reference string '{localReference}' has invalid format.");
- }
}
}
\ No newline at end of file
diff --git a/src/LEGO.AsyncAPI.Readers/YamlConverter.cs b/src/LEGO.AsyncAPI.Readers/YamlConverter.cs
index 648ee50a..942bc28f 100644
--- a/src/LEGO.AsyncAPI.Readers/YamlConverter.cs
+++ b/src/LEGO.AsyncAPI.Readers/YamlConverter.cs
@@ -10,55 +10,55 @@ namespace LEGO.AsyncAPI.Readers
internal static class YamlConverter
{
- public static JsonNode ToJsonNode(this YamlDocument yamlDocument, AsyncApiReaderSettings settings)
+ public static JsonNode ToJsonNode(this YamlDocument yamlDocument, CultureInfo cultureInfo)
{
- return yamlDocument.RootNode.ToJsonNode(settings);
+ return yamlDocument.RootNode.ToJsonNode(cultureInfo);
}
- public static JsonObject ToJsonObject(this YamlMappingNode yamlMappingNode, AsyncApiReaderSettings settings)
+ public static JsonObject ToJsonObject(this YamlMappingNode yamlMappingNode, CultureInfo cultureInfo)
{
var node = new JsonObject();
foreach (var keyValuePair in yamlMappingNode)
{
var key = ((YamlScalarNode)keyValuePair.Key).Value!;
- node[key] = keyValuePair.Value.ToJsonNode(settings);
+ node[key] = keyValuePair.Value.ToJsonNode(cultureInfo);
}
return node;
}
- public static JsonArray ToJsonArray(this YamlSequenceNode yaml, AsyncApiReaderSettings settings)
+ public static JsonArray ToJsonArray(this YamlSequenceNode yaml, CultureInfo cultureInfo)
{
var node = new JsonArray();
foreach (var value in yaml)
{
- node.Add(value.ToJsonNode(settings));
+ node.Add(value.ToJsonNode(cultureInfo));
}
return node;
}
- public static JsonNode ToJsonNode(this YamlNode yaml, AsyncApiReaderSettings settings)
+ public static JsonNode ToJsonNode(this YamlNode yaml, CultureInfo cultureInfo)
{
return yaml switch
{
- YamlMappingNode map => map.ToJsonObject(settings),
- YamlSequenceNode seq => seq.ToJsonArray(settings),
- YamlScalarNode scalar => scalar.ToJsonValue(settings),
+ YamlMappingNode map => map.ToJsonObject(cultureInfo),
+ YamlSequenceNode seq => seq.ToJsonArray(cultureInfo),
+ YamlScalarNode scalar => scalar.ToJsonValue(cultureInfo),
_ => throw new NotSupportedException("This yaml isn't convertible to JSON"),
};
}
- private static JsonValue ToJsonValue(this YamlScalarNode yaml, AsyncApiReaderSettings settings)
+ private static JsonValue ToJsonValue(this YamlScalarNode yaml, CultureInfo cultureInfo)
{
switch (yaml.Style)
{
case ScalarStyle.Plain:
- return decimal.TryParse(yaml.Value, NumberStyles.Float, settings.CultureInfo, out var d)
+ return decimal.TryParse(yaml.Value, NumberStyles.Float, cultureInfo, out var d)
? JsonValue.Create(d)
: bool.TryParse(yaml.Value, out var b)
? JsonValue.Create(b)
- : JsonValue.Create(yaml.Value) !;
+ : JsonValue.Create(yaml.Value)!;
case ScalarStyle.SingleQuoted:
case ScalarStyle.DoubleQuoted:
case ScalarStyle.Literal:
diff --git a/src/LEGO.AsyncAPI/AsyncApiVersion.cs b/src/LEGO.AsyncAPI/AsyncApiVersion.cs
index c5be2cc8..dbac63de 100644
--- a/src/LEGO.AsyncAPI/AsyncApiVersion.cs
+++ b/src/LEGO.AsyncAPI/AsyncApiVersion.cs
@@ -5,7 +5,7 @@ namespace LEGO.AsyncAPI
public enum AsyncApiVersion
{
///
- /// Represents AsyncAPI V2 spec
+ /// Represents AsyncAPI V2 spec.
///
AsyncApi2_0,
}
diff --git a/src/LEGO.AsyncAPI/AsyncApiWorkspace.cs b/src/LEGO.AsyncAPI/AsyncApiWorkspace.cs
new file mode 100644
index 00000000..5d581e22
--- /dev/null
+++ b/src/LEGO.AsyncAPI/AsyncApiWorkspace.cs
@@ -0,0 +1,192 @@
+// Copyright (c) The LEGO Group. All rights reserved.
+
+namespace LEGO.AsyncAPI
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using LEGO.AsyncAPI.Models;
+ using LEGO.AsyncAPI.Models.Interfaces;
+
+ public class AsyncApiWorkspace
+ {
+ private readonly Dictionary artifactsRegistry = new();
+ private readonly Dictionary resolvedReferenceRegistry = new();
+
+ public void RegisterComponents(AsyncApiDocument document)
+ {
+ if (document?.Components == null)
+ {
+ return;
+ }
+
+ string baseUri = "#/components/";
+ string location;
+
+ // Register Schema
+ foreach (var item in document.Components.Schemas)
+ {
+ location = baseUri + ReferenceType.Schema.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register Parameters
+ foreach (var item in document.Components.Parameters)
+ {
+ location = baseUri + ReferenceType.Parameter.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register Channels
+ foreach (var item in document.Components.Channels)
+ {
+ location = baseUri + ReferenceType.Channel.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register Servers
+ foreach (var item in document.Components.Servers)
+ {
+ location = baseUri + ReferenceType.Server.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register ServerVariables
+ foreach (var item in document.Components.ServerVariables)
+ {
+ location = baseUri + ReferenceType.ServerVariable.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register Messages
+ foreach (var item in document.Components.Messages)
+ {
+ location = baseUri + ReferenceType.Message.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register SecuritySchemes
+ foreach (var item in document.Components.SecuritySchemes)
+ {
+ location = baseUri + ReferenceType.SecurityScheme.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ this.RegisterComponent(item.Key, item.Value);
+ }
+
+ // Register Parameters
+ foreach (var item in document.Components.Parameters)
+ {
+ location = baseUri + ReferenceType.Parameter.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register CorrelationIds
+ foreach (var item in document.Components.CorrelationIds)
+ {
+ location = baseUri + ReferenceType.CorrelationId.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register OperationTraits
+ foreach (var item in document.Components.OperationTraits)
+ {
+ location = baseUri + ReferenceType.OperationTrait.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register MessageTraits
+ foreach (var item in document.Components.MessageTraits)
+ {
+ location = baseUri + ReferenceType.MessageTrait.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register ServerBindings
+ foreach (var item in document.Components.ServerBindings)
+ {
+ location = baseUri + ReferenceType.ServerBindings.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register ChannelBindings
+ foreach (var item in document.Components.ChannelBindings)
+ {
+ location = baseUri + ReferenceType.ChannelBindings.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register OperationBindings
+ foreach (var item in document.Components.OperationBindings)
+ {
+ location = baseUri + ReferenceType.OperationBindings.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+
+ // Register MessageBindings
+ foreach (var item in document.Components.MessageBindings)
+ {
+ location = baseUri + ReferenceType.MessageBindings.GetDisplayName() + "/" + item.Key;
+ this.RegisterComponent(location, item.Value);
+ }
+ }
+
+ public bool RegisterComponent(string location, T component)
+ {
+ var uri = this.ToLocationUrl(location);
+ if (component is IAsyncApiSerializable referenceable)
+ {
+ if (!this.resolvedReferenceRegistry.ContainsKey(uri))
+ {
+ this.resolvedReferenceRegistry[uri] = referenceable;
+ return true;
+ }
+ }
+
+ if (component is Stream stream)
+ {
+ if (!this.artifactsRegistry.ContainsKey(uri))
+ {
+ this.artifactsRegistry[uri] = stream;
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool Contains(string location)
+ {
+ var key = this.ToLocationUrl(location);
+ return this.resolvedReferenceRegistry.ContainsKey(key) || this.artifactsRegistry.ContainsKey(key);
+ }
+
+ public T ResolveReference(AsyncApiReference reference)
+ where T : class
+ {
+ return this.ResolveReference(reference.Reference);
+ }
+
+ public T ResolveReference(string location)
+ where T : class
+ {
+ var uri = this.ToLocationUrl(location);
+ if (this.resolvedReferenceRegistry.TryGetValue(uri, out var referenceableValue))
+ {
+ return referenceableValue as T;
+ }
+
+ if (this.artifactsRegistry.TryGetValue(uri, out var stream))
+ {
+ stream.Position = 0;
+ return (T)(object)stream;
+ }
+
+ return default;
+ }
+
+ private Uri ToLocationUrl(string location)
+ {
+ return new(location, UriKind.RelativeOrAbsolute);
+ }
+ }
+}
diff --git a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj
index 99e69016..c73cc9d0 100644
--- a/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj
+++ b/src/LEGO.AsyncAPI/LEGO.AsyncAPI.csproj
@@ -5,6 +5,7 @@
AsyncAPI.NET
LEGO.AsyncAPI
LEGO.AsyncAPI
+ netstandard2.0;net8
@@ -19,7 +20,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
<_Parameter1>$(MSBuildProjectName).Tests
diff --git a/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs b/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs
index 5c5f2b31..45b764b3 100644
--- a/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs
+++ b/src/LEGO.AsyncAPI/Models/Any/AsyncApiAny.cs
@@ -2,7 +2,6 @@
namespace LEGO.AsyncAPI.Models
{
- using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Nodes;
using LEGO.AsyncAPI.Models.Interfaces;
diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs
index 4034b4e9..a2c8303a 100644
--- a/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs
+++ b/src/LEGO.AsyncAPI/Models/AsyncApiBinding.cs
@@ -4,7 +4,6 @@ namespace LEGO.AsyncAPI.Bindings
{
using System;
using System.Collections.Generic;
- using LEGO.AsyncAPI.Models;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;
@@ -12,10 +11,6 @@ public abstract class AsyncApiBinding : IBinding
{
public abstract string BindingKey { get; }
- public bool UnresolvedReference { get; set; }
-
- public AsyncApiReference Reference { get; set; }
-
public IDictionary Extensions { get; set; } = new Dictionary();
public string BindingVersion { get; set; }
@@ -27,12 +22,6 @@ public void SerializeV2(IAsyncApiWriter writer)
throw new ArgumentNullException(nameof(writer));
}
- if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference))
- {
- this.Reference.SerializeV2(writer);
- return;
- }
-
this.SerializeProperties(writer);
}
diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs b/src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs
index f11858aa..260629ae 100644
--- a/src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs
+++ b/src/LEGO.AsyncAPI/Models/AsyncApiBindings{TBinding}.cs
@@ -3,39 +3,17 @@
namespace LEGO.AsyncAPI.Models
{
using System;
+ using System.Collections;
using System.Collections.Generic;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Writers;
- public class AsyncApiBindings : Dictionary, IAsyncApiReferenceable
+ public class AsyncApiBindings : IDictionary, IAsyncApiSerializable
where TBinding : IBinding
{
- public bool UnresolvedReference { get; set; }
+ private Dictionary inner = new Dictionary();
- public AsyncApiReference Reference { get; set; }
-
- public void Add(TBinding binding)
- {
- this[binding.BindingKey] = binding;
- }
-
- public void SerializeV2(IAsyncApiWriter writer)
- {
- if (writer is null)
- {
- throw new ArgumentNullException(nameof(writer));
- }
-
- if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference))
- {
- this.Reference.SerializeV2(writer);
- return;
- }
-
- this.SerializeV2WithoutReference(writer);
- }
-
- public void SerializeV2WithoutReference(IAsyncApiWriter writer)
+ public virtual void SerializeV2(IAsyncApiWriter writer)
{
if (writer is null)
{
@@ -56,5 +34,79 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer)
writer.WriteEndObject();
}
+
+ public virtual void Add(TBinding binding)
+ {
+ this[binding.BindingKey] = binding;
+ }
+
+ public virtual TBinding this[string key]
+ {
+ get => inner[key];
+ set => inner[key] = value;
+ }
+
+ public virtual ICollection Keys => inner.Keys;
+
+ public virtual ICollection Values => inner.Values;
+
+ public virtual int Count => inner.Count;
+
+ public virtual bool IsReadOnly => ((IDictionary)inner).IsReadOnly;
+
+ public virtual void Add(string key, TBinding value)
+ {
+ inner.Add(key, value);
+ }
+
+ public virtual bool ContainsKey(string key)
+ {
+ return inner.ContainsKey(key);
+ }
+
+ public virtual bool Remove(string key)
+ {
+ return inner.Remove(key);
+ }
+
+ public virtual bool TryGetValue(string key, out TBinding value)
+ {
+ return inner.TryGetValue(key, out value);
+ }
+
+ public virtual void Add(KeyValuePair item)
+ {
+ ((IDictionary)inner).Add(item);
+ }
+
+ public virtual void Clear()
+ {
+ inner.Clear();
+ }
+
+ public virtual bool Contains(KeyValuePair item)
+ {
+ return ((IDictionary)inner).Contains(item);
+ }
+
+ public virtual void CopyTo(KeyValuePair[] array, int arrayIndex)
+ {
+ ((IDictionary)inner).CopyTo(array, arrayIndex);
+ }
+
+ public virtual bool Remove(KeyValuePair item)
+ {
+ return ((IDictionary)inner).Remove(item);
+ }
+
+ public virtual IEnumerator> GetEnumerator()
+ {
+ return inner.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return inner.GetEnumerator();
+ }
}
}
diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiChannel.cs b/src/LEGO.AsyncAPI/Models/AsyncApiChannel.cs
index 2ff637f5..a7e25963 100644
--- a/src/LEGO.AsyncAPI/Models/AsyncApiChannel.cs
+++ b/src/LEGO.AsyncAPI/Models/AsyncApiChannel.cs
@@ -10,12 +10,12 @@ namespace LEGO.AsyncAPI.Models
///
/// Describes the operations available on a single channel.
///
- public class AsyncApiChannel : IAsyncApiReferenceable, IAsyncApiExtensible
+ public class AsyncApiChannel : IAsyncApiSerializable, IAsyncApiExtensible
{
///
/// an optional description of this channel item. CommonMark syntax can be used for rich text representation.
///
- public string Description { get; set; }
+ public virtual string Description { get; set; }
///
/// the servers on which this channel is available, specified as an optional unordered list of names (string keys) of Server Objects defined in the Servers Object (a map).
@@ -23,52 +23,32 @@ public class AsyncApiChannel : IAsyncApiReferenceable, IAsyncApiExtensible
///
/// If servers is absent or empty then this channel must be available on all servers defined in the Servers Object.
///
- public IList Servers { get; set; } = new List();
+ public virtual IList Servers { get; set; } = new List();
///
/// a definition of the SUBSCRIBE operation, which defines the messages produced by the application and sent to the channel.
///
- public AsyncApiOperation Subscribe { get; set; }
+ public virtual AsyncApiOperation Subscribe { get; set; }
///
/// a definition of the PUBLISH operation, which defines the messages consumed by the application from the channel.
///
- public AsyncApiOperation Publish { get; set; }
+ public virtual AsyncApiOperation Publish { get; set; }
///
/// a map of the parameters included in the channel name. It SHOULD be present only when using channels with expressions (as defined by RFC 6570 section 2.2).
///
- public IDictionary Parameters { get; set; } = new Dictionary();
+ public virtual IDictionary Parameters { get; set; } = new Dictionary();
///
/// a map where the keys describe the name of the protocol and the values describe protocol-specific definitions for the channel.
///
- public AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings();
+ public virtual AsyncApiBindings Bindings { get; set; } = new AsyncApiBindings();
///
- public IDictionary Extensions { get; set; } = new Dictionary();
+ public virtual IDictionary Extensions { get; set; } = new Dictionary();
- public bool UnresolvedReference { get; set; }
-
- public AsyncApiReference Reference { get; set; }
-
- public void SerializeV2(IAsyncApiWriter writer)
- {
- if (writer is null)
- {
- throw new ArgumentNullException(nameof(writer));
- }
-
- if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference))
- {
- this.Reference.SerializeV2(writer);
- return;
- }
-
- this.SerializeV2WithoutReference(writer);
- }
-
- public void SerializeV2WithoutReference(IAsyncApiWriter writer)
+ public virtual void SerializeV2(IAsyncApiWriter writer)
{
if (writer is null)
{
@@ -92,11 +72,9 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer)
// parameters
writer.WriteOptionalMap(AsyncApiConstants.Parameters, this.Parameters, (writer, key, component) =>
{
- if (component.Reference != null &&
- component.Reference.Type == ReferenceType.Channel &&
- component.Reference.Id == key)
+ if (component is AsyncApiParameterReference reference)
{
- component.SerializeV2WithoutReference(writer);
+ reference.SerializeV2(writer);
}
else
{
diff --git a/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs b/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs
index 6c8a2a4c..0d00a9d9 100644
--- a/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs
+++ b/src/LEGO.AsyncAPI/Models/AsyncApiComponents.cs
@@ -19,7 +19,7 @@ public class AsyncApiComponents : IAsyncApiExtensible, IAsyncApiSerializable
///
/// An object to hold reusable Schema Objects.
///
- public IDictionary Schemas { get; set; } = new Dictionary();
+ public IDictionary Schemas { get; set; } = new Dictionary();
///
/// An object to hold reusable Server Objects.
@@ -97,21 +97,21 @@ public void SerializeV2(IAsyncApiWriter writer)
// If references have been inlined we don't need the to render the components section
// however if they have cycles, then we will need a component rendered
- if (writer.GetSettings().InlineReferences)
+ if (writer.GetSettings().InlineLocalReferences)
{
var loops = writer.GetSettings().LoopDetector.Loops;
writer.WriteStartObject();
- if (loops.TryGetValue(typeof(AsyncApiSchema), out List