Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: separate bindings and allow for custom bindings. #107

Merged
merged 17 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/release-internal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
paths:
- 'src/LEGO.AsyncAPI/**'
- 'src/LEGO.AsyncAPI.Readers/**'
- 'src/LEGO.AsyncAPI.Writers/**'
- 'src/LEGO.AsyncAPI.Bindings/**'
- ".github/workflows/release-package.yml"
- '!**/*.md'
workflow_dispatch:
Expand All @@ -16,7 +16,7 @@ jobs:
name: Publish NuGet packages
strategy:
matrix:
package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers"]
package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers", "LEGO.AsyncAPI.Bindings"]
steps:
- name: Checkout repository
uses: actions/checkout@v1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
environment: AsyncAPI
strategy:
matrix:
package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers" ]
package-name: [ "LEGO.AsyncAPI", "LEGO.AsyncAPI.Readers", "LEGO.AsyncAPI.Bindings" ]
steps:
- name: Checkout repository
uses: actions/checkout@v1
Expand Down
6 changes: 6 additions & 0 deletions AsyncAPI.sln
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.editorconfig = .editorconfig
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LEGO.AsyncAPI.Bindings", "src\LEGO.AsyncAPI.Bindings\LEGO.AsyncAPI.Bindings.csproj", "{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -32,6 +34,10 @@ Global
{7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7D9C6FBA-4B6F-48A0-B3F5-E7357021F8F9}.Release|Any CPU.Build.0 = Release|Any CPU
{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{33CA31F4-ECFE-4227-BFE9-F49783DD29A0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
30 changes: 30 additions & 0 deletions src/LEGO.AsyncAPI.Bindings/Binding.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) The LEGO Group. All rights reserved.

namespace LEGO.AsyncAPI.Bindings
{
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.Interface;
using LEGO.AsyncAPI.Readers.ParseNodes;

public abstract class Binding<T> : AsyncApiBinding, IBindingParser<T>
where T : IBinding, new()
{
protected static void ParseMap<T>(
MapNode mapNode,
T domainObject,
FixedFieldMap<T> fixedFieldMap)
{
if (mapNode == null)
{
return;
}

foreach (var propertyNode in mapNode)
{
propertyNode.ParseField(domainObject, fixedFieldMap, null);
}
}

public abstract T LoadBinding(PropertyNode node);
}
}
71 changes: 71 additions & 0 deletions src/LEGO.AsyncAPI.Bindings/BindingsCollection.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) The LEGO Group. All rights reserved.

namespace LEGO.AsyncAPI.Bindings
{
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Bindings.Http;
using LEGO.AsyncAPI.Bindings.Kafka;
using LEGO.AsyncAPI.Bindings.Pulsar;
using LEGO.AsyncAPI.Bindings.WebSockets;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers.Interface;

public static class BindingsCollection
{
public static TCollection Add<TCollection, TItem>(
this TCollection destination,
IEnumerable<TItem> source)
where TCollection : ICollection<TItem>
{
ArgumentNullException.ThrowIfNull(destination);
ArgumentNullException.ThrowIfNull(source);

if (destination is List<TItem> list)
{
list.AddRange(source);
return destination;
}

foreach (var item in source)
{
destination.Add(item);
}

return destination;
}

public static IEnumerable<IBindingParser<IBinding>> All => new List<IBindingParser<IBinding>>
{
Pulsar,
Kafka,
Http,
};

public static IEnumerable<IBindingParser<IBinding>> Http => new List<IBindingParser<IBinding>>
{
new HttpOperationBinding(),
new HttpMessageBinding()
};

public static IEnumerable<IBindingParser<IBinding>> Websockets => new List<IBindingParser<IBinding>>
{
new WebSocketsChannelBinding(),
};

public static IEnumerable<IBindingParser<IBinding>> Kafka => new List<IBindingParser<IBinding>>
{
new KafkaServerBinding(),
new KafkaChannelBinding(),
new KafkaOperationBinding(),
new KafkaMessageBinding(),
};

public static IEnumerable<IBindingParser<IBinding>> Pulsar => new List<IBindingParser<IBinding>>
{
// Pulsar
new PulsarServerBinding(),
new PulsarChannelBinding(),
};
}
}
16 changes: 16 additions & 0 deletions src/LEGO.AsyncAPI.Bindings/ChannelBinding{T}.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) The LEGO Group. All rights reserved.

namespace LEGO.AsyncAPI.Bindings
{
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Readers;
using LEGO.AsyncAPI.Readers.ParseNodes;

public abstract class ChannelBinding<T> : Binding<T>, IChannelBinding
where T : IChannelBinding, new()
{
protected abstract FixedFieldMap<T> FixedFieldMap { get; }

public override T LoadBinding(PropertyNode node) => BindingDeserializer.LoadBinding("ChannelBinding", node.Value, this.FixedFieldMap);
}
}
51 changes: 51 additions & 0 deletions src/LEGO.AsyncAPI.Bindings/Http/HttpMessageBinding.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) The LEGO Group. All rights reserved.

