Skip to content

Commit

Permalink
feat(core): add AsyncGenericBinding (#370)
Browse files Browse the repository at this point in the history
* feat(core): add AsyncGenericBinding

* feat(core): refine AsyncGenericOperationBinding

* feat(core): improve example

* feat(core): add BindingProcessorPriority

* feat(core): handle multiple bindings with same type

Removed AsyncGenericOperationBinding

* refactor(core): move binding processors to binding package

Leave public annotations in current package to avoid breaking changes

* feat(generic-binding): Move `@AsyncGenericOperationBinding` to own addon

* feat(generic-binding): Update README.md

* chore(generic-binding): fixup

* chore(generic-binding): fixup

* refactor(generic-binding): Use spring configuration

* feat(generic-binding): Improve sqs example (overwrite existing binding)

* chore(generic-binding): move to package addons

* chore(generic-binding): spotless

* chore(generic-binding): spotless

* chore(generic-binding): move to addons package

* chore(generic-binding): move to addons package

* chore(generic-binding): move to generic_binding package

* chore(generic-binding): add to sns plugin

* chore(common-model-converters): fix auto configuration path

* chore(generic-binding): fix log formatting

* chore(gh): Run full matrix, do not abort early

* test(sqs): update asyncapi.json (temporary output)

Co-authored-by: sam0r040 <[email protected]>
  • Loading branch information
timonback and sam0r040 authored Oct 13, 2023
1 parent 359599f commit 182b13c
Show file tree
Hide file tree
Showing 51 changed files with 608 additions and 74 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: springwolf-common-model-converters
name: springwolf-addons

on:
push:
Expand All @@ -7,14 +7,19 @@ on:
pull_request:
types: [ opened, synchronize, ready_for_review ]

env:
addon: springwolf-add-ons/springwolf-common-model-converters

jobs:
build:

runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
addon: [ "common-model-converters", "generic-binding" ]

env:
addon: springwolf-add-ons/springwolf-${{ matrix.addon }}

steps:
- uses: actions/checkout@v4

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/springwolf-plugins.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ jobs:
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
plugin: [ "amqp", "cloud-stream", "kafka", "sns", "sqs" ]

Expand Down
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
**Automated documentation for event-driven applications built with Spring Boot**

[![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/workflows/springwolf-core/badge.svg)
![springwolf-amqp](https://github.com/springwolf/springwolf-core/workflows/springwolf-amqp/badge.svg)
![springwolf-cloud-stream](https://github.com/springwolf/springwolf-core/workflows/springwolf-cloud-stream/badge.svg)
![springwolf-kafka](https://github.com/springwolf/springwolf-core/workflows/springwolf-kafka/badge.svg)
![springwolf-sns](https://github.com/springwolf/springwolf-core/workflows/springwolf-sns/badge.svg)
![springwolf-sqs](https://github.com/springwolf/springwolf-core/workflows/springwolf-sqs/badge.svg)
![springwolf-common-model-converters](https://github.com/springwolf/springwolf-core/actions/workflows/springwolf-common-model-converters.yml/badge.svg)

[![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-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)
> We are on discord for any question, discussion, request etc.
> Join us at https://discord.gg/HZYqd5RPTd
Expand Down Expand Up @@ -66,6 +62,7 @@ More details in the documentation.
| [Cloud Stream](https://github.com/springwolf/springwolf-core/tree/master/springwolf-plugins/springwolf-cloud-stream-plugin) | [Cloud Stream Example](https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-cloud-stream-example) | ![Maven Central](https://img.shields.io/maven-central/v/io.github.springwolf/springwolf-cloud-stream?color=green&label=springwolf-cloud-stream&style=plastic) | ![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/io.github.springwolf/springwolf-cloud-stream?label=springwolf-cloud-stream&server=https%3A%2F%2Fs01.oss.sonatype.org&style=plastic) |
| [Kafka](https://github.com/springwolf/springwolf-core/tree/master/springwolf-plugins/springwolf-kafka-plugin) | [Kafka Example](https://github.com/springwolf/springwolf-core/tree/master/springwolf-examples/springwolf-kafka-example) | ![Maven Central](https://img.shields.io/maven-central/v/io.github.springwolf/springwolf-kafka?color=green&label=springwolf-kafka&style=plastic) | ![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/io.github.springwolf/springwolf-kafka?label=springwolf-kafka&server=https%3A%2F%2Fs01.oss.sonatype.org&style=plastic) |
| [Common Model Converter](https://github.com/springwolf/springwolf-core/tree/master/springwolf-add-ons/springwolf-common-model-converters) | | ![Maven Central](https://img.shields.io/maven-central/v/io.github.springwolf/springwolf-common-model-converters?color=green&label=springwolf-common-model-converters&style=plastic) | ![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/io.github.springwolf/springwolf-common-model-converters?label=springwolf-common-model-converters&server=https%3A%2F%2Fs01.oss.sonatype.org&style=plastic) |
| [Generic Binding](https://github.com/springwolf/springwolf-core/tree/master/springwolf-add-ons/springwolf-generic-binding) | | ![Maven Central](https://img.shields.io/maven-central/v/io.github.springwolf/springwolf-generic-binding?color=green&label=springwolf-generic-binding&style=plastic) | ![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/io.github.springwolf/springwolf-generic-binding?label=springwolf-generic-binding&server=https%3A%2F%2Fs01.oss.sonatype.org&style=plastic) |

### Development

Expand Down
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ allprojects {
project.name == 'springwolf-sns' ||
project.name == 'springwolf-sqs' ||
project.name == 'springwolf-ui' ||
project.name == 'springwolf-common-model-converters')
project.name == 'springwolf-common-model-converters' ||
project.name == 'springwolf-generic-binding')
tasks.withType(PublishToMavenRepository).configureEach { it.enabled = publishingEnabled }
tasks.withType(PublishToMavenLocal).configureEach { it.enabled = publishingEnabled }
publishing {
Expand Down
3 changes: 2 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ include(
'springwolf-examples:springwolf-sns-example',
'springwolf-examples:springwolf-sqs-example',
'springwolf-ui',
'springwolf-add-ons:springwolf-common-model-converters'
'springwolf-add-ons:springwolf-common-model-converters',
'springwolf-add-ons:springwolf-generic-binding'
)

project(':springwolf-plugins:springwolf-amqp-plugin').name = 'springwolf-amqp'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@ Add the following dependency:

```groovy
dependencies {
implementation 'io.github.springwolf:springwolf-common-model-converters:0.1.1'
implementation 'io.github.springwolf:springwolf-common-model-converters:<springwolf-version>'
}
```
Empty file.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.common_converters.configuration;
package io.github.stavshamir.springwolf.addons.common_model_converters.configuration;

import io.github.stavshamir.springwolf.common_converters.converters.monetaryamount.MonetaryAmountConverter;
import io.github.stavshamir.springwolf.addons.common_model_converters.converters.monetaryamount.MonetaryAmountConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.common_converters.converters.monetaryamount;
package io.github.stavshamir.springwolf.addons.common_model_converters.converters.monetaryamount;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.common_converters.converters.monetaryamount;
package io.github.stavshamir.springwolf.addons.common_model_converters.converters.monetaryamount;

import com.fasterxml.jackson.databind.JavaType;
import io.swagger.v3.core.converter.AnnotatedType;
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
io.github.stavshamir.springwolf.common_converters.configuration.CommonModelConvertersAutoConfiguration
io.github.stavshamir.springwolf.addons.common_model_converters.configuration.CommonModelConvertersAutoConfiguration
Empty file.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.common_converters.converters;
package io.github.stavshamir.springwolf.addons.common_model_converters.converters;

import io.github.stavshamir.springwolf.common_converters.converters.monetaryamount.MonetaryAmountConverter;
import io.github.stavshamir.springwolf.addons.common_model_converters.converters.monetaryamount.MonetaryAmountConverter;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.oas.models.media.Schema;
Expand Down
48 changes: 48 additions & 0 deletions springwolf-add-ons/springwolf-generic-binding/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Springwolf Generic Binding Add-on

### Table Of Contents

- [About](#about)
- [Usage](#usage)
- [Dependencies](#dependencies)
- [Code](#code)

### About

This module allows to document any binding in a generic way.

It is intended to document fields that are not yet support by Springwolf and/or AsyncApi.
It can also be used to document vendor-specific protocols or properties.

There exists no validation on key or value names.

### Usage

Add the following dependency:

#### Dependencies

```groovy
dependencies {
implementation 'io.github.springwolf:springwolf-generic-binding:<springwolf-version>'
}
```

#### Code

```java
class TestClass {
@AsyncPublisher(
// ...
)
@AsyncGenericOperationBinding(
type = "custom-binding",
fields = {
"internal-field=customValue",
"nested.key=nestedValue"
})
public void sendMessage(AnotherPayloadDto msg) {
// ...
}
}
```
51 changes: 51 additions & 0 deletions springwolf-add-ons/springwolf-generic-binding/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
plugins {
id 'java-library'

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

dependencies {
api project(":springwolf-core")

implementation "com.asyncapi:asyncapi-core:${asyncapiCoreVersion}"
implementation "org.slf4j:slf4j-api:${slf4jApiVersion}"

implementation "org.springframework:spring-context"
implementation "org.springframework:spring-core"

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

testImplementation "org.assertj:assertj-core:${assertjCoreVersion}"
testImplementation "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter:${junitJupiterVersion}"
}

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

java {
withJavadocJar()
withSourcesJar()
}

test {
dependsOn spotlessApply // Automatically fix code formatting if possible

useJUnitPlatform()
}

publishing {
publications {
mavenJava(MavenPublication) {
pom {
name = 'springwolf-generic-binding'
description = 'Document any AsyncApi binding in Springwolf using a generic way'
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.addons.generic_binding.annotation;

import io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation.AsyncOperationBinding;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Springwolf cannot support all available protocol bindings that exist.
* To allow users to manually define them, {@link AsyncGenericOperationBinding} can be used.
* <p>
* Use the {@link AsyncGenericOperationBinding#fields()} to define the attributes
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD})
@AsyncOperationBinding
public @interface AsyncGenericOperationBinding {
/**
* The name of the binding
*/
String type();

/**
* All binding fields
*/
String[] fields() default {};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.addons.generic_binding.annotation.configuration;

import io.github.stavshamir.springwolf.addons.generic_binding.annotation.processor.AsyncGenericOperationBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.BindingProcessorPriority;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

@Configuration
public class SpringwolfGenericBindingConfiguration {

@Bean
@Order(value = BindingProcessorPriority.GENERIC_BINDING)
public AsyncGenericOperationBindingProcessor asyncGenericOperationBindingProcessor() {
return new AsyncGenericOperationBindingProcessor();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.addons.generic_binding.annotation.processor;

import com.asyncapi.v2.binding.operation.OperationBinding;
import io.github.stavshamir.springwolf.addons.generic_binding.annotation.AsyncGenericOperationBinding;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.ProcessedOperationBinding;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.processor.AbstractOperationBindingProcessor;

import java.util.HashMap;
import java.util.Map;

public class AsyncGenericOperationBindingProcessor
extends AbstractOperationBindingProcessor<AsyncGenericOperationBinding> {

@Override
protected ProcessedOperationBinding mapToOperationBinding(AsyncGenericOperationBinding bindingAnnotation) {
Map<String, Object> bindingData = PropertiesUtil.toMap(bindingAnnotation.fields());

return new ProcessedOperationBinding(
bindingAnnotation.type(), new DefaultAsyncGenerialOperationBinding(bindingData));
}

public static class DefaultAsyncGenerialOperationBinding extends OperationBinding {
public DefaultAsyncGenerialOperationBinding(Map<String, Object> properties) {
this.extensionFields = new HashMap<>(properties);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.addons.generic_binding.annotation.processor;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.io.StringReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;

@Slf4j
public class PropertiesUtil {

public static Map<String, Object> toMap(String[] propertyStrings) {
return convertPropertiesToNestedMap(buildPropertiesFrom((propertyStrings)));
}

private static Properties buildPropertiesFrom(String[] propertyStrings) {
Properties properties = new Properties();
for (String bindingProperty : propertyStrings) {
try {
properties.load(new StringReader(bindingProperty));
} catch (IOException e) {
log.warn("Unable to parse property {}", bindingProperty, e);
}
}
return properties;
}

@SuppressWarnings("unchecked")
private static Map<String, Object> convertPropertiesToNestedMap(Properties properties) {
Map<String, Object> bindingData = new HashMap<>();
for (String propertyName : properties.stringPropertyNames()) {
LinkedList<String> path = new LinkedList<>(Arrays.asList(propertyName.split("\\.")));

Map<String, Object> mapNode = bindingData;
while (path.size() > 1) {
String pathElement = path.get(0);
if (!mapNode.containsKey(pathElement)) {
mapNode.put(pathElement, new HashMap<>());
}

mapNode = (Map<String, Object>) mapNode.get(pathElement);

path.pop();
}

mapNode.put(path.get(0), properties.get(propertyName));
}
return bindingData;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.github.stavshamir.springwolf.addons.generic_binding.annotation.processor.AsyncGenericOperationBindingProcessor
Loading

0 comments on commit 182b13c

Please sign in to comment.