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.ConsumerRecordin 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.
Only properties that are part of AsyncAPI spec are part of the produced AsyncAPI file,
while the @Schema annotation contains some additional properties.
Contribute in #378, so that all properties are supported in springwolf-ui.
Usage
Add the following dependency:
- Groovy
- Maven
dependencies {
implementation 'io.swagger.core.v3:swagger-core-jakarta:2.2.32'
}
<dependencies>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-core-jakarta</artifactId>
<version>2.2.32</version>
</dependency>
</dependencies>
Then, add the @Schema annotation to the payload class:
- Java
- Kotlin
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;
}
}
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED
@Schema(description = "Example payload model")
public data class ExamplePayloadDto(
// The `@field:` annotation use-site target is important
@field:Schema(description = "Some string field", example = "some string value", requiredMode = REQUIRED)
public val someString: String,
)
// Note: Kotlin inline value classes use mangling: https://kotlinlang.org/docs/inline-classes.html#mangling
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
Arrays using @ArraySchema
When the payload is an array, use @ArraySchema annotation instead.
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