asyncapi / spec

The AsyncAPI specification allows you to create machine-readable definitions of your asynchronous APIs.
https://www.asyncapi.com
Apache License 2.0
4.3k stars 273 forks source link

Discriminator support to be aligned with OAS3 #1073

Open francocm opened 1 month ago

francocm commented 1 month ago

Context

The overlap in specification between AsyncAPI and OpenAPI specs allows the combining of common models across both.

Example:

./models/foo.yaml ```yaml openapi: 3.1.0 info: title: foo-models description: Models for Foo contact: name: Foo Team version: 1.0.0 paths: { } components: schemas: Foo: type: object properties: id: type: string ```
./apis/foo.openapi.ayml ```yaml openapi: "3.1.0" info: title: "foo" description: "API Specifications for `foo`." version: "1.0.0" servers: - url: http://localhost:8080/ description: Local Dev paths: /foo: get: summary: "Get Foos" description: "Get Foos" operationId: "getFoos" responses: "200": description: "OK" content: 'application/json': schema: type: array items: $ref: "../models/foo.yaml#/components/schemas/Foo" ```
./events/foo.asyncapi.ayml ```yaml asyncapi: 3.0.0 info: title: foo Events version: 1.0.0 servers: foo-local: host: localhost:1234 protocol: foo description: Foo broker (local dev) security: - $ref: '#/components/securitySchemes/plainSecurity' defaultContentType: application/json channels: FooEvents: address: foo.events description: The topic on which foo events are published on. messages: fooQueriedEvent: $ref: '#/components/messages/fooQueriedEvent' operations: 'foo.queried': action: send channel: $ref: '#/channels/FooEvents' messages: - $ref: '#/channels/FooEvents/messages/fooQueriedEvent' components: messages: fooQueriedEvent: name: fooQueriedEvent title: Foo Queried Event summary: Foo Queried Event description: Foo Queried Event contentType: application/json payload: $ref: '../models/foo.yaml#/components/schemas/Foo' securitySchemes: plainSecurity: type: plain description: Plain unauthenticated connection ```

Problem

It's quite evident that AsyncAPI in general has no compatibility issues with OpenAPI specifications.

However, the problem lies with the use of discriminator in a oneOf situation.

If I update ./models/foo.yaml to this:

openapi: 3.1.0
info:
  title: foo-models
  description: Models for Foo
  contact:
    name: Foo Team
  version: 1.0.0
paths: { }
components:
  schemas:
    Foo:
      type: object
      properties:
        id:
          type: string
        bazAttributes:
          $ref: '#/components/schemas/OneOfBazAttributes'
    OneOfBazAttributes:
      type: object
      properties:
        type:
          type: string
          enum: [ 'RealBaz', 'MockBaz' ]
      discriminator:
        propertyName: type
        mapping:
          RealBaz: '#/components/schemas/RealBazAttributes'
          MockBaz: '#/components/schemas/MockBazAttributes'
    RealBazAttributes:
      allOf:
        - $ref: '#/components/schemas/OneOfBazAttributes'
      type: object
      properties:
        bazId:
          type: string
    MockBazAttributes:
      allOf:
        - $ref: '#/components/schemas/OneOfBazAttributes'
      type: object
      properties:
        xyzId:
          type: string
Output Result
OpenAPI Works as expected
AsyncAPI Fails with "discriminator" property type must be string channels.FooEvents.messages.fooQueriedEvent.payload.properties.bazAttributes.discriminator

This is using the official asyncapi CLI binary provided by asyncapi/cli:2.6.0 docker image, but also any other AsyncAPI tool I could find.

Root Cause

From what I can understand, AsyncAPI implements discriminator using the schema that OpenAPI 2.x (Swagger) had, i.e.:

OneOfBazAttributes:
  type: object
  properties:
    type: 
      type: string
      enum: [ 'RealBazAttributes', 'MockBazAttributes' ]
  discriminator: type
# with the expectation of having the value of `type` matching
# the object name exactly as defined in the YAML

(although the AsyncAPI render does not render any oneOf options, but all validations now pass successfully).

OpenAPI moved to a newer schema in 3.x, and whilst AsyncAPI does not seem to have problems with the OpenAPI 3.x schema compatibility, the specific discriminator capability seems to still be stuck in the 2.x world.

As it is, I do not see any clean way to be able to support discriminator and both OpenAPI and AsyncAPI simultaneously, without fixing this issue, or rolling everything back to 2.x.

Proposal

Align the AsyncAPI spec around discriminator to that of OpenAPI 3.1.x, and release it as AsyncAPI 3.1.0.


Keen to see your thoughts.

Thanks anyone participating.

github-actions[bot] commented 1 month ago

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.