I'm using SwaggerHub with OpenAPI 3 to define an API. One route GET /foo/{id], should return the foo object of a given id, with its associated bar objects. The API will return something like: {id: 4, name: 'test', bars: [{id: 53, name: 'barName1'}, {id: 87, name: 'barName2'}]}. I.e. there is a many-to-many relationship between foo and bar.
How do I describe this in OpenAPI 3 syntax? I have tried using the anyOf property. So far I have:
paths:
/foo/{id}:
get:
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Foo'
anyOf:
- $ref: '#/components/schemas/Bar'
But this doesn't appear to show the correct schema in the UI (there is no mention of Bar in the UI).
You don't need anyOf. anyOf indicates an alternative ("either Foo or Bar"), whereas you have a usual nested structure - an object Foo with the property bars that is an array of Bars. This can be described as:
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/Foo'
components:
schemas:
Foo:
type: object
properties:
bars:
type: array
items:
$ref: '#/components/schemas/Bar'
required:
- bars
Bar:
...
Related
Which syntax is correct - the first one, the second one, or both?
components:
schemas:
FileContent:
allOf:
- $ref: '#/components/schemas/FileInfo'
- type: object
properties:
storageMethod:
$ref: '#/components/schemas/StorageMethod'
contentRange:
type: string
nullable: true
# ... other properties ...
additionalProperties: false
components:
schemas:
FileContent:
type: object
allOf:
- $ref: '#/components/schemas/FileInfo'
properties:
storageMethod:
$ref: '#/components/schemas/StorageMethod'
contentRange:
type: string
nullable: true
# ... other properties ...
additionalProperties: false
In terms of allOf syntax, both versions are correct and technically equivalent:
allOf:
- $ref: '#/components/schemas/Foo'
- properties:
# other properties
# ...
allOf:
- $ref: '#/components/schemas/Foo'
properties:
# other properties
# ...
In OpenAPI 3.1 (which uses JSON Schema 2020-12 by default), there's even no need for allOf if you only have one $ref because $ref now allows sibling keywords. (But you still need allOf to "combine" multiple $refs.)
# openapi: 3.1.0
$ref: '#/components/schemas/Foo'
properties:
# other properties
# ...
The error in your examples is elsewhere - it's the presence of additionalProperties: false. This keyword is problematic because it only knows about its immediate sibling properties and has no visibility into allOf/oneOf/anyOf subschemas or "inherited" schemas. For your examples, this means that properties defined in the FileInfo schema won't actually be allowed in the composed schema.
Here are some more examples to illustrate that additionalProperties: false doesn't work the way one would expect:
allOf:
- $ref: '#/components/schemas/Foo'
- $ref: '#/components/schemas/Bar'
additionalProperties: false
# Expected: Only the properties defined in Foo and Bar are allowed
# Actual: No properties are allowed
allOf:
- $ref: '#/components/schemas/Foo'
- properties:
prop:
type: string
additionalProperties: false
# Expected: The allowed properties are `prop` and those defined in the Foo schema
# Actual: Only the `prop` property is allowed
allOf:
- $ref: '#/components/schemas/Foo'
properties:
prop:
type: string
additionalProperties: false
# Expected: The allowed properties are `prop` and those defined in the Foo schema
# Actual: Only the `prop` property is allowed
Foo:
type: object
properties:
foo:
type: string
additionalProperties: false
Bar:
allOf:
- $ref: '#/components/schemas/Foo'
- properties:
prop:
type: string
# Expected: The Bar schema allows properties from Foo + the `prop` property
# Actual: The Bar schema allows only properties from Foo
This is solved in OpenAPI 3.1 / JSON Schema 2019-09+ by the new unevaluatedProperties: false keyword. So the following will work the way you expect:
# openapi: 3.1.0
$ref: '#/components/schemas/Foo'
properties:
prop:
type: string
unevaluatedProperties: false
I think the second one: for a long time, schema keywords adjacent to $ref were ignored in json schemas. Later they changed it in the spec (AFAIK), but I'm not sure if all the tooling & implementations caught up with that.
Yet my personal preference is putting both the parent schema and child schema props under an allOf, like
schemas:
FileContent:
allOf:
- $ref: '#/components/schemas/FileInfo'
- type: object
properties:
storageMethod:
$ref: '#/components/schemas/StorageMethod'
contentRange:
type: string
nullable: true
totalLength:
type: integer
format: int64
I want to extend the "200SuccessDefault" response with a schema or example.
paths:
/home:
...
responses:
200:
$ref: '#/components/responses/200SuccessDefault'
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/PieChartElement'
examples:
PieChart:
$ref: '#/components/examples/PieChart_1'
This approach runs into an error, the schema and examples fields are ignored:
Sibling values alongside $refs are ignored. To add properties to a $ref, wrap the $ref into allOf, or move the extra properties into the referenced definition (if applicable).
I tried allOf:
paths:
/home:
responses:
200:
allOf:
- $ref: '#/components/responses/200SuccessDefault'
- content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/PieChartElement'
examples:
PieChart:
$ref: '#/components/examples/PieChart_1'
This approach runs into the error:
should NOT have additional properties additionalProperty: allOf
should have required property 'description' missingProperty: description
You cannot extend a referenced response object. But, you can use a shared schema object and extend it utilizing allOf within schema.
Inside allOf you can put:
your $ref
a new type extending your default response
If you want to give an example of an entire extended response (JSON), just put it into "application/json".
An example of OpenAPI would be:
"202":
description: Extended response sample
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/application"
- type: object
properties:
anotherProperty:
type: string
maxLength: 200
example: "Property example"
example: {id: 1234, anotherProperty: "Hello"}
I am trying to define my API using OpenAPI version 3.0. I am trying to generate a YAML file which has four maps, and each map contains a different information. How can I create a YAML file to achieve that goal? I know that my components are not right because of which I am not getting the right result.
Request body should be like this:
[
UserInformation{FirstName, LastName},
AddressInformation{Phone, Address},
ContactInformation{Email, Phone}
]
openapi: 3.0.0
info:
version: 1.0.0
title: 'INPUT-FORM-API'
paths:
/api/v1/test/healthcheck:
get:
summary: Health check for the test api services. Used Internally
operationId: Externalhealthcheck
description: healthcheck for the test services status.
responses:
'200':
description: This status is always returned when service is Ok.
content:
application/json:
schema:
$ref: '#/components/schemas/HealthcheckObject'
/api/v1/test/newformentry:
post:
summary: End Point to insert data into the new table.
operationId: NewFormEntry
description: EndPoint to insert data for new form.
requestBody:
content:
application/json:
schema:
items:
$ref: '#/components/schemas/NewFormEntry'
responses:
'200':
description: This status is always returned when service is Ok.
content:
application/json:
schema:
$ref: '#/components/schemas/HealthcheckObject'
components:
schemas:
NewFormEntry:
$ref: '#/components/schemas/UserInformation'
$ref: '#/components/schemas/AddressInformation'
$ref: '#/components/schemas/ContactInformation'
$ref: '#/components/schemas/MessageFromBene'
UserInformation:
required:
- FirstName
- LastName
properties:
FirstName:
type: string
LastName:
type: string
AddressInformation:
required:
- StreetAddress
- City
- State
- ZipCode
properties:
StreetAddress:
type: string
StreetAddress2:
type: string
City:
type: string
State:
type: string
ZipCode:
type: integer
format: int64
ContactInformation:
required:
- PhoneNumber
- Email
properties:
PhoneNumber:
type: integer
format: int64
maximum: 9
Email:
type: string
HomePhone:
type: integer
format: int64
maximum: 9
Cell:
type: integer
format: int64
maximum: 9
WorkPhone:
type: integer
format: int64
maximum: 9
MessageFromBene:
required:
- Message
properties:
PhoneNumber:
type: integer
format: int64
maximum: 9
Message:
type: string
HealthcheckObject:
required:
- Status
- ErrorMessage
properties:
Status:
type: string
ErrorMessage:
type: string
So the NewFormEntry schema must be an array containing 3 objects, where the 1st object must be UserInformation, the second object must be AddressInformation, and the 3rd object mube be ContactInformation. This is like a tuple, i.e. an ordered sequence of elements where each element has a specfic type. Tuple definitions are slightly different in different OpenAPI versions.
OpenAPI 3.1
If or when you migrate to OAS 3.1, such an array can be defined using prefixItems. This keyword specifies the schema for each element position (in other words, it specifies the order of elements in the array):
components:
schemas:
NewFormEntry:
type: array
prefixItems:
- $ref: '#/components/schemas/UserInformation' # type of the 1st element
- $ref: '#/components/schemas/AddressInformation' # type of the 2nd element
- $ref: '#/components/schemas/ContactInformation' # type of the 3rd element
minItems: 3
maxItems: 3
additionalItems: false # can be omitted if `maxItems: 3` is specified
OpenAPI 3.0
In OAS 3.0, you can define the array length (i.e. 3 items) and the possible types of array items (i.e. each item can be either A, B, or C), but there's no way to define a specific order of objects in the array. So the most you can do is this:
components:
schemas:
NewFormEntry:
type: array
items:
oneOf:
- $ref: '#/components/schemas/UserInformation'
- $ref: '#/components/schemas/AddressInformation'
- $ref: '#/components/schemas/ContactInformation'
minItems: 3
maxItems: 3
Note that this definition allows arbitrary order of objects in the array and also multiple instances of the same object (e.g. [UserInformation, UserInformation, UserInformation]). You might want to implement additional backend validations to verify the desired order of objects in this array.
I use OpenAPI 3.0.0 and want to pass an array of Items as a parameter in the requestBody. My API definition looks like this:
post:
tags:
- test
summary: Test dummy
operationId: requestBodyTests
requestBody:
description: test the body
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Items'
components:
schemas:
Items:
type: array
items:
$ref: '#/components/schemas/Item'
examples:
- id: bla
text: blubb
- id: bla
text: blubb
Item:
type: object
properties:
id:
type: string
name:
type: string
Swagger UI displays the request body example as follows:
and the request body schema as follows:
Why does it show an orderedmap instead of my normal objects?
Can someone tell me how to do the spec right for having the array with items correct?
Examples inside schemas use the example keyword (singular), not examples (plural).
Also, your YAML indentation is wrong - Items and Item must be indented under schemas, list items in the example must have the same indent, etc. If you paste your spec into http://editor.swagger.io, it will point out syntax errors.
Here's the fixed version:
components:
schemas:
Items:
type: array
items:
$ref: '#/components/schemas/Item'
example: # <------
- id: bla
text: blubb
- id: bla
text: blubb
Item:
type: object
properties:
id:
type: string
name:
type: string
Moreover, you get an example as 'orderedmap' because the example field is A free-form property.
But represent examples that cannot be naturally represented in JSON or YAML, a string value can be used to contain the example with escaping where necessary. (OpenAPI spec)
We can write an example as 'string' in both ways:
1.
example: '[ currency: USD, amount: 123 ]'
example: |
[
currency: USD,
amount: 123
]
I have an object that has a property that is an object whose type would be one of list of types. All my attempts have been rejected by Swagger Editor with the following error:
Data does not match any schemas from 'anyOf'
Jump to line 43
Details
Object
code: "ANY_OF_MISSING"
message: "Data does not match any schemas from 'anyOf'"
path: Array [7]
inner: Array [2]
level: 900
type: "Swagger Error"
description: "Data does not match any schemas from 'anyOf'"
lineNumber: 43
The full swagger specification file is as follows (the field in question is DataSetsInquiryRsp.dataSets.dataSet):
swagger: '2.0'
info:
title: My API
description: My Awesome API
version: 1.0.0
paths:
/dataSetsInquiry:
get:
description: Retrieve one or more data-sets.
parameters:
- name: ids
in: query
description: List of identifiers of requested data-sets.
required: true
type: array
items:
type: string
responses:
'200':
description: Requested data-sets.
schema:
$ref: '#/definitions/DataSetsInquiryRsp'
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
definitions:
DataSetsInquiryRsp:
type: object
additionalProperties: false
properties:
sessionIdentifier:
description: Identifier of the secure session with the server.
type: number
dataSets:
type: object
additionalProperties: false
properties:
id:
type: string
dataSet:
type: array
items:
oneOf:
- $ref: '#/definitions/Customer'
- $ref: '#/definitions/Merchant'
Customer:
type: object
additionalProperties: false
properties:
firstName:
description: First name of the customer
type: string
lastName:
description: Last name of the customer
type: string
Merchant:
type: object
additionalProperties: false
properties:
code:
description: Code the Merchant.
type: string
name:
description: Name of the Merchant.
type: string
Well, the issue is simply the fact that Swagger doesn't support oneOff. In fact, Swagger supports a subset of Json-Schema and add a few things (datatype file for instance).
What is bad here is the error returned by Swagger. It doesn't help match. And this is the case of all Json-Schema validators I have worked with so far: is-my-json-valid, jsen.