Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
stavshamir committed Dec 2, 2021
1 parent 464aa2e commit 632ea8c
Show file tree
Hide file tree
Showing 23 changed files with 9,767 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.docusaurus
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Website

This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.

### Installation

```
$ yarn
```

### Local Development

```
$ yarn start
```

This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.

### Build

```
$ yarn build
```

This command generates static content into the `build` directory and can be served using any static contents hosting service.

### Deployment

```
$ GIT_USER=<Your GitHub username> USE_SSH=true yarn deploy
```

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
3 changes: 3 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};
84 changes: 84 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
sidebar_position: 3
---

# Configuration

## Configuration Class

- You need to provide a configuration class annotated with:
1. `@Configuration`
2. `@EnableAsyncApi`
- The name of the class does no matter. You don't have to, but it is a good idea to name the class related to Springwolf or AsyncApi.

```java
@Configuration
@EnableAsyncApi
public class AsyncApiConfiguration {
...
}
```

## AsyncApiDocket

You need to provide an `AsyncApiDocket` bean, which provides Springwolf with metadata that is either not specified in code or can't be picked up automatically.

```java
@Bean
public AsyncApiDocket asyncApiDocket() {
return AsyncApiDocket.builder()
.basePackage(...)
.info(...)
.server(...)
.build();
}
```

### basePackage (required)

It is recommended to structue the project such that all consumers (classes containing listener methods) are in the same package - it is not mandatory, and if the consumer are scattered across multiple packages, just provide the highest in hierarchy package that containes all of them.

The base package will be scanned for classes containing `@Component` annotated classes (that includes `@Service` annotated classes) for methods annotated with `@KafkaListener`, `@RabbitListener`, etc.

### Info (required)

The `Info` object provides metadata about the API (see [Info Object][info]).

All provided fields will be present in the generated document, but not all will be displayed in the UI.

### Server

The `Server` object provides metadata the can help the reader understand the protocol, version, login details and more (see [Server Object][server]).

An AsyncAPI document can contain more than one server, but it is not common.

The server is provided to the document with an arbitrary name as the key, and a `Server` object as the value:

```java
@Bean
public AsyncApiDocket asyncApiDocket() {
Server kafkaServer = Server.builder()
.protocol("kafka")
.url(BOOTSTRAP_SERVERS)
.build();

return AsyncApiDocket.builder()
.basePackage(...)
.info(...)
.server("whatever name you want", kafkaServer)
.build();
}
```

As with the `Info` object, all provided fields will be present in the generated document, but not all will be displayed in the UI.

## application.properties

The following table contains the complete list of additional properties that can be specified in the `application.properties` file:

| Property Name | Default Value | Description |
| ------------- | ------------- | ----------- |
| `springwolf.paths.docs` | `/springwolf/docs` | The path of the AsyncAPI document in JSON format. *Note that at the moment the UI will work only with the default value.* |

[info]: https://www.asyncapi.com/docs/specifications/v2.0.0#infoObject).
[server]: https://www.asyncapi.com/docs/specifications/v2.0.0#serverObject
51 changes: 51 additions & 0 deletions docs/documenting-producers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
sidebar_position: 4
---

# Documenting Producers

Unlike consumers which are defined declaratively with an annotation, producers are defined imperatively, and there is no implementation uniform enough so that metadata can be picked up automatically.

Because producers are also an important part of Async APIs, Springwolf provides a way to explicitly add them to the generated document, by declaring them in the `AsyncApiDocket` using the `ProducerData` object.

## ProducerData

Below is an example of describing a Kafka producer:

```java
@Bean
public AsyncApiDocket asyncApiDocket() {

ProducerData exampleProducerData = ProducerData.builder()
.channelName("example-producer-topic")
.binding(ImmutableMap.of("kafka", new KafkaOperationBinding()))
.payloadType(ExamplePayloadDto.class)
.build();

return AsyncApiDocket.builder()
.basePackage(...)
.info(...)
.server(...)
.producer(exampleProducerData)
.build();
}
```

### Channel Name

The channel name (or topic name in case of Kafka).

This is the name that will be used to publish messages to by the UI (if an [example producer is provided](providing-an-example-producer)).


### Binding

This property is used to discriminate the producer's protocl and provide protocol-specific properties (see [Operation Binding Object](https://www.asyncapi.com/docs/specifications/v2.0.0#operationBindingsObject)).

### Payload Type

The class object of the payload published to this channel.

## Example

