From c4ea459aa5cf346c70937445cada4bd8d14140ba Mon Sep 17 00:00:00 2001 From: Timon Back Date: Sun, 7 Jan 2024 14:23:47 +0100 Subject: [PATCH] chore: remove deprecated asyncapidocket bean (#524) * chore: remove deprecated asyncapidocket bean * chore(cloud-stream): update test dependencies * feat(core): implement review comment --- .../SpringwolfAutoConfiguration.java | 7 +- .../SpringwolfScannerConfiguration.java | 20 -- .../AbstractOperationDataScanner.java | 206 ---------------- .../ConsumerOperationDataScanner.java | 40 --- .../ProducerOperationDataScanner.java | 40 --- .../AsyncHeadersForCloudEventsBuilder.java | 134 ---------- .../configuration/AsyncApiDocket.java | 20 -- .../DefaultAsyncApiDocketService.java | 30 +-- .../SpringwolfConfigProperties.java | 31 +-- ...CustomBeanAsyncApiDocketConfiguration.java | 22 -- .../SpringContextIntegrationTest.java | 62 ++--- ...DefaultAsyncApiServiceIntegrationTest.java | 137 +++++------ ...erOperationDataScannerIntegrationTest.java | 229 ------------------ ...erOperationDataScannerIntegrationTest.java | 228 ----------------- ...tAsyncApiDocketServiceIntegrationTest.java | 114 ++------- .../DefaultAsyncApiDocketServiceTest.java | 34 +-- .../AutoConfigurationIntegrationTest.java | 3 + .../InitModeIntegrationTest.java | 6 +- .../integrationtests/TestApplication.java | 21 +- .../springwolf-amqp-plugin/README.md | 37 +-- .../build.gradle | 1 + ...unctionChannelsScannerIntegrationTest.java | 28 +-- .../springwolf-kafka-plugin/README.md | 39 +-- 23 files changed, 152 insertions(+), 1337 deletions(-) delete mode 100644 springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/AbstractOperationDataScanner.java delete mode 100644 springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ConsumerOperationDataScanner.java delete mode 100644 springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ProducerOperationDataScanner.java delete mode 100644 springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersForCloudEventsBuilder.java delete mode 100644 springwolf-core/src/test/java/io/github/stavshamir/springwolf/CustomBeanAsyncApiDocketConfiguration.java delete mode 100644 springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ConsumerOperationDataScannerIntegrationTest.java delete mode 100644 springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ProducerOperationDataScannerIntegrationTest.java diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/SpringwolfAutoConfiguration.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/SpringwolfAutoConfiguration.java index 3ce88cfca..57b4d0f7b 100644 --- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/SpringwolfAutoConfiguration.java +++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/SpringwolfAutoConfiguration.java @@ -9,7 +9,6 @@ import io.github.stavshamir.springwolf.asyncapi.SpringwolfInitApplicationListener; import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelsScanner; import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocket; import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService; import io.github.stavshamir.springwolf.configuration.DefaultAsyncApiDocketService; import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants; @@ -26,7 +25,6 @@ import org.springframework.context.annotation.Import; import java.util.List; -import java.util.Optional; /** * Spring Boot auto-configuration which loads all spring-beans for springwolf core module. @@ -77,9 +75,8 @@ public SchemasService schemasService( @Bean @ConditionalOnMissingBean - public AsyncApiDocketService asyncApiDocketService( - Optional optionalAsyncApiDocket, SpringwolfConfigProperties springwolfConfigProperties) { - return new DefaultAsyncApiDocketService(optionalAsyncApiDocket, springwolfConfigProperties); + public AsyncApiDocketService asyncApiDocketService(SpringwolfConfigProperties springwolfConfigProperties) { + return new DefaultAsyncApiDocketService(springwolfConfigProperties); } @Bean diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/SpringwolfScannerConfiguration.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/SpringwolfScannerConfiguration.java index 100f86cc0..58e3cbff4 100644 --- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/SpringwolfScannerConfiguration.java +++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/SpringwolfScannerConfiguration.java @@ -7,8 +7,6 @@ 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.operationdata.ConsumerOperationDataScanner; -import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.ProducerOperationDataScanner; 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; @@ -30,8 +28,6 @@ import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_SCANNER_ASYNC_LISTENER_ENABLED; import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_SCANNER_ASYNC_PUBLISHER_ENABLED; -import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_SCANNER_CONSUMER_DATA_ENABLED; -import static io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants.SPRINGWOLF_SCANNER_PRODUCER_DATA_ENABLED; /** * Spring configuration defining the core scanner beans. @@ -65,22 +61,6 @@ public SpringwolfClassScanner springwolfClassScanner( return new SpringwolfClassScanner(componentClassScanner, beanMethodsScanner); } - @Bean - @ConditionalOnProperty(name = SPRINGWOLF_SCANNER_CONSUMER_DATA_ENABLED, havingValue = "true", matchIfMissing = true) - @Order(value = ChannelPriority.MANUAL_DEFINED) - public ConsumerOperationDataScanner consumerOperationDataScanner( - AsyncApiDocketService asyncApiDocketService, SchemasService schemasService) { - return new ConsumerOperationDataScanner(asyncApiDocketService, schemasService); - } - - @Bean - @ConditionalOnProperty(name = SPRINGWOLF_SCANNER_PRODUCER_DATA_ENABLED, havingValue = "true", matchIfMissing = true) - @Order(value = ChannelPriority.MANUAL_DEFINED) - public ProducerOperationDataScanner producerOperationDataScanner( - AsyncApiDocketService asyncApiDocketService, SchemasService schemasService) { - return new ProducerOperationDataScanner(asyncApiDocketService, schemasService); - } - @Bean @ConditionalOnProperty( name = SPRINGWOLF_SCANNER_ASYNC_LISTENER_ENABLED, diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/AbstractOperationDataScanner.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/AbstractOperationDataScanner.java deleted file mode 100644 index e7f7f580d..000000000 --- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/AbstractOperationDataScanner.java +++ /dev/null @@ -1,206 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata; - -import com.asyncapi.v2._6_0.model.channel.ChannelItem; -import com.asyncapi.v2._6_0.model.channel.operation.Operation; -import com.asyncapi.v2._6_0.model.server.Server; -import com.asyncapi.v2.binding.channel.ChannelBinding; -import com.asyncapi.v2.binding.operation.OperationBinding; -import io.github.stavshamir.springwolf.asyncapi.scanners.channels.ChannelsScanner; -import io.github.stavshamir.springwolf.asyncapi.types.OperationData; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.Message; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.PayloadReference; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.HeaderReference; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService; -import io.github.stavshamir.springwolf.schemas.SchemasService; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.extern.slf4j.Slf4j; -import org.springframework.util.StringUtils; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static io.github.stavshamir.springwolf.asyncapi.MessageHelper.toMessageObjectOrComposition; -import static java.util.stream.Collectors.groupingBy; -import static java.util.stream.Collectors.toMap; -import static java.util.stream.Collectors.toSet; - -@Slf4j -@Deprecated(forRemoval = true) -public abstract class AbstractOperationDataScanner implements ChannelsScanner { - - protected abstract SchemasService getSchemaService(); - - protected abstract AsyncApiDocketService getAsyncApiDocketService(); - - /** - * provides a list of all {@link OperationData} items detected by the concrete - * subclass. - * - * @return - */ - protected abstract List getOperationData(); - - protected abstract OperationData.OperationType getOperationType(); - - @Override - public Map scan() { - Map> operationDataGroupedByChannelName = this.getOperationData().stream() - .filter(this::allFieldsAreNonNull) - .collect(groupingBy(OperationData::getChannelName)); - - return operationDataGroupedByChannelName.entrySet().stream() - .collect(toMap(Map.Entry::getKey, entry -> buildChannel(entry.getValue()))); - } - - private boolean allFieldsAreNonNull(OperationData operationData) { - boolean allNonNull = operationData.getChannelName() != null - && operationData.getPayloadType() != null - && operationData.getOperationBinding() != null; - - if (!allNonNull) { - log.warn("Some data fields are null - this producer will not be documented: {}", operationData); - } - - return allNonNull; - } - - /** - * Creates an asyncapi {@link ChannelItem} using the given list of {@link OperationData}. Expects, that all {@link OperationData} - * items belong to the same channel. Most properties of the resulting {@link ChannelItem} are extracted from the - * first {@link OperationData} item in the list, assuming that all {@link OperationData} contains the same channel - * informations. - * - * @param operationDataList List of all {@link OperationData} items for a single channel. - * @return the resulting {@link ChannelItem} - */ - private ChannelItem buildChannel(List operationDataList) { - // All bindings in the group are assumed to be the same - // AsyncApi does not support multiple bindings on a single channel - Map channelBinding = - operationDataList.get(0).getChannelBinding(); - Map operationBinding = - operationDataList.get(0).getOperationBinding(); - Map opBinding = operationBinding != null ? new HashMap<>(operationBinding) : null; - Map chBinding = channelBinding != null ? new HashMap<>(channelBinding) : null; - String operationId = operationDataList.get(0).getChannelName() + "_" + this.getOperationType().operationName; - String description = operationDataList.get(0).getDescription(); - List servers = operationDataList.get(0).getServers(); - - if (description.isEmpty()) { - description = "Auto-generated description"; - } - - Operation operation = Operation.builder() - .description(description) - .operationId(operationId) - .message(getMessageObject(operationDataList)) - .bindings(opBinding) - .build(); - - ChannelItem.ChannelItemBuilder channelBuilder = ChannelItem.builder().bindings(chBinding); - channelBuilder = switch (getOperationType()) { - case PUBLISH -> channelBuilder.publish(operation); - case SUBSCRIBE -> channelBuilder.subscribe(operation);}; - - // Only set servers if servers are defined. Avoid setting an emtpy list - // because this would generate empty server entries for each channel in the resulting - // async api. - if (servers != null && !servers.isEmpty()) { - validateServers(servers, operationId); - channelBuilder.servers(servers); - } - return channelBuilder.build(); - } - - private Object getMessageObject(List operationDataList) { - Set messages = - operationDataList.stream().map(this::buildMessage).collect(toSet()); - - return toMessageObjectOrComposition(messages); - } - - private Message buildMessage(OperationData operationData) { - Class payloadType = operationData.getPayloadType(); - String modelName = this.getSchemaService().register(payloadType); - String headerModelName = this.getSchemaService().register(operationData.getHeaders()); - - /* - * Message information can be obtained via a @AsyncMessage annotation on the method parameter, the Payload - * itself or via the Swagger @Schema annotation on the Payload. - */ - - var schema = payloadType.getAnnotation(Schema.class); - String description = schema != null ? schema.description() : null; - - var builder = Message.builder() - .name(payloadType.getName()) - .title(payloadType.getSimpleName()) - .description(description) - .payload(PayloadReference.fromModelName(modelName)) - .headers(HeaderReference.fromModelName(headerModelName)) - .bindings(operationData.getMessageBinding()); - - // Retrieve the Message information obtained from the @AsyncMessage annotation. These values have higher - // priority - // so if we find them, we need to override the default values. - processAsyncMessageAnnotation(operationData.getMessage(), builder); - - return builder.build(); - } - - private void processAsyncMessageAnnotation(Message annotationMessage, Message.MessageBuilder builder) { - if (annotationMessage != null) { - builder.messageId(annotationMessage.getMessageId()); - - var schemaFormat = annotationMessage.getSchemaFormat() != null - ? annotationMessage.getSchemaFormat() - : Message.DEFAULT_SCHEMA_FORMAT; - builder.schemaFormat(schemaFormat); - - var annotationMessageDescription = annotationMessage.getDescription(); - if (StringUtils.hasText(annotationMessageDescription)) { - builder.description(annotationMessageDescription); - } - - var name = annotationMessage.getName(); - if (StringUtils.hasText(name)) { - builder.name(name); - } - - var title = annotationMessage.getTitle(); - if (StringUtils.hasText(title)) { - builder.title(title); - } - } - } - - /** - * validates the given list of server names (for a specific operation) with the servers defined in the 'servers' part of - * the current AsyncApi. - * - * @param serversFromOperation the server names defined for the current operation - * @param operationId operationId of the current operation - used for exception messages - * @throws IllegalArgumentException if server from operation is not present in AsyncApi's servers definition. - */ - void validateServers(List serversFromOperation, String operationId) { - if (!serversFromOperation.isEmpty()) { - Map asyncApiServers = - getAsyncApiDocketService().getAsyncApiDocket().getServers(); - if (asyncApiServers == null || asyncApiServers.isEmpty()) { - throw new IllegalArgumentException(String.format( - "Operation '%s' defines server refs (%s) but there are no servers defined in this AsyncAPI.", - operationId, serversFromOperation)); - } - for (String server : serversFromOperation) { - if (!asyncApiServers.containsKey(server)) { - throw new IllegalArgumentException(String.format( - "Operation '%s' defines unknown server ref '%s'. This AsyncApi defines these server(s): %s", - operationId, server, asyncApiServers.keySet())); - } - } - } - } -} diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ConsumerOperationDataScanner.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ConsumerOperationDataScanner.java deleted file mode 100644 index 95d9999ea..000000000 --- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ConsumerOperationDataScanner.java +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata; - -import io.github.stavshamir.springwolf.asyncapi.types.OperationData; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService; -import io.github.stavshamir.springwolf.schemas.SchemasService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import java.util.ArrayList; -import java.util.List; - -@Slf4j -@RequiredArgsConstructor -@Deprecated(forRemoval = true) -public class ConsumerOperationDataScanner extends AbstractOperationDataScanner { - - private final AsyncApiDocketService asyncApiDocketService; - private final SchemasService schemasService; - - @Override - protected SchemasService getSchemaService() { - return this.schemasService; - } - - @Override - protected List getOperationData() { - return new ArrayList<>(asyncApiDocketService.getAsyncApiDocket().getConsumers()); - } - - @Override - protected AsyncApiDocketService getAsyncApiDocketService() { - return this.asyncApiDocketService; - } - - @Override - protected OperationData.OperationType getOperationType() { - return OperationData.OperationType.PUBLISH; - } -} diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ProducerOperationDataScanner.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ProducerOperationDataScanner.java deleted file mode 100644 index cbd13ec16..000000000 --- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ProducerOperationDataScanner.java +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata; - -import io.github.stavshamir.springwolf.asyncapi.types.OperationData; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService; -import io.github.stavshamir.springwolf.schemas.SchemasService; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -import java.util.ArrayList; -import java.util.List; - -@Slf4j -@RequiredArgsConstructor -@Deprecated(forRemoval = true) -public class ProducerOperationDataScanner extends AbstractOperationDataScanner { - - private final AsyncApiDocketService asyncApiDocketService; - private final SchemasService schemasService; - - @Override - protected SchemasService getSchemaService() { - return this.schemasService; - } - - @Override - protected AsyncApiDocketService getAsyncApiDocketService() { - return this.asyncApiDocketService; - } - - @Override - protected List getOperationData() { - return new ArrayList<>(asyncApiDocketService.getAsyncApiDocket().getProducers()); - } - - @Override - protected OperationData.OperationType getOperationType() { - return OperationData.OperationType.SUBSCRIBE; - } -} diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersForCloudEventsBuilder.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersForCloudEventsBuilder.java deleted file mode 100644 index 2df864c47..000000000 --- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersForCloudEventsBuilder.java +++ /dev/null @@ -1,134 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header; - -import org.springframework.http.MediaType; -import org.springframework.util.MimeType; - -import java.util.List; - -/** - * Only used in combination with AsyncApiDocket bean - * If not, let us know by raising a GitHub issue - */ -@Deprecated -public class AsyncHeadersForCloudEventsBuilder { - - private final AsyncHeaders headers; - - public AsyncHeadersForCloudEventsBuilder() { - this("CloudEventsBase"); - } - - public AsyncHeadersForCloudEventsBuilder(String schemaName) { - this.headers = new AsyncHeaders(schemaName); - } - - public AsyncHeadersForCloudEventsBuilder(String newSchemaName, AsyncHeaders baseHeaders) { - this.headers = AsyncHeaders.from(baseHeaders, newSchemaName); - } - - public AsyncHeadersForCloudEventsBuilder withContentTypeHeader(MediaType contentType) { - return withContentTypeHeader(contentType, List.of(contentType)); - } - - public AsyncHeadersForCloudEventsBuilder withContentTypeHeader( - MediaType exampleContentType, List contentTypeValues) { - List contentTypeStringValues = - contentTypeValues.stream().map(MimeType::toString).toList(); - return withHeader( - AsyncHeadersCloudEventConstants.CONTENT_TYPE, - contentTypeStringValues, - exampleContentType.toString(), - AsyncHeadersCloudEventConstants.CONTENT_TYPE_DESC); - } - - public AsyncHeadersForCloudEventsBuilder withSpecVersionHeader(String specVersion) { - return withSpecVersionHeader(specVersion, List.of(specVersion)); - } - - public AsyncHeadersForCloudEventsBuilder withSpecVersionHeader(String specVersion, List specValues) { - return withHeader( - AsyncHeadersCloudEventConstants.SPECVERSION, - specValues, - specVersion, - AsyncHeadersCloudEventConstants.SPECVERSION_DESC); - } - - public AsyncHeadersForCloudEventsBuilder withIdHeader(String idExample) { - return withIdHeader(idExample, List.of(idExample)); - } - - public AsyncHeadersForCloudEventsBuilder withIdHeader(String idExample, List idValues) { - return withHeader( - AsyncHeadersCloudEventConstants.ID, idValues, idExample, AsyncHeadersCloudEventConstants.ID_DESC); - } - - public AsyncHeadersForCloudEventsBuilder withTimeHeader(String timeExample) { - return withTimeHeader(timeExample, List.of(timeExample)); - } - - public AsyncHeadersForCloudEventsBuilder withTimeHeader(String timeExample, List timeValues) { - return withHeader( - AsyncHeadersCloudEventConstants.TIME, - timeValues, - timeExample, - AsyncHeadersCloudEventConstants.TIME_DESC); - } - - public AsyncHeadersForCloudEventsBuilder withTypeHeader(String typeExample) { - return withTypeHeader(typeExample, List.of(typeExample)); - } - - public AsyncHeadersForCloudEventsBuilder withTypeHeader(String typeExample, List typeValues) { - return withHeader( - AsyncHeadersCloudEventConstants.TYPE, - typeValues, - typeExample, - AsyncHeadersCloudEventConstants.TYPE_DESC); - } - - public AsyncHeadersForCloudEventsBuilder withSourceHeader(String sourceExample) { - return withSourceHeader(sourceExample, List.of(sourceExample)); - } - - public AsyncHeadersForCloudEventsBuilder withSourceHeader(String sourceExample, List sourceValues) { - return withHeader( - AsyncHeadersCloudEventConstants.SOURCE, - sourceValues, - sourceExample, - AsyncHeadersCloudEventConstants.SOURCE_DESC); - } - - public AsyncHeadersForCloudEventsBuilder withSubjectHeader(String subjectExample) { - return withSubjectHeader(subjectExample, List.of(subjectExample)); - } - - public AsyncHeadersForCloudEventsBuilder withSubjectHeader(String subjectExample, List subjectValues) { - return withHeader( - AsyncHeadersCloudEventConstants.SUBJECT, - subjectValues, - subjectExample, - AsyncHeadersCloudEventConstants.SUBJECT_DESC); - } - - public AsyncHeadersForCloudEventsBuilder withExtension( - String headerName, List values, String exampleValue, String description) { - return withHeader(headerName, values, exampleValue, description); - } - - private AsyncHeadersForCloudEventsBuilder withHeader( - String headerName, List values, String exampleValue, String description) { - AsyncHeaderSchema header = AsyncHeaderSchema.headerBuilder() - .headerName(headerName) - .description(description) - .example(exampleValue) - .enumValue(values) - .build(); - headers.addHeader(header); - return this; - } - - public AsyncHeaders build() { - return AsyncHeaders.from(this.headers, this.headers.getSchemaName()); - } -} diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/AsyncApiDocket.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/AsyncApiDocket.java index dd1282dec..a164568e7 100644 --- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/AsyncApiDocket.java +++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/AsyncApiDocket.java @@ -3,23 +3,14 @@ import com.asyncapi.v2._6_0.model.info.Info; import com.asyncapi.v2._6_0.model.server.Server; -import io.github.stavshamir.springwolf.asyncapi.types.ConsumerData; -import io.github.stavshamir.springwolf.asyncapi.types.ProducerData; import lombok.Builder; import lombok.Data; import lombok.NonNull; import lombok.Singular; import org.springframework.http.MediaType; -import java.util.List; import java.util.Map; -/** - * Use to (manually) configure springwolf - *

- * This will not be the final AsyncApiDocket, use {@link AsyncApiDocketService#getAsyncApiDocket()} to get it. - * This will not be the final api definition, use {@link io.github.stavshamir.springwolf.asyncapi.AsyncApiService} to get it. - */ @Data @Builder public class AsyncApiDocket { @@ -44,17 +35,6 @@ public class AsyncApiDocket { @Singular private final Map servers; - /** - * Provides information about the producers. - */ - @Singular - @Deprecated(forRemoval = true) - private final List producers; - - @Singular - @Deprecated(forRemoval = true) - private final List consumers; - /** * A string representing the default content type to use when encoding/decoding a message's payload. * diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketService.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketService.java index b5744e3af..c7c2d2569 100644 --- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketService.java +++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketService.java @@ -10,25 +10,14 @@ import org.springframework.util.StringUtils; import java.util.Map; -import java.util.Optional; @Slf4j @RequiredArgsConstructor public class DefaultAsyncApiDocketService implements AsyncApiDocketService { - - /** - * Docket defined by the user as a @Bean - */ - private final Optional customDocket; - - /** - * Docket definition in application.properties - */ private final SpringwolfConfigProperties configProperties; /** - * valid Docket instance, either reference to customDocket (if set) or environment based Docket. - * Lazy initialized on first invocation of getAsyncApiDocket(). + * lazily initialized AsyncApiDocket instance. */ @Nullable private AsyncApiDocket docket; @@ -36,25 +25,14 @@ public class DefaultAsyncApiDocketService implements AsyncApiDocketService { @Override public AsyncApiDocket getAsyncApiDocket() { if (docket == null) { - createDocket(); + docket = createDocket(); } return docket; } - private void createDocket() { - if (customDocket.isPresent()) { - log.debug("Reading springwolf configuration from custom defined @Bean AsyncApiDocket"); - log.warn("The usage of the @Bean AsyncApiDocket is deprecated and scheduled to be deleted. " - + "Use the spring properties file instead. " - + "More details: https://www.springwolf.dev/docs/quickstart"); - docket = customDocket.get(); - } else { - log.debug("Reading springwolf configuration from application.properties files"); - docket = parseApplicationConfigProperties(); - } - } + private AsyncApiDocket createDocket() { + log.debug("Reading springwolf configuration from application.properties files"); - private AsyncApiDocket parseApplicationConfigProperties() { if (configProperties.getDocket() == null || configProperties.getDocket().getBasePackage() == null) { throw new IllegalArgumentException( "One or more required fields (docket, basePackage) " + "in application.properties with path prefix " diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/properties/SpringwolfConfigProperties.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/properties/SpringwolfConfigProperties.java index 010c7a21c..f34073c01 100644 --- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/properties/SpringwolfConfigProperties.java +++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/properties/SpringwolfConfigProperties.java @@ -14,6 +14,9 @@ import java.util.Map; import java.util.stream.Collectors; +/** + * Springwolf configuration loaded from Spring application.properties file. + */ @ConfigurationProperties(prefix = SpringwolfConfigConstants.SPRINGWOLF_CONFIG_PREFIX) @Getter @Setter @@ -164,12 +167,6 @@ public static class Scanner { @Nullable private static AsyncPublisher asyncPublisher; - @Nullable - private static ConsumerData consumerData; - - @Nullable - private static ProducerData producerData; - @Getter @Setter public static class AsyncListener { @@ -189,28 +186,6 @@ public static class AsyncPublisher { */ private boolean enabled = true; } - - @Getter - @Setter - public static class ConsumerData { - - /** - * This mirrors the ConfigConstant {@see SpringwolfConfigConstants#SPRINGWOLF_SCANNER_PRODUCER_DATA_ENABLED} - */ - @Deprecated(forRemoval = true) - private boolean enabled = true; - } - - @Getter - @Setter - public static class ProducerData { - - /** - * This mirrors the ConfigConstant {@see SpringwolfConfigConstants#SPRINGWOLF_SCANNER_RABBIT_LISTENER_ENABLED} - */ - @Deprecated(forRemoval = true) - private boolean enabled = true; - } } @Getter diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/CustomBeanAsyncApiDocketConfiguration.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/CustomBeanAsyncApiDocketConfiguration.java deleted file mode 100644 index 161893097..000000000 --- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/CustomBeanAsyncApiDocketConfiguration.java +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.stavshamir.springwolf; - -import com.asyncapi.v2._6_0.model.info.Info; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocket; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; - -@TestConfiguration -@ConditionalOnProperty(name = "test.springwolf.asyncapidocket", havingValue = "true", matchIfMissing = true) -class CustomBeanAsyncApiDocketConfiguration { - @Bean - public AsyncApiDocket docket() { - return AsyncApiDocket.builder() - .info(Info.builder() - .title("CustomBeanAsyncApiDocketConfiguration-title") - .version("CustomBeanAsyncApiDocketConfiguration-version") - .build()) - .build(); - } -} diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/SpringContextIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/SpringContextIntegrationTest.java index 7cdb77aa3..5edb1c93e 100644 --- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/SpringContextIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/SpringContextIntegrationTest.java @@ -3,66 +3,32 @@ import io.github.stavshamir.springwolf.asyncapi.AsyncApiService; import io.github.stavshamir.springwolf.fixtures.MinimalIntegrationTestContextConfiguration; -import io.github.stavshamir.springwolf.fixtures.ObjectMapperTestConfiguration; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; -import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; +@ExtendWith(SpringExtension.class) +@MinimalIntegrationTestContextConfiguration public class SpringContextIntegrationTest { + @Autowired + private ApplicationContext context; - @ExtendWith(SpringExtension.class) - @Nested - @ContextConfiguration( - classes = { - SpringwolfAutoConfiguration.class, - ObjectMapperTestConfiguration.class, - CustomBeanAsyncApiDocketConfiguration.class, // user has defined an own AsyncApiDocket bean - }) - class AsyncApiDocketTest { + @Autowired + private AsyncApiService asyncApiService; - @Autowired - private ApplicationContext context; + @Test + void testContextWithApplicationProperties() { + assertNotNull(context); - @Autowired - private AsyncApiService asyncApiService; - - @Test - void testContextWithAsyncApiDocketBean() { - assertNotNull(context); - - assertThat(asyncApiService.getAsyncAPI()).isNotNull(); - assertThat(asyncApiService.getAsyncAPI().getInfo().getTitle()) - .isEqualTo("CustomBeanAsyncApiDocketConfiguration-title"); - } - } - - @ExtendWith(SpringExtension.class) - @Nested - @MinimalIntegrationTestContextConfiguration - class ApplicationPropertiesConfigurationTest { - - @Autowired - private ApplicationContext context; - - @Autowired - private AsyncApiService asyncApiService; - - @Test - void testContextWithApplicationProperties() { - assertNotNull(context); - - assertThat(asyncApiService.getAsyncAPI()).isNotNull(); - assertThat(asyncApiService.getAsyncAPI().getInfo().getTitle()) - .isEqualTo("Info title was loaded from spring properties"); - assertThat(asyncApiService.getAsyncAPI().getDefaultContentType()).isEqualTo("application/yaml"); - assertThat(asyncApiService.getAsyncAPI().getId()).isEqualTo("urn:io:github:stavshamir:springwolf:example"); - } + assertThat(asyncApiService.getAsyncAPI()).isNotNull(); + assertThat(asyncApiService.getAsyncAPI().getInfo().getTitle()) + .isEqualTo("Info title was loaded from spring properties"); + assertThat(asyncApiService.getAsyncAPI().getDefaultContentType()).isEqualTo("application/yaml"); + assertThat(asyncApiService.getAsyncAPI().getId()).isEqualTo("urn:io:github:stavshamir:springwolf:example"); } } diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiServiceIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiServiceIntegrationTest.java index cc7efe013..861780b2a 100644 --- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiServiceIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiServiceIntegrationTest.java @@ -2,35 +2,31 @@ package io.github.stavshamir.springwolf.asyncapi; import com.asyncapi.v2._6_0.model.channel.ChannelItem; +import com.asyncapi.v2._6_0.model.channel.operation.Operation; import com.asyncapi.v2._6_0.model.info.Info; import com.asyncapi.v2._6_0.model.server.Server; -import com.asyncapi.v2.binding.message.kafka.KafkaMessageBinding; import com.asyncapi.v2.binding.operation.kafka.KafkaOperationBinding; -import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.ConsumerOperationDataScanner; -import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.ProducerOperationDataScanner; -import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor; import io.github.stavshamir.springwolf.asyncapi.types.AsyncAPI; -import io.github.stavshamir.springwolf.asyncapi.types.ConsumerData; -import io.github.stavshamir.springwolf.asyncapi.types.ProducerData; import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.Message; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocket; import io.github.stavshamir.springwolf.configuration.DefaultAsyncApiDocketService; import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties; -import io.github.stavshamir.springwolf.schemas.DefaultSchemasService; -import io.github.stavshamir.springwolf.schemas.example.ExampleJsonGenerator; +import io.github.stavshamir.springwolf.schemas.SchemasService; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.core.annotation.Order; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @ContextConfiguration( @@ -38,66 +34,60 @@ SpringwolfConfigProperties.class, DefaultAsyncApiDocketService.class, DefaultAsyncApiService.class, - DefaultChannelsService.class, - DefaultSchemasService.class, - PayloadClassExtractor.class, - ExampleJsonGenerator.class, - ProducerOperationDataScanner.class, - ConsumerOperationDataScanner.class, }) @Import({ - DefaultAsyncApiServiceIntegrationTest.DefaultAsyncApiServiceTestConfiguration.class, DefaultAsyncApiServiceIntegrationTest.TestDescriptionCustomizer.class, DefaultAsyncApiServiceIntegrationTest.TestDescriptionCustomizer2.class }) +@EnableConfigurationProperties +@TestPropertySource( + properties = { + "springwolf.enabled=true", + "springwolf.docket.info.title=Info title was loaded from spring properties", + "springwolf.docket.info.version=1.0.0", + "springwolf.docket.id=urn:io:github:stavshamir:springwolf:example", + "springwolf.docket.default-content-type=application/yaml", + "springwolf.docket.base-package=io.github.stavshamir.springwolf.example", + "springwolf.docket.servers.test-protocol.protocol=test", + "springwolf.docket.servers.test-protocol.url=some-server:1234", + }) class DefaultAsyncApiServiceIntegrationTest { - @TestConfiguration - public static class DefaultAsyncApiServiceTestConfiguration { - - @Bean - public AsyncApiDocket docket() { - Info info = Info.builder().title("Test").version("1.0.0").build(); - - ProducerData kafkaProducerData = ProducerData.builder() - .channelName("producer-topic") - .description("producer-topic-description") - .payloadType(String.class) - .operationBinding(Map.of("kafka", new KafkaOperationBinding())) - .messageBinding(Map.of("kafka", new KafkaMessageBinding())) - .build(); - - ConsumerData kafkaConsumerData = ConsumerData.builder() - .channelName("consumer-topic") - .description("consumer-topic-description") - .payloadType(String.class) - .operationBinding(Map.of("kafka", new KafkaOperationBinding())) - .messageBinding(Map.of("kafka", new KafkaMessageBinding())) - .build(); - - return AsyncApiDocket.builder() - .info(info) - .basePackage("package") - .server( - "kafka", - Server.builder().protocol("kafka").url("kafka:9092").build()) - .producer(kafkaProducerData) - .consumer(kafkaConsumerData) - .build(); - } - } + @MockBean + private ChannelsService channelsService; - @Autowired - private AsyncApiDocket docket; + @MockBean + private SchemasService schemasService; @Autowired - private DefaultAsyncApiService asyncApiService; + private AsyncApiService asyncApiService; + + @BeforeEach + public void setUp() { + when(channelsService.findChannels()) + .thenReturn(Map.of( + "consumer-topic", + ChannelItem.builder() + .subscribe(Operation.builder() + .bindings(Map.of("kafka", new KafkaOperationBinding())) + .message(Message.builder().build()) + .build()) + .build(), + "producer-topic", + ChannelItem.builder() + .publish(Operation.builder() + .bindings(Map.of("kafka", new KafkaOperationBinding())) + .message(Message.builder().build()) + .build()) + .build())); + } @Test void getAsyncAPI_info_should_be_correct() { Info actualInfo = asyncApiService.getAsyncAPI().getInfo(); - assertThat(actualInfo).isEqualTo(docket.getInfo()); + assertThat(actualInfo.getTitle()).isEqualTo("Info title was loaded from spring properties"); + assertThat(actualInfo.getVersion()).isEqualTo("1.0.0"); assertThat(actualInfo.getDescription()).isEqualTo("AsyncApiInfoDescriptionCustomizer2"); } @@ -105,33 +95,30 @@ void getAsyncAPI_info_should_be_correct() { void getAsyncAPI_servers_should_be_correct() { Map actualServers = asyncApiService.getAsyncAPI().getServers(); - assertThat(actualServers).isEqualTo(docket.getServers()); + assertThat(actualServers.get("test-protocol").getProtocol()).isEqualTo("test"); + assertThat(actualServers.get("test-protocol").getUrl()).isEqualTo("some-server:1234"); } @Test - void getAsyncAPI_producers_should_be_correct() { + void getAsyncAPI_channels_should_be_correct() { Map actualChannels = asyncApiService.getAsyncAPI().getChannels(); - assertThat(actualChannels).isNotEmpty().containsKey("producer-topic"); - - final ChannelItem channel = actualChannels.get("producer-topic"); - assertThat(channel.getSubscribe()).isNotNull(); - final Message message = (Message) channel.getSubscribe().getMessage(); - assertThat(message.getDescription()).isNull(); - assertThat(message.getBindings()).isEqualTo(Map.of("kafka", new KafkaMessageBinding())); - } - - @Test - void getAsyncAPI_consumers_should_be_correct() { - Map actualChannels = asyncApiService.getAsyncAPI().getChannels(); + assertThat(actualChannels).hasSize(2); assertThat(actualChannels).isNotEmpty().containsKey("consumer-topic"); + final ChannelItem consumerChannel = actualChannels.get("consumer-topic"); + assertThat(consumerChannel.getSubscribe()).isNotNull(); + assertThat(consumerChannel.getSubscribe().getBindings()) + .isEqualTo(Map.of("kafka", new KafkaOperationBinding())); + assertThat(((Message) consumerChannel.getSubscribe().getMessage()).getDescription()) + .isNull(); - final ChannelItem channel = actualChannels.get("consumer-topic"); - assertThat(channel.getPublish()).isNotNull(); - final Message message = (Message) channel.getPublish().getMessage(); - assertThat(message.getDescription()).isNull(); - assertThat(message.getBindings()).isEqualTo(Map.of("kafka", new KafkaMessageBinding())); + assertThat(actualChannels).isNotEmpty().containsKey("producer-topic"); + final ChannelItem publishChannel = actualChannels.get("producer-topic"); + assertThat(publishChannel.getPublish()).isNotNull(); + assertThat(publishChannel.getPublish().getBindings()).isEqualTo(Map.of("kafka", new KafkaOperationBinding())); + assertThat(((Message) publishChannel.getPublish().getMessage()).getDescription()) + .isNull(); } @Order(TestDescriptionCustomizer.CUSTOMIZER_ORDER) diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ConsumerOperationDataScannerIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ConsumerOperationDataScannerIntegrationTest.java deleted file mode 100644 index 689be6566..000000000 --- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ConsumerOperationDataScannerIntegrationTest.java +++ /dev/null @@ -1,229 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata; - -import com.asyncapi.v2._6_0.model.channel.ChannelItem; -import com.asyncapi.v2._6_0.model.channel.operation.Operation; -import com.asyncapi.v2._6_0.model.info.Info; -import com.asyncapi.v2._6_0.model.server.Server; -import com.asyncapi.v2.binding.channel.kafka.KafkaChannelBinding; -import com.asyncapi.v2.binding.message.kafka.KafkaMessageBinding; -import com.asyncapi.v2.binding.operation.kafka.KafkaOperationBinding; -import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor; -import io.github.stavshamir.springwolf.asyncapi.types.ConsumerData; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.Message; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.PayloadReference; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.AsyncHeaders; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.HeaderReference; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocket; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService; -import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties; -import io.github.stavshamir.springwolf.schemas.DefaultSchemasService; -import io.github.stavshamir.springwolf.schemas.example.ExampleJsonGenerator; -import io.swagger.v3.oas.annotations.media.Schema; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static io.github.stavshamir.springwolf.asyncapi.MessageHelper.toMessageObjectOrComposition; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.when; - -@ExtendWith(SpringExtension.class) -@ContextConfiguration( - classes = { - ConsumerOperationDataScanner.class, - DefaultSchemasService.class, - PayloadClassExtractor.class, - ExampleJsonGenerator.class, - SpringwolfConfigProperties.class, - }) -class ConsumerOperationDataScannerIntegrationTest { - - @Autowired - private ConsumerOperationDataScanner scanner; - - @MockBean - private AsyncApiDocketService asyncApiDocketService; - - @BeforeEach - public void defaultDocketSetup() { - AsyncApiDocket docket = AsyncApiDocket.builder() - .info(Info.builder() - .title("Default Asyncapi Title") - .version("1.0.0") - .build()) - .server("kafka1", new Server()) - .server("kafka2", new Server()) - .build(); - - when(asyncApiDocketService.getAsyncApiDocket()).thenReturn(docket); - } - - @Test - void allFieldsConsumerData() { - // Given a consumer data with all fields set - String channelName = "example-consumer-topic-foo1"; - String description = channelName + "-description"; - ConsumerData consumerData = ConsumerData.builder() - .channelName(channelName) - .description(description) - .channelBinding(Map.of("kafka", new KafkaChannelBinding())) - .operationBinding(Map.of("kafka", new KafkaOperationBinding())) - .messageBinding(Map.of("kafka", new KafkaMessageBinding())) - .payloadType(ExamplePayloadDto.class) - .build(); - - mockConsumers(List.of(consumerData)); - - // When scanning for consumers - Map consumerChannels = scanner.scan(); - - // Then the channel should be created correctly - assertThat(consumerChannels).containsKey(channelName); - - String messageDescription = "Example Payload DTO Description"; - Operation operation = Operation.builder() - .description(description) - .operationId("example-consumer-topic-foo1_publish") - .bindings(Map.of("kafka", new KafkaOperationBinding())) - .message(Message.builder() - .name(ExamplePayloadDto.class.getName()) - .title(ExamplePayloadDto.class.getSimpleName()) - .description(messageDescription) - .payload(PayloadReference.fromModelName(ExamplePayloadDto.class.getSimpleName())) - .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())) - .bindings(Map.of("kafka", new KafkaMessageBinding())) - .build()) - .build(); - - ChannelItem expectedChannel = ChannelItem.builder() - .bindings(Map.of("kafka", new KafkaChannelBinding())) - .publish(operation) - .build(); - - assertThat(consumerChannels.get(channelName)).isEqualTo(expectedChannel); - } - - @Test - void missingFieldConsumerData() { - // Given a consumer data with missing fields - String channelName = "example-consumer-topic-foo1"; - ConsumerData consumerData = - ConsumerData.builder().channelName(channelName).build(); - - mockConsumers(List.of(consumerData)); - - // When scanning for consumers - Map consumerChannels = scanner.scan(); - - // Then the channel is not created, and no exception is thrown - assertThat(consumerChannels).isEmpty(); - } - - @Test - void multipleConsumersForSameTopic() { - // Given a multiple ConsumerData objects for the same topic - String channelName = "example-consumer-topic"; - String description1 = channelName + "-description1"; - String description2 = channelName + "-description2"; - - ConsumerData consumerData1 = ConsumerData.builder() - .channelName(channelName) - .description(description1) - .server("kafka1") - .channelBinding(Map.of("kafka", new KafkaChannelBinding())) - .operationBinding(Map.of("kafka", new KafkaOperationBinding())) - .messageBinding(Map.of("kafka", new KafkaMessageBinding())) - .payloadType(ExamplePayloadDto.class) - .build(); - - ConsumerData consumerData2 = ConsumerData.builder() - .channelName(channelName) - .description(description2) - .server("kafka2") - .channelBinding(Map.of("kafka", new KafkaChannelBinding())) - .operationBinding(Map.of("kafka", new KafkaOperationBinding())) - .messageBinding(Map.of("kafka", new KafkaMessageBinding())) - .payloadType(AnotherExamplePayloadDto.class) - .headers(AsyncHeaders.NOT_USED) - .build(); - - mockConsumers(List.of(consumerData1, consumerData2)); - - // When scanning for consumers - Map consumerChannels = scanner.scan(); - - // Then one channel is created for the ConsumerData objects with multiple messages - assertThat(consumerChannels).hasSize(1).containsKey(channelName); - - String messageDescription1 = "Example Payload DTO Description"; - String messageDescription2 = "Another Example Payload DTO Description"; - Set messages = Set.of( - Message.builder() - .name(ExamplePayloadDto.class.getName()) - .title(ExamplePayloadDto.class.getSimpleName()) - .description(messageDescription1) - .payload(PayloadReference.fromModelName(ExamplePayloadDto.class.getSimpleName())) - .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())) - .bindings(Map.of("kafka", new KafkaMessageBinding())) - .build(), - Message.builder() - .name(AnotherExamplePayloadDto.class.getName()) - .title(AnotherExamplePayloadDto.class.getSimpleName()) - .description(messageDescription2) - .payload(PayloadReference.fromModelName(AnotherExamplePayloadDto.class.getSimpleName())) - .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_USED.getSchemaName())) - .bindings(Map.of("kafka", new KafkaMessageBinding())) - .build()); - - Operation operation = Operation.builder() - .description(description1) - .operationId("example-consumer-topic_publish") - .bindings(Map.of("kafka", new KafkaOperationBinding())) - .message(toMessageObjectOrComposition(messages)) - .build(); - - ChannelItem expectedChannel = ChannelItem.builder() - .servers(List.of("kafka1")) // First Consumerdata Server Entry - .bindings(Map.of("kafka", new KafkaChannelBinding())) - .publish(operation) - .build(); - - assertThat(consumerChannels.get(channelName)).isEqualTo(expectedChannel); - } - - private void mockConsumers(Collection consumers) { - AsyncApiDocket asyncApiDocket = AsyncApiDocket.builder() - .info(Info.builder() - .title("ConsumerOperationDataScannerTest-title") - .version("ConsumerOperationDataScannerTest-version") - .build()) - .server("kafka1", new Server()) - .server("kafka2", new Server()) - .consumers(consumers) - .build(); - - reset(asyncApiDocketService); - when(asyncApiDocketService.getAsyncApiDocket()).thenReturn(asyncApiDocket); - } - - @Schema(description = "Example Payload DTO Description") - static class ExamplePayloadDto { - private String foo; - } - - @Schema(description = "Another Example Payload DTO Description") - static class AnotherExamplePayloadDto { - private String bar; - } -} diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ProducerOperationDataScannerIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ProducerOperationDataScannerIntegrationTest.java deleted file mode 100644 index 575bcb220..000000000 --- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/operationdata/ProducerOperationDataScannerIntegrationTest.java +++ /dev/null @@ -1,228 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -package io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata; - -import com.asyncapi.v2._6_0.model.channel.ChannelItem; -import com.asyncapi.v2._6_0.model.channel.operation.Operation; -import com.asyncapi.v2._6_0.model.info.Info; -import com.asyncapi.v2._6_0.model.server.Server; -import com.asyncapi.v2.binding.channel.kafka.KafkaChannelBinding; -import com.asyncapi.v2.binding.message.kafka.KafkaMessageBinding; -import com.asyncapi.v2.binding.operation.kafka.KafkaOperationBinding; -import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor; -import io.github.stavshamir.springwolf.asyncapi.types.ProducerData; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.Message; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.PayloadReference; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.AsyncHeaders; -import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.HeaderReference; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocket; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService; -import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties; -import io.github.stavshamir.springwolf.schemas.DefaultSchemasService; -import io.github.stavshamir.springwolf.schemas.example.ExampleJsonGenerator; -import io.swagger.v3.oas.annotations.media.Schema; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static io.github.stavshamir.springwolf.asyncapi.MessageHelper.toMessageObjectOrComposition; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.when; - -@ExtendWith(SpringExtension.class) -@ContextConfiguration( - classes = { - ProducerOperationDataScanner.class, - DefaultSchemasService.class, - PayloadClassExtractor.class, - ExampleJsonGenerator.class, - SpringwolfConfigProperties.class, - }) -class ProducerOperationDataScannerIntegrationTest { - - @Autowired - private ProducerOperationDataScanner scanner; - - @MockBean - private AsyncApiDocketService asyncApiDocketService; - - @BeforeEach - public void defaultDocketSetup() { - AsyncApiDocket docket = AsyncApiDocket.builder() - .info(Info.builder() - .title("Default Asyncapi Title") - .version("1.0.0") - .build()) - .server("kafka1", new Server()) - .server("kafka2", new Server()) - .build(); - - when(asyncApiDocketService.getAsyncApiDocket()).thenReturn(docket); - } - - @Test - void allFieldsProducerData() { - // Given a producer data with all fields set - String channelName = "example-producer-topic-foo1"; - String description = channelName + "-description"; - ProducerData producerData = ProducerData.builder() - .channelName(channelName) - .description(description) - .channelBinding(Map.of("kafka", new KafkaChannelBinding())) - .operationBinding(Map.of("kafka", new KafkaOperationBinding())) - .messageBinding(Map.of("kafka", new KafkaMessageBinding())) - .payloadType(ExamplePayloadDto.class) - .build(); - - mockProducers(List.of(producerData)); - - // When scanning for producers - Map producerChannels = scanner.scan(); - - // Then the channel should be created correctly - assertThat(producerChannels).containsKey(channelName); - - String messageDescription1 = "Example Payload DTO Description"; - Operation operation = Operation.builder() - .description(description) - .operationId("example-producer-topic-foo1_subscribe") - .bindings(Map.of("kafka", new KafkaOperationBinding())) - .message(Message.builder() - .name(ExamplePayloadDto.class.getName()) - .title(ExamplePayloadDto.class.getSimpleName()) - .description(messageDescription1) - .payload(PayloadReference.fromModelName(ExamplePayloadDto.class.getSimpleName())) - .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())) - .bindings(Map.of("kafka", new KafkaMessageBinding())) - .build()) - .build(); - - ChannelItem expectedChannel = ChannelItem.builder() - .bindings(Map.of("kafka", new KafkaChannelBinding())) - .subscribe(operation) - .build(); - - assertThat(producerChannels.get(channelName)).isEqualTo(expectedChannel); - } - - @Test - void missingFieldProducerData() { - // Given a producer data with missing fields - String channelName = "example-producer-topic-foo1"; - ProducerData producerData = - ProducerData.builder().channelName(channelName).build(); - - mockProducers(List.of(producerData)); - - // When scanning for producers - Map producerChannels = scanner.scan(); - - // Then the channel is not created, and no exception is thrown - assertThat(producerChannels).isEmpty(); - } - - @Test - void multipleProducersForSameTopic() { - // Given a multiple ProducerData objects for the same topic - String channelName = "example-producer-topic"; - String description1 = channelName + "-description1"; - String description2 = channelName + "-description2"; - - ProducerData producerData1 = ProducerData.builder() - .channelName(channelName) - .description(description1) - .server("kafka1") - .channelBinding(Map.of("kafka", new KafkaChannelBinding())) - .operationBinding(Map.of("kafka", new KafkaOperationBinding())) - .messageBinding(Map.of("kafka", new KafkaMessageBinding())) - .payloadType(ExamplePayloadDto.class) - .build(); - - ProducerData producerData2 = ProducerData.builder() - .channelName(channelName) - .description(description2) - .server("kafka2") - .channelBinding(Map.of("kafka", new KafkaChannelBinding())) - .operationBinding(Map.of("kafka", new KafkaOperationBinding())) - .messageBinding(Map.of("kafka", new KafkaMessageBinding())) - .payloadType(AnotherExamplePayloadDto.class) - .headers(AsyncHeaders.NOT_USED) - .build(); - - mockProducers(List.of(producerData1, producerData2)); - - // When scanning for producers - Map producerChannels = scanner.scan(); - - // Then one channel is created for the ProducerData objects with multiple messages - assertThat(producerChannels).hasSize(1).containsKey(channelName); - - String messageDescription1 = "Example Payload DTO Description"; - String messageDescription2 = "Another Example Payload DTO Description"; - Set messages = Set.of( - Message.builder() - .name(ExamplePayloadDto.class.getName()) - .title(ExamplePayloadDto.class.getSimpleName()) - .description(messageDescription1) - .payload(PayloadReference.fromModelName(ExamplePayloadDto.class.getSimpleName())) - .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())) - .bindings(Map.of("kafka", new KafkaMessageBinding())) - .build(), - Message.builder() - .name(AnotherExamplePayloadDto.class.getName()) - .title(AnotherExamplePayloadDto.class.getSimpleName()) - .description(messageDescription2) - .payload(PayloadReference.fromModelName(AnotherExamplePayloadDto.class.getSimpleName())) - .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_USED.getSchemaName())) - .bindings(Map.of("kafka", new KafkaMessageBinding())) - .build()); - - Operation operation = Operation.builder() - .description(description1) - .operationId("example-producer-topic_subscribe") - .bindings(Map.of("kafka", new KafkaOperationBinding())) - .message(toMessageObjectOrComposition(messages)) - .build(); - - ChannelItem expectedChannel = ChannelItem.builder() - .servers(List.of("kafka1")) // First Consumerdata Server Entry - .bindings(Map.of("kafka", new KafkaChannelBinding())) - .subscribe(operation) - .build(); - - assertThat(producerChannels.get(channelName)).isEqualTo(expectedChannel); - } - - private void mockProducers(Collection producers) { - AsyncApiDocket asyncApiDocket = AsyncApiDocket.builder() - .info(Info.builder() - .title("ProducerOperationDataScannerTest-title") - .version("ProducerOperationDataScannerTest-version") - .build()) - .server("kafka1", new Server()) - .server("kafka2", new Server()) - .producers(producers) - .build(); - reset(asyncApiDocketService); - when(asyncApiDocketService.getAsyncApiDocket()).thenReturn(asyncApiDocket); - } - - @Schema(description = "Example Payload DTO Description") - static class ExamplePayloadDto { - private String foo; - } - - @Schema(description = "Another Example Payload DTO Description") - static class AnotherExamplePayloadDto { - private String bar; - } -} diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketServiceIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketServiceIntegrationTest.java index 5c452b752..b283bab65 100644 --- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketServiceIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketServiceIntegrationTest.java @@ -1,106 +1,44 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.stavshamir.springwolf.configuration; -import com.asyncapi.v2._6_0.model.info.Info; -import com.asyncapi.v2._6_0.model.server.Server; import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.assertThat; +@ExtendWith(SpringExtension.class) +@Nested +@ContextConfiguration( + classes = { + DefaultAsyncApiDocketService.class, + }) +@EnableConfigurationProperties(SpringwolfConfigProperties.class) +@TestPropertySource( + properties = { + "springwolf.enabled=true", + "springwolf.docket.info.title=Info title was loaded from spring properties", + "springwolf.docket.info.version=1.0.0", + "springwolf.docket.info.extension-fields.x-api-name=api-name", + "springwolf.docket.base-package=io.github.stavshamir.springwolf.example", + "springwolf.docket.servers.test-protocol.protocol=test", + "springwolf.docket.servers.test-protocol.url=some-server:1234" + }) public class DefaultAsyncApiDocketServiceIntegrationTest { - - @ExtendWith(SpringExtension.class) - @Nested - @ContextConfiguration( - classes = { - DefaultAsyncApiDocketService.class, - }) - @EnableConfigurationProperties(SpringwolfConfigProperties.class) - @TestPropertySource( - properties = { - "springwolf.enabled=true", - "springwolf.docket.info.title=Info title was loaded from spring properties", - "springwolf.docket.info.version=1.0.0", - "springwolf.docket.info.extension-fields.x-api-name=api-name", - "springwolf.docket.base-package=io.github.stavshamir.springwolf.example", - "springwolf.docket.servers.test-protocol.protocol=test", - "springwolf.docket.servers.test-protocol.url=some-server:1234" - }) - class DocketWillBeAutomaticallyCreateIfNoCustomDocketIsPresentTest { - - @Autowired - private DefaultAsyncApiDocketService asyncApiDocketService; - - @Test - void testDocketContentShouldBeLoadedFromProperties() { - AsyncApiDocket docket = asyncApiDocketService.getAsyncApiDocket(); - assertThat(docket).isNotNull(); - assertThat(docket.getInfo().getTitle()).isEqualTo("Info title was loaded from spring properties"); - assertThat(docket.getInfo().getExtensionFields().get("x-api-name")).isEqualTo("api-name"); - } - } - - @ExtendWith(SpringExtension.class) - @ContextConfiguration( - classes = { - DefaultAsyncApiDocketService.class, - }) - @EnableConfigurationProperties(SpringwolfConfigProperties.class) - @TestPropertySource( - properties = { - "springwolf.enabled=true", - "springwolf.docket.info.title=Docket was loaded from spring properties", - "springwolf.docket.info.version=1.0.0", - "springwolf.docket.base-package=io.github.stavshamir.springwolf.example", - "springwolf.docket.servers.test-protocol.protocol=test", - "springwolf.docket.servers.test-protocol.url=some-server:1234" - }) - @Import(DocketWillNotBeAutomaticallyCreateIfCustomDocketIsPresentTest.CustomAsyncApiDocketConfiguration.class) - @Nested - class DocketWillNotBeAutomaticallyCreateIfCustomDocketIsPresentTest { - - @TestConfiguration - public static class CustomAsyncApiDocketConfiguration { - @Bean - public AsyncApiDocket docket() { - Info info = Info.builder() - .title("Custom docket was used") - .version("1.0.0") - .build(); - - return AsyncApiDocket.builder() - .info(info) - .basePackage("package") - .server( - "kafka", - Server.builder() - .protocol("kafka") - .url("kafka:9092") - .build()) - .build(); - } - } - - @Autowired - private DefaultAsyncApiDocketService asyncApiDocketService; - - @Test - void testDocketContentShouldNotBeLoadedFromProperties() { - AsyncApiDocket docket = asyncApiDocketService.getAsyncApiDocket(); - - assertThat(docket).isNotNull(); - assertThat(docket.getInfo().getTitle()).isEqualTo("Custom docket was used"); - } + @Autowired + private DefaultAsyncApiDocketService asyncApiDocketService; + + @Test + void testDocketContentShouldBeLoadedFromProperties() { + AsyncApiDocket docket = asyncApiDocketService.getAsyncApiDocket(); + assertThat(docket).isNotNull(); + assertThat(docket.getInfo().getTitle()).isEqualTo("Info title was loaded from spring properties"); + assertThat(docket.getInfo().getExtensionFields().get("x-api-name")).isEqualTo("api-name"); } } diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketServiceTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketServiceTest.java index 4b9bfd96f..6317385bf 100644 --- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketServiceTest.java +++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/configuration/DefaultAsyncApiDocketServiceTest.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test; import java.util.Map; -import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.util.Maps.newHashMap; @@ -41,7 +40,7 @@ void testServiceShouldMapAllPropertiesToTheDocket() { SpringwolfConfigProperties properties = new SpringwolfConfigProperties(); properties.setDocket(configDocket); - AsyncApiDocketService docketService = new DefaultAsyncApiDocketService(Optional.empty(), properties); + AsyncApiDocketService docketService = new DefaultAsyncApiDocketService(properties); // when AsyncApiDocket asyncApiDocket = docketService.getAsyncApiDocket(); @@ -59,34 +58,7 @@ void testServiceShouldMapAllPropertiesToTheDocket() { } @Test - void docketServiceShouldDeliverCachedConfigDocketBean() { - // repeatable invocations of AsyncApiDocketService.getAsyncApiDocket() should return the same docket instance - // if a ConfigDocket @Bean is present. - - // given - AsyncApiDocket customDocket = AsyncApiDocket.builder() - .basePackage("test-base-package") - .info(com.asyncapi.v2._6_0.model.info.Info.builder() - .title("some-title") - .version("some-version") - .build()) - .build(); - - SpringwolfConfigProperties configProperties = new SpringwolfConfigProperties(); - - AsyncApiDocketService docketService = - new DefaultAsyncApiDocketService(Optional.of(customDocket), configProperties); - - // when - AsyncApiDocket asyncApiDocket = docketService.getAsyncApiDocket(); - - // then - // second invocation should again return same instance - assertThat(docketService.getAsyncApiDocket()).isSameAs(asyncApiDocket); - } - - @Test - void docketServiceShouldDeliverCachedSpringPropertiesBasedDocket() { + void docketServiceShouldDeliverCachedDocket() { // repeatable invocations of AsyncApiDocketService.getAsyncApiDocket() should return the same docket instance // if docket is based on environment properties. @@ -107,7 +79,7 @@ void docketServiceShouldDeliverCachedSpringPropertiesBasedDocket() { SpringwolfConfigProperties configProperties = new SpringwolfConfigProperties(); configProperties.setDocket(configDocket); - AsyncApiDocketService docketService = new DefaultAsyncApiDocketService(Optional.empty(), configProperties); + AsyncApiDocketService docketService = new DefaultAsyncApiDocketService(configProperties); // when AsyncApiDocket asyncApiDocket = docketService.getAsyncApiDocket(); diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/AutoConfigurationIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/AutoConfigurationIntegrationTest.java index f1a195568..b59ed7276 100644 --- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/AutoConfigurationIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/AutoConfigurationIntegrationTest.java @@ -2,6 +2,7 @@ package io.github.stavshamir.springwolf.integrationtests; import io.github.stavshamir.springwolf.asyncapi.controller.AsyncApiController; +import io.github.stavshamir.springwolf.fixtures.MinimalIntegrationTestContextConfiguration; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.ObjectProvider; @@ -18,6 +19,7 @@ public class AutoConfigurationIntegrationTest { @Nested @SpringBootTest(classes = TestApplication.class) + @MinimalIntegrationTestContextConfiguration class TestSpringwolfEnabled { @Autowired private AsyncApiController asyncApiController; @@ -31,6 +33,7 @@ void autoconfigurationShouldBeLoaded() { @Nested @SpringBootTest(classes = TestApplication.class) @TestPropertySource(properties = {"springwolf.enabled=false"}) + @MinimalIntegrationTestContextConfiguration class TestSpringwolfDisabled { @Autowired diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/InitModeIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/InitModeIntegrationTest.java index 68070d0a5..7ffa31d97 100644 --- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/InitModeIntegrationTest.java +++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/InitModeIntegrationTest.java @@ -3,6 +3,7 @@ import io.github.stavshamir.springwolf.asyncapi.DefaultAsyncApiService; import io.github.stavshamir.springwolf.asyncapi.controller.AsyncApiController; +import io.github.stavshamir.springwolf.fixtures.MinimalIntegrationTestContextConfiguration; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -23,6 +24,7 @@ public class InitModeIntegrationTest { @Nested @SpringBootTest(classes = TestApplication.class) @TestPropertySource(properties = {"springwolf.init-mode=fail_fast"}) + @MinimalIntegrationTestContextConfiguration class TestInitModeFailFast { @Autowired @@ -40,6 +42,7 @@ void asyncApiShouldHaveBeenInitializedDuringStartup() { @Nested @SpringBootTest(classes = TestApplication.class) @TestPropertySource(properties = {"springwolf.init-mode=background"}) + @MinimalIntegrationTestContextConfiguration class TestInitModeBackground { @Autowired @@ -61,7 +64,7 @@ class TestThrowExceptionWhenConfigurationError { @Test void applicationShouldNotStart() { // using title=empty to trigger a validation exception during startup - String[] args = new String[] {"--test.springwolf.asyncapidocket=false", "--springwolf.docket.info.title="}; + String[] args = new String[] {"--springwolf.docket.info.title="}; try { SpringApplication.run(TestApplication.class, args); @@ -75,6 +78,7 @@ void applicationShouldNotStart() { @Nested @SpringBootTest(classes = TestApplication.class) @TestPropertySource(properties = {"springwolf.enabled=false"}) + @MinimalIntegrationTestContextConfiguration class TestSpringwolfDisabled { @Autowired(required = false) diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/TestApplication.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/TestApplication.java index f243cb27c..6c12f0f98 100644 --- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/TestApplication.java +++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/integrationtests/TestApplication.java @@ -1,26 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 package io.github.stavshamir.springwolf.integrationtests; -import com.asyncapi.v2._6_0.model.info.Info; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocket; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.test.context.TestConfiguration; -import org.springframework.context.annotation.Bean; @SpringBootApplication -class TestApplication { - @TestConfiguration - @ConditionalOnProperty(name = "test.springwolf.asyncapidocket", havingValue = "true", matchIfMissing = true) - public static class TestApplicationConfiguration { - @Bean - public AsyncApiDocket docket() { - return AsyncApiDocket.builder() - .info(Info.builder() - .title("AsyncApiDocketConfiguration-title") - .version("AsyncApiDocketConfiguration-version") - .build()) - .build(); - } - } -} +class TestApplication {} diff --git a/springwolf-plugins/springwolf-amqp-plugin/README.md b/springwolf-plugins/springwolf-amqp-plugin/README.md index be3cde209..6d53340bf 100644 --- a/springwolf-plugins/springwolf-amqp-plugin/README.md +++ b/springwolf-plugins/springwolf-amqp-plugin/README.md @@ -31,39 +31,18 @@ dependencies { } ``` -### Configuration class +### Configuration -Add a configuration class and provide a `AsyncApiDocket` bean: +Add a `application.properties` file: -```java +```properties +springwolf.docket.base-package=io.github.stavshamir.springwolf.example.consumers -@Configuration -public class AsyncApiConfiguration { - - private final String amqpHost = "localhost"; - private final String amqpPort = "5672"; - - @Bean - public AsyncApiDocket asyncApiDocket() { - Info info = Info.builder() - .version("1.0.0") - .title("Springwolf example project - AMQP") - .build(); - - Server amqp = Server.builder() - .protocol("amqp") - .url(String.format("%s:%s", amqpHost, amqpPort)) - .build(); - - return AsyncApiDocket.builder() - .basePackage("io.github.stavshamir.springwolf.example.amqp.consumers") - .info(info) - .server("amqp", amqp) - .build(); - } - -} +springwolf.docket.info.title=${spring.application.name} +springwolf.docket.info.version=1.0.0 +springwolf.docket.servers.amqp.protocol=amqp +springwolf.docket.servers.amqp.url=amqp:5672 ``` The basePackage field must be set with the name of the package containing the classes to be scanned for `@RabbitListener` diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle b/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle index 6745d37ef..f214c38a6 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/build.gradle @@ -38,6 +38,7 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}" testImplementation "org.mockito:mockito-core:${mockitoCoreVersion}" + testImplementation "org.springframework.boot:spring-boot" testImplementation "org.springframework.boot:spring-boot-test" testImplementation "org.springframework:spring-beans" testImplementation "org.springframework:spring-test" diff --git a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/cloudstream/CloudStreamFunctionChannelsScannerIntegrationTest.java b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/cloudstream/CloudStreamFunctionChannelsScannerIntegrationTest.java index d9d3a7781..796546ecb 100644 --- a/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/cloudstream/CloudStreamFunctionChannelsScannerIntegrationTest.java +++ b/springwolf-plugins/springwolf-cloud-stream-plugin/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/cloudstream/CloudStreamFunctionChannelsScannerIntegrationTest.java @@ -3,8 +3,6 @@ import com.asyncapi.v2._6_0.model.channel.ChannelItem; import com.asyncapi.v2._6_0.model.channel.operation.Operation; -import com.asyncapi.v2._6_0.model.info.Info; -import com.asyncapi.v2._6_0.model.server.Server; import io.github.stavshamir.springwolf.asyncapi.scanners.beans.DefaultBeanMethodsScanner; import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor; import io.github.stavshamir.springwolf.asyncapi.scanners.classes.ComponentClassScanner; @@ -16,7 +14,6 @@ import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.bindings.EmptyMessageBinding; import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.AsyncHeaders; import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.HeaderReference; -import io.github.stavshamir.springwolf.configuration.AsyncApiDocket; import io.github.stavshamir.springwolf.configuration.DefaultAsyncApiDocketService; import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties; import io.github.stavshamir.springwolf.schemas.DefaultSchemasService; @@ -25,6 +22,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.cloud.stream.config.BindingProperties; @@ -32,6 +30,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Collections; @@ -57,6 +56,16 @@ FunctionalChannelBeanBuilder.class, SpringwolfConfigProperties.class }) +@TestPropertySource( + properties = { + "springwolf.enabled=true", + "springwolf.docket.info.title=Test", + "springwolf.docket.info.version=1.0.0", + "springwolf.docket.base-package=io.github.stavshamir.springwolf.asyncapi.scanners.channels.cloudstream", + "springwolf.docket.servers.kafka.protocol=kafka", + "springwolf.docket.servers.kafka.url=kafka:9092", + }) +@EnableConfigurationProperties @Import(CloudStreamFunctionChannelsScannerIntegrationTest.Configuration.class) class CloudStreamFunctionChannelsScannerIntegrationTest { @@ -337,19 +346,6 @@ void testFunctionBindingWithSameTopicName() { @TestConfiguration public static class Configuration { - @Bean - public AsyncApiDocket docket() { - Info info = Info.builder().title("Test").version("1.0.0").build(); - - return AsyncApiDocket.builder() - .info(info) - .basePackage(this.getClass().getPackage().getName()) - .server( - "kafka", - Server.builder().protocol("kafka").url("kafka:9092").build()) - .build(); - } - @Bean public Consumer testConsumer() { return System.out::println; diff --git a/springwolf-plugins/springwolf-kafka-plugin/README.md b/springwolf-plugins/springwolf-kafka-plugin/README.md index 5d0f38f85..d7a783a4d 100644 --- a/springwolf-plugins/springwolf-kafka-plugin/README.md +++ b/springwolf-plugins/springwolf-kafka-plugin/README.md @@ -33,41 +33,18 @@ dependencies { } ``` -### Configuration class +### Configuration -Add a configuration class and provide a `AsyncApiDocket` bean: +Add a `application.properties` file: -```java +```properties +springwolf.docket.base-package=io.github.stavshamir.springwolf.example.consumers -@Configuration -public class AsyncApiConfiguration { +springwolf.docket.info.title=${spring.application.name} +springwolf.docket.info.version=1.0.0 - private final static String BOOTSTRAP_SERVERS = "localhost:9092"; // Change to your actual bootstrap server - - @Bean - public AsyncApiDocket asyncApiDocket() { - Info info = Info.builder() - .version("1.0.0") - .title("Springwolf example project") - .build(); - - // Producers are not picked up automatically - if you want them to be included in the asyncapi doc and the UI, - // you will need to build a ProducerData and register it in the docket (line 65) - ProducerData exampleProducerData = ProducerData.builder() - .channelName("example-producer-topic") - .binding(Map.of("kafka", new KafkaOperationBinding())) - .payloadType(ExamplePayloadDto.class) - .build(); - - return AsyncApiDocket.builder() - .basePackage("io.github.stavshamir.springwolf.example.kafka.consumers") // Change to your actual base package of listeners - .info(info) - .server("kafka", Server.builder().protocol("kafka").url(BOOTSTRAP_SERVERS).build()) - .producer(exampleProducerData) - .build(); - } - -} +springwolf.docket.servers.kafka.protocol=kafka +springwolf.docket.servers.kafka.url=${kafka.bootstrap.servers:localhost:29092} ``` The basePackage field must be set with the name of the package containing the classes to be scanned for `@KafkaListener`