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

2022-02-08-Websockets #2880

Closed
bali182 opened this issue Feb 8, 2022 · 3 comments
Closed

2022-02-08-Websockets #2880

bali182 opened this issue Feb 8, 2022 · 3 comments

Comments

@bali182
Copy link

bali182 commented Feb 8, 2022

As a follow up to #55, I'm adding a proposal for Websocket support. If this is not the right channel to do so, please let me know what's the official way of opening a proposal, as I don't see it in any of the docs, and I don't have the proposals folder when I forked the repo.

Find the filled proposal template below:

@bali182
Copy link
Author

bali182 commented Feb 8, 2022

WebSockets

Metadata

Tag Value
Proposal 2022-02-08-Websockets
Authors Balazs Edes
Review Manager TBD
Status Proposal
Implementations -
Issues #55
Previous Revisions -

Change Log

Date Responsible Party Description
2022-02-08 Balazs Edes First draft created

Introduction

This proposal aims to make WebSocket api descriptions part of OpenAPI.

Motivation

Currently the OpenAPI standard is limited to describing what the http standard allows. More and more applications make use of WebSockets, which is a simple and standard way to build event driven systems, and runs in the most important platform end users see: the browser. Since there is demand for it, I propose to add the ability to describe WebSockets in OpenAPI schemas, as HTTP/WebSocket apis often complement each other.

There are other initiatives to describe event driven messaging (eg.: AsyncAPI), but in terms of maturity I haven't seen anything close to OpenAPI. AsyncAPI is also trying to acomplish a wide array of other goals which delay it's maturity further. Looking at the traction that #55 gained over the years, I think it would be a reasonable goal to add the ability to describe WebSockets in OpenAPI.

Proposed solution

This proposal aims to add a new WebSocket Payload Object type to the spec, and 2 additional fields to the Operation Object, publish and subscribe (better name suggestions are wellcome), that would allow us describing how to connect to a ws server, and what messages can we expect to receive or send.

Example

openapi: '3.3.0'
paths:
  /sample-socket:
    get:
      operationId: 'sampleSocket'
      publish:
        description: 'Short description about the messages you can push towards the server'
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/CreateUserSchema'
                - $ref: '#/components/schemas/DeleteUserSchema'
                - $ref: '#/components/schemas/LikePostSchema'
      subscribe:
        description: 'Short description about the messages you can receive from the server'
        content:
          application/json:
            schema:
              oneOf:
                - $ref: '#/components/schemas/UserCreatedSchema'
                - $ref: '#/components/schemas/UserDeletedSchema'
                - $ref: '#/components/schemas/PostLikedSchema'

Detailed design

The WebSocket spec is currently very simple. From our point of view it allows:

  1. Connecting to an endpoint using a URL
  2. Sending messages in either binary or text format
  3. Receiving messages in either binary or text format

This proposal aims to allow OpenAPI users to describe exactly the above mentioned points:

  1. What URL can I connect to, to talk to this WebSocket?
  2. What's the format and shape of data I'm allowed to send
  3. What's the format and shape of data I can expect to receive

Important: This OpenAPI spec proposal doesn't concern itself with any frameworks, or any higher level abstractions people usually build on top of event driven messaging protocols, like topics/subjects/channels, implied message serialization, etc. It simply describes the above mentioned 3 points, and any further abstractions can be built on top of this, but not part of the core schema.

Extension of the current OpenAPI spec

  • Add a WebSocket Payload Object type (very similar to Request Body Object, except the required field - which makes no sense here). It has the following fields:
    • description: string - A brief description of the payload.
    • content: Map[string, Media Type Object] The content of the payload.
  • Add an optional field in the Components Object type, so payloads can be reused:
    • webSocketPayloads: Map[string, WebSocket Payload Object] - Reuseable payloads
  • Add 2 optional fields on the Operation Object type:
    • publish: Reference Object | WebSocket Payload Object - describes the message(s) the client can publish/push to the server.
    • subscribe: Reference Object | WebSocket Payload Object - describes the message(s) the client can receive from the server.

Simple chat example

openapi: '3.3.0'
components:
  schemas:
    ChatMessage:
      type: 'object'
      required:
        - 'from'
        - 'to'
        - 'message'
      properties:
        from:
          type: 'string'
        to:
          type: 'string'
        message:
          type: 'string'
