springwolf / springwolf-core

Automated documentation for event-driven applications built with Spring Boot
https://www.springwolf.dev
Apache License 2.0
245 stars 69 forks source link

Build example for schema: error with SpringWolf 1.4.0, but working fine with 1.2.0 #829

Closed pdalfarr closed 1 week ago

pdalfarr commented 2 months ago

Describe the bug Schema generation was working fine with SpringWolf 1.2.0. I changed version from 1.2.0 to 1.4.0: as a result, my Spring app is working fine, but Springwolf does not work. See log message and stack trace below.

Dependencies and versions used SpringWolf 1.2.0 :

    <!-- Springwolf -->
    <dependency>
        <groupId>io.github.springwolf</groupId>
        <artifactId>springwolf-core</artifactId>
        <version>${version.springwolf}</version>
    </dependency>
    <dependency>
        <groupId>io.github.springwolf</groupId>
        <artifactId>springwolf-amqp</artifactId>
        <version>${version.springwolf}</version>
    </dependency>
    <dependency>
        <groupId>io.github.springwolf</groupId>
        <artifactId>springwolf-ui</artifactId>
        <version>${version.springwolf}</version>
    </dependency>
    <!-- Springwolf end -->

SpringWolf 1.4.0: same as above plus:

        <dependency>
            <groupId>io.github.springwolf</groupId>
            <artifactId>springwolf-amqp-binding</artifactId>
            <version>${version.springwolf}</version>
        </dependency>

Code example No code example

Stack trace and error logs

I see multiple log messages like this :

2024-07-02 10:15:48.297  INFO [rapport-controle,,] 50188 --- [           main] i.g.s.c.a.c.e.w.DefaultSchemaWalker      : Failed to build example for schema: There is no name set for Schema: class StringSchema {
    class Schema {
        type: string
        format: null
        $ref: null
        description: null
        title: null
        multipleOf: null
        maximum: null
        exclusiveMaximum: null
        minimum: null
        exclusiveMinimum: null
        maxLength: null
        minLength: null
        pattern: null
        maxItems: null
        minItems: null
        uniqueItems: null
        maxProperties: null
        minProperties: null
        required: null
        not: null
        properties: null
        additionalProperties: null
        nullable: null
        readOnly: null
        writeOnly: null
        example: null
        externalDocs: null
        deprecated: null
        discriminator: null
        xml: null
    }
}

