2
votes

I am trying to build up a response from a variety of schema components using OpenAPI 3. There are basically three parts to the response:

  1. A shared component that other endpoints use (i.e. success/failure flags). - #/components/schemas/core_response_schema inside allOf.
  2. Properties that all responses on this endpoint use (i.e., user_id) - the properties component of the below.
  3. One of several schemas that will vary depending on the type of user. - the oneOf component.

I've determined that I have to use allOf to be able to mix properties (item 2) and the core response (item 1), though this feels wrong as there's only one item. I tried a $ref, but it didn't work.

The below successfully passes three different OpenAPI linting tools, but in the example it builds, Swagger UI does not show the item 2 things (properties), and does show all of the item 3 things (should be oneOf).

"responses": {
    "200": {
        "description": "Operation successfully executed.",
        "content": {
            "application/json": {
                "schema": {
                    "properties": {
                        "user_id": {
                            "$ref": "#/components/schemas/user_id"
                        },
                        "results": {
                            "type": "array",
                            "items": {
                                "$ref": "#/components/schemas/result_user_by_id"
                            }
                        }
                    },
                    "type": "object",
                    "allOf": [
                        {
                            "$ref": "#/components/schemas/core_response_schema"
                        }
                    ],
                    "oneOf": [
                        {
                            "$ref": "#/components/schemas/user_type_a"
                        },
                        {
                            "$ref": "#/components/schemas/user_type_b"
                        },
                        {
                            "$ref": "#/components/schemas/user_type_c"
                        }
                    ]
                }
            }
        }
    }
},
"components": {
    "schemas": {
        "core_response_schema": {
            "properties": {
                "success": {
                    "description": "A flag indicating whether the request was successfully completed or not.",
                    "type": "boolean"
                },
                "num_results": {
                    "description": "The number of results for this request",
                    "type": "integer"
                }
            },
            "type": "object"
        },
        "user_id": {
            "description": "Unique 10 character `user_id`.",
            "type": "string",
            "maxLength": 10,
            "minLength": 10,
            "example": "a1b2c3d4e5"
        },
    }
}

And example payloads for two users. Type A and B (it's a contrived example).

User Type A:

{
    "success": true,
    "num_results": 1,
    "user_id": "c1b00cb714",
    "results": [{
            "user_type": "a",
            "group_id": "e7a99e3769",
            "name": null,
            "title": null,
            ... (and so on until we get to the stuff that's unique to this type of user) ...
            "favourite_artworks": [
                "sunflowers",
                "landscapes"
            ],
            "artwork_urls": [
                "http://sunflowers.example"
            ]
        }
    ]
}

User Type B:

{
    "success": true,
    "num_results": 1,
    "user_id": "c1b00cb715",
    "results": [{
            "user_type": "B",
            "group_id": "e7a99e3769",
            "name": null,
            "title": null,
            ... (and so on until we get to the stuff that's unique to this type of user) ...
            "supported_charities": [
                "UN Foundations"
            ],
            "charity_urls": [
                "http://www.un.int"
            ],
        }
    ]
}

What's the correct way to merge together different schemas and properties in OpenAPI? Is this right and Swagger UI just can't handle it?

And how do you mix a schema with properties without having to use allOf?

This suggests it's possible: Swagger Schema: oneOf, anyOf, allOf valid at the same time?

1
Can you also post the user_id schema and add some JSON payload examples with user_type_a/user_type_b/user_type_c parts that vary based on the user_id? Such scenario would probably be best handled using model inheritance and discriminator rather than oneOf, but it help to see payload examples to be sure.Helen
@Helen - thanks for the feedback. I've added the schemas and a couple of payload examples. Please note that it's a contrived, simplified example of my actual schema, but the principle is the same.GIS-Jonathan
@Helen I did come across the discriminator element but it didn't quite seem to suit this. But maybe that's just because I didn't quite grok it.GIS-Jonathan

1 Answers

1
votes

After further investigation, I've determined this is a bug in swagger-ui - https://github.com/swagger-api/swagger-ui/issues/3803 - they simply don't support oneOf (or anyOf) currently.

As far as at least three different linting tools are concerned, a mixture of anyOf, oneOf, and allOf can be used together in the same schema.

Redoc appears to have similar problems - https://github.com/Rebilly/ReDoc/issues/641