Skip to content

Commit

Permalink
feat: Added AsyncAPI internal implementation (#501)
Browse files Browse the repository at this point in the history
  • Loading branch information
ctasada authored Dec 20, 2023
1 parent ca5f841 commit cd88d02
Show file tree
Hide file tree
Showing 202 changed files with 8,217 additions and 0 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/springwolf-asyncapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: springwolf-asyncapi

on:
push:
branches:
- master
pull_request:
types: [ opened, synchronize, ready_for_review ]

jobs:
build:

runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- uses: actions/checkout@v4

- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'

- name: Setup Gradle
uses: gradle/gradle-build-action@v2

- name: Check formatting (before running tests)
run: ./gradlew -p springwolf-asyncapi spotlessCheck

- name: Build with Gradle
run: ./gradlew -p springwolf-asyncapi build

- name: Publish package
if: github.ref == 'refs/heads/master'
run: ./gradlew -p springwolf-asyncapi publish
env:
ORG_GRADLE_PROJECT_SNAPSHOT: true

ORG_GRADLE_PROJECT_SIGNINGKEY: ${{secrets.ORG_GRADLE_PROJECT_SIGNINGKEY}}
ORG_GRADLE_PROJECT_SIGNINGPASSWORD: ${{secrets.ORG_GRADLE_PROJECT_SIGNINGPASSWORD}}

ORG_GRADLE_PROJECT_MAVEN_URL: https://s01.oss.sonatype.org/content/repositories/snapshots/
ORG_GRADLE_PROJECT_MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }}
ORG_GRADLE_PROJECT_MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
![Last Version](https://img.shields.io/github/tag-pre/springwolf/springwolf-core.svg)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![springwolf-core](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-core.yml/badge.svg)](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-core.yml)
[![springwolf-asyncapi](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-asyncapi.yml/badge.svg)](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-asyncapi.yml)
[![springwolf-ui](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-ui.yml/badge.svg)](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-ui.yml)
[![springwolf-plugins](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-plugins.yml/badge.svg)](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-plugins.yml)
[![springwolf-addons](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-addons.yml/badge.svg)](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-addons.yml)
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ allprojects {
}

var publishingEnabled = (
project.name == 'springwolf-asyncapi' ||
project.name == 'springwolf-core' ||
project.name == 'springwolf-amqp' ||
project.name == 'springwolf-cloud-stream' ||
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
rootProject.name = 'springwolf-core'

include(
'springwolf-asyncapi',
'springwolf-core',
'springwolf-plugins:springwolf-amqp-plugin',
'springwolf-plugins:springwolf-cloud-stream-plugin',
Expand Down
55 changes: 55 additions & 0 deletions springwolf-asyncapi/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Springwolf AsyncAPI

### About

This library provides a JVM-friendly binding to generate [AsyncAPI document](https://www.asyncapi.com/).
It allows to create specifications for AsyncAPI from code.

Even when this library can be use by any JVM system, it's main goal is to support Springwolf-Core.

### Usage

Add the following dependencies and configuration class to enable this plugin.

#### Dependencies

```groovy
dependencies {
// Provides the JVM Builders to create an AsyncAPI Spec file
implementation 'io.github.springwolf:springwolf-asyncapi:<springwolf-version>'
}
```

### Configuration class

```java
AsyncAPI asyncAPI = AsyncAPI.builder()
.info(Info.builder()
.title("Account Service")
.version("1.0.0")
.description("This service is in charge of processing user signups")
.build())
.channels(Map.of(
"userSignedup",
Channel.builder().build()))
.operations(Map.of(
"sendUserSignedup",
Operation.builder().build()))
.build();
```

## TODO:

* Test that the serialization is always respecting the case format
* Test that the validations are applied
* Finish to implement and test all the Bindings
* Deserialize a JSON/YAML file to Java instances

## PENDING:

* [[Docs Bug 🐞 report]: JMS Server Binding Example doesn't match AsyncAPI v3 specification #232](https://github.com/asyncapi/bindings/issues/232) - In Review
* [Confusing Operation Object Example #1007](https://github.com/asyncapi/spec/issues/1007) - In Review

## Convert AsyncAPI v2.x to v3.0.0

See https://github.com/asyncapi/converter-js#conversion-2xx-to-3xx
54 changes: 54 additions & 0 deletions springwolf-asyncapi/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
plugins {
id 'java'
id 'java-library'

id 'org.springframework.boot'
id 'io.spring.dependency-management'
id 'ca.cutterslade.analyze'
}

dependencies {
implementation "io.swagger.core.v3:swagger-core-jakarta:${swaggerVersion}"
implementation "jakarta.validation:jakarta.validation-api"

implementation "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}"
implementation "com.fasterxml.jackson.core:jackson-annotations:${jacksonVersion}"
implementation "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${jacksonVersion}"

compileOnly "org.projectlombok:lombok:${lombokVersion}"

annotationProcessor "org.projectlombok:lombok:${lombokVersion}"

testAnnotationProcessor "org.projectlombok:lombok:${lombokVersion}"

testImplementation "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}"
testImplementation "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
testImplementation "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:${jacksonVersion}"
testImplementation "io.swagger.core.v3:swagger-core-jakarta:${swaggerVersion}"
testImplementation "net.javacrumbs.json-unit:json-unit-assertj:3.2.2"

testRuntimeOnly "org.junit.jupiter:junit-jupiter:${junitJupiterVersion}"
}

jar {
enabled = true
archiveClassifier = ''
}
bootJar.enabled = false

java {
withJavadocJar()
withSourcesJar()
}

publishing {
publications {
mavenJava(MavenPublication) {
pom {
name = 'springwolf-asyncapi'
description = 'AsyncAPI schema generator'
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.core.v3.bindings;

import io.github.stavshamir.springwolf.asyncapi.core.v3.model.ExtendableObject;
import lombok.EqualsAndHashCode;

/**
* Describes AsyncAPI channel binding.
*/
@EqualsAndHashCode(callSuper = true)
public abstract class ChannelBinding extends ExtendableObject {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.core.v3.bindings;

import io.github.stavshamir.springwolf.asyncapi.core.v3.model.ExtendableObject;
import lombok.EqualsAndHashCode;

@EqualsAndHashCode(callSuper = true)
public class MessageBinding extends ExtendableObject {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.core.v3.bindings;

import io.github.stavshamir.springwolf.asyncapi.core.v3.model.ExtendableObject;
import lombok.EqualsAndHashCode;

/**
* Describes AsyncAPI operation binding.
*/
@EqualsAndHashCode(callSuper = true)
public abstract class OperationBinding extends ExtendableObject {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.core.v3.bindings;

import io.github.stavshamir.springwolf.asyncapi.core.v3.model.ExtendableObject;
import lombok.EqualsAndHashCode;

/**
* Describes AsyncAPI operation binding.
*/
@EqualsAndHashCode(callSuper = true)
public abstract class ServerBinding extends ExtendableObject {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.core.v3.bindings.amqp;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.github.stavshamir.springwolf.asyncapi.core.v3.bindings.ChannelBinding;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
* Protocol-specific information for an AMQP 0-9-1 channel.
*
* @see <a href="https://github.com/asyncapi/bindings/blob/master/amqp/README.md#channel">AMQP Channel</a>
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class AMQPChannelBinding extends ChannelBinding {
@Builder.Default
@NotNull
@JsonProperty(value = "is", required = true, defaultValue = "routingKey")
private AMQPChannelType is = AMQPChannelType.ROUTING_KEY;

@JsonProperty("exchange")
private AMQPChannelExchangeProperties exchange;

/**
* When is=queue, this object defines the queue properties.
*/
@JsonProperty("queue")
private AMQPChannelQueueProperties queue;

@Builder.Default
@JsonProperty(value = "bindingVersion")
private final String bindingVersion = "0.3.0";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.core.v3.bindings.amqp;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class AMQPChannelExchangeProperties {
/**
* The name of the exchange. It MUST NOT exceed 255 characters long.
*/
@Size(max = 255, message = "Exchange name must not exceed 255 characters long.")
@JsonProperty("name")
private String name;

/**
* The type of the exchange. Can be either topic, direct, fanout, default or headers.
*/
@JsonProperty("type")
private AMQPChannelExchangeType type;

/**
* Whether the exchange should survive broker restarts or not.
*/
@JsonProperty("durable")
private Boolean durable;

/**
* Whether the exchange should be deleted when the last queue is unbound from it.
*/
@JsonProperty("autoDelete")
private Boolean autoDelete;

/**
* The virtual host of the exchange. Defaults to /.
*/
@Builder.Default
@JsonProperty(value = "vhost", defaultValue = "/")
private String vhost = "/";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.core.v3.bindings.amqp;

import com.fasterxml.jackson.annotation.JsonProperty;

public enum AMQPChannelExchangeType {
@JsonProperty("topic")
TOPIC,

@JsonProperty("direct")
DIRECT,

@JsonProperty("fanout")
FANOUT,

@JsonProperty("default")
DEFAULT,

@JsonProperty("headers")
HEADERS
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.core.v3.bindings.amqp;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class AMQPChannelQueueProperties {

/**
* The name of the queue. It MUST NOT exceed 255 characters long.
*/
@Size(max = 255, message = "Queue name must not exceed 255 characters long.")
@JsonProperty("name")
private String name;

/**
* Whether the queue should survive broker restarts or not.
*/
@JsonProperty("durable")
private Boolean durable;

/**
* Whether the queue should be used only by one connection or not.
*/
@JsonProperty("exclusive")
private Boolean exclusive;

/**
* Whether the queue should be deleted when the last consumer unsubscribes.
*/
@JsonProperty("autoDelete")
private Boolean autoDelete;

/**
* The virtual host of the queue. Defaults to /.
*/
@Builder.Default
@JsonProperty(value = "vhost", defaultValue = "/")
private String vhost = "/";
}
Loading

0 comments on commit cd88d02

Please sign in to comment.