apigenie-generatorAsyncAPI serversserver go (AsyncAPI)
server go (AsyncAPI)
Generate a Go Kafka application of the Kafka channels of an AsyncAPI document.
Usage
apigenie-generator server go [...OPTIONS] INPUT_FILE
Description
apigenie-generator server go generates from OpenAPI documents (see
apigenie-generator server 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) 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 application is the one the
document describes: it sends the messages of send operations and receives the messages of
receive operations. Set the perspective option to counterpart to swap them.
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.
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 }
func main() {
var handlers events.Handlers
handlers.RequestLabelRender = labelFarm{}
// ...
app, err := events.NewApp(events.Options{Brokers: []string{"localhost:9092"}}, handlers)
if err != nil {
log.Fatal(err)
}
defer app.Close()
err = app.PublishShipmentEvents().SendShipmentDelivered(ctx, delivered)
// ...
_ = app.Run(ctx) // consumes until ctx is done
}
The module has one package at its root, 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 (switch v := event.Value.(type)); 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 interfaces, Handlers, App and the senders |
runtime_*.gen.go |
the Kafka runtime (consumers, requesters, headers), validation, Nullable[T], Date |
go.mod, go.sum |
the module, requiring franz-go |
cmd/<package>/main.go |
a starting point, written once and never overwritten |
- Messages. Headers are the message’s headers merged with those of its traits as AsyncAPI
does (JSON Merge Patch: a later
requiredlist replaces an earlier one). Header values are text on Kafka, so headers arestring,int64,float64orboolfields, pointers when optional. Headers with a constant value, e.g. thece_specversionandce_typeof CloudEvents in binary mode, are constants (ShipmentStatusChangedCeType), which senders write and receivers compare. Messages are validated as a whole before they are sent and after they are received. - Received operations.
NewApp(options, handlers)takes a handler per received operation (PublishShipmentEventsHandler) with anOn<Message>method per message andOnUnknown. Several messages of a channel are told apart by a payload property or a header with a distinct constant value (e.g.typeorce_type); otherwise the first message whose payload decodes strictly and is valid is chosen. Records matching no message go toOnUnknown.Runconsumes the operations’ topics, each in its consumer group, until its context is done;Closecommits the offsets of the handled records and leaves the groups. Records that aren’t valid messages, and handler errors, go toOptions.OnError(logged by default); the records are committed either way. - Sent operations.
app.PublishShipmentEvents()returns the sender of the operation, with aSend<Message>(ctx, message)method per message that validates the message and returns once the brokers have it. - Request/reply with a reply channel: the requester’s
Send<Message>returns the reply (one of the reply messages,RequestLabelRenderReply), matched by the request’s correlation id, which is generated when the request has none;Options.ReplyTimeoutbounds the wait when the context has no deadline. A replier’s handler returns the reply, which is sent to the reply topic with the request’s correlation id copied (to a header, or a payload property, as thecorrelationIdlocations say).
Group and client ids come from the groupId and clientId schemas of the Kafka operation
bindings, of the operation or its traits: their const, single enum value, default or first
example. Without a value, <package name in kebab case>-<operation> is used, and the doc comment
of the constant names the binding’s pattern. Options.GroupIDs replaces them by operation id.
The partitions, replicas and topic configuration of channel bindings are documented on the topic
constants; the generated code creates no topics.
main.go connects to the brokers of the -brokers flag, else of the KAFKA_BROKERS environment
variable (comma-separated), else localhost:9092, wires stub handlers that return
api.ErrNotImplemented, and stops cleanly on SIGINT or SIGTERM.
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. With a correlation
id in the payload at a place that isn’t a string field of the payload’s structs, the request must
carry the id and the replier’s handler must copy it.
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 server 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.server_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 server go;
its other keys configure the generation from OpenAPI documents.
version: '1.0'
commands:
generate:
server_go:
asyncapi:
module_path: github.com/acme/events
package_name: events
initialisms: [SKU]
main: true
perspective: application
| 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, …) |
main |
true (default): write cmd/<package>/main.go when it doesn’t exist |
perspective |
application (default): the described application; counterpart: its counterpart, receiving what it sends |