# client elixir (AsyncAPI)

Source: https://apigenie.pl/techdocs/reference/cli-generate_client_elixir_asyncapi

Generate an Elixir client library of the WebSocket and Kafka channels of an AsyncAPI document.

## Usage

```sh
apigenie-generator client elixir [...OPTIONS] INPUT_FILE
```

## Description

`apigenie-generator client elixir` generates from OpenAPI documents (see
[apigenie-generator client elixir](/techdocs/reference/cli-generate_client_elixir)) 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 Mix library (Elixir 1.18 or later) for the counterpart of
the described application, by default: it sends what the application receives and receives what
it sends. The WebSocket channels connect on [Mint.WebSocket](https://hexdocs.pm/mint_web_socket),
the Kafka channels use [brod](https://github.com/kafka4beam/brod). Set the `perspective` option
to `application` for the application's Kafka library; WebSocket channels are connected to in the
counterpart perspective only. With Kafka channels the library depends on
[snappyer](https://hex.pm/packages/snappyer), a NIF that needs a C++ compiler, so that brod reads
Snappy-compressed records (the default of Go's franz-go producers); LZ4 and zstd records need
`lz4b` or `ezstd` added by hand.

The generator writes the channels of servers with the `ws`/`wss` and `kafka`/`kafka-secure`
protocols, and channels with a WebSocket or Kafka binding in documents without servers. Channels
of other protocols (MQTT, AMQP, HTTP) are skipped and reported as info.

```elixir
{:ok, socket} =
  CarpetExpressEvents.WebSocket.TrackingStream.connect("shp_8Yb2Kk1qP0wZcT3uXr5sN7aD",
    url: "ws://localhost:4000",
    query: [include: ["location"]],
    handler: MyApp.Tracking
  )

{:ok, pickup} = CarpetExpressEvents.WebSocket.PickupDesk.connect(url: "ws://localhost:4000")
{:ok, %CarpetExpressEvents.Models.PickupConfirmed{}} =
  CarpetExpressEvents.WebSocket.PickupDesk.request_pickup(pickup, request)
```

The library, for the application `carpet_express_events` (named by `app`, `--package-name` or
the document's title):

| File | Contents |
| ---- | -------- |
| `lib/<app>/models/*.ex` | the structs of the schemas the payloads use, decoded and encoded by `decode.ex` and `encode.ex` |
| `lib/<app>/websocket/<channel>.ex` | per WebSocket channel, `connect`, and the functions sending its messages and requests |
| `lib/<app>/websocket/<channel>/handler.ex` | the behaviour of the callbacks receiving what the server sends on the channel |
| `lib/<app>/messages/*.ex` | per Kafka message, a struct (`payload`, `headers`, `key`) and the description of its record |
| `lib/<app>/kafka.ex` | the supervisor of the Kafka processes and a function per topic |
| `lib/<app>/kafka/<operation>.ex` | per sent operation, its send (and request) functions; per received operation, the behaviour of its callbacks |
| `lib/<app>/kafka/<operation>_consumer.ex` | per received operation, its group subscriber |
| `lib/<app>/runtime/` | the WebSocket connection (`ChannelSocket`) and the Kafka runtime |
| `mix.exs`, `.formatter.exs`, `.credo.exs` | the project, a library without application |

- **WebSocket channels, two framings.** `connect` (with the channel's address parameters first)
  opens a connection to the server of `:url` and joins the channel. With `framing: :phoenix`, the
  default, it speaks the Phoenix Channels protocol (version 2) of `apigenie-generator server
  phoenix`: the socket `/socket/websocket`, the channel's topic (the address segments joined by
  `:`), the ws binding's `:query` as the join payload, a heartbeat every 30 seconds. With
  `framing: :plain` it connects to the channel's address, the query in the URL, and the frames
  are the messages of the AsyncAPI document. `send_<message>` sends a message; for a request,
  the function named after it waits for the reply: the reply to its ref in the Phoenix framing,
  the next message with the request's correlation id in the plain one. What the server sends is
  decoded into its struct and given to the callbacks of the channel's `Handler` behaviour (the
  `:handler` option), or sent to the calling process.
- **Kafka.** Add `{CarpetExpressEvents.Kafka, brokers: "localhost:9092"}` to your supervision
  tree. `send_<message>/1` checks a message's headers and returns once the brokers have the
  record; `request_<message>/2` waits for the reply on the reply topic, matched by the
  correlation id (generated when the request has none; `:timeout`, 30 seconds by default). Each
  received operation has a consumer group (`:brod_group_subscriber_v2`) handing its records to
  the behaviour's callbacks, as `server phoenix` does: told apart by a constant header
  (`ce_type`), payload property or decoding, with the optional `on_unknown/1` and `on_invalid/2`;
  a replier's callback returns its reply, sent with the request's correlation id.

Group ids come from the `groupId` schemas of the Kafka operation bindings, else `<app in kebab
case>-<operation>`, and the consumer modules' configuration overrides them (`config :app,
<Consumer>, group_id: ..., offset_reset_policy: :earliest`).

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, and
WebSocket addresses whose segments mix text and parameters. Security is not generated: the
WebSocket connections carry no credentials (the streamToken is a query parameter) and the Kafka
clients connect without SASL or TLS. The client ids of the Kafka bindings are not applied, the
headers schema of the ws binding is not sent, and the models decode by type.

## Options

### -c, --config

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

### -o, --output

Directory where the library is generated.

### --package-name

Name of the Mix application, converted to snake_case (`carpet-express-events` gives
`carpet_express_events`, root module `CarpetExpressEvents`); defaults to the title of the API.
Its kebab case is the base of default group ids.

### --package-version

Version of the Mix project; 0.1.0 by default.

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

```yaml
version: '1.0'
commands:
  generate:
    client_elixir:
      asyncapi:
        app: carpet_express_events
        perspective: counterpart
```

| Option | Type | Default | Description |
| ------ | ---- | ------- | ----------- |
| `app` | string | from `--package-name` or the title | Name of the Mix application (snake_case) |
| `namespace` | string | the application in PascalCase | Root module of the generated modules |
| `perspective` | `counterpart` \| `application` | `counterpart` | The counterpart of the described application, or the application itself |
| `field_prefix` | string | `field_` | Prefix of struct fields whose property names give no valid name |
| `module_overrides` | map | | Module names of payload schemas, e.g. `ShipmentId: ShipmentRef` |
| `dev_deps` | boolean | `true` | credo and dialyxir as dependencies of the dev and test environments |