io.github.springwolf.core.asyncapi.components.examples.walkers.SchemaWalker$ExampleGeneratingException: There is no name set for Schema: class StringSchema {
    class Schema {
        type: string
        format: null
        $ref: null
        description: null
        title: null
        multipleOf: null
        maximum: null
        exclusiveMaximum: null
        minimum: null
        exclusiveMinimum: null
        maxLength: null
        minLength: null
        pattern: null
        maxItems: null
        minItems: null
        uniqueItems: null
        maxProperties: null
        minProperties: null
        required: null
        not: null
        properties: null
        additionalProperties: null
        nullable: null
        readOnly: null
        writeOnly: null
        example: null
        externalDocs: null
        deprecated: null
        discriminator: null
        xml: null
    }
}
    at io.github.springwolf.core.asyncapi.components.examples.walkers.DefaultSchemaWalker.lambda$fromSchema$0(DefaultSchemaWalker.java:60) ~[springwolf-core-1.4.0.jar:na]
    at java.base/java.util.Optional.orElseThrow(Optional.java:403) ~[na:na]
    at io.github.springwolf.core.asyncapi.components.examples.walkers.DefaultSchemaWalker.fromSchema(DefaultSchemaWalker.java:59) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.components.postprocessors.ExampleGeneratorPostProcessor.process(ExampleGeneratorPostProcessor.java:26) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService.postProcessSchema(SwaggerSchemaService.java:210) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.schemas.SwaggerSchemaService.extractSchema(SwaggerSchemaService.java:89) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.components.DefaultComponentsService.registerSchema(DefaultComponentsService.java:51) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadService.buildSchema(PayloadService.java:35) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.scanners.common.payload.internal.PayloadService.buildSchema(PayloadService.java:31) ~[springwolf-core-1.4.0.jar:na]
    at java.base/java.util.Optional.map(Optional.java:260) ~[na:na]
    at io.github.springwolf.core.asyncapi.scanners.common.payload.PayloadMethodParameterService.extractSchema(PayloadMethodParameterService.java:19) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.scanners.operations.annotations.SpringAnnotationMethodLevelOperationsScanner.mapMethodToOperation(SpringAnnotationMethodLevelOperationsScanner.java:77) ~[springwolf-core-1.4.0.jar:na]
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) ~[na:na]
    at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) ~[na:na]
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) ~[na:na]
    at java.base/java.util.HashMap$KeySpliterator.forEachRemaining(HashMap.java:1707) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575) ~[na:na]
    at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622) ~[na:na]
    at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627) ~[na:na]
    at io.github.springwolf.core.asyncapi.scanners.operations.SpringAnnotationOperationsScanner.mapToOperations(SpringAnnotationOperationsScanner.java:31) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.scanners.operations.SpringAnnotationOperationsScanner.scan(SpringAnnotationOperationsScanner.java:25) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.operations.DefaultOperationsService.findOperations(DefaultOperationsService.java:34) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.DefaultAsyncApiService.initAsyncAPI(DefaultAsyncApiService.java:71) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.asyncapi.DefaultAsyncApiService.getAsyncAPI(DefaultAsyncApiService.java:42) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.SpringwolfInitApplicationListener.onApplicationEvent(SpringwolfInitApplicationListener.java:31) ~[springwolf-core-1.4.0.jar:na]
    at io.github.springwolf.core.SpringwolfInitApplicationListener.onApplicationEvent(SpringwolfInitApplicationListener.java:17) ~[springwolf-core-1.4.0.jar:na]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:178) ~[spring-context-5.3.31.jar:5.3.31]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:171) ~[spring-context-5.3.31.jar:5.3.31]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:145) ~[spring-context-5.3.31.jar:5.3.31]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:429) ~[spring-context-5.3.31.jar:5.3.31]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:386) ~[spring-context-5.3.31.jar:5.3.31]
    at org.springframework.boot.context.event.EventPublishingRunListener.ready(EventPublishingRunListener.java:114) ~[spring-boot-2.7.18.jar:2.7.18]
    at org.springframework.boot.SpringApplicationRunListeners.lambda$ready$6(SpringApplicationRunListeners.java:82) ~[spring-boot-2.7.18.jar:2.7.18]
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) ~[na:na]
    at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:120) ~[spring-boot-2.7.18.jar:2.7.18]
    at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:114) ~[spring-boot-2.7.18.jar:2.7.18]
    at org.springframework.boot.SpringApplicationRunListeners.ready(SpringApplicationRunListeners.java:82) ~[spring-boot-2.7.18.jar:2.7.18]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:323) ~[spring-boot-2.7.18.jar:2.7.18]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1300) ~[spring-boot-2.7.18.jar:2.7.18]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1289) ~[spring-boot-2.7.18.jar:2.7.18]
sam0r040 commented 2 months ago

Hi @pdalfarr, thank you for the report. For us it is difficult to reproduce this without the class for the schema. Please enable debug mode to finde the method that includes the problematic payload and share the code for the payload with us.

ruskaof commented 1 month ago

Hello, @sam0r040 I've found code that can generate the problematic payload:

public record MyRootObject(
    MyEnumObject myEnumObject
) {
}
@Schema(enumAsRef = true)
public enum MyEnumObject {
    VALUE1,
    VALUE2
}
@Component
public class Producer {
    @AsyncPublisher(operation = @AsyncOperation(channelName = "mychannel"))
    public void produce(MyRootObject message) {
    }
}

The problem is that schema name for MyEnumObject is resolved to null and thus DefaultShemaWalker throws an exception here https://github.com/springwolf/springwolf-core/blob/master/springwolf-core/src/main/java/io/github/springwolf/core/asyncapi/components/examples/walkers/DefaultSchemaWalker.java#L61

Also, if you remove the wrapper class MyRootObject and return MyEnumObject itself in the produce() method no exception is thrown but no example for MyEnumObject is generated.

timonback commented 1 month ago

Hi @pdalfarr , @ruskaof, I reproduced it with your code in https://github.com/springwolf/springwolf-core/pull/887

I do see the (unexpected) exception: There is no name set... MyRootObject does have a valid example, MyEnumObject is missing one.

Do you expect an example for MyEnumObject and/or no exception? Will keep looking into it.

ruskaof commented 1 month ago

I think that there should not be an exception thrown because schema name should be generated from a class name if it is not defined explicitly in the @Schema annotation. Also, it would be nice to have the enum class with its values referenced in the Schemas section of the UI. Something like springdoc generates with REST endpoints: image

github-actions[bot] commented 1 week ago

The change is staged for release and will be part of the next release.

If you want to try and verify it in your application today, use the latest 1.X.0-SNAPSHOT build as described in our README.md > Testing SNAPSHOT version

Thank you for the report/contribution!

github-actions[bot] commented 1 week ago

The change is available in the latest release. 🎉

Thank you for the report/contribution and making Springwolf better!