messageBinding;
/**
* Operation message.
*/
- protected Message message;
+ protected MessageObject message;
}
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/bindings/EmptyChannelBinding.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/bindings/EmptyChannelBinding.java
index a0b228ca5..ee261b4ca 100644
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/bindings/EmptyChannelBinding.java
+++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/bindings/EmptyChannelBinding.java
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.types.channel.bindings;
-import com.asyncapi.v2.binding.channel.ChannelBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.ChannelBinding;
public class EmptyChannelBinding extends ChannelBinding {}
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/bindings/EmptyOperationBinding.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/bindings/EmptyOperationBinding.java
index 234d863b6..9a3446030 100644
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/bindings/EmptyOperationBinding.java
+++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/bindings/EmptyOperationBinding.java
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.types.channel.operation.bindings;
-import com.asyncapi.v2.binding.operation.OperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;
public class EmptyOperationBinding extends OperationBinding {}
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/Message.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/Message.java
deleted file mode 100644
index da0168c9e..000000000
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/Message.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-package io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message;
-
-import com.asyncapi.v2.binding.message.MessageBinding;
-import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.HeaderReference;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-
-import java.util.Map;
-
-/**
- * Describes a message received on a given channel and operation.
- *
- * @see Message specification
- */
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class Message {
-
- public static final String DEFAULT_SCHEMA_FORMAT = "application/vnd.oai.openapi+json;version=3.0.0";
-
- @Builder.Default
- private String schemaFormat = DEFAULT_SCHEMA_FORMAT;
-
- /**
- * Unique string used to identify the message.
- *
- * The id MUST be unique among all messages described in the API. The messageId value is case-sensitive.
- * Tools and libraries MAY use the messageId to uniquely identify a message, therefore, it is RECOMMENDED to
- * follow common programming naming conventions.
- */
- private String messageId;
-
- /**
- * A machine-friendly name for the message.
- */
- private String name;
-
- /**
- * A human-friendly title for the message.
- */
- private String title;
-
- /**
- * A human-friendly description for the message.
- */
- private String description;
-
- private PayloadReference payload;
-
- private HeaderReference headers;
-
- private Map bindings;
-
- // Why do we add this empty class if Lombok @Builder is doing this job? Because this class is used as an argument
- // in one method. Since Lombok works as an annotation Processor, the JavaDoc tool cannot find the generated class
- // and fails.
- // The alternative to define this class would be to use `delombok` during the Javadoc generation. This is really
- // easy
- // in Maven, but with Gradle seems to be more complicated. Creating an empty class that Lombok overrides and expands
- // is much cleaner.
- public static class MessageBuilder {
- MessageBuilder() {}
- }
-}
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/PayloadReference.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/PayloadReference.java
deleted file mode 100644
index 204ccd334..000000000
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/PayloadReference.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-package io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message;
-
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.ToString;
-
-@NoArgsConstructor
-@EqualsAndHashCode
-@ToString
-public class PayloadReference {
-
- @Getter
- @EqualsAndHashCode.Include
- @ToString.Include
- private String $ref;
-
- private PayloadReference(String $ref) {
- this.$ref = $ref;
- }
-
- public static PayloadReference fromModelName(String modelName) {
- return new PayloadReference("#/components/schemas/" + modelName);
- }
-}
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/bindings/EmptyMessageBinding.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/bindings/EmptyMessageBinding.java
index aad02b8eb..3d7233754 100644
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/bindings/EmptyMessageBinding.java
+++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/bindings/EmptyMessageBinding.java
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.bindings;
-import com.asyncapi.v2.binding.message.MessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding;
public class EmptyMessageBinding extends MessageBinding {}
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersCloudEventConstants.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersCloudEventConstants.java
index 46f2a41d5..d407f7bf3 100644
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersCloudEventConstants.java
+++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersCloudEventConstants.java
@@ -16,4 +16,6 @@ public class AsyncHeadersCloudEventConstants {
public static final String TIME_DESC = "CloudEvent Time Header";
public static final String TYPE = "ce_type";
public static final String TYPE_DESC = "CloudEvent Payload Type Header";
+
+ private AsyncHeadersCloudEventConstants() {}
}
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersNotDocumented.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersNotDocumented.java
index e2b25b227..1c7020034 100644
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersNotDocumented.java
+++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/AsyncHeadersNotDocumented.java
@@ -10,6 +10,6 @@ public class AsyncHeadersNotDocumented implements AsyncHeadersBuilder {
@Override
public AsyncHeaders buildHeaders(Class> payloadType) {
- return AsyncHeaders.NOT_DOCUMENTED;
+ return NOT_DOCUMENTED;
}
}
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/HeaderReference.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/HeaderReference.java
deleted file mode 100644
index 897290dde..000000000
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/asyncapi/types/channel/operation/message/header/HeaderReference.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-package io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header;
-
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.ToString;
-
-@NoArgsConstructor
-@EqualsAndHashCode
-@ToString
-public class HeaderReference {
-
- @Getter
- @EqualsAndHashCode.Include
- @ToString.Include
- private String $ref;
-
- private HeaderReference(String $ref) {
- this.$ref = $ref;
- }
-
- public static HeaderReference fromModelName(String modelName) {
- return new HeaderReference("#/components/schemas/" + modelName);
- }
-}
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 a164568e7..2486db330 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
@@ -1,8 +1,8 @@
// 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.asyncapi.v3.model.info.Info;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.server.Server;
import lombok.Builder;
import lombok.Data;
import lombok.NonNull;
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 c7c2d2569..55f8593a4 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
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.configuration;
-import com.asyncapi.v2._6_0.model.info.Info;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.info.Info;
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigConstants;
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties;
import lombok.RequiredArgsConstructor;
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/properties/SpringwolfConfigConstants.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/properties/SpringwolfConfigConstants.java
index 999231101..e37124bdf 100644
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/properties/SpringwolfConfigConstants.java
+++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/configuration/properties/SpringwolfConfigConstants.java
@@ -28,4 +28,6 @@ public class SpringwolfConfigConstants {
SPRINGWOLF_SCANNER_PREFIX + ".producer-data" + ENABLED;
public static final String SPRINGWOLF_SCHEMA_EXAMPLE_GENERATOR = SPRINGWOLF_CONFIG_PREFIX + ".example-generator";
+
+ private SpringwolfConfigConstants() {}
}
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 74f8c8a51..c91e01bba 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
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.configuration.properties;
-import com.asyncapi.v2._6_0.model.info.Contact;
-import com.asyncapi.v2._6_0.model.info.License;
-import com.asyncapi.v2._6_0.model.server.Server;
+import io.github.stavshamir.springwolf.asyncapi.types.AsyncAPI;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.info.Contact;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.info.License;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.server.Server;
import io.github.stavshamir.springwolf.configuration.AsyncApiDocket;
import lombok.Getter;
import lombok.Setter;
@@ -82,7 +83,7 @@ public static class ConfigDocket {
/**
* Identifier of the application the AsyncAPI document is defining.
*
- * @see com.asyncapi.v2._6_0.model.AsyncAPI#id
+ * @see AsyncAPI#getId()
*/
@Nullable
private String id;
@@ -90,7 +91,7 @@ public static class ConfigDocket {
/**
* A string representing the default content type to use when encoding/decoding a message's payload.
*
- * @see com.asyncapi.v2._6_0.model.AsyncAPI#getdefaultContentType
+ * @see AsyncAPI#getDefaultContentType()
*/
@Nullable
private String defaultContentType;
@@ -101,7 +102,7 @@ public static class ConfigDocket {
/**
* The object provides metadata about the API. The metadata can be used by the clients if needed.
*
- * @see com.asyncapi.v2._6_0.model.info.Info
+ * @see Info
*/
@Nullable
private Info info;
@@ -113,7 +114,7 @@ public static class Info {
/**
* The title of the application
*
- * @see com.asyncapi.v2._6_0.model.info.Info#getTitle()
+ * @see io.github.stavshamir.springwolf.asyncapi.v3.model.info.Info#getTitle()
*/
@Nullable
private String title;
@@ -121,7 +122,7 @@ public static class Info {
/**
* Required. Provides the version of the application API (not to be confused with the specification version).
*
- * @see com.asyncapi.v2._6_0.model.info.Info#getVersion()
+ * @see io.github.stavshamir.springwolf.asyncapi.v3.model.info.Info#getVersion()
*/
@Nullable
private String version;
@@ -129,14 +130,14 @@ public static class Info {
/**
* A short description of the application. CommonMark syntax can be used for rich text representation.
*
- * @see com.asyncapi.v2._6_0.model.info.Info#getDescription()
+ * @see io.github.stavshamir.springwolf.asyncapi.v3.model.info.Info#getDescription()
*/
@Nullable
private String description;
/**
* A URL to the Terms of Service for the API. MUST be in the format of a URL.
- * {@link com.asyncapi.v2._6_0.model.info.Info#getTermsOfService()}
+ * {@link io.github.stavshamir.springwolf.asyncapi.v3.model.info.Info#getTermsOfService()}
*/
@Nullable
private String termsOfService;
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/DefaultSchemasService.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/DefaultSchemasService.java
index 7583acf4b..f4aa58bcd 100644
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/DefaultSchemasService.java
+++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/DefaultSchemasService.java
@@ -3,6 +3,9 @@
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.AsyncApiPayload;
import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.AsyncHeaders;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties;
import io.github.stavshamir.springwolf.schemas.postprocessor.SchemasPostProcessor;
import io.swagger.v3.core.converter.ModelConverter;
@@ -24,13 +27,15 @@
import java.util.function.Function;
@Slf4j
+// TODO: rename to DefaultComponentService & interface to ComponentService
public class DefaultSchemasService implements SchemasService {
private final ModelConverters converter = ModelConverters.getInstance();
private final List schemaPostProcessors;
private final SpringwolfConfigProperties properties;
- private final Map definitions = new HashMap<>();
+ private final Map schemas = new HashMap<>();
+ private final Map messages = new HashMap<>();
public DefaultSchemasService(
List externalModelConverters,
@@ -43,38 +48,52 @@ public DefaultSchemasService(
}
@Override
- public Map getDefinitions() {
- return definitions;
+ public Map getSchemas() {
+ return schemas;
}
@Override
- public String register(AsyncHeaders headers) {
+ public String registerSchema(AsyncHeaders headers) {
log.debug("Registering schema for {}", headers.getSchemaName());
MapSchema headerSchema = new MapSchema();
headerSchema.setName(headers.getSchemaName());
headerSchema.properties(headers);
- this.definitions.put(headers.getSchemaName(), headerSchema);
+ this.schemas.put(headers.getSchemaName(), headerSchema);
postProcessSchema(headerSchema);
return headers.getSchemaName();
}
@Override
- public String register(Class> type) {
+ public String registerSchema(Class> type) {
log.debug("Registering schema for {}", type.getSimpleName());
Map schemas = new LinkedHashMap<>(runWithFqnSetting((unused) -> converter.readAll(type)));
String schemaName = getSchemaName(type, schemas);
preProcessSchemas(schemas, schemaName, type);
- this.definitions.putAll(schemas);
+ this.schemas.putAll(schemas);
schemas.values().forEach(this::postProcessSchema);
return schemaName;
}
+ @Override
+ public Map getMessages() {
+ return this.messages;
+ }
+
+ @Override
+ public MessageReference registerMessage(MessageObject message) {
+ log.debug("Registering message for {}", message.getName());
+
+ messages.put(message.getName(), message);
+
+ return MessageReference.toComponentMessage(message);
+ }
+
private String getSchemaName(Class> type, Map schemas) {
if (schemas.isEmpty() && type.equals(String.class)) {
return registerString();
@@ -124,7 +143,7 @@ private String registerString() {
StringSchema schema = new StringSchema();
schema.setName(String.class.getName());
- this.definitions.put(schemaName, schema);
+ this.schemas.put(schemaName, schema);
postProcessSchema(schema);
return schemaName;
@@ -145,6 +164,6 @@ private R runWithFqnSetting(Function callable) {
}
private void postProcessSchema(Schema schema) {
- schemaPostProcessors.forEach(processor -> processor.process(schema, definitions));
+ schemaPostProcessors.forEach(processor -> processor.process(schema, schemas));
}
}
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/SchemasService.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/SchemasService.java
index 7ced4b101..08309ec60 100644
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/SchemasService.java
+++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/SchemasService.java
@@ -2,15 +2,22 @@
package io.github.stavshamir.springwolf.schemas;
import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.AsyncHeaders;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
import io.swagger.v3.oas.models.media.Schema;
import java.util.Map;
public interface SchemasService {
- Map getDefinitions();
+ Map getSchemas();
- String register(AsyncHeaders headers);
+ String registerSchema(AsyncHeaders headers);
- String register(Class> type);
+ String registerSchema(Class> type);
+
+ Map getMessages();
+
+ MessageReference registerMessage(MessageObject message);
}
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/example/ExampleGenerator.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/example/ExampleGenerator.java
index 425e50acb..e3767b3f3 100644
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/example/ExampleGenerator.java
+++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/example/ExampleGenerator.java
@@ -9,7 +9,7 @@
/**
* Builds an example that is embedded into {@link Schema#setExample(Object)}
*
- * Handles types defined in https://www.asyncapi.com/docs/reference/specification/v2.6.0#dataTypeFormat
+ * Handles types defined in https://www.asyncapi.com/docs/reference/specification/v3.0.0#dataTypeFormat
*/
public interface ExampleGenerator {
diff --git a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/postprocessor/SchemasPostProcessor.java b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/postprocessor/SchemasPostProcessor.java
index 3cb450cf4..1c75936ed 100644
--- a/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/postprocessor/SchemasPostProcessor.java
+++ b/springwolf-core/src/main/java/io/github/stavshamir/springwolf/schemas/postprocessor/SchemasPostProcessor.java
@@ -8,7 +8,7 @@
/**
* Internal interface to allow post-processing of a new schema (and their definition) after detection.
*
- * It is closely coupled with the data structure of the SchemaService.
+ * It is closely coupled with the data structure of the SchemasService.
*/
public interface SchemasPostProcessor {
void process(Schema schema, Map definitions);
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/ClasspathUtil.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/ClasspathUtil.java
new file mode 100644
index 000000000..cf2bec14d
--- /dev/null
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/ClasspathUtil.java
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: Apache-2.0
+package io.github.stavshamir.springwolf;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.github.stavshamir.springwolf.asyncapi.v3.jackson.DefaultAsyncApiSerializer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+public final class ClasspathUtil {
+ private ClasspathUtil() {}
+
+ public static String readAsString(String resourceName) throws IOException {
+ InputStream inputStream = ClasspathUtil.class.getResourceAsStream(resourceName);
+ return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
+ }
+
+ public static JsonNode parseYamlFile(String resourceName) throws IOException {
+ InputStream inputStream = ClasspathUtil.class.getResourceAsStream(resourceName);
+ ObjectMapper objectMapper = new DefaultAsyncApiSerializer().getYamlObjectMapper();
+ return objectMapper.readTree(inputStream);
+ }
+}
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 5edb1c93e..3978490ac 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
@@ -14,7 +14,7 @@
@ExtendWith(SpringExtension.class)
@MinimalIntegrationTestContextConfiguration
-public class SpringContextIntegrationTest {
+class SpringContextIntegrationTest {
@Autowired
private ApplicationContext context;
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiSerializerServiceIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiSerializerServiceIntegrationTest.java
index d91b612f5..653152b1f 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiSerializerServiceIntegrationTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiSerializerServiceIntegrationTest.java
@@ -1,23 +1,30 @@
// SPDX-License-Identifier: Apache-2.0
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.Contact;
-import com.asyncapi.v2._6_0.model.info.Info;
-import com.asyncapi.v2._6_0.model.info.License;
-import com.asyncapi.v2._6_0.model.server.Server;
-import com.asyncapi.v2.binding.message.kafka.KafkaMessageBinding;
-import com.asyncapi.v2.binding.operation.OperationBinding;
-import com.asyncapi.v2.binding.operation.kafka.KafkaOperationBinding;
-import com.asyncapi.v2.schema.Type;
+import io.github.stavshamir.springwolf.ClasspathUtil;
import io.github.stavshamir.springwolf.asyncapi.types.AsyncAPI;
import io.github.stavshamir.springwolf.asyncapi.types.Components;
-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.v3.bindings.OperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.kafka.KafkaMessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.kafka.KafkaOperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ServerReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.Message;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessagePayload;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.info.Contact;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.info.Info;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.info.License;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.OperationAction;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.MultiFormatSchema;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaType;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.server.Server;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.oas.models.media.Schema;
-import io.swagger.v3.oas.models.media.StringSchema;
import lombok.Data;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -31,7 +38,7 @@
import java.util.List;
import java.util.Map;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static net.javacrumbs.jsonunit.assertj.JsonAssertions.assertThatJson;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {DefaultAsyncApiSerializerService.class})
@@ -58,36 +65,48 @@ private AsyncAPI getAsyncAPITestObject() {
.build();
Server productionServer = Server.builder()
- .url("development.gigantic-server.com")
+ .host("development.gigantic-server.com")
.description("Development server")
.protocol("kafka")
.protocolVersion("1.0.0")
.build();
- Message message = Message.builder()
+ MessageObject message = MessageObject.builder()
.name("io.github.stavshamir.springwolf.ExamplePayload")
.title("Example Payload")
- .payload(PayloadReference.fromModelName("ExamplePayload"))
+ .payload(MessagePayload.of(MultiFormatSchema.builder()
+ .schema(MessageReference.toSchema("ExamplePayload"))
+ .build()))
.bindings(Map.of(
- "kafka", new KafkaMessageBinding(new StringSchema(), null, null, null, "binding-version-1")))
+ "kafka",
+ KafkaMessageBinding.builder()
+ // FIXME: We should have a SchemaString (Schema)
+ .key(SchemaObject.builder().type("string").build())
+ .build()))
.build();
+ Map messages = Map.of(message.getMessageId(), message);
+
+ SchemaObject groupId = new SchemaObject();
+ groupId.setEnumValues(List.of("myGroupId"));
+ groupId.setType(SchemaType.STRING);
- com.asyncapi.v2.schema.Schema groupId = new com.asyncapi.v2.schema.Schema();
- groupId.setEnumValue(List.of("myGroupId"));
- groupId.setType(Type.STRING);
OperationBinding operationBinding =
KafkaOperationBinding.builder().groupId(groupId).build();
Operation newUserOperation = Operation.builder()
- .operationId("new-user_listenerMethod_subscribe")
- .message(message)
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("new-user"))
+ .messages(List.of(MessageReference.toChannelMessage("new-user", message.getName())))
.bindings(Map.of("kafka", operationBinding))
.build();
- ChannelItem newUserChannel = ChannelItem.builder()
+ ChannelObject newUserChannel = ChannelObject.builder()
+ // FIXME: Can we autogenerate the address somehow?
+ .address("new-user")
.description("This channel is used to exchange messages about users signing up")
- .servers(List.of("production"))
- .subscribe(newUserOperation)
+ .servers(List.of(
+ ServerReference.builder().ref("#/servers/production").build()))
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
.build();
Map schemas = ModelConverters.getInstance()
@@ -98,7 +117,9 @@ private AsyncAPI getAsyncAPITestObject() {
.defaultContentType("application/json")
.servers(Map.of("production", productionServer))
.channels(Map.of("new-user", newUserChannel))
- .components(Components.builder().schemas(schemas).build())
+ .components(
+ Components.builder().schemas(schemas).messages(messages).build())
+ .operations(Map.of("new-user_listenerMethod_subscribe", newUserOperation))
.build();
return asyncapi;
@@ -110,16 +131,15 @@ void AsyncAPI_should_map_to_a_valid_asyncapi_json() throws IOException {
String actual = serializer.toJsonString(asyncapi);
InputStream s = this.getClass().getResourceAsStream("/asyncapi/asyncapi.json");
String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8);
- assertEquals(expected, actual);
+
+ assertThatJson(actual).isEqualTo(expected);
}
@Test
void AsyncAPI_should_map_to_a_valid_asyncapi_yaml() throws IOException {
var asyncapi = getAsyncAPITestObject();
- String actual = serializer.toYaml(asyncapi);
- InputStream s = this.getClass().getResourceAsStream("/asyncapi/asyncapi.yaml");
- String expected = new String(s.readAllBytes(), StandardCharsets.UTF_8);
- assertEquals(expected, actual);
+ var expected = ClasspathUtil.parseYamlFile("/asyncapi/asyncapi.yaml");
+ assertThatJson(serializer.toJsonString(asyncapi)).isEqualTo(expected);
}
@Data
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 861780b2a..caa5927bb 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
@@ -1,13 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
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.operation.kafka.KafkaOperationBinding;
import io.github.stavshamir.springwolf.asyncapi.types.AsyncAPI;
-import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.Message;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.kafka.KafkaChannelBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.info.Info;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.server.Server;
import io.github.stavshamir.springwolf.configuration.DefaultAsyncApiDocketService;
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties;
import io.github.stavshamir.springwolf.schemas.SchemasService;
@@ -49,13 +48,16 @@
"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",
+ "springwolf.docket.servers.test-protocol.host=some-server:1234",
})
class DefaultAsyncApiServiceIntegrationTest {
@MockBean
private ChannelsService channelsService;
+ @MockBean
+ private OperationsService operationsService;
+
@MockBean
private SchemasService schemasService;
@@ -67,18 +69,14 @@ 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())
+ ChannelObject.builder()
+ .bindings(Map.of("kafka", new KafkaChannelBinding()))
+ .messages(Map.of("receiveId", MessageReference.toComponentMessage("receiveId")))
.build(),
"producer-topic",
- ChannelItem.builder()
- .publish(Operation.builder()
- .bindings(Map.of("kafka", new KafkaOperationBinding()))
- .message(Message.builder().build())
- .build())
+ ChannelObject.builder()
+ .bindings(Map.of("kafka", new KafkaChannelBinding()))
+ .messages(Map.of("sendId", MessageReference.toComponentMessage("sendId")))
.build()));
}
@@ -96,29 +94,31 @@ void getAsyncAPI_servers_should_be_correct() {
Map actualServers = asyncApiService.getAsyncAPI().getServers();
assertThat(actualServers.get("test-protocol").getProtocol()).isEqualTo("test");
- assertThat(actualServers.get("test-protocol").getUrl()).isEqualTo("some-server:1234");
+ assertThat(actualServers.get("test-protocol").getHost()).isEqualTo("some-server:1234");
}
@Test
void getAsyncAPI_channels_should_be_correct() {
- Map actualChannels = asyncApiService.getAsyncAPI().getChannels();
+ 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 ChannelObject consumerChannel = actualChannels.get("consumer-topic");
+ assertThat(consumerChannel.getBindings()).isEqualTo(Map.of("kafka", new KafkaChannelBinding()));
+ assertThat(consumerChannel.getMessages()).hasSize(1);
+ MessageReference receiveMessage =
+ (MessageReference) consumerChannel.getMessages().get("receiveId");
+ assertThat(receiveMessage.getRef()).isEqualTo("#/components/messages/receiveId");
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();
+ final ChannelObject publishChannel = actualChannels.get("producer-topic");
+ assertThat(publishChannel.getBindings()).isEqualTo(Map.of("kafka", new KafkaChannelBinding()));
+ assertThat(publishChannel.getMessages()).hasSize(1);
+ MessageReference sendMessage =
+ (MessageReference) publishChannel.getMessages().get("sendId");
+ assertThat(sendMessage.getRef()).isEqualTo("#/components/messages/sendId");
}
@Order(TestDescriptionCustomizer.CUSTOMIZER_ORDER)
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiServiceTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiServiceTest.java
index 36e474734..172d22dc4 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiServiceTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultAsyncApiServiceTest.java
@@ -21,11 +21,12 @@
/**
* Pure unit tests of {@link DefaultAsyncApiService}. Faking spring context support
*/
-public class DefaultAsyncApiServiceTest {
+class DefaultAsyncApiServiceTest {
private DefaultAsyncApiService defaultAsyncApiService;
private AsyncApiDocketService asyncApiDocketService;
private ChannelsService channelsService;
+ private OperationsService operationsService;
private SchemasService schemasService;
private List customizers = new ArrayList<>();
@@ -33,13 +34,14 @@ public class DefaultAsyncApiServiceTest {
public void setup() {
asyncApiDocketService = mock(AsyncApiDocketService.class);
channelsService = mock(ChannelsService.class);
+ operationsService = mock(OperationsService.class);
schemasService = mock(SchemasService.class);
when(channelsService.findChannels()).thenReturn(Map.of());
- when(schemasService.getDefinitions()).thenReturn(Map.of());
+ when(schemasService.getSchemas()).thenReturn(Map.of());
- defaultAsyncApiService =
- new DefaultAsyncApiService(asyncApiDocketService, channelsService, schemasService, customizers);
+ defaultAsyncApiService = new DefaultAsyncApiService(
+ asyncApiDocketService, channelsService, operationsService, schemasService, customizers);
}
@Test
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultChannelsServiceIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultChannelsServiceIntegrationTest.java
index d67d09908..c559b5e17 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultChannelsServiceIntegrationTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultChannelsServiceIntegrationTest.java
@@ -1,9 +1,12 @@
// SPDX-License-Identifier: Apache-2.0
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 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.channel.ChannelReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.OperationAction;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -19,10 +22,9 @@
@ContextConfiguration(
classes = {
DefaultChannelsService.class,
- DefaultChannelsServiceIntegrationTest.FooChannelScanner.class,
- DefaultChannelsServiceIntegrationTest.BarChannelScanner.class,
- DefaultChannelsServiceIntegrationTest.SameTopic.SubscribeChannelScanner.class,
- DefaultChannelsServiceIntegrationTest.SameTopic.ProduceChannelScanner.class
+ DefaultChannelsServiceIntegrationTest.SimpleChannelScanner.class,
+ DefaultChannelsServiceIntegrationTest.SameTopic.ReceiveChannelScanner.class,
+ DefaultChannelsServiceIntegrationTest.SameTopic.SendChannelScanner.class
})
class DefaultChannelsServiceIntegrationTest {
@@ -30,67 +32,67 @@ class DefaultChannelsServiceIntegrationTest {
private DefaultChannelsService defaultChannelsService;
@Autowired
- private FooChannelScanner fooChannelScanner;
-
- @Autowired
- private BarChannelScanner barChannelScanner;
+ private SimpleChannelScanner simpleChannelScanner;
@Test
void getChannels() {
- Map actualChannels = defaultChannelsService.findChannels();
+ Map actualChannels = defaultChannelsService.findChannels();
assertThat(actualChannels)
- .containsAllEntriesOf(fooChannelScanner.scan())
- .containsAllEntriesOf(barChannelScanner.scan())
+ .containsAllEntriesOf(simpleChannelScanner.scan())
.containsEntry(SameTopic.topicName, SameTopic.expectedMergedChannel);
}
@Component
- static class FooChannelScanner implements ChannelsScanner {
- @Override
- public Map scan() {
- return Map.of("foo", new ChannelItem());
- }
- }
-
- @Component
- static class BarChannelScanner implements ChannelsScanner {
+ static class SimpleChannelScanner implements ChannelsScanner {
@Override
- public Map scan() {
- return Map.of("bar", new ChannelItem());
+ public Map scan() {
+ return Map.of("foo", new ChannelObject());
}
}
static class SameTopic {
- static final String topicName = "subscribeProduceTopic";
- static final ChannelItem expectedMergedChannel = ChannelItem.builder()
- .publish(SameTopic.ProduceChannelScanner.publishOperation)
- .subscribe(SameTopic.SubscribeChannelScanner.subscribeOperation)
+ static final String topicName = "receiveSendTopic";
+ static final ChannelObject expectedMergedChannel = ChannelObject.builder()
+ .messages(Map.of(
+ "receiveMessage",
+ MessageReference.toComponentMessage("receiveMessage"),
+ "sendMessage",
+ MessageReference.toComponentMessage("sendMessage")))
.build();
@Component
- static class ProduceChannelScanner implements ChannelsScanner {
- static final Operation publishOperation =
- Operation.builder().message("publish").build();
+ static class SendChannelScanner implements ChannelsScanner {
+ static final Operation sentOperation = Operation.builder()
+ .channel(ChannelReference.fromChannel(topicName))
+ .action(OperationAction.SEND)
+ .build();
@Override
- public Map scan() {
+ public Map scan() {
return Map.of(
topicName,
- ChannelItem.builder().publish(publishOperation).build());
+ ChannelObject.builder()
+ .messages(Map.of("sendMessage", MessageReference.toComponentMessage("sendMessage")))
+ .build());
}
}
@Component
- static class SubscribeChannelScanner implements ChannelsScanner {
- static final Operation subscribeOperation =
- Operation.builder().message("consumer").build();
+ static class ReceiveChannelScanner implements ChannelsScanner {
+ static final Operation receiveOperation = Operation.builder()
+ .channel(ChannelReference.fromChannel(topicName))
+ .action(OperationAction.RECEIVE)
+ .build();
@Override
- public Map scan() {
+ public Map scan() {
return Map.of(
topicName,
- ChannelItem.builder().subscribe(subscribeOperation).build());
+ ChannelObject.builder()
+ .messages(
+ Map.of("receiveMessage", MessageReference.toComponentMessage("receiveMessage")))
+ .build());
}
}
}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultOperationsServiceIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultOperationsServiceIntegrationTest.java
new file mode 100644
index 000000000..b82bb9727
--- /dev/null
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/DefaultOperationsServiceIntegrationTest.java
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: Apache-2.0
+package io.github.stavshamir.springwolf.asyncapi;
+
+import io.github.stavshamir.springwolf.asyncapi.scanners.channels.OperationsScanner;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.OperationAction;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(
+ classes = {
+ DefaultOperationsService.class,
+ DefaultOperationsServiceIntegrationTest.SimpleOperationScanner.class,
+ DefaultOperationsServiceIntegrationTest.SameTopic.ReceiveOperationScanner.class,
+ DefaultOperationsServiceIntegrationTest.SameTopic.SendOperationScanner.class
+ })
+class DefaultOperationsServiceIntegrationTest {
+
+ @Autowired
+ private DefaultOperationsService defaultOperationsService;
+
+ @Autowired
+ private SimpleOperationScanner simpleOperationsScanner;
+
+ @Test
+ void getOperations() {
+ Map actualChannels = defaultOperationsService.findOperations();
+
+ assertThat(actualChannels)
+ .containsAllEntriesOf(simpleOperationsScanner.scan())
+ .containsEntry("receive", SameTopic.ReceiveOperationScanner.receiveOperation)
+ .containsEntry("send", SameTopic.SendOperationScanner.sentOperation);
+ }
+
+ @Component
+ static class SimpleOperationScanner implements OperationsScanner {
+ @Override
+ public Map scan() {
+ return Map.of(
+ "foo",
+ Operation.builder()
+ .channel(ChannelReference.fromChannel("foo"))
+ .action(OperationAction.RECEIVE)
+ .build());
+ }
+ }
+
+ static class SameTopic {
+ static final String topicName = "receiveSendTopic";
+
+ @Component
+ static class SendOperationScanner implements OperationsScanner {
+ static final Operation sentOperation = Operation.builder()
+ .channel(ChannelReference.fromChannel(topicName))
+ .action(OperationAction.SEND)
+ .build();
+
+ @Override
+ public Map scan() {
+ return Map.of("send", sentOperation);
+ }
+ }
+
+ @Component
+ static class ReceiveOperationScanner implements OperationsScanner {
+ static final Operation receiveOperation = Operation.builder()
+ .channel(ChannelReference.fromChannel(topicName))
+ .action(OperationAction.RECEIVE)
+ .build();
+
+ @Override
+ public Map scan() {
+ return Map.of("receive", receiveOperation);
+ }
+ }
+ }
+}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/MessageHelperTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/MessageHelperTest.java
index ea4037f93..d09410370 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/MessageHelperTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/MessageHelperTest.java
@@ -1,16 +1,17 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi;
-import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.Message;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
import org.junit.jupiter.api.Test;
import java.util.Collections;
-import java.util.List;
import java.util.Map;
import java.util.Set;
-import static io.github.stavshamir.springwolf.asyncapi.MessageHelper.messageObjectToSet;
-import static io.github.stavshamir.springwolf.asyncapi.MessageHelper.toMessageObjectOrComposition;
+import static io.github.stavshamir.springwolf.asyncapi.MessageHelper.toMessagesMap;
+import static io.github.stavshamir.springwolf.asyncapi.MessageHelper.toOperationsMessagesMap;
+import static java.util.Map.entry;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@@ -18,110 +19,135 @@ class MessageHelperTest {
@Test
void toMessageObjectOrComposition_emptySet() {
- assertThatThrownBy(() -> toMessageObjectOrComposition(Collections.emptySet()))
- .isInstanceOf(IllegalArgumentException.class);
+ assertThatThrownBy(() -> toMessagesMap(Collections.emptySet())).isInstanceOf(IllegalArgumentException.class);
}
@Test
- void toMessageObjectOrComposition_oneMessage() {
- Message message = Message.builder().name("foo").build();
-
- Object asObject = toMessageObjectOrComposition(Set.of(message));
-
- assertThat(asObject).isInstanceOf(Message.class).isEqualTo(message);
+ void toOperationsMessageObjectOrComposition_emptySet() {
+ // When messages set is empty, the method should fail
+ assertThatThrownBy(() -> toOperationsMessagesMap("channel", Collections.emptySet()))
+ .isInstanceOf(IllegalArgumentException.class);
}
@Test
- void toMessageObjectOrComposition_multipleMessages() {
- Message message1 = Message.builder().name("foo").build();
-
- Message message2 = Message.builder().name("bar").build();
-
- Object asObject = toMessageObjectOrComposition(Set.of(message1, message2));
+ void toOperationsMessageObjectOrComposition_invalidChannel() {
+ // When channel is not valid, the method should fail
+ MessageObject message = MessageObject.builder().name("foo").build();
- assertThat(asObject).isInstanceOf(Map.class).isEqualTo(Map.of("oneOf", List.of(message2, message1)));
+ assertThatThrownBy(() -> toOperationsMessagesMap(null, Set.of(message)))
+ .isInstanceOf(IllegalArgumentException.class);
+ assertThatThrownBy(() -> toOperationsMessagesMap("", Set.of(message)))
+ .isInstanceOf(IllegalArgumentException.class);
+ assertThatThrownBy(() -> toOperationsMessagesMap(" ", Set.of(message)))
+ .isInstanceOf(IllegalArgumentException.class);
}
@Test
- void toMessageObjectOrComposition_multipleMessages_remove_duplicates() {
- Message message1 =
- Message.builder().name("foo").description("This is message 1").build();
+ void toMessagesMap_oneMessage() {
+ MessageObject message = MessageObject.builder().name("foo").build();
- Message message2 =
- Message.builder().name("bar").description("This is message 2").build();
+ var messagesMap = toMessagesMap(Set.of(message));
- Message message3 = Message.builder()
- .name("bar")
- .description("This is message 3, but in essence the same payload type as message 2")
- .build();
-
- Object asObject = toMessageObjectOrComposition(Set.of(message1, message2, message3));
-
- Map> oneOfMap = (Map>) asObject;
- assertThat(oneOfMap).hasSize(1);
- List deduplicatedMessageList = oneOfMap.get("oneOf");
- // we do not have any guarantee wether message2 or message3 won the deduplication.
- assertThat(deduplicatedMessageList).hasSize(2).contains(message1).containsAnyOf(message2, message3);
+ assertThat(messagesMap)
+ .containsExactlyInAnyOrderEntriesOf(Map.of("foo", MessageReference.toComponentMessage(message)));
}
@Test
- void toMessageObjectOrComposition_multipleMessages_should_not_break_deep_equals() {
- Message actualMessage1 = Message.builder()
- .name("foo")
- .description("This is actual message 1")
- .build();
+ void toOperationsMessagesMap_oneMessage() {
+ MessageObject message = MessageObject.builder().name("foo").build();
- Message actualMessage2 = Message.builder()
- .name("bar")
- .description("This is actual message 2")
- .build();
+ var messagesMap = toOperationsMessagesMap("channelName", Set.of(message));
- Object actualObject = toMessageObjectOrComposition(Set.of(actualMessage1, actualMessage2));
+ assertThat(messagesMap)
+ .containsExactlyInAnyOrderEntriesOf(
+ Map.of("foo", MessageReference.toChannelMessage("channelName", message)));
+ }
- Message expectedMessage1 = Message.builder()
- .name("foo")
- .description("This is expected message 1")
- .build();
+ @Test
+ void toMessagesMap_multipleMessages() {
+ MessageObject message1 = MessageObject.builder().name("foo").build();
- Message expectedMessage2 = Message.builder()
- .name("bar")
- .description("This is expected message 2")
- .build();
+ MessageObject message2 = MessageObject.builder().name("bar").build();
- Object expectedObject = toMessageObjectOrComposition(Set.of(expectedMessage1, expectedMessage2));
+ var messages = toMessagesMap(Set.of(message1, message2));
- assertThat(actualObject).isNotEqualTo(expectedObject);
+ assertThat(messages)
+ .containsExactlyInAnyOrderEntriesOf(Map.of(
+ "bar",
+ MessageReference.toComponentMessage(message2),
+ "foo",
+ MessageReference.toComponentMessage(message1)));
}
@Test
- void messageObjectToSet_notAMessageOrAMap() {
- Object string = "foo";
+ void toOperationsMessagesMap_multipleMessages() {
+ MessageObject message1 = MessageObject.builder().name("foo").build();
- Set messages = messageObjectToSet(string);
+ MessageObject message2 = MessageObject.builder().name("bar").build();
- assertThat(messages).isEmpty();
+ var messages = toOperationsMessagesMap("channelName", Set.of(message1, message2));
+
+ assertThat(messages)
+ .containsExactlyInAnyOrderEntriesOf(Map.of(
+ "bar",
+ MessageReference.toChannelMessage("channelName", message2),
+ "foo",
+ MessageReference.toChannelMessage("channelName", message1)));
}
@Test
- void messageObjectToSet_Message() {
- Message message = Message.builder().name("foo").build();
- Object asObject = toMessageObjectOrComposition(Set.of(message));
+ void toMessagesMap_multipleMessages_remove_duplicates() {
+ MessageObject message1 = MessageObject.builder()
+ .name("foo")
+ .description("This is message 1")
+ .build();
+
+ MessageObject message2 = MessageObject.builder()
+ .name("bar")
+ .description("This is message 2")
+ .build();
+
+ MessageObject message3 = MessageObject.builder()
+ .name("bar")
+ .description("This is message 3, but in essence the same payload type as message 2")
+ .build();
- Set messages = messageObjectToSet(asObject);
+ var messages = toMessagesMap(Set.of(message1, message2, message3));
- assertThat(messages).containsExactly(message);
+ // we do not have any guarantee whether message2 or message3 won the deduplication.
+ assertThat(messages)
+ .hasSize(2)
+ .containsValue(MessageReference.toComponentMessage(message1))
+ .containsAnyOf(
+ entry("bar", MessageReference.toComponentMessage(message2)),
+ entry("bar", MessageReference.toComponentMessage(message3)));
}
@Test
- void messageObjectToSet_SetOfMessage() {
- Message message1 = Message.builder().name("foo").build();
+ void toOperationsMessagesMap_multipleMessages_remove_duplicates() {
+ MessageObject message1 = MessageObject.builder()
+ .name("foo")
+ .description("This is message 1")
+ .build();
- Message message2 = Message.builder().name("bar").build();
+ MessageObject message2 = MessageObject.builder()
+ .name("bar")
+ .description("This is message 2")
+ .build();
- Object asObject = toMessageObjectOrComposition(Set.of(message1, message2));
+ MessageObject message3 = MessageObject.builder()
+ .name("bar")
+ .description("This is message 3, but in essence the same payload type as message 2")
+ .build();
- Set messages = messageObjectToSet(asObject);
+ var messages = toOperationsMessagesMap("channelName", Set.of(message1, message2, message3));
- assertThat(messages).containsExactlyInAnyOrder(message1, message2);
+ // we do not have any guarantee whether message2 or message3 won the deduplication.
+ assertThat(messages)
+ .hasSize(2)
+ .containsValue(MessageReference.toChannelMessage("channelName", message1))
+ .containsAnyOf(
+ entry("bar", MessageReference.toChannelMessage("channelName", message2)),
+ entry("bar", MessageReference.toChannelMessage("channelName", message3)));
}
}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestAbstractOperationBindingProcessor.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestAbstractOperationBindingProcessor.java
index 45475e537..a96cfbf29 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestAbstractOperationBindingProcessor.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestAbstractOperationBindingProcessor.java
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.bindings.processor;
-import com.asyncapi.v2.binding.operation.OperationBinding;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.BindingProcessorPriority;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.ProcessedOperationBinding;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation.AsyncOperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;
import org.springframework.core.annotation.Order;
import java.lang.annotation.ElementType;
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestMessageBindingProcessor.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestMessageBindingProcessor.java
index 66cbfb894..8abf21621 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestMessageBindingProcessor.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestMessageBindingProcessor.java
@@ -1,10 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.bindings.processor;
-import com.asyncapi.v2.binding.message.MessageBinding;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.BindingProcessorPriority;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.MessageBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.ProcessedMessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.bindings.EmptyMessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding;
import org.springframework.core.annotation.Order;
import java.lang.annotation.ElementType;
@@ -20,7 +21,7 @@
public class TestMessageBindingProcessor implements MessageBindingProcessor {
public static final String TYPE = "testType";
- public static final MessageBinding BINDING = new MessageBinding();
+ public static final MessageBinding BINDING = new EmptyMessageBinding();
@Override
public Optional process(Method method) {
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestOperationBindingProcessor.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestOperationBindingProcessor.java
index e3e68e06a..9c7dc9c8c 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestOperationBindingProcessor.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/bindings/processor/TestOperationBindingProcessor.java
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.bindings.processor;
-import com.asyncapi.v2.binding.operation.OperationBinding;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.BindingProcessorPriority;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.OperationBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.ProcessedOperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;
import org.springframework.core.annotation.Order;
import java.lang.annotation.ElementType;
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/ChannelMergerTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/ChannelMergerTest.java
index 3f600219c..d5d7e39f9 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/ChannelMergerTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/ChannelMergerTest.java
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.channels;
-import com.asyncapi.v2._6_0.model.channel.ChannelItem;
-import com.asyncapi.v2._6_0.model.channel.operation.Operation;
import io.github.stavshamir.springwolf.asyncapi.MessageHelper;
-import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.Message;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
@@ -20,158 +20,118 @@ void shouldNotMergeDifferentChannelNames() {
// given
String channelName1 = "channel1";
String channelName2 = "channel2";
- Operation publishOperation =
- Operation.builder().operationId("publisher").build();
- Operation subscribeOperation =
- Operation.builder().operationId("subscribe").build();
- ChannelItem publisherChannel =
- ChannelItem.builder().publish(publishOperation).build();
- ChannelItem subscriberChannel =
- ChannelItem.builder().subscribe(subscribeOperation).build();
+ ChannelObject publisherChannel = ChannelObject.builder().build();
+ ChannelObject subscriberChannel = ChannelObject.builder().build();
// when
- Map mergedChannels = ChannelMerger.merge(
+ Map mergedChannels = ChannelMerger.mergeChannels(
Arrays.asList(Map.entry(channelName1, publisherChannel), Map.entry(channelName2, subscriberChannel)));
// then
- assertThat(mergedChannels)
- .hasSize(2)
- .hasEntrySatisfying(channelName1, it -> {
- assertThat(it.getPublish()).isEqualTo(publishOperation);
- assertThat(it.getSubscribe()).isNull();
- })
- .hasEntrySatisfying(channelName2, it -> {
- assertThat(it.getPublish()).isNull();
- assertThat(it.getSubscribe()).isEqualTo(subscribeOperation);
- });
+ assertThat(mergedChannels).hasSize(2);
}
@Test
- void shouldMergePublisherAndSubscriberIntoOneChannel() {
+ void shouldMergeEqualChannelNamesIntoOneChannel() {
// given
String channelName = "channel";
- Operation publishOperation =
- Operation.builder().operationId("publisher").build();
- Operation subscribeOperation =
- Operation.builder().operationId("subscribe").build();
- ChannelItem publisherChannel =
- ChannelItem.builder().publish(publishOperation).build();
- ChannelItem subscriberChannel =
- ChannelItem.builder().subscribe(subscribeOperation).build();
+ ChannelObject publisherChannel = ChannelObject.builder().build();
+ ChannelObject subscriberChannel = ChannelObject.builder().build();
// when
- Map mergedChannels = ChannelMerger.merge(
+ Map mergedChannels = ChannelMerger.mergeChannels(
Arrays.asList(Map.entry(channelName, publisherChannel), Map.entry(channelName, subscriberChannel)));
// then
- assertThat(mergedChannels).hasSize(1).hasEntrySatisfying(channelName, it -> {
- assertThat(it.getPublish()).isEqualTo(publishOperation);
- assertThat(it.getSubscribe()).isEqualTo(subscribeOperation);
- });
+ assertThat(mergedChannels).hasSize(1);
}
@Test
- void shouldUseFirstOperationFound() {
+ void shouldUseFirstChannelFound() {
// given
String channelName = "channel";
- Operation publishOperation1 =
- Operation.builder().operationId("publisher1").build();
- Operation publishOperation2 =
- Operation.builder().operationId("publisher2").build();
- ChannelItem publisherChannel1 =
- ChannelItem.builder().publish(publishOperation1).build();
- ChannelItem publisherChannel2 =
- ChannelItem.builder().publish(publishOperation2).build();
+ ChannelObject publisherChannel1 =
+ ChannelObject.builder().title("channel1").build();
+ ChannelObject publisherChannel2 =
+ ChannelObject.builder().title("channel2").build();
// when
- Map mergedChannels = ChannelMerger.merge(
+ Map mergedChannels = ChannelMerger.mergeChannels(
Arrays.asList(Map.entry(channelName, publisherChannel1), Map.entry(channelName, publisherChannel2)));
// then
assertThat(mergedChannels).hasSize(1).hasEntrySatisfying(channelName, it -> {
- assertThat(it.getPublish()).isEqualTo(publishOperation1);
- assertThat(it.getSubscribe()).isNull();
+ assertThat(it.getTitle()).isEqualTo("channel1");
});
}
@Test
- void shouldMergeDifferentMessageForSameOperation() {
+ void shouldMergeDifferentMessagesForSameChannel() {
// given
String channelName = "channel";
- Message message1 = Message.builder()
+ MessageObject message1 = MessageObject.builder()
+ .messageId("message1")
.name(String.class.getCanonicalName())
.description("This is a string")
.build();
- Message message2 = Message.builder()
+ MessageObject message2 = MessageObject.builder()
+ .messageId("message2")
.name(Integer.class.getCanonicalName())
.description("This is an integer")
.build();
- Message message3 = Message.builder()
+ MessageObject message3 = MessageObject.builder()
+ .messageId("message3")
.name(Integer.class.getCanonicalName())
.description("This is also an integer, but in essence the same payload type")
.build();
- Operation publishOperation1 =
- Operation.builder().operationId("publisher1").message(message1).build();
- Operation publishOperation2 =
- Operation.builder().operationId("publisher2").message(message2).build();
- Operation publishOperation3 =
- Operation.builder().operationId("publisher3").message(message3).build();
- ChannelItem publisherChannel1 =
- ChannelItem.builder().publish(publishOperation1).build();
- ChannelItem publisherChannel2 =
- ChannelItem.builder().publish(publishOperation2).build();
- ChannelItem publisherChannel3 =
- ChannelItem.builder().publish(publishOperation3).build();
+ ChannelObject publisherChannel1 = ChannelObject.builder()
+ .messages(Map.of(message1.getName(), MessageReference.toComponentMessage(message1)))
+ .build();
+ ChannelObject publisherChannel2 = ChannelObject.builder()
+ .messages(Map.of(message2.getName(), MessageReference.toComponentMessage(message2)))
+ .build();
+ ChannelObject publisherChannel3 = ChannelObject.builder()
+ .messages(Map.of(message3.getName(), MessageReference.toComponentMessage(message3)))
+ .build();
// when
- Map mergedChannels = ChannelMerger.merge(Arrays.asList(
+ Map mergedChannels = ChannelMerger.mergeChannels(Arrays.asList(
Map.entry(channelName, publisherChannel1),
Map.entry(channelName, publisherChannel2),
Map.entry(channelName, publisherChannel3)));
// then expectedMessage only includes message1 and message2.
// Message3 is not included as it is identical in terms of payload type (Message#name) to message 2
- Object expectedMessages = MessageHelper.toMessageObjectOrComposition(Set.of(message1, message2));
+ var expectedMessages = MessageHelper.toMessagesMap(Set.of(message1, message2));
assertThat(mergedChannels).hasSize(1).hasEntrySatisfying(channelName, it -> {
- assertThat(it.getPublish())
- .isEqualTo(Operation.builder()
- .operationId("publisher1")
- .message(expectedMessages)
- .build());
- assertThat(it.getSubscribe()).isNull();
+ assertThat(it.getMessages()).containsExactlyInAnyOrderEntriesOf(expectedMessages);
});
}
@Test
- void shouldUseOtherMessageIfFirstMessageIsMissing() {
+ void shouldUseOtherMessageIfFirstMessageIsMissingForChannels() {
// given
String channelName = "channel";
- Message message2 = Message.builder()
+ MessageObject message2 = MessageObject.builder()
+ .messageId(String.class.getCanonicalName())
.name(String.class.getCanonicalName())
.description("This is a string")
.build();
- Operation publishOperation1 =
- Operation.builder().operationId("publisher1").build();
- Operation publishOperation2 =
- Operation.builder().operationId("publisher2").message(message2).build();
- ChannelItem publisherChannel1 =
- ChannelItem.builder().publish(publishOperation1).build();
- ChannelItem publisherChannel2 =
- ChannelItem.builder().publish(publishOperation2).build();
+ ChannelObject publisherChannel1 = ChannelObject.builder().build();
+ ChannelObject publisherChannel2 = ChannelObject.builder()
+ .messages(Map.of(message2.getName(), message2))
+ .build();
// when
- Map mergedChannels = ChannelMerger.merge(
+ Map mergedChannels = ChannelMerger.mergeChannels(
Arrays.asList(Map.entry(channelName, publisherChannel1), Map.entry(channelName, publisherChannel2)));
// then expectedMessage message2
- Object expectedMessages = MessageHelper.toMessageObjectOrComposition(Set.of(message2));
+ var expectedMessages = Map.of(message2.getName(), message2);
+
assertThat(mergedChannels).hasSize(1).hasEntrySatisfying(channelName, it -> {
- assertThat(it.getPublish())
- .isEqualTo(Operation.builder()
- .operationId("publisher1")
- .message(expectedMessages)
- .build());
- assertThat(it.getSubscribe()).isNull();
+ assertThat(it.getMessages()).hasSize(1);
+ assertThat(it.getMessages()).containsExactlyInAnyOrderEntriesOf(expectedMessages);
});
}
}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/OperationMergerTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/OperationMergerTest.java
new file mode 100644
index 000000000..75b8d6a82
--- /dev/null
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/OperationMergerTest.java
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: Apache-2.0
+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.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.OperationAction;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class OperationMergerTest {
+
+ @Test
+ void shouldNotMergeDifferentoperationIds() {
+ // given
+ String operationId1 = "operation1";
+ String operationId2 = "operation2";
+ Operation publisherOperation = Operation.builder().build();
+ Operation subscriberOperation = Operation.builder().build();
+
+ // when
+ Map mergedOperations = OperationMerger.mergeOperations(Arrays.asList(
+ Map.entry(operationId1, publisherOperation), Map.entry(operationId2, subscriberOperation)));
+
+ // then
+ assertThat(mergedOperations).hasSize(2);
+ }
+
+ @Test
+ void shouldMergeEqualoperationIdsIntoOneOperation() {
+ // given
+ String operationId = "operation";
+ Operation publishOperation = Operation.builder()
+ .action(OperationAction.SEND)
+ .title("publisher")
+ .build();
+ Operation subscribeOperation = Operation.builder()
+ .action(OperationAction.RECEIVE)
+ .title("subscribe")
+ .build();
+
+ // when
+ Map mergedOperations = OperationMerger.mergeOperations(
+ Arrays.asList(Map.entry(operationId, publishOperation), Map.entry(operationId, subscribeOperation)));
+
+ // then
+ assertThat(mergedOperations).hasSize(1);
+ }
+
+ @Test
+ void shouldUseFirstOperationFound() {
+ // given
+ String operationId = "operation";
+ Operation senderOperation =
+ Operation.builder().action(OperationAction.SEND).build();
+ Operation receiverOperation =
+ Operation.builder().action(OperationAction.RECEIVE).build();
+
+ // when
+ Map mergedOperations = OperationMerger.mergeOperations(
+ Arrays.asList(Map.entry(operationId, senderOperation), Map.entry(operationId, receiverOperation)));
+
+ // then
+ assertThat(mergedOperations).hasSize(1).hasEntrySatisfying(operationId, it -> {
+ assertThat(it.getAction()).isEqualTo(OperationAction.SEND);
+ });
+ }
+
+ @Test
+ void shouldMergeDifferentMessageForSameOperation() {
+ // given
+ String channelName = "channel";
+ String operationId = "operation";
+ MessageObject message1 = MessageObject.builder()
+ .messageId("message1")
+ .name(String.class.getCanonicalName())
+ .description("This is a string")
+ .build();
+ MessageObject message2 = MessageObject.builder()
+ .messageId("message2")
+ .name(Integer.class.getCanonicalName())
+ .description("This is an integer")
+ .build();
+ MessageObject message3 = MessageObject.builder()
+ .messageId("message3")
+ .name(Integer.class.getCanonicalName())
+ .description("This is also an integer, but in essence the same payload type")
+ .build();
+ MessageReference messageRef1 = MessageReference.toChannelMessage(channelName, message1);
+ MessageReference messageRef2 = MessageReference.toChannelMessage(channelName, message2);
+ MessageReference messageRef3 = MessageReference.toChannelMessage(channelName, message3);
+
+ Operation senderOperation1 = Operation.builder()
+ .action(OperationAction.SEND)
+ .title("sender1")
+ .messages(List.of(messageRef1))
+ .build();
+ Operation senderOperation2 = Operation.builder()
+ .action(OperationAction.SEND)
+ .title("sender2")
+ .messages(List.of(messageRef2))
+ .build();
+ Operation senderOperation3 = Operation.builder()
+ .action(OperationAction.SEND)
+ .title("sender3")
+ .messages(List.of(messageRef3))
+ .build();
+
+ // when
+ Map mergedOperations = OperationMerger.mergeOperations(List.of(
+ Map.entry(operationId, senderOperation1),
+ Map.entry(operationId, senderOperation2),
+ Map.entry(operationId, senderOperation3)));
+
+ // then expectedMessage only includes message1 and message2.
+ // Message3 is not included as it is identical in terms of payload type (Message#name) to message 2
+ assertThat(mergedOperations).hasSize(1).hasEntrySatisfying(operationId, it -> {
+ assertThat(it.getMessages()).containsExactlyInAnyOrder(messageRef1, messageRef2);
+ });
+ }
+
+ @Test
+ void shouldUseOtherMessageIfFirstMessageIsMissingForChannels() {
+ // given
+ String channelName = "channel";
+ MessageObject message2 = MessageObject.builder()
+ .messageId(String.class.getCanonicalName())
+ .name(String.class.getCanonicalName())
+ .description("This is a string")
+ .build();
+ ChannelObject publisherChannel1 = ChannelObject.builder().build();
+ ChannelObject publisherChannel2 = ChannelObject.builder()
+ .messages(Map.of(message2.getName(), message2))
+ .build();
+
+ // when
+ Map mergedChannels = ChannelMerger.mergeChannels(
+ Arrays.asList(Map.entry(channelName, publisherChannel1), Map.entry(channelName, publisherChannel2)));
+
+ // then expectedMessage message2
+ var expectedMessages = Map.of(message2.getName(), message2);
+
+ assertThat(mergedChannels).hasSize(1).hasEntrySatisfying(channelName, it -> {
+ assertThat(it.getMessages()).hasSize(1);
+ assertThat(it.getMessages()).containsExactlyInAnyOrderEntriesOf(expectedMessages);
+ });
+ }
+
+ @Test
+ void shouldUseOtherMessageIfFirstMessageIsMissingForOperations() {
+ // given
+ String channelName = "channel-name";
+ MessageObject message2 = MessageObject.builder()
+ .messageId(String.class.getCanonicalName())
+ .name(String.class.getCanonicalName())
+ .description("This is a string")
+ .build();
+ Operation publishOperation1 = Operation.builder()
+ .action(OperationAction.SEND)
+ .title("publisher1")
+ .build();
+ Operation publishOperation2 = Operation.builder()
+ .action(OperationAction.SEND)
+ .title("publisher2")
+ .messages(List.of(MessageReference.toChannelMessage(channelName, message2)))
+ .build();
+
+ // when
+ Map mergedOperations = OperationMerger.mergeOperations(
+ Arrays.asList(Map.entry("publisher1", publishOperation1), Map.entry("publisher1", publishOperation2)));
+ // then expectedMessage message2
+ var expectedMessage = MessageReference.toChannelMessage(channelName, message2);
+
+ assertThat(mergedOperations).hasSize(1).hasEntrySatisfying("publisher1", it -> {
+ assertThat(it.getMessages()).hasSize(1);
+ assertThat(it.getMessages()).containsExactlyInAnyOrder(expectedMessage);
+ });
+ }
+}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/SimpleChannelsScannerTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/SimpleChannelsScannerTest.java
index dbac812a4..9c0e8c365 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/SimpleChannelsScannerTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/SimpleChannelsScannerTest.java
@@ -1,9 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.channels;
-import com.asyncapi.v2._6_0.model.channel.ChannelItem;
-import com.asyncapi.v2._6_0.model.channel.operation.Operation;
import io.github.stavshamir.springwolf.asyncapi.scanners.classes.ClassScanner;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
import org.junit.jupiter.api.Test;
import java.util.Map;
@@ -24,71 +23,57 @@ class SimpleChannelsScannerTest {
private final SimpleChannelsScanner simpleChannelsScanner = new SimpleChannelsScanner(classScanner, classProcessor);
@Test
- public void noClassFoundTest() {
+ void noClassFoundTest() {
// when
- Map channels = simpleChannelsScanner.scan();
+ Map channels = simpleChannelsScanner.scan();
// then
assertThat(channels).isEmpty();
}
@Test
- public void processClassTest() {
+ void processClassTest() {
// given
when(classScanner.scan()).thenReturn(Set.of(String.class));
- Map.Entry channel1 = Map.entry(
- "channel1",
- ChannelItem.builder().publish(Operation.builder().build()).build());
- Map.Entry channel2 = Map.entry(
- "channel2",
- ChannelItem.builder().subscribe(Operation.builder().build()).build());
+ Map.Entry channel1 =
+ Map.entry("channel1", ChannelObject.builder().build());
+ Map.Entry channel2 =
+ Map.entry("channel2", ChannelObject.builder().build());
when(classProcessor.process(any())).thenReturn(Stream.of(channel1, channel2));
// when
- Map channels = simpleChannelsScanner.scan();
+ Map channels = simpleChannelsScanner.scan();
// then
assertThat(channels).containsExactly(channel1, channel2);
}
@Test
- public void sameChannelsAreMergedTest() {
+ void sameChannelsAreMergedTest() {
// given
when(classScanner.scan()).thenReturn(Set.of(String.class));
- Map.Entry channel1 = Map.entry(
- "channel1",
- ChannelItem.builder()
- .publish(Operation.builder().operationId("pub").build())
- .build());
- Map.Entry channel2 = Map.entry(
- "channel1",
- ChannelItem.builder()
- .subscribe(Operation.builder().operationId("sub").build())
- .build());
+ Map.Entry channel1 =
+ Map.entry("channel1", ChannelObject.builder().build());
+ Map.Entry channel2 =
+ Map.entry("channel1", ChannelObject.builder().build());
when(classProcessor.process(any())).thenReturn(Stream.of(channel1, channel2));
// when
- Map channels = simpleChannelsScanner.scan();
+ Map channels = simpleChannelsScanner.scan();
// then
assertThat(channels)
- .containsExactly(Map.entry(
- "channel1",
- ChannelItem.builder()
- .publish(Operation.builder().operationId("pub").build())
- .subscribe(
- Operation.builder().operationId("sub").build())
- .build()));
+ .containsExactly(Map.entry("channel1", ChannelObject.builder().build()));
}
@Test
- public void processEmptyClassTest() {
+ void processEmptyClassTest() {
// given
when(classScanner.scan()).thenReturn(Set.of(String.class));
when(classProcessor.process(any())).thenReturn(Stream.of());
// when
- Map channels = simpleChannelsScanner.scan();
+ Map channels = simpleChannelsScanner.scan();
// then
assertThat(channels).isEmpty();
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/SimpleOperationsScannerTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/SimpleOperationsScannerTest.java
new file mode 100644
index 000000000..770c92fce
--- /dev/null
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/SimpleOperationsScannerTest.java
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: Apache-2.0
+package io.github.stavshamir.springwolf.asyncapi.scanners.channels;
+
+import io.github.stavshamir.springwolf.asyncapi.scanners.classes.ClassScanner;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;
+import org.junit.jupiter.api.Test;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class SimpleOperationsScannerTest {
+
+ private final ClassScanner classScanner = mock(ClassScanner.class);
+ private final SimpleOperationsScanner.ClassProcessor classProcessor =
+ mock(SimpleOperationsScanner.ClassProcessor.class);
+
+ private final SimpleOperationsScanner simpleOperationsScanner =
+ new SimpleOperationsScanner(classScanner, classProcessor);
+
+ @Test
+ void noClassFoundTest() {
+ // when
+ Map operations = simpleOperationsScanner.scan();
+
+ // then
+ assertThat(operations).isEmpty();
+ }
+
+ @Test
+ void processClassTest() {
+ // given
+ when(classScanner.scan()).thenReturn(Set.of(String.class));
+ Map.Entry operation1 =
+ Map.entry("operation1", Operation.builder().build());
+ Map.Entry operation2 =
+ Map.entry("operation2", Operation.builder().build());
+ when(classProcessor.process(any())).thenReturn(Stream.of(operation1, operation2));
+
+ // when
+ Map operations = simpleOperationsScanner.scan();
+
+ // then
+ assertThat(operations).containsExactly(operation2, operation1);
+ }
+
+ @Test
+ void sameOperationsAreMergedTest() {
+ // given
+ when(classScanner.scan()).thenReturn(Set.of(String.class));
+ Map.Entry operation1 =
+ Map.entry("operation1", Operation.builder().build());
+ Map.Entry operation2 =
+ Map.entry("operation1", Operation.builder().build());
+ when(classProcessor.process(any())).thenReturn(Stream.of(operation1, operation2));
+
+ // when
+ Map operations = simpleOperationsScanner.scan();
+
+ // then
+ assertThat(operations)
+ .containsExactly(Map.entry("operation1", Operation.builder().build()));
+ }
+
+ @Test
+ void processEmptyClassTest() {
+ // given
+ when(classScanner.scan()).thenReturn(Set.of(String.class));
+ when(classProcessor.process(any())).thenReturn(Stream.of());
+
+ // when
+ Map operations = simpleOperationsScanner.scan();
+
+ // then
+ assertThat(operations).isEmpty();
+ }
+}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AnnotationUtilTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AnnotationUtilTest.java
index de5e6d993..db82323ed 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AnnotationUtilTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AnnotationUtilTest.java
@@ -19,7 +19,7 @@
class AnnotationUtilTest {
@Nested
- public class FindAnnotationOrThrow {
+ class FindAnnotationOrThrow {
@Test
void findNoAnnotationTest() throws NoSuchMethodException {
Method method = TestClass.class.getMethod("notAnnotatedMethod");
@@ -72,7 +72,7 @@ void findMetaAnnotationTest() throws NoSuchMethodException {
}
@Nested
- public class FindAnnotation {
+ class FindAnnotation {
@Test
void findNoAnnotationTest() throws NoSuchMethodException {
Method method = TestClass.class.getMethod("notAnnotatedMethod");
@@ -123,7 +123,7 @@ void findMetaAnnotationTest() throws NoSuchMethodException {
}
@Nested
- public class FindAnnotations {
+ class FindAnnotations {
@Test
void findNoAnnotationTest() throws NoSuchMethodException {
Method method = TestClass.class.getMethod("notAnnotatedMethod");
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationChannelsScannerTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationChannelsScannerTest.java
index 16a0aa651..325903449 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationChannelsScannerTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationChannelsScannerTest.java
@@ -1,10 +1,6 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation;
-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.bindings.MessageBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.OperationBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.processor.TestOperationBindingProcessor;
@@ -13,11 +9,20 @@
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncOperation;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor;
import io.github.stavshamir.springwolf.asyncapi.scanners.classes.ClassScanner;
-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.AsyncHeaders;
-import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.HeaderReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ServerReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageHeaders;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessagePayload;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.info.Info;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.OperationAction;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.MultiFormatSchema;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.server.Server;
import io.github.stavshamir.springwolf.configuration.AsyncApiDocket;
import io.github.stavshamir.springwolf.configuration.AsyncApiDocketService;
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties;
@@ -53,8 +58,8 @@
class AsyncAnnotationChannelsScannerTest {
- private AsyncAnnotationChannelsScanner.AsyncAnnotationProvider asyncAnnotationProvider =
- new AsyncAnnotationChannelsScanner.AsyncAnnotationProvider<>() {
+ private final AsyncAnnotationScanner.AsyncAnnotationProvider asyncAnnotationProvider =
+ new AsyncAnnotationScanner.AsyncAnnotationProvider<>() {
@Override
public Class getAnnotation() {
return AsyncListener.class;
@@ -66,8 +71,8 @@ public AsyncOperation getAsyncOperation(AsyncListener annotation) {
}
@Override
- public OperationData.OperationType getOperationType() {
- return OperationData.OperationType.PUBLISH;
+ public OperationAction getOperationType() {
+ return OperationAction.SEND;
}
};
private final SpringwolfConfigProperties properties = new SpringwolfConfigProperties();
@@ -120,40 +125,39 @@ private void setClassToScan(Class> classToScan) {
void scan_componentHasNoListenerMethods() {
setClassToScan(ClassWithoutListenerAnnotation.class);
- Map channels = channelScanner.scan();
+ Map channels = channelScanner.scan();
assertThat(channels).isEmpty();
}
@Test
- void scan_componentHasListenerMethod() {
+ void scan_componentChannelHasListenerMethod() {
// Given a class with methods annotated with AsyncListener, where only the channel-name is set
setClassToScan(ClassWithListenerAnnotation.class);
// When scan is called
- Map actualChannels = channelScanner.scan();
+ Map actualChannels = channelScanner.scan();
// Then the returned collection contains the channel
- Message message = Message.builder()
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(SimpleFoo.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(SimpleFoo.class.getName())
.name(SimpleFoo.class.getName())
.title(SimpleFoo.class.getSimpleName())
.description("SimpleFoo Message Description")
- .payload(PayloadReference.fromModelName(SimpleFoo.class.getSimpleName()))
- .schemaFormat(Message.DEFAULT_SCHEMA_FORMAT)
- .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
.bindings(EMPTY_MAP)
.build();
- Operation operation = Operation.builder()
- .description("Auto-generated description")
- .operationId("test-channel_publish")
- .bindings(EMPTY_MAP)
- .message(message)
+ ChannelObject expectedChannel = ChannelObject.builder()
+ .bindings(null)
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
.build();
- ChannelItem expectedChannel =
- ChannelItem.builder().bindings(null).publish(operation).build();
-
assertThat(actualChannels).containsExactly(Map.entry("test-channel", expectedChannel));
}
@@ -162,10 +166,10 @@ void scan_componentHasListenerMethodWithUnknownServer() {
// Given a class with method annotated with AsyncListener, with an unknown servername
setClassToScan(ClassWithListenerAnnotationWithInvalidServer.class);
- assertThatThrownBy(() -> channelScanner.scan())
+ assertThatThrownBy(channelScanner::scan)
.isInstanceOf(IllegalArgumentException.class)
.hasMessage(
- "Operation 'test-channel_publish' defines unknown server ref 'server3'. This AsyncApi defines these server(s): [server1, server2]");
+ "Operation 'test-channel_send' defines unknown server ref 'server3'. This AsyncApi defines these server(s): [server1, server2]");
}
@Test
@@ -174,30 +178,38 @@ void scan_componentHasListenerMethodWithAllAttributes() {
setClassToScan(ClassWithListenerAnnotationWithAllAttributes.class);
// When scan is called
- Map actualChannels = channelScanner.scan();
+ Map actualChannels = channelScanner.scan();
// Then the returned collection contains the channel
- Message message = Message.builder()
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(String.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(String.class.getName())
.name(String.class.getName())
.title(String.class.getSimpleName())
.description(null)
- .schemaFormat(Message.DEFAULT_SCHEMA_FORMAT)
- .payload(PayloadReference.fromModelName(String.class.getSimpleName()))
- .headers(HeaderReference.fromModelName("TestSchema"))
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema("TestSchema")))
.bindings(EMPTY_MAP)
.build();
- Operation operation = Operation.builder()
- .description("description")
- .operationId("test-channel_publish")
- .bindings(Map.of(TestOperationBindingProcessor.TYPE, TestOperationBindingProcessor.BINDING))
- .message(message)
+ ChannelObject expectedChannel = ChannelObject.builder()
+ .bindings(null)
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
+ .servers(List.of(
+ ServerReference.builder().ref("server1").build(),
+ ServerReference.builder().ref("server2").build()))
.build();
- ChannelItem expectedChannel = ChannelItem.builder()
- .bindings(null)
- .servers(List.of("server1", "server2"))
- .publish(operation)
+ Operation expectedOperation = Operation.builder()
+ .action(OperationAction.SEND)
+ .title("test-channel_send")
+ .channel(ChannelReference.fromChannel("test-channel"))
+ .description("description")
+ .bindings(Map.of(TestOperationBindingProcessor.TYPE, TestOperationBindingProcessor.BINDING))
+ .messages(List.of(MessageReference.toChannelMessage("test-channel", message)))
.build();
assertThat(actualChannels).containsExactly(Map.entry("test-channel", expectedChannel));
@@ -209,36 +221,50 @@ void scan_componentHasMultipleListenerAnnotations() {
setClassToScan(ClassWithMultipleListenerAnnotations.class);
// When scan is called
- Map actualChannels = channelScanner.scan();
+ Map actualChannels = channelScanner.scan();
// Then the returned collection contains the channel
- Message.MessageBuilder builder = Message.builder()
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(SimpleFoo.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(SimpleFoo.class.getName())
.name(SimpleFoo.class.getName())
.title(SimpleFoo.class.getSimpleName())
- .payload(PayloadReference.fromModelName(SimpleFoo.class.getSimpleName()))
- .schemaFormat(Message.DEFAULT_SCHEMA_FORMAT)
- .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))
- .bindings(EMPTY_MAP);
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
+ .bindings(EMPTY_MAP)
+ .description("SimpleFoo Message Description")
+ .build();
- Operation operation1 = Operation.builder()
+ Operation expectedOperation1 = Operation.builder()
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("test-channel-1"))
.description("test-channel-1-description")
- .operationId("test-channel-1_publish")
+ .title("test-channel-1_send")
.bindings(EMPTY_MAP)
- .message(builder.description("SimpleFoo Message Description").build())
+ .messages(List.of(MessageReference.toChannelMessage("test-channel-1", message)))
.build();
- ChannelItem expectedChannel1 =
- ChannelItem.builder().bindings(null).publish(operation1).build();
+ ChannelObject expectedChannel1 = ChannelObject.builder()
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
+ .bindings(null)
+ .build();
- Operation operation2 = Operation.builder()
+ Operation expectedOperation2 = Operation.builder()
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("test-channel-2"))
.description("test-channel-2-description")
- .operationId("test-channel-2_publish")
+ .title("test-channel-2_send")
.bindings(EMPTY_MAP)
- .message(builder.description("SimpleFoo Message Description").build())
+ .messages(List.of(MessageReference.toChannelMessage("test-channel-2", message)))
.build();
- ChannelItem expectedChannel2 =
- ChannelItem.builder().bindings(null).publish(operation2).build();
+ ChannelObject expectedChannel2 = ChannelObject.builder()
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
+ .bindings(null)
+ .build();
assertThat(actualChannels)
.containsExactlyInAnyOrderEntriesOf(Map.of(
@@ -252,29 +278,36 @@ void scan_componentHasAsyncMethodAnnotation() {
setClassToScan(ClassWithMessageAnnotation.class);
// When scan is called
- Map actualChannels = channelScanner.scan();
+ Map actualChannels = channelScanner.scan();
// Then the returned collection contains the channel
- Message message = Message.builder()
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(SimpleFoo.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
.messageId("simpleFoo")
.name("SimpleFooPayLoad")
.title("Message Title")
.description("Message description")
- .payload(PayloadReference.fromModelName(SimpleFoo.class.getSimpleName()))
- .schemaFormat("application/schema+json;version=draft-07")
- .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
.bindings(EMPTY_MAP)
.build();
- Operation operation = Operation.builder()
+ Operation expectedOperation = Operation.builder()
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("test-channel"))
.description("test channel operation description")
- .operationId("test-channel_publish")
+ .title("test-channel_send")
.bindings(EMPTY_MAP)
- .message(message)
+ .messages(List.of(MessageReference.toChannelMessage("test-channel", message)))
.build();
- ChannelItem expectedChannel =
- ChannelItem.builder().bindings(null).publish(operation).build();
+ ChannelObject expectedChannel = ChannelObject.builder()
+ .bindings(null)
+ .messages(Map.of(message.getName(), MessageReference.toComponentMessage(message)))
+ .build();
assertThat(actualChannels).containsExactly(Map.entry("test-channel", expectedChannel));
}
@@ -345,7 +378,7 @@ private static class ClassWithMessageAnnotation {
description = "Message description",
messageId = "simpleFoo",
name = "SimpleFooPayLoad",
- schemaFormat = "application/schema+json;version=draft-07",
+ contentType = "application/schema+json;version=draft-07",
title = "Message Title")))
private void methodWithAnnotation(SimpleFoo payload) {}
@@ -361,28 +394,35 @@ void scan_componentHasOnlyDeclaredMethods(Class> clazz) {
setClassToScan(clazz);
// When scan is called
- Map actualChannels = channelScanner.scan();
+ Map actualChannels = channelScanner.scan();
// Then the returned collection contains the channel with the actual method, excluding type erased methods
- Message message = Message.builder()
+ var messagePayload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(String.class.getSimpleName()))
+ .build());
+ MessageObject message = MessageObject.builder()
+ .messageId(String.class.getName())
.name(String.class.getName())
.title(String.class.getSimpleName())
.description(null)
- .payload(PayloadReference.fromModelName(String.class.getSimpleName()))
- .schemaFormat("application/vnd.oai.openapi+json;version=3.0.0")
- .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))
+ .payload(messagePayload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
.bindings(EMPTY_MAP)
.build();
- Operation operation = Operation.builder()
+ Operation expectedOperation = Operation.builder()
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("test-channel"))
.description("test channel operation description")
- .operationId("test-channel_publish")
+ .title("test-channel_send")
.bindings(EMPTY_MAP)
- .message(message)
+ .messages(List.of(MessageReference.toChannelMessage("test-channel", message)))
.build();
- ChannelItem expectedChannel =
- ChannelItem.builder().bindings(null).publish(operation).build();
+ ChannelObject expectedChannel = ChannelObject.builder()
+ .bindings(null)
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
+ .build();
assertThat(actualChannels).containsExactly(Map.entry("test-channel", expectedChannel));
}
@@ -426,28 +466,36 @@ void scan_componentHasListenerMethodWithMetaAnnotation() {
setClassToScan(ClassWithMetaAnnotation.class);
// When scan is called
- Map actualChannels = channelScanner.scan();
+ Map actualChannels = channelScanner.scan();
// Then the returned collection contains the channel
- Message message = Message.builder()
+ var messagePayload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(String.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(String.class.getName())
.name(String.class.getName())
.title(String.class.getSimpleName())
.description(null)
- .payload(PayloadReference.fromModelName(String.class.getSimpleName()))
- .schemaFormat("application/vnd.oai.openapi+json;version=3.0.0")
- .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))
+ .payload(messagePayload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
.bindings(EMPTY_MAP)
.build();
- Operation operation = Operation.builder()
+ Operation expectedOperation = Operation.builder()
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("test-channel"))
.description("test channel operation description")
- .operationId("test-channel_publish")
+ .title("test-channel_send")
.bindings(EMPTY_MAP)
- .message(message)
+ .messages(List.of(MessageReference.toChannelMessage("test-channel", message)))
.build();
- ChannelItem expectedChannel =
- ChannelItem.builder().bindings(null).publish(operation).build();
+ ChannelObject expectedChannel = ChannelObject.builder()
+ .bindings(null)
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
+ .build();
assertThat(actualChannels).containsExactly(Map.entry("test-channel", expectedChannel));
}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationOperationsScannerTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationOperationsScannerTest.java
new file mode 100644
index 000000000..8ed34f92c
--- /dev/null
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationOperationsScannerTest.java
@@ -0,0 +1,523 @@
+// SPDX-License-Identifier: Apache-2.0
+package io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation;
+
+import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.MessageBindingProcessor;
+import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.OperationBindingProcessor;
+import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.processor.TestOperationBindingProcessor;
+import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncListener;
+import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncMessage;
+import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncOperation;
+import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor;
+import io.github.stavshamir.springwolf.asyncapi.scanners.classes.ClassScanner;
+import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.AsyncHeaders;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ServerReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageHeaders;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessagePayload;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.info.Info;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.OperationAction;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.MultiFormatSchema;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.server.Server;
+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.SchemasService;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.springframework.util.StringValueResolver;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static java.util.Collections.EMPTY_MAP;
+import static java.util.Collections.emptyList;
+import static java.util.Collections.singleton;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class AsyncAnnotationOperationsScannerTest {
+
+ private final AsyncAnnotationScanner.AsyncAnnotationProvider asyncAnnotationProvider =
+ new AsyncAnnotationScanner.AsyncAnnotationProvider<>() {
+ @Override
+ public Class getAnnotation() {
+ return AsyncListener.class;
+ }
+
+ @Override
+ public AsyncOperation getAsyncOperation(AsyncListener annotation) {
+ return annotation.operation();
+ }
+
+ @Override
+ public OperationAction getOperationType() {
+ return OperationAction.SEND;
+ }
+ };
+ private final SpringwolfConfigProperties properties = new SpringwolfConfigProperties();
+ private final ClassScanner classScanner = mock(ClassScanner.class);
+ private final SchemasService schemasService = new DefaultSchemasService(emptyList(), emptyList(), properties);
+ private final AsyncApiDocketService asyncApiDocketService = mock(AsyncApiDocketService.class);
+ private final PayloadClassExtractor payloadClassExtractor = new PayloadClassExtractor(properties);
+
+ private final List operationBindingProcessors =
+ List.of(new TestOperationBindingProcessor());
+ private final List messageBindingProcessors = emptyList();
+
+ private final StringValueResolver stringValueResolver = mock(StringValueResolver.class);
+
+ private final AsyncAnnotationOperationsScanner operationsScanner =
+ new AsyncAnnotationOperationsScanner<>(
+ asyncAnnotationProvider,
+ classScanner,
+ schemasService,
+ payloadClassExtractor,
+ operationBindingProcessors,
+ messageBindingProcessors);
+
+ @BeforeEach
+ public void setup() {
+ when(asyncApiDocketService.getAsyncApiDocket())
+ .thenReturn(AsyncApiDocket.builder()
+ .info(new Info())
+ .server("server1", new Server())
+ .server("server2", new Server())
+ .build());
+
+ operationsScanner.setEmbeddedValueResolver(stringValueResolver);
+ when(stringValueResolver.resolveStringValue(any()))
+ .thenAnswer(invocation -> switch ((String) invocation.getArgument(0)) {
+ case "${test.property.test-channel}" -> "test-channel";
+ case "${test.property.description}" -> "description";
+ case "${test.property.server1}" -> "server1";
+ case "${test.property.server2}" -> "server2";
+ default -> invocation.getArgument(0);
+ });
+ }
+
+ private void setClassToScan(Class> classToScan) {
+ Set> classesToScan = singleton(classToScan);
+ when(classScanner.scan()).thenReturn(classesToScan);
+ }
+
+ @Test
+ void scan_componentHasNoListenerMethods() {
+ setClassToScan(ClassWithoutListenerAnnotation.class);
+
+ Map channels = operationsScanner.scan();
+
+ assertThat(channels).isEmpty();
+ }
+
+ @Test
+ void scan_componentOperationHasListenerMethod() {
+ // Given a class with methods annotated with AsyncListener, where only the channel-name is set
+ setClassToScan(ClassWithListenerAnnotation.class);
+
+ // When scan is called
+ Map actualOperations = operationsScanner.scan();
+
+ // Then the returned collection contains the channel
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(SimpleFoo.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(SimpleFoo.class.getName())
+ .name(SimpleFoo.class.getName())
+ .title(SimpleFoo.class.getSimpleName())
+ .description("SimpleFoo Message Description")
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
+ .bindings(EMPTY_MAP)
+ .build();
+
+ Operation expectedOperation = Operation.builder()
+ .title("test-channel_send")
+ .action(OperationAction.SEND)
+ .description("Auto-generated description")
+ .channel(ChannelReference.fromChannel("test-channel"))
+ .messages(List.of(MessageReference.toChannelMessage("test-channel", message)))
+ .bindings(EMPTY_MAP)
+ .build();
+
+ assertThat(actualOperations)
+ .containsExactly(Map.entry("test-channel_send_methodWithAnnotation", expectedOperation));
+ }
+
+ @Test
+ void scan_componentHasListenerMethodWithAllAttributes() {
+ // Given a class with method annotated with AsyncListener, where all attributes are set
+ setClassToScan(ClassWithListenerAnnotationWithAllAttributes.class);
+
+ // When scan is called
+ Map actualOperations = operationsScanner.scan();
+
+ // Then the returned collection contains the channel
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(String.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(String.class.getName())
+ .name(String.class.getName())
+ .title(String.class.getSimpleName())
+ .description(null)
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema("TestSchema")))
+ .bindings(EMPTY_MAP)
+ .build();
+
+ ChannelObject expectedChannel = ChannelObject.builder()
+ .bindings(null)
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
+ .servers(List.of(
+ ServerReference.builder().ref("server1").build(),
+ ServerReference.builder().ref("server2").build()))
+ .build();
+
+ Operation expectedOperation = Operation.builder()
+ .action(OperationAction.SEND)
+ .title("test-channel_send")
+ .channel(ChannelReference.fromChannel("test-channel"))
+ .description("description")
+ .bindings(Map.of(TestOperationBindingProcessor.TYPE, TestOperationBindingProcessor.BINDING))
+ .messages(List.of(MessageReference.toChannelMessage("test-channel", message)))
+ .build();
+
+ assertThat(actualOperations)
+ .containsExactly(Map.entry("test-channel_send_methodWithAnnotation", expectedOperation));
+ }
+
+ @Test
+ void scan_componentHasMultipleListenerAnnotations() {
+ // Given a class with methods annotated with AsyncListener, where only the channel-name is set
+ setClassToScan(ClassWithMultipleListenerAnnotations.class);
+
+ // When scan is called
+ Map actualOperations = operationsScanner.scan();
+
+ // Then the returned collection contains the channel
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(SimpleFoo.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(SimpleFoo.class.getName())
+ .name(SimpleFoo.class.getName())
+ .title(SimpleFoo.class.getSimpleName())
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
+ .bindings(EMPTY_MAP)
+ .description("SimpleFoo Message Description")
+ .build();
+
+ Operation expectedOperation1 = Operation.builder()
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("test-channel-1"))
+ .description("test-channel-1-description")
+ .title("test-channel-1_send")
+ .bindings(EMPTY_MAP)
+ .messages(List.of(MessageReference.toChannelMessage("test-channel-1", message)))
+ .build();
+
+ ChannelObject expectedChannel1 = ChannelObject.builder()
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
+ .bindings(null)
+ .build();
+
+ Operation expectedOperation2 = Operation.builder()
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("test-channel-2"))
+ .description("test-channel-2-description")
+ .title("test-channel-2_send")
+ .bindings(EMPTY_MAP)
+ .messages(List.of(MessageReference.toChannelMessage("test-channel-2", message)))
+ .build();
+
+ ChannelObject expectedChannel2 = ChannelObject.builder()
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
+ .bindings(null)
+ .build();
+
+ assertThat(actualOperations)
+ .containsExactlyInAnyOrderEntriesOf(Map.of(
+ "test-channel-1_send_methodWithMultipleAnnotation", expectedOperation1,
+ "test-channel-2_send_methodWithMultipleAnnotation", expectedOperation2));
+ }
+
+ @Test
+ void scan_componentHasAsyncMethodAnnotation() {
+ // Given a class with methods annotated with AsyncListener, where only the channel-name is set
+ setClassToScan(ClassWithMessageAnnotation.class);
+
+ // When scan is called
+ Map actualOperations = operationsScanner.scan();
+
+ // Then the returned collection contains the channel
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(SimpleFoo.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId("simpleFoo")
+ .name("SimpleFooPayLoad")
+ .title("Message Title")
+ .description("Message description")
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
+ .bindings(EMPTY_MAP)
+ .build();
+
+ Operation expectedOperation = Operation.builder()
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("test-channel"))
+ .description("test channel operation description")
+ .title("test-channel_send")
+ .bindings(EMPTY_MAP)
+ .messages(List.of(MessageReference.toChannelMessage("test-channel", message)))
+ .build();
+
+ ChannelObject expectedChannel = ChannelObject.builder()
+ .bindings(null)
+ .messages(Map.of(message.getName(), MessageReference.toComponentMessage(message)))
+ .build();
+
+ assertThat(actualOperations)
+ .containsExactly(Map.entry("test-channel_send_methodWithAnnotation", expectedOperation));
+ }
+
+ private static class ClassWithoutListenerAnnotation {
+
+ private void methodWithoutAnnotation() {}
+ }
+
+ private static class ClassWithListenerAnnotation {
+
+ @AsyncListener(operation = @AsyncOperation(channelName = "test-channel"))
+ private void methodWithAnnotation(SimpleFoo payload) {}
+
+ private void methodWithoutAnnotation() {}
+ }
+
+ private static class ClassWithListenerAnnotationWithInvalidServer {
+
+ @AsyncListener(
+ operation =
+ @AsyncOperation(
+ channelName = "test-channel",
+ description = "test channel operation description",
+ servers = {"server3"}))
+ private void methodWithAnnotation(SimpleFoo payload) {}
+ }
+
+ private static class ClassWithListenerAnnotationWithAllAttributes {
+
+ @AsyncListener(
+ operation =
+ @AsyncOperation(
+ channelName = "${test.property.test-channel}",
+ description = "${test.property.description}",
+ payloadType = String.class,
+ servers = {"${test.property.server1}", "${test.property.server2}"},
+ headers =
+ @AsyncOperation.Headers(
+ schemaName = "TestSchema",
+ values = {
+ @AsyncOperation.Headers.Header(name = "header", value = "value")
+ })))
+ @TestOperationBindingProcessor.TestOperationBinding()
+ private void methodWithAnnotation(SimpleFoo payload) {}
+
+ private void methodWithoutAnnotation() {}
+ }
+
+ private static class ClassWithMultipleListenerAnnotations {
+
+ @AsyncListener(
+ operation = @AsyncOperation(channelName = "test-channel-1", description = "test-channel-1-description"))
+ @AsyncListener(
+ operation = @AsyncOperation(channelName = "test-channel-2", description = "test-channel-2-description"))
+ private void methodWithMultipleAnnotation(SimpleFoo payload) {}
+ }
+
+ private static class ClassWithMessageAnnotation {
+
+ @AsyncListener(
+ operation =
+ @AsyncOperation(
+ channelName = "test-channel",
+ description = "test channel operation description",
+ message =
+ @AsyncMessage(
+ description = "Message description",
+ messageId = "simpleFoo",
+ name = "SimpleFooPayLoad",
+ contentType = "application/schema+json;version=draft-07",
+ title = "Message Title")))
+ private void methodWithAnnotation(SimpleFoo payload) {}
+
+ private void methodWithoutAnnotation() {}
+ }
+
+ @Nested
+ class ImplementingInterface {
+ @ParameterizedTest
+ @ValueSource(classes = {ClassImplementingInterface.class, ClassImplementingInterfaceWithAnnotation.class})
+ void scan_componentHasOnlyDeclaredMethods(Class> clazz) {
+ // Given a class with a method, which is declared in a generic interface
+ setClassToScan(clazz);
+
+ // When scan is called
+ Map actualOperations = operationsScanner.scan();
+
+ // Then the returned collection contains the channel with the actual method, excluding type erased methods
+ var messagePayload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(String.class.getSimpleName()))
+ .build());
+ MessageObject message = MessageObject.builder()
+ .messageId(String.class.getName())
+ .name(String.class.getName())
+ .title(String.class.getSimpleName())
+ .description(null)
+ .payload(messagePayload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
+ .bindings(EMPTY_MAP)
+ .build();
+
+ Operation expectedOperation = Operation.builder()
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("test-channel"))
+ .description("test channel operation description")
+ .title("test-channel_send")
+ .bindings(EMPTY_MAP)
+ .messages(List.of(MessageReference.toChannelMessage("test-channel", message)))
+ .build();
+
+ ChannelObject expectedChannel = ChannelObject.builder()
+ .bindings(null)
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
+ .build();
+
+ assertThat(actualOperations)
+ .containsExactly(Map.entry("test-channel_send_methodFromInterface", expectedOperation));
+ }
+
+ private static class ClassImplementingInterface implements ClassInterface {
+
+ @AsyncListener(
+ operation =
+ @AsyncOperation(
+ channelName = "test-channel",
+ description = "test channel operation description"))
+ @Override
+ public void methodFromInterface(String payload) {}
+ }
+
+ interface ClassInterface {
+ void methodFromInterface(T payload);
+ }
+
+ private static class ClassImplementingInterfaceWithAnnotation implements ClassInterfaceWithAnnotation {
+
+ @Override
+ public void methodFromInterface(String payload) {}
+ }
+
+ interface ClassInterfaceWithAnnotation {
+ @AsyncListener(
+ operation =
+ @AsyncOperation(
+ channelName = "test-channel",
+ description = "test channel operation description"))
+ void methodFromInterface(T payload);
+ }
+ }
+
+ @Nested
+ class MetaAnnotation {
+ @Test
+ void scan_componentHasListenerMethodWithMetaAnnotation() {
+ // Given a class with methods annotated with a AsyncListener meta annotation
+ setClassToScan(ClassWithMetaAnnotation.class);
+
+ // When scan is called
+ Map actualOperations = operationsScanner.scan();
+
+ // Then the returned collection contains the channel
+ var messagePayload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(String.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(String.class.getName())
+ .name(String.class.getName())
+ .title(String.class.getSimpleName())
+ .description(null)
+ .payload(messagePayload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
+ .bindings(EMPTY_MAP)
+ .build();
+
+ Operation expectedOperation = Operation.builder()
+ .action(OperationAction.SEND)
+ .channel(ChannelReference.fromChannel("test-channel"))
+ .description("test channel operation description")
+ .title("test-channel_send")
+ .bindings(EMPTY_MAP)
+ .messages(List.of(MessageReference.toChannelMessage("test-channel", message)))
+ .build();
+
+ ChannelObject expectedChannel = ChannelObject.builder()
+ .bindings(null)
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
+ .build();
+
+ assertThat(actualOperations)
+ .containsExactly(Map.entry("test-channel_send_methodFromInterface", expectedOperation));
+ }
+
+ public static class ClassWithMetaAnnotation {
+ @AsyncListenerMetaAnnotation
+ void methodFromInterface(String payload) {}
+ }
+
+ @Target({ElementType.TYPE, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
+ @Retention(RetentionPolicy.RUNTIME)
+ @Inherited
+ @AsyncListener(
+ operation =
+ @AsyncOperation(
+ channelName = "test-channel",
+ description = "test channel operation description"))
+ public @interface AsyncListenerMetaAnnotation {}
+ }
+
+ @Data
+ @NoArgsConstructor
+ @Schema(description = "SimpleFoo Message Description")
+ private static class SimpleFoo {
+ private String s;
+ private boolean b;
+ }
+}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationScannerUtilTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationScannerUtilTest.java
index 88b4d77e7..44675ba7b 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationScannerUtilTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/AsyncAnnotationScannerUtilTest.java
@@ -1,16 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation;
-import com.asyncapi.v2.binding.message.MessageBinding;
-import com.asyncapi.v2.binding.operation.OperationBinding;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.processor.TestAbstractOperationBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.processor.TestMessageBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.processor.TestOperationBindingProcessor;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncListener;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncMessage;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.operationdata.annotation.AsyncOperation;
-import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.Message;
import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.AsyncHeaders;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
import org.assertj.core.util.Maps;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@@ -127,11 +127,11 @@ void processMessageFromAnnotationWithoutAsyncMessage(Class> classWithOperation
.thenAnswer(invocation -> invocation.getArgument(0).toString());
// when
- Message.MessageBuilder actual = Message.builder();
+ MessageObject.MessageObjectBuilder actual = MessageObject.builder();
AsyncAnnotationScannerUtil.processAsyncMessageAnnotation(actual, message, stringResolver);
// then
- var expectedMessage = Message.builder().build();
+ var expectedMessage = MessageObject.builder().build();
assertEquals(expectedMessage, actual.build());
}
@@ -150,15 +150,14 @@ void processMessageFromAnnotationWithAsyncMessage(Class> classWithOperationBin
.thenAnswer(invocation -> invocation.getArgument(0).toString());
// when
- Message.MessageBuilder actual = Message.builder();
+ MessageObject.MessageObjectBuilder actual = MessageObject.builder();
AsyncAnnotationScannerUtil.processAsyncMessageAnnotation(actual, message, stringResolver);
// then
- var expectedMessage = Message.builder()
+ var expectedMessage = MessageObject.builder()
.description("Message description")
.messageId("simpleFoo")
.name("SimpleFooPayLoad")
- .schemaFormat("application/schema+json;version=draft-07")
.title("Message Title")
.build();
assertEquals(expectedMessage, actual.build());
@@ -216,7 +215,7 @@ private void methodWithAnnotation(String payload) {}
description = "Message description",
messageId = "simpleFoo",
name = "SimpleFooPayLoad",
- schemaFormat = "application/schema+json;version=draft-07",
+ contentType = "application/schema+json;version=draft-07",
title = "Message Title")))
@TestOperationBindingProcessor.TestOperationBinding()
private void methodWithAsyncMessageAnnotation(String payload) {}
@@ -259,7 +258,7 @@ private void methodWithAnnotation(String payload) {}
description = "Message description",
messageId = "simpleFoo",
name = "SimpleFooPayLoad",
- schemaFormat = "application/schema+json;version=draft-07",
+ contentType = "application/schema+json;version=draft-07",
title = "Message Title")))
@TestAbstractOperationBindingProcessor.TestOperationBinding()
private void methodWithAsyncMessageAnnotation(String payload) {}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationChannelsScannerIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationChannelsScannerIntegrationTest.java
index 5ffbef12a..95885078d 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationChannelsScannerIntegrationTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationChannelsScannerIntegrationTest.java
@@ -1,17 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation;
-import com.asyncapi.v2._6_0.model.channel.ChannelItem;
-import com.asyncapi.v2._6_0.model.channel.operation.Operation;
-import com.asyncapi.v2.binding.channel.ChannelBinding;
-import com.asyncapi.v2.binding.message.MessageBinding;
-import com.asyncapi.v2.binding.operation.OperationBinding;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.BindingFactory;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor;
-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.AsyncHeadersNotDocumented;
-import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.HeaderReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.ChannelBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageHeaders;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessagePayload;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.MultiFormatSchema;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaReference;
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties;
import io.github.stavshamir.springwolf.schemas.DefaultSchemasService;
import io.github.stavshamir.springwolf.schemas.SchemasService;
@@ -34,9 +36,7 @@
import java.lang.annotation.Target;
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;
@ExtendWith(SpringExtension.class)
@@ -77,7 +77,7 @@ class NoClassListener {
@Test
void scan_componentHasNoClassLevelRabbitListenerAnnotation() {
// when
- List> channels =
+ List> channels =
scanner.process(ClassWithoutClassListener.class).toList();
// then
@@ -96,7 +96,7 @@ class NoMethodListener {
@Test
void scan_componentHasNoClassLevelRabbitListenerAnnotation() {
// when
- List> channels =
+ List> channels =
scanner.process(ClassWithoutMethodListener.class).toList();
// then
@@ -115,28 +115,27 @@ class OneMethodLevelAnnotation {
@Test
void scan_componentWithOneMethodLevelAnnotation() {
// when
- List> actualChannels =
+ List> actualChannels =
scanner.process(ClassWithOneMethodLevelHandler.class).toList();
// then
- Message message = Message.builder()
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(SimpleFoo.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(SimpleFoo.class.getName())
.name(SimpleFoo.class.getName())
.title(SimpleFoo.class.getSimpleName())
- .payload(PayloadReference.fromModelName(SimpleFoo.class.getSimpleName()))
- .headers(HeaderReference.fromModelName(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getSchemaName()))
+ .payload(payload)
+ .headers(MessageHeaders.of(
+ MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getSchemaName())))
.bindings(TestBindingFactory.defaultMessageBinding)
.build();
- Operation operation = Operation.builder()
- .description("Auto-generated description")
- .operationId("test-channel_publish_ClassWithOneMethodLevelHandler")
- .bindings(TestBindingFactory.defaultOperationBinding)
- .message(message)
- .build();
-
- ChannelItem expectedChannel = ChannelItem.builder()
+ ChannelObject expectedChannel = ChannelObject.builder()
.bindings(TestBindingFactory.defaultChannelBinding)
- .publish(operation)
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
.build();
assertThat(actualChannels).containsExactly(Map.entry(TestBindingFactory.CHANNEL, expectedChannel));
@@ -158,36 +157,43 @@ class MultipleMethodLevelAnnotations {
@Test
void scan_componentWithMultipleRabbitHandlerMethods() {
// when
- List> actualChannels =
+ List> actualChannels =
scanner.process(ClassWithMultipleMethodLevelHandlers.class).toList();
// Then the returned collection contains the channel with message set to oneOf
- Message fooMessage = Message.builder()
+ MessagePayload simpleFooPayload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(SimpleFoo.class.getSimpleName()))
+ .build());
+
+ MessageObject fooMessage = MessageObject.builder()
.name(SimpleFoo.class.getName())
.title(SimpleFoo.class.getSimpleName())
- .payload(PayloadReference.fromModelName(SimpleFoo.class.getSimpleName()))
- .headers(HeaderReference.fromModelName(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getSchemaName()))
+ .payload(simpleFooPayload)
+ .headers(MessageHeaders.of(
+ MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getSchemaName())))
.bindings(TestBindingFactory.defaultMessageBinding)
.build();
- Message barMessage = Message.builder()
+ MessagePayload stringPayload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(String.class.getSimpleName()))
+ .build());
+
+ MessageObject barMessage = MessageObject.builder()
.name(String.class.getName())
.title(String.class.getSimpleName())
- .payload(PayloadReference.fromModelName(String.class.getSimpleName()))
- .headers(HeaderReference.fromModelName(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getSchemaName()))
+ .payload(stringPayload)
+ .headers(MessageHeaders.of(
+ MessageReference.toSchema(AsyncHeadersNotDocumented.NOT_DOCUMENTED.getSchemaName())))
.bindings(TestBindingFactory.defaultMessageBinding)
.build();
- Operation operation = Operation.builder()
- .description("Auto-generated description")
- .operationId("test-channel_publish_ClassWithMultipleMethodLevelHandlers")
- .bindings(TestBindingFactory.defaultOperationBinding)
- .message(toMessageObjectOrComposition(Set.of(fooMessage, barMessage)))
- .build();
-
- ChannelItem expectedChannel = ChannelItem.builder()
+ ChannelObject expectedChannel = ChannelObject.builder()
.bindings(TestBindingFactory.defaultChannelBinding)
- .publish(operation)
+ .messages(Map.of(
+ fooMessage.getMessageId(),
+ MessageReference.toComponentMessage(fooMessage),
+ barMessage.getMessageId(),
+ MessageReference.toComponentMessage(barMessage)))
.build();
assertThat(actualChannels).containsExactly(Map.entry(TestBindingFactory.CHANNEL, expectedChannel));
@@ -224,11 +230,11 @@ private static class SimpleFoo {
static class TestBindingFactory implements BindingFactory {
public static final String CHANNEL = "test-channel";
- public static final Map defaultMessageBinding =
+ public static final Map defaultMessageBinding =
Map.of(CHANNEL, new TestBindingFactory.TestMessageBinding());
- public static final Map defaultChannelBinding =
+ public static final Map defaultChannelBinding =
Map.of(CHANNEL, new TestBindingFactory.TestChannelBinding());
- public static final Map defaultOperationBinding =
+ public static final Map defaultOperationBinding =
Map.of(CHANNEL, new TestBindingFactory.TestOperationBinding());
@Override
@@ -237,17 +243,17 @@ public String getChannelName(TestClassListener annotation) {
}
@Override
- public Map buildChannelBinding(TestClassListener annotation) {
- return (Map) defaultChannelBinding;
+ public Map buildChannelBinding(TestClassListener annotation) {
+ return defaultChannelBinding;
}
@Override
- public Map buildOperationBinding(TestClassListener annotation) {
- return (Map) defaultOperationBinding;
+ public Map buildOperationBinding(TestClassListener annotation) {
+ return defaultOperationBinding;
}
@Override
- public Map buildMessageBinding(TestClassListener annotation) {
+ public Map buildMessageBinding(TestClassListener annotation) {
return defaultMessageBinding;
}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationChannelsScannerTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationChannelsScannerTest.java
index fd6f7f22f..8a66b459d 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationChannelsScannerTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationChannelsScannerTest.java
@@ -1,19 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation;
-import com.asyncapi.v2._6_0.model.channel.ChannelItem;
-import com.asyncapi.v2._6_0.model.channel.operation.Operation;
-import com.asyncapi.v2.binding.channel.amqp.AMQPChannelBinding;
-import com.asyncapi.v2.binding.message.MessageBinding;
-import com.asyncapi.v2.binding.message.amqp.AMQPMessageBinding;
-import com.asyncapi.v2.binding.operation.amqp.AMQPOperationBinding;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.BindingFactory;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor;
-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.AsyncHeadersNotDocumented;
-import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.HeaderReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.ChannelBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.amqp.AMQPChannelBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.amqp.AMQPMessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.amqp.AMQPOperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageHeaders;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessagePayload;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.MultiFormatSchema;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaReference;
import io.github.stavshamir.springwolf.schemas.SchemasService;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -24,7 +28,6 @@
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Map;
-import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -48,10 +51,12 @@ class ClassLevelAnnotationChannelsScannerTest {
schemasService);
private static final String CHANNEL = "test-channel";
- private static final Map defaultOperationBinding = Map.of("protocol", new AMQPOperationBinding());
- private static final Map defaultMessageBinding =
+ private static final Map defaultOperationBinding =
+ Map.of("protocol", new AMQPOperationBinding());
+ private static final Map defaultMessageBinding =
Map.of("protocol", new AMQPMessageBinding());
- private static final Map defaultChannelBinding = Map.of("protocol", new AMQPChannelBinding());
+ private static final Map defaultChannelBinding =
+ Map.of("protocol", new AMQPChannelBinding());
@BeforeEach
void setUp() {
@@ -65,37 +70,35 @@ void setUp() {
doReturn(String.class).when(payloadClassExtractor).extractFrom(any());
doAnswer(invocation -> invocation.>getArgument(0).getSimpleName())
.when(schemasService)
- .register(any(Class.class));
+ .registerSchema(any(Class.class));
doAnswer(invocation -> AsyncHeaders.NOT_DOCUMENTED.getSchemaName())
.when(schemasService)
- .register(any(AsyncHeaders.class));
+ .registerSchema(any(AsyncHeaders.class));
}
@Test
void scan_componentHasTestListenerMethods() {
// when
- List> channels =
- scanner.process(ClassWithTestListenerAnnotation.class).collect(Collectors.toList());
+ List> channels =
+ scanner.process(ClassWithTestListenerAnnotation.class).toList();
// then
- Message message = Message.builder()
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(String.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(String.class.getName())
.name(String.class.getName())
.title(String.class.getSimpleName())
- .payload(PayloadReference.fromModelName(String.class.getSimpleName()))
- .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
.bindings(defaultMessageBinding)
.build();
- Operation operation = Operation.builder()
- .description("Auto-generated description")
- .operationId(CHANNEL + "_publish_ClassWithTestListenerAnnotation")
- .bindings(defaultOperationBinding)
- .message(message)
- .build();
-
- ChannelItem expectedChannelItem = ChannelItem.builder()
+ ChannelObject expectedChannelItem = ChannelObject.builder()
.bindings(defaultChannelBinding)
- .publish(operation)
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
.build();
assertThat(channels).containsExactly(Map.entry(CHANNEL, expectedChannelItem));
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationOperationsScannerTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationOperationsScannerTest.java
new file mode 100644
index 000000000..d79837b5f
--- /dev/null
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/ClassLevelAnnotationOperationsScannerTest.java
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: Apache-2.0
+package io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation;
+
+import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.BindingFactory;
+import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor;
+import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.AsyncHeaders;
+import io.github.stavshamir.springwolf.asyncapi.types.channel.operation.message.header.AsyncHeadersNotDocumented;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.ChannelBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.amqp.AMQPChannelBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.amqp.AMQPMessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.amqp.AMQPOperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageHeaders;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessagePayload;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.Operation;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.operation.OperationAction;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.MultiFormatSchema;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaReference;
+import io.github.stavshamir.springwolf.schemas.SchemasService;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+import java.util.Map;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class ClassLevelAnnotationOperationsScannerTest {
+
+ private final PayloadClassExtractor payloadClassExtractor = mock(PayloadClassExtractor.class);
+ private final BindingFactory bindingFactory = mock(BindingFactory.class);
+ private final SchemasService schemasService = mock(SchemasService.class);
+ ClassLevelAnnotationOperationsScanner scanner =
+ new ClassLevelAnnotationOperationsScanner<>(
+ TestClassListener.class,
+ TestMethodListener.class,
+ bindingFactory,
+ new AsyncHeadersNotDocumented(),
+ payloadClassExtractor,
+ schemasService);
+
+ private static final String CHANNEL = "test-channel";
+ private static final Map defaultOperationBinding =
+ Map.of("protocol", new AMQPOperationBinding());
+ private static final Map defaultMessageBinding =
+ Map.of("protocol", new AMQPMessageBinding());
+ private static final Map defaultChannelBinding =
+ Map.of("protocol", new AMQPChannelBinding());
+
+ @BeforeEach
+ void setUp() {
+ // when
+ when(bindingFactory.getChannelName(any())).thenReturn(CHANNEL);
+
+ doReturn(defaultOperationBinding).when(bindingFactory).buildOperationBinding(any());
+ doReturn(defaultChannelBinding).when(bindingFactory).buildChannelBinding(any());
+ doReturn(defaultMessageBinding).when(bindingFactory).buildMessageBinding(any());
+
+ doReturn(String.class).when(payloadClassExtractor).extractFrom(any());
+ doAnswer(invocation -> invocation.>getArgument(0).getSimpleName())
+ .when(schemasService)
+ .registerSchema(any(Class.class));
+ doAnswer(invocation -> AsyncHeaders.NOT_DOCUMENTED.getSchemaName())
+ .when(schemasService)
+ .registerSchema(any(AsyncHeaders.class));
+ }
+
+ @Test
+ void scan_componentHasTestListenerMethods() {
+ // when
+ List> operations =
+ scanner.process(ClassWithTestListenerAnnotation.class).toList();
+
+ // then
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(String.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(String.class.getName())
+ .name(String.class.getName())
+ .title(String.class.getSimpleName())
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
+ .bindings(defaultMessageBinding)
+ .build();
+
+ Operation expectedOperation = Operation.builder()
+ .action(OperationAction.RECEIVE)
+ .channel(ChannelReference.fromChannel(CHANNEL))
+ .messages(List.of(MessageReference.toChannelMessage(CHANNEL, message)))
+ .bindings(Map.of("protocol", AMQPOperationBinding.builder().build()))
+ .build();
+ String operationName = CHANNEL + "_receive_ClassWithTestListenerAnnotation";
+ assertThat(operations).containsExactly(Map.entry(operationName, expectedOperation));
+ }
+
+ @TestClassListener
+ private static class ClassWithTestListenerAnnotation {
+ @TestMethodListener
+ private void methodWithAnnotation(String payload) {}
+
+ private void methodWithoutAnnotation() {}
+ }
+
+ @Data
+ @NoArgsConstructor
+ private static class SimpleFoo {
+ private String s;
+ private boolean b;
+ }
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface TestClassListener {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @interface TestMethodListener {}
+}
diff --git a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/MethodLevelAnnotationChannelsScannerIntegrationTest.java b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/MethodLevelAnnotationChannelsScannerIntegrationTest.java
index 68993915b..a1b12ed33 100644
--- a/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/MethodLevelAnnotationChannelsScannerIntegrationTest.java
+++ b/springwolf-core/src/test/java/io/github/stavshamir/springwolf/asyncapi/scanners/channels/annotation/MethodLevelAnnotationChannelsScannerIntegrationTest.java
@@ -1,17 +1,19 @@
// SPDX-License-Identifier: Apache-2.0
package io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation;
-import com.asyncapi.v2._6_0.model.channel.ChannelItem;
-import com.asyncapi.v2._6_0.model.channel.operation.Operation;
-import com.asyncapi.v2.binding.channel.ChannelBinding;
-import com.asyncapi.v2.binding.message.MessageBinding;
-import com.asyncapi.v2.binding.operation.OperationBinding;
import io.github.stavshamir.springwolf.asyncapi.scanners.bindings.BindingFactory;
import io.github.stavshamir.springwolf.asyncapi.scanners.channels.payload.PayloadClassExtractor;
-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.asyncapi.v3.bindings.ChannelBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.MessageBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.bindings.OperationBinding;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.ChannelObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageHeaders;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageObject;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessagePayload;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.channel.message.MessageReference;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.MultiFormatSchema;
+import io.github.stavshamir.springwolf.asyncapi.v3.model.schema.SchemaReference;
import io.github.stavshamir.springwolf.configuration.properties.SpringwolfConfigProperties;
import io.github.stavshamir.springwolf.schemas.DefaultSchemasService;
import io.github.stavshamir.springwolf.schemas.SchemasService;
@@ -37,7 +39,6 @@
import static io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation.MethodLevelAnnotationChannelsScannerIntegrationTest.TestBindingFactory.defaultChannelBinding;
import static io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation.MethodLevelAnnotationChannelsScannerIntegrationTest.TestBindingFactory.defaultMessageBinding;
-import static io.github.stavshamir.springwolf.asyncapi.scanners.channels.annotation.MethodLevelAnnotationChannelsScannerIntegrationTest.TestBindingFactory.defaultOperationBinding;
import static org.assertj.core.api.Assertions.assertThat;
@ExtendWith(SpringExtension.class)
@@ -72,7 +73,7 @@ class NoListener {
@Test
void scan_componentHasNoListenerMethods() {
// when
- List> channels =
+ List> channels =
scanner.process(ClassWithoutListenerAnnotation.class).toList();
// then
@@ -89,28 +90,26 @@ class WithListener {
@Test
void scan_componentHasListenerMethod() {
// when
- List> actualChannels =
+ List> actualChannels =
scanner.process(ClassWithListenerAnnotation.class).toList();
// then
- Message message = Message.builder()
+ MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(SimpleFoo.class.getSimpleName()))
+ .build());
+
+ MessageObject message = MessageObject.builder()
+ .messageId(SimpleFoo.class.getName())
.name(SimpleFoo.class.getName())
.title(SimpleFoo.class.getSimpleName())
- .payload(PayloadReference.fromModelName(SimpleFoo.class.getSimpleName()))
- .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))
+ .payload(payload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
.bindings(TestBindingFactory.defaultMessageBinding)
.build();
- Operation operation = Operation.builder()
- .description("Auto-generated description")
- .operationId("test-channel_publish_methodWithAnnotation")
- .bindings(TestBindingFactory.defaultOperationBinding)
- .message(message)
- .build();
-
- ChannelItem expectedChannel = ChannelItem.builder()
+ ChannelObject expectedChannel = ChannelObject.builder()
.bindings(defaultChannelBinding)
- .publish(operation)
+ .messages(Map.of(message.getMessageId(), MessageReference.toComponentMessage(message)))
.build();
assertThat(actualChannels).containsExactly(Map.entry(TestBindingFactory.CHANNEL, expectedChannel));
@@ -130,48 +129,48 @@ class OneChannelTwoPayloads {
@Test
void scan_componentHasTestListenerMethods_multiplePayloads() {
// when
- List> channels = scanner.process(
+ List> channels = scanner.process(
ClassWithTestListenerAnnotationMultiplePayloads.class)
.toList();
// then
- Message messageSimpleFoo = Message.builder()
+ MessagePayload simpleFooPayload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(SimpleFoo.class.getSimpleName()))
+ .build());
+ MessagePayload stringPayload = MessagePayload.of(MultiFormatSchema.builder()
+ .schema(SchemaReference.fromSchema(String.class.getSimpleName()))
+ .build());
+
+ MessageObject messageSimpleFoo = MessageObject.builder()
+ .messageId(SimpleFoo.class.getName())
.name(SimpleFoo.class.getName())
.title(SimpleFoo.class.getSimpleName())
- .payload(PayloadReference.fromModelName(SimpleFoo.class.getSimpleName()))
- .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))
+ .payload(simpleFooPayload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
.bindings(TestBindingFactory.defaultMessageBinding)
.build();
- Message messageString = Message.builder()
+ MessageObject messageString = MessageObject.builder()
+ .messageId(String.class.getName())
.name(String.class.getName())
.title(String.class.getSimpleName())
- .payload(PayloadReference.fromModelName(String.class.getSimpleName()))
- .headers(HeaderReference.fromModelName(AsyncHeaders.NOT_DOCUMENTED.getSchemaName()))
+ .payload(stringPayload)
+ .headers(MessageHeaders.of(MessageReference.toSchema(AsyncHeaders.NOT_DOCUMENTED.getSchemaName())))
.bindings(TestBindingFactory.defaultMessageBinding)
.build();
- ChannelItem expectedChannelItem = ChannelItem.builder()
+ ChannelObject expectedChannelItem = ChannelObject.builder()
+ .messages(Map.of(
+ messageSimpleFoo.getMessageId(), MessageReference.toComponentMessage(messageSimpleFoo)))
.bindings(defaultChannelBinding)
- .publish(Operation.builder()
- .description("Auto-generated description")
- .operationId(TestBindingFactory.CHANNEL + "_publish_methodWithAnnotation")
- .bindings(TestBindingFactory.defaultOperationBinding)
- .message(messageSimpleFoo)
- .build())
.build();
- ChannelItem expectedChannelItem2 = ChannelItem.builder()
+ ChannelObject expectedChannelItem2 = ChannelObject.builder()
.bindings(defaultChannelBinding)
- .publish(Operation.builder()
- .description("Auto-generated description")
- .operationId(TestBindingFactory.CHANNEL + "_publish_methodWithAnnotation")
- .bindings(TestBindingFactory.defaultOperationBinding)
- .message(messageString)
- .build())
+ .messages(Map.of(messageString.getMessageId(), MessageReference.toComponentMessage(messageString)))
.build();
assertThat(channels)
- .containsExactly(
+ .containsExactlyInAnyOrder(
Map.entry(TestBindingFactory.CHANNEL, expectedChannelItem),
Map.entry(TestBindingFactory.CHANNEL, expectedChannelItem2));
}
@@ -191,28 +190,26 @@ class MetaAnnotation {
@Test
void scan_componentHasListenerMetaMethod() {
// when
- List> actualChannels =
+ List