apigenie-generatorAsyncAPI clientsclient go (AsyncAPI)

client go (AsyncAPI)

Generate a Go Kafka client package for the Kafka channels of an AsyncAPI document.

Usage

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

Description

apigenie-generator client go generates from OpenAPI documents (see apigenie-generator client go) and from AsyncAPI 3.x documents, telling them apart by their openapi or asyncapi field. Documents referencing other files are bundled first.

From an AsyncAPI document it generates a Go module (Go 1.22) with one package, using the Kafka client franz-go (github.com/twmb/franz-go/pkg/kgo, pinned at v1.18.1 in go.mod, with a complete go.sum). By default the client is the counterpart of the application the document describes: it sends the messages of the application’s receive operations and receives those of its send operations. Set the perspective option to application to generate the described application’s side instead. apigenie-generator server go generates the other side as an application with its own main.go.

The generator writes the Kafka channels: the channels of servers with the kafka or kafka-secure protocol, and channels with a Kafka binding in documents without servers. Channels of other protocols are skipped and reported as info.

With perspective: application, the client of the Carpet Express Event Platform sends the shipment events and requests labels:

client, err := events.NewClient(events.Options{
	Brokers:  []string{"localhost:9092"},
	ClientID: "label-printer",
})
if err != nil {
	log.Fatal(err)
}
defer client.Close()

// a sent operation with several messages takes any of them
err = client.SendPublishShipmentEvents(ctx, delivered)

// request/reply: waits for the reply with the request's correlation id
reply, err := client.RequestLabelRender(ctx, request)
switch reply := reply.(type) {
case events.LabelRendered:
	fmt.Println(reply.Payload.URL)
case events.LabelRenderFailed:
	fmt.Println(reply.Payload.Title)
}

In the default perspective, it receives them and renders the labels:

type labelFarm struct{}

func (labelFarm) OnRenderLabel(ctx context.Context, message events.RenderLabel) (events.RequestLabelRenderReply, error) {
	var reply events.LabelRendered
	reply.Key = message.Key
	reply.Payload = render(message.Payload)
	return reply, nil // sent to TopicLabelRenderResults with the request's correlation id
}

func (labelFarm) OnUnknown(ctx context.Context, record *kgo.Record) error { return nil }

// received operations are consumed in the background until Close
err = client.ListenRequestLabelRender(labelFarm{})
err = client.ListenPublishShipmentEvents(shipmentTracker{})

The package is named by package_name, --package-name or the document’s title (letters and digits, lower case):

File Contents
types.gen.go the types of the schemas the payloads use: structs with json tags, enums, and unions as a struct whose Value holds one variant of a sealed interface; each with a Validate method
messages.gen.go a struct per message (Key, Headers, Payload), its headers struct and header constants, and the code writing, reading and validating records
kafka.gen.go the server hosts, topics, group and client ids, the handler and message interfaces, and Client
runtime_*.gen.go the Kafka runtime (consumers, requesters, headers), validation, Nullable[T], Date
go.mod, go.sum the module, requiring franz-go
  • The client. NewClient(options) creates the producer and, for every operation with a reply, one consumer of its reply topic that all of that operation’s requests share. Close stops the listeners, commits the offsets of the records they handled, leaves their consumer groups and closes the clients. Options takes the brokers, ClientID (replacing the generated ProducerClientID), ClientOptions (franz-go options added to every client, e.g. kgo.SASL, kgo.DialTLSConfig or kgo.AllowAutoTopicCreation), GroupIDs (consumer groups by operation id), OnError and ReplyTimeout.
  • Sent operations. Send<Operation>(ctx, message) validates the message, writes its key, its headers (constant headers such as the ce_specversion and ce_type of CloudEvents included) and its JSON payload to the operation’s topic, and returns once the brokers have it. With several messages, it takes an interface (PublishShipmentEventsMessage) that each of them implements.
  • Request/reply with a reply channel: Request<Operation>(ctx, message) sets a new correlation id unless the message has one, sends the request and waits for the reply with that id until the context is done (Options.ReplyTimeout, 30 seconds by default, when the context has no deadline). It returns the reply message, or an interface of the reply messages (RequestLabelRenderReply) when there are several. An operation named after the verb isn’t repeated: requestLabelRender gives RequestLabelRender.
  • Received operations. Listen<Operation>(handler) consumes the operation’s topic in the background, in its consumer group, until Close. The handler interface (PublishShipmentEventsHandler) has an On<Message> method per message and OnUnknown. Several messages are told apart by a payload property or a header with a distinct constant value (e.g. type or ce_type); otherwise the first message whose payload decodes strictly and is valid is chosen. Records that aren’t valid messages, and handler errors, go to Options.OnError (logged by default). An operation with a reply gets On<Message>(ctx, message) (Reply, error): the reply is sent to the reply topic with the request’s correlation id copied.

Group and client ids come from the Kafka operation bindings as for apigenie-generator server go.

Not supported, and reported as warnings: Avro payloads and payloads of other non-JSON schema formats (the messages are skipped, and operations left without a message too), reply addresses given by runtime expressions, and Kafka topics with parameters. Security (SASL/SCRAM, mTLS) is not configured: pass kgo.SASL or kgo.DialTLSConfig in Options.ClientOptions. Validate doesn’t check not, if/then/else and dependencies, tuples are []any (only their item count is checked), and contentEncoding: base64 strings stay strings.

Extensions

Extension Effect
x-go-name name of the type of a schema, or of the field of a property
x-go-type Go type of a schema, e.g. decimal.Decimal; with x-go-type-import: {path: ...} for its package
x-go-type-skip-optional-pointer the optional property isn’t a pointer
x-go-json-ignore the field gets the tag json:"-"
x-omitempty true or false overrides the omitempty of the field
x-enum-varnames, x-enumNames names of the enum constants
x-enum-descriptions comments of the enum constants
x-apigenie.codegen.name name of the type of a component schema, when it has no x-go-name
x-apigenie.codegen.disabled true on a channel or an operation leaves it out (with the operations of a channel left out)

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

overlay: 1.0.0
info:
  title: Go types
  version: 1.0.0
actions:
  - target: $.components.schemas.Order
    update:
      x-go-name: PurchaseOrder
  - target: $.components.schemas.Order.properties.total
    update:
      x-go-type: decimal.Decimal
      x-go-type-import:
        path: github.com/shopspring/decimal
apigenie-generator client go asyncapi.yaml --overlay go.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_go.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 module is generated.

–package-name

Name of the Go package, as letters and digits in lower case (carpet-express-events gives carpetexpressevents); defaults to the title of the API (events when it gives no valid name). Its kebab case is the base of default group and client ids.

–package-version

Ignored: Go modules have no version in go.mod.

-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 go; its other keys configure the generation from OpenAPI documents.

version: '1.0'
commands:
  generate:
    client_go:
      asyncapi:
        module_path: github.com/acme/events
        package_name: events
        initialisms: [SKU]
        perspective: counterpart
Option Description
module_path Module path of go.mod (default: example.com/<package>)
package_name Name of the package, a lower-case Go identifier (default: from --package-name or the title)
initialisms Words written in capitals in identifiers, besides the common ones (ID, URL, …)
perspective counterpart (default): the counterpart of the described application; application: the described application, sending what it sends