Skip to content

Commit

Permalink
refactor(core): simplify OperationsInClassScanner interface (springwo…
Browse files Browse the repository at this point in the history
…lf#1120)

Possible because Operation@operationId added.
Continuation of 0eaa374
  • Loading branch information
timonback authored Dec 13, 2024
1 parent 4861d50 commit 399e2e1
Show file tree
Hide file tree
Showing 16 changed files with 69 additions and 85 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ public class DefaultOperationsService implements OperationsService {
*/
@Override
public Map<String, Operation> findOperations() {
List<Map.Entry<String, Operation>> foundOperations = new ArrayList<>();
List<Operation> foundOperations = new ArrayList<>();

for (OperationsScanner scanner : operationScanners) {
try {
Map<String, Operation> channels = scanner.scan();
foundOperations.addAll(channels.entrySet());
foundOperations.addAll(channels.values());
} catch (Exception e) {
log.error("An error was encountered during operation scanning with {}: {}", scanner, e.getMessage(), e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,18 @@ private OperationMerger() {}
* If an operation is null, the next non-null operation is used
* Messages within operations are merged
*
* @param operationEntries Ordered pairs of operation name to Operation
* @param operations Ordered pairs of operation name to Operation
* @return A map of operationId to a single Operation
*/
public static Map<String, Operation> mergeOperations(List<Map.Entry<String, Operation>> operationEntries) {
public static Map<String, Operation> mergeOperations(List<Operation> operations) {
Map<String, Operation> mergedOperations = new HashMap<>();

for (Map.Entry<String, Operation> entry : operationEntries) {
if (!mergedOperations.containsKey(entry.getKey())) {
mergedOperations.put(entry.getKey(), entry.getValue());
for (Operation operation : operations) {
if (!mergedOperations.containsKey(operation.getOperationId())) {
mergedOperations.put(operation.getOperationId(), operation);
} else {
Operation operation = mergeOperation(mergedOperations.get(entry.getKey()), entry.getValue());
mergedOperations.put(entry.getKey(), operation);
Operation mergedOperation = mergeOperation(mergedOperations.get(operation.getOperationId()), operation);
mergedOperations.put(operation.getOperationId(), mergedOperation);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@

import io.github.springwolf.asyncapi.v3.model.operation.Operation;

import java.util.Map;
import java.util.stream.Stream;

public interface OperationsInClassScanner {
Stream<Map.Entry<String, Operation>> scan(Class<?> clazz);
Stream<Operation> scan(Class<?> clazz);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ public class OperationsInClassScannerAdapter implements OperationsScanner {
public Map<String, Operation> scan() {
Set<Class<?>> components = classScanner.scan();

List<Map.Entry<String, Operation>> operations = mapToOperations(components);
List<Operation> operations = mapToOperations(components);

return OperationMerger.mergeOperations(operations);
}

private List<Map.Entry<String, Operation>> mapToOperations(Set<Class<?>> components) {
private List<Operation> mapToOperations(Set<Class<?>> components) {
return components.stream().flatMap(classProcessor::scan).toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -32,7 +31,7 @@ public class AsyncAnnotationClassLevelOperationsScanner<ClassAnnotation extends
private final List<OperationCustomizer> customizers;

@Override
public Stream<Map.Entry<String, Operation>> scan(Class<?> clazz) {
public Stream<Operation> scan(Class<?> clazz) {
Set<MethodAndAnnotation<ClassAnnotation>> methodAndAnnotation = AnnotationScannerUtil.findAnnotatedMethods(
clazz, classAnnotationClass, AllMethods.class, (cl, m) -> {
ClassAnnotation classAnnotation =
Expand All @@ -48,7 +47,7 @@ public Stream<Map.Entry<String, Operation>> scan(Class<?> clazz) {
return mapClassToOperation(clazz, methodAndAnnotation);
}

private Stream<Map.Entry<String, Operation>> mapClassToOperation(
private Stream<Operation> mapClassToOperation(
Class<?> component, Set<MethodAndAnnotation<ClassAnnotation>> annotatedMethods) {
ClassAnnotation classAnnotation = AnnotationUtil.findFirstAnnotationOrThrow(classAnnotationClass, component);
AsyncOperation asyncOperation = asyncAnnotationProvider.getAsyncOperation(classAnnotation);
Expand All @@ -58,6 +57,6 @@ private Stream<Map.Entry<String, Operation>> mapClassToOperation(
Operation operation = asyncAnnotationOperationsService.buildOperation(asyncOperation, methods);
annotatedMethods.forEach(
method -> customizers.forEach(customizer -> customizer.customize(operation, method.method())));
return Stream.of(Map.entry(operation.getOperationId(), operation));
return Stream.of(operation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

Expand All @@ -27,19 +26,18 @@ public class AsyncAnnotationMethodLevelOperationsScanner<MethodAnnotation extend
private final List<OperationCustomizer> customizers;

@Override
public Stream<Map.Entry<String, Operation>> scan(Class<?> clazz) {
public Stream<Operation> scan(Class<?> clazz) {
return AnnotationScannerUtil.findAnnotatedMethods(clazz, this.asyncAnnotationProvider.getAnnotation())
.map(this::mapMethodToOperation);
}

private Map.Entry<String, Operation> mapMethodToOperation(
MethodAndAnnotation<MethodAnnotation> methodAndAnnotation) {
private Operation mapMethodToOperation(MethodAndAnnotation<MethodAnnotation> methodAndAnnotation) {
AsyncOperation operationAnnotation =
this.asyncAnnotationProvider.getAsyncOperation(methodAndAnnotation.annotation());

Operation operation = asyncAnnotationOperationService.buildOperation(
operationAnnotation, Set.of(methodAndAnnotation.method()));
customizers.forEach(customizer -> customizer.customize(operation, methodAndAnnotation.method()));
return Map.entry(operation.getOperationId(), operation);
return operation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -30,12 +29,12 @@ public class SpringAnnotationClassLevelOperationsScanner<
private final List<OperationCustomizer> customizers;

@Override
public Stream<Map.Entry<String, Operation>> scan(Class<?> clazz) {
public Stream<Operation> scan(Class<?> clazz) {
return AnnotationScannerUtil.findAnnotatedMethods(
clazz, classAnnotationClass, methodAnnotationClass, this::mapClassToOperation);
}

private Stream<Map.Entry<String, Operation>> mapClassToOperation(
private Stream<Operation> mapClassToOperation(
Class<?> component, Set<MethodAndAnnotation<MethodAnnotation>> annotatedMethods) {
ClassAnnotation classAnnotation = AnnotationUtil.findFirstAnnotationOrThrow(classAnnotationClass, component);

Expand All @@ -44,6 +43,6 @@ private Stream<Map.Entry<String, Operation>> mapClassToOperation(
Operation operation = springAnnotationOperationsService.buildOperation(classAnnotation, component, methods);
annotatedMethods.forEach(
method -> customizers.forEach(customizer -> customizer.customize(operation, method.method())));
return Stream.of(Map.entry(operation.getOperationId(), operation));
return Stream.of(operation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

@Slf4j
Expand All @@ -30,17 +29,17 @@ public class SpringAnnotationMethodLevelOperationsScanner<MethodAnnotation exten
private final List<OperationCustomizer> customizers;

@Override
public Stream<Map.Entry<String, Operation>> scan(Class<?> clazz) {
public Stream<Operation> scan(Class<?> clazz) {
return AnnotationScannerUtil.findAnnotatedMethods(clazz, methodAnnotationClass)
.map(this::mapMethodToOperation);
}

private Map.Entry<String, Operation> mapMethodToOperation(MethodAndAnnotation<MethodAnnotation> method) {
private Operation mapMethodToOperation(MethodAndAnnotation<MethodAnnotation> method) {
PayloadSchemaObject payloadSchema = payloadMethodParameterService.extractSchema(method.method());
SchemaObject headerSchema = headerClassExtractor.extractHeader(method.method(), payloadSchema);

Operation operation = springAnnotationOperationService.buildOperation(method, payloadSchema, headerSchema);
customizers.forEach(customizer -> customizer.customize(operation, method.method()));
return Map.entry(operation.getOperationId(), operation);
return operation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@ void getOperations() {
static class SimpleOperationScanner implements OperationsScanner {
@Override
public Map<String, Operation> scan() {
return Map.of(
"foo",
Operation.builder()
.channel(ChannelReference.fromChannel("foo"))
.action(OperationAction.RECEIVE)
.build());
Operation operation = Operation.builder()
.operationId("foo")
.channel(ChannelReference.fromChannel("foo"))
.action(OperationAction.RECEIVE)
.build();

return Map.of(operation.getOperationId(), operation);
}
}

Expand All @@ -61,26 +62,28 @@ static class SameTopic {
@Component
static class SendOperationScanner implements OperationsScanner {
static final Operation sentOperation = Operation.builder()
.operationId("send")
.channel(ChannelReference.fromChannel(topicName))
.action(OperationAction.SEND)
.build();

@Override
public Map<String, Operation> scan() {
return Map.of("send", sentOperation);
return Map.of(sentOperation.getOperationId(), sentOperation);
}
}

@Component
static class ReceiveOperationScanner implements OperationsScanner {
static final Operation receiveOperation = Operation.builder()
.operationId("receive")
.channel(ChannelReference.fromChannel(topicName))
.action(OperationAction.RECEIVE)
.build();

@Override
public Map<String, Operation> scan() {
return Map.of("receive", receiveOperation);
return Map.of(receiveOperation.getOperationId(), receiveOperation);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ class OperationMergerTest {
@Test
void shouldNotMergeDifferentoperationIds() {
// given
String operationId1 = "operation1";
String operationId2 = "operation2";
Operation publisherOperation = Operation.builder().build();
Operation subscriberOperation = Operation.builder().build();
Operation publisherOperation =
Operation.builder().operationId("operation1").build();
Operation subscriberOperation =
Operation.builder().operationId("operation2").build();

// when
Map<String, Operation> mergedOperations = OperationMerger.mergeOperations(Arrays.asList(
Map.entry(operationId1, publisherOperation), Map.entry(operationId2, subscriberOperation)));
Map<String, Operation> mergedOperations =
OperationMerger.mergeOperations(Arrays.asList(publisherOperation, subscriberOperation));

// then
assertThat(mergedOperations).hasSize(2);
Expand All @@ -49,9 +49,8 @@ void shouldMergeEqualOperationIdsIntoOneOperation() {
.build();

// when
Map<String, Operation> mergedOperations = OperationMerger.mergeOperations(Arrays.asList(
Map.entry(publishOperation.getOperationId(), publishOperation),
Map.entry(subscribeOperation.getOperationId(), subscribeOperation)));
Map<String, Operation> mergedOperations =
OperationMerger.mergeOperations(Arrays.asList(publishOperation, subscribeOperation));

// then
assertThat(mergedOperations).hasSize(1);
Expand All @@ -71,9 +70,8 @@ void shouldUseFirstOperationFound() {
.build();

// when
Map<String, Operation> mergedOperations = OperationMerger.mergeOperations(Arrays.asList(
Map.entry(senderOperation.getOperationId(), senderOperation),
Map.entry(receiverOperation.getOperationId(), receiverOperation)));
Map<String, Operation> mergedOperations =
OperationMerger.mergeOperations(Arrays.asList(senderOperation, receiverOperation));

// then
assertThat(mergedOperations).hasSize(1).hasEntrySatisfying(operationId, it -> {
Expand Down Expand Up @@ -122,10 +120,8 @@ void shouldMergeDifferentMessageForSameOperation() {
.build();

// when
Map<String, Operation> mergedOperations = OperationMerger.mergeOperations(List.of(
Map.entry(senderOperation1.getOperationId(), senderOperation1),
Map.entry(senderOperation2.getOperationId(), senderOperation2),
Map.entry(senderOperation3.getOperationId(), senderOperation3)));
Map<String, Operation> mergedOperations =
OperationMerger.mergeOperations(List.of(senderOperation1, senderOperation2, 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
Expand Down Expand Up @@ -174,17 +170,17 @@ void shouldUseOtherMessageIfFirstMessageIsMissingForOperations() {
.build();
Operation publishOperation1 = Operation.builder()
.action(OperationAction.SEND)
.title("publisher1")
.operationId("publisher1")
.build();
Operation publishOperation2 = Operation.builder()
.action(OperationAction.SEND)
.title("publisher2")
.operationId("publisher1")
.messages(List.of(MessageReference.toChannelMessage(channelId, message2)))
.build();

// when
Map<String, Operation> mergedOperations = OperationMerger.mergeOperations(
Arrays.asList(Map.entry("publisher1", publishOperation1), Map.entry("publisher1", publishOperation2)));
Map<String, Operation> mergedOperations =
OperationMerger.mergeOperations(Arrays.asList(publishOperation1, publishOperation2));
// then expectedMessage message2
var expectedMessage = MessageReference.toChannelMessage(channelId, message2);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,35 +35,32 @@ void noClassFoundTest() {
void processClassTest() {
// given
when(classScanner.scan()).thenReturn(Set.of(String.class));
Map.Entry<String, Operation> operation1 =
Map.entry("operation1", Operation.builder().build());
Map.Entry<String, Operation> operation2 =
Map.entry("operation2", Operation.builder().build());
Operation operation1 = Operation.builder().operationId("operation1").build();
Operation operation2 = Operation.builder().operationId("operation2").build();
when(classProcessor.scan(any())).thenReturn(Stream.of(operation1, operation2));

// when
Map<String, Operation> operations = operationsInClassScannerAdapter.scan();

// then
assertThat(operations).containsExactly(operation2, operation1);
assertThat(operations).containsExactlyEntriesOf(Map.of("operation1", operation1, "operation2", operation2));
}

@Test
void sameOperationsAreMergedTest() {
// given
when(classScanner.scan()).thenReturn(Set.of(String.class));
Map.Entry<String, Operation> operation1 =
Map.entry("operation1", Operation.builder().build());
Map.Entry<String, Operation> operation2 =
Map.entry("operation1", Operation.builder().build());
Operation operation1 =
Operation.builder().operationId("operation1").description("op1").build();
Operation operation2 =
Operation.builder().operationId("operation1").description("op2").build();
when(classProcessor.scan(any())).thenReturn(Stream.of(operation1, operation2));

// when
Map<String, Operation> operations = operationsInClassScannerAdapter.scan();

// then
assertThat(operations)
.containsExactly(Map.entry("operation1", Operation.builder().build()));
assertThat(operations).hasSize(1);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -59,7 +60,7 @@ void scan() {
// when
Map<String, Operation> actualOperations = operationsScanner
.scan(ClassWithListenerAnnotation.class)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
.collect(Collectors.toMap(Operation::getOperationId, Function.identity()));

// then
assertThat(actualOperations).hasSize(1);
Expand Down
Loading

0 comments on commit 399e2e1

Please sign in to comment.