using LEGO.AsyncAPI.Models;

namespace LEGO.AsyncAPI.Bindings.Http
{
using System;
using LEGO.AsyncAPI.Readers;
using LEGO.AsyncAPI.Readers.ParseNodes;
using LEGO.AsyncAPI.Writers;

/// <summary>
/// Binding class for http messaging channels.
/// </summary>
public class HttpMessageBinding : MessageBinding<HttpMessageBinding>
{

/// <summary>
/// A Schema object containing the definitions for HTTP-specific headers. This schema MUST be of type object and have a properties key.
/// </summary>
public AsyncApiSchema Headers { get; set; }

/// <summary>
/// Serialize to AsyncAPI V2 document without using reference.
/// </summary>
public override void SerializeProperties(IAsyncApiWriter writer)
{
if (writer is null)
{
throw new ArgumentNullException(nameof(writer));
}

writer.WriteStartObject();

writer.WriteOptionalObject(AsyncApiConstants.Headers, this.Headers, (w, h) => h.SerializeV2(w));
writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion);
writer.WriteExtensions(this.Extensions);

writer.WriteEndObject();
}

public override string BindingKey => "http";

protected override FixedFieldMap<HttpMessageBinding> FixedFieldMap => new()
{
{ "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } },
{ "headers", (a, n) => { a.Headers = JsonSchemaDeserializer.LoadSchema(n); } },
};

}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
// Copyright (c) The LEGO Group. All rights reserved.

namespace LEGO.AsyncAPI.Models.Bindings.Http
using LEGO.AsyncAPI.Models;

namespace LEGO.AsyncAPI.Bindings.Http
{
using System;
using System.Collections.Generic;
using LEGO.AsyncAPI.Models.Interfaces;
using LEGO.AsyncAPI.Attributes;
using LEGO.AsyncAPI.Readers;
using LEGO.AsyncAPI.Readers.ParseNodes;
using LEGO.AsyncAPI.Writers;

/// <summary>
/// Binding class for http operations.
/// </summary>
public class HttpOperationBinding : IOperationBinding
public class HttpOperationBinding : OperationBinding<HttpOperationBinding>
{
public enum HttpOperationType
{
[Display("request")]
Request,

[Display("response")]
Response,
}
/// <summary>
/// REQUIRED. Type of operation. Its value MUST be either request or response.
/// </summary>
public string Type { get; set; }
public HttpOperationType? Type { get; set; }

/// <summary>
/// When type is request, this is the HTTP method, otherwise it MUST be ignored. Its value MUST be one of GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, CONNECT, and TRACE.
Expand All @@ -27,15 +38,10 @@ public class HttpOperationBinding : IOperationBinding
/// </summary>
public AsyncApiSchema Query { get; set; }

/// <summary>
/// The version of this binding. If omitted, "latest" MUST be assumed.
/// </summary>
public string BindingVersion { get; set; }

/// <summary>
/// Serialize to AsyncAPI V2 document without using reference.
/// </summary>
public void SerializeV2WithoutReference(IAsyncApiWriter writer)
public override void SerializeProperties(IAsyncApiWriter writer)
{
if (writer is null)
{
Expand All @@ -44,37 +50,22 @@ public void SerializeV2WithoutReference(IAsyncApiWriter writer)

writer.WriteStartObject();

writer.WriteRequiredProperty(AsyncApiConstants.Type, this.Type);
writer.WriteRequiredProperty(AsyncApiConstants.Type, this.Type.GetDisplayName());
writer.WriteOptionalProperty(AsyncApiConstants.Method, this.Method);
writer.WriteOptionalObject(AsyncApiConstants.Query, this.Query, (w, h) => h.SerializeV2(w));
writer.WriteOptionalProperty(AsyncApiConstants.BindingVersion, this.BindingVersion);

writer.WriteExtensions(this.Extensions);
writer.WriteEndObject();
}

public void SerializeV2(IAsyncApiWriter writer)
protected override FixedFieldMap<HttpOperationBinding> FixedFieldMap => new()
{
if (writer is null)
{
throw new ArgumentNullException(nameof(writer));
}

if (this.Reference != null && !writer.GetSettings().ShouldInlineReference(this.Reference))
{
this.Reference.SerializeV2(writer);
return;
}

this.SerializeV2WithoutReference(writer);
}

/// <inheritdoc/>
public IDictionary<string, IAsyncApiExtension> Extensions { get; set; } = new Dictionary<string, IAsyncApiExtension>();

public bool UnresolvedReference { get; set; }

public AsyncApiReference Reference { get; set; }
{ "bindingVersion", (a, n) => { a.BindingVersion = n.GetScalarValue(); } },
{ "type", (a, n) => { a.Type = n.GetScalarValue().GetEnumFromDisplayName<HttpOperationType>(); } },
{ "method", (a, n) => { a.Method = n.GetScalarValue(); } },
{ "query", (a, n) => { a.Query = JsonSchemaDeserializer.LoadSchema(n); } },
};

BindingType IBinding.Type => BindingType.Http;
public override string BindingKey => "http";
}
}
Loading