diff --git a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/operation/Operation.java b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/operation/Operation.java index 567e9099f..e699afe1f 100644 --- a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/operation/Operation.java +++ b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/operation/Operation.java @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.asyncapi.v3.model.operation; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import io.github.springwolf.asyncapi.v3.bindings.OperationBinding; import io.github.springwolf.asyncapi.v3.model.ExtendableObject; @@ -10,11 +11,13 @@ import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; import io.github.springwolf.asyncapi.v3.model.security_scheme.SecurityScheme; import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import lombok.Setter; import java.util.List; import java.util.Map; @@ -30,6 +33,15 @@ @AllArgsConstructor @EqualsAndHashCode(callSuper = true) public class Operation extends ExtendableObject { + /** + * The key represents the operation identifier. The operationId value is case-sensitive. Tools and libraries MAY use + * the operationId value to uniquely identify a operation, therefore, it is RECOMMENDED to follow common programming + * naming conventions. + */ + @JsonIgnore + @Setter(AccessLevel.NONE) + private String operationId; + /** * Required. Use send when it's expected that the application will send a message to the given channel, * and receive when the application should expect receiving messages from the given channel. diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java index 6e1959ce9..046bf251e 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/grouping/GroupingService.java @@ -67,14 +67,13 @@ private void markChannels(AsyncAPI fullAsyncApi, AsyncApiGroup asyncApiGroup, Ma } private void markOperationsInChannel(AsyncAPI fullAsyncApi, MarkingContext markingContext, ChannelObject channel) { - fullAsyncApi.getOperations().entrySet().stream() + fullAsyncApi.getOperations().values().stream() .filter(operationEntry -> matchesOperationInChannel(channel, operationEntry)) - .forEach(operationEntry -> markingContext.markedOperationIds.add(operationEntry.getKey())); + .forEach(operationEntry -> markingContext.markedOperationIds.add(operationEntry.getOperationId())); } - private boolean matchesOperationInChannel(ChannelObject channel, Map.Entry operationEntry) { - return operationEntry - .getValue() + private boolean matchesOperationInChannel(ChannelObject channel, Operation operation) { + return operation .getChannel() .getRef() .equals(ChannelReference.fromChannel(channel.getChannelId()).getRef()); @@ -85,14 +84,14 @@ private void markOperations(AsyncAPI fullAsyncApi, AsyncApiGroup asyncApiGroup, return; } - fullAsyncApi.getOperations().entrySet().stream() - .filter(operationEntry -> asyncApiGroup.isMatch(operationEntry.getValue())) - .forEach(operationEntry -> { - markingContext.markedOperationIds.add(operationEntry.getKey()); + fullAsyncApi.getOperations().values().stream() + .filter(asyncApiGroup::isMatch) + .forEach(operation -> { + markingContext.markedOperationIds.add(operation.getOperationId()); - markChannelsForOperation(fullAsyncApi, markingContext, operationEntry.getValue()); + markChannelsForOperation(fullAsyncApi, markingContext, operation); - operationEntry.getValue().getMessages().stream() + operation.getMessages().stream() .map(MessageReference::getRef) .map(ReferenceUtil::getLastSegment) .forEach(markingContext.markedComponentMessageIds::add); @@ -151,6 +150,13 @@ private void markSchemasInMessageIds(AsyncAPI fullAsyncApi, MarkingContext marki .filter(message -> markingContext.markedComponentMessageIds.contains(message.getMessageId())) .toList(); + markMessageHeadersForSchemas(messages, schemaIds); + markMessagePayloadsForSchemas(messages, schemaIds); + + markSchemas(fullAsyncApi, markingContext, schemaIds); + } + + private void markMessageHeadersForSchemas(List messages, Set schemaIds) { messages.stream() .map(MessageObject::getHeaders) .filter(Objects::nonNull) @@ -158,6 +164,9 @@ private void markSchemasInMessageIds(AsyncAPI fullAsyncApi, MarkingContext marki .map(MessageReference::getRef) .map(ReferenceUtil::getLastSegment) .forEach(schemaIds::add); + } + + private void markMessagePayloadsForSchemas(List messages, Set schemaIds) { messages.stream() .map(MessageObject::getPayload) .map(MessagePayload::getMultiFormatSchema) @@ -168,8 +177,6 @@ private void markSchemasInMessageIds(AsyncAPI fullAsyncApi, MarkingContext marki .map(MessageReference::getRef) .map(ReferenceUtil::getLastSegment) .forEach(schemaIds::add); - - markSchemas(fullAsyncApi, markingContext, schemaIds); } private void markSchemas(AsyncAPI fullAsyncApi, MarkingContext markingContext, Set schemaIds) { @@ -181,15 +188,7 @@ private void markSchemas(AsyncAPI fullAsyncApi, MarkingContext markingContext, S markingContext.markedComponentSchemaIds.add(schemaEntry.getKey()); if (schemaEntry.getValue().getProperties() != null) { - Set nestedSchemas = schemaEntry.getValue().getProperties().values().stream() - .filter(el -> el instanceof ComponentSchema) - .map(el -> (ComponentSchema) el) - .map(ComponentSchema::getReference) - .filter(Objects::nonNull) - .map(MessageReference::getRef) - .map(ReferenceUtil::getLastSegment) - .filter(schemaId -> !markingContext.markedComponentSchemaIds.contains(schemaId)) - .collect(Collectors.toSet()); + Set nestedSchemas = findUnmarkedNestedSchemas(markingContext, schemaEntry.getValue()); if (!nestedSchemas.isEmpty()) { markSchemas(fullAsyncApi, markingContext, nestedSchemas); } @@ -197,6 +196,18 @@ private void markSchemas(AsyncAPI fullAsyncApi, MarkingContext markingContext, S }); } + private static Set findUnmarkedNestedSchemas(MarkingContext markingContext, SchemaObject schema) { + return schema.getProperties().values().stream() + .filter(el -> el instanceof ComponentSchema) + .map(el -> (ComponentSchema) el) + .map(ComponentSchema::getReference) + .filter(Objects::nonNull) + .map(MessageReference::getRef) + .map(ReferenceUtil::getLastSegment) + .filter(schemaId -> !markingContext.markedComponentSchemaIds.contains(schemaId)) + .collect(Collectors.toSet()); + } + private Map filterChannels(AsyncAPI fullAsyncApi, MarkingContext markingContext) { return fullAsyncApi.getChannels().values().stream() .filter(channel -> markingContext.isChannelMarked(channel.getChannelId())) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/channel/AsyncAnnotationChannelService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/channel/AsyncAnnotationChannelService.java index 4b58836a1..f5ca3ed79 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/channel/AsyncAnnotationChannelService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/channel/AsyncAnnotationChannelService.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; +import java.util.Set; @RequiredArgsConstructor @Slf4j @@ -35,20 +36,20 @@ public class AsyncAnnotationChannelService methodAndAnnotation) { AsyncOperation operationAnnotation = this.asyncAnnotationProvider.getAsyncOperation(methodAndAnnotation.annotation()); - String channelName = stringValueResolver.resolveStringValue(operationAnnotation.channelName()); - String channelId = ReferenceUtil.toValidId(channelName); ChannelObject.ChannelObjectBuilder channelBuilder = ChannelObject.builder(); List servers = AsyncAnnotationUtil.getServers(operationAnnotation, stringValueResolver); if (servers != null && !servers.isEmpty()) { Operation operation = asyncAnnotationOperationService.buildOperation( - operationAnnotation, methodAndAnnotation.method(), channelId); + operationAnnotation, Set.of(methodAndAnnotation.method())); validateServers(servers, operation.getTitle()); channelBuilder.servers( servers.stream().map(ServerReference::fromServer).toList()); } + String channelName = stringValueResolver.resolveStringValue(operationAnnotation.channelName()); + String channelId = ReferenceUtil.toValidId(channelName); MessageObject message = asyncAnnotationMessageService.buildMessage(operationAnnotation, methodAndAnnotation.method()); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/AsyncAnnotationOperationService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/AsyncAnnotationOperationService.java index 27f2d2d83..0b55c881e 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/AsyncAnnotationOperationService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/AsyncAnnotationOperationService.java @@ -2,8 +2,8 @@ package io.github.springwolf.core.asyncapi.scanners.common.operation; import io.github.springwolf.asyncapi.v3.bindings.OperationBinding; +import io.github.springwolf.asyncapi.v3.model.ReferenceUtil; import io.github.springwolf.asyncapi.v3.model.channel.ChannelReference; -import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; import io.github.springwolf.asyncapi.v3.model.operation.Operation; import io.github.springwolf.core.asyncapi.annotations.AsyncOperation; @@ -12,6 +12,7 @@ import io.github.springwolf.core.asyncapi.scanners.common.annotation.AsyncAnnotationUtil; import io.github.springwolf.core.asyncapi.scanners.common.message.AsyncAnnotationMessageService; import io.github.springwolf.core.asyncapi.scanners.common.utils.TextUtils; +import io.github.springwolf.core.asyncapi.scanners.operations.OperationIdHelper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -33,14 +34,10 @@ public class AsyncAnnotationOperationService messages = List.of(MessageReference.toChannelMessage(channelId, message)); + public Operation buildOperation(AsyncOperation asyncOperation, Set methods) { + String channelName = stringValueResolver.resolveStringValue(asyncOperation.channelName()); + String channelId = ReferenceUtil.toValidId(channelName); - return buildOperation(asyncOperation, method, channelId, messages); - } - - public Operation buildOperation(AsyncOperation asyncOperation, Set methods, String channelId) { Method method = methods.stream().findFirst().orElseThrow(); List messages = methods.stream() .map(m -> asyncAnnotationMessageService.buildMessage(asyncOperation, m)) @@ -52,6 +49,9 @@ public Operation buildOperation(AsyncOperation asyncOperation, Set metho private Operation buildOperation( AsyncOperation asyncOperation, Method method, String channelId, List messages) { + String operationId = OperationIdHelper.createOperationId( + channelId, this.asyncAnnotationProvider.getOperationType(), method.getName()); + String description = this.stringValueResolver.resolveStringValue(asyncOperation.description()); if (StringUtils.isBlank(description)) { description = "Auto-generated description"; @@ -67,6 +67,7 @@ private Operation buildOperation( Map opBinding = operationBinding != null ? new HashMap<>(operationBinding) : null; return Operation.builder() + .operationId(operationId) .channel(ChannelReference.fromChannel(channelId)) .action(this.asyncAnnotationProvider.getOperationType()) .description(description) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationService.java index a40550aac..f63a4bc05 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationService.java @@ -9,8 +9,10 @@ import io.github.springwolf.asyncapi.v3.model.operation.OperationAction; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory; +import io.github.springwolf.core.asyncapi.scanners.common.annotation.MethodAndAnnotation; import io.github.springwolf.core.asyncapi.scanners.common.message.SpringAnnotationMessageService; import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadSchemaObject; +import io.github.springwolf.core.asyncapi.scanners.operations.OperationIdHelper; import lombok.RequiredArgsConstructor; import java.lang.annotation.Annotation; @@ -25,13 +27,20 @@ public class SpringAnnotationOperationService springAnnotationMessageService; public Operation buildOperation( - MethodAnnotation annotation, PayloadSchemaObject payloadType, SchemaObject headerSchema) { - MessageObject message = springAnnotationMessageService.buildMessage(annotation, payloadType, headerSchema); - Map operationBinding = bindingFactory.buildOperationBinding(annotation); + MethodAndAnnotation annotation, + PayloadSchemaObject payloadType, + SchemaObject headerSchema) { + String channelId = bindingFactory.getChannelId(annotation.annotation()); + String operationId = OperationIdHelper.createOperationId( + channelId, OperationAction.RECEIVE, annotation.method().getName()); + + MessageObject message = + springAnnotationMessageService.buildMessage(annotation.annotation(), payloadType, headerSchema); + Map operationBinding = bindingFactory.buildOperationBinding(annotation.annotation()); Map opBinding = operationBinding != null ? new HashMap<>(operationBinding) : null; - String channelId = bindingFactory.getChannelId(annotation); return Operation.builder() + .operationId(operationId) .action(OperationAction.RECEIVE) .channel(ChannelReference.fromChannel(channelId)) .messages(List.of(MessageReference.toChannelMessage(channelId, message))) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationsService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationsService.java index d90feb9c7..e72bcee85 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationsService.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationsService.java @@ -2,13 +2,12 @@ package io.github.springwolf.core.asyncapi.scanners.common.operation; import io.github.springwolf.asyncapi.v3.bindings.OperationBinding; -import io.github.springwolf.asyncapi.v3.model.ReferenceUtil; import io.github.springwolf.asyncapi.v3.model.channel.ChannelReference; -import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; import io.github.springwolf.asyncapi.v3.model.operation.Operation; import io.github.springwolf.asyncapi.v3.model.operation.OperationAction; import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory; import io.github.springwolf.core.asyncapi.scanners.common.message.SpringAnnotationMessagesService; +import io.github.springwolf.core.asyncapi.scanners.operations.OperationIdHelper; import lombok.RequiredArgsConstructor; import java.lang.annotation.Annotation; @@ -23,19 +22,19 @@ public class SpringAnnotationOperationsService bindingFactory; private final SpringAnnotationMessagesService springAnnotationMessagesService; - public Operation buildOperation(ClassAnnotation classAnnotation, Set methods) { + public Operation buildOperation(ClassAnnotation classAnnotation, Class component, Set methods) { var messages = springAnnotationMessagesService.buildMessages( classAnnotation, methods, SpringAnnotationMessagesService.MessageType.OPERATION); - return buildOperation(classAnnotation, messages); - } - private Operation buildOperation(ClassAnnotation classAnnotation, Map messages) { Map operationBinding = bindingFactory.buildOperationBinding(classAnnotation); Map opBinding = operationBinding != null ? new HashMap<>(operationBinding) : null; - String channelName = bindingFactory.getChannelName(classAnnotation); - String channelId = ReferenceUtil.toValidId(channelName); + + String channelId = bindingFactory.getChannelId(classAnnotation); + String operationId = + OperationIdHelper.createOperationId(channelId, OperationAction.RECEIVE, component.getSimpleName()); return Operation.builder() + .operationId(operationId) .action(OperationAction.RECEIVE) .channel(ChannelReference.fromChannel(channelId)) .messages(messages.values().stream().toList()) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/OperationIdHelper.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/OperationIdHelper.java new file mode 100644 index 000000000..08f700014 --- /dev/null +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/OperationIdHelper.java @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.asyncapi.scanners.operations; + +import io.github.springwolf.asyncapi.v3.model.operation.OperationAction; + +public class OperationIdHelper { + public static String createOperationId(String channelId, OperationAction operationAction, String componentName) { + return String.join("_", channelId, operationAction.type, componentName); + } +} diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationClassLevelOperationsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationClassLevelOperationsScanner.java index c4787b9e4..e971fa8b6 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationClassLevelOperationsScanner.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationClassLevelOperationsScanner.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.core.asyncapi.scanners.operations.annotations; -import io.github.springwolf.asyncapi.v3.model.ReferenceUtil; import io.github.springwolf.asyncapi.v3.model.operation.Operation; import io.github.springwolf.core.asyncapi.annotations.AsyncOperation; import io.github.springwolf.core.asyncapi.scanners.common.AsyncAnnotationProvider; @@ -13,7 +12,6 @@ import io.github.springwolf.core.asyncapi.scanners.operations.OperationsInClassScanner; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -55,17 +53,11 @@ private Stream> mapClassToOperation( ClassAnnotation classAnnotation = AnnotationUtil.findFirstAnnotationOrThrow(classAnnotationClass, component); AsyncOperation asyncOperation = asyncAnnotationProvider.getAsyncOperation(classAnnotation); - String channelName = - asyncAnnotationProvider.getAsyncOperation(classAnnotation).channelName(); - String channelId = ReferenceUtil.toValidId(channelName); - String operationId = StringUtils.joinWith( - "_", channelId, asyncAnnotationProvider.getOperationType().type, component.getSimpleName()); - Set methods = annotatedMethods.stream().map(MethodAndAnnotation::method).collect(Collectors.toSet()); - Operation operation = asyncAnnotationOperationsService.buildOperation(asyncOperation, methods, channelId); + Operation operation = asyncAnnotationOperationsService.buildOperation(asyncOperation, methods); annotatedMethods.forEach( method -> customizers.forEach(customizer -> customizer.customize(operation, method.method()))); - return Stream.of(Map.entry(operationId, operation)); + return Stream.of(Map.entry(operation.getOperationId(), operation)); } } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationMethodLevelOperationsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationMethodLevelOperationsScanner.java index 8c6be6779..86a520b13 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationMethodLevelOperationsScanner.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationMethodLevelOperationsScanner.java @@ -1,7 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.core.asyncapi.scanners.operations.annotations; -import io.github.springwolf.asyncapi.v3.model.ReferenceUtil; import io.github.springwolf.asyncapi.v3.model.operation.Operation; import io.github.springwolf.core.asyncapi.annotations.AsyncOperation; import io.github.springwolf.core.asyncapi.scanners.common.AsyncAnnotationProvider; @@ -11,12 +10,11 @@ import io.github.springwolf.core.asyncapi.scanners.operations.OperationsInClassScanner; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.util.StringValueResolver; import java.lang.annotation.Annotation; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Stream; @Slf4j @@ -27,7 +25,6 @@ public class AsyncAnnotationMethodLevelOperationsScanner asyncAnnotationProvider; private final AsyncAnnotationOperationService asyncAnnotationOperationService; private final List customizers; - private final StringValueResolver stringValueResolver; @Override public Stream> scan(Class clazz) { @@ -40,18 +37,9 @@ private Map.Entry mapMethodToOperation( AsyncOperation operationAnnotation = this.asyncAnnotationProvider.getAsyncOperation(methodAndAnnotation.annotation()); - String channelName = stringValueResolver.resolveStringValue(operationAnnotation.channelName()); - String channelId = ReferenceUtil.toValidId(channelName); - // operationId should be part of the buildOperation method and part of Operation object - String operationId = StringUtils.joinWith( - "_", - channelId, - this.asyncAnnotationProvider.getOperationType().type, - methodAndAnnotation.method().getName()); - Operation operation = asyncAnnotationOperationService.buildOperation( - operationAnnotation, methodAndAnnotation.method(), channelId); + operationAnnotation, Set.of(methodAndAnnotation.method())); customizers.forEach(customizer -> customizer.customize(operation, methodAndAnnotation.method())); - return Map.entry(operationId, operation); + return Map.entry(operation.getOperationId(), operation); } } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScanner.java index 0a2ed6701..805d5e016 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScanner.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScanner.java @@ -2,8 +2,6 @@ package io.github.springwolf.core.asyncapi.scanners.operations.annotations; import io.github.springwolf.asyncapi.v3.model.operation.Operation; -import io.github.springwolf.asyncapi.v3.model.operation.OperationAction; -import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory; import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationScannerUtil; import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationUtil; import io.github.springwolf.core.asyncapi.scanners.common.annotation.MethodAndAnnotation; @@ -11,7 +9,6 @@ import io.github.springwolf.core.asyncapi.scanners.operations.OperationsInClassScanner; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import java.lang.annotation.Annotation; import java.lang.reflect.Method; @@ -29,7 +26,6 @@ public class SpringAnnotationClassLevelOperationsScanner< private final Class classAnnotationClass; private final Class methodAnnotationClass; - private final BindingFactory bindingFactory; private final SpringAnnotationOperationsService springAnnotationOperationsService; private final List customizers; @@ -43,15 +39,11 @@ private Stream> mapClassToOperation( Class component, Set> annotatedMethods) { ClassAnnotation classAnnotation = AnnotationUtil.findFirstAnnotationOrThrow(classAnnotationClass, component); - String channelId = bindingFactory.getChannelId(classAnnotation); - String operationId = - StringUtils.joinWith("_", channelId, OperationAction.RECEIVE.type, component.getSimpleName()); - Set methods = annotatedMethods.stream().map(MethodAndAnnotation::method).collect(Collectors.toSet()); - Operation operation = springAnnotationOperationsService.buildOperation(classAnnotation, methods); + Operation operation = springAnnotationOperationsService.buildOperation(classAnnotation, component, methods); annotatedMethods.forEach( method -> customizers.forEach(customizer -> customizer.customize(operation, method.method()))); - return Stream.of(Map.entry(operationId, operation)); + return Stream.of(Map.entry(operation.getOperationId(), operation)); } } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScanner.java index ef3ac9834..560324173 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScanner.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScanner.java @@ -2,11 +2,8 @@ package io.github.springwolf.core.asyncapi.scanners.operations.annotations; import io.github.springwolf.asyncapi.v3.model.operation.Operation; -import io.github.springwolf.asyncapi.v3.model.operation.OperationAction; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; -import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory; import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationScannerUtil; -import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationUtil; import io.github.springwolf.core.asyncapi.scanners.common.annotation.MethodAndAnnotation; import io.github.springwolf.core.asyncapi.scanners.common.headers.HeaderClassExtractor; import io.github.springwolf.core.asyncapi.scanners.common.operation.SpringAnnotationOperationService; @@ -15,7 +12,6 @@ import io.github.springwolf.core.asyncapi.scanners.operations.OperationsInClassScanner; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import java.lang.annotation.Annotation; import java.util.List; @@ -28,7 +24,6 @@ public class SpringAnnotationMethodLevelOperationsScanner methodAnnotationClass; - private final BindingFactory bindingFactory; private final HeaderClassExtractor headerClassExtractor; private final PayloadMethodParameterService payloadMethodParameterService; private final SpringAnnotationOperationService springAnnotationOperationService; @@ -41,17 +36,11 @@ public Stream> scan(Class clazz) { } private Map.Entry mapMethodToOperation(MethodAndAnnotation method) { - MethodAnnotation annotation = AnnotationUtil.findFirstAnnotationOrThrow(methodAnnotationClass, method.method()); - - String channelId = bindingFactory.getChannelId(annotation); - String operationId = StringUtils.joinWith( - "_", channelId, OperationAction.RECEIVE.type, method.method().getName()); - PayloadSchemaObject payloadSchema = payloadMethodParameterService.extractSchema(method.method()); SchemaObject headerSchema = headerClassExtractor.extractHeader(method.method(), payloadSchema); - Operation operation = springAnnotationOperationService.buildOperation(annotation, payloadSchema, headerSchema); + Operation operation = springAnnotationOperationService.buildOperation(method, payloadSchema, headerSchema); customizers.forEach(customizer -> customizer.customize(operation, method.method())); - return Map.entry(operationId, operation); + return Map.entry(operation.getOperationId(), operation); } } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfScannerConfiguration.java b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfScannerConfiguration.java index 9e3af9b0d..4bea2a9a7 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfScannerConfiguration.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfScannerConfiguration.java @@ -146,7 +146,7 @@ public OperationsScanner asyncListenerMethodLevelAnnotationOperationScanner( asyncAnnotationMessageService, stringValueResolver); val strategy = new AsyncAnnotationMethodLevelOperationsScanner<>( - asyncAnnotationProvider, asyncAnnotationOperationService, operationCustomizers, stringValueResolver); + asyncAnnotationProvider, asyncAnnotationOperationService, operationCustomizers); return new OperationsInClassScannerAdapter(springwolfClassScanner, strategy); } @@ -261,7 +261,7 @@ public OperationsScanner asyncPublisherClassLevelOperationAnnotationScanner( asyncAnnotationMessageService, stringValueResolver); val strategy = new AsyncAnnotationMethodLevelOperationsScanner<>( - asyncAnnotationProvider, asyncAnnotationOperationService, customizers, stringValueResolver); + asyncAnnotationProvider, asyncAnnotationOperationService, customizers); return new OperationsInClassScannerAdapter(springwolfClassScanner, strategy); } diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/grouping/GroupingServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/grouping/GroupingServiceTest.java index c1f45eb78..44f6e491e 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/grouping/GroupingServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/grouping/GroupingServiceTest.java @@ -71,6 +71,7 @@ class GroupingServiceTest { .build(); private final Operation sendOperation = Operation.builder() + .operationId("send") .action(OperationAction.SEND) .channel(ChannelReference.fromChannel(channel1)) .messages(List.of( @@ -78,6 +79,7 @@ class GroupingServiceTest { MessageReference.toChannelMessage(channel1.getChannelId(), message2.getMessageId()))) .build(); private final Operation receiveOperation = Operation.builder() + .operationId("receive") .action(OperationAction.RECEIVE) .channel(ChannelReference.fromChannel(channel2)) .messages(List.of(MessageReference.toChannelMessage(channel2.getChannelId(), message3.getMessageId()))) diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/channel/AsyncAnnotationChannelServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/channel/AsyncAnnotationChannelServiceTest.java index 88f9d7a12..037ab026a 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/channel/AsyncAnnotationChannelServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/channel/AsyncAnnotationChannelServiceTest.java @@ -30,6 +30,7 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -108,7 +109,7 @@ void scan() throws NoSuchMethodException { MethodAndAnnotation methodAndAnnotation = new MethodAndAnnotation<>(method, method.getAnnotation(TestListener.class)); - when(asyncAnnotationOperationService.buildOperation(any(), any(Method.class), any())) + when(asyncAnnotationOperationService.buildOperation(any(), anySet())) .thenReturn(Operation.builder().title("operationId").build()); AsyncApiDocket docket = AsyncApiDocket.builder() .info(Info.builder().build()) @@ -141,7 +142,7 @@ void scanInvalid() throws NoSuchMethodException { MethodAndAnnotation methodAndAnnotation = new MethodAndAnnotation<>(method, method.getAnnotation(TestListener.class)); - when(asyncAnnotationOperationService.buildOperation(any(), any(Method.class), any())) + when(asyncAnnotationOperationService.buildOperation(any(), anySet())) .thenReturn(Operation.builder().title("operationId").build()); AsyncApiDocket docket = AsyncApiDocket.builder() .info(Info.builder().build()) diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/AsyncAnnotationOperationServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/AsyncAnnotationOperationServiceTest.java new file mode 100644 index 000000000..edf9324c0 --- /dev/null +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/AsyncAnnotationOperationServiceTest.java @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.asyncapi.scanners.common.operation; + +import io.github.springwolf.asyncapi.v3.bindings.OperationBinding; +import io.github.springwolf.asyncapi.v3.bindings.amqp.AMQPOperationBinding; +import io.github.springwolf.asyncapi.v3.model.channel.ChannelReference; +import io.github.springwolf.asyncapi.v3.model.channel.message.MessageObject; +import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; +import io.github.springwolf.asyncapi.v3.model.operation.Operation; +import io.github.springwolf.asyncapi.v3.model.operation.OperationAction; +import io.github.springwolf.core.asyncapi.annotations.AsyncOperation; +import io.github.springwolf.core.asyncapi.scanners.bindings.operations.OperationBindingProcessor; +import io.github.springwolf.core.asyncapi.scanners.bindings.operations.ProcessedOperationBinding; +import io.github.springwolf.core.asyncapi.scanners.common.AsyncAnnotationProvider; +import io.github.springwolf.core.asyncapi.scanners.common.message.AsyncAnnotationMessageService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.util.StringValueResolver; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class AsyncAnnotationOperationServiceTest { + private final AsyncAnnotationProvider asyncAnnotationProvider = + mock(AsyncAnnotationProvider.class); + private final OperationBindingProcessor operationBindingProcessor = mock(OperationBindingProcessor.class); + private final List operationBindingProcessors = List.of(operationBindingProcessor); + private final AsyncAnnotationMessageService asyncAnnotationMessageService = + mock(AsyncAnnotationMessageService.class); + private final StringValueResolver stringValueResolver = mock(StringValueResolver.class); + + AsyncAnnotationOperationService asyncAnnotationOperationService = + new AsyncAnnotationOperationService<>( + asyncAnnotationProvider, + operationBindingProcessors, + asyncAnnotationMessageService, + stringValueResolver); + + private static final String CHANNEL_ID = "test-channel-id"; + private static final OperationBinding defaultOperationBinding = new AMQPOperationBinding(); + + @BeforeEach + void setUp() { + doAnswer(invocation -> invocation.getArgument(0)) + .when(stringValueResolver) + .resolveStringValue(any()); + + when(asyncAnnotationProvider.getOperationType()).thenReturn(OperationAction.RECEIVE); + } + + @Test + void buildOperation() { + // given + Class clazz = ClassWithTestListenerAnnotation.class; + AsyncOperation asyncOperation = + clazz.getAnnotation(TestMethodListener.class).operation(); + Set methods = Arrays.stream(clazz.getDeclaredMethods()).collect(Collectors.toSet()); + + when(asyncAnnotationProvider.getAsyncOperation(any())).thenReturn(asyncOperation); + when(asyncAnnotationMessageService.buildMessage(any(), any())) + .thenReturn(MessageObject.builder().messageId("messageId").build()); + when(operationBindingProcessor.process(any())) + .thenReturn(Optional.of(new ProcessedOperationBinding("type", defaultOperationBinding))); + + // when + Operation operation = asyncAnnotationOperationService.buildOperation(asyncOperation, methods); + + // then + assertThat(operation) + .isEqualTo(Operation.builder() + .operationId(CHANNEL_ID + "_receive_method") + .action(OperationAction.RECEIVE) + .channel(ChannelReference.fromChannel(CHANNEL_ID)) + .title(CHANNEL_ID + "_receive") + .description("Auto-generated description") + .messages(List.of(MessageReference.toChannelMessage(CHANNEL_ID, "messageId"))) + .bindings(Map.of("type", defaultOperationBinding)) + .build()); + } + + @Test + void buildOperationWithMultipleMethods() { + // given + Class clazz = ClassWithMultipleTestListenerAnnotation.class; + AsyncOperation asyncOperation = + clazz.getAnnotation(TestMethodListener.class).operation(); + Set methods = Arrays.stream(clazz.getDeclaredMethods()).collect(Collectors.toSet()); + + when(asyncAnnotationProvider.getAsyncOperation(any())).thenReturn(asyncOperation); + when(asyncAnnotationMessageService.buildMessage(any(), any())) + .thenReturn(MessageObject.builder().messageId("messageId1").build()) + .thenReturn(MessageObject.builder().messageId("messageId2").build()); + when(operationBindingProcessor.process(any())) + .thenReturn(Optional.of(new ProcessedOperationBinding("type", defaultOperationBinding))); + + // when + Operation operation = asyncAnnotationOperationService.buildOperation(asyncOperation, methods); + + // then + assertThat(operation) + .isEqualTo(Operation.builder() + .operationId(CHANNEL_ID + "_receive_method") + .action(OperationAction.RECEIVE) + .channel(ChannelReference.fromChannel(CHANNEL_ID)) + .title(CHANNEL_ID + "_receive") + .description("Test description") + .messages(List.of( + MessageReference.toChannelMessage(CHANNEL_ID, "messageId1"), + MessageReference.toChannelMessage(CHANNEL_ID, "messageId2"))) + .bindings(Map.of("type", defaultOperationBinding)) + .build()); + } + + @TestMethodListener(operation = @AsyncOperation(channelName = CHANNEL_ID)) + private static class ClassWithTestListenerAnnotation { + private void method(String payload) {} + } + + @TestMethodListener(operation = @AsyncOperation(channelName = CHANNEL_ID, description = "Test description")) + private static class ClassWithMultipleTestListenerAnnotation { + private void method(String payload) {} + + private void method(Integer payload) {} + } + + @Retention(RetentionPolicy.RUNTIME) + @interface TestMethodListener { + AsyncOperation operation(); + } +} diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationServiceTest.java index cc0b9fb48..a8b759777 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationServiceTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationServiceTest.java @@ -10,6 +10,7 @@ import io.github.springwolf.asyncapi.v3.model.operation.OperationAction; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory; +import io.github.springwolf.core.asyncapi.scanners.common.annotation.MethodAndAnnotation; import io.github.springwolf.core.asyncapi.scanners.common.message.SpringAnnotationMessageService; import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadSchemaObject; import org.junit.jupiter.api.BeforeEach; @@ -17,6 +18,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; import java.util.List; import java.util.Map; @@ -47,23 +49,25 @@ void setUp() { @Test void scan_componentHasTestListenerMethods() throws NoSuchMethodException { // given - TestMethodListener annotation = ClassWithTestListenerAnnotation.class - .getDeclaredMethod("methodWithAnnotation", String.class) - .getAnnotation(TestMethodListener.class); + Method method = ClassWithTestListenerAnnotation.class.getDeclaredMethod("methodWithAnnotation", String.class); + MethodAndAnnotation methodAndAnnotation = + new MethodAndAnnotation<>(method, method.getAnnotation(TestMethodListener.class)); PayloadSchemaObject payloadSchemaName = new PayloadSchemaObject("name", "full.name", null); SchemaObject headerSchema = new SchemaObject(); MessageObject messageObject = MessageObject.builder().messageId(String.class.getName()).build(); - when(springAnnotationMessageService.buildMessage(annotation, payloadSchemaName, headerSchema)) + when(springAnnotationMessageService.buildMessage( + methodAndAnnotation.annotation(), payloadSchemaName, headerSchema)) .thenReturn(messageObject); // when Operation operations = - springAnnotationOperationService.buildOperation(annotation, payloadSchemaName, headerSchema); + springAnnotationOperationService.buildOperation(methodAndAnnotation, payloadSchemaName, headerSchema); // then Operation expectedOperation = Operation.builder() + .operationId(CHANNEL_ID + "_receive_methodWithAnnotation") .action(OperationAction.RECEIVE) .channel(ChannelReference.fromChannel(CHANNEL_ID)) .messages(List.of(MessageReference.toChannelMessage(CHANNEL_ID, messageObject))) diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationsServiceTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationsServiceTest.java new file mode 100644 index 000000000..3cf6ccbda --- /dev/null +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/common/operation/SpringAnnotationOperationsServiceTest.java @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: Apache-2.0 +package io.github.springwolf.core.asyncapi.scanners.common.operation; + +import io.github.springwolf.asyncapi.v3.bindings.OperationBinding; +import io.github.springwolf.asyncapi.v3.bindings.amqp.AMQPOperationBinding; +import io.github.springwolf.asyncapi.v3.model.channel.ChannelReference; +import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; +import io.github.springwolf.asyncapi.v3.model.operation.Operation; +import io.github.springwolf.asyncapi.v3.model.operation.OperationAction; +import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory; +import io.github.springwolf.core.asyncapi.scanners.common.message.SpringAnnotationMessagesService; +import org.junit.jupiter.api.Test; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class SpringAnnotationOperationsServiceTest { + private final BindingFactory bindingFactory = mock(BindingFactory.class); + private final SpringAnnotationMessagesService springAnnotationMessageService = + mock(SpringAnnotationMessagesService.class); + SpringAnnotationOperationsService springAnnotationOperationsService = + new SpringAnnotationOperationsService<>(bindingFactory, springAnnotationMessageService); + + private static final String CHANNEL_ID = "test-channel-id"; + private static final Map defaultOperationBinding = + Map.of("protocol", new AMQPOperationBinding()); + + @Test + void buildOperation() { + // given + Class clazz = ClassWithTestListenerAnnotation.class; + TestMethodListener clazzAnnotation = clazz.getAnnotation(TestMethodListener.class); + Set methods = Set.of(clazz.getDeclaredMethods()); + + when(bindingFactory.getChannelId(clazzAnnotation)).thenReturn(CHANNEL_ID); + when(bindingFactory.buildOperationBinding(clazzAnnotation)).thenReturn(defaultOperationBinding); + when(springAnnotationMessageService.buildMessages( + clazzAnnotation, methods, SpringAnnotationMessagesService.MessageType.OPERATION)) + .thenReturn(Map.of("message", MessageReference.toComponentMessage("messageId"))); + + // when + Operation operation = springAnnotationOperationsService.buildOperation( + clazzAnnotation, ClassWithTestListenerAnnotation.class, methods); + + // then + assertThat(operation) + .isEqualTo(Operation.builder() + .operationId(CHANNEL_ID + "_receive_ClassWithTestListenerAnnotation") + .action(OperationAction.RECEIVE) + .channel(ChannelReference.fromChannel(CHANNEL_ID)) + .messages(List.of(MessageReference.toComponentMessage("messageId"))) + .bindings(defaultOperationBinding) + .build()); + } + + @Test + void buildOperationWithMultipleMethods() { + // given + Class clazz = ClassWithMultipleTestListenerAnnotation.class; + TestMethodListener clazzAnnotation = clazz.getAnnotation(TestMethodListener.class); + Set methods = Set.of(clazz.getDeclaredMethods()); + + when(bindingFactory.getChannelId(clazzAnnotation)).thenReturn(CHANNEL_ID); + when(bindingFactory.buildOperationBinding(clazzAnnotation)).thenReturn(defaultOperationBinding); + when(springAnnotationMessageService.buildMessages( + clazzAnnotation, methods, SpringAnnotationMessagesService.MessageType.OPERATION)) + .thenReturn(Map.of( + "message1", + MessageReference.toComponentMessage("messageId1"), + "message2", + MessageReference.toComponentMessage("messageId2"))); + + // when + Operation operation = springAnnotationOperationsService.buildOperation( + clazzAnnotation, ClassWithTestListenerAnnotation.class, methods); + + // then + assertThat(operation) + .usingRecursiveComparison() + .ignoringFields("messages") + .isEqualTo(Operation.builder() + .operationId(CHANNEL_ID + "_receive_ClassWithTestListenerAnnotation") + .action(OperationAction.RECEIVE) + .channel(ChannelReference.fromChannel(CHANNEL_ID)) + .bindings(defaultOperationBinding) + .build()); + assertThat(operation.getMessages()) + .containsExactlyInAnyOrder( + MessageReference.toComponentMessage("messageId1"), + MessageReference.toComponentMessage("messageId2")); + } + + @TestMethodListener + private static class ClassWithTestListenerAnnotation { + private void method(String payload) {} + } + + @TestMethodListener + private static class ClassWithMultipleTestListenerAnnotation { + private void method(String payload) {} + + private void method(Integer payload) {} + } + + @Retention(RetentionPolicy.RUNTIME) + @interface TestMethodListener {} +} diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/OperationMergerTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/OperationMergerTest.java index 7354f832e..5330da4dd 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/OperationMergerTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/OperationMergerTest.java @@ -34,21 +34,24 @@ void shouldNotMergeDifferentoperationIds() { } @Test - void shouldMergeEqualoperationIdsIntoOneOperation() { + void shouldMergeEqualOperationIdsIntoOneOperation() { // given String operationId = "operation"; Operation publishOperation = Operation.builder() + .operationId(operationId) .action(OperationAction.SEND) .title("publisher") .build(); Operation subscribeOperation = Operation.builder() + .operationId(operationId) .action(OperationAction.RECEIVE) .title("subscribe") .build(); // when - Map mergedOperations = OperationMerger.mergeOperations( - Arrays.asList(Map.entry(operationId, publishOperation), Map.entry(operationId, subscribeOperation))); + Map mergedOperations = OperationMerger.mergeOperations(Arrays.asList( + Map.entry(publishOperation.getOperationId(), publishOperation), + Map.entry(subscribeOperation.getOperationId(), subscribeOperation))); // then assertThat(mergedOperations).hasSize(1); @@ -58,14 +61,19 @@ void shouldMergeEqualoperationIdsIntoOneOperation() { void shouldUseFirstOperationFound() { // given String operationId = "operation"; - Operation senderOperation = - Operation.builder().action(OperationAction.SEND).build(); - Operation receiverOperation = - Operation.builder().action(OperationAction.RECEIVE).build(); + Operation senderOperation = Operation.builder() + .operationId(operationId) + .action(OperationAction.SEND) + .build(); + Operation receiverOperation = Operation.builder() + .operationId(operationId) + .action(OperationAction.RECEIVE) + .build(); // when - Map mergedOperations = OperationMerger.mergeOperations( - Arrays.asList(Map.entry(operationId, senderOperation), Map.entry(operationId, receiverOperation))); + Map mergedOperations = OperationMerger.mergeOperations(Arrays.asList( + Map.entry(senderOperation.getOperationId(), senderOperation), + Map.entry(receiverOperation.getOperationId(), receiverOperation))); // then assertThat(mergedOperations).hasSize(1).hasEntrySatisfying(operationId, it -> { @@ -95,16 +103,19 @@ void shouldMergeDifferentMessageForSameOperation() { MessageReference messageRef3 = MessageReference.toChannelMessage(channelId, message3); Operation senderOperation1 = Operation.builder() + .operationId(operationId) .action(OperationAction.SEND) .title("sender1") .messages(List.of(messageRef1)) .build(); Operation senderOperation2 = Operation.builder() + .operationId(operationId) .action(OperationAction.SEND) .title("sender2") .messages(List.of(messageRef2)) .build(); Operation senderOperation3 = Operation.builder() + .operationId(operationId) .action(OperationAction.SEND) .title("sender3") .messages(List.of(messageRef3)) @@ -112,9 +123,9 @@ void shouldMergeDifferentMessageForSameOperation() { // when Map mergedOperations = OperationMerger.mergeOperations(List.of( - Map.entry(operationId, senderOperation1), - Map.entry(operationId, senderOperation2), - Map.entry(operationId, senderOperation3))); + Map.entry(senderOperation1.getOperationId(), senderOperation1), + Map.entry(senderOperation2.getOperationId(), senderOperation2), + Map.entry(senderOperation3.getOperationId(), senderOperation3))); // then expectedMessage only includes message1 and message2. // Message3 is not included as it is identical in terms of payload type (Message#name) to message 2 diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationClassLevelOperationsScannerTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationClassLevelOperationsScannerTest.java index 98b37e883..bd76110fd 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationClassLevelOperationsScannerTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationClassLevelOperationsScannerTest.java @@ -53,9 +53,8 @@ public OperationAction getOperationType() { @Test void scan() { // given - Operation operation = Operation.builder().build(); - when(asyncAnnotationOperationService.buildOperation(any(), anySet(), any())) - .thenReturn(operation); + Operation operation = Operation.builder().operationId("operationId").build(); + when(asyncAnnotationOperationService.buildOperation(any(), anySet())).thenReturn(operation); // when Map actualOperations = operationsScanner @@ -64,16 +63,14 @@ void scan() { // then assertThat(actualOperations).hasSize(1); - assertThat(actualOperations) - .containsExactlyEntriesOf(Map.of("test-channel_send_ClassWithListenerAnnotation", operation)); + assertThat(actualOperations).containsExactlyEntriesOf(Map.of("operationId", operation)); } @Test void operationCustomizerIsCalled() { // given - Operation operation = Operation.builder().build(); - when(asyncAnnotationOperationService.buildOperation(any(), anySet(), any())) - .thenReturn(operation); + Operation operation = Operation.builder().operationId("operationId").build(); + when(asyncAnnotationOperationService.buildOperation(any(), anySet())).thenReturn(operation); // when operationsScanner.scan(ClassWithListenerAnnotation.class).toList(); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationMethodLevelOperationsScannerTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationMethodLevelOperationsScannerTest.java index c0dee8a11..1b5194d54 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationMethodLevelOperationsScannerTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationMethodLevelOperationsScannerTest.java @@ -8,17 +8,15 @@ import io.github.springwolf.core.asyncapi.scanners.common.AsyncAnnotationProvider; import io.github.springwolf.core.asyncapi.scanners.common.operation.AsyncAnnotationOperationService; import io.github.springwolf.core.asyncapi.scanners.common.utils.StringValueResolverProxy; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.lang.reflect.Method; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; +import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -49,24 +47,13 @@ public OperationAction getOperationType() { private final AsyncAnnotationMethodLevelOperationsScanner operationsScanner = new AsyncAnnotationMethodLevelOperationsScanner<>( - asyncAnnotationProvider, - asyncAnnotationOperationService, - List.of(operationCustomizer), - stringValueResolver); - - @BeforeEach - public void setup() { - doAnswer(invocation -> invocation.getArgument(0)) - .when(stringValueResolver) - .resolveStringValue(any()); - } + asyncAnnotationProvider, asyncAnnotationOperationService, List.of(operationCustomizer)); @Test void scan_componentOperationHasListenerMethod() { // given - Operation operation = Operation.builder().build(); - when(asyncAnnotationOperationService.buildOperation(any(), any(Method.class), any())) - .thenReturn(operation); + Operation operation = Operation.builder().operationId("operationId").build(); + when(asyncAnnotationOperationService.buildOperation(any(), anySet())).thenReturn(operation); // when Map actualOperations = operationsScanner @@ -74,20 +61,17 @@ void scan_componentOperationHasListenerMethod() { .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); // then - assertThat(actualOperations).containsExactly(Map.entry("test-channel_send_methodWithAnnotation", operation)); + assertThat(actualOperations).containsExactly(Map.entry("operationId", operation)); } @Test void operationCustomizerIsCalled() { // given - Operation operation = Operation.builder().build(); - when(asyncAnnotationOperationService.buildOperation(any(), any(Method.class), any())) - .thenReturn(operation); + Operation operation = Operation.builder().operationId("operationId").build(); + when(asyncAnnotationOperationService.buildOperation(any(), anySet())).thenReturn(operation); // when - operationsScanner - .scan(ClassWithListenerAnnotation.class) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + operationsScanner.scan(ClassWithListenerAnnotation.class).toList(); // then verify(operationCustomizer).customize(any(), any()); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScannerTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScannerTest.java index c0626c184..b9783d405 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScannerTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScannerTest.java @@ -1,12 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.core.asyncapi.scanners.operations.annotations; -import io.github.springwolf.asyncapi.v3.bindings.MessageBinding; -import io.github.springwolf.asyncapi.v3.bindings.amqp.AMQPMessageBinding; import io.github.springwolf.asyncapi.v3.model.operation.Operation; -import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory; import io.github.springwolf.core.asyncapi.scanners.common.operation.SpringAnnotationOperationsService; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.annotation.Retention; @@ -23,7 +19,6 @@ class SpringAnnotationClassLevelOperationsScannerTest { - private final BindingFactory bindingFactory = mock(BindingFactory.class); private final OperationCustomizer operationCustomizer = mock(OperationCustomizer.class); private final SpringAnnotationOperationsService springAnnotationOperationsService = mock(SpringAnnotationOperationsService.class); @@ -31,40 +26,30 @@ class SpringAnnotationClassLevelOperationsScannerTest { new SpringAnnotationClassLevelOperationsScanner<>( TestClassListener.class, TestMethodListener.class, - bindingFactory, springAnnotationOperationsService, List.of(operationCustomizer)); - private static final String CHANNEL_NAME_ID = "test-channel"; - - private static final Map defaultMessageBinding = - Map.of("protocol", new AMQPMessageBinding()); - - @BeforeEach - void setUp() { - when(bindingFactory.getChannelId(any())).thenReturn(CHANNEL_NAME_ID); - } - @Test void scan() { // given - Operation operation = Operation.builder().build(); - when(springAnnotationOperationsService.buildOperation(any(), anySet())).thenReturn(operation); + Operation operation = Operation.builder().operationId("operationId").build(); + when(springAnnotationOperationsService.buildOperation(any(), any(), anySet())) + .thenReturn(operation); // when List> operations = scanner.scan(ClassWithTestListenerAnnotation.class).toList(); // then - String operationName = CHANNEL_NAME_ID + "_receive_ClassWithTestListenerAnnotation"; - assertThat(operations).containsExactly(Map.entry(operationName, operation)); + assertThat(operations).containsExactly(Map.entry("operationId", operation)); } @Test void operationCustomizerIsCalled() { // given - Operation operation = Operation.builder().build(); - when(springAnnotationOperationsService.buildOperation(any(), anySet())).thenReturn(operation); + Operation operation = Operation.builder().operationId("operationId").build(); + when(springAnnotationOperationsService.buildOperation(any(), any(), anySet())) + .thenReturn(operation); // when scanner.scan(ClassWithTestListenerAnnotation.class).toList(); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScannerTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScannerTest.java index 6a1090d08..7fe423fa3 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScannerTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScannerTest.java @@ -2,11 +2,9 @@ package io.github.springwolf.core.asyncapi.scanners.operations.annotations; import io.github.springwolf.asyncapi.v3.model.operation.Operation; -import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory; import io.github.springwolf.core.asyncapi.scanners.common.headers.HeaderClassExtractor; import io.github.springwolf.core.asyncapi.scanners.common.operation.SpringAnnotationOperationService; import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadMethodParameterService; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.annotation.Retention; @@ -24,30 +22,21 @@ class SpringAnnotationMethodLevelOperationsScannerTest { private final PayloadMethodParameterService payloadMethodParameterService = mock(); private final HeaderClassExtractor headerClassExtractor = mock(HeaderClassExtractor.class); - private final BindingFactory bindingFactory = mock(BindingFactory.class); private final OperationCustomizer operationCustomizer = mock(OperationCustomizer.class); private final SpringAnnotationOperationService springAnnotationOperationService = mock(SpringAnnotationOperationService.class); SpringAnnotationMethodLevelOperationsScanner scanner = new SpringAnnotationMethodLevelOperationsScanner<>( TestMethodListener.class, - bindingFactory, headerClassExtractor, payloadMethodParameterService, springAnnotationOperationService, List.of(operationCustomizer)); - private static final String CHANNEL_ID = "test-channel"; - - @BeforeEach - void setUp() { - when(bindingFactory.getChannelId(any())).thenReturn(CHANNEL_ID); - } - @Test void scan_componentHasTestListenerMethods() { // given - Operation operation = Operation.builder().build(); + Operation operation = Operation.builder().operationId("operationId").build(); when(springAnnotationOperationService.buildOperation(any(), any(), any())) .thenReturn(operation); @@ -56,13 +45,13 @@ void scan_componentHasTestListenerMethods() { scanner.scan(ClassWithTestListenerAnnotation.class).toList(); // then - String operationName = CHANNEL_ID + "_receive_methodWithAnnotation"; - assertThat(operations).containsExactly(Map.entry(operationName, operation)); + assertThat(operations).containsExactly(Map.entry("operationId", operation)); } @Test - void operationCustomizerIsCalled() { // given - Operation operation = Operation.builder().build(); + void operationCustomizerIsCalled() { + // given + Operation operation = Operation.builder().operationId("operationId").build(); when(springAnnotationOperationService.buildOperation(any(), any(), any())) .thenReturn(operation); diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentIntegrationTest.java index 4e42053d8..4ecef15dc 100644 --- a/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/springwolf/core/integrationtests/AsyncApiDocumentIntegrationTest.java @@ -54,7 +54,7 @@ void asyncListenerAnnotationIsFound() { "listener-channel_receive_listen2", "listener-channel_receive_listen3", "listener-channel_receive_listen4", - "listener-class-channel_receive_ClassListener"); + "listener-class-channel_receive_listen"); assertThat(asyncAPI.getComponents().getMessages()) .containsOnlyKeys( "java.lang.String", @@ -119,7 +119,7 @@ void asyncPublisherAnnotationIsFound() { "publisher-channel_send_publish2", "publisher-channel_send_publish3", "publisher-channel_send_publish4", - "publisher-class-channel_send_ClassPublisher"); + "publisher-class-channel_send_publish"); assertThat(asyncAPI.getComponents().getMessages()) .containsOnlyKeys( "java.lang.String", diff --git a/springwolf-examples/springwolf-cloud-stream-example/src/test/resources/asyncapi.json b/springwolf-examples/springwolf-cloud-stream-example/src/test/resources/asyncapi.json index b5206b668..5dad9af52 100644 --- a/springwolf-examples/springwolf-cloud-stream-example/src/test/resources/asyncapi.json +++ b/springwolf-examples/springwolf-cloud-stream-example/src/test/resources/asyncapi.json @@ -296,7 +296,7 @@ } }, "operations": { - "another-topic_subscribe_process": { + "another-topic_send_process": { "action": "send", "channel": { "$ref": "#/channels/another-topic" @@ -311,7 +311,7 @@ } ] }, - "biconsumer-topic_publish_biConsumerMethod": { + "biconsumer-topic_receive_biConsumerMethod": { "action": "receive", "channel": { "$ref": "#/channels/biconsumer-topic" @@ -326,7 +326,7 @@ } ] }, - "bifunction-output-topic_subscribe_biProcess": { + "bifunction-output-topic_send_biProcess": { "action": "send", "channel": { "$ref": "#/channels/bifunction-output-topic" @@ -341,7 +341,7 @@ } ] }, - "bifunction-topic_publish_biProcess": { + "bifunction-topic_receive_biProcess": { "action": "receive", "channel": { "$ref": "#/channels/bifunction-topic" @@ -356,7 +356,7 @@ } ] }, - "consumer-class-topic_publish_ConsumerClass": { + "consumer-class-topic_receive_ConsumerClass": { "action": "receive", "channel": { "$ref": "#/channels/consumer-class-topic" @@ -371,7 +371,7 @@ } ] }, - "consumer-topic_publish_consumerMethod": { + "consumer-topic_receive_consumerMethod": { "action": "receive", "channel": { "$ref": "#/channels/consumer-topic" @@ -386,7 +386,7 @@ } ] }, - "example-topic_publish_process": { + "example-topic_receive_process": { "action": "receive", "channel": { "$ref": "#/channels/example-topic" @@ -401,7 +401,7 @@ } ] }, - "google-pubsub-topic_publish_googlePubSubConsumerMethod": { + "google-pubsub-topic_receive_googlePubSubConsumerMethod": { "action": "receive", "channel": { "$ref": "#/channels/google-pubsub-topic" diff --git a/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/configuration/SpringwolfAmqpScannerConfiguration.java b/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/configuration/SpringwolfAmqpScannerConfiguration.java index 30195f6a6..0c86b64cf 100644 --- a/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/configuration/SpringwolfAmqpScannerConfiguration.java +++ b/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/configuration/SpringwolfAmqpScannerConfiguration.java @@ -123,7 +123,6 @@ public OperationsScanner simpleRabbitClassLevelListenerAnnotationOperationsScann new SpringAnnotationClassLevelOperationsScanner<>( RabbitListener.class, RabbitHandler.class, - amqpBindingFactory, springAnnotationOperationsService, operationCustomizers); @@ -178,7 +177,6 @@ public OperationsScanner simpleRabbitMethodLevelListenerAnnotationOperationsScan SpringAnnotationMethodLevelOperationsScanner strategy = new SpringAnnotationMethodLevelOperationsScanner<>( RabbitListener.class, - amqpBindingFactory, headerClassExtractor, payloadMethodParameterService, springAnnotationOperationService, diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java index 23975100e..640c2e546 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/common/FunctionalChannelBeanData.java @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.springwolf.plugins.cloudstream.asyncapi.scanners.common; +import io.github.springwolf.asyncapi.v3.model.operation.OperationAction; + import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Type; @@ -20,6 +22,13 @@ public record FunctionalChannelBeanData( public enum BeanType { CONSUMER, - SUPPLIER + SUPPLIER; + + public OperationAction toOperationAction() { + return switch (this) { + case CONSUMER -> OperationAction.RECEIVE; + case SUPPLIER -> OperationAction.SEND; + }; + } } } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java index 4aa1f0737..6ce7118ac 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/main/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/operations/CloudStreamFunctionOperationsScanner.java @@ -12,7 +12,6 @@ import io.github.springwolf.asyncapi.v3.model.channel.message.MessagePayload; import io.github.springwolf.asyncapi.v3.model.channel.message.MessageReference; import io.github.springwolf.asyncapi.v3.model.operation.Operation; -import io.github.springwolf.asyncapi.v3.model.operation.OperationAction; import io.github.springwolf.asyncapi.v3.model.schema.MultiFormatSchema; import io.github.springwolf.asyncapi.v3.model.server.Server; import io.github.springwolf.core.asyncapi.components.ComponentsService; @@ -22,6 +21,7 @@ import io.github.springwolf.core.asyncapi.scanners.common.headers.AsyncHeadersNotDocumented; import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadSchemaObject; import io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadService; +import io.github.springwolf.core.asyncapi.scanners.operations.OperationIdHelper; import io.github.springwolf.core.asyncapi.scanners.operations.OperationMerger; import io.github.springwolf.core.configuration.docket.AsyncApiDocket; import io.github.springwolf.core.configuration.docket.AsyncApiDocketService; @@ -71,19 +71,20 @@ private boolean isChannelBean(FunctionalChannelBeanData beanData) { } private Map.Entry toOperationEntry(FunctionalChannelBeanData beanData) { + Operation operation = buildOperation(beanData); + + return Map.entry(operation.getOperationId(), operation); + } + + private Operation buildOperation(FunctionalChannelBeanData beanData) { String channelName = cloudStreamBindingsProperties .getBindings() .get(beanData.cloudStreamBinding()) .getDestination(); String channelId = ReferenceUtil.toValidId(channelName); + String operationId = OperationIdHelper.createOperationId( + channelId, beanData.beanType().toOperationAction(), beanData.elementName()); - String operationId = buildOperationId(beanData, channelId); - Operation operation = buildOperation(beanData, channelId); - - return Map.entry(operationId, operation); - } - - private Operation buildOperation(FunctionalChannelBeanData beanData, String channelId) { Type payloadType = beanData.payloadType(); PayloadSchemaObject payloadSchema = payloadService.buildSchema(payloadType); MessagePayload payload = MessagePayload.of( @@ -100,18 +101,14 @@ private Operation buildOperation(FunctionalChannelBeanData beanData, String chan .bindings(buildMessageBinding()) .build(); - var builder = Operation.builder() + return Operation.builder() + .operationId(operationId) + .action(beanData.beanType().toOperationAction()) .description("Auto-generated description") .channel(ChannelReference.fromChannel(channelId)) .messages(List.of(MessageReference.toChannelMessage(channelId, message))) - .bindings(buildOperationBinding()); - if (beanData.beanType() == FunctionalChannelBeanData.BeanType.CONSUMER) { - builder.action(OperationAction.RECEIVE); - } else { - builder.action(OperationAction.SEND); - } - - return builder.build(); + .bindings(buildOperationBinding()) + .build(); } private Map buildMessageBinding() { @@ -140,11 +137,4 @@ private String getProtocolName() { .orElseThrow(() -> new IllegalStateException("There must be at least one server define in the AsyncApiDocker")); } - - private String buildOperationId(FunctionalChannelBeanData beanData, String channelId) { - String operationName = - beanData.beanType() == FunctionalChannelBeanData.BeanType.CONSUMER ? "publish" : "subscribe"; - - return String.format("%s_%s_%s", channelId, operationName, beanData.elementName()); - } } diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java index 5dafa1407..7d86f66ab 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/springwolf/plugins/cloudstream/asyncapi/scanners/channels/CloudStreamFunctionChannelsScannerIntegrationTest.java @@ -152,6 +152,7 @@ void testConsumerBinding() { .build(); Operation expectedOperation = Operation.builder() + .operationId("test-consumer-input-topic_receive_testConsumer") .action(OperationAction.RECEIVE) .bindings(operationBinding) .description("Auto-generated description") @@ -161,7 +162,7 @@ void testConsumerBinding() { assertThat(actualChannels).containsExactly(Map.entry(topicName, expectedChannel)); assertThat(actualOperations) - .containsExactly(Map.entry("test-consumer-input-topic_publish_testConsumer", expectedOperation)); + .containsExactly(Map.entry("test-consumer-input-topic_receive_testConsumer", expectedOperation)); assertThat(componentsService.getMessages()).contains(Map.entry(String.class.getName(), message)); } @@ -197,6 +198,7 @@ void testBiConsumerBinding() { .build(); Operation expectedOperation = Operation.builder() + .operationId("test-biconsumer-input-topic_receive_testBiConsumer") .action(OperationAction.RECEIVE) .bindings(operationBinding) .description("Auto-generated description") @@ -206,7 +208,7 @@ void testBiConsumerBinding() { assertThat(actualChannels).containsExactly(Map.entry(topicName, expectedChannel)); assertThat(actualOperations) - .containsExactly(Map.entry("test-biconsumer-input-topic_publish_testBiConsumer", expectedOperation)); + .containsExactly(Map.entry("test-biconsumer-input-topic_receive_testBiConsumer", expectedOperation)); assertThat(componentsService.getMessages()).contains(Map.entry(String.class.getName(), message)); } @@ -236,7 +238,7 @@ void testSupplierBinding() { .build(); Operation expectedOperation = Operation.builder() - .bindings(operationBinding) + .operationId("test-supplier-output-topic_send_testSupplier") .action(OperationAction.SEND) .bindings(operationBinding) .description("Auto-generated description") @@ -253,7 +255,7 @@ void testSupplierBinding() { assertThat(actualChannels).containsExactly(Map.entry(topicName, expectedChannel)); assertThat(actualOperations) - .containsExactly(Map.entry("test-supplier-output-topic_subscribe_testSupplier", expectedOperation)); + .containsExactly(Map.entry("test-supplier-output-topic_send_testSupplier", expectedOperation)); assertThat(componentsService.getMessages()).contains(Map.entry(String.class.getName(), message)); } @@ -279,10 +281,10 @@ void testFunctionBinding() { // Then the returned channels contain a publish ChannelItem and a subscribe ChannelItem MessageObject subscribeMessage = MessageObject.builder() - .name(Integer.class.getName()) - .title(Integer.class.getSimpleName()) + .name(String.class.getName()) + .title(String.class.getSimpleName()) .payload(MessagePayload.of(MultiFormatSchema.builder() - .schema(SchemaReference.fromSchema(Number.class.getSimpleName())) + .schema(SchemaReference.fromSchema(String.class.getName())) .build())) .headers(MessageHeaders.of( MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getTitle()))) @@ -290,27 +292,27 @@ void testFunctionBinding() { .build(); Operation subscribeOperation = Operation.builder() - .bindings(operationBinding) - .action(OperationAction.SEND) + .operationId("test-in-topic_receive_testFunction") + .action(OperationAction.RECEIVE) .bindings(operationBinding) .description("Auto-generated description") - .channel(ChannelReference.fromChannel(outputTopicName)) - .messages(List.of(MessageReference.toChannelMessage(outputTopicName, subscribeMessage))) + .channel(ChannelReference.fromChannel(inputTopicName)) + .messages(List.of(MessageReference.toChannelMessage(inputTopicName, subscribeMessage))) .build(); ChannelObject subscribeChannel = ChannelObject.builder() - .channelId(outputTopicName) - .address(outputTopicName) + .channelId(inputTopicName) + .address(inputTopicName) .bindings(channelBinding) .messages( Map.of(subscribeMessage.getMessageId(), MessageReference.toComponentMessage(subscribeMessage))) .build(); MessageObject publishMessage = MessageObject.builder() - .name(String.class.getName()) - .title(String.class.getSimpleName()) + .name(Integer.class.getName()) + .title(Integer.class.getSimpleName()) .payload(MessagePayload.of(MultiFormatSchema.builder() - .schema(SchemaReference.fromSchema(String.class.getName())) + .schema(SchemaReference.fromSchema(Number.class.getSimpleName())) .build())) .headers(MessageHeaders.of( MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getTitle()))) @@ -318,27 +320,27 @@ void testFunctionBinding() { .build(); Operation publishOperation = Operation.builder() - .bindings(operationBinding) - .action(OperationAction.RECEIVE) + .operationId("test-out-topic_send_testFunction") + .action(OperationAction.SEND) .bindings(operationBinding) .description("Auto-generated description") - .channel(ChannelReference.fromChannel(inputTopicName)) - .messages(List.of(MessageReference.toChannelMessage(inputTopicName, publishMessage))) + .channel(ChannelReference.fromChannel(outputTopicName)) + .messages(List.of(MessageReference.toChannelMessage(outputTopicName, publishMessage))) .build(); ChannelObject publishChannel = ChannelObject.builder() - .channelId(inputTopicName) - .address(inputTopicName) + .channelId(outputTopicName) + .address(outputTopicName) .bindings(channelBinding) .messages(Map.of(publishMessage.getMessageId(), MessageReference.toComponentMessage(publishMessage))) .build(); assertThat(actualChannels) - .contains(Map.entry(inputTopicName, publishChannel), Map.entry(outputTopicName, subscribeChannel)); + .contains(Map.entry(inputTopicName, subscribeChannel), Map.entry(outputTopicName, publishChannel)); assertThat(actualOperations) .contains( - Map.entry("test-in-topic_publish_testFunction", publishOperation), - Map.entry("test-out-topic_subscribe_testFunction", subscribeOperation)); + Map.entry("test-in-topic_receive_testFunction", subscribeOperation), + Map.entry("test-out-topic_send_testFunction", publishOperation)); } @Test @@ -363,10 +365,10 @@ void testBiFunctionBinding() { // Then the returned channels contain a publish ChannelItem and a subscribe ChannelItem MessageObject subscribeMessage = MessageObject.builder() - .name(Integer.class.getName()) - .title(Integer.class.getSimpleName()) + .name(String.class.getName()) + .title(String.class.getSimpleName()) .payload(MessagePayload.of(MultiFormatSchema.builder() - .schema(SchemaReference.fromSchema(Number.class.getSimpleName())) + .schema(SchemaReference.fromSchema(String.class.getName())) .build())) .headers(MessageHeaders.of( MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getTitle()))) @@ -374,27 +376,27 @@ void testBiFunctionBinding() { .build(); Operation subscribeOperation = Operation.builder() - .bindings(operationBinding) - .action(OperationAction.SEND) + .operationId("test-in-topic_receive_testBiFunction") + .action(OperationAction.RECEIVE) .bindings(operationBinding) .description("Auto-generated description") - .channel(ChannelReference.fromChannel(outputTopicName)) - .messages(List.of(MessageReference.toChannelMessage(outputTopicName, subscribeMessage))) + .channel(ChannelReference.fromChannel(inputTopicName)) + .messages(List.of(MessageReference.toChannelMessage(inputTopicName, subscribeMessage))) .build(); ChannelObject subscribeChannel = ChannelObject.builder() - .channelId(outputTopicName) - .address(outputTopicName) + .channelId(inputTopicName) + .address(inputTopicName) .bindings(channelBinding) .messages( Map.of(subscribeMessage.getMessageId(), MessageReference.toComponentMessage(subscribeMessage))) .build(); MessageObject publishMessage = MessageObject.builder() - .name(String.class.getName()) - .title(String.class.getSimpleName()) + .name(Integer.class.getName()) + .title(Integer.class.getSimpleName()) .payload(MessagePayload.of(MultiFormatSchema.builder() - .schema(SchemaReference.fromSchema(String.class.getName())) + .schema(SchemaReference.fromSchema(Number.class.getSimpleName())) .build())) .headers(MessageHeaders.of( MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getTitle()))) @@ -402,27 +404,27 @@ void testBiFunctionBinding() { .build(); Operation publishOperation = Operation.builder() - .bindings(operationBinding) - .action(OperationAction.RECEIVE) + .operationId("test-out-topic_send_testBiFunction") + .action(OperationAction.SEND) .bindings(operationBinding) .description("Auto-generated description") - .channel(ChannelReference.fromChannel(inputTopicName)) - .messages(List.of(MessageReference.toChannelMessage(inputTopicName, publishMessage))) + .channel(ChannelReference.fromChannel(outputTopicName)) + .messages(List.of(MessageReference.toChannelMessage(outputTopicName, publishMessage))) .build(); ChannelObject publishChannel = ChannelObject.builder() - .channelId(inputTopicName) - .address(inputTopicName) + .channelId(outputTopicName) + .address(outputTopicName) .bindings(channelBinding) .messages(Map.of(publishMessage.getMessageId(), MessageReference.toComponentMessage(publishMessage))) .build(); assertThat(actualChannels) - .contains(Map.entry(inputTopicName, publishChannel), Map.entry(outputTopicName, subscribeChannel)); + .contains(Map.entry(outputTopicName, publishChannel), Map.entry(inputTopicName, subscribeChannel)); assertThat(actualOperations) .contains( - Map.entry("test-in-topic_publish_testBiFunction", publishOperation), - Map.entry("test-out-topic_subscribe_testBiFunction", subscribeOperation)); + Map.entry("test-in-topic_receive_testBiFunction", subscribeOperation), + Map.entry("test-out-topic_send_testBiFunction", publishOperation)); } @Test @@ -447,13 +449,10 @@ void testKStreamFunctionBinding() { // Then the returned channels contain a publish ChannelItem and a subscribe ChannelItem MessageObject subscribeMessage = MessageObject.builder() - .name(Integer.class.getName()) - .title("integer") + .name(String.class.getName()) + .title("string") .payload(MessagePayload.of(MultiFormatSchema.builder() - .schema(SchemaObject.builder() - .type(SchemaType.INTEGER) - .format("int32") - .build()) + .schema(SchemaObject.builder().type(SchemaType.STRING).build()) .build())) .headers(MessageHeaders.of( MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getTitle()))) @@ -461,27 +460,30 @@ void testKStreamFunctionBinding() { .build(); Operation subscribeOperation = Operation.builder() - .bindings(operationBinding) - .action(OperationAction.SEND) + .operationId("test-in-topic_receive_kStreamTestFunction") + .action(OperationAction.RECEIVE) .bindings(operationBinding) .description("Auto-generated description") - .channel(ChannelReference.fromChannel(outputTopicName)) - .messages(List.of(MessageReference.toChannelMessage(outputTopicName, subscribeMessage))) + .channel(ChannelReference.fromChannel(inputTopicName)) + .messages(List.of(MessageReference.toChannelMessage(inputTopicName, subscribeMessage))) .build(); ChannelObject subscribeChannel = ChannelObject.builder() - .channelId(outputTopicName) - .address(outputTopicName) + .channelId(inputTopicName) + .address(inputTopicName) .bindings(channelBinding) .messages( Map.of(subscribeMessage.getMessageId(), MessageReference.toComponentMessage(subscribeMessage))) .build(); MessageObject publishMessage = MessageObject.builder() - .name(String.class.getName()) - .title("string") + .name(Integer.class.getName()) + .title("integer") .payload(MessagePayload.of(MultiFormatSchema.builder() - .schema(SchemaObject.builder().type(SchemaType.STRING).build()) + .schema(SchemaObject.builder() + .type(SchemaType.INTEGER) + .format("int32") + .build()) .build())) .headers(MessageHeaders.of( MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getTitle()))) @@ -489,29 +491,29 @@ void testKStreamFunctionBinding() { .build(); Operation publishOperation = Operation.builder() - .bindings(operationBinding) - .action(OperationAction.RECEIVE) + .operationId("test-out-topic_send_kStreamTestFunction") + .action(OperationAction.SEND) .bindings(operationBinding) .description("Auto-generated description") - .channel(ChannelReference.fromChannel(inputTopicName)) - .messages(List.of(MessageReference.toChannelMessage(inputTopicName, publishMessage))) + .channel(ChannelReference.fromChannel(outputTopicName)) + .messages(List.of(MessageReference.toChannelMessage(outputTopicName, publishMessage))) .build(); ChannelObject publishChannel = ChannelObject.builder() - .channelId(inputTopicName) - .address(inputTopicName) + .channelId(outputTopicName) + .address(outputTopicName) .bindings(channelBinding) .messages(Map.of(publishMessage.getMessageId(), MessageReference.toComponentMessage(publishMessage))) .build(); assertThat(actualChannels) - .contains(Map.entry(inputTopicName, publishChannel), Map.entry(outputTopicName, subscribeChannel)); + .contains(Map.entry(outputTopicName, publishChannel), Map.entry(inputTopicName, subscribeChannel)); assertThat(actualOperations) .contains( - Map.entry("test-in-topic_publish_kStreamTestFunction", publishOperation), - Map.entry("test-out-topic_subscribe_kStreamTestFunction", subscribeOperation)); - assertThat(componentsService.getMessages()).contains(Map.entry(String.class.getName(), publishMessage)); - assertThat(componentsService.getMessages()).contains(Map.entry(Integer.class.getName(), subscribeMessage)); + Map.entry("test-in-topic_receive_kStreamTestFunction", subscribeOperation), + Map.entry("test-out-topic_send_kStreamTestFunction", publishOperation)); + assertThat(componentsService.getMessages()).contains(Map.entry(String.class.getName(), subscribeMessage)); + assertThat(componentsService.getMessages()).contains(Map.entry(Integer.class.getName(), publishMessage)); } @Test @@ -535,13 +537,10 @@ void testFunctionBindingWithSameTopicName() { // Then the returned merged channels contain a publish operation and a subscribe operation MessageObject subscribeMessage = MessageObject.builder() - .name(Integer.class.getName()) - .title("integer") + .name(String.class.getName()) + .title("string") .payload(MessagePayload.of(MultiFormatSchema.builder() - .schema(SchemaObject.builder() - .type(SchemaType.INTEGER) - .format("int32") - .build()) + .schema(SchemaObject.builder().type(SchemaType.STRING).build()) .build())) .headers(MessageHeaders.of( MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getTitle()))) @@ -549,8 +548,8 @@ void testFunctionBindingWithSameTopicName() { .build(); Operation subscribeOperation = Operation.builder() - .bindings(operationBinding) - .action(OperationAction.SEND) + .operationId("test-topic_receive_testFunction") + .action(OperationAction.RECEIVE) .bindings(operationBinding) .description("Auto-generated description") .channel(ChannelReference.fromChannel(topicName)) @@ -558,20 +557,21 @@ void testFunctionBindingWithSameTopicName() { .build(); MessageObject publishMessage = MessageObject.builder() - .name(String.class.getName()) - .title("string") + .name(Integer.class.getName()) + .title("integer") .payload(MessagePayload.of(MultiFormatSchema.builder() - .schema(SchemaObject.builder().type(SchemaType.STRING).build()) + .schema(SchemaObject.builder() + .type(SchemaType.INTEGER) + .format("int32") + .build()) .build())) .headers(MessageHeaders.of( MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getTitle()))) .bindings(Map.of("kafka", new EmptyMessageBinding())) .build(); - - // "test-topic_publish_testFunction" Operation publishOperation = Operation.builder() - .bindings(operationBinding) - .action(OperationAction.RECEIVE) + .operationId("test-topic_send_testFunction") + .action(OperationAction.SEND) .bindings(operationBinding) .description("Auto-generated description") .channel(ChannelReference.fromChannel(topicName)) @@ -592,10 +592,10 @@ void testFunctionBindingWithSameTopicName() { assertThat(actualChannels).contains(Map.entry(topicName, mergedChannel)); assertThat(actualOperations) .contains( - Map.entry("test-topic_publish_testFunction", publishOperation), - Map.entry("test-topic_subscribe_testFunction", subscribeOperation)); - assertThat(componentsService.getMessages()).contains(Map.entry(String.class.getName(), publishMessage)); - assertThat(componentsService.getMessages()).contains(Map.entry(Integer.class.getName(), subscribeMessage)); + Map.entry("test-topic_send_testFunction", publishOperation), + Map.entry("test-topic_receive_testFunction", subscribeOperation)); + assertThat(componentsService.getMessages()).contains(Map.entry(String.class.getName(), subscribeMessage)); + assertThat(componentsService.getMessages()).contains(Map.entry(Integer.class.getName(), publishMessage)); } @TestConfiguration diff --git a/springwolf-plugins/springwolf-jms-plugin/src/main/java/io/github/springwolf/plugins/jms/configuration/SpringwolfJmsScannerConfiguration.java b/springwolf-plugins/springwolf-jms-plugin/src/main/java/io/github/springwolf/plugins/jms/configuration/SpringwolfJmsScannerConfiguration.java index 80258565a..c041d446d 100644 --- a/springwolf-plugins/springwolf-jms-plugin/src/main/java/io/github/springwolf/plugins/jms/configuration/SpringwolfJmsScannerConfiguration.java +++ b/springwolf-plugins/springwolf-jms-plugin/src/main/java/io/github/springwolf/plugins/jms/configuration/SpringwolfJmsScannerConfiguration.java @@ -82,7 +82,6 @@ public OperationsScanner simpleJmsMethodLevelListenerAnnotationOperationsScanner SpringAnnotationMethodLevelOperationsScanner strategy = new SpringAnnotationMethodLevelOperationsScanner<>( JmsListener.class, - jmsBindingFactory, headerClassExtractor, payloadMethodParameterService, springAnnotationOperationService, diff --git a/springwolf-plugins/springwolf-kafka-plugin/src/main/java/io/github/springwolf/plugins/kafka/configuration/SpringwolfKafkaScannerConfiguration.java b/springwolf-plugins/springwolf-kafka-plugin/src/main/java/io/github/springwolf/plugins/kafka/configuration/SpringwolfKafkaScannerConfiguration.java index 0e1bf5867..4342edc04 100644 --- a/springwolf-plugins/springwolf-kafka-plugin/src/main/java/io/github/springwolf/plugins/kafka/configuration/SpringwolfKafkaScannerConfiguration.java +++ b/springwolf-plugins/springwolf-kafka-plugin/src/main/java/io/github/springwolf/plugins/kafka/configuration/SpringwolfKafkaScannerConfiguration.java @@ -115,7 +115,6 @@ public OperationsScanner simpleKafkaClassLevelListenerAnnotationOperationScanner new SpringAnnotationClassLevelOperationsScanner<>( KafkaListener.class, KafkaHandler.class, - kafkaBindingFactory, springAnnotationOperationsService, operationCustomizers); @@ -169,7 +168,6 @@ public OperationsScanner simpleKafkaMethodLevelListenerAnnotationOperationsScann SpringAnnotationMethodLevelOperationsScanner strategy = new SpringAnnotationMethodLevelOperationsScanner<>( KafkaListener.class, - kafkaBindingFactory, headerClassExtractor, payloadMethodParameterService, springAnnotationOperationService, diff --git a/springwolf-plugins/springwolf-sqs-plugin/src/main/java/io/github/springwolf/plugins/sqs/configuration/SpringwolfSqsScannerConfiguration.java b/springwolf-plugins/springwolf-sqs-plugin/src/main/java/io/github/springwolf/plugins/sqs/configuration/SpringwolfSqsScannerConfiguration.java index 4360ef137..25d7ff1ab 100644 --- a/springwolf-plugins/springwolf-sqs-plugin/src/main/java/io/github/springwolf/plugins/sqs/configuration/SpringwolfSqsScannerConfiguration.java +++ b/springwolf-plugins/springwolf-sqs-plugin/src/main/java/io/github/springwolf/plugins/sqs/configuration/SpringwolfSqsScannerConfiguration.java @@ -81,7 +81,6 @@ public OperationsScanner simpleSqsMethodLevelListenerAnnotationOperationsScanner SpringAnnotationMethodLevelOperationsScanner strategy = new SpringAnnotationMethodLevelOperationsScanner<>( SqsListener.class, - sqsBindingFactory, headerClassExtractor, payloadMethodParameterService, springAnnotationOperationService, diff --git a/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/configuration/SpringwolfStompScannerConfiguration.java b/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/configuration/SpringwolfStompScannerConfiguration.java index 8a7eac8ee..cde6be40a 100644 --- a/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/configuration/SpringwolfStompScannerConfiguration.java +++ b/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/configuration/SpringwolfStompScannerConfiguration.java @@ -162,7 +162,6 @@ public OperationsScanner simpleStompClassLevelListenerAnnotationOperationScanner new SpringAnnotationClassLevelOperationsScanner<>( MessageMapping.class, AllMethods.class, - stompBindingMessageMappingFactory, springAnnotationOperationsService, operationCustomizers); @@ -216,7 +215,6 @@ public OperationsScanner simpleStompMethodLevelListenerAnnotationOperationsScann SpringAnnotationMethodLevelOperationsScanner strategy = new SpringAnnotationMethodLevelOperationsScanner<>( MessageMapping.class, - stompBindingMessageMappingFactory, headerClassExtractor, payloadMethodParameterService, springAnnotationOperationService,