OneOf doesn't seem to work as schema

Issue #257 resolved
Jonathan Willis created an issue

In a post, this seems to fail:

post:
  requestBody:
    description: Object
    required: true
    content:
      application/json:
        schema:
          oneOf:
            - $ref: '#/components/schemas/Obj1'
            - $ref: '#/components/schemas/Obj2'
            - $ref: '#/components/schemas/Obj3'

I checked the features list and oneOf seems to be supported but nothing but empty payloads seem to pass this swagger validation.

Comments (9)

  1. James Navin

    Hi Jonathan,

    Thanks for raising this. Could I please ask you to include a full spec (including the referenced schemas) and an example request you are using. This will help me to reproduce what you are seeing (there is reasonably good coverage of this feature in the test suite, but it is quite possible I’ve missed something).

    Cheers,

    James

  2. Jonathan Willis reporter

    Here you go James:

    openapi: "3.0.0"
    info:
      description: Auth Api Documentation
      version: "1.0.0"
      title: Api Documentation
      contact: {}
    servers:
      - url: https://example.com"
        description: prod
      - url: https://stage.example.com"
        description: Staging
    paths:
      /integrations:
        get:
          tags:
            - integration-controller
          operationId: IntegrationsController.index
          summary: Get Account Integrations
          description: >
            Returns the integrations configured for the account.
          parameters:
            - in: query
              name: accountId
              schema:
                type: string
              required: false
          responses:
            '200':
              description: Successful response
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/ArrayOfIntegrations'
            '401':
              description: Unauthorized
            '403':
              description: Forbidden
        post:
          operationId: create Integrations
          tags:
            - integration-controller
          summary: Create Account Integrations
          description: Create a new account integration
          parameters:
            - in: query
              name: accountId
              schema:
                type: integer
              required: false
          requestBody:
            description: Integration Object
            required: true
            content:
              application/json:
                schema:
                  oneOf:
                    - $ref: '#/components/schemas/AdobeSharedSecret'
                    - $ref: '#/components/schemas/AdobeOAuthSupport'
                    - $ref: '#/components/schemas/BitlyUrl'
          responses:
            '201':
              description: Creation Successful
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Integration'
            '401':
              description: UnAuthorized
            '403':
              description: Forbidden
    components:
      schemas:
        ArrayOfIntegrations:
          type: array
          items:
            $ref: '#/components/schemas/Integration'
        Integration:
          oneOf:
            - $ref: '#/components/schemas/AdobeSharedSecret'
            - $ref: '#/components/schemas/AdobeOAuthSupport'
            - $ref: '#/components/schemas/BitlyUrl'
        MetaDataDestination:
          type: object
          required:
            - type
          properties:
            name:
              type: string
            type:
              type: string
        URLShortener:
          type: object
          properties:
            name:
              type: string
            url:
              type: string
        AdobeSharedSecret:
          allOf:
            - $ref: '#/components/schemas/MetaDataDestination'
            - type: object
              required:
                - type
              properties:
                type:
                  type: string
                  enum: ["AdobeSharedSecret"]
                companyName:
                  type: string
                reportSuites:
                  $ref: '#/components/schemas/ArrayOfReportSuites'
        AdobeOAuthSupport:
          allOf:
            - $ref: '#/components/schemas/MetaDataDestination'
            - type: object
              properties:
                type:
                  type: string
                  enum: ["AdobeOAuthSupport"]
                companyName:
                  type: string
                reportSuites:
                  $ref: '#/components/schemas/ArrayOfReportSuites'
        BitlyUrl:
          allOf:
            - $ref: '#/components/schemas/URLShortener'
            - type: object
              properties:
                login:
                  type: string
                apiKey:
                  type: string
                companyName:
                  type: string
                createdAt:
                  type: string
                type:
                  type: string
                  enum: ["BitlyUrl"]
        ElementClassification:
          type: object
          properties:
            name:
              type: string
        ArrayOfReportSuites:
          type: array
          items:
            $ref: '#/components/schemas/ReportSuite'
        ReportSuite:
          type: object
          properties:
            rsid:
              type: string
            site_title:
              type: string
            element_classifications:
              $ref: '#/components/schemas/ElementClassification'
    

    I believe this fails:

    {
        "name": "integration name",
        "type": "BitlyUrl",
        "provider": "bitly",
        "accountId": 1,
        "config": { }
    }
    

  3. Jonathan Willis reporter

    Hey @James Navin ,

    I decided to actually put together a good example instead of throwing something together last second. Here is the yaml file:

    openapi: "3.0.0"
    info:
      description: Api Documentation
      version: "1.0.0"
      title: Api Documentation
      contact: {}
    servers:
      - url: https://example.com"
        description: Production
      - url: https://stage.example.com"
        description: Staging
    paths:
      /integrations:
        get:
          tags:
            - integration-controller
          operationId: IntegrationsController.index
          summary: Get Account Integrations
          description: >
            Returns the integrations configured for the account.
            For Admin, it returns the current account's settings
            For OP Admin, it can return any account's settings.
            For normal user, it returns 401.
          parameters:
            - in: query
              name: accountId
              schema:
                type: string
              required: false
          responses:
            '200':
              description: Successful response
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/ArrayOfIntegrations'
            '401':
              description: Unauthorized
            '403':
              description: Forbidden
        post:
          operationId: IntegrationsController.create
          tags:
            - integration-controller
          summary: Create Account Integrations
          description: >
            Create a new account integration
          parameters:
            - in: query
              name: accountId
              schema:
                type: integer
              required: false
          requestBody:
            description: Integration Object
            required: true
            content:
              application/json:
                schema:
                  $ref: '#/components/schemas/Integration'
          responses:
            '201':
              description: Creation Successful
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/IntegrationResponse'
            '401':
              description: UnAuthorized
            '403':
              description: Forbidden
      /integrations/{integrationId}:
        put:
          tags:
            - integration-controller
          operationId: Update Integration
          summary: Update Integration
          description: >
            Update an account integration
          parameters:
            - in: path
              name: integrationId
              schema:
                type: integer
              required: true
            - in: path
              name: accountId
              schema:
                type: integer
              required: true
            - in: query
              name: integration
              schema:
                $ref: '#/components/schemas/Integration'
              required: true
          requestBody:
            description: Integration Object
            required: true
            content:
              application/json:
                schema:
                  oneOf:
                    - $ref: '#/components/schemas/Integration'
          responses:
            '200':
              description: Successful response
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Integration'
            '401':
              description: UnAuthorized
            '403':
              description: Forbidden
        delete:
          #      operationId: Delete Integration
          tags:
            - integration-controller
          summary: Delete Integration
          description: >
            delete an account integration
          parameters:
            - in: path
              name: accountId
              schema:
                type: integer
              required: true
            - in: path
              name: integrationId
              schema:
                type: integer
              required: true
          responses:
            '200':
              description: Successful response
              content:
                application/json:
                  schema:
                    type: integer
            '401':
              description: UnAuthorized
            '403':
              description: Forbidden
        get:
          tags:
            - integration-controller
          summary: get Integration
          description: get a single account integration
          parameters:
            - in: path
              name: accountId
              schema:
                type: integer
              required: true
            - in: path
              name: integrationId
              schema:
                type: integer
              required: true
          responses:
            '200':
              description: Successful response
              content:
                application/json:
                  schema:
                    $ref: '#/components/schemas/Integration'
            '401':
              description: UnAuthorized
            '403':
              description: Forbidden
      /integration/{integrationId}/reconnect:
        post:
          tags:
            - integration-controller
          #      operationId: reconnectIntegration
          summary: reconnectIntegation
          description: >
            reconnect an account integration and update status
          parameters:
            - in: path
              name: accountId
              schema:
                type: integer
              required: true
            - in: path
              name: integrationId
              schema:
                type: integer
              required: true
          responses:
            '200':
              description: Integration reconnected
              content:
                application/json:
                  schema:
                    type: string
            '401':
              description: UnAuthorized
            '403':
              description: Forbidden
    components:
      schemas:
        ArrayOfIntegrations:
          type: array
          items:
            $ref: '#/components/schemas/IntegrationResponse'
        IntegrationResponse:
          allOf:
            - $ref: '#/components/schemas/Integration'
            - type: object
              properties:
                lastUpdated:
                  type: integer
        Integration:
          allOf:
            - type: object
              properties:
                name:
                  type: string
            - type: object
              oneOf:
                - $ref: '#/components/schemas/UrlShortener'
                - $ref: '#/components/schemas/MetaDataDestination'
        MetaDataDestination:
          allOf:
            - type: object
              oneOf:
                - $ref: '#/components/schemas/AdobeOAuth'
                - $ref: '#/components/schemas/AdobeSharedSecret'
            - type: object
              properties:
                type:
                  type: string
                  enum: ["MetaDataDestination"]
        AdobeSharedSecret:
          type: object
          properties:
            provider:
              type: string
              enum: ["AdobeSharedSecret"]
            config:
              type: object
              properties:
                username:
                  type: string
                secret:
                  type: string
        AdobeOAuth:
          type: object
          properties:
            provider:
              type: string
              enum: ["AdobeOAuth"]
        BitlyUrl:
          allOf:
            - type: object
              properties:
                config:
                  type: object
                  properties:
                    login:
                      type: string
                    apiKey:
                      type: string
                    companyName:
                      type: string
            - type: object
              properties:
                provider:
                  type: string
                  enum: ["Bitly"]
        UrlShortener:
          allOf:
            - type: object
              properties:
                type:
                  type: string
                  enum: ["UrlShortener"]
            - $ref: '#/components/schemas/BitlyUrl'
        ElementClassification:
          type: object
          properties:
            name:
              type: string
        ArrayOfReportSuites:
          type: array
          items:
            $ref: '#/components/schemas/ReportSuite'
        ReportSuite:
          type: object
          properties:
            rsId:
              type: string
            rsLabel:
              type: string
            element_classifications:
              $ref: '#/components/schemas/ElementClassification'
    

    Here is the payload:

    {
        "name": "integration name",
        "type": "MetaDataDestination",
        "provider": "AdobeOAuth"
    }
    

    This should be valid and should match the Integration → MetaDataDestination → AdobeOAuth but the validator doesn’t work. This is the swagger validator response:

    {
      "messages" : [ {
        "key" : "validation.request.body.schema.additionalProperties",
        "level" : "ERROR",
        "message" : "Object instance has properties which are not allowed by the schema: [\"name\",\"provider\",\"type\"]",
        "context" : {
          "requestPath" : "/integrations",
          "apiRequestContentType" : "application/json",
          "location" : "REQUEST",
          "requestMethod" : "POST"
        }
      } ]
    }
    
    [ERROR] [2020-01-14 17:42:42,380] [] OpenAPI location=REQUEST key=POST#/integrations levels=ERROR messages=Validation failed.
    [ERROR][REQUEST][POST /integrations @body] Object instance has properties which are not allowed by the schema: ["name","provider","type"]
    

  4. James Navin

    Can I check whether the FAQ resolved your problem? If it does I’ll close out this ticket.

    Cheers.

  5. Gediminas Rimša

    If anyone stumbles here, this workaround might work for you (in case you want exactly one of 3 types, without any extra properties):

    SomeType:
      additionalProperties: true       // to prevent using "false" during validation
      oneOf:
        - $ref: '#/components/schemas/Obj1'   // these types can all define `additionalProperties: false`
        - $ref: '#/components/schemas/Obj2'
        - $ref: '#/components/schemas/Obj3'
    

  6. Log in to comment