See a full example [here](https://github.com/springwolf/springwolf-core/blob/master/springwolf-examples/springwolf-kafka-example/src/main/java/io/github/stavshamir/springwolf/example/configuration/AsyncApiConfiguration.java).
5 changes: 5 additions & 0 deletions docs/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
sidebar_position: 1
---

# Introduction
98 changes: 98 additions & 0 deletions docs/providing-an-example-producer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
---
sidebar_position: 5
---

# Providing An Example Producer

Springwolf is great not only for auto-generated documentation from code - it provides additional value by allowing you to publish a payload with one click:

![Publisher UI](/img/publisher-ui.png)

This feature must be enabled by providing an example producer implementation in you Spring Boot application.

## SpringwolfProducer

Provide a bean implementing the `SpringwolfProducer` interface. The name of the bean is important and must match the name of the protocl (i.e. `SpringwolfKafkaProducer`, `SpringwolfAmqpProducer` etc.).

The published payload type is `Map<String, Object>` so that all types from the different channels can be serialized and published.

## Kafka Producer Configuration

There are multiple ways to implement a Kafka producer, this is just an example to help you get started.
You can see the full example [here][kafka-example].

### Producer Configuration

**Make sure to include the `JsonSerializer.ADD_TYPE_INFO_HEADERS, false` configuration property, otherwise Spring adds type headers which mess up the deserialization to the actual payload type in the consumer.**

```java
@Configuration
@EnableKafka
public class KafkaConfiguration {

private final String BOOTSTRAP_SERVERS;

public KafkaConfiguration(@Value("${kafka.bootstrap.servers}") String bootstrapServers) {
this.BOOTSTRAP_SERVERS = bootstrapServers;
}

@Bean
public KafkaTemplate<String, Map<String, Object>> objectKafkaTemplate() {
Map<String, Object> configuration = ImmutableMap.of(
ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS,
ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class,
ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class,
JsonSerializer.ADD_TYPE_INFO_HEADERS, false
);

return new KafkaTemplate<>(new DefaultKafkaProducerFactory<>(configuration));
}

}
```

### SpringwolfKafkaProducer

```java
@Service
public class SpringwolfKafkaProducer implements SpringwolfProducer {

@Autowired
private KafkaTemplate<String, Map<String, Object>> kafkaTemplate;

@Override
public void send(String channelName, Map<String, Object> payload) {
kafkaTemplate.send(channelName, payload);
}

}
```

## AMQP (RabbitMQ) Producer Configuration

There are multiple ways to implement a Rabbit producer, this is just an example to help you get started.
You can see the full example [here][amqp-example].

### Producer Configuration

There is no special configuration required for the producer.

### SpringwolfAmqpProducer

```java
@Service
public class SpringwolfAmqpProducer implements SpringwolfProducer {

@Autowired
private RabbitTemplate rabbitTemplate;

@Override
public void send(String channelName, Map<String, Object> payload) {
rabbitTemplate.convertAndSend(channelName, payload);
}

}
```

[kafka-example]: https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-kafka-example
[amqp-example]:https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-amqp-example
81 changes: 81 additions & 0 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
sidebar_position: 2
---

# Quickstart

** You can see and run a full working example for [kafka][kafka] and [rabbitmq][amqp]. **

Assuming you have a `@KafkaListener` like the following:

```java
@Service
public class ExampleConsumer {

@KafkaListener(topics = "example-topic", containerFactory = "exampleKafkaListenerContainerFactory")
public void receiveExamplePayload(ExamplePayloadDto payload) {
// Do something with payload
}

}
```

Follow the instructions below, and Springwolf will automatically pick up its metadata and generate documentation for it.

## Dependencies

Add the following dependencies:
```groovy
dependencies {
// Provides the documentation API
implementation 'io.github.springwolf:springwolf-core:0.4.0'
// Provides the UI - optional (recommended)
runtimeOnly 'io.github.springwolf:springwolf-ui:0.3.0'
}
```

## Configuration Class

Add the following configuration class.

*Make sure to change the value of `CONSUMERS_BASE_PACKAGE` to the package containing your Kafka listeners.*

```java
@Configuration
@EnableAsyncApi
public class AsyncApiConfiguration {

private final String BOOTSTRAP_SERVERS = "kafka:29092";
private final String CONSUMERS_BASE_PACKAGE = "io.github.stavshamir.springwolf.example.consumers";

@Bean
public AsyncApiDocket asyncApiDocket() {
Info info = Info.builder()
.version("1.0.0")
.title("Springwolf example project")
.build();

Server kafkaServer = Server.builder()
.protocol("kafka")
.url(BOOTSTRAP_SERVERS)
.build();

return AsyncApiDocket.builder()
.basePackage(CONSUMERS_BASE_PACKAGE)
.info(info)
.server("kafka", kafkaServer)
.build();
}

}
```

## View the docs
After starting the application, visit `<host>:<port>/springwolf/asyncapi-ui.html` to view the UI or `<host>:<port>/springwolf/docs` to view the raw AsyncAPI document.

If you configured a different context path in your application, make sure to prepend it to springwolf urls, i.e. `<host>:<port>/<context-path>/springwolf/asyncapi-ui.html`


[kafka]: https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-kafka-example
[amqp]:https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-amqp-example
16 changes: 16 additions & 0 deletions docs/supported-protocols.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
sidebar_position: 6
---

# Supported Protocols

| Protocol | Annotation | Example Project |
| --------------- | ----------------- | --------------- |
| Kafka | `@KafkaListener` | [springwolf-kafka-example][kafka] |
| AMQP (RabbitMQ) | `@RabbitListener` | [springwolf-amqp-example][amqp] |

Please [open an issue](https://github.com/springwolf/springwolf-core/issues/new) if you want a protocol to be supported.


[kafka]: https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-kafka-example
[amqp]:https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-amqp-example
Loading

0 comments on commit 632ea8c

Please sign in to comment.