Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: support for beanRef in KafkaListener annotation #1089

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,28 @@
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 io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;

import java.util.Map;

public interface BindingFactory<T> {

// maintainer note: replaced by #getChannelId(T, BindingContext)
default String getChannelId(T annotation) {
return ReferenceUtil.toValidId(getChannelName(annotation));
}

// maintainer note: replaced by #getChannelName(T, BindingContext)
String getChannelName(T annotation);

default String getChannelId(T annotation, BindingContext bindingContext) {
return getChannelId(annotation);
}

default String getChannelName(T annotation, BindingContext bindingContext) {
return getChannelName(annotation);
}

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

Map<String, OperationBinding> buildOperationBinding(T annotation);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.springwolf.core.asyncapi.scanners.bindings.common;

import java.lang.reflect.Method;

public record BindingContext(Class<?> annotatedClass, Method annotatedMethod) {
public BindingContext {
if (annotatedClass == null && annotatedMethod == null) {
throw new IllegalArgumentException("Either annotatedClass or annotatedMethod must be non-null");
}
}

public Class<?> getClassContext() {
if (annotatedClass != null) {
return annotatedClass;
}
if (annotatedMethod != null) {
return annotatedMethod.getDeclaringClass();
}

throw new IllegalStateException("Either annotatedClass or annotatedMethod must be non-null");
}

public static BindingContext ofAnnotatedMethod(Method annotatedMethod) {
return new BindingContext(null, annotatedMethod);
}

public static BindingContext ofAnnotatedClass(Class<?> annotatedClass) {
return new BindingContext(annotatedClass, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import io.github.springwolf.asyncapi.v3.model.channel.ChannelObject;
import io.github.springwolf.asyncapi.v3.model.channel.message.Message;
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
import io.github.springwolf.core.asyncapi.scanners.channels.ChannelsInClassScanner;
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationScannerUtil;
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationUtil;
Expand Down Expand Up @@ -42,15 +43,17 @@ public List<ChannelObject> scan(Class<?> clazz) {
private Stream<ChannelObject> mapClassToChannel(
Class<?> component, Set<MethodAndAnnotation<MethodAnnotation>> annotatedMethods) {
ClassAnnotation classAnnotation = AnnotationUtil.findFirstAnnotationOrThrow(classAnnotationClass, component);
BindingContext bindingContext = BindingContext.ofAnnotatedClass(component);
Set<Method> methods =
annotatedMethods.stream().map(MethodAndAnnotation::method).collect(Collectors.toSet());
Map<String, Message> messages = new HashMap<>(springAnnotationMessagesService.buildMessages(
classAnnotation, methods, SpringAnnotationMessagesService.MessageType.CHANNEL));
classAnnotation, bindingContext, methods, SpringAnnotationMessagesService.MessageType.CHANNEL));

return mapClassToChannel(classAnnotation, messages);
return mapClassToChannel(classAnnotation, bindingContext, messages);
}

private Stream<ChannelObject> mapClassToChannel(ClassAnnotation classAnnotation, Map<String, Message> messages) {
return Stream.of(springAnnotationChannelService.buildChannel(classAnnotation, messages));
private Stream<ChannelObject> mapClassToChannel(
ClassAnnotation classAnnotation, BindingContext bindingContext, Map<String, Message> messages) {
return Stream.of(springAnnotationChannelService.buildChannel(classAnnotation, bindingContext, messages));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
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.schema.SchemaObject;
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
import io.github.springwolf.core.asyncapi.scanners.channels.ChannelsInClassScanner;
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationScannerUtil;
import io.github.springwolf.core.asyncapi.scanners.common.annotation.AnnotationUtil;
Expand Down Expand Up @@ -48,6 +49,8 @@ private ChannelObject mapMethodToChannel(MethodAndAnnotation<MethodAnnotation> m
MessageObject message = springAnnotationMessageService.buildMessage(annotation, payloadSchema, headerSchema);
Map<String, Message> messages = Map.of(message.getMessageId(), MessageReference.toComponentMessage(message));

return springAnnotationChannelService.buildChannel(annotation, messages);
BindingContext bindingContext = BindingContext.ofAnnotatedMethod(method.method());

return springAnnotationChannelService.buildChannel(annotation, bindingContext, messages);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.github.springwolf.asyncapi.v3.model.channel.ChannelObject;
import io.github.springwolf.asyncapi.v3.model.channel.message.Message;
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

Expand All @@ -18,10 +19,11 @@ public class SpringAnnotationChannelService<Annotation extends java.lang.annotat

private final BindingFactory<Annotation> bindingFactory;

public ChannelObject buildChannel(Annotation annotation, Map<String, Message> messages) {
public ChannelObject buildChannel(
Annotation annotation, BindingContext bindingContext, Map<String, Message> messages) {
Map<String, ChannelBinding> channelBinding = bindingFactory.buildChannelBinding(annotation);
Map<String, ChannelBinding> chBinding = channelBinding != null ? new HashMap<>(channelBinding) : null;
String channelName = bindingFactory.getChannelName(annotation);
String channelName = bindingFactory.getChannelName(annotation, bindingContext);
return ChannelObject.builder()
.channelId(ReferenceUtil.toValidId(channelName))
.address(channelName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
import io.github.springwolf.core.asyncapi.components.ComponentsService;
import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
import io.github.springwolf.core.asyncapi.scanners.bindings.common.BindingContext;
import io.github.springwolf.core.asyncapi.scanners.common.headers.AsyncHeadersBuilder;
import io.github.springwolf.core.asyncapi.scanners.common.headers.HeaderClassExtractor;
import io.github.springwolf.core.asyncapi.scanners.common.headers.HeaderSchemaObjectMerger;
Expand Down Expand Up @@ -43,13 +44,16 @@ public enum MessageType {
}

public Map<String, MessageReference> buildMessages(
ClassAnnotation classAnnotation, Set<Method> methods, MessageType messageType) {
ClassAnnotation classAnnotation,
BindingContext bindingContext,
Set<Method> methods,
MessageType messageType) {
Set<MessageObject> messages = methods.stream()
.map(method -> buildMessage(classAnnotation, method))
.collect(toSet());

if (messageType == MessageType.OPERATION) {
String channelId = bindingFactory.getChannelName(classAnnotation);
String channelId = bindingFactory.getChannelName(classAnnotation, bindingContext);
return toOperationsMessagesMap(channelId, messages);
}
return toMessagesMap(messages);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,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.bindings.common.BindingContext;
import io.github.springwolf.core.asyncapi.scanners.common.message.SpringAnnotationMessageService;
import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadSchemaObject;
import lombok.RequiredArgsConstructor;
Expand All @@ -25,11 +26,14 @@ public class SpringAnnotationOperationService<MethodAnnotation extends Annotatio
private final SpringAnnotationMessageService<MethodAnnotation> springAnnotationMessageService;

public Operation buildOperation(
MethodAnnotation annotation, PayloadSchemaObject payloadType, SchemaObject headerSchema) {
MethodAnnotation annotation,
BindingContext bindingContext,
PayloadSchemaObject payloadType,
SchemaObject headerSchema) {
MessageObject message = springAnnotationMessageService.buildMessage(annotation, payloadType, headerSchema);
Map<String, OperationBinding> operationBinding = bindingFactory.buildOperationBinding(annotation);
Map<String, OperationBinding> opBinding = operationBinding != null ? new HashMap<>(operationBinding) : null;
String channelId = bindingFactory.getChannelId(annotation);
String channelId = bindingFactory.getChannelId(annotation, bindingContext);

return Operation.builder()
.action(OperationAction.RECEIVE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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.bindings.common.BindingContext;
import io.github.springwolf.core.asyncapi.scanners.common.message.SpringAnnotationMessagesService;
import lombok.RequiredArgsConstructor;

Expand All @@ -23,16 +24,18 @@ public class SpringAnnotationOperationsService<ClassAnnotation extends Annotatio
private final BindingFactory<ClassAnnotation> bindingFactory;
private final SpringAnnotationMessagesService<ClassAnnotation> springAnnotationMessagesService;

public Operation buildOperation(ClassAnnotation classAnnotation, Set<Method> methods) {
public Operation buildOperation(
ClassAnnotation classAnnotation, BindingContext bindingContext, Set<Method> methods) {
var messages = springAnnotationMessagesService.buildMessages(
classAnnotation, methods, SpringAnnotationMessagesService.MessageType.OPERATION);
return buildOperation(classAnnotation, messages);
classAnnotation, bindingContext, methods, SpringAnnotationMessagesService.MessageType.OPERATION);
return buildOperation(classAnnotation, bindingContext, messages);
}

private Operation buildOperation(ClassAnnotation classAnnotation, Map<String, MessageReference> messages) {
private Operation buildOperation(
ClassAnnotation classAnnotation, BindingContext bindingContext, Map<String, MessageReference> messages) {
Map<String, OperationBinding> operationBinding = bindingFactory.buildOperationBinding(classAnnotation);
Map<String, OperationBinding> opBinding = operationBinding != null ? new HashMap<>(operationBinding) : null;
String channelName = bindingFactory.getChannelName(classAnnotation);
String channelName = bindingFactory.getChannelName(classAnnotation, bindingContext);
String channelId = ReferenceUtil.toValidId(channelName);

return Operation.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
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.bindings.common.BindingContext;
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;
Expand Down Expand Up @@ -42,14 +43,16 @@ public Stream<Map.Entry<String, Operation>> scan(Class<?> clazz) {
private Stream<Map.Entry<String, Operation>> mapClassToOperation(
Class<?> component, Set<MethodAndAnnotation<MethodAnnotation>> annotatedMethods) {
ClassAnnotation classAnnotation = AnnotationUtil.findFirstAnnotationOrThrow(classAnnotationClass, component);
BindingContext bindingContext = BindingContext.ofAnnotatedClass(component);

String channelId = bindingFactory.getChannelId(classAnnotation);
String channelId = bindingFactory.getChannelId(classAnnotation, bindingContext);
String operationId =
StringUtils.joinWith("_", channelId, OperationAction.RECEIVE.type, component.getSimpleName());

Set<Method> methods =
annotatedMethods.stream().map(MethodAndAnnotation::method).collect(Collectors.toSet());
Operation operation = springAnnotationOperationsService.buildOperation(classAnnotation, methods);
Operation operation =
springAnnotationOperationsService.buildOperation(classAnnotation, bindingContext, methods);
annotatedMethods.forEach(
method -> customizers.forEach(customizer -> customizer.customize(operation, method.method())));
return Stream.of(Map.entry(operationId, operation));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,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.bindings.common.BindingContext;
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;
Expand Down Expand Up @@ -42,15 +43,17 @@ public Stream<Map.Entry<String, Operation>> scan(Class<?> clazz) {

private Map.Entry<String, Operation> mapMethodToOperation(MethodAndAnnotation<MethodAnnotation> method) {
MethodAnnotation annotation = AnnotationUtil.findFirstAnnotationOrThrow(methodAnnotationClass, method.method());
BindingContext bindingContext = BindingContext.ofAnnotatedMethod(method.method());

String channelId = bindingFactory.getChannelId(annotation);
String channelId = bindingFactory.getChannelId(annotation, bindingContext);
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(
annotation, bindingContext, payloadSchema, headerSchema);
customizers.forEach(customizer -> customizer.customize(operation, method.method()));
return Map.entry(operationId, operation);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ void scan() {
MessageReference message = MessageReference.toComponentMessage("messageId");
Map<String, Message> messages = Map.of("messageId", message);

when(springAnnotationChannelService.buildChannel(any(), any()))
when(springAnnotationChannelService.buildChannel(any(), any(), any()))
.thenReturn(ChannelObject.builder()
.channelId(TestBindingFactory.CHANNEL_ID)
.messages(messages)
Expand All @@ -58,7 +58,7 @@ void scan() {

int methodsInClass = 2;
verify(springAnnotationMessagesService)
.buildMessages(any(), argThat(list -> list.size() == methodsInClass), any());
.buildMessages(any(), any(), argThat(list -> list.size() == methodsInClass), any());
}

@TestClassListener
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void scan() {
.build();

when(springAnnotationMessageService.buildMessage(any(), any(), any())).thenReturn(message);
when(springAnnotationChannelService.buildChannel(any(), any())).thenReturn(expectedChannelItem);
when(springAnnotationChannelService.buildChannel(any(), any(), any())).thenReturn(expectedChannelItem);

// when
List<ChannelObject> channels = scanner.scan(ClassWithTestListenerAnnotation.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.bindings.common.BindingContext;
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;
Expand Down Expand Up @@ -40,7 +41,7 @@ class SpringAnnotationOperationServiceTest {
@BeforeEach
void setUp() {
// when
when(bindingFactory.getChannelId(any())).thenReturn(CHANNEL_ID);
when(bindingFactory.getChannelId(any(), any())).thenReturn(CHANNEL_ID);
doReturn(defaultOperationBinding).when(bindingFactory).buildOperationBinding(any());
}

Expand All @@ -59,8 +60,11 @@ void scan_componentHasTestListenerMethods() throws NoSuchMethodException {
.thenReturn(messageObject);

// when
Operation operations =
springAnnotationOperationService.buildOperation(annotation, payloadSchemaName, headerSchema);
Operation operations = springAnnotationOperationService.buildOperation(
annotation,
BindingContext.ofAnnotatedClass(ClassWithTestListenerAnnotation.class),
payloadSchemaName,
headerSchema);

// then
Operation expectedOperation = Operation.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,15 @@ class SpringAnnotationClassLevelOperationsScannerTest {

@BeforeEach
void setUp() {
when(bindingFactory.getChannelId(any())).thenReturn(CHANNEL_NAME_ID);
when(bindingFactory.getChannelId(any(), any())).thenReturn(CHANNEL_NAME_ID);
}

@Test
void scan() {
// given
Operation operation = Operation.builder().build();
when(springAnnotationOperationsService.buildOperation(any(), anySet())).thenReturn(operation);
when(springAnnotationOperationsService.buildOperation(any(), any(), anySet()))
.thenReturn(operation);

// when
List<Map.Entry<String, Operation>> operations =
Expand All @@ -64,7 +65,8 @@ void scan() {
void operationCustomizerIsCalled() {
// given
Operation operation = Operation.builder().build();
when(springAnnotationOperationsService.buildOperation(any(), anySet())).thenReturn(operation);
when(springAnnotationOperationsService.buildOperation(any(), any(), anySet()))
.thenReturn(operation);

// when
scanner.scan(ClassWithTestListenerAnnotation.class).toList();
Expand Down
Loading