Swagger YAML specification, conditionally mandatory field - swagger

I want to create YAML schema for REST POST service.
requestBody:
description: Details of the request
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateCompanyRequest'
components:
schemas:
CreateCompanyRequest:
description: The requested data to the service.
type: object
required:
- address
- identifier
- type
And how to specify, that the identifier field is obligatory only if type field value = "BIG" ?
Or maybe the identifier field shouldn't be on requried list, instead I should add some comments somewhere to let third party know about the api requirements?

OpenAPI 3.0 allows us to define a property as a discriminator in the model that represents your superset. You can then create additional models representing your subsets that can change the required flags of your model properties. Example here from Swaggers documentation https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/:
Pet:
type: object
required:
- pet_type
properties:
pet_type:
type: string
discriminator:
propertyName: pet_type
Dog: # "Dog" is a value for the pet_type property (the discriminator value)
allOf: # Combines the main `Pet` schema with `Dog`-specific properties
- $ref: '#/components/schemas/Pet'
- type: object
# all other properties specific to a `Dog`
required:
- bark
properties:
bark:
type: boolean
breed:
type: string
enum: [Dingo, Husky, Retriever, Shepherd]
Cat: # "Cat" is a value for the pet_type property (the discriminator value)
allOf: # Combines the main `Pet` schema with `Cat`-specific properties
- $ref: '#/components/schemas/Pet'
- type: object
# all other properties specific to a `Cat`
properties:
hunts:
type: boolean
age:
type: integer

Related

Which is the correct allOf syntax for OpenAPI 3 schema inheritance?

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

How to define an OpenAPI ordered array of different objects?

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.

How to specify required properties on a requestBody object in an openApi project?

I have a yaml file for an openapi project that looks something like this...
components:
schemas:
Thing:
type: object
properties:
id:
type: integer
prop1:
type: string
prop2:
type: string
prop3:
type: string
paths:
/things:
summary: Create a thing
requestBody:
description: Thing object
content:
application/json:
schema:
$ref: '#/components/schemas/Thing'
required: true
/things/{id}:
put:
summary: Update a thing
parameters:
- name: id
in: path
description: Thing id
schema:
type: integer
requestBody:
description: Thing object
content:
application/json:
schema:
$ref: '#/components/schemas/Thing'
required: true
I am wondering:
Is there a way to configure this so that when creating a "thing" the list of required properties is different than when "updating" a thing? (If I add the 'required' definition to components it affects all paths the same.)
For example, when creating a thing, prop1, prop2, and prop3 may all be required. But when updating a thing, only prop1 may be required.
Thanks!
You can accomplish this by using specific objects for "update" and "create", which inherit from Thing and impose the restrictions you need:
Thing:
type: object
required:
- id
additionalProperties: false
properties:
id:
type: integer
prop1:
type: string
prop2:
type: string
prop3:
type: string
ThingUpdate:
allOf:
- $ref: '#/components/schemas/Thing'
minProperties: 2
ThingCreate:
allOf:
- $ref: '#/components/schemas/Thing'
required:
- prop1
- prop2
- prop3
You then obviously need to modify your POST /things endpoint to take a ThingUpdate in the request body rather than just a Thing, and likewise for PUT /things/{id} and ThingCreate.
To accomplish the "update" validation, notice that in the Thing base object I specified additionalProperties: false and in ThingUpdate I specified minProperties: 2; that enforces that there need to be at least 2 properties in the "Thing", with no properties other than id, prop1, prop2 and prop3 (I assumed id is required, so I put it at the top of Thing).
For the "create" validation, I simply inherited from the Thing base class and specified that all of the non-id properties are required.
There's some redundancy here with the id parameter, since it's not needed for "create", and for "update" it's specified in the path rather than in the request body. You could remove id from the Thing base object and modify minProperties in ThingUpdate to instead be 1. Then, assuming you intend to add a separate GET /things endpoint where you want to retrieve all Things from your database or wherever, you can create a separate derived object ThingGet that includes allOf: Thing and adds the additional id property (under a properties: object).

swagger docs doesnt reflect object value

I defined in swagger editor the following object(amountInCurrency):
pot:
type: object
properties:
potId:
type: string
name:
type: string
amount:
type: number
status:
type: string
lastChangeTimestamp:
type: string
amountInCurrency:
type: object
items:
$ref: '#/definitions/amountInCurrency'
and
amountInCurrency:
type: object
properties:
currency:
type: string
amount:
type: number
I got in that items currency and amount. however on swagger visualization i see amountInCurrency as an empty map. any idea why?
i would expect to see the currency and amount fields describe in it
-- edit:
if I change the AmountInCurrency type from object to array I do see the internal docs within the doc
doing this:
amountInCurrency:
type: array
items:
$ref: '#/definitions/amountInCurrency'
The amountInCurrency property definition does not look right - it mixes type: object with an array-specific keyword items.
If the amountInCurrency property is an array of amountInCurrency objects, use type: array instead:
amountInCurrency:
type: array # <-----
items:
$ref: '#/definitions/amountInCurrency'
If the amountInCurrency property is an object (an instance of the amountInCurrency object), use $ref like so:
amountInCurrency:
$ref: '#/definitions/amountInCurrency'

Reusing references to models but redefining required properties

There's a year old question with a similar title here, but the
only answer does not answer the question fully so hoping that things
have changed since I'm trying again.
I've a model of a Car which I would like to use with GET requests for creating new instances in which I require to have name.
Additionally, I would like to use this with PUT requests to update an existing instance. Update requests should not have all properties, the Car identifier is already specified in the path and only the present properties would be 'merged' into the existing object.
I tried using the following spec:
CarProperties:
type: object
properties:
name:
type: string
title: Car Name
description: A friendly name for this car.
minLength: 1
md:
type: object
hidden: 'true'
description:
type: string
title: Car Description
description: A detailed description of the car.
md:
type: object
hidden: 'true'
UpdateCar:
allOf:
- { $ref: '#/definitions/CarProperties' }
type: object
NewCar:
allOf:
- { $ref: '#/definitions/CarProperties' }
type: object
required: [name]
I expected having name a required property only in NewCar, however in both NewCar and UpdateCar the property name is optional.
How can I specify a subset of required properties from a referenced model?
After fiddling for a while it seems like I had bad indentation for the required field. The following syntax seems to produce what I wanted:
CarProperties:
type: object
properties:
name:
type: string
title: Car Name
description: A friendly name for this car.
minLength: 1
md:
type: object
hidden: 'true'
description:
type: string
title: Car Description
description: A detailed description of the car.
md:
type: object
hidden: 'true'
UpdateCar:
allOf:
- { $ref: '#/definitions/CarProperties' }
- type: object
NewCar:
allOf:
- { $ref: '#/definitions/CarProperties' }
- type: object
required: [name]
Now I have:
NewCar entity with a required name field.
UpdateCar entity with an optional name field.
If I want to add another property to Car I only need to do it in one place.

Resources