|
| 1 | +--- |
| 2 | +sidebar_position: 5 |
| 3 | +--- |
| 4 | + |
| 5 | +# Documenting Consumers |
| 6 | + |
| 7 | +Springwolf comes with build-in support to auto-detect listeners of supported protocols. |
| 8 | + |
| 9 | +Sometimes projects are configured in a way that makes Springwolf unable to automatically locate consumers or the generated documentation is insufficient. |
| 10 | +For these use-cases, Springwolf provides additional ways to explicitly add them to the generated document. |
| 11 | + |
| 12 | +To document consumers, either: |
| 13 | +- add the `@AsyncListener` annotation or |
| 14 | +- declare the `ConsumerData` object as part of the `AsyncApiDocket` or |
| 15 | +- rely on the auto-detection of `@KafkaListener`, `@RabbitListener` |
| 16 | + |
| 17 | +You are free to use all options together. Per channel and operation, first `ConsumerData` is used, then `@AsyncListener` and last the auto-detected annotations. |
| 18 | + |
| 19 | +## Option 1: `@AsyncListener` |
| 20 | + |
| 21 | +The `@AsyncListener` annotation is added to the method of the listeners and extracts the payload from its arguments. |
| 22 | +Additional fields can be documented. |
| 23 | + |
| 24 | +The protocol operation binding is configured via `@AmqpAsyncOperationBinding` or `@KafkaAsyncOperationBinding`, which has to be on the same method. |
| 25 | + |
| 26 | +Below is an example to demonstrate the annotation: |
| 27 | +```java |
| 28 | +@KafkaListener |
| 29 | +@AsyncListener(operation = @AsyncOperation( |
| 30 | + channelName = "example-consumer-topic", |
| 31 | + description = "Optional. Customer uploaded an example payload", |
| 32 | + headers = @AsyncOperation.Headers( |
| 33 | + schemaName = "SpringKafkaDefaultHeaders", |
| 34 | + values = { |
| 35 | + @AsyncOperation.Headers.Header( |
| 36 | + name = DEFAULT_CLASSID_FIELD_NAME, |
| 37 | + description = "Spring Type Id Header", |
| 38 | + value = "io.github.stavshamir.springwolf.example.dtos.ExamplePayloadDto" |
| 39 | + ), |
| 40 | + } |
| 41 | + ) |
| 42 | +)) |
| 43 | +@KafkaAsyncOperationBinding |
| 44 | +public void receiveMessage(ExamplePayloadDto msg) { |
| 45 | + // process |
| 46 | +} |
| 47 | +``` |
| 48 | + |
| 49 | +:::note |
| 50 | +Springwolf only finds methods that are within the `base-package`. |
| 51 | +::: |
| 52 | + |
| 53 | +### Channel Name |
| 54 | + |
| 55 | +The channel name (or topic name in case of Kafka) - this is the name that will be used to subscribe to messages to by the UI. |
| 56 | + |
| 57 | +### Description |
| 58 | + |
| 59 | +Optional. The description allows for human-friendly text to verbosely explain the _message_, like specific domain, what the topic is used for and which data it contains. |
| 60 | + |
| 61 | +### Payload Type |
| 62 | + |
| 63 | +The class object of the payload that will be consumed from this channel. |
| 64 | +If not specified, it is extracted from the method arguments. |
| 65 | + |
| 66 | +### Header |
| 67 | + |
| 68 | +Optional. The headers describing the metadata of the payload. |
| 69 | + |
| 70 | +### `@AmqpAsyncOperationBinding` |
| 71 | + |
| 72 | +Associate this operation with amqp, see [operation-binding] for details. |
| 73 | + |
| 74 | +```java |
| 75 | +@AmqpAsyncOperationBinding(cc = "example-topic-routing-key") |
| 76 | +``` |
| 77 | + |
| 78 | +### `@KafkaAsyncOperationBinding` |
| 79 | + |
| 80 | +Associate this operation with kafka, see [operation-binding] for details. |
| 81 | + |
| 82 | +```java |
| 83 | +@KafkaAsyncOperationBinding( |
| 84 | + bindingVersion = "1", |
| 85 | + clientId = "foo-clientId", |
| 86 | + groupId = "#{'foo-groupId'}" |
| 87 | +) |
| 88 | +``` |
| 89 | + |
| 90 | + |
| 91 | +## Option 2: `ConsumerData` |
| 92 | + |
| 93 | +:::tip |
| 94 | +Use specific ConsumerData types `AmqpConsumerData` & `KafkaConsumerData` for protocol specific attributes |
| 95 | +::: |
| 96 | + |
| 97 | +Below is an example of describing a Kafka consumer: |
| 98 | + |
| 99 | +```java |
| 100 | +@Bean |
| 101 | +public AsyncApiDocket asyncApiDocket() { |
| 102 | + |
| 103 | + ConsumerData exampleConsumerData = ConsumerData.builder() |
| 104 | + .channelName("example-consumer-topic") |
| 105 | + .description("Optional. Customer uploaded an example payload") |
| 106 | + .operationBinding(ImmutableMap.of("kafka", new KafkaOperationBinding())) |
| 107 | + .payloadType(ExamplePayloadDto.class) |
| 108 | + .headers(AsyncHeaders.NOT_USED) |
| 109 | + .build(); |
| 110 | + |
| 111 | + return AsyncApiDocket.builder() |
| 112 | + .basePackage(...) |
| 113 | + .info(...) |
| 114 | + .server(...) |
| 115 | + .consumer(exampleConsumerData) |
| 116 | + .build(); |
| 117 | +} |
| 118 | +``` |
| 119 | + |
| 120 | +Multiple consumers can be configured by calling the `consumer()` method multiple times. |
| 121 | + |
| 122 | +### Channel Name |
| 123 | + |
| 124 | +The channel name (or topic name in case of Kafka) - this is the name that will be used to subscribe to messages to by the UI. |
| 125 | + |
| 126 | +### Description |
| 127 | + |
| 128 | +Optional. The description allows for human-friendly text to verbosely explain the _message_, like specific domain, what the topic is used for and which data it contains. |
| 129 | + |
| 130 | +### Binding |
| 131 | + |
| 132 | +This property is used to discriminate the producer's protocol and provide protocol-specific properties (see [operation-binding])). |
| 133 | + |
| 134 | +### Payload Type |
| 135 | + |
| 136 | +The class object of the payload that will be consumed from this channel. |
| 137 | + |
| 138 | +### Header |
| 139 | + |
| 140 | +Optional. The headers describing the metadata of the payload. |
| 141 | +By default, `AsyncHeaders.NOT_DOCUMENTED` is used to indicate that no explicit header documentation exists. |
| 142 | +Use `AsyncHeaders` to add your custom headers, use `AsyncHeaders.NOT_USED` if you do not use headers and `AsyncHeadersForCloudEventsBuilder` if your events follow the CloudEvent specification. |
| 143 | + |
| 144 | + |
| 145 | +### `AmqpConsumerData` |
| 146 | + |
| 147 | +The above Kafka `ConsumerData` equivalent in `AmqpConsumerData`: |
| 148 | +```java |
| 149 | + AmqpConsumerData exampleConsumer = AmqpConsumerData.amqpConsumerDataBuilder() |
| 150 | + .queueName("example-consumer-channel") |
| 151 | + .description("example-consumer-channel-description") |
| 152 | + .exchangeName("example-topic-exchange") |
| 153 | + .routingKey("example-topic-routing-key") |
| 154 | + .payloadType(AnotherPayloadDto.class) |
| 155 | + .build(); |
| 156 | +``` |
| 157 | + |
| 158 | +### `KafkaConsumerData` |
| 159 | + |
| 160 | +The above Kafka `ConsumerData` simplifies to the following `KafkaConsumerData`: |
| 161 | +```java |
| 162 | + KafkaConsumerData exampleConsumerData = KafkaConsumerData.kafkaConsumerDataBuilder() |
| 163 | + .topicName("example-consumer-topic") |
| 164 | + .description("Optional. Customer uploaded an example payload") |
| 165 | + .payloadType(ExamplePayloadDto.class) |
| 166 | + .headers(AsyncHeaders.NOT_USED) |
| 167 | + .build(); |
| 168 | +``` |
| 169 | + |
| 170 | + |
| 171 | +## Option 3: `@KafkaListener`, `@RabbitListener` |
| 172 | +The `@KafkaListener` and `@RabbitListener` annotations are detected automatically. |
| 173 | +There is nothing more to do. |
| 174 | +Use the other options if the provided documentation is insufficient. |
| 175 | + |
| 176 | + |
| 177 | +## AMQP Parameters |
| 178 | +### Queue Name (Channel Name) |
| 179 | + |
| 180 | +The queue name that will be used to consume messages from. |
| 181 | + |
| 182 | +### Description |
| 183 | + |
| 184 | +Optional. The description allows for human-friendly text to verbosely explain the _message_, like specific domain, what the topic is used for and which data it contains. |
| 185 | + |
| 186 | +### Exchange Name |
| 187 | + |
| 188 | +The exchange name that will be used to bind queues to. |
| 189 | + |
| 190 | +### Routing Key |
| 191 | + |
| 192 | +The routing key used when publishing a message. |
| 193 | + |
| 194 | +### Payload Type |
| 195 | + |
| 196 | +The class object of the payload that will be consumed from this channel. |
| 197 | + |
| 198 | + |
| 199 | +## Kafka Parameters |
| 200 | + |
| 201 | +### Topic Name (Channel Name) |
| 202 | + |
| 203 | +The topic name that will be used to consume messages from. |
| 204 | + |
| 205 | +### Description |
| 206 | + |
| 207 | +Optional. The description allows for human-friendly text to verbosely explain the _message_, like specific domain, what the topic is used for and which data it contains. |
| 208 | + |
| 209 | +### Payload Type |
| 210 | + |
| 211 | +The class object of the payload that will be consumed from this channel. |
| 212 | + |
| 213 | +### Headers |
| 214 | + |
| 215 | +The Kafka headers describing the metadata of the payload, more details in the generic ConsumerData. |
| 216 | + |
| 217 | +The Springwolf Kafka plugin comes with a special `AsyncHeadersForSpringKafkaBuilder` to document the `__TypeId__` header of the spring-kafka dependency. |
| 218 | + |
| 219 | +## Examples |
| 220 | + |
| 221 | +- [AMQP Example](https://github.com/springwolf/springwolf-core/blob/master/springwolf-examples/springwolf-amqp-example/src/main/java/io/github/stavshamir/springwolf/example/configuration/AsyncApiConfiguration.java) |
| 222 | +- [Cloud Stream Example](https://github.com/springwolf/springwolf-core/blob/master/springwolf-examples/springwolf-cloud-stream-example/src/main/java/io/github/stavshamir/springwolf/example/configuration/AsyncApiConfiguration.java) |
| 223 | +- [Kafka Example](https://github.com/springwolf/springwolf-core/blob/master/springwolf-examples/springwolf-kafka-example/src/main/java/io/github/stavshamir/springwolf/example/configuration/AsyncApiConfiguration.java) |
| 224 | + |
| 225 | +[operation-binding]: https://www.asyncapi.com/docs/reference/specification/v2.0.0#operationBindingsObject |
0 commit comments