Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat): Decoupled Binding annotations #655

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/springwolf-bindings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: springwolf-bindings

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

jobs:
build:

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

strategy:
fail-fast: false
matrix:
binding: [ "sns", "sqs" ]

env:
binding: springwolf-bindings/springwolf-${{ matrix.binding }}-binding

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/actions/setup-gradle@v3

- name: Check formatting (before running tests) on binding
run: ./gradlew -p ${{ env.binding }} spotlessCheck

- name: Run unit tests
run: ./gradlew -p ${{ env.binding }} test

- name: Run build, check, analyzeDependencies on binding
run: ./gradlew -p ${{ env.binding }} build

- name: Publish package
if: github.ref == 'refs/heads/master'
run: ./gradlew -p ${{ env.binding }} 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 }}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ springwolf-add-ons/build/
springwolf-core/build/
springwolf-examples/build/
springwolf-plugins/build/
springwolf-bindings/build/
springwolf-ui/build/
springwolf-add-ons/springwolf-common-model-converters/build/
springwolf-examples/springwolf-amqp-example/build/
Expand All @@ -14,6 +15,7 @@ springwolf-plugins/springwolf-amqp-plugin/build/
springwolf-plugins/springwolf-cloud-stream-plugin/build/
springwolf-plugins/springwolf-kafka-plugin/build/
springwolf-plugins/springwolf-sqs-plugin/build/
springwolf-bindings/springwolf-sqs-binding/build/

.gradle/
.idea/
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,20 @@ More details in the documentation.
| [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) |

<details>
<summary>Click to expand all artifacts and add-ons</summary>
<summary>Click to expand all artifacts, bindings and add-ons</summary>

