znsio / specmatic

Turn your contracts into executable specifications. Contract Driven Development - Collaboratively Design & Independently Deploy MicroServices & MicroFrontends.
https://specmatic.io
MIT License
281 stars 52 forks source link

Discriminator as enum #1277

Closed joelrosario closed 2 months ago

joelrosario commented 2 months ago

What:

Implemented the use of discriminator as an enum.

Why:

Sometimes a schema is composed within other schemas, and depending on which schema it is composed within, the value of one of it's properties needs to be restricted.

For example:

# vehicle_type.yaml
components:
  schemas:
    VehicleType:
      type: object
      properties:
        type:
          type: string
# openapi spec
paths:
  /car:
    post:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Car'
  /bike:
    post:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Bike'
components:
  schemas:
    Car:
      allOf:
        - $ref: 'vehicle_type.yaml#/components/chemas/VehicleType'
        - type: object
           properties:
             transmission_type:
               type: string
    Bike:
      allOf:
        - $ref: 'vehicle_type.yaml#/components/chemas/VehicleType'
        - type: object
           properties:
             stand_type:
               type: string

Now, in Bike, the value of type should be "bike", and in Car, it should be "car". We could have added an enum to type in VehicleType. But we also want to allow new types to compose VehicleType at will, without having to modify the vehicle_type.yaml file.

It turns out, multiple tools leverage the discriminator for this.

According to the OpenAPI documentation, the discriminator feature is meant to be used by validators or deserialisers to determine which of the options in a oneOf or anyOf an object should match. The property used as the discriminator therefore has fixed values. If we declare a discriminator without the oneOf, with the mappings, it acts as an enum, where the value of the property (here type) is limited to the keys, and the values are meaningless.

Armed with the discrimator, we can now restrict the value of type using this syntax:

# openapi spec
paths:
  /car:
    post:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Car'
  /bike:
    post:
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Bike'
components:
  schemas:
    Car:
      allOf:
        - $ref: 'vehicle_type.yaml#/components/chemas/VehicleType'
        - type: object
           properties:
             transmission_type:
               type: string
      discriminator:
        propertyName: "type"
        mapping:
          "car": "#/components/schemas/Car"
    Bike:
      allOf:
        - $ref: 'vehicle_type.yaml#/components/chemas/VehicleType'
        - type: object
           properties:
             stand_type:
               type: string
      discriminator:
        propertyName: "type"
        mapping:
          "bike": "#/components/schemas/Bike"

How:

We've modified the parser in OpenApiSpecification to convert the discriminator value into an enum.

Checklist: