fastify / fastify-swagger

Swagger documentation generator for Fastify
MIT License
910 stars 200 forks source link

Support OpenAPI Callbacks #727

Open MatanYadaev opened 1 year ago

MatanYadaev commented 1 year ago

Prerequisites

🚀 Feature Proposal

OpenAPI 3 has a concept of callbacks, which means, async responses for an endpoint.

Currently, Fastify Swagger doesn't support this feature.

Motivation

It's part of the OpenAPI 3 specification. Many APIs use this feature.

Example

Defining the route and the schema:

app.post(
    '/start-process/:id',
    {
        schema: {
            summary: 'Start a process',
            operationId: 'startProcess',
            params: {...},
            body: {...},
            response: {...},
            // Callbacks configuration:
            callbacks: {
                onStart: {
                    '{$request.body#/callbackUrl}': {
                        post: {
                            summary: 'Callback when process starts',
                            operationId: 'onStart',
                            requestBody: {
                                type: 'object',
                                properties: {
                                    id: {
                                        type: 'string',
                                    },
                                },
                            },
                            response: {
                                200: {
                                    description: 'Your server received the callback',
                                },
                            },
                        },
                    },
                },
            },
        },
    },
    () => {
        // Endpoint logic
    },
);

Should generate this OpenAPI spec:

openapi: 3.0.3
info:
  title: Fastify Project
  description: Fastify Project
  version: 0.0.0
components:
  schemas: {}
paths:
  "/start-process/{id}":
    post:
      operationId: startProcess
      summary: Start a process
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                callbackUrl:
                  type: string
                  format: uri
      parameters:
        - schema:
            type: string
          in: path
          name: id
          required: true
      responses:
        "200":
          description: Default Response
          content:
            application/json:
              schema:
                type: object
                properties:
                  message:
                    type: string
      // Callbacks:
      callbacks:
        onStart:
          '{$request.body#/callbackUrl}':
            post:
              requestBody:
                content:
                  application/json:
                    schema:
                      type: object
                      properties:
                        status:
                          id: string
              responses:
                "200":
                  description: Your server received the callback
mcollina commented 1 year ago

Thanks for reporting! Would you like to send a Pull Request to address this issue? Remember to add unit tests.

MatanYadaev commented 1 year ago

Hi @mcollina, I tried implementing it and found it a bit challenging. I didn't have enough time to learn and research the project. Could you perhaps suggest a workaround to manually insert callbacks inside the generated OpenAPI Spec? Thanks for your time.

mcollina commented 1 year ago

Have you tried using the transform option?

MatanYadaev commented 1 year ago

@mcollina No, but it will work for me. I am still wondering if there is a better workaround, that is not centralized, for example, make the transform per route, and not in the plugin registration. Something like that:

app.route({
  url: '/users',
  method: 'POST',
  schema: {...},
  transform: (schema) => {
    // Transform here
  },
});
mcollina commented 1 year ago

That would be awesome to implement. You should store that function somewhere in:


app.route({
  url: '/users',
  method: 'POST',
  schema: {...},
  config: {
    swaggerTransform: (schema) => {
      // Transform here
    }
  }
});
MatanYadaev commented 1 year ago

@mcollina I just tried the transform method and it seems to not use the callbacks property I added to the route schema.

mcollina commented 1 year ago

I currently have no bandwidth to investigate.