Messages
Springwolf provides different ways to document the messages. The message
is part of the AsyncAPI operationObject
and mapped as messageObject
.
A definition of the message that will be published or received by this operation
A message can be defined as part of the @AsyncOperation
annotation, using message = @AsyncMessage()
field.
For example:
@AsyncPublisher(operation = @AsyncOperation(
channelName = "example-producer-topic",
description = "Customer uploaded an example payload", // Optional
payloadType = ExamplePayloadDto.class, // Optional. Overwrites the detected payload
message = @AsyncMessage( // Optional
messageId = "my-unique-id",
name = "ExamplePayloadDto",
contentType = "application/vnd.aai.asyncapi+json;version=3.0.0",
description = "Example payload model for sending messages"
)
))
public void sendMessage(ExamplePayloadDto msg) {
// process
}
Payload Type
Springwolf tries to auto-detect the payload type based on the method signature.
When the method has multiple arguments, the payload can be indicated via @Payload
, that's
public void sendMessage(@Payload ExamplePayloadDto msg, String traceId, Object loggingContext) {}
Alternatively, the annotation property payloadType
of @AsyncOperation
allows to overwrite the detected class.
Unwrapping the Payload
Sometimes, the payload type is wrapped in other objects.
Some wrappers are automatically unwrapped, including Message<String>
, which becomes String
.
The configuration property to modify the defaults is currently in beta.
Assuming a method signature of sendMessage(ConsumerRecord<String, MyEvent> msg)
, where the actual payload is located in parameter index 1 (String).
Adding the configuration property springwolf.payload.extractable-classes.org.apache.kafka.clients.consumer.ConsumerRecord=1
tells Springwolf how to handle this payload type.
The configuration property is split into three parts:
- The base configuration property
springwolf.payload.extractable-classes
. - The canonical class name,
org.apache.kafka.clients.consumer.ConsumerRecord
in this case. - The parameter index (
1
) of the actual payload class (MyEvent
).
Schema
Under the hood Springwolf relies on swagger-core ModelConverters
to define the message schema.
By default, the type and example values for the properties are guessed.
The default Jackson ModelResolver
supports schema definitions via @Schema
to overwrite the property definitions.
Using @Schema
The @Schema
annotation allows to set many properties like description
, example
, requiredMode
, minimum
to document payloads.
All properties are part of the produced AsyncAPI file. However, not all are displayed in springwolf-ui
(see #378)
Usage
Add the following dependency:
- Groovy
- Maven
dependencies {
implementation 'io.swagger.core.v3:swagger-core-jakarta:2.2.22'
}
<dependencies>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-core-jakarta</artifactId>
<version>2.2.22</version>
</dependency>
</dependencies>
Then, add the @Schema
annotation to the payload class:
import io.swagger.v3.oas.annotations.media.Schema;
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;
@Schema(description = "Example payload model")
public class ExamplePayloadDto {
@Schema(description = "Some string field", example = "some string value", requiredMode = REQUIRED)
private String someString;
public String getSomeString() {
return someString;
}
}
The @AsyncMessage.description
field will always override the @Schema
description if provided
For a full example, take a look at ExamplePayloadDto.java in springwolf-amqp-example
Primitive, final and external classes
When the @Schema
annotation can't be attached to the payload class (that's java.lang.String
), the payload can be wrapped in an envelope class. The actual payload is a field within this class (StringEnvelope
), marked using @AsyncApiPayload
and documented using the @Schema
annotation.
@AsyncListener( operation = @AsyncOperation( channelName = TOPIC,
payloadType = StringEnvelope.class) // <- envelope class
)
public void receiveStringPayload(String stringPayload) { // <- The original class is used here
// ...
}
@Data
static class StringEnvelope {
@AsyncApiPayload // <- The annotation marker
@Schema(description = "Payload description using @Schema annotation and @AsyncApiPayload within envelope class")
private final String payload;
}
See Add-Ons for more information on how to document other formats