springwolf / springwolf-core

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

SpringWolf UI send the payloadType with the name of the message when the payload is a String and produce a 400 BadRequest #752

Closed pcomp96 closed 3 months ago

pcomp96 commented 4 months ago

Describe the bug

I'm using spring integration with some jms adapter, I created the specification on my listener like in the code example. When i try to publish a message from the SpringWolf UI , the client send this request body:

{
   "payload":"\"string\"",
   "payloadType":"XDataMessagePayload",
   "headers":{
      "x1":"xValue",
      "x2":"xValue"
   }

The payloadType is filled with the attribute name = "XDataMessagePayload" and not with the attribute payloadType = String.class setted in the annoation. This produce a 400 BadRequest and a pubilsh failed error in the UI.


Dependencies and versions used springboot version 3.2.3 springwolf-ui version 1.2.0 springwolf-core version 1.2.0 springwolf-jms version 1.2.0


Code example

Note: If I omit the attribute name="XDataMessagePayload" and have multiple messages with the same payloadType (String.class), in the UI the labels all appear the same as the last processed message.. πŸ‘ŽπŸ»

    @AsyncListener(
            operation = @AsyncOperation(
                    channelName = "queue-x-to-y-data",
                    description = "X send the data to Y over this channel", 
                    payloadType = String.class,
                    message = @AsyncMessage(
                            name = "XDataMessagePayload",
                            messageId = "XDataMessage",
                            description = "Example payload message that X send to Y",
                            title = "X Data to Y Message"
                    ),
                    headers = @Headers(
                            schemaName = "XToYDataHeaders",
                            values = {
                                    @Header(name = "header1", value = "X1", description = "X1 value"),
                                    @Header(name = "header2", value = "X2", description = "X2 value")
                            }
                    )
            ))
    @JmsAsyncOperationBinding
    public IntegrationFlow xDataToYListener(....){
              //Flow logic
             }

    @AsyncListener(
            operation = @AsyncOperation(
                    channelName = "queue-z-to-y-data",
                    description = "Z send the data to Y over this channel", 
                    payloadType = String.class,
                    message = @AsyncMessage(
                            name = "ZDataMessagePayload",
                            messageId = "ZDataMessage",
                            description = "Example payload message that Z send to Y",
                            title = "Z Data to Y Message"
                    ),
                    headers = @Headers(
                            schemaName = "ZToYDataHeaders",
                            values = {
                                    @Header(name = "header1", value = "Z1", description = "Z1 value"),
                                    @Header(name = "header2", value = "Z2", description = "Z2 value")
                            }
                    )
            ))
    @JmsAsyncOperationBinding
    public IntegrationFlow zDataToYListener(....){
              //Flow logic
             }

Stack trace and error logs

On the chrome console side :

Publishing to /springwolf/jms/publish with messageBinding {} and headers {"x1":"2021-07-01T00:00:00Z","x2":"24324234234"}: {"payload":"\"string\"","payloadType":"XDataMessagePayload ","headers":{"x1":"xValue","x2":"xValue"},"bindings":{}}

        POST http://localhost:8080/springwolf/jms/publish?topic=queue-x-to-y-data 400 (Bad Request)

On the backend side with the debug log enabled

INFO 20528 --- [nio-8080-exec-4] i.g.s.c.c.PublishingPayloadCreator       : Specified payloadType XDataMessagePayload is not a registered springwolf schema. Known payloadTypes: [XToYDataHeaders,"ZToYDataHeaders",java.lang.String]
github-actions[bot] commented 4 months ago

Welcome to Springwolf. Thanks a lot for reporting your first issue. Please check out our contributors guide and feel free to join us on discord.

github-actions[bot] commented 4 months 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!

pcomp96 commented 4 months ago

Thank you for the fix, when u think to publish the release 1.3.x @timonback ?

I need to implement springwolf in my project for the docs generation and for testing purpose. ☺️

timonback commented 4 months ago

Hi @pcomp96 , The plan is to release at the end of each month, so in two weeks. Until then, you can use/verify the current snapshot version.

pcomp96 commented 4 months ago

This morning i have tested the 1.3.0-SNAPSHOT version but the error still presented. Maybe i wasn't clear when i explain my issue.

If i remove the name attribute from @AsyncMessage annotation the UI use the payloadType String.class as name of the message and this cause UI issue for the labels and consider both messages as same.

 @AsyncListener(
            operation = @AsyncOperation(
                    channelName = "${queue.in.x-to-me-feedback}",
                    description = "X send the feedback to Me over this channel", // Optional
                    payloadType = String.class,
                    message = @AsyncMessage(
                            name= "XFeedbackMessagePayload",
                            messageId = "XFeedbackMessage",
                            description = "Example payload message that X send to me",
                            title = "X send feedback to Me -  Message"
                    ),
                    headers = @Headers(
                            schemaName = "XToMeFeedbackHeaders",
                            values = {
                                    @Header(name = "headerXFeedback1", value = "1", description = "..."),
                                    @Header(name = "headerXFeedback2", value = "2", description = "...")
                            }
                    )
            )
    )
    @JmsAsyncOperationBinding
public IntegrationFlow jmsListenerXFeedback(...)

@AsyncListener(
            operation = @AsyncOperation(
                    channelName = "${queue.in.x-to-me-data}",
                    description = "X send the data to Me over this channel", 
                    payloadType = String.class,
                    message = @AsyncMessage(
                            name= "XDataMessagePayload",
                            messageId = "XDataMessage",
                            description = "Example payload message that X send to Me",
                            title = "X send data to Me - Message"
                    ),
                    headers = @Headers(
                            schemaName = "XToMeDataHeaders",
                            values = {
                                    @Header(name = "headerXData1", value = "1", description = ""),
                                    @Header(name = "headerXData2", value = "2", description = ""),
                            }
                    )
            )
    )
    @JmsAsyncOperationBinding
    @Bean
    public IntegrationFlow jmsListenerXData(...)

Without name attribute

You can see that i have the same labels on the operation but i have different message declaration on the annotation.

image

With name attribute

This error appear in Java console because send the name of the message and not the payload type...

Specified payloadType XDataMessagePayload is not a registered springwolf schema. Known payloadTypes

timonback commented 4 months ago

Thanks for the update.

Can you share (parts of) the payload? Is it a string in both cases, but you want to have a different documentation for each one of them?

You might be looking for AsyncApiPayload annotation, see https://www.springwolf.dev/docs/configuration/documenting-messages/#primitive-final-and-external-classes So that you have class XDataMessagePayload(){ @AsyncApiPayload private String payload; }

(Havent tested it, there can be a bug in publishing)

As a safety mechanism, you can only publish classes that have been registered as a schema. So, XDataMessagePayload must be used either as part of the method signature or payloadType in the annotation.

pcomp96 commented 4 months ago

The @AsyncApiPayload annotation not solve the problem (i tried it, and the labels issue was there)

I do not want declare n. envelope classes for my n. String Messages(with different docs), honestly speaking, in my opinion is a bad approach.

I want only specific the message description and unique messageid for the @AsyncMessage annotation.

I think that the UI might send the payloadType as a String type in this case and not with the name of the message

UI

The labels are the same and the messages payload and header that the operations sends are the same.

This is a problem on testing side because the headers in the string messages are different.

Question: UI not use the message-id to distinguish the different message?

timonback commented 4 months ago

Thanks for clarifying, I am trying to summarize.

First issue: You want to use the message description and id, but that does not work when using plain string. Second issue: The ui mixes multiple messages together.

Publishing is just to verify, but the ui shows already unexpected data. Just to confirm you have 2 different listener, for two different channels, both with the string payload, however different message ids and descriptions?

Regardless, I will need to properly reproduce this, probably on Friday. You can help, by forking the repo, adapting the jms example https://github.com/springwolf/springwolf-core/blob/master/springwolf-examples/springwolf-jms-example/src/main/java/io/github/springwolf/examples/jms/consumers/ExampleConsumer.java and opening a draft PR (no need for passing tests, etc). I want to make sure that I fully understand your issue and we can resolve it the next release.

pcomp96 commented 4 months ago

First issue: You want to use the message description and id, but that does not work when using plain string. Second issue: The ui mixes multiple messages together.

  1. For the first issue

precisely when i set the name attribute in the @AsyncMessage and the payloadType in @AsyncLis/Pub annotations is setted to String.class, the UI use this value (name attribute) to fill the field payloadType in the request for testing the channel. (It's wrong because not exist a payloadType with this name).

  1. For the second issue instead,

When i set String.class as payloadType and not set the name attribute in @AsyncMessage, the UI show the same messages label for the messages that have different messageId and same payloadType. (It's caused by the name duplicate because the schemaName now is the String.class and so mix the message)

Publishing is just to verify, but the ui shows already unexpected data. Just to confirm you have 2 different listener, for two different channels, both with the string payload, however different message ids and descriptions?

I have 2 listener on 2 different channels, They have a String payload with different messageId,name and description.

Regardless, I will need to properly reproduce this, probably on Friday. You can help, by forking the repo, adapting the jms example https://github.com/springwolf/springwolf-core/blob/master/springwolf-examples/springwolf-jms-example/src/main/java/io/github/springwolf/examples/jms/consumers/ExampleConsumer.java and opening a draft PR (no need for passing tests, etc). I want to make sure that I fully understand your issue and we can resolve it the next release.

I'm not able to make a PR this week because i'm really busy sorry , but i think that my example on the top is enough to reproduce the steps. I paste a skeleton to test

 @AsyncListener(
            operation = @AsyncOperation(
                    channelName = "${queue.in.x-to-me-feedback}",
                    description = "X send the feedback to Me over this channel", // Optional
                    payloadType = String.class,
                    message = @AsyncMessage(
                            name= "XFeedbackMessagePayload",
                            messageId = "XFeedbackMessage",
                            description = "Example payload message that X send to me",
                            title = "X send FEEDBACK to Me -  Message"
                    ),
                    headers = @Headers(
                            schemaName = "XToMeFeedbackHeaders",
                            values = {
                                    @Header(name = "headerFeedbackXToMe1", value = "1", description = "..headerFeedback XToMe1 description.."),
                                    @Header(name = "headerFeedbackXToMe2", value = "2", description = "..headerFeedback XToMe2 description.."),
                            }
                    )
            )
    )
    @JmsAsyncOperationBinding
    @Bean
    public IntegrationFlow xSendFeedbackListenerFlow(!NoPayloadHere){
//Flow logic
}

    @AsyncListener(
            operation = @AsyncOperation(
                    channelName = "${queue.in.x-to-me-data}",
                    description = "X send the data to Me over this channel", // Optional
                    payloadType = String.class,
                    message = @AsyncMessage(
                            name= "XDataMessagePayload",
                            messageId = "XDataMessage",
                            description = "Example payload message that X send to Me",
                            title = "X send DATA to Me - Message"
                    ),
                    headers = @Headers(
                            schemaName = "XToMeDataHeaders",
                            values = {
                                    @Header(name = "headerDataXToMe1", value = "1", description = "..headerData XToMe1 description.."),
                                    @Header(name = "headerDataXToMe2", value = "2", description = "..headerData XToMe2 description.."),
                            }
                    )
            )
    )
    @JmsAsyncOperationBinding
    @Bean
    public IntegrationFlow xSendDataListenerFlow(!NoPayloadHere){
//Flow logic
}
timonback commented 4 months ago

Hi @pcomp96, I used your code to reproduce it in https://github.com/springwolf/springwolf-core/pull/764

https://github.com/springwolf/springwolf-core/pull/762 (show both descriptions, fix publishing with custom AsyncMessage#name) and https://github.com/springwolf/springwolf-core/pull/763 (using the messageId field internally) should address it.

pcomp96 commented 4 months ago

Hi @pcomp96, I used your code to reproduce it in https://github.com/springwolf/springwolf-core/pull/764

https://github.com/springwolf/springwolf-core/pull/762 (show both descriptions, fix publishing with custom AsyncMessage#name) and https://github.com/springwolf/springwolf-core/pull/763 (using the messageId field internally) should address it.

Nice! πŸ’― @timonback How can i test it into my project?

When you merge it into the master branch?

timonback commented 4 months ago

Probably Friday, just tuen on the notifucations on both PRs, then you will get a ping automatically

timonback commented 4 months ago

@pcomp96 The PRs have been merged and are ready as SNAPSHOT

pcomp96 commented 4 months ago

Hi @timonback , I tested some minutes ago the new solution and it works! πŸš€ I'm looking forward to introducing version 1.3.0 into my project to document it πŸ˜„

Thanks again for your effort! I hope I was helpful! It was a pleasure, see you next time πŸ™πŸ» ⏭️

timonback commented 4 months ago

Thank you, great collaboration!

Can we feature your company as an user, see https://github.com/springwolf/springwolf-core/issues/342 ?

github-actions[bot] commented 3 months ago

The change is available in the latest release. πŸŽ‰

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