Skip to content

Commit

Permalink
chore: Split ChannelScanner and OperationScanner (#562)
Browse files Browse the repository at this point in the history
  • Loading branch information
ctasada committed Jan 24, 2024
1 parent 75f2156 commit 3d4a433
Show file tree
Hide file tree
Showing 46 changed files with 2,147 additions and 784 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
import io.github.stavshamir.springwolf.asyncapi.ChannelsService;
import io.github.stavshamir.springwolf.asyncapi.DefaultAsyncApiService;
import io.github.stavshamir.springwolf.asyncapi.DefaultChannelsService;
import io.github.stavshamir.springwolf.asyncapi.DefaultOperationsService;
import io.github.stavshamir.springwolf.asyncapi.OperationsService;
import io.github.stavshamir.springwolf.asyncapi.SpringwolfInitApplicationListener;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelsScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.OperationsScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor;
import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService;
import io.github.stavshamir.springwolf.configuration.DefaultAsyncApiDocketService;
Expand Down Expand Up @@ -58,9 +61,11 @@ public SpringwolfInitApplicationListener springwolfInitApplicationListener(
public AsyncApiService asyncApiService(
AsyncApiDocketService asyncApiDocketService,
ChannelsService channelsService,
OperationsService operationsService,
SchemasService schemasService,
List<AsyncApiCustomizer> customizers) {
return new DefaultAsyncApiService(asyncApiDocketService, channelsService, schemasService, customizers);
return new DefaultAsyncApiService(
asyncApiDocketService, channelsService, operationsService, schemasService, customizers);
}

@Bean
Expand All @@ -69,6 +74,12 @@ public ChannelsService channelsService(List<? extends ChannelsScanner> channelsS
return new DefaultChannelsService(channelsScanners);
}

@Bean
@ConditionalOnMissingBean
public OperationsService operationsService(List<? extends OperationsScanner> operationsScanners) {
return new DefaultOperationsService(operationsScanners);
}

@Bean
@ConditionalOnMissingBean
public SchemasService schemasService(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.OperationBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelPriority;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation.AsyncAnnotationChannelsScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation.AsyncAnnotationOperationsScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation.AsyncAnnotationScanner;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncListener;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncOperation;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncPublisher;
Expand Down Expand Up @@ -67,7 +69,7 @@ public SpringwolfClassScanner springwolfClassScanner(
havingValue = "true",
matchIfMissing = true)
@Order(value = ChannelPriority.ASYNC_ANNOTATION)
public AsyncAnnotationChannelsScanner<AsyncListener> asyncListenerAnnotationScanner(
public AsyncAnnotationChannelsScanner<AsyncListener> asyncListenerAnnotationChannelScanner(
SpringwolfClassScanner springwolfClassScanner,
SchemasService schemasService,
AsyncApiDocketService asyncApiDocketService,
Expand All @@ -84,13 +86,34 @@ public AsyncAnnotationChannelsScanner<AsyncListener> asyncListenerAnnotationScan
messageBindingProcessors);
}

@Bean
@ConditionalOnProperty(
name = SPRINGWOLF_SCANNER_ASYNC_LISTENER_ENABLED,
havingValue = "true",
matchIfMissing = true)
@Order(value = ChannelPriority.ASYNC_ANNOTATION)
public AsyncAnnotationOperationsScanner<AsyncListener> asyncListenerAnnotationOperationScanner(
SpringwolfClassScanner springwolfClassScanner,
SchemasService schemasService,
PayloadClassExtractor payloadClassExtractor,
List<OperationBindingProcessor> operationBindingProcessors,
List<MessageBindingProcessor> messageBindingProcessors) {
return new AsyncAnnotationOperationsScanner<>(
buildAsyncListenerAnnotationProvider(),
springwolfClassScanner,
schemasService,
payloadClassExtractor,
operationBindingProcessors,
messageBindingProcessors);
}

@Bean
@ConditionalOnProperty(
name = SPRINGWOLF_SCANNER_ASYNC_PUBLISHER_ENABLED,
havingValue = "true",
matchIfMissing = true)
@Order(value = ChannelPriority.ASYNC_ANNOTATION)
public AsyncAnnotationChannelsScanner<AsyncPublisher> asyncPublisherAnnotationScanner(
public AsyncAnnotationChannelsScanner<AsyncPublisher> asyncPublisherChannelAnnotationScanner(
SpringwolfClassScanner springwolfClassScanner,
SchemasService schemasService,
AsyncApiDocketService asyncApiDocketService,
Expand All @@ -107,9 +130,30 @@ public AsyncAnnotationChannelsScanner<AsyncPublisher> asyncPublisherAnnotationSc
messageBindingProcessors);
}

private static AsyncAnnotationChannelsScanner.AsyncAnnotationProvider<AsyncListener>
@Bean
@ConditionalOnProperty(
name = SPRINGWOLF_SCANNER_ASYNC_PUBLISHER_ENABLED,
havingValue = "true",
matchIfMissing = true)
@Order(value = ChannelPriority.ASYNC_ANNOTATION)
public AsyncAnnotationOperationsScanner<AsyncPublisher> asyncPublisherOperationAnnotationScanner(
SpringwolfClassScanner springwolfClassScanner,
SchemasService schemasService,
PayloadClassExtractor payloadClassExtractor,
List<OperationBindingProcessor> operationBindingProcessors,
List<MessageBindingProcessor> messageBindingProcessors) {
return new AsyncAnnotationOperationsScanner<>(
buildAsyncPublisherAnnotationProvider(),
springwolfClassScanner,
schemasService,
payloadClassExtractor,
operationBindingProcessors,
messageBindingProcessors);
}

private static AsyncAnnotationScanner.AsyncAnnotationProvider<AsyncListener>
buildAsyncListenerAnnotationProvider() {
return new AsyncAnnotationChannelsScanner.AsyncAnnotationProvider<>() {
return new AsyncAnnotationScanner.AsyncAnnotationProvider<>() {
@Override
public Class<AsyncListener> getAnnotation() {
return AsyncListener.class;
Expand All @@ -127,9 +171,9 @@ public OperationAction getOperationType() {
};
}

private static AsyncAnnotationChannelsScanner.AsyncAnnotationProvider<AsyncPublisher>
private static AsyncAnnotationScanner.AsyncAnnotationProvider<AsyncPublisher>
buildAsyncPublisherAnnotationProvider() {
return new AsyncAnnotationChannelsScanner.AsyncAnnotationProvider<>() {
return new AsyncAnnotationScanner.AsyncAnnotationProvider<>() {
@Override
public Class<AsyncPublisher> getAnnotation() {
return AsyncPublisher.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
package io.github.stavshamir.springwolf.asyncapi;

import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;

import java.util.Map;

Expand All @@ -17,11 +16,4 @@ public interface ChannelsService {
* @return Map of channel names mapping to detected ChannelItems
*/
Map<String, ChannelObject> findChannels();

/**
* Detects all available AsyncAPI Operation in the spring context.
*
* @return Map of operation names mapping to detected Operations
*/
Map<String, Operation> findOperations();
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ private record AsyncAPIResult(AsyncAPI asyncAPI, Throwable exception) {}

private final AsyncApiDocketService asyncApiDocketService;
private final ChannelsService channelsService;
private final OperationsService operationsService;
private final SchemasService schemasService;
private final List<AsyncApiCustomizer> customizers;

Expand Down Expand Up @@ -65,7 +66,7 @@ protected synchronized void initAsyncAPI() {
// SchemasService.
Map<String, ChannelObject> channels = channelsService.findChannels();

Map<String, Operation> operations = channelsService.findOperations();
Map<String, Operation> operations = operationsService.findOperations();

Components components = Components.builder()
.schemas(schemasService.getSchemas())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelMerger;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelsScanner;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

Expand Down Expand Up @@ -32,32 +31,12 @@ public Map<String, ChannelObject> findChannels() {

for (ChannelsScanner scanner : channelsScanners) {
try {
Map<String, ChannelObject> channels = scanner.scanChannels();
Map<String, ChannelObject> channels = scanner.scan();
foundChannelItems.addAll(channels.entrySet());
} catch (Exception e) {
log.error("An error was encountered during channel scanning with {}: {}", scanner, e.getMessage(), e);
}
}
return ChannelMerger.mergeChannels(foundChannelItems);
}

/**
* Collects all AsyncAPI Operation using the available {@link ChannelsScanner}
* beans.
* @return Map of operation names mapping to detected Operation
*/
@Override
public Map<String, Operation> findOperations() {
List<Map.Entry<String, Operation>> foundOperations = new ArrayList<>();
for (ChannelsScanner scanner : channelsScanners) {
try {
Map<String, Operation> channels = scanner.scanOperations();
foundOperations.addAll(channels.entrySet());
} catch (Exception e) {
log.error("An error was encountered during operation scanning with {}: {}", scanner, e.getMessage(), e);
}
}

return ChannelMerger.mergeOperations(foundOperations);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi;

import io.github.stavshamir.springwolf.asyncapi.scanners.channels.OperationMerger;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.OperationsScanner;
import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
* Service to detect AsyncAPI operations in the current spring context.
*/
@Slf4j
@RequiredArgsConstructor
public class DefaultOperationsService implements OperationsService {

private final List<? extends OperationsScanner> operationScanners;

/**
* Collects all AsyncAPI Operation using the available {@link OperationsScanner}
* beans.
* @return Map of operation names mapping to detected Operation
*/
@Override
public Map<String, Operation> findOperations() {
List<Map.Entry<String, Operation>> foundOperations = new ArrayList<>();

for (OperationsScanner scanner : operationScanners) {
try {
Map<String, Operation> channels = scanner.scan();
foundOperations.addAll(channels.entrySet());
} catch (Exception e) {
log.error("An error was encountered during operation scanning with {}: {}", scanner, e.getMessage(), e);
}
}
return OperationMerger.mergeOperations(foundOperations);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi;

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

import java.util.Map;

/**
* Service to detect AsyncAPI channels in the current spring context.
*/
public interface OperationsService {

/**
* Detects all available AsyncAPI Operation in the spring context.
*
* @return Map of operation names mapping to detected Operations
*/
Map<String, Operation> findOperations();
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.Channel;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message;
import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -44,31 +40,6 @@ public static Map<String, ChannelObject> mergeChannels(List<Map.Entry<String, Ch
return mergedChannels;
}

/**
* Merges multiple operations by operation name
* <p>
* Given two operations for the same operation name, the first seen Operation is used
* 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
* @return A map of operationId to a single Operation
*/
public static Map<String, Operation> mergeOperations(List<Map.Entry<String, Operation>> operationEntries) {
Map<String, Operation> mergedOperations = new HashMap<>();

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

return mergedOperations;
}

private static ChannelObject mergeChannel(ChannelObject channel, ChannelObject otherChannel) {
ChannelObject mergedChannel = channel != null ? channel : otherChannel;

Expand All @@ -89,27 +60,4 @@ private static ChannelObject mergeChannel(ChannelObject channel, ChannelObject o

return mergedChannel;
}

private static Operation mergeOperation(Operation operation, Operation otherOperation) {
Operation mergedOperation = operation != null ? operation : otherOperation;

List<MessageReference> mergedMessages =
mergeMessageReferences(operation.getMessages(), otherOperation.getMessages());
if (!mergedMessages.isEmpty()) {
mergedOperation.setMessages(mergedMessages);
}
return mergedOperation;
}

private static List<MessageReference> mergeMessageReferences(
Collection<MessageReference> messages, Collection<MessageReference> otherMessages) {
var messageReferences = new HashSet<MessageReference>();
if (messages != null) {
messageReferences.addAll(messages);
}
if (otherMessages != null) {
messageReferences.addAll(otherMessages);
}
return messageReferences.stream().toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
package io.github.stavshamir.springwolf.asyncapi.scanners.channels;

import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;

import java.util.Map;

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

/**
* @return A mapping of operation names to their respective operation object for a given protocol.
*/
Map<String, Operation> scanOperations();
Map<String, ChannelObject> scan();
}
Loading

0 comments on commit 3d4a433

Please sign in to comment.