Skip to main content

Messages

Springwolf provides different ways to document the messages. The message is part of the AsyncAPI operationObject

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
message = @AsyncMessage( // Optional
messageId = "my-unique-id",
name = "ExamplePayloadDto",
schemaFormat = "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.

note

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:

  1. The base configuration property springwolf.payload.extractable-classes.
  2. The canonical class name, org.apache.kafka.clients.consumer.ConsumerRecord in this case.
  3. 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:

dependencies {
implementation 'io.swagger.core.v3:swagger-core:2.2.20'
}

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;
}
}
note

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;
}
info

See Add-Ons for more information on how to document other formats