Springwolf UI - Error parsing AsyncAPI message of channel io.eventuate.tram.spring.springwolf.events.Customer: Cannot read properties of undefined (reading '$ref')
My service generates this Async API doc: https://gist.github.com/cer/ca1f729dbbc40ee417616d9e3dcefd59
https://studio.asyncapi.com/ says that it is valid.
But Springwolf UI displays the error
Error parsing AsyncAPI message of channel io.eventuate.tram.spring.springwolf.events.Customer: Cannot read properties of undefined (reading '$ref')
No operations are displayed.
I did notice, btw, that Springwolf UI requires channels to have an address property - otherwise the error message shows undefined as the channel name.
Suggestions?
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.
It seems that the UI requires messages to have a "headers" property. I've added that (see updated gist). I'm now seeing these errors in the console:
It seems that the UI requires messages to have a name and/or title property.
Hi @cer, Thank you for the report and versioned gists.
In the latest revision (2), springwolf-ui renders the messages and operations, only the binding is missing. You probably want to add one of binding annotations, see: https://www.springwolf.dev/docs/configuration/documenting-bindings/
Indeed, name, title and headers are mandatory fields in springwolf-ui at this point 1.
Did you generate the AsyncAPI using Springwolf (i.e. springwolf-kafka) or use springwolf-ui only 2?
@timonback Yes revision (2) worked. I will work on adding bindings.
The AsyncAPI doc is generated by an Eventuate-specific SpringWolf plugin - https://github.com/eventuate-platform/eventuate-tram-spring-wolf-support/
Thanks for the link to https://github.com/springwolf/springwolf-core/blob/master/springwolf-ui/src/app/models/message.model.ts - the challenge is that the UI is generating difficult to debug errors on AsyncAPI docs that are valid according to asyncapi validate
Here's a different doc - https://gist.github.com/cer/cba0099872915d8ec16fd89d0c7209d0 It uses request/reply.
SpringWolf UI seems require the reply channel to have an address property: https://gist.github.com/cer/cba0099872915d8ec16fd89d0c7209d0#file-request-reply-json-L29
AFAIK this contradicts the specification for operation reply objects
When address is specified, the address property of the channel referenced by this property MUST be either null or not defined.
Thoughts?
Hi @cer, Great to see you building your custom plugins!
We started looking into how to validate the provided AsyncAPI files in springwolf-ui (https://github.com/springwolf/springwolf-core/pull/1219), so that better error messages are being displayed in the browser console as INFO. Currently we use a strict mode so that also additional properties that are no issue appear in the console.
We are about to merge a preview version as -SNAPSHOT so that you can continue testing. We will come back to improve it.
Regarding your second question of address in channels for request/reply semantics:
Stomp (WebSocket) uses the reply object: https://github.com/springwolf/springwolf-core/blob/ee447fbf9f8f7b4ad27053ae8c2c21200f8a19d6/springwolf-examples/springwolf-stomp-example/src/test/resources/asyncapi.json#L266-L275
We address your concern by writing the address always to the channel object and do not specify it as part of the reply object. Will that work for you too?
Regarding your second question of
addressinchannelsfor request/reply semantics: ... We address your concern by writing the address always to the channel object and do not specify it as part of the reply object. Will that work for you too?
I'm a bit confused about what you are asking. In my scenario, the actual reply channel is specified by a header in the request message.
The spec says that this is specified using the reply.address.location property:
"operations": {
"io.eventuate.tram.spring.springwolf.commands.requestasyncresponse.CustomerCommandHandler.reserveCredit": {
"action": "receive",
....
"reply": {
"address": {
"location": "$message.header#/command_reply_to"
},
"channel": {
"$ref": "#/channels/io.eventuate.tram.spring.springwolf.commands.requestasyncresponse.CustomerCommandHandler.reserveCredit-reply"
},
Moreover, the specification for operation reply objects says:
When address is specified, the address property of the channel referenced by this property MUST be either null or not defined.
In other words, the reply channel's address property that the UI requires is not compliant:
"channels": {
...
"io.eventuate.tram.spring.springwolf.commands.requestasyncresponse.CustomerCommandHandler.reserveCredit-reply": {
"address": "io.eventuate.tram.spring.springwolf.commands.requestasyncresponse.CustomerCommandHandler.reserveCredit-replyfoo",
"messages": {
Please clarify.
PS. I'm a bit confused by the fact that asyncapi validate doesn't catch these errors.
Hi @cer, I plan to read the AsyncAPI spec in more detail on Friday, but want to give you a quick response now.
Springwolf currently covers request/reply semantics for fixed channel names. My personal impression is that the AsyncAPI spec allows to describe the same thing in multiple different APIs. For the Springwolf use-case, we could use the reply.address fields but have opted for leaving the reply.address empty and instead use the address in the channel.$ref.
In my scenario, the actual reply channel is specified by a header in the request message.
Since you use a runtime expression, the current Springwolf approach might not work (haven't read the spec in depth).
In other words, the reply channel's address property that the [Springwolf] UI requires is not compliant:
Before we did not encounter this case, however I agree that springwolf-ui cannot rely on every channel having an address.
This begs the questions, whether this reply-only channel without address should show up in springwolf-ui's navigation (when the actual address is unclear / dependent on the request). Possibly it is enough when it is being shown within the request channel.
PS. I'm a bit confused by the fact that asyncapi validate doesn't catch these errors.
The spec is specified using json-schema and some spectral rules. My personal impression is that it covers the structure of the document, but not everything (semantical). AsyncAPI studio/validate does show some errors in invalid specifications, but not all.
Is it fair to re-word the current bug description to the following or is there something else missing?
springwolf-ui is unable to render AsyncAPI compliant documents, which use a runtime expression in channel.location within reply operation objects.
This need is expressed by a custom plugin, see https://github.com/eventuate-platform/eventuate-tram-spring-wolf-support/
Is it fair to re-word the current bug description to the following or is there something else missing?
I think this is about right.
However, there is an issue I reported earlier about the UI requiring message name/title/headers: https://github.com/springwolf/springwolf-core/issues/1216#issuecomment-2705304938
As far as I can tell the spec says they are optional - perhaps the UI should not require them.
Hi @cer, we included a small improvement https://github.com/springwolf/springwolf-core/issues/1216 in todays release: 1.12.0.
springwolf-ui is unable to render AsyncAPI compliant documents, which use a runtime expression in channel.location within reply operation objects.
The original issue is not resolved yet. My time will be limited the next month. Maybe you or @sam0r040 has a good idea on how to resolve this in a simple way.