paths:
  /chat-using-socket:
    get:
      operationId: 'chatSocket'
      publish:
        description: 'You can publish chat messages to this server'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChatMessage'
      subscribe:
        description: 'You can expect chat messages from the server'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserCreatedSchema'

Simple chat, but reusing payloads

openapi: '3.3.0'
components:
  schemas:
    ChatMessage:
      type: 'object'
      required:
        - 'from'
        - 'to'
        - 'message'
      properties:
        from:
          type: 'string'
        to:
          type: 'string'
        message:
          type: 'string'
  webSocketPayloads:
    ChatMessagePayload:
      description: 'A chat message payload'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ChatMessage'
paths:
  /chat-using-socket:
    get:
      operationId: 'chatSocket'
      publish:
        $ref: '#/components/webSocketPayloads/ChatMessagePayload'
      subscribe:
        $ref: '#/components/webSocketPayloads/ChatMessagePayload'

Multiple message types

Note since channels/topics/subjects cannot be expressed we can use a union type with a discriminator to mimic this.

openapi: '3.3.0'
components:
  schemas:
    CreateUserSchema: '...'
    DeleteUserSchema: '...'
    LikePostSchema: '...'
    PublishMessageSchema:
      discriminator:
        propertyName: 'action'
        mapping:
          CreateUser: '#/components/schemas/CreateUserSchema'
          CreateUser: '#/components/schemas/CreateUserSchema'
          LikePost: '#/components/schemas/LikePostSchema'
      oneOf:
        - $ref: '#/components/schemas/CreateUserSchema'
        - $ref: '#/components/schemas/DeleteUserSchema'
        - $ref: '#/components/schemas/LikePostSchema'
    UserCreatedSchema: '...'
    UserDeletedSchema: '...'
    PostLikedSchema: '...'
    SubscribeMessageSchema:
      discriminator:
        propertyName: 'type'
        mapping:
          UserCreated: '#/components/schemas/UserCreatedSchema'
          UserDeleted: '#/components/schemas/UserDeletedSchema'
          PostLiked: '#/components/schemas/PostLikedSchema'
      oneOf:
        - $ref: '#/components/schemas/UserCreatedSchema'
        - $ref: '#/components/schemas/UserDeletedSchema'
        - $ref: '#/components/schemas/PostLikedSchema'
paths:
  /sample-socket:
    get:
      operationId: 'sampleSocket'
      publish:
        description: 'Short description about the messages you can push towards the server'
        content:
          application/json:
            schema:
              - $ref: '#/components/schemas/PublishMessageSchema'
      subscribe:
        description: 'Short description about the messages you can receive from the server'
        content:
          application/json:
            schema:
              - $ref: '#/components/schemas/SubscribeMessageSchema'

Backwards compatibility

As this change is additive (nothing is removed or modified in a non-backward compatible way) I don't see any backward compatibilty issues.

Alternatives considered

There can be many alternatives, my motivation was to add support for WebSocket api descriptions, while

  • Adding as few new concepts as possible
  • Allowing reuse the same way as with all other parts of the schema, through $refs
  • Just describing what the standard allows (no frameworks)
  • Not causing any breaking changes or backward compatibility issues
  • I'm happy to take onboard any suggestions, alternatives, modifications to this! I'm sure there are many things I haven't thought of.

@darrelmiller
Copy link
Member

The proposals folder is here https://github.com/OAI/OpenAPI-Specification/tree/main/proposals

Personally, I don't see value is overlapping with AsyncAPI. It will just cause customer confusion and make the lives of tooling developers hard.

@bali182
Copy link
Author

bali182 commented Feb 10, 2022

@darrelmiller thanks, I must have mixed up the repos. Opened #2881 instead.

In terms of overlapping, they will overlap, no matter what OpenAPI does, considering the AsyncAPI goals / roadmap. My aim with this proposal is to add this highly desired feature without mixing in a whole jungle of frameworks and features, and create a well documented, framework independent spec for this web technology as well.

@bali182 bali182 closed this as completed Feb 10, 2022
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

No branches or pull requests

2 participants