apigenie-generatorAsyncAPI clientsclient csharp (AsyncAPI)

client csharp (AsyncAPI)

Generate a C# Kafka client library for the Kafka channels of an AsyncAPI document.

Usage

apigenie-generator client csharp [...OPTIONS] INPUT_FILE

Description

apigenie-generator client csharp generates from OpenAPI documents (see apigenie-generator client csharp) and from AsyncAPI 3.x documents. It tells them apart by their openapi or asyncapi field. Documents that reference other files are bundled first.

From an AsyncAPI document it generates a .NET 8 class library (Microsoft.NET.Sdk) on the Kafka client Confluent.Kafka (Apache-2.0, pinned at 2.15.1). It also references Microsoft.Extensions.Hosting.Abstractions and Microsoft.Extensions.Options.ConfigurationExtensions.

By default the client implements the counterpart of the application the document describes: it sends the messages of the application’s receive operations, and receives the messages of its send operations. Set the perspective option to application to generate the application’s side instead. The two sides talk to each other, and to the application of apigenie-generator server aspnet (AsyncAPI).

The generator writes the Kafka channels:

  • the channels of servers with the kafka or kafka-secure protocol;
  • in documents without servers, the channels with a Kafka binding.

Channels of other protocols are skipped and reported as info.

// the application's side of the Carpet Express Event Platform
builder.Services.AddCarpetExpressKafkaClient(builder.Configuration);

public sealed class Shipping(CarpetExpressKafkaClient kafka)
{
    public async Task DeliveredAsync(ShipmentDelivered message, CancellationToken cancellationToken)
    {
        await kafka.ShipmentEvents.SendShipmentDeliveredAsync(message, cancellationToken);
    }

    public async Task<Uri?> LabelAsync(RenderLabel request, CancellationToken cancellationToken)
    {
        // sends the request with a new correlation id and waits for its reply
        var reply = await kafka.LabelRenderRequests.RequestRenderLabelAsync(request, TimeSpan.FromSeconds(10), cancellationToken);
        return reply is LabelRendered rendered ? rendered.Payload.Url : null;
    }
}

The project’s root namespace (and assembly name) is root_namespace, else --package-name, else the document’s title in PascalCase followed by .Client. The class names start with the namespace’s stem: its last segment, or the segment before a last Client, Clients or Sdk. For example, CarpetExpress.Client gives CarpetExpressKafkaClient.

