asyncapi/bindings

Understanding WebSocket API description's query and Chanels Object keys

bali182 opened this issue · 11 comments

Hello, I'm raising this issue to have a better understanding how the ws protocol is described. I'm looking at this example:

https://github.com/asyncapi/spec/blob/master/examples/2.0.0/websocket-gemini.yml

  1. When using websockets, the keys in Channels Object are the URLs to connect to right? So this:
channels:
  /v1/marketdata/{symbol}:

Would mean connect to this URI (where symbol is a parameter), right?:

//                  in servers              in the channel
new WebSocket("wss://api.gemini.com" + "/v1/marketdata/{symbol}")

If we have multiple entries in the Channels Object does that mean we can connect to multiple endpoints with their respective parameters and available subscriptions/publishes?

  1. What is query under ws bindings? Is that something that has to be turned into a query string when establishing the connection? So this:
query:
  type: object
  properties:
    heartbeat:
      type: boolean
      default: false
    top_of_book:
      type: boolean
      default: false
    bids:
      type: boolean
      default: true
    ...

Would also go into the URI?

new WebSocket("wss://api.gemini.com" + "/v1/marketdata/{symbol}?heartbeat=...&top_of_book=...")

In case yes, how to serialize the fields of this object? I saw that it must be an object with properties in the docs, but can the properties objects themselves? If so how deeply nested can this structure go and what would be the serialization method for it? OpenAPI has a nightmarish amount of parameter serialization methods, I hope you went with a simpler approach :D

In case I'm completely wrong please advise!

Hey @bali182

If we have multiple entries in the Channels Object does that mean we can connect to multiple endpoints with their respective parameters and available subscriptions/publishes?

yes, it means you have have multiple connections with the server. Have a look at this example I've build with one of our code generators.

What is query under ws bindings? Is that something that has to be turned into a query string when establishing the connection

exactly. You have few examples in official Gemini docs https://docs.gemini.com/websocket-api/#market-data

but can the properties objects themselves

what do you mean?

If so how deeply nested can this structure go

in the end it describes a list of available query params. The question is how complex can this get. For me it is always flat. This means one level, no nesting. Can be, as I used, object type JSON Schema, but you can go with array too if you think it is better

Hey @derberg thanks for getting back to me! Let me elaborate what I meant by parameter serialization.

OpenAPI describes parameter serialization here: https://swagger.io/docs/specification/serialization/

It basically says parameters can be primitive types (string, number or boolean) or 1 level, non nested objects or arrays.

What I'm asking here is if there is a (possibly nested) object or array schema for the schema of the path or query parameters, for example (first 2 examples look like they are nested but they are not as the root schema replaces the need for a list of parameters):

Non nested object (valid according to OpenAPI spec):

query:
  type: object
  properties:
    heartbeat:
      type: object
      properties:
        foo:
          type: string
        bar:
          type: number

Non nested array (valid according to OpenAPI spec):

query:
  type: object
  properties:
    heartbeat:
      type: array
      items:
        type: string

Nested object (invalid according to OpenAPI spec):

query:
  type: object
  properties:
    heartbeat:
      type: object
      properties:
        foo:
          type: object
          properties:
            ... so on

Nested array (invalid according to OpenAPI spec):

query:
  type: object
  properties:
    heartbeat:
      type: array
      items:
        type: object
        properties:
          foo: 
            type: array
            items: 
              type: string

Is this valid according to AsyncAPI spec, or not, and if it is what's the protocol for serializing and deserializing these in query/path?

Since there is no style option for parameters, like OpenAPI, I assumed there is only one format for each, but for tools its kinda important to know what that format is.

Thank you,
Balazs

At the moment, this specific binding information is described in the WebSocket binding spec -> https://github.com/asyncapi/bindings/tree/master/websockets

So the schema must be of type: object and have properties key. The Gemini example that I provided shows a non nested flat structure. For me the example that you provided is already nested:

query:
  type: object
  properties:
    heartbeat:
      type: object
      properties:
        foo:
          type: string
        bar:
          type: number

it is object in object.

Does it even make sense from technical point of view? I mean, how would the resulting query param look like for such example?


I movie this issue to binding repo as it is discussion about the binding

Welcome to AsyncAPI. Thanks a lot for reporting your first issue. Please check out our contributors guide and the instructions about a basic recommended setup useful for opening a pull request.

Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out this issue.

Cool thank you for moving it!

I mean, how would the resulting query param look like for such example?

That is exactly my question :) JSON Schema allows it. AsyncAPI docs don't say as far as I have seen that you can't do this. So my question is:

A) Is nesting not allowed, and this info missing from the docs?
B) If nesting is allowed are there specs on serialization?

Assume we have the schema you mentioned:

query:
  type: object
  properties:
    heartbeat:
      type: object
      properties:
        foo:
          type: string
        bar:
          type: number

OpenAPI would prescribe serializition of the equivalent like:
style: form, explode: true -> ?foo=someString&bar=1
style: form, explode: false -> ?heartbeat=foo,someString,bar,1
style: deepObject, explode: true -> ?heartbeat[foo]=someString&heartbeat[bar]=1

Its described here: https://swagger.io/docs/specification/serialization/

The purpose of their ParameterObject is to describe this.

However if it would look like this (so nested object in a parameter):

 ...
        foo:
          type: object
          properties:
            foobar: 
               type: string

OpenAPI does not allow this according to spec.

I'm looking for the AsyncAPI equivalent of this doc basically: https://swagger.io/docs/specification/serialization/ if it exists.

Thank you:
Balazs

In other words lets assume the following scenario:

  1. I'm the maintainer of a code generating tool for AsyncAPI.
  2. Someone tries to use an object or array as a path or query parameter.
  3. What do I do?
  • Say it's not allowed according to spec
  • Implement serialization/deserialization for it, but how?

I have to say ?heartbeat[foo]=someString&heartbeat[bar]=1 is interesting to see. Never actually saw it in any API

I would say that better clearly set limitations in the WebSocket binding rather than come up with some complex serialization guidelines really.

We already had a sentence that it must be an object with properties, we just need to add that properties of the object cannot be of type object and array, right?

I think that would definitely clarify things! If the object schema is interpreted as the collection of query params, and it the properties cannot be arrays or objects, serialization becomes pretty simple, as the only thing to do is url encode/decode the key/values and we are done. Thank you!

Does the same apply to the path parameters too? Meaning the schema for a Parameter Object describing a path parameter cannot be an object or array? I think that would be a good idea, as for OpenAPI that's just as messy as the query params.

I think that would definitely clarify things! If the object schema is interpreted as the collection of query params, and it the properties cannot be arrays or objects, serialization becomes pretty simple, as the only thing to do is url encode/decode the key/values and we are done. Thank you!

This is just my opinion though 😄 I just like to keep things simple. Would you open a PR to clarify this? I could engage other maintainers

Does the same apply to the path parameters too? Meaning the schema for a Parameter Object describing a path parameter cannot be an object or array? I think that would be a good idea, as for OpenAPI that's just as messy as the query params.

you mean Parameters in the Channel name? this is different story, recommend separate issue in spec repo. For me, schema for parameter is definitely not array or object as again, it would not make sense

Awesome, thank you, I'll dig into the docs more and come up with a PR, based on my understanding, and then it's easier to critique, if that's how it is/supposed to be or not!

@bali182 perfect, thanks a lot for taking the time to have this discussion