Swagger Inheritance and Composition
In my "simplified" API, all responses are derived (inherit) from a base "response" class. The response class is composed of a header filled with metadata, and the body which contains the core data the the user is requesting. The response (in JSON) is laid out such that all the metadata is on the first "layer" and the body is a single attribute called "body" as such
response
|--metadata attribute 1 (string/int/object)
|--metadata attribute 2 (string/int/object)
|--body (object)
|--body attribute 1 (string/int/object)
|--body attribute 2 (string/int/object)
I have tried to define this relationship in swagger with the following JSON:
{
...
"definitions": {
"response": {
"allOf": [
{
"$ref": "#/definitions/response_header"
},
{
"properties": {
"body": {
"description": "The body of the response (not metadata)",
"schema": {
"$ref": "#/definitions/response_body"
}
}
}
}
]
},
"response_header": {
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"type": "string",
"description": "value of 'success', for a successful response, or 'error' if there is an error",
"enum": [
"error",
"success"
]
},
"message": {
"type": "string",
"description": "A suitable error message if something went wrong."
}
}
},
"response_body": {
"type": "object"
}
}
}
I then try to create different responses by creating the various body/header classes that inherit from body/header, and then create child response classes that are composed of the relevant header/body classes (shown in source code at bottom). However, I am certain that either this is the wrong way to do things, or that my implementation is incorrect. I have been unable to find an example of inheritance in the swagger 2.0 specification (shown below) but have found an example of composition.
I am pretty certain that this "discriminator" has a large part to play, but not sure what I need to do.
Question
Could someone show me how one is supposed to implement composition+inheritance in swagger 2.0 (JSON), preferably by "fixing" my example code below. It would also be great if I could specify an ErrorResponse class that inherits from response where the "result" attribute in the header is always set to "error".
{
"swagger": "2.0",
"info": {
"title": "Test API",
"description": "Request data from the system.",
"version": "1.0.0"
},
"host": "xxx.xxx.com",
"schemes": [
"https"
],
"basePath": "/",
"produces": [
"application/json"
],
"paths": {
"/request_filename": {
"post": {
"summary": "Request Filename",
"description": "Generates an appropriate filename for a given data request.",
"responses": {
"200": {
"description": "A JSON response with the generated filename",
"schema": {
"$ref": "#/definitions/filename_response"
}
}
}
}
}
},
"definitions": {
"response": {
"allOf": [
{
"$ref": "#/definitions/response_header"
},
{
"properties": {
"body": {
"description": "The body of the response (not metadata)",
"schema": {
"$ref": "#/definitions/response_body"
}
}
}
}
]
},
"response_header": {
"type": "object",
"required": [
"result"
],
"properties": {
"result": {
"type": "string",
"description": "value of 'success', for a successful response, or 'error' if there is an error",
"enum": [
"error",
"success"
]
},
"message": {
"type": "string",
"description": "A suitable error message if something went wrong."
}
}
},
"response_body": {
"type": "object"
},
"filename_response": {
"extends": "response",
"allOf": [
{
"$ref": "#definitions/response_header"
},
{
"properties": {
"body": {
"schema": {
"$ref": "#definitions/filename_response_body"
}
}
}
}
]
},
"filename_response_body": {
"extends": "#/definitions/response_body",
"properties": {
"filename": {
"type": "string",
"description": "The automatically generated filename"
}
}
}
}
}
Diagram Update
To try and clarify what I want, I have created the very basic diagram below which aims to show that all responses are instantiations of the "response" object that have been built by (composition) using any combination of response_header and response_body objects. The response_header and response_body objects can be extended and inserted into any response object, which is done in the case of a filename_response which uses the filename_response_body child of the base response_body class. Both error and successful responses use the "response" object.
As a beginner in swagger I don't find the official documentation about polimorphism and composition easy to undestand, because it lacks an example. When I searched the net, there are lots of good examples refering to swagger 1.2 when extends
was valid.
For swagger 2.0 I found a good example in swagger spec sources on github via this google group
Based on above sources, here is a short valid inheritance example in YAML:
definitions:
Pet:
discriminator: petType
required:
- name
- petType # required for inheritance to work
properties:
name:
type: string
petType:
type: string
Cat:
allOf:
- $ref: '#/definitions/Pet' # Cat has all properties of a Pet
- properties: # extra properties only for cats
huntingSkill:
type: string
default: lazy
enum:
- lazy
- aggressive
Dog:
allOf:
- $ref: '#/definitions/Pet' # Dog has all properties of a Pet
- properties: # extra properties only for dogs
packSize:
description: The size of the pack the dog is from
type: integer
I've found that composition works fine even without definition of discriminator
.
For example, base Response
:
definitions:
Response:
description: Default API response
properties:
status:
description: Response status `success` or `error`
type: string
enum: ["success", "error"]
error_details:
description: Exception message if called
type: ["string", "object", "null"]
error_message:
description: Human readable error message
type: ["string", "null"]
result:
description: Result body
type: ["object", "null"]
timestamp:
description: UTC timestamp in ISO 8601 format
type: string
required:
- status
- timestamp
- error_details
- error_message
- result
Is rendered as:
And we can extend it to refine custom schema of result
field:
FooServiceResponse:
description: Response for Foo service
allOf:
- $ref: '#/definitions/Response'
- properties:
result:
type: object
properties:
foo_field:
type: integer
format: int32
bar_field:
type: string
required:
- result
And it will be correctly rendered as:
Note, that allOf
is enough for this to work and no discriminator
field is used. This is good, because it works and this is important, as I think, tools will be able to generate code without discriminator
field.
All the answers here are excellent already, but I just want to add a minor note about composition versus inheritance. According to the Swagger/OpenAPI Spec, to implement composition, using the allOf
property is enough, as @oblalex correctly points out. However, to implement inheritance, you need to use allOf
with discriminator
, as in the example by @TomaszSętkowski.
Also, I found some more Swagger examples of both composition and inheritance at API Handyman. They're part of an excellent Swagger/OpenAPI tutorial series by Arnaud Lauret that I think everyone should check out.
The Swagger 2.0 standard example you have shared depicts a composition relationship, specifically it captures an "is a kind of" super-type/sub-type relationship however it is not polymorphism in and of itself.
It would be if you could reference the base definition of Pet as an input parameter, then pick Cat or enter a Cat JSON object as the value for the input request, and have this acceptable to Swagger UI.
I could not get this to directly work.
The best I could get working was to set additionalProperties to true on the base object (e.g. Pet), specify Pet using JSON pointer reference as the input schema, and finally copy and paste my Cat JSON value object into Swagger UI. Since the additional properties are allowed Swagger UI generated a valid input request payload.