Skip to content

Commit

Permalink
Update documentation for actuator (#92)
Browse files Browse the repository at this point in the history
* Update documentation for actuator

* docs: update to 1.4.0

* docs: document stomp binding and plugin

* chore: add lint command

* docs: update swagger version

* docs: add headers page

* docs: improve consumer docs

* docs: add springwolf.studio-compatibility configuration

* docs: add presentation link
  • Loading branch information
timonback authored Jun 29, 2024
1 parent f9b2613 commit b5fbbed
Show file tree
Hide file tree
Showing 25 changed files with 232 additions and 114 deletions.
1 change: 1 addition & 0 deletions .github/styles/config/vocabularies/Springwolf/accept.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ Kotlinx
Protobuf
Springfox
Springwolf
STOMP
UI
2 changes: 1 addition & 1 deletion docs/behind-the-scenes.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ From there, Springwolf forwards the message to the protocol specific producer.
## Plugins

`springwolf-core` provides the base functionality to orchestrate the scanning and building of the AsyncAPI document.
The different protocol (AMQP, Cloud-Stream, JMS, Kafka, SNS, SQS) are supported through plugins.
The different protocol (AMQP, Cloud-Stream, JMS, Kafka, SNS, SQS, STOMP/WebSocket) are supported through plugins.
These plugins are found through the Spring dependency injection functionality.
When building own scanner plugins, your plugin will need to implement the `ChannelsScanner` interface.

Expand Down
59 changes: 32 additions & 27 deletions docs/configuration/configuration.mdx

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions docs/configuration/customizing.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ public class AsyncApiTitleCustomizer implements AsyncApiCustomizer {
}
```

## ObjectMapper in `DefaultAsyncApiSerializerService`

The `DefaultAsyncApiSerializerService` is responsible for serializing the AsyncAPI document into a `String` for the Controller.

Use `DefaultAsyncApiSerializerService#getJsonObjectMapper()` and `DefaultAsyncApiSerializerService#getYamlObjectMapper()` to customize the `ObjectMapper`.

## `ChannelScanners` - Channel detection

All `ChannelScanner` beans are called to scan for channels.
Expand Down
12 changes: 10 additions & 2 deletions docs/configuration/documenting-bindings.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ Associate this operation with SQS, see [operation-binding] for details.
@SqsAsyncOperationBinding
```

### `@StompAsyncOperationBinding`

Associate this operation with STOMP (WebSocket), see [operation-binding] for details.

```java
@StompAsyncOperationBinding
```

## Generic annotation

This binding is generic, so that any properties can be specified.
Expand Down Expand Up @@ -123,7 +131,7 @@ The group id that will be used during message consumption

The client id to identify the consumer

### Google PubSub binding annotations
### Google PubSub

#### Channel Binding Object

Expand All @@ -148,7 +156,7 @@ The Channel Bindings Object is used to describe the Google Cloud Pub/Sub Topic d

- A list of IDs of GCP regions where messages that are published to the topic may be persisted in storage

`Schema Settings`:The Schema Settings Object is used to describe the Google Cloud Pub/Sub SchemaSettings.
`Schema Settings`: The Schema Settings Object is used to describe the Google Cloud Pub/Sub SchemaSettings.

- encoding: The encoding of the message
- firstRevisionId: The minimum (inclusive) revision allowed for validating messages
Expand Down
39 changes: 10 additions & 29 deletions docs/configuration/documenting-consumers.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ For these use-cases, Springwolf provides additional ways to explicitly add them

To document consumers, either:

- add the `@AsyncListener` annotation or
- rely on the auto-detection of `@JmsListener`, `@KafkaListener`, `@RabbitListener`, `@SqsListener`
- rely on the auto-detection of `@JmsListener`, `@KafkaListener`, `@MessageMapping`, `@RabbitListener`, `@SendTo`, `@SendToUser`, `@SqsListener`
- and/or use the `@AsyncListener` annotation

You are free to use both options together. Channel and operation, documented via `@AsyncListener` have a higher precedence than auto-detected annotations.

## Auto-detection

The `@JmsListener`, `@KafkaListener`, `@MessageMapping`, `@RabbitListener`, `@SendTo`, `@SendToUser`, `@SqsListener` annotations are detected automatically.
There is nothing more to do.

Use the other option if the provided documentation is insufficient.

## `@AsyncListener`

The `@AsyncListener` annotation is added to the method of the listeners and extracts the payload from its arguments.
Expand All @@ -30,23 +37,7 @@ Below is an example to demonstrate the annotation:
@AsyncListener(operation = @AsyncOperation(
channelName = "example-consumer-topic",
description = "Customer uploaded an example payload", // Optional
servers = {"kafka-server"}, // Optional
headers = @AsyncOperation.Headers( // Optional
schemaName = "SpringKafkaDefaultHeaders",
values = {
@AsyncOperation.Headers.Header(
name = DEFAULT_CLASSID_FIELD_NAME,
description = "Spring Type Id Header",
value = "io.github.springwolf.example.dtos.ExamplePayloadDto"
),
// (demonstrating https://cloudevents.io)
@AsyncOperation.Headers.Header(
name = AsyncHeadersCloudEventConstants.TYPE,
description = AsyncHeadersCloudEventConstants.TYPE_DESC,
value = "NestedPayloadDto.v1")
// ...
}
)
servers = {"kafka-server"} // Optional
))
@KafkaAsyncOperationBinding
public void receiveMessage(ExamplePayloadDto msg) {
Expand All @@ -66,17 +57,7 @@ The channel name (or topic name in case of Kafka) - this is the name that will b

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.

### Header

Optional. The headers describing the metadata of the payload.

### Servers

Optional. Useful when an application is connect to multiple brokers and wants to indicate to which broker the channel belongs to.
The server name needs to exist in [configuration > Servers](configuration.mdx) as well.

## `@JmsListener`, `@KafkaListener`, `@RabbitListener`, `@SqsListener`

The `@JmsListener`, `@KafkaListener`, `@RabbitListener`, `@SqsListener` annotations are detected automatically.
There is nothing more to do.
Use the other option if the provided documentation is insufficient.
131 changes: 131 additions & 0 deletions docs/configuration/documenting-headers.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
---
sidebar_position: 69
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import CodeBlock from '@theme/CodeBlock';
import CodeSchemaGroovy from '!!raw-loader!./snippets/_schema_groovy.gradle';
import CodeSchemaMaven from '!!raw-loader!./snippets/_schema_maven.xml';

# Headers

Springwolf provides different ways to document headers. The `header` is mapped to an AsyncAPI `schemaObject`.

## Auto-detection

Besides the payload, Springwolf detects the Spring `@Header` annotation within the method signature:

```java
@KafkaListener(topics = "example-topic")
public void receiveExamplePayload(
@Payload ExamplePayloadDto payload, // @Payload is required for multiple parameters
@Header(KafkaHeaders.RECEIVED_KEY) String key,
@Header(KafkaHeaders.OFFSET) Integer offset) {
// process
}
```

## Using `@AsyncOperation.Headers`

Again, the annotation property `headers` of `@AsyncOperation` allows to overwrite the headers, as shown below:

```java
@AsyncPublisher(operation = @AsyncOperation(
channelName = "example-producer-topic",
headers = @AsyncOperation.Headers( // Optional
schemaName = "SpringKafkaDefaultHeaders",
values = {
@AsyncOperation.Headers.Header(
name = DEFAULT_CLASSID_FIELD_NAME,
description = "Spring Type Id Header",
value = "io.github.springwolf.example.dtos.ExamplePayloadDto"
),
// demonstrating https://cloudevents.io
@AsyncOperation.Headers.Header(
name = AsyncHeadersCloudEventConstants.TYPE,
description = AsyncHeadersCloudEventConstants.TYPE_DESC,
value = "ExamplePayloadDto.v1")
// ...
}
)
))
public void sendMessage(ExamplePayloadDto msg) {
// process
}
```

## 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](https://github.com/springwolf/springwolf-core/issues/378))

#### Usage

Add the following dependency:

<Tabs>
<TabItem value="Groovy" label="Groovy" default>
<CodeBlock language="groovy">{CodeSchemaGroovy}</CodeBlock>
</TabItem>
<TabItem value="Maven" label="Maven">
<CodeBlock language="xml">{CodeSchemaMaven}</CodeBlock>
</TabItem>
</Tabs>

Then, add the `@Schema` annotation to the payload class:

<!-- vale off -->
```java
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;
}
}
```
<!-- vale on -->

:::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`](https://github.com/springwolf/springwolf-core/blob/master/springwolf-examples/springwolf-amqp-example/src/main/java/io/github/springwolf/examples/amqp/dtos/ExamplePayloadDto.java)

#### 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.

```java
@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](../add-ons) for more information on how to document other formats
:::
4 changes: 2 additions & 2 deletions docs/configuration/documenting-messages.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import CodeSchemaMaven from '!!raw-loader!./snippets/_schema_maven.xml';

# Messages

Springwolf provides different ways to document the messages. The `message` is part of the AsyncAPI `operationObject`
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
Expand All @@ -22,7 +22,7 @@ For example:
@AsyncPublisher(operation = @AsyncOperation(
channelName = "example-producer-topic",
description = "Customer uploaded an example payload", // Optional
payloadType = ExamplePayloadDto.class, // Optional
payloadType = ExamplePayloadDto.class, // Optional. Overwrites the detected payload
message = @AsyncMessage( // Optional
messageId = "my-unique-id",
name = "ExamplePayloadDto",
Expand Down
22 changes: 1 addition & 21 deletions docs/configuration/documenting-producers.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,7 @@ Below is an example to demonstrate the annotation:
@AsyncPublisher(operation = @AsyncOperation(
channelName = "example-producer-topic",
description = "Customer uploaded an example payload", // Optional
servers = {"kafka-server"}, // Optional
headers = @AsyncOperation.Headers( // Optional
schemaName = "SpringKafkaDefaultHeaders",
values = {
@AsyncOperation.Headers.Header(
name = DEFAULT_CLASSID_FIELD_NAME,
description = "Spring Type Id Header",
value = "io.github.springwolf.example.dtos.ExamplePayloadDto"
),
// (demonstrating https://cloudevents.io)
@AsyncOperation.Headers.Header(
name = AsyncHeadersCloudEventConstants.TYPE,
description = AsyncHeadersCloudEventConstants.TYPE_DESC,
value = "NestedPayloadDto.v1")
// ...
}
)
servers = {"kafka-server"} // Optional
))
@KafkaAsyncOperationBinding
public void sendMessage(ExamplePayloadDto msg) {
Expand All @@ -60,10 +44,6 @@ The channel name (or topic name in case of Kafka) - this is the name that will b

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.

### Header

Optional. The headers describing the metadata of the payload.

### Servers

Optional. Useful when an application is connect to multiple brokers and wants to indicate to which broker the channel belongs to.
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration/snippets/_schema_groovy.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
dependencies {
implementation 'io.swagger.core.v3:swagger-core:2.2.20'
implementation 'io.swagger.core.v3:swagger-core:2.2.22'
}
2 changes: 1 addition & 1 deletion docs/configuration/snippets/_schema_maven.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-core</artifactId>
<version>2.2.20</version>
<version>2.2.22</version>
</dependency>
</dependencies>
5 changes: 3 additions & 2 deletions docs/introduction/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Integrate Springwolf in minutes using the [Quickstart](../quickstart.mdx).
API Documentation is an important part of every project and product, but can be painful to maintain manually.
Spring Boot projects have great solutions for auto-generated documentation for REST APIs to overcome this pain (such as Springfox, or springdoc-openapi).

However, until now there were no solutions for asynchronous APIs (such as AMQP, JMS, Kafka, SNS, SQS, etc.). Springwolf aims to solve this and provides auto-generated documentation for asynchronous APIs built in Spring Boot.
However, until now there were no solutions for asynchronous APIs (such as AMQP, JMS, Kafka, SNS, SQS, STOMP/WebSocket, etc.). Springwolf aims to solve this and provides auto-generated documentation for asynchronous APIs built in Spring Boot.

Springwolf is compliant to [AsyncAPI](https://www.asyncapi.com), which brings the [swagger/OpenAPI](https://www.asyncapi.com/docs/tutorials/getting-started/coming-from-openapi) specification you know already from REST APIs into the world of event-driven architectures.

Expand All @@ -27,7 +27,8 @@ Also, the demos of the
[JMS](https://jms.demo.springwolf.dev),
[Kafka](https://kafka.demo.springwolf.dev),
[SNS](https://sns.demo.springwolf.dev),
[SQS](https://sqs.demo.springwolf.dev)
[SQS](https://sqs.demo.springwolf.dev),
[STOMP (WebSocket)](https://stomp.demo.springwolf.dev)
example projects are available.

![Springwolf publishing demo](/img/demo.gif)
Expand Down
Loading

0 comments on commit b5fbbed

Please sign in to comment.