File Contents
Models/*.cs, Serialization/*.cs, Messages/*.cs the payload types, the JSON context and the message records, as apigenie-generator server aspnet (AsyncAPI) writes them
Kafka/<Name>KafkaClient.cs the client: a producer per channel it sends to
Kafka/<Channel>Producer.cs the Send…Async and Request…Async methods of a channel
Kafka/*Handler.cs, Kafka/*Consumer.cs the handler interfaces and consumers of the received operations
Kafka/<Name>KafkaClientOptions.cs the options, with the topics and ids
Kafka/<Name>KafkaClientServiceCollectionExtensions.cs, Kafka/<Name>KafkaClientBuilder.cs Add<Name>KafkaClient and the registration of handlers
Infrastructure/*.cs the Kafka runtime (producer, consumer service, reply consumer, headers) and the validation of messages
<RootNamespace>.csproj, README.md the project
  • Sent operations. The client has a producer per channel, e.g. client.ShipmentEvents. A producer’s Send<Message>Async validates a message and completes once the brokers have it. It writes the constant headers (e.g. ce_type), the key and the topic from the bindings.

  • Request/reply. A request/reply operation gets Request<Message>Async(message, cancellationToken) and an overload with a timeout.

    • It sends the request with its correlation id, or with a new Guid when the request has none.
    • It returns the reply: the reply message, or an interface of the replies (IRequestLabelRenderReply) when there are several.
    • It throws TimeoutException when no reply comes within the timeout (by default ReplyTimeout), and OperationCanceledException when the token is cancelled.

    One consumer per client reads the replies of every request. It has no consumer group, and starts with the first request reading from a minute before, so no reply is missed.

  • Received operations. They have handler interfaces and consumers, as in apigenie-generator server aspnet (AsyncAPI):

    • the dispatch by a payload property or a header, with OnUnknownAsync for other records;
    • validation;
    • commits once the handler returns, and retries after RetryDelay when it throws;
    • replies sent with the request’s correlation id.

    Register a handler with the builder: its consumer runs as a hosted service of the host.

// the counterpart's side: consume the shipment events and answer label requests
builder.Services.AddCarpetExpressKafkaClient(builder.Configuration)
    .AddPublishShipmentEventsHandler<ShipmentEventsHandler>()
    .AddRequestLabelRenderHandler<LabelRenderer>();

Add<Name>KafkaClient(configuration, configure) does the following:

  • binds the options to the AsyncApi:Kafka section of configuration when it is given, then applies configure;
  • registers the client and its producers as singletons;
  • returns the builder.

Without dependency injection, new CarpetExpressKafkaClient(options) sends and requests; dispose it when done.

Settings

The options are those of apigenie-generator server aspnet (AsyncAPI): BootstrapServers, ClientId, ReplyTimeout, RetryDelay, Topics, Operations (the group and client ids of received operations), Consumer and Producer. A client that sends requests also has ReplyClientId, the client id of its reply consumer. It defaults to the client id of the first request/reply operation’s Kafka binding. Without a value in the binding, the default is <namespace stem in kebab case>-<operation>.

Not supported, and reported as warnings:

  • Avro payloads, and payloads of other non-JSON schema formats;
  • reply addresses given by runtime expressions;
  • Kafka topics with parameters.

Security (SASL/SCRAM, mTLS) is not configured: set the librdkafka properties in Consumer and Producer.

Extensions

Extension Effect
x-apigenie.codegen.disabled true on a channel or an operation leaves it out (with the operations of a channel left out)
x-enum-varnames, x-enum-names names of the members of an enum, in PascalCase
x-enum-descriptions, x-enumDescriptions documentation comments of the members of an enum

The extensions can live in an overlay for this target instead of in the document, e.g. csharp.overlay.yaml:

overlay: 1.0.0
info:
  title: C# names
  version: 1.0.0
actions:
  - target: $.components.schemas.OrderStatus
    update:
      x-enum-varnames: [Created, Paid, Shipped]
  - target: $.operations.publishAudit
    update:
      x-apigenie:
        codegen:
          disabled: true
apigenie-generator client csharp asyncapi.yaml --overlay csharp.overlay.yaml -o out

Options

-c, –config

Path to the configuration file to use. See the “Configuration” section for more information.

–overlay OVERLAY

Path to an OpenAPI Overlay (1.0 to 1.2) applied to the input before generating, e.g. one setting the extensions of the “Extensions” section. Can be repeated: the overlays apply in order, after those listed under commands.generate.client_csharp.overlay in the configuration file (with paths relative to it). Warnings and errors about a value an overlay set point to the overlay. The overlays apply to the AsyncAPI document bundled with the files it references.

–strict

Makes an overlay action whose target selects nothing an error (exit status 2, nothing is generated) instead of a warning (OVERLAY:LINE:COL: action #N (TARGET) selects no nodes.).

-o, –output

Directory where the project is generated.

–package-name

Root namespace of the generated code, e.g. Acme.Events.Client. Defaults to the title of the API in PascalCase, followed by .Client.

–package-version

Version of the project. Defaults to the version of the API when it is a .NET version, else 1.0.0.

-p, –property

Applies to OpenAPI documents only. Using it with an AsyncAPI document is an error.

Configuration

The options are read from the asyncapi section of the configuration of apigenie-generator client csharp. Its other keys configure the generation from OpenAPI documents.

version: '1.0'
commands:
  generate:
    client_csharp:
      asyncapi:
        root_namespace: Acme.Events.Client
        validation: true
        perspective: counterpart
Option Description
root_namespace Root namespace and assembly name of the project (default: --package-name, or the title followed by .Client)
validation true (default): validate messages by their DataAnnotations before sending and after receiving them
perspective counterpart (default): the counterpart of the described application; application: the application itself