1

I have a model defined as:

Event:
  type: object
  properties:
    id:
      type: string
    timestamp:
      type: string
      format: date-time
    type:
      type: string
      enum:
        - click
        - open
        - sent
    click:
      type: object
      properties:
        url:
          type: string
        title:
          type: string
    open:
      type: object
      properties:
        subject:
          type: string
    sent:
      type: object
      properties:
        subject:
          type: string
        from:
          type: string
        to:
          type: string

However, the shape is actually different depending on the value of the type enum field. It can be one of three, I tried to define the differences in the examples (see below) but this did not work.

As you can see, the below shows three examples, each one is different depending on the type. This is how the API behaves, but it is not straightforward to document this expected behaviour.

  examples:
    application/json:
      - id: >
          ad1b12f0-63a8-47b5-9820-3e447143ce7e
        timestamp: >
          2017-09-29T16:45:20.000+00:00
        type: click
        click:
          url: >
            www.some-website-somewhere.org/12345
          title: >
            Click here!
      - id: >
          d9e787fa-db49-4bd8-97c3-df45f159295c
        timestamp: >
          2017-09-29T16:45:20.000+00:00
        type: open
        open:
          subject: >
            Have you seen this?
      - id: >
          30adc194-0f5f-4889-abd4-4fa964d0bb42
        timestamp: >
          2017-09-29T16:45:20.000+00:00
        type: sent
        sent:
          subject: >
            Have you seen this?
          from: >
            bill@office.gov
          to: >
            mon@gmail.com

I read a few places that mention this is not possible, but I am hoping to get some "outside the box" thinkers to help me find a solution.

Helen
  • 87,344
  • 17
  • 243
  • 314
smactive
  • 58
  • 1
  • 7
  • 1
    Each and every folding style indicator (`>`) for scalars, in your second example, is superfluous and makes your YAML unnecessarily unreadable. That is because none of those scalars consists of multiple lines and for many of those that is even impossible. This is a good example on how **not** to do YAML – Anthon Oct 04 '17 at 06:18
  • Fair enough. No help on the actual question? – smactive Oct 04 '17 at 06:22
  • I have no knowledge about swagger, I came here because of the (IMO misplaced) [tag:yaml] tag. – Anthon Oct 04 '17 at 07:06

1 Answers1

3

This is possible using model inheritance/composition and discriminator.

First, define the base model Event that contains just the common properties, and mark the type property as discriminator:

definitions:
  Event:
    type: object
    discriminator: type
    required:
      - type  # discriminator property is required
    properties:
      id:
        type: string
        example: ad1b12f0-63a8-47b5-9820-3e447143ce7e
      timestamp:
        type: string
        format: date-time
        example: 2017-09-29T16:45:20.000+00:00
      type:
        type: string
        enum:
          - click
          - open
          - sent

Then inherit the click/open/sent models from the base Event model via allOf. The names of the child models must be the same as the values of the discriminator property. In your example, the models must be named click, open and sent. If you use Swagger UI, you can add title to override the displayed model names.

  click:
    title: ClickEvent
    allOf:
      - $ref: '#/definitions/Event'
      - type: object
        properties:
          click:
            type: object
            properties:
              url:
                type: string
                example: www.some-website-somewhere.org/12345
              title:
                type: string
                example: Click here!

In operations, use the base model (Event) as the request or response schema:

responses:
  200:
    description: OK
    schema:
      $ref: '#/definitions/Event'

The clients should examine the value of the discriminator property (in this example - type) to decide which inherited model to use.

Note for Swagger UI users: Swagger UI and Swagger Editor currently do not support switching models based on discriminator. Follow this issue for updates.


In OpenAPI 3.0 it's easier - you can simply use oneOf to define alternate schemas for a request or response.

responses:
  '200':
    description: OK
    content:
      application/json:
        schema:
          oneOf:
            - $ref: '#/components/schemas/ClickEvent'
            - $ref: '#/components/schemas/OpenEvent'
            - $ref: '#/components/schemas/SentEvent'
          # Optional mapping of `type` values to models
          discriminator:
            propertyName: type
            mapping:
              click: '#/components/schemas/ClickEvent'
              open:  '#/components/schemas/OpenEvent'
              sent:  '#/components/schemas/SentEvent'
Helen
  • 87,344
  • 17
  • 243
  • 314