Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Feat/handle no payload #687

Merged
merged 7 commits into from Apr 26, 2024
Merged

Conversation

sam0r040
Copy link
Collaborator

@sam0r040 sam0r040 commented Apr 5, 2024

resolves: GH-665

Copy link

netlify bot commented Apr 5, 2024

Deploy Preview for springwolf-ui canceled.

Name Link
🔨 Latest commit b7f2a4e
🔍 Latest deploy log https://app.netlify.com/sites/springwolf-ui/deploys/662b6b50eb2439000837b0cc

@timonback timonback force-pushed the feat/handle-no-payload branch 2 times, most recently from 18dc5c0 to 3d37f09 Compare April 12, 2024 17:02
@timonback timonback marked this pull request as ready for review April 19, 2024 16:58
timonback and others added 2 commits April 19, 2024 19:00
Co-authored-by: David Müller <david.mueller@codecentric.de>

fix(core): extract AsyncMessage#description

Co-authored-by: David Müller <david.mueller@codecentric.de>

feat(core): use fqn in for schema name

Co-authored-by: David Müller <david.mueller@codecentric.de>

refactor(core): split test for PayloadClassExtractor and TypeToClassConverter

Co-authored-by: Timon Back <timonback@users.noreply.github.com>

test(kafka): remove unused use-fqn setting

Co-authored-by: David Müller <david.mueller@codecentric.de>

test(kafka): update asyncapi.json

Co-authored-by: David Müller <david.mueller@codecentric.de>

refactor(core): extract TypeToClassConverter

Co-authored-by: David Müller <david.mueller@codecentric.de>

refactor(core): replace pair of resolved schema name and schema object with a specific record

Co-authored-by: Timon Back <timonback@users.noreply.github.com>

feat(core): add NoPayloadUsedConsumer

Co-authored-by: David Müller <david.mueller@codecentric.de>

refactor(core): update AsyncHeadersBuilder (wip)

Co-authored-by: David Müller <david.mueller@codecentric.de>

feat(core): Use new PayloadService (wip)

Co-authored-by: David Müller <david.mueller@codecentric.de>

refactor(core): Check description in Schema annotation in DefaultComponentsService

test(core): Add PayloadServiceTest

refactor(core): Give PayloadService a name

feat(core): Add PayloadNotUsed Schema

Co-authored-by: Timon Back <timonback@users.noreply.github.com>

refactor(core): PayloadClassExtractor returns optional instead of throwing an exception

Co-authored-by: Timon Back <timonback@users.noreply.github.com>

diff --git a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/channel/message/MessagePayload.java b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/channel/message/MessagePayload.java
index 9ccf6e1c..e6302005 100644
--- a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/channel/message/MessagePayload.java
+++ b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/channel/message/MessagePayload.java
@@ -7,10 +7,12 @@ import io.github.springwolf.asyncapi.v3.model.schema.MultiFormatSchema;
 import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
+import lombok.ToString;

 @Getter
 @JsonSerialize(using = MessagePayloadSerializer.class)
 @EqualsAndHashCode
+@ToString
 public class MessagePayload {
     private MultiFormatSchema multiFormatSchema;
     private SchemaObject schema;
diff --git a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/channel/message/MessageReference.java b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/channel/message/MessageReference.java
index b938b4d4..bc6b16b9 100644
--- a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/channel/message/MessageReference.java
+++ b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/channel/message/MessageReference.java
@@ -9,6 +9,7 @@ import lombok.NoArgsConstructor;
 import lombok.ToString;

 @EqualsAndHashCode
+@ToString
 @NoArgsConstructor
 @AllArgsConstructor
 public class MessageReference implements Message, Reference {
diff --git a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaObject.java b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaObject.java
index 710a3ed0..497fb78e 100644
--- a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaObject.java
+++ b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaObject.java
@@ -10,6 +10,7 @@ import lombok.Builder;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.NoArgsConstructor;
+import lombok.ToString;

 import java.math.BigDecimal;
 import java.util.List;
@@ -31,6 +32,7 @@ import java.util.Map;
 @NoArgsConstructor
 @AllArgsConstructor
 @EqualsAndHashCode(callSuper = true)
+@ToString
 public class SchemaObject extends ExtendableObject implements Schema {
     /**
      * Adds support for polymorphism. The discriminator is the schema property name that is used to differentiate
diff --git a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaReference.java b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaReference.java
index 2384a153..848c27fb 100644
--- a/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaReference.java
+++ b/springwolf-asyncapi/src/main/java/io/github/springwolf/asyncapi/v3/model/schema/SchemaReference.java
@@ -9,6 +9,7 @@ import lombok.NoArgsConstructor;
 import lombok.ToString;

 @EqualsAndHashCode
+@ToString
 @NoArgsConstructor
 @AllArgsConstructor
 public class SchemaReference implements Schema, Reference {
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java
index 9c982b3d..f5132257 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/ComponentsService.java
@@ -12,6 +12,8 @@ public interface ComponentsService {

     Map<String, SchemaObject> getSchemas();

+    SchemaObject resolveSchema(String schemaName);
+
     String registerSchema(SchemaObject headers);

     String registerSchema(Class<?> type);
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java
index 59caf4e5..f2e3104c 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/DefaultComponentsService.java
@@ -14,6 +14,7 @@ import io.swagger.v3.core.jackson.TypeNameResolver;
 import io.swagger.v3.oas.models.media.ObjectSchema;
 import io.swagger.v3.oas.models.media.Schema;
 import io.swagger.v3.oas.models.media.StringSchema;
+import jakarta.annotation.Nullable;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;

@@ -60,6 +61,15 @@ public class DefaultComponentsService implements ComponentsService {
                 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
     }

+    @Override
+    @Nullable
+    public SchemaObject resolveSchema(String schemaName) {
+        if (schemas.containsKey(schemaName)) {
+            return swaggerSchemaUtil.mapSchema(schemas.get(schemaName));
+        }
+        return null;
+    }
+
     @Override
     public String registerSchema(SchemaObject headers) {
         log.debug("Registering schema for {}", headers.getTitle());
@@ -81,6 +91,7 @@ public class DefaultComponentsService implements ComponentsService {

     @Override
     public String registerSchema(Class<?> type) {
+        // FIXME: Move this to the new HeadersService
         return this.registerSchema(type, properties.getDocket().getDefaultContentType());
     }

@@ -90,13 +101,13 @@ public class DefaultComponentsService implements ComponentsService {
         String actualContentType =
                 StringUtils.isBlank(contentType) ? properties.getDocket().getDefaultContentType() : contentType;

-        Map<String, Schema> schemas = new LinkedHashMap<>(runWithFqnSetting((unused) -> converter.readAll(type)));
+        Map<String, Schema> newSchemas = new LinkedHashMap<>(runWithFqnSetting((unused) -> converter.readAll(type)));

-        String schemaName = getSchemaName(type, schemas);
+        String schemaName = getSchemaName(type, newSchemas);

-        preProcessSchemas(schemas, schemaName, type);
-        schemas.forEach(this.schemas::putIfAbsent);
-        schemas.values().forEach(schema -> postProcessSchema(schema, actualContentType));
+        preProcessSchemas(newSchemas, schemaName, type);
+        newSchemas.forEach(this.schemas::putIfAbsent);
+        newSchemas.values().forEach(schema -> postProcessSchema(schema, actualContentType));

         return schemaName;
     }
@@ -130,11 +141,22 @@ public class DefaultComponentsService implements ComponentsService {
             return new ArrayList<>(resolvedPayloadModelName).get(0);
         }

-        return type.getSimpleName();
+        return getNameFromClass(type);
     }

     private void preProcessSchemas(Map<String, Schema> schemas, String schemaName, Class<?> type) {
         processAsyncApiPayloadAnnotation(schemas, schemaName, type);
+        processSchemaAnnotation(schemas, schemaName, type);
+    }
+
+    private void processSchemaAnnotation(Map<String, Schema> schemas, String schemaName, Class<?> type) {
+        Schema schemaForType = schemas.get(schemaName);
+        if (schemaForType != null) {
+            var schemaAnnotation = type.getAnnotation(io.swagger.v3.oas.annotations.media.Schema.class);
+            if (schemaAnnotation != null) {
+                schemaForType.setDescription(schemaAnnotation.description());
+            }
+        }
     }

     private void processAsyncApiPayloadAnnotation(Map<String, Schema> schemas, String schemaName, Class<?> type) {
@@ -161,9 +183,9 @@ public class DefaultComponentsService implements ComponentsService {
     }

     private String registerString() {
-        String schemaName = "String";
+        String schemaName = getNameFromClass(String.class);
         StringSchema schema = new StringSchema();
-        schema.setName(String.class.getName());
+        schema.setName(schemaName);

         this.schemas.put(schemaName, schema);
         postProcessSchema(schema, DEFAULT_CONTENT_TYPE);
@@ -181,6 +203,13 @@ public class DefaultComponentsService implements ComponentsService {
         return result;
     }

+    private String getNameFromClass(Class<?> type) {
+        if (properties.isUseFqn()) {
+            return type.getName();
+        }
+        return type.getSimpleName();
+    }
+
     private void postProcessSchema(Schema schema, String contentType) {
         for (SchemasPostProcessor processor : schemaPostProcessors) {
             processor.process(schema, schemas, contentType);
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersBuilder.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersBuilder.java
index 195fcb13..0496125f 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersBuilder.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersBuilder.java
@@ -2,7 +2,8 @@
 package io.github.springwolf.core.asyncapi.components.headers;

 import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.NamedSchemaObject;

 public interface AsyncHeadersBuilder {
-    SchemaObject buildHeaders(Class<?> payloadType);
+    SchemaObject buildHeaders(NamedSchemaObject payloadSchema);
 }
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersNotDocumented.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersNotDocumented.java
index b1548c4e..77b177bf 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersNotDocumented.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersNotDocumented.java
@@ -2,6 +2,7 @@
 package io.github.springwolf.core.asyncapi.components.headers;

 import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.NamedSchemaObject;

 import java.util.List;
 import java.util.Map;
@@ -22,7 +23,7 @@ public class AsyncHeadersNotDocumented implements AsyncHeadersBuilder {
     }

     @Override
-    public SchemaObject buildHeaders(Class<?> payloadType) {
+    public SchemaObject buildHeaders(NamedSchemaObject payloadSchema) {
         return NOT_DOCUMENTED;
     }
 }
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersNotUsed.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersNotUsed.java
index 19ffed26..8b026f07 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersNotUsed.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/headers/AsyncHeadersNotUsed.java
@@ -2,6 +2,7 @@
 package io.github.springwolf.core.asyncapi.components.headers;

 import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.NamedSchemaObject;

 import java.util.List;
 import java.util.Map;
@@ -21,7 +22,7 @@ public class AsyncHeadersNotUsed implements AsyncHeadersBuilder {
     }

     @Override
-    public SchemaObject buildHeaders(Class<?> payloadType) {
+    public SchemaObject buildHeaders(NamedSchemaObject payloadSchema) {
         return NOT_USED;
     }
 }
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/AsyncAnnotationChannelsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/AsyncAnnotationChannelsScanner.java
index 253e1c7c..7c28ddab 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/AsyncAnnotationChannelsScanner.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/AsyncAnnotationChannelsScanner.java
@@ -15,6 +15,7 @@ import io.github.springwolf.core.asyncapi.scanners.bindings.operations.Operation
 import io.github.springwolf.core.asyncapi.scanners.classes.ClassScanner;
 import io.github.springwolf.core.asyncapi.scanners.common.AsyncAnnotationScanner;
 import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
 import io.github.springwolf.core.asyncapi.scanners.common.utils.AsyncAnnotationUtil;
 import io.github.springwolf.core.configuration.docket.AsyncApiDocketService;
 import lombok.extern.slf4j.Slf4j;
@@ -36,11 +37,13 @@ public class AsyncAnnotationChannelsScanner<A extends Annotation> extends AsyncA
             ComponentsService componentsService,
             AsyncApiDocketService asyncApiDocketService,
             PayloadClassExtractor payloadClassExtractor,
+            PayloadService payloadService,
             List<OperationBindingProcessor> operationBindingProcessors,
             List<MessageBindingProcessor> messageBindingProcessors) {
         super(
                 asyncAnnotationProvider,
                 payloadClassExtractor,
+                payloadService,
                 componentsService,
                 operationBindingProcessors,
                 messageBindingProcessors);
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScanner.java
index d86fff29..22d88226 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScanner.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScanner.java
@@ -8,7 +8,7 @@ import io.github.springwolf.core.asyncapi.components.ComponentsService;
 import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersBuilder;
 import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
 import io.github.springwolf.core.asyncapi.scanners.common.ClassLevelAnnotationScanner;
-import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
 import io.github.springwolf.core.asyncapi.scanners.common.utils.AnnotationScannerUtil;
 import lombok.extern.slf4j.Slf4j;

@@ -30,14 +30,14 @@ public class SpringAnnotationClassLevelChannelsScanner<
             Class<MethodAnnotation> methodAnnotationClass,
             BindingFactory<ClassAnnotation> bindingFactory,
             AsyncHeadersBuilder asyncHeadersBuilder,
-            PayloadClassExtractor payloadClassExtractor,
+            PayloadService payloadService,
             ComponentsService componentsService) {
         super(
                 classAnnotationClass,
                 methodAnnotationClass,
                 bindingFactory,
                 asyncHeadersBuilder,
-                payloadClassExtractor,
+                payloadService,
                 componentsService);
     }

diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScanner.java
index c2942147..2321fa21 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScanner.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationMethodLevelChannelsScanner.java
@@ -9,7 +9,8 @@ import io.github.springwolf.core.asyncapi.components.ComponentsService;
 import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersBuilder;
 import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
 import io.github.springwolf.core.asyncapi.scanners.common.MethodLevelAnnotationScanner;
-import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.NamedSchemaObject;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
 import io.github.springwolf.core.asyncapi.scanners.common.utils.AnnotationScannerUtil;
 import lombok.extern.slf4j.Slf4j;

@@ -25,17 +26,17 @@ public class SpringAnnotationMethodLevelChannelsScanner<MethodAnnotation extends
         extends MethodLevelAnnotationScanner<MethodAnnotation> implements SpringAnnotationChannelsScannerDelegator {

     private final Class<MethodAnnotation> methodAnnotationClass;
-    private final PayloadClassExtractor payloadClassExtractor;
+    private final PayloadService payloadService;

     public SpringAnnotationMethodLevelChannelsScanner(
             Class<MethodAnnotation> methodAnnotationClass,
             BindingFactory<MethodAnnotation> bindingFactory,
             AsyncHeadersBuilder asyncHeadersBuilder,
-            PayloadClassExtractor payloadClassExtractor,
+            PayloadService payloadService,
             ComponentsService componentsService) {
         super(bindingFactory, asyncHeadersBuilder, componentsService);
         this.methodAnnotationClass = methodAnnotationClass;
-        this.payloadClassExtractor = payloadClassExtractor;
+        this.payloadService = payloadService;
     }

     @Override
@@ -56,16 +57,15 @@ public class SpringAnnotationMethodLevelChannelsScanner<MethodAnnotation extends

         MethodAnnotation annotation = AnnotationScannerUtil.findAnnotationOrThrow(methodAnnotationClass, method);

+        NamedSchemaObject payloadSchema = payloadService.extractSchema(method);
+        ChannelObject channelItem = buildChannelItem(annotation, payloadSchema);
+
         String channelName = bindingFactory.getChannelName(annotation);
-        Class<?> payload = payloadClassExtractor.extractFrom(method);
-
-        ChannelObject channelItem = buildChannelItem(annotation, payload);
-
         return Map.entry(channelName, channelItem);
     }

-    private ChannelObject buildChannelItem(MethodAnnotation annotation, Class<?> payloadType) {
-        MessageObject message = buildMessage(annotation, payloadType);
+    private ChannelObject buildChannelItem(MethodAnnotation annotation, NamedSchemaObject payloadSchema) {
+        MessageObject message = buildMessage(annotation, payloadSchema);
         return buildChannelItem(annotation, message);
     }

diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/AsyncAnnotationScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/AsyncAnnotationScanner.java
index 0dbba50d..48884d26 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/AsyncAnnotationScanner.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/AsyncAnnotationScanner.java
@@ -17,7 +17,9 @@ import io.github.springwolf.core.asyncapi.annotations.AsyncOperation;
 import io.github.springwolf.core.asyncapi.components.ComponentsService;
 import io.github.springwolf.core.asyncapi.scanners.bindings.messages.MessageBindingProcessor;
 import io.github.springwolf.core.asyncapi.scanners.bindings.operations.OperationBindingProcessor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.NamedSchemaObject;
 import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
 import io.github.springwolf.core.asyncapi.scanners.common.utils.AnnotationScannerUtil;
 import io.github.springwolf.core.asyncapi.scanners.common.utils.AsyncAnnotationUtil;
 import io.github.springwolf.core.asyncapi.scanners.common.utils.TextUtils;
@@ -42,6 +44,7 @@ public abstract class AsyncAnnotationScanner<A extends Annotation> implements Em

     protected final AsyncAnnotationProvider<A> asyncAnnotationProvider;
     protected final PayloadClassExtractor payloadClassExtractor;
+    protected final PayloadService payloadService;
     protected final ComponentsService componentsService;
     protected final List<OperationBindingProcessor> operationBindingProcessors;
     protected final List<MessageBindingProcessor> messageBindingProcessors;
@@ -89,30 +92,33 @@ public abstract class AsyncAnnotationScanner<A extends Annotation> implements Em
     }

     protected MessageObject buildMessage(AsyncOperation operationData, Method method) {
-        Class<?> payloadType = operationData.payloadType() != Object.class
-                ? operationData.payloadType()
-                : payloadClassExtractor.extractFrom(method);
+        NamedSchemaObject payloadSchema = payloadService.extractSchema(operationData, method);

-        String modelName = this.componentsService.registerSchema(
-                payloadType, operationData.message().contentType());
-        SchemaObject asyncHeaders = AsyncAnnotationUtil.getAsyncHeaders(operationData, resolver);
-        String headerModelName = this.componentsService.registerSchema(asyncHeaders);
-        var headers = MessageHeaders.of(MessageReference.toSchema(headerModelName));
-
-        var schema = payloadType.getAnnotation(Schema.class);
-        String description = schema != null ? schema.description() : null;
+        // TODO: move block to own HeaderService
+        SchemaObject headerSchema = AsyncAnnotationUtil.getAsyncHeaders(operationData, resolver);
+        String headerSchemaName = this.componentsService.registerSchema(headerSchema);
+        var headers = MessageHeaders.of(MessageReference.toSchema(headerSchemaName));

         Map<String, MessageBinding> messageBinding =
                 AsyncAnnotationUtil.processMessageBindingFromAnnotation(method, messageBindingProcessors);

         var messagePayload = MessagePayload.of(MultiFormatSchema.builder()
-                .schema(SchemaReference.fromSchema(modelName))
+                .schema(SchemaReference.fromSchema(payloadSchema.name()))
                 .build());

+        String description = operationData.message().description();
+        if (!StringUtils.hasText(description)) {
+            description = payloadSchema.schema().getDescription();
+        }
+        if (StringUtils.hasText(description)) {
+            description = this.resolver.resolveStringValue(description);
+            description = TextUtils.trimIndent(description);
+        }
+
         var builder = MessageObject.builder()
-                .messageId(payloadType.getName())
-                .name(payloadType.getName())
-                .title(payloadType.getSimpleName())
+                .messageId(payloadSchema.name())
+                .name(payloadSchema.name())
+                .title(payloadSchema.schema().getTitle())
                 .description(description)
                 .payload(messagePayload)
                 .headers(headers)
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/ClassLevelAnnotationScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/ClassLevelAnnotationScanner.java
index f814e939..8a7f6507 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/ClassLevelAnnotationScanner.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/ClassLevelAnnotationScanner.java
@@ -11,7 +11,8 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference;
 import io.github.springwolf.core.asyncapi.components.ComponentsService;
 import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersBuilder;
 import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
-import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.NamedSchemaObject;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
 import io.github.springwolf.core.asyncapi.scanners.common.utils.AnnotationScannerUtil;
 import io.github.springwolf.core.asyncapi.scanners.operations.annotations.SpringAnnotationClassLevelOperationsScanner;
 import lombok.RequiredArgsConstructor;
@@ -37,7 +38,7 @@ public abstract class ClassLevelAnnotationScanner<
     protected final Class<MethodAnnotation> methodAnnotationClass;
     protected final BindingFactory<ClassAnnotation> bindingFactory;
     protected final AsyncHeadersBuilder asyncHeadersBuilder;
-    protected final PayloadClassExtractor payloadClassExtractor;
+    protected final PayloadService payloadService;
     protected final ComponentsService componentsService;

     protected enum MessageType {
@@ -67,8 +68,8 @@ public abstract class ClassLevelAnnotationScanner<
             SpringAnnotationClassLevelOperationsScanner.MessageType messageType) {
         Set<MessageObject> messages = methods.stream()
                 .map((Method method) -> {
-                    Class<?> payloadType = payloadClassExtractor.extractFrom(method);
-                    return buildMessage(classAnnotation, payloadType);
+                    NamedSchemaObject payloadSchema = payloadService.extractSchema(method);
+                    return buildMessage(classAnnotation, payloadSchema);
                 })
                 .collect(toSet());

@@ -79,18 +80,19 @@ public abstract class ClassLevelAnnotationScanner<
         return toMessagesMap(messages);
     }

-    protected MessageObject buildMessage(ClassAnnotation classAnnotation, Class<?> payloadType) {
+    protected MessageObject buildMessage(ClassAnnotation classAnnotation, NamedSchemaObject payloadSchema) {
         Map<String, MessageBinding> messageBinding = bindingFactory.buildMessageBinding(classAnnotation);
-        String modelName = componentsService.registerSchema(payloadType);
-        String headerModelName = componentsService.registerSchema(asyncHeadersBuilder.buildHeaders(payloadType));
+
+        String headerModelName = componentsService.registerSchema(asyncHeadersBuilder.buildHeaders(payloadSchema));
+
         MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
-                .schema(SchemaReference.fromSchema(modelName))
+                .schema(SchemaReference.fromSchema(payloadSchema.name()))
                 .build());

         MessageObject message = MessageObject.builder()
-                .messageId(payloadType.getName())
-                .name(payloadType.getName())
-                .title(payloadType.getSimpleName())
+                .messageId(payloadSchema.name())
+                .name(payloadSchema.name())
+                .title(payloadSchema.schema().getTitle())
                 .description(null)
                 .payload(payload)
                 .headers(MessageHeaders.of(MessageReference.toSchema(headerModelName)))
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/MethodLevelAnnotationScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/MethodLevelAnnotationScanner.java
index 1d548122..72f5e823 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/MethodLevelAnnotationScanner.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/MethodLevelAnnotationScanner.java
@@ -11,6 +11,7 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference;
 import io.github.springwolf.core.asyncapi.components.ComponentsService;
 import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersBuilder;
 import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.NamedSchemaObject;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;

@@ -25,18 +26,20 @@ public abstract class MethodLevelAnnotationScanner<MethodAnnotation extends Anno
     protected final AsyncHeadersBuilder asyncHeadersBuilder;
     protected final ComponentsService componentsService;

-    protected MessageObject buildMessage(MethodAnnotation annotation, Class<?> payloadType) {
+    protected MessageObject buildMessage(MethodAnnotation annotation, NamedSchemaObject payloadSchema) {
         Map<String, MessageBinding> messageBinding = bindingFactory.buildMessageBinding(annotation);
-        String modelName = componentsService.registerSchema(payloadType);
-        String headerModelName = componentsService.registerSchema(asyncHeadersBuilder.buildHeaders(payloadType));
+
+        // TODO: move block to own HeaderService
+        String headerModelName = componentsService.registerSchema(asyncHeadersBuilder.buildHeaders(payloadSchema));
+
         MessagePayload payload = MessagePayload.of(MultiFormatSchema.builder()
-                .schema(SchemaReference.fromSchema(modelName))
+                .schema(SchemaReference.fromSchema(payloadSchema.name()))
                 .build());

         MessageObject message = MessageObject.builder()
-                .messageId(payloadType.getName())
-                .name(payloadType.getName())
-                .title(payloadType.getSimpleName())
+                .messageId(payloadSchema.name())
+                .name(payloadSchema.name())
+                .title(payloadSchema.schema().getTitle())
                 .description(null)
                 .payload(payload)
                 .headers(MessageHeaders.of(MessageReference.toSchema(headerModelName)))
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/NamedSchemaObject.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/NamedSchemaObject.java
new file mode 100644
index 00000000..9509becf
--- /dev/null
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/NamedSchemaObject.java
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: Apache-2.0
+package io.github.springwolf.core.asyncapi.scanners.common.payload;
+
+import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
+
+/**
+ * Encapsulates the resolved name for the contained schema.
+ * @param name The fully qualified name or the simple name of the schema.
+ * @param schema The SchemaObject.
+ */
+public record NamedSchemaObject(String name, SchemaObject schema) {}
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadClassExtractor.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadClassExtractor.java
index 0d19c5e6..9e80ef74 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadClassExtractor.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadClassExtractor.java
@@ -1,52 +1,34 @@
 // SPDX-License-Identifier: Apache-2.0
 package io.github.springwolf.core.asyncapi.scanners.common.payload;

-import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.messaging.handler.annotation.Payload;

 import java.lang.annotation.Annotation;
 import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
 import java.util.Arrays;
-import java.util.Map;
+import java.util.Optional;

+@RequiredArgsConstructor
 @Slf4j
 public class PayloadClassExtractor {
     private final TypeToClassConverter typeToClassConverter;

-    public PayloadClassExtractor(SpringwolfConfigProperties properties) {
-        Map<String, Integer> extractableClasses = Map.of();
-        if (properties.getPayload() != null) {
-            extractableClasses = properties.getPayload().getExtractableClasses();
-        }
-        typeToClassConverter = new TypeToClassConverter(extractableClasses);
-    }
-
-    public Class<?> extractFrom(Method method) {
+    public Optional<Class<?>> extractFrom(Method method) {
         String methodName = String.format("%s::%s", method.getDeclaringClass().getSimpleName(), method.getName());
         log.debug("Finding payload type for {}", methodName);

-        int parameterPayloadIndex =
-                getPayloadParameterIndex(method.getParameterTypes(), method.getParameterAnnotations(), methodName);
-
-        return typeToClassConverter.extractClass(method.getGenericParameterTypes()[parameterPayloadIndex]);
+        return getPayloadParameterIndex(method.getParameterTypes(), method.getParameterAnnotations(), methodName)
+                .map((parameterPayloadIndex) ->
+                        typeToClassConverter.extractClass(method.getGenericParameterTypes()[parameterPayloadIndex]));
     }

-    public Class<?> typeToClass(Type type) {
-        return typeToClassConverter.extractClass(type);
-    }
-
-    private int getPayloadParameterIndex(
+    private Optional<Integer> getPayloadParameterIndex(
             Class<?>[] parameterClasses, Annotation[][] parameterAnnotations, String methodName) {
-        switch (parameterClasses.length) {
-            case 0 -> throw new IllegalArgumentException(
-                    "Payload cannot be detected. Method must not have 0 parameters: " + methodName);
-            case 1 -> {
-                return 0;
-            }
+        return switch (parameterClasses.length) {
+            case 0 -> Optional.empty();
+            case 1 -> Optional.of(0);
             default -> {
                 int payloadAnnotatedParameterIndex = getPayloadAnnotatedParameterIndex(parameterAnnotations);
                 if (payloadAnnotatedParameterIndex == -1) {
@@ -57,9 +39,9 @@ public class PayloadClassExtractor {

                     throw new IllegalArgumentException(msg);
                 }
-                return payloadAnnotatedParameterIndex;
+                yield Optional.of(payloadAnnotatedParameterIndex);
             }
-        }
+        };
     }

     private int getPayloadAnnotatedParameterIndex(Annotation[][] parameterAnnotations) {
@@ -74,58 +56,4 @@ public class PayloadClassExtractor {

         return -1;
     }
-
-    @RequiredArgsConstructor
-    private static class TypeToClassConverter {
-
-        private final Map<String, Integer> extractableClassToArgumentIndex;
-
-        private Class<?> extractClass(Type parameterType) {
-            try {
-                if (parameterType instanceof ParameterizedType) {
-                    Type rawParameterType = ((ParameterizedType) parameterType).getRawType();
-                    String rawParameterTypeName = rawParameterType.getTypeName();
-
-                    Class<?> actualPayloadClass =
-                            extractActualGenericClass((ParameterizedType) parameterType, rawParameterTypeName);
-                    if (actualPayloadClass != Void.class) {
-                        return actualPayloadClass;
-                    }
-
-                    // nested generic class - fallback to most outer container
-                    return Class.forName(rawParameterTypeName);
-                }
-
-                // no generics used - just a normal type
-                return Class.forName(parameterType.getTypeName());
-            } catch (Exception ex) {
-                log.info("Unable to extract generic data type of {}", parameterType, ex);
-            }
-            return Void.class;
-        }
-
-        private Class<?> extractActualGenericClass(ParameterizedType parameterType, String rawParameterTypeName) {
-            Type type = parameterType;
-            String typeName = rawParameterTypeName;
-
-            while (type instanceof ParameterizedType && extractableClassToArgumentIndex.containsKey(typeName)) {
-                Integer index = extractableClassToArgumentIndex.get(rawParameterTypeName);
-
-                type = ((ParameterizedType) type).getActualTypeArguments()[index];
-
-                typeName = type.getTypeName();
-                if (type instanceof ParameterizedType) {
-                    typeName = ((ParameterizedType) type).getRawType().getTypeName();
-                }
-            }
-
-            try {
-                return Class.forName(typeName);
-            } catch (ClassNotFoundException ex) {
-                log.debug("Unable to find class for type {}", typeName, ex);
-            }
-
-            return Void.class;
-        }
-    }
 }
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadService.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadService.java
new file mode 100644
index 00000000..69342a90
--- /dev/null
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/PayloadService.java
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: Apache-2.0
+package io.github.springwolf.core.asyncapi.scanners.common.payload;
+
+import io.github.springwolf.asyncapi.v3.model.schema.SchemaObject;
+import io.github.springwolf.core.asyncapi.annotations.AsyncOperation;
+import io.github.springwolf.core.asyncapi.components.ComponentsService;
+import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.Optional;
+
+@Slf4j
+@RequiredArgsConstructor
+public class PayloadService {
+    private final PayloadClassExtractor payloadClassExtractor;
+    private final ComponentsService componentsService;
+    private final SpringwolfConfigProperties properties;
+
+    private static final String PAYLOAD_NOT_USED_KEY = "PayloadNotUsed";
+    static final NamedSchemaObject PAYLOAD_NOT_USED = new NamedSchemaObject(
+            PAYLOAD_NOT_USED_KEY,
+            SchemaObject.builder()
+                    .title(PAYLOAD_NOT_USED_KEY)
+                    .description("No payload specified")
+                    .properties(Map.of())
+                    .build());
+
+    public NamedSchemaObject extractSchema(Method method) {
+        Optional<Class<?>> payloadType = payloadClassExtractor.extractFrom(method);
+
+        String contentType = properties.getDocket().getDefaultContentType();
+        return payloadType.map((type) -> buildSchema(contentType, type)).orElseGet(this::useUnusedPayload);
+    }
+
+    public NamedSchemaObject extractSchema(AsyncOperation operationData, Method method) {
+        Optional<Class<?>> payloadType = operationData.payloadType() != Object.class
+                ? Optional.of(operationData.payloadType())
+                : payloadClassExtractor.extractFrom(method);
+
+        String contentType = operationData.message().contentType();
+        return payloadType.map((type) -> buildSchema(contentType, type)).orElseGet(this::useUnusedPayload);
+    }
+
+    private NamedSchemaObject buildSchema(String contentType, Class<?> payloadType) {
+        String componentsSchemaName = this.componentsService.registerSchema(payloadType, contentType);
+
+        SchemaObject schema = componentsService.resolveSchema(componentsSchemaName);
+        schema.setTitle(payloadType.getSimpleName());
+
+        return new NamedSchemaObject(componentsSchemaName, schema);
+    }
+
+    private NamedSchemaObject useUnusedPayload() {
+        this.componentsService.registerSchema(PAYLOAD_NOT_USED.schema());
+        return PAYLOAD_NOT_USED;
+    }
+}
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/TypeToClassConverter.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/TypeToClassConverter.java
new file mode 100644
index 00000000..c55f8254
--- /dev/null
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/common/payload/TypeToClassConverter.java
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: Apache-2.0
+package io.github.springwolf.core.asyncapi.scanners.common.payload;
+
+import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties;
+import lombok.extern.slf4j.Slf4j;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Map;
+
+@Slf4j
+public class TypeToClassConverter {
+
+    private final Map<String, Integer> extractableClassToArgumentIndex;
+
+    public TypeToClassConverter(SpringwolfConfigProperties properties) {
+        if (properties.getPayload() != null) {
+            extractableClassToArgumentIndex = properties.getPayload().getExtractableClasses();
+        } else {
+            extractableClassToArgumentIndex = Map.of();
+        }
+    }
+
+    public Class<?> extractClass(Type parameterType) {
+        try {
+            if (parameterType instanceof ParameterizedType) {
+                Type rawParameterType = ((ParameterizedType) parameterType).getRawType();
+                String rawParameterTypeName = rawParameterType.getTypeName();
+
+                Class<?> actualPayloadClass =
+                        extractActualGenericClass((ParameterizedType) parameterType, rawParameterTypeName);
+                if (actualPayloadClass != Void.class) {
+                    return actualPayloadClass;
+                }
+
+                // nested generic class - fallback to most outer container
+                return Class.forName(rawParameterTypeName);
+            }
+
+            // no generics used - just a normal type
+            return Class.forName(parameterType.getTypeName());
+        } catch (Exception ex) {
+            log.info("Unable to extract generic data type of %s".formatted(parameterType), ex);
+        }
+        return Void.class;
+    }
+
+    private Class<?> extractActualGenericClass(ParameterizedType parameterType, String rawParameterTypeName) {
+        Type type = parameterType;
+        String typeName = rawParameterTypeName;
+
+        while (type instanceof ParameterizedType && extractableClassToArgumentIndex.containsKey(typeName)) {
+            Integer index = extractableClassToArgumentIndex.get(rawParameterTypeName);
+
+            type = ((ParameterizedType) type).getActualTypeArguments()[index];
+
+            typeName = type.getTypeName();
+            if (type instanceof ParameterizedType) {
+                typeName = ((ParameterizedType) type).getRawType().getTypeName();
+            }
+        }
+
+        try {
+            return Class.forName(typeName);
+        } catch (ClassNotFoundException ex) {
+            log.debug("Unable to find class for type %s".formatted(typeName), ex);
+        }
+
+        return Void.class;
+    }
+}
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationOperationsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationOperationsScanner.java
index 6cee589b..16ac6204 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationOperationsScanner.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/AsyncAnnotationOperationsScanner.java
@@ -10,6 +10,7 @@ import io.github.springwolf.core.asyncapi.scanners.bindings.operations.Operation
 import io.github.springwolf.core.asyncapi.scanners.classes.ClassScanner;
 import io.github.springwolf.core.asyncapi.scanners.common.AsyncAnnotationScanner;
 import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
 import io.github.springwolf.core.asyncapi.scanners.operations.OperationMerger;
 import lombok.extern.slf4j.Slf4j;

@@ -28,11 +29,13 @@ public class AsyncAnnotationOperationsScanner<A extends Annotation> extends Asyn
             ClassScanner classScanner,
             ComponentsService componentsService,
             PayloadClassExtractor payloadClassExtractor,
+            PayloadService payloadService,
             List<OperationBindingProcessor> operationBindingProcessors,
             List<MessageBindingProcessor> messageBindingProcessors) {
         super(
                 asyncAnnotationProvider,
                 payloadClassExtractor,
+                payloadService,
                 componentsService,
                 operationBindingProcessors,
                 messageBindingProcessors);
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScanner.java
index 74b0db0c..81166950 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScanner.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationClassLevelOperationsScanner.java
@@ -10,7 +10,7 @@ import io.github.springwolf.core.asyncapi.components.ComponentsService;
 import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersBuilder;
 import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
 import io.github.springwolf.core.asyncapi.scanners.common.ClassLevelAnnotationScanner;
-import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
 import io.github.springwolf.core.asyncapi.scanners.common.utils.AnnotationScannerUtil;
 import lombok.extern.slf4j.Slf4j;

@@ -32,14 +32,14 @@ public class SpringAnnotationClassLevelOperationsScanner<
             Class<MethodAnnotation> methodAnnotationClass,
             BindingFactory<ClassAnnotation> bindingFactory,
             AsyncHeadersBuilder asyncHeadersBuilder,
-            PayloadClassExtractor payloadClassExtractor,
+            PayloadService payloadService,
             ComponentsService componentsService) {
         super(
                 classAnnotationClass,
                 methodAnnotationClass,
                 bindingFactory,
                 asyncHeadersBuilder,
-                payloadClassExtractor,
+                payloadService,
                 componentsService);
     }

diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScanner.java b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScanner.java
index e222893f..ac5448ac 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScanner.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/scanners/operations/annotations/SpringAnnotationMethodLevelOperationsScanner.java
@@ -11,7 +11,8 @@ import io.github.springwolf.core.asyncapi.components.ComponentsService;
 import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersBuilder;
 import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
 import io.github.springwolf.core.asyncapi.scanners.common.MethodLevelAnnotationScanner;
-import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.NamedSchemaObject;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
 import io.github.springwolf.core.asyncapi.scanners.common.utils.AnnotationScannerUtil;
 import lombok.extern.slf4j.Slf4j;

@@ -28,17 +29,17 @@ public class SpringAnnotationMethodLevelOperationsScanner<MethodAnnotation exten
         extends MethodLevelAnnotationScanner<MethodAnnotation> implements SpringAnnotationOperationsScannerDelegator {

     private final Class<MethodAnnotation> methodAnnotationClass;
-    private final PayloadClassExtractor payloadClassExtractor;
+    private final PayloadService payloadService;

     public SpringAnnotationMethodLevelOperationsScanner(
             Class<MethodAnnotation> methodAnnotationClass,
             BindingFactory<MethodAnnotation> bindingFactory,
             AsyncHeadersBuilder asyncHeadersBuilder,
-            PayloadClassExtractor payloadClassExtractor,
+            PayloadService payloadService,
             ComponentsService componentsService) {
         super(bindingFactory, asyncHeadersBuilder, componentsService);
         this.methodAnnotationClass = methodAnnotationClass;
-        this.payloadClassExtractor = payloadClassExtractor;
+        this.payloadService = payloadService;
     }

     @Override
@@ -61,13 +62,13 @@ public class SpringAnnotationMethodLevelOperationsScanner<MethodAnnotation exten

         String channelName = bindingFactory.getChannelName(annotation);
         String operationId = channelName + "_" + OperationAction.RECEIVE + "_" + method.getName();
-        Class<?> payload = payloadClassExtractor.extractFrom(method);
+        NamedSchemaObject payloadSchema = payloadService.extractSchema(method);

-        Operation operation = buildOperation(annotation, payload);
+        Operation operation = buildOperation(annotation, payloadSchema);
         return Map.entry(operationId, operation);
     }

-    private Operation buildOperation(MethodAnnotation annotation, Class<?> payloadType) {
+    private Operation buildOperation(MethodAnnotation annotation, NamedSchemaObject payloadType) {
         MessageObject message = buildMessage(annotation, payloadType);
         return buildOperation(annotation, message);
     }
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfAutoConfiguration.java b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfAutoConfiguration.java
index b8284b26..2851142c 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfAutoConfiguration.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfAutoConfiguration.java
@@ -28,6 +28,8 @@ import io.github.springwolf.core.asyncapi.operations.OperationsService;
 import io.github.springwolf.core.asyncapi.scanners.ChannelsScanner;
 import io.github.springwolf.core.asyncapi.scanners.OperationsScanner;
 import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.TypeToClassConverter;
 import io.github.springwolf.core.configuration.docket.AsyncApiDocketService;
 import io.github.springwolf.core.configuration.docket.DefaultAsyncApiDocketService;
 import io.github.springwolf.core.configuration.properties.SpringwolfConfigConstants;
@@ -161,7 +163,22 @@ public class SpringwolfAutoConfiguration {

     @Bean
     @ConditionalOnMissingBean
-    public PayloadClassExtractor payloadClassExtractor(SpringwolfConfigProperties springwolfConfigProperties) {
-        return new PayloadClassExtractor(springwolfConfigProperties);
+    public TypeToClassConverter typeToClassConverter(SpringwolfConfigProperties springwolfConfigProperties) {
+        return new TypeToClassConverter(springwolfConfigProperties);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public PayloadClassExtractor payloadClassExtractor(TypeToClassConverter typeToClassConverter) {
+        return new PayloadClassExtractor(typeToClassConverter);
+    }
+
+    @Bean
+    @ConditionalOnMissingBean
+    public PayloadService payloadService(
+            PayloadClassExtractor payloadClassExtractor,
+            ComponentsService componentsService,
+            SpringwolfConfigProperties properties) {
+        return new PayloadService(payloadClassExtractor, componentsService, properties);
     }
 }
diff --git a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfScannerConfiguration.java b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfScannerConfiguration.java
index a0c9e7d8..ab416da7 100644
--- a/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfScannerConfiguration.java
+++ b/springwolf-core/src/main/java/io/github/springwolf/core/configuration/SpringwolfScannerConfiguration.java
@@ -17,6 +17,7 @@ import io.github.springwolf.core.asyncapi.scanners.classes.spring.ComponentClass
 import io.github.springwolf.core.asyncapi.scanners.classes.spring.ConfigurationClassScanner;
 import io.github.springwolf.core.asyncapi.scanners.common.AsyncAnnotationScanner;
 import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
 import io.github.springwolf.core.asyncapi.scanners.operations.annotations.AsyncAnnotationOperationsScanner;
 import io.github.springwolf.core.configuration.docket.AsyncApiDocketService;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -74,6 +75,7 @@ public class SpringwolfScannerConfiguration {
             ComponentsService componentsService,
             AsyncApiDocketService asyncApiDocketService,
             PayloadClassExtractor payloadClassExtractor,
+            PayloadService payloadService,
             List<OperationBindingProcessor> operationBindingProcessors,
             List<MessageBindingProcessor> messageBindingProcessors) {
         return new AsyncAnnotationChannelsScanner<>(
@@ -82,6 +84,7 @@ public class SpringwolfScannerConfiguration {
                 componentsService,
                 asyncApiDocketService,
                 payloadClassExtractor,
+                payloadService,
                 operationBindingProcessors,
                 messageBindingProcessors);
     }
@@ -96,6 +99,7 @@ public class SpringwolfScannerConfiguration {
             SpringwolfClassScanner springwolfClassScanner,
             ComponentsService componentsService,
             PayloadClassExtractor payloadClassExtractor,
+            PayloadService payloadService,
             List<OperationBindingProcessor> operationBindingProcessors,
             List<MessageBindingProcessor> messageBindingProcessors) {
         return new AsyncAnnotationOperationsScanner<>(
@@ -103,6 +107,7 @@ public class SpringwolfScannerConfiguration {
                 springwolfClassScanner,
                 componentsService,
                 payloadClassExtractor,
+                payloadService,
                 operationBindingProcessors,
                 messageBindingProcessors);
     }
@@ -118,6 +123,7 @@ public class SpringwolfScannerConfiguration {
             ComponentsService componentsService,
             AsyncApiDocketService asyncApiDocketService,
             PayloadClassExtractor payloadClassExtractor,
+            PayloadService payloadService,
             List<OperationBindingProcessor> operationBindingProcessors,
             List<MessageBindingProcessor> messageBindingProcessors) {
         return new AsyncAnnotationChannelsScanner<>(
@@ -126,6 +132,7 @@ public class SpringwolfScannerConfiguration {
                 componentsService,
                 asyncApiDocketService,
                 payloadClassExtractor,
+                payloadService,
                 operationBindingProcessors,
                 messageBindingProcessors);
     }
@@ -140,6 +147,7 @@ public class SpringwolfScannerConfiguration {
             SpringwolfClassScanner springwolfClassScanner,
             ComponentsService componentsService,
             PayloadClassExtractor payloadClassExtractor,
+            PayloadService payloadService,
             List<OperationBindingProcessor> operationBindingProcessors,
             List<MessageBindingProcessor> messageBindingProcessors) {
         return new AsyncAnnotationOperationsScanner<>(
@@ -147,6 +155,7 @@ public class SpringwolfScannerConfiguration {
                 springwolfClassScanner,
                 componentsService,
                 payloadClassExtractor,
+                payloadService,
                 operationBindingProcessors,
                 messageBindingProcessors);
     }
diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/AsyncAnnotationChannelsScannerTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/AsyncAnnotationChannelsScannerTest.java
index 39b2002d..7e4bdb5c 100644
--- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/AsyncAnnotationChannelsScannerTest.java
+++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/AsyncAnnotationChannelsScannerTest.java
@@ -28,6 +28,8 @@ import io.github.springwolf.core.asyncapi.scanners.channels.AsyncAnnotationChann
 import io.github.springwolf.core.asyncapi.scanners.classes.ClassScanner;
 import io.github.springwolf.core.asyncapi.scanners.common.AsyncAnnotationScanner;
 import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.TypeToClassConverter;
 import io.github.springwolf.core.configuration.docket.AsyncApiDocket;
 import io.github.springwolf.core.configuration.docket.AsyncApiDocketService;
 import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties;
@@ -85,7 +87,10 @@ class AsyncAnnotationChannelsScannerTest {
             new DefaultComponentsService(emptyList(), emptyList(), swaggerSchemaUtil, properties);

     private final AsyncApiDocketService asyncApiDocketService = mock(AsyncApiDocketService.class);
-    private final PayloadClassExtractor payloadClassExtractor = new PayloadClassExtractor(properties);
+    private final TypeToClassConverter typeToClassConverter = new TypeToClassConverter(properties);
+    private final PayloadClassExtractor payloadClassExtractor = new PayloadClassExtractor(typeToClassConverter);
+    private final PayloadService payloadService =
+            new PayloadService(payloadClassExtractor, componentsService, properties);

     private final List<OperationBindingProcessor> operationBindingProcessors =
             List.of(new TestOperationBindingProcessor());
@@ -99,6 +104,7 @@ class AsyncAnnotationChannelsScannerTest {
             componentsService,
             asyncApiDocketService,
             payloadClassExtractor,
+            payloadService,
             operationBindingProcessors,
             messageBindingProcessors);

diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java
index 11e2dec6..3671c992 100644
--- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java
+++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerIntegrationTest.java
@@ -20,6 +20,8 @@ import io.github.springwolf.core.asyncapi.components.examples.walkers.json.Examp
 import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersNotDocumented;
 import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
 import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.TypeToClassConverter;
 import io.github.springwolf.core.configuration.properties.SpringwolfConfigProperties;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
@@ -48,7 +50,9 @@ import static org.assertj.core.api.Assertions.assertThat;
             SpringAnnotationClassLevelChannelsScannerIntegrationTest.TestBindingFactory.class,
             DefaultComponentsService.class,
             SwaggerSchemaUtil.class,
+            PayloadService.class,
             PayloadClassExtractor.class,
+            TypeToClassConverter.class,
             DefaultSchemaWalker.class,
             SchemaWalkerProvider.class,
             ExampleJsonValueGenerator.class,
@@ -60,7 +64,7 @@ class SpringAnnotationClassLevelChannelsScannerIntegrationTest {
     BindingFactory<TestClassListener> bindingFactory;

     @Autowired
-    PayloadClassExtractor payloadClassExtractor;
+    PayloadService payloadService;

     @Autowired
     ComponentsService componentsService;
@@ -74,7 +78,7 @@ class SpringAnnotationClassLevelChannelsScannerIntegrationTest {
                 TestMethodListener.class,
                 this.bindingFactory,
                 new AsyncHeadersNotDocumented(),
-                payloadClassExtractor,
+                payloadService,
                 componentsService);
     }

diff --git a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerTest.java b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerTest.java
index 9f0ba78b..601cf424 100644
--- a/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerTest.java
+++ b/springwolf-core/src/test/java/io/github/springwolf/core/asyncapi/scanners/channels/annotations/SpringAnnotationClassLevelChannelsScannerTest.java
@@ -18,7 +18,8 @@ import io.github.springwolf.asyncapi.v3.model.schema.SchemaReference;
 import io.github.springwolf.core.asyncapi.components.ComponentsService;
 import io.github.springwolf.core.asyncapi.components.headers.AsyncHeadersNotDocumented;
 import io.github.springwolf.core.asyncapi.scanners.bindings.BindingFactory;
-import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadClassExtractor;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.NamedSchemaObject;
+import io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadService;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import org.junit.jupiter.api.BeforeEach;
@@ -38,7 +39,7 @@ import static org.mockito.Mockito.when;

 class SpringAnnotationClassLevelChannelsScannerTest {

-    private final PayloadClassExtractor payloadClassExtractor = mock(PayloadClassExtractor.class);
+    pri…
Co-authored-by: David Müller <david.mueller@codecentric.de>
timonback and others added 3 commits April 19, 2024 19:08
Co-authored-by: David Müller <david.mueller@codecentric.de>
Co-authored-by: Timon Back <timonback@users.noreply.github.com>
@sam0r040 sam0r040 merged commit baa8627 into springwolf:master Apr 26, 2024
20 checks passed
@sam0r040 sam0r040 deleted the feat/handle-no-payload branch April 26, 2024 13:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

KafkaListener method without any argument throw exception
2 participants