AsyncAPI tooling update - Week 2

Jonas Lagoni Avatar

Jonas Lagoni

·6 min read

This post bundles updates from the following official tools, but not limited to: bundler, chatbot, studio, diff, glee, create-glee-app, cli, optimizer, modelina, generator, generator-react-sdk, java-template, java-spring-cloud-stream-template, java-spring-template, dotnet-nats-template and ts-nats-template.

You can find the last tooling update here.

In case you want to read what other changes have been happening, you can get the overviews here:

Highlights

These are some of the highlights of changes that have happened in the tools or what's to come! It of course does not cover all the changes but only certain ones.

Glee

Glee now supports WebSocket client adapter which enables you to use Glee for a WebSocket client. You can check out a Crypto Glee example that contains both a server and client implementation.

Since AsyncAPI does not (yet, #689, #874) support when a server is remote vs run locally, it uses the extension x-remoteServers to define when glee should act as a client or server.

An example AsyncAPI document.

1asyncapi: 2.5.0
2info: 
3  title: AsyncAPI coin WebSocket client
4  version: 1.0.0
5  description: |
6    This app creates a client that subscribes to the server for the price change.
7servers:
8  websockets:
9    url: ws://localhost:3000
10    protocol: ws
11x-remoteServers:
12  - websockets
13channels:
14  /price:
15    publish:
16      operationId: index
17      message:
18        payload:
19          type: object
20          properties:
21            status:
22              type: string
23            time:
24              type: number
25            price:
26              type: number

You can then have a Glee function that reacts to the server sending messages:

1export default async function (event) {
2    const payload = event.payload
3    switch (payload.status) {
4        case 'started':
5          ...
6          break
7        case 'intransit':
8          ...
9          break
10        case 'finished':
11          ...
12    }
13    return undefined;
14}

CLI

Currently, the Modelina integration within the CLI is very simple, with almost no customization, but now it supports a range of the TypeScript options where you can change the enum type (union or enum), export types (default or named), class type (interface or class), and which module system to target (ESM or CJS).

1% asyncapi generate models typescript --help
2Generates typed models
3
4USAGE
5  $ asyncapi generate models [LANGUAGE] [FILE] [-h] [-o <value>] [--tsModelType class|interface] [--tsEnumType enum|union] [--tsModuleSystem ESM|CJS] [--tsExportType default|named] [--packageName <value>] [--namespace <value>]
6
7ARGUMENTS
8  LANGUAGE  (typescript|csharp|golang|java|javascript|dart) The language you want the typed models generated for.
9  FILE      Path or URL to the AsyncAPI document, or context-name
10
11FLAGS
12  -h, --help                 Show CLI help.
13  -o, --output=<value>       The output directory where the models should be written to. Omitting this flag will write the models to `stdout`.
14  --namespace=<value>        C# specific, define the namespace to use for the generated models. This is required when language is `csharp`.
15  --packageName=<value>      Go and Java specific, define the package to use for the generated models. This is required when language is `go` or `java`.
16  --tsEnumType=<option>      TypeScript specific, define which type of enums needs to be generated.
17                             <options: enum|union>
18  --tsExportType=<option>    TypeScript specific, define which type of export needs to be generated.
19                             <options: default|named>
20  --tsModelType=<option>     TypeScript specific, define which type of model needs to be generated.
21                             <options: class|interface>
22  --tsModuleSystem=<option>  TypeScript specific, define the module system to be used.
23                             <options: ESM|CJS>

Modelina

The model generators are becoming increasingly more accurate and slowly getting ready for its V1 release. Herein we had to figure out how versioning has to be dealt with going forward.

.... In short, any changes that change the generated outcome are not allowed as it's a breaking change for the consumer of the generated models.

Here is a list of changes we are allowed to do that would not require a breaking change:

  • Adding new features (that do not change existing output), such as generators, presets, input processors, etc.
  • Change existing features, by providing options that default to current behavior. This could be a preset that adapts the output based on options, as long as the API of Modelina and the API of the generated models does not have any breaking changes.
  • Bug fixes where the generated code is otherwise unusable (syntax errors, etc).

Breaking changes are allowed and expected at a frequent rate, of course where it makes sense we will try to bundle multiple changes together.

Read the full description here: https://github.com/asyncapi/modelina/tree/next#versioning-and-maintenance

Rust

Union types now generate more accurate namings as before a union was generated as such:

1// VideoSource represents a union of types: Camera0, PlaybackVideo1
2#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
3pub enum VideoSource {
4  #[serde(rename="Camera0")]
5  Camera0(crate::Camera),
6  #[serde(rename="PlaybackVideo1")]
7  PlaybackVideo1(crate::PlaybackVideo),
8}

Which is now generated as:

1// VideoSource represents a union of types: Camera, PlaybackVideo
2#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
3pub enum VideoSource {
4  #[serde(rename="Camera")]
5  Camera(crate::Camera),
6  #[serde(rename="PlaybackVideo")]
7  PlaybackVideo(crate::PlaybackVideo),
8}

C#

The C# generator now accurately generates optional and required properties.

1{
2  "$schema":"http://json-schema.org/draft-07/schema#",
3  "type":"object",
4  "additionalProperties":false,
5  "properties":{
6    "requiredBoolean":{
7      "type":"boolean"
8    },
9    "notRequiredBoolean":{
10      "type":"boolean"
11    },
12    "requiredString":{
13      "type":"string"
14    },
15    "notRequiredString":{
16      "type":"string"
17    }
18  },
19  "required":[
20    "requiredBoolean",
21    "requiredString"
22  ]
23}
1public class Root
2{
3  private bool requiredBoolean;
4  private bool? notRequiredBoolean;
5  private string requiredString;
6  private string? notRequiredString;
7  public bool RequiredBoolean 
8  {
9    get { return requiredBoolean; }
10    set { requiredBoolean = value; }
11  }
12  public bool? NotRequiredBoolean 
13  {
14    get { return notRequiredBoolean; }
15    set { notRequiredBoolean = value; }
16  }
17  public string RequiredString 
18  {
19    get { return requiredString; }
20    set { requiredString = value; }
21  }
22  public string? NotRequiredString 
23  {
24    get { return notRequiredString; }
25    set { notRequiredString = value; }
26  }
27}

NATS code templates

The NATS TypeScript code template now support the JetStream operations publish, fetch, push, pull and pull subscribe. This means 5 new functions are generated per defined channel (checkout these two examples, simple subscribe and simple publish).

1...
2export class NatsAsyncApiClient {
3  ...
4  // Core subscribe function
5  public subscribeToStreetlightStreetlightIdCommandTurnon(...) {}
6  public publishToStreetlightStreetlightIdCommandTurnon(...) {}
7
8  // New JetStream operations
9  public jetStreamPublishToStreetlightStreetlightIdCommandTurnon(...) {}
10  public jetStreamPullStreetlightStreetlightIdCommandTurnon(...) {}
11  public jetStreamPushSubscribeToStreetlightStreetlightIdCommandTurnon(...) {}
12  public jetStreamPullSubscribeToStreetlightStreetlightIdCommandTurnon(...) {}
13  public jetStreamFetchStreetlightStreetlightIdCommandTurnon(...) {}
14}

For the .NET NATS template, it now supports publishing to JetStream.

1...
2namespace Asyncapi.Nats.Client
3{
4  ...
5  public class NatsClient
6  {
7    // Core publish function
8    public void PublishToStreetlightStreetlightIdEventTurnon() {}
9
10    // New JetStream operation
11    public void JetStreamPublishToStreetlightStreetlightIdEventTurnon() {}
12  }
13}

The NATS .NET template is also one of the first code templates to support multiple serialization libraries, as it currently supports Newtonsoft and system.text.json, it's rather easy to dynamically switch between them (see the template code for system.text.json and Newtonsoft), because of the use of Modelina.

When using the CLI you can choose between the two libraries by using the parameter serializationLibrary.

asyncapi generate fromTemplate asyncapi.yaml @asyncapi/ts-nats-template --param serializationLibrary="newtonsoft | json"

To that end

In the end, thank you to everyone who contributes to AsyncAPI in any way you can 💜 If you also want to help out but don't know where to begin, then join the #11_how-to-contribute channel on Slack so we can help you any way we can 💪

If you have worked on something or are working on something that you would like to be included in these updates, feel free to reach out!

Photo by Roman Kraft on Unsplash