Skip to content

Commit

Permalink
feat: Replaced usage of jAsyncAPI by the new springwolf-asyncapi modu…
Browse files Browse the repository at this point in the history
…le (#523)

* feat: Replaced usage of jAsyncAPI by the new springwolf-asyncapi module

We migrate from jAsyncAPI, with support for AsyncAPI v2, to the new SpringWolf-AsyncAPI module, which supports AsyncAPI v3.

This PR is only a first step. Plenty of tests are still not working and others are partially commented.

Please take a look to the multiple FIXMEs in the code
  • Loading branch information
ctasada authored Jan 3, 2024
1 parent 638e13b commit cb577ea
Show file tree
Hide file tree
Showing 118 changed files with 1,618 additions and 1,312 deletions.
2 changes: 0 additions & 2 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
ext {
assertjCoreVersion = '3.24.2'

asyncapiCoreVersion = '1.0.0-EAP-2'

awaitilityVersion = '4.2.0'

commonsLang3Version = '3.14.0'
Expand Down
4 changes: 3 additions & 1 deletion springwolf-add-ons/springwolf-generic-binding/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ plugins {

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

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}"

compileOnly "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}"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// 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 io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;

import java.util.HashMap;
import java.util.Map;
Expand Down
1 change: 1 addition & 0 deletions springwolf-add-ons/springwolf-json-schema/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies {
implementation "org.springframework:spring-context"

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

testImplementation "org.mockito:mockito-core:${mockitoCoreVersion}"
testImplementation "org.assertj:assertj-core:${assertjCoreVersion}"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.v3.model.schema;

public class SchemaType {
public static final String NULL = "null";
public static final String BOOLEAN = "boolean";
public static final String OBJECT = "object";
public static final String ARRAY = "array";
public static final String NUMBER = "number";
public static final String STRING = "string";
public static final String INTEGER = "integer";

private SchemaType() {}
}
5 changes: 3 additions & 2 deletions springwolf-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

dependencies {
api "com.asyncapi:asyncapi-core:${asyncapiCoreVersion}"
api project(":springwolf-asyncapi")

implementation "io.swagger.core.v3:swagger-annotations-jakarta:${swaggerVersion}@jar"
implementation "io.swagger.core.v3:swagger-core-jakarta:${swaggerVersion}"
Expand Down Expand Up @@ -44,8 +44,9 @@ dependencies {
testCompileOnly "org.projectlombok:lombok:${lombokVersion}"

testImplementation "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}"
testImplementation("org.junit.jupiter:junit-jupiter-params:${junitJupiterVersion}")
testImplementation "org.junit.jupiter:junit-jupiter-params:${junitJupiterVersion}"
testImplementation "org.assertj:assertj-core:${assertjCoreVersion}"
testImplementation "net.javacrumbs.json-unit:json-unit-assertj:${jsonUnitAssertJVersion}"
testImplementation "org.awaitility:awaitility:${awaitilityVersion}"
testImplementation "org.mockito:mockito-core:${mockitoCoreVersion}"
testImplementation "org.springframework.boot:spring-boot-test"
Expand Down
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.asyncapi;

import com.asyncapi.v2._6_0.model.channel.ChannelItem;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;

import java.util.Map;

Expand All @@ -15,5 +15,5 @@ public interface ChannelsService {
*
* @return Map of channel names mapping to detected ChannelItems
*/
Map<String, ChannelItem> findChannels();
Map<String, ChannelObject> findChannels();
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.swagger.v3.core.util.Yaml;
import jakarta.annotation.PostConstruct;

// FIXME: Replace this class by the AsyncAPI 'DefaultAsyncApiSerializerService'
public class DefaultAsyncApiSerializerService implements AsyncApiSerializerService {

private ObjectMapper jsonMapper = Json.mapper();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi;

import com.asyncapi.v2._6_0.model.channel.ChannelItem;
import io.github.stavshamir.springwolf.asyncapi.types.AsyncAPI;
import io.github.stavshamir.springwolf.asyncapi.types.Components;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
import io.github.stavshamir.springwolf.configuration.AsyncApiDocket;
import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService;
import io.github.stavshamir.springwolf.schemas.SchemasService;
Expand Down Expand Up @@ -62,10 +62,15 @@ protected synchronized void initAsyncAPI() {
// ChannelsService must be invoked before accessing SchemasService,
// because during channel scanning, all detected schemas are registered with
// SchemasService.
Map<String, ChannelItem> channels = channelsService.findChannels();
Map<String, ChannelObject> channels = channelsService.findChannels();

Components components = Components.builder()
.schemas(schemasService.getDefinitions())
// FIXME
// .schemas(schemasService.getDefinitions().entrySet().stream()
// .collect(Collectors.toMap(
// Map.Entry::getKey,
// entry -> ComponentSchema.of(entry.getValue())
// )))
.build();

AsyncAPI asyncAPI = AsyncAPI.builder()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi;

import com.asyncapi.v2._6_0.model.channel.ChannelItem;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelMerger;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelsScanner;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

Expand All @@ -26,12 +26,12 @@ public class DefaultChannelsService implements ChannelsService {
* @return Map of channel names mapping to detected ChannelItems
*/
@Override
public Map<String, ChannelItem> findChannels() {
List<Map.Entry<String, ChannelItem>> foundChannelItems = new ArrayList<>();
public Map<String, ChannelObject> findChannels() {
List<Map.Entry<String, ChannelObject>> foundChannelItems = new ArrayList<>();

for (ChannelsScanner scanner : channelsScanners) {
try {
Map<String, ChannelItem> channels = scanner.scan();
Map<String, ChannelObject> channels = scanner.scan();
foundChannelItems.addAll(channels.entrySet());
} catch (Exception e) {
log.error("An error was encountered during channel scanning with {}: {}", scanner, e.getMessage(), e);
Expand Down
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.asyncapi;

import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.Message;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
Expand All @@ -19,11 +19,11 @@
public class MessageHelper {
private static final String ONE_OF = "oneOf";

private static final Comparator<Message> byMessageName = Comparator.comparing(Message::getName);
private static final Comparator<MessageObject> byMessageName = Comparator.comparing(MessageObject::getName);

private static final Supplier<Set<Message>> messageSupplier = () -> new TreeSet<>(byMessageName);
private static final Supplier<Set<MessageObject>> messageSupplier = () -> new TreeSet<>(byMessageName);

public static Object toMessageObjectOrComposition(Set<Message> messages) {
public static Object toMessageObjectOrComposition(Set<MessageObject> messages) {
return switch (messages.size()) {
case 0 -> throw new IllegalArgumentException("messages must not be empty");
case 1 -> messages.toArray()[0];
Expand All @@ -33,13 +33,13 @@ public static Object toMessageObjectOrComposition(Set<Message> messages) {
}

@SuppressWarnings("unchecked")
public static Set<Message> messageObjectToSet(Object messageObject) {
if (messageObject instanceof Message message) {
public static Set<MessageObject> messageObjectToSet(Object messageObject) {
if (messageObject instanceof MessageObject message) {
return new HashSet<>(Collections.singletonList(message));
}

if (messageObject instanceof Map) {
List<Message> messages = ((Map<String, List<Message>>) messageObject).get(ONE_OF);
List<MessageObject> messages = ((Map<String, List<MessageObject>>) messageObject).get(ONE_OF);
return new HashSet<>(messages);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.bindings;

import com.asyncapi.v2.binding.channel.ChannelBinding;
import com.asyncapi.v2.binding.message.MessageBinding;
import com.asyncapi.v2.binding.operation.OperationBinding;
import io.github.stavshamir.springwolf.asyncapi.v3.bindings.ChannelBinding;
import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding;
import io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;

import java.util.Map;

public interface BindingFactory<T> {
String getChannelName(T annotation);

Map<String, ? extends ChannelBinding> buildChannelBinding(T annotation);
Map<String, ChannelBinding> buildChannelBinding(T annotation);

Map<String, ? extends OperationBinding> buildOperationBinding(T annotation);
Map<String, OperationBinding> buildOperationBinding(T annotation);

Map<String, ? extends MessageBinding> buildMessageBinding(T annotation);
Map<String, MessageBinding> buildMessageBinding(T annotation);
}
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.asyncapi.scanners.bindings;

import com.asyncapi.v2.binding.message.MessageBinding;
import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding;
import lombok.Data;

@Data
Expand Down
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.asyncapi.scanners.bindings;

import com.asyncapi.v2.binding.operation.OperationBinding;
import io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;
import lombok.Data;

@Data
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.channels;

import com.asyncapi.v2._6_0.model.channel.ChannelItem;
import com.asyncapi.v2._6_0.model.channel.operation.Operation;
import io.github.stavshamir.springwolf.asyncapi.MessageHelper;
import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.Message;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.Channel;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;

import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -15,35 +16,33 @@
import java.util.function.Function;
import java.util.stream.Collectors;

import static io.github.stavshamir.springwolf.asyncapi.MessageHelper.toMessageObjectOrComposition;

/**
* Util to merge multiple {@link ChannelItem}s
* Util to merge multiple {@link Channel}s
*/
public class ChannelMerger {

private ChannelMerger() {}

/**
* Merges multiple channelItems by channel name
* Merges multiple channels by channel name
* <p>
* Given two channelItems for the same channel name, the first seen ChannelItem is used
* Given two channels for the same channel name, the first seen Channel is used
* If an operation is null, the next non-null operation is used
* Messages within operations are merged
*
* @param channelEntries Ordered pairs of channel name to ChannelItem
* @return A map of channelName to a single ChannelItem
* @param channelEntries Ordered pairs of channel name to Channel
* @return A map of channelName to a single Channel
*/
public static Map<String, ChannelItem> merge(List<Map.Entry<String, ChannelItem>> channelEntries) {
Map<String, ChannelItem> mergedChannels = new HashMap<>();
public static Map<String, ChannelObject> merge(List<Map.Entry<String, ChannelObject>> channelEntries) {
Map<String, ChannelObject> mergedChannels = new HashMap<>();

for (Map.Entry<String, ChannelItem> entry : channelEntries) {
for (Map.Entry<String, ChannelObject> entry : channelEntries) {
if (!mergedChannels.containsKey(entry.getKey())) {
mergedChannels.put(entry.getKey(), entry.getValue());
} else {
ChannelItem channelItem = mergedChannels.get(entry.getKey());
channelItem.setPublish(mergeOperation(
channelItem.getPublish(), entry.getValue().getPublish()));
channelItem.setSubscribe(mergeOperation(
channelItem.getSubscribe(), entry.getValue().getSubscribe()));
ChannelObject channel = mergedChannels.get(entry.getKey());
// channel.setPublish(mergeOperation(channel.getPublish(), entry.getValue().getPublish()));
// channel.setSubscribe(mergeOperation(channel.getSubscribe(), entry.getValue().getSubscribe()));
}
}

Expand All @@ -53,27 +52,27 @@ public static Map<String, ChannelItem> merge(List<Map.Entry<String, ChannelItem>
private static Operation mergeOperation(Operation operation, Operation otherOperation) {
Operation mergedOperation = operation != null ? operation : otherOperation;

Set<Message> mergedMessages = mergeMessages(getMessages(operation), getMessages(otherOperation));
if (!mergedMessages.isEmpty()) {
mergedOperation.setMessage(toMessageObjectOrComposition(mergedMessages));
}
Set<MessageObject> mergedMessages = mergeMessages(getMessages(operation), getMessages(otherOperation));
// if (!mergedMessages.isEmpty()) {
// mergedOperation.setMessage(toMessageObjectOrComposition(mergedMessages)); FIXME
// }
return mergedOperation;
}

private static Set<Message> mergeMessages(Set<Message> messages, Set<Message> otherMessages) {
Map<String, Message> nameToMessage =
messages.stream().collect(Collectors.toMap(Message::getName, Function.identity()));
private static Set<MessageObject> mergeMessages(Set<MessageObject> messages, Set<MessageObject> otherMessages) {
Map<String, MessageObject> nameToMessage =
messages.stream().collect(Collectors.toMap(MessageObject::getName, Function.identity()));

for (Message otherMessage : otherMessages) {
for (MessageObject otherMessage : otherMessages) {
nameToMessage.putIfAbsent(otherMessage.getName(), otherMessage);
}

return new HashSet<>(nameToMessage.values());
}

private static Set<Message> getMessages(Operation operation) {
private static Set<MessageObject> getMessages(Operation operation) {
return Optional.ofNullable(operation)
.map(Operation::getMessage)
.map(Operation::getMessages)
.map(MessageHelper::messageObjectToSet)
.orElseGet(HashSet::new);
}
Expand Down
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.asyncapi.scanners.channels;

import com.asyncapi.v2._6_0.model.channel.ChannelItem;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;

import java.util.Map;

Expand All @@ -10,5 +10,5 @@ public interface ChannelsScanner {
/**
* @return A mapping of channel names to their respective channel object for a given protocol.
*/
Map<String, ChannelItem> scan();
Map<String, ChannelObject> scan();
}
Loading

0 comments on commit cb577ea

Please sign in to comment.