From 1f369d9a0c8094914e1d1afb43214ef05be38e92 Mon Sep 17 00:00:00 2001 From: Timon Back Date: Tue, 27 Aug 2024 18:47:10 +0200 Subject: [PATCH] feat(amqp): implement amqp binding proposal --- .../asyncapi/v3/model/ReferenceUtil.java | 2 +- .../scanners/bindings/BindingFactory.java | 6 +++- ...ngAnnotationClassLevelChannelsScanner.java | 2 +- ...gAnnotationMethodLevelChannelsScanner.java | 2 +- .../common/ClassLevelAnnotationScanner.java | 4 +-- .../scanners/common/MessageHelper.java | 8 ++--- ...AnnotationClassLevelOperationsScanner.java | 8 ++--- ...nnotationMethodLevelOperationsScanner.java | 6 ++-- .../scanners/bindings/AmqpBindingFactory.java | 5 +++ .../scanners/bindings/RabbitListenerUtil.java | 31 ++++++++++++++++++- .../channels/RabbitQueueBeanScanner.java | 4 +-- .../annotations/SendToCustomizer.java | 2 +- .../annotations/SendToUserCustomizer.java | 2 +- 13 files changed, 59 insertions(+), 23 deletions(-) diff --git a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/ReferenceUtil.java b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/ReferenceUtil.java index 2768130ec..9749d4197 100644 --- a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/ReferenceUtil.java +++ b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/ReferenceUtil.java @@ -5,6 +5,6 @@ public class ReferenceUtil { private static final String FORBIDDEN_ID_CHARACTER = "/"; public static String toValidId(String name) { - return name.replaceAll(FORBIDDEN_ID_CHARACTER, "_"); + return name.replaceAll(FORBIDDEN_ID_CHARACTER, "_"); // TODO: easier to verify correct usage +"_id"; } } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/BindingFactory.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/BindingFactory.java index ceafa3d7e..5ca9bea32 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/BindingFactory.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/bindings/BindingFactory.java @@ -4,12 +4,16 @@ import io.github.springwolf.asyncapi.v3.bindings.ChannelBinding; import io.github.springwolf.asyncapi.v3.bindings.MessageBinding; import io.github.springwolf.asyncapi.v3.bindings.OperationBinding; +import io.github.springwolf.asyncapi.v3.model.ReferenceUtil; import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject; import java.util.Map; public interface BindingFactory { - String getChannelName(T annotation); + default String getChannelId(T annotation) { + return ReferenceUtil.toValidId(getChannelName(annotation)); + } + String getChannelName(T annotation); Map buildChannelBinding(T annotation); diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScanner.java index 0ed3c95c2..806e1ed28 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScanner.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScanner.java @@ -80,7 +80,7 @@ private ChannelObject buildChannelItem(ClassAnnotation classAnnotation, Map chBinding = channelBinding != null ? new HashMap<>(channelBinding) : null; String channelName = bindingFactory.getChannelName(classAnnotation); return ChannelObject.builder() - .channelId(ReferenceUtil.toValidId(channelName)) + .channelId(bindingFactory.getChannelId(classAnnotation)) .address(channelName) .bindings(chBinding) .messages(new HashMap<>(messages)) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScanner.java index fea8f2f95..ffa191a27 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScanner.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScanner.java @@ -82,7 +82,7 @@ private ChannelObject buildChannelItem(MethodAnnotation annotation, MessageObjec Map chBinding = channelBinding != null ? new HashMap<>(channelBinding) : null; String channelName = bindingFactory.getChannelName(annotation); return ChannelObject.builder() - .channelId(ReferenceUtil.toValidId(channelName)) + .channelId(bindingFactory.getChannelId(annotation)) .address(channelName) .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message))) .bindings(chBinding) diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/ClassLevelAnnotationScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/ClassLevelAnnotationScanner.java index dc054eb77..38b621bd0 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/ClassLevelAnnotationScanner.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/ClassLevelAnnotationScanner.java @@ -88,8 +88,8 @@ protected Map buildMessages( .collect(toSet()); if (messageType == MessageType.OPERATION) { - String channelName = bindingFactory.getChannelName(classAnnotation); - return toOperationsMessagesMap(channelName, messages); + String channelId = bindingFactory.getChannelId(classAnnotation); + return toOperationsMessagesMap(channelId, messages); } return toMessagesMap(messages); } diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/MessageHelper.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/MessageHelper.java index ccd3c37ed..0c34117ec 100644 --- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/MessageHelper.java +++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/MessageHelper.java @@ -32,9 +32,9 @@ public static Map toMessagesMap(Set mes } public static Map toOperationsMessagesMap( - String channelName, Set messages) { - if (channelName == null || channelName.isBlank()) { - throw new IllegalArgumentException("channelName must not be empty"); + String channelId, Set messages) { + if (channelId == null || channelId.isBlank()) { + throw new IllegalArgumentException("channelId must not be empty"); } if (messages.isEmpty()) { @@ -46,6 +46,6 @@ public static Map toOperationsMessagesMap( .collect(Collectors.toMap( MessageObject::getMessageId, e -> MessageReference.toChannelMessage( - ReferenceUtil.toValidId(channelName), e.getMessageId()))); + channelId, e.getMessageId()))); } } 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 59d526df1..4be4cfa56 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 @@ -71,9 +71,9 @@ private Stream> mapClassToOperation(Class compon return Stream.empty(); } - String channelName = bindingFactory.getChannelName(classAnnotation); + String channelId = bindingFactory.getChannelId(classAnnotation); String operationId = StringUtils.joinWith( - "_", ReferenceUtil.toValidId(channelName), OperationAction.RECEIVE, component.getSimpleName()); + "_", channelId, OperationAction.RECEIVE, component.getSimpleName()); Operation operation = buildOperation(classAnnotation, annotatedMethods); annotatedMethods.forEach(method -> customizers.forEach(customizer -> customizer.customize(operation, method))); @@ -89,11 +89,11 @@ private Operation buildOperation(ClassAnnotation classAnnotation, Set me 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 = bindingFactory.getChannelId(classAnnotation); return Operation.builder() .action(OperationAction.RECEIVE) - .channel(ChannelReference.fromChannel(ReferenceUtil.toValidId(channelName))) + .channel(ChannelReference.fromChannel(channelId)) .messages(messages.values().stream().toList()) .bindings(opBinding) .build(); 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 d60659d1d..da4a332cc 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 @@ -70,9 +70,9 @@ private Map.Entry mapMethodToOperation(Method method) { MethodAnnotation annotation = AnnotationScannerUtil.findAnnotationOrThrow(methodAnnotationClass, method); - String channelName = bindingFactory.getChannelName(annotation); + String channelId = bindingFactory.getChannelId(annotation); String operationId = StringUtils.joinWith( - "_", ReferenceUtil.toValidId(channelName), OperationAction.RECEIVE, method.getName()); + "_", channelId, OperationAction.RECEIVE, method.getName()); NamedSchemaObject payloadSchema = payloadMethodParameterService.extractSchema(method); SchemaObject headerSchema = headerClassExtractor.extractHeader(method, payloadSchema); @@ -87,7 +87,7 @@ private Operation buildOperation( MessageObject message = buildMessage(annotation, payloadType, headerSchema); Map operationBinding = bindingFactory.buildOperationBinding(annotation); Map opBinding = operationBinding != null ? new HashMap<>(operationBinding) : null; - String channelId = ReferenceUtil.toValidId(bindingFactory.getChannelName(annotation)); + String channelId = bindingFactory.getChannelId(annotation); return Operation.builder() .action(OperationAction.RECEIVE) diff --git a/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/bindings/AmqpBindingFactory.java b/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/bindings/AmqpBindingFactory.java index 2df3dd760..6ef6436bb 100644 --- a/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/bindings/AmqpBindingFactory.java +++ b/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/bindings/AmqpBindingFactory.java @@ -29,6 +29,11 @@ public String getChannelName(RabbitListener annotation) { return RabbitListenerUtil.getChannelName(annotation, stringValueResolver); } + @Override + public String getChannelId(RabbitListener annotation) { + return RabbitListenerUtil.getChannelId(annotation, stringValueResolver); + } + @Override public Map buildChannelBinding(RabbitListener annotation) { return RabbitListenerUtil.buildChannelBinding(annotation, stringValueResolver, context); diff --git a/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/bindings/RabbitListenerUtil.java b/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/bindings/RabbitListenerUtil.java index 90bc0e3d4..015f0bb8d 100644 --- a/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/bindings/RabbitListenerUtil.java +++ b/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/bindings/RabbitListenerUtil.java @@ -70,6 +70,20 @@ public static String getChannelName(RabbitListener annotation, StringValueResolv "No channel name was found in @RabbitListener annotation (neither in queues nor bindings property)")); } + public static String getChannelId(RabbitListener annotation, StringValueResolver resolver) { + Stream annotationBindingChannelNames = Arrays.stream(annotation.bindings()) + .flatMap(binding -> channelIdFromAnnotationBindings(binding, resolver)); + + return Stream.concat(streamQueueNames(annotation), annotationBindingChannelNames) + .map(resolver::resolveStringValue) + .filter(Objects::nonNull) + .peek(queue -> log.debug("Resolved channel id: {}", queue)) + .findFirst() + .orElseThrow( + () -> new IllegalArgumentException( + "No channel id was found in @RabbitListener annotation (neither in queues nor bindings property)")); + } + private static String getQueueName(RabbitListener annotation, StringValueResolver resolver) { Stream annotationBindingChannelNames = Arrays.stream(annotation.bindings()) .flatMap(binding -> Stream.of(binding.value().name())); @@ -96,7 +110,22 @@ private static Stream channelNameFromAnnotationBindings( return Arrays.stream(routingKeys) .map(resolver::resolveStringValue) - .map(routingKey -> String.join("_", queueName, routingKey, exchangeName)); + .map(routingKey -> exchangeName); + } + + private static Stream channelIdFromAnnotationBindings( + QueueBinding binding, StringValueResolver resolver) { + String queueName = resolver.resolveStringValue(binding.value().name()); + String exchangeName = resolver.resolveStringValue(binding.exchange().name()); + + String[] routingKeys = binding.key(); + if (routingKeys.length == 0) { + routingKeys = List.of(DEFAULT_EXCHANGE_ROUTING_KEY).toArray(new String[0]); + } + + return Arrays.stream(routingKeys) + .map(resolver::resolveStringValue) + .map(routingKey -> String.join("_", queueName+"-id", routingKey, exchangeName+"-id")); } /** diff --git a/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/channels/RabbitQueueBeanScanner.java b/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/channels/RabbitQueueBeanScanner.java index 79ef8726a..5aa992b6c 100644 --- a/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/channels/RabbitQueueBeanScanner.java +++ b/springwolf-plugins/springwolf-amqp-plugin/src/main/java/io/github/springwolf/plugins/amqp/asyncapi/scanners/channels/RabbitQueueBeanScanner.java @@ -21,9 +21,7 @@ public Map scan() { return queues.stream() .map(RabbitListenerUtil::buildChannelObject) .collect(Collectors.toMap( - o -> ((AMQPChannelBinding) o.getBindings().get(RabbitListenerUtil.BINDING_NAME)) - .getQueue() - .getName(), + ChannelObject::getChannelId, c -> c, (a, b) -> a)); } diff --git a/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/asyncapi/scanners/operation/annotations/SendToCustomizer.java b/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/asyncapi/scanners/operation/annotations/SendToCustomizer.java index 6ef62f48c..e9688a05b 100644 --- a/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/asyncapi/scanners/operation/annotations/SendToCustomizer.java +++ b/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/asyncapi/scanners/operation/annotations/SendToCustomizer.java @@ -25,7 +25,7 @@ public class SendToCustomizer implements OperationCustomizer { public void customize(Operation operation, Method method) { SendTo annotation = AnnotationScannerUtil.findAnnotation(SendTo.class, method); if (annotation != null) { - String channelId = ReferenceUtil.toValidId(bindingFactory.getChannelName(annotation)); + String channelId = bindingFactory.getChannelId(annotation); String payloadName = payloadService.extractSchema(method).name(); operation.setReply(OperationReply.builder() diff --git a/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/asyncapi/scanners/operation/annotations/SendToUserCustomizer.java b/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/asyncapi/scanners/operation/annotations/SendToUserCustomizer.java index 326ec59c8..a789095bf 100644 --- a/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/asyncapi/scanners/operation/annotations/SendToUserCustomizer.java +++ b/springwolf-plugins/springwolf-stomp-plugin/src/main/java/io/github/springwolf/plugins/stomp/asyncapi/scanners/operation/annotations/SendToUserCustomizer.java @@ -25,7 +25,7 @@ public class SendToUserCustomizer implements OperationCustomizer { public void customize(Operation operation, Method method) { SendToUser annotation = AnnotationScannerUtil.findAnnotation(SendToUser.class, method); if (annotation != null) { - String channelId = ReferenceUtil.toValidId(bindingFactory.getChannelName(annotation)); + String channelId = bindingFactory.getChannelId(annotation); String payloadName = payloadService.extractSchema(method).name(); operation.setReply(OperationReply.builder()