| Artifact | Current version | SNAPSHOT version |
|----------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [AsyncAPI implementation](https://github.com/springwolf/springwolf-core/tree/master/springwolf-asyncapi) | ![Maven Central](https://img.shields.io/maven-central/v/io.github.springwolf/springwolf-asyncapi?color=green&label=springwolf-asyncapi&style=plastic) | ![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/io.github.springwolf/springwolf-asyncapi?label=springwolf-asyncapi&server=https%3A%2F%2Fs01.oss.sonatype.org&style=plastic) |
| [Core](https://github.com/springwolf/springwolf-core/tree/master/springwolf-core) | ![Maven Central](https://img.shields.io/maven-central/v/io.github.springwolf/springwolf-core?color=green&label=springwolf-core&style=plastic) | ![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/io.github.springwolf/springwolf-core?label=springwolf-core&server=https%3A%2F%2Fs01.oss.sonatype.org&style=plastic) |
| [UI](https://github.com/springwolf/springwolf-core/tree/master/springwolf-ui) | ![Maven Central](https://img.shields.io/maven-central/v/io.github.springwolf/springwolf-ui?color=green&label=springwolf-ui&style=plastic) | ![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/io.github.springwolf/springwolf-ui?label=springwolf-ui&server=https%3A%2F%2Fs01.oss.sonatype.org&style=plastic) |

| Bindings | Current version | SNAPSHOT version |
|-------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [AWS SNS Binding](https://github.com/springwolf/springwolf-core/tree/master/springwolf-bindings/springwolf-sns-binding) | ![Maven Central](https://img.shields.io/maven-central/v/io.github.springwolf/springwolf-sns-binding?color=green&label=springwolf-sns-binding&style=plastic) | ![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/io.github.springwolf/springwolf-sns-binding?label=springwolf-sns-binding&server=https%3A%2F%2Fs01.oss.sonatype.org&style=plastic) |
| [AWS SQS Binding](https://github.com/springwolf/springwolf-core/tree/master/springwolf-bindings/springwolf-sqs-binding) | ![Maven Central](https://img.shields.io/maven-central/v/io.github.springwolf/springwolf-sqs-binding?color=green&label=springwolf-sqs-binding&style=plastic) | ![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/io.github.springwolf/springwolf-sqs-binding?label=springwolf-sqs-binding&server=https%3A%2F%2Fs01.oss.sonatype.org&style=plastic) |


| Add-on | Current version | SNAPSHOT version |
|-------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [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) |
Expand Down
4 changes: 3 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
rootProject.name = 'springwolf-core'
rootProject.name = 'springwolf'

include(
'springwolf-asyncapi',
Expand All @@ -15,6 +15,8 @@ include(
'springwolf-examples:springwolf-kafka-example',
'springwolf-examples:springwolf-sns-example',
'springwolf-examples:springwolf-sqs-example',
'springwolf-bindings:springwolf-sns-binding',
'springwolf-bindings:springwolf-sqs-binding',
'springwolf-ui',
'springwolf-add-ons:springwolf-common-model-converters',
'springwolf-add-ons:springwolf-generic-binding',
Expand Down
43 changes: 43 additions & 0 deletions springwolf-bindings/springwolf-sns-binding/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
plugins {
id 'java-library'

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

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

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

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()
}

publishing {
publications {
mavenJava(MavenPublication) {
pom {
name = 'springwolf-sns-binding'
description = 'Automated JSON API documentation for AWS SNS Bindings'
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.bindings.sns.annotations;

import io.github.springwolf.core.asyncapi.annotations.AsyncListener;
import io.github.springwolf.core.asyncapi.annotations.AsyncOperationBinding;
import io.github.springwolf.core.asyncapi.annotations.AsyncPublisher;

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

/**
* {@code @SnsAsyncOperationBinding} is a method-level annotation used in combination with {@link AsyncPublisher} or {@link AsyncListener}.
* It configures the operation binding for the SNS protocol.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@AsyncOperationBinding
@Inherited
public @interface SnsAsyncOperationBinding {

String type() default "sns";

String protocol();

SnsAsyncOperationBindingIdentifier endpoint();

boolean rawMessageDelivery() default true;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.bindings.sns.annotations;

import io.github.springwolf.asyncapi.v3.bindings.sns.SNSOperationBindingIdentifier;

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

/**
* @see SNSOperationBindingIdentifier
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@Inherited
public @interface SnsAsyncOperationBindingIdentifier {
/**
* Optional. The endpoint is a URL
*/
String url() default "";
/**
* Optional. The endpoint is an email address
*/
String email() default "";
/**
* Optional. The endpoint is a phone number
*/
String phone() default "";
/**
* Optional. The target is an ARN. For example, for SQS, the identifier may be an ARN, which will be of the form:
* "arn:aws:sqs:{region}:{account-id}:{queueName}"
*/
String arn() default "";
/**
* Optional. The endpoint is identified by a name, which corresponds to an identifying field called 'name' of a
* binding for that protocol on this publish Operation Object. For example, if the protocol is 'sqs' then the name
* refers to the name field sqs binding. We don't use $ref because we are referring, not including.
*/
String name() default "";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.bindings.sns.configuration;

import io.github.springwolf.bindings.sns.scanners.messages.SnsMessageBindingProcessor;
import io.github.springwolf.bindings.sns.scanners.operations.SnsOperationBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingProcessorPriority;
import io.github.springwolf.core.configuration.properties.SpringwolfConfigConstants;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.core.annotation.Order;

/**
* Autoconfiguration for the springwolf SQS Binding.
*/
@AutoConfiguration
@ConditionalOnProperty(name = SpringwolfConfigConstants.SPRINGWOLF_ENABLED, havingValue = "true", matchIfMissing = true)
public class SpringwolfSnsBindingAutoConfiguration {

@Bean
@Order(value = BindingProcessorPriority.PROTOCOL_BINDING)
@ConditionalOnMissingBean
public SnsMessageBindingProcessor sqsMessageBindingProcessor() {
return new SnsMessageBindingProcessor();
}

@Bean
@Order(value = BindingProcessorPriority.PROTOCOL_BINDING)
@ConditionalOnMissingBean
public SnsOperationBindingProcessor sqsOperationBindingProcessor() {
return new SnsOperationBindingProcessor();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.bindings.sns.scanners.messages;

import io.github.springwolf.asyncapi.v3.bindings.sns.SNSMessageBinding;
import io.github.springwolf.bindings.sns.annotations.SnsAsyncOperationBinding;
import io.github.springwolf.core.asyncapi.scanners.bindings.messages.MessageBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.bindings.messages.ProcessedMessageBinding;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.util.StringValueResolver;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Optional;

public class SnsMessageBindingProcessor implements MessageBindingProcessor, EmbeddedValueResolverAware {
private StringValueResolver resolver;

@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}

@Override
public Optional<ProcessedMessageBinding> process(Method method) {
return Arrays.stream(method.getAnnotations())
.filter(SnsAsyncOperationBinding.class::isInstance)
.map(SnsAsyncOperationBinding.class::cast)
.findAny()
.map(this::mapToMessageBinding);
}

private ProcessedMessageBinding mapToMessageBinding(SnsAsyncOperationBinding bindingAnnotation) {
return new ProcessedMessageBinding(bindingAnnotation.type(), new SNSMessageBinding());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.bindings.sns.scanners.operations;

import io.github.springwolf.asyncapi.v3.bindings.sns.SNSOperationBinding;
import io.github.springwolf.asyncapi.v3.bindings.sns.SNSOperationBindingConsumer;
import io.github.springwolf.asyncapi.v3.bindings.sns.SNSOperationBindingIdentifier;
import io.github.springwolf.bindings.sns.annotations.SnsAsyncOperationBinding;
import io.github.springwolf.bindings.sns.annotations.SnsAsyncOperationBindingIdentifier;
import io.github.springwolf.core.asyncapi.scanners.bindings.operations.AbstractOperationBindingProcessor;
import io.github.springwolf.core.asyncapi.scanners.bindings.operations.ProcessedOperationBinding;

import java.util.List;

public class SnsOperationBindingProcessor extends AbstractOperationBindingProcessor<SnsAsyncOperationBinding> {

@Override
protected ProcessedOperationBinding mapToOperationBinding(SnsAsyncOperationBinding bindingAnnotation) {
var identifier = convertAnnotation(bindingAnnotation.endpoint());
var protocol = readProtocol(bindingAnnotation.protocol());

var consumer = SNSOperationBindingConsumer.builder()
.protocol(protocol)
.endpoint(identifier)
.rawMessageDelivery(bindingAnnotation.rawMessageDelivery())
.build();
var snsOperationBinding =
SNSOperationBinding.builder().consumers(List.of(consumer)).build();
return new ProcessedOperationBinding(bindingAnnotation.type(), snsOperationBinding);
}

private SNSOperationBindingConsumer.Protocol readProtocol(String protocol) {
return SNSOperationBindingConsumer.Protocol.valueOf(protocol.toUpperCase());
}

private SNSOperationBindingIdentifier convertAnnotation(SnsAsyncOperationBindingIdentifier identifier) {
var builder = SNSOperationBindingIdentifier.builder();

if (!identifier.url().isBlank()) {
builder.url(identifier.url());
}
if (!identifier.arn().isBlank()) {
builder.arn(identifier.arn());
}
if (!identifier.name().isBlank()) {
builder.name(identifier.name());
}
if (!identifier.email().isBlank()) {
builder.email(identifier.email());
}
if (!identifier.phone().isBlank()) {
builder.phone(identifier.phone());
}

return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.github.springwolf.bindings.sns.configuration.SpringwolfSnsBindingAutoConfiguration
Loading