Skip to content

Commit

Permalink
LEGO DTO (#221)
Browse files Browse the repository at this point in the history
Migrate to asyncapi schema data structrures from LEGO library
  • Loading branch information
yurvon-screamo authored Aug 12, 2024
1 parent 1bd5ed1 commit 0461df2
Show file tree
Hide file tree
Showing 121 changed files with 2,698 additions and 4,049 deletions.
30 changes: 30 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
!**/.gitignore
!.git/HEAD
!.git/config
!.git/packed-refs
!.git/refs/heads/**
2 changes: 1 addition & 1 deletion .github/dotnet/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ runs:
id: setup-dotnet
uses: actions/setup-dotnet@v1
with:
dotnet-version: "6.0.x"
dotnet-version: "6.0.423"
10 changes: 8 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: setup dotnet
uses: ./.github/dotnet

# bug of format tool in dotnet 6
- name: setup
id: setup-dotnet
uses: actions/setup-dotnet@v1
with:
dotnet-version: "8.0.300"

- name: dotnet format check
run: dotnet format --verify-no-changes *.sln
env:
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)

<!-- Please update the links section at the bottom when adding a new version. -->

## [v0.14.0] - ?
### Changed
- [Change AsyncApi data structure to LEGO AsyncAPI.NET](https://github.com/m-wild/saunter/issues/188)
- [Replace NJsonSchema with own implementation](https://github.com/m-wild/saunter/issues/188)
- [Allow usages of the annotation attributes on interfaces](https://github.com/m-wild/saunter/issues/213)
- Bump ws from 7.5.3 to 7.5.10 in /src/Saunter.UI

## [v0.13.0] - 2024-01-16
### Changed
Expand Down
125 changes: 70 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Saunter

![CI](https://github.com/tehmantra/saunter/workflows/CI/badge.svg)
![CI](https://github.com/asyncapi/saunter/workflows/CI/badge.svg)
[![NuGet Badge](https://buildstats.info/nuget/saunter?includePreReleases=true)](https://www.nuget.org/packages/Saunter/)

Saunter is an [AsyncAPI](https://github.com/asyncapi/asyncapi) documentation generator for dotnet.
Expand All @@ -11,7 +11,7 @@ Saunter is an [AsyncAPI](https://github.com/asyncapi/asyncapi) documentation gen

## Getting Started

See [examples/StreetlightsAPI](https://github.com/tehmantra/saunter/blob/main/examples/StreetlightsAPI).
See [examples/StreetlightsAPI](https://github.com/asyncapi/saunter/tree/main/examples/StreetlightsAPI).


1. Install the Saunter package
Expand All @@ -27,25 +27,30 @@ See [examples/StreetlightsAPI](https://github.com/tehmantra/saunter/blob/main/ex
services.AddAsyncApiSchemaGeneration(options =>
{
// Specify example type(s) from assemblies to scan.
options.AssemblyMarkerTypes = new[] {typeof(StreetlightMessageBus)};

options.AssemblyMarkerTypes = new[] { typeof(StreetlightMessageBus) };
// Build as much (or as little) of the AsyncApi document as you like.
// Saunter will generate Channels, Operations, Messages, etc, but you
// may want to specify Info here.
options.Middleware.UiTitle = "Streetlights API";
options.AsyncApi = new AsyncApiDocument
{
Info = new Info("Streetlights API", "1.0.0")
Info = new AsyncApiInfo()
{
Description = "The Smartylighting Streetlights API allows you\nto remotely manage the city lights.",
License = new License("Apache 2.0")
Title = "Streetlights API",
Version = "1.0.0",
Description = "The Smartylighting Streetlights API allows you to remotely manage the city lights.",
License = new AsyncApiLicense()
{
Url = "https://www.apache.org/licenses/LICENSE-2.0"
Name = "Apache 2.0",
Url = new("https://www.apache.org/licenses/LICENSE-2.0"),
}
},
Servers =
{
{ "mosquitto", new Server("test.mosquitto.org", "mqtt") }
}
["mosquitto"] = new AsyncApiServer(){ Url = "test.mosquitto.org", Protocol = "mqtt"},
["webapi"] = new AsyncApiServer(){ Url = "localhost:5000", Protocol = "http"},
},
};
});
```
Expand All @@ -56,9 +61,11 @@ See [examples/StreetlightsAPI](https://github.com/tehmantra/saunter/blob/main/ex
[AsyncApi] // Tells Saunter to scan this class.
public class StreetlightMessageBus : IStreetlightMessageBus
{
[Channel("publish/light/measured")] // Creates a Channel
[PublishOperation(typeof(LightMeasuredEvent), Summary = "Inform about environmental lighting conditions for a particular streetlight.")] // A simple Publish operation.
public void PublishLightMeasuredEvent(Streetlight streetlight, int lumens) {}
private const string SubscribeLightMeasuredTopic = "subscribe/light/measured";

[Channel(SubscribeLightMeasuredTopic, Servers = new[] { "mosquitto" })]
[SubscribeOperation(typeof(LightMeasuredEvent), "Light", Summary = "Subscribe to environmental lighting conditions for a particular streetlight.")]
public void PublishLightMeasuredEvent(Streetlight streetlight, int lumens) {}
```

4. Add saunter middleware to host the AsyncApi json document. In the `Configure` method of `Startup.cs`:
Expand All @@ -68,6 +75,8 @@ See [examples/StreetlightsAPI](https://github.com/tehmantra/saunter/blob/main/ex
{
endpoints.MapAsyncApiDocuments();
endpoints.MapAsyncApiUi();

endpoints.MapControllers();
});
```

Expand Down Expand Up @@ -96,11 +105,11 @@ See [examples/StreetlightsAPI](https://github.com/tehmantra/saunter/blob/main/ex

6. Use the published AsyncAPI UI:

![AsyncAPI UI](https://raw.githubusercontent.com/tehmantra/saunter/main/assets/asyncapi-ui-screenshot.png)
![AsyncAPI UI](https://raw.githubusercontent.com/asyncapi/saunter/main/assets/asyncapi-ui-screenshot.png)
## Configuration

See [the options source code](https://github.com/tehmantra/saunter/blob/main/src/Saunter/AsyncApiOptions.cs) for detailed info.
See [the options source code](https://github.com/asyncapi/saunter/blob/main/src/Saunter/AsyncApiOptions.cs) for detailed info.
Common options are below:

Expand All @@ -109,7 +118,7 @@ services.AddAsyncApiSchemaGeneration(options =>
{
options.AssemblyMarkerTypes = new[] { typeof(Startup) }; // Tell Saunter where to scan for your classes.
options.AddChannelItemFilter<MyChannelItemFilter>(); // Dynamically update ChanelItems
options.AddChannelFilter<MyAsyncApiChannelFilter>(); // Dynamically update ChanelItems
options.AddOperationFilter<MyOperationFilter>(); // Dynamically update Operations
options.Middleware.Route = "/asyncapi/asyncapi.json"; // AsyncAPI JSON document URL
Expand All @@ -118,30 +127,6 @@ services.AddAsyncApiSchemaGeneration(options =>
}
```


## JSON Schema Settings

The JSON schema generation can be customized using the `options.JsonSchemaGeneratorSettings`. Saunter defaults to the popular `camelCase` naming strategy for both properties and types.

For example, setting to use PascalCase:

```c#
services.AddAsyncApiSchemaGeneration(options =>
{
options.JsonSchemaGeneratorSettings.TypeNameGenerator = new DefaultTypeNameGenerator();

// Note: need to assign a new JsonSerializerSettings, not just set the properties within it.
options.JsonSchemaGeneratorSettings.SerializerSettings = new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver(),
Formatting = Formatting.Indented;
};
}
```

You have access to the full range of both [NJsonSchema](https://github.com/RicoSuter/NJsonSchema) and [JSON.NET](https://github.com/JamesNK/Newtonsoft.Json) settings to configure the JSON schema generation, including custom ContractResolvers.

## Bindings

Bindings are used to describe protocol specific information. These can be added to the AsyncAPI document and then applied to different components by setting the `BindingsRef` property in the relevant attributes `[OperationAttribute]`, `[MessageAttribute]`, `[ChannelAttribute]`
Expand All @@ -155,17 +140,31 @@ services.AddAsyncApiSchemaGeneration(options =>
{
Components =
{
ChannelBindings =
ChannelBindings =
{
["my-amqp-binding"] = new ChannelBindings
["amqpDev"] = new()
{
Amqp = new AmqpChannelBinding
new AMQPChannelBinding
{
Is = AmqpChannelBindingIs.RoutingKey,
Exchange = new AmqpChannelBindingExchange
Is = ChannelType.Queue,
Exchange = new()
{
Name = "example-exchange",
VirtualHost = "/development"
Vhost = "/development"
}
}
}
},
OperationBindings =
{
{
"postBind",
new()
{
new HttpOperationBinding
{
Method = "POST",
Type = HttpOperationType.Response,
}
}
}
Expand All @@ -176,16 +175,16 @@ services.AddAsyncApiSchemaGeneration(options =>
```

```csharp
[Channel("light.measured", BindingsRef = "my-amqp-binding")] // Set the BindingsRef property
[Channel("light.measured", BindingsRef = "amqpDev")] // Set the BindingsRef property
public void PublishLightMeasuredEvent(Streetlight streetlight, int lumens) {}
```

Available bindings:
* [AMQP](https://github.com/tehmantra/saunter/tree/main/src/Saunter/AsyncApiSchema/v2/Bindings/Amqp)
* [HTTP](https://github.com/tehmantra/saunter/tree/main/src/Saunter/AsyncApiSchema/v2/Bindings/Http)
* [Kafka](https://github.com/tehmantra/saunter/tree/main/src/Saunter/AsyncApiSchema/v2/Bindings/Kafka)
* [MQTT](https://github.com/tehmantra/saunter/tree/main/src/Saunter/AsyncApiSchema/v2/Bindings/Mqtt)
```csharp
[PublishOperation(typeof(LightMeasuredEvent), "Light", BindingsRef = "postBind")]
public void MeasureLight([FromBody] LightMeasuredEvent lightMeasuredEvent)
```

Available bindings: https://www.nuget.org/packages/AsyncAPI.NET.Bindings/
## Multiple AsyncAPI documents

Expand Down Expand Up @@ -250,10 +249,27 @@ Each document can be accessed by specifying the name in the URL
}
```

## Migration to LEGO AsyncApi.Net

When switching to the LEGO AsyncApi.Net, we broke the public API.

To simplify the transition to new versions of the library, this note was created.

What was broken:

* Namespaces have changed:
* Saunter.AsyncApiSchema.v2 -> LEGO.AsyncAPI.Models
* Saunter.Attributes; -> Saunter.AttributeProvider.Attributes
* Change the name of the data structures, add prefix 'AsyncApi' (example 'class Info' -> 'class AsyncApiInfo')
* All data structure constructors are now with the parameterless constructor

There was no more significant changes on public API.

Keep this in mind when planning the migration process.

## Contributing

See our [contributing guide](https://github.com/tehmantra/saunter/blob/main/CONTRIBUTING.md/CONTRIBUTING.md).
See our [contributing guide](https://github.com/asyncapi/saunter/blob/main/CONTRIBUTING.md/CONTRIBUTING.md).
Feel free to get involved in the project by opening issues, or submitting pull requests.

Expand All @@ -262,5 +278,4 @@ You can also find me on the [AsyncAPI community slack](https://asyncapi.com/slac
## Thanks

* This project is heavily inspired by [Swashbuckle](https://github.com/domaindrivendev/Swashbuckle.AspNetCore).
* We use [NJsonSchema](https://github.com/RicoSuter/NJsonSchema) for the JSON schema heavy lifting,
* We use [LEGO AsyncAPI.NET](https://github.com/LEGO/AsyncAPI.NET) schema and serializing.
17 changes: 8 additions & 9 deletions examples/StreetlightsAPI/API.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Saunter.Attributes;
using Saunter.AttributeProvider.Attributes;

namespace StreetlightsAPI
{
Expand Down Expand Up @@ -51,10 +51,9 @@ public class StreetlightsController
{
private const string PublishLightMeasuredTopic = "publish/light/measured";


// Simulate a database of streetlights
private static int StreetlightSeq = 2;
private static readonly List<Streetlight> StreetlightDatabase = new List<Streetlight>
private static int s_streetlightSeq = 2;
private static readonly List<Streetlight> s_streetlightDatabase = new()
{
new Streetlight { Id = 1, Position = new [] { -36.320320, 175.485986 }, LightIntensity = new() },
};
Expand All @@ -73,7 +72,7 @@ public StreetlightsController(IStreetlightMessageBus streetlightMessageBus, ILog
/// </summary>
[HttpGet]
[Route("api/streetlights")]
public IEnumerable<Streetlight> Get() => StreetlightDatabase;
public IEnumerable<Streetlight> Get() => s_streetlightDatabase;

/// <summary>
/// Add a new streetlight
Expand All @@ -82,16 +81,16 @@ public StreetlightsController(IStreetlightMessageBus streetlightMessageBus, ILog
[Route("api/streetlights")]
public Streetlight Add([FromBody] Streetlight streetlight)
{
streetlight.Id = StreetlightSeq++;
StreetlightDatabase.Add(streetlight);
streetlight.Id = s_streetlightSeq++;
s_streetlightDatabase.Add(streetlight);
return streetlight;
}

/// <summary>
/// Inform about environmental lighting conditions for a particular streetlight.
/// </summary>
[Channel(PublishLightMeasuredTopic, Servers = new[] { "webapi" })]
[PublishOperation(typeof(LightMeasuredEvent), "Light")]
[PublishOperation(typeof(LightMeasuredEvent), "Light", BindingsRef = "postBind")]
[HttpPost]
[Route(PublishLightMeasuredTopic)]
public void MeasureLight([FromBody] LightMeasuredEvent lightMeasuredEvent)
Expand All @@ -102,7 +101,7 @@ public void MeasureLight([FromBody] LightMeasuredEvent lightMeasuredEvent)

_logger.LogInformation("Received message on {Topic} with payload {Payload} ", PublishLightMeasuredTopic, payload);

var streetlight = StreetlightDatabase.SingleOrDefault(s => s.Id == lightMeasuredEvent.Id);
var streetlight = s_streetlightDatabase.SingleOrDefault(s => s.Id == lightMeasuredEvent.Id);
if (streetlight != null)
{
streetlight.LightIntensity.Add(new(lightMeasuredEvent.SentAt, lightMeasuredEvent.Lumens));
Expand Down
4 changes: 2 additions & 2 deletions examples/StreetlightsAPI/Messaging.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Text.Json;
using Microsoft.Extensions.Logging;
using Saunter.Attributes;
using Saunter.AttributeProvider.Attributes;

namespace StreetlightsAPI
{
Expand All @@ -21,7 +21,7 @@ public StreetlightMessageBus(ILoggerFactory logger)
_logger = logger.CreateLogger("Streetlight");
}

[Channel(SubscribeLightMeasuredTopic, Servers = new[] { "mosquitto" })]
[Channel(SubscribeLightMeasuredTopic, Servers = new[] { "mosquitto" }, BindingsRef = "amqpDev")]
[SubscribeOperation(typeof(LightMeasuredEvent), "Light", Summary = "Subscribe to environmental lighting conditions for a particular streetlight.")]
public void PublishLightMeasurement(LightMeasuredEvent lightMeasuredEvent)
{
Expand Down
Loading

0 comments on commit 0461df2

Please sign in to comment.