mocks-server / main

Node.js mock server running live, interactive mocks in place of real APIs
https://www.mocks-server.org
Apache License 2.0
290 stars 17 forks source link

OpenAPI spec callbacks #435

Open jayvdb opened 2 years ago

jayvdb commented 2 years ago

Is your feature request related to a problem? Please describe. OpenAPI callbacks define outgoing async requests, typically back to the sender. docs: https://swagger.io/docs/specification/callbacks/ spec: https://spec.openapis.org/oas/v3.0.2#callbackObject

Describe the solution you'd like https://blog.stoplight.io/mocking-callbacks-openapi-prism shows how these callbacks are utilised by https://github.com/stoplightio/prism

It would be great if the OpenAPI support included support for these in the same manner.

Describe alternatives you've considered I havent determined whether the mock server config json can support this. No doubt it would be possible with a custom plugin, but using prism is easier.

Additional context

javierbrea commented 2 years ago

Hi @jayvdb ,

Thanks for the suggestion, it's a great idea to support OpenAPI callbacks. I only have a doubt about when to trigger registered callbacks. I have read the Stoplight article and it seems that in Prism, callbacks are executed just in the moment that the subscriber url is requested. That is a good first approach, but, talking about async callbacks, it would be great if we also provide a mechanism to trigger callbacks at any other moment.

It needs to be investigated further, but, as a draft, maybe async callbacks could be natively supported by the server, by adding a callbacks property to routes, for example. Then, a method could be available in the APIs (Javascript and REST) for triggering the callbacks at any other moment.

{
    id: "subscribe-to-notifications",
    url: "/api/notifications/subscribe",
    method: "POST",
    variants: [
      {
        id: "success",
        type: "status",
        options: {
          status: 201,
        },
        events: [
           {
               callback: "notification", // ID of the callback to be executed
               delay: 3000, // Wait this time before requesting the callback url when the variant is executed
           }
        ]
      }
    ],
    callbacks: [
           {
               id: "notification", // Event name
               url: "{$request.body#/url}?token={$request.body#/token}", // Callback URL (should support templating using JSON paths)
              method: "post",
              body: { // Body to send when requesting the callback URL. (should support templating using JSON paths)
                     message: "Operation completed",
                     name: "{$request.body#/name}"
               }
           }
        ]
  }

In the example above, the webhook would be triggered 3 seconds after a POST request is performed to "/api/notifications/subscribe". The callback URL would be built using the url property received in the request body, and it would send as body an object containing a "message" property, and a "name" property equals to the "name" property received in the request body.

The callback could be triggered also at any moment using the JavaScript API (this example is based on the oncoming API, which exposes routes):

core.mock.routes.get("subscribe-to-notifications").callbacks.get("notification").send({
   url: "http://127.0.0.1:4000/receive-notifications",
   name: "foo"
})

Or even using the REST API:

curl -v -H 'Content-type: application/json' -d '{ "url":"http://127.0.0.1:4000/receive-notifications", "name": "foo-name" }' http://127.0.0.1:3110/api/mock/routes/subscribe-to-notifications/callbacks/notification/send

Once this feature is natively supported, it would be "easy" to convert the OpenAPI callbacks spec into corresponding Mocks Server route callbacks.

I also like this draft because the events property added to route variants could be also useful to trigger another type of events, such as WebSocket messages, for example (related to #428), and it also introduces the concept of templating in the routes properties (related to #411 )

jayvdb commented 2 years ago

In an ideal world, it would be possible to define a delay for the callbacks, which is easy enough to do in the native config and APIs, and could be embedded in the openapi spec in a non-standardised way - the callback name seems like the right level, e.g. callback_1_delay_3s, or the summary field for the operation e.g. ... (delay 3s). A benefit of this is that then multiple callbacks can be described that are separated by increasing delay, emulating a real workload occurring and emitting different status messages to the callback url, e.g. to indicate progress.

javierbrea commented 2 years ago

In an ideal world, it would be possible to define a delay for the callbacks, which is easy enough to do in the native config and APIs, and could be embedded in the openapi spec in a non-standardised way - the callback name seems like the right level, e.g. callback_1_delay_3s, or the summary field for the operation e.g. ... (delay 3s). A benefit of this is that then multiple callbacks can be described that are separated by increasing delay, emulating a real workload occurring and emitting different status messages to the callback url, e.g. to indicate progress.

Of course, that's why I added the "delay" property to the variant.events :wink:. About where to get the delay in the OpenAPI spec, maybe it could be defined in a custom "x-mocks-server-delay" property, which is the strategy chosen for other properties when converting OpenAPI specs into routes.

manojvignesh commented 2 months ago

@javierbrea Any update on this?

Also are there any examples where we can achieve this using middlewares?