Given the following OpenAPI definition
Person:
required:
- id
type: object
properties:
id:
type: string
Which of the below objects are valid? Just 1. or 1. & 2.?
{"id": ""}
{"id": null}
{}
This boils down to the question whether "required = true" means "non-null value" or "property must be present".
The JSON schema validator at https://json-schema-validator.herokuapp.com/ says that 2. be invalid because null doesn't satisfy the type: string constraint. Note that it doesn't complain because id is null but because null is not a string. BUT how relevant is this for OpenAPI/Swagger?
The required keyword in OpenAPI Schema Objects is taken from JSON Schema and means:
An object instance is valid against this keyword if every item in the [required] array is the name of a property in the instance.
In other words, required means "property must be present", regardless of its value. The type, format, etc. of the property value are separate constraints that are evaluated separately from required, but together as a combined schema.
In your example:
{"id": ""} is valid:
✓ validates against required
✓ the value "" validates against type: string
{"id": null} is NOT valid:
✓ validates against required
✗ null does NOT validate against type: string (see the notes about nulls below)
{} is NOT valid:
✗ does NOT validate against required
Note that 'null' as a type is not supported in OpenAPI 2.0 but is supported in OpenAPI 3.1, and 3.0 has nullable to handle nulls. So, {"id": null} is valid against this OpenAPI 3 schema:
Person:
required:
- id
type: object
properties:
id:
# OAS 3.1
type: [string, 'null']
# OAS 3.0
# type: string
# nullable: true
Related
I have this OpenAPI schema:
components:
schemas:
User:
type: object
required:
- username
- email
- description
- profile_icon
properties:
username
type: string
email
type: string
format: email
description
type: string
profile_icon
type: string
All of properties are required. This is for PUT /user/profile.
I will change this to PATCH /user/profile.
Users send parameters just they only request to change.
System validates that at least one parameter is required.
How can I describe one of [username, email, description, profile_icon] is required?
Acceptable example requests:
{
"username": "Will Smith"
}
{
"email": "test#example.com",
"profile_icon": "aaaaa.png"
}
Not acceptable example (error):
{}
The anyOf annotator is close but it seems to be only for $ref schemas, not properties.
https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/
The easiest way to require at least 1 property in an object is to use minProperties: 1.
User:
type: object
minProperties: 1 # <--------
properties:
username:
type: string
...
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).
Using Open API 3.0.1, I'm trying to describe a query parameter of type "integer", lets say we call it ids, that can be alone or can be an array.
For example:
/my-endpoint?ids=111
or
/my-endpoint?ids=111&ids=222
I did try:
- name: ids
in: query
required: false
schema:
type: array
items:
type: integer
And I understand that style: form and explode: true are the default.
But when I validate this with actual requests (I use express-openapi-validate which is a wrapper around Ajv), I get those errors:
/my-endpoint?ids=111
"Error while validating request: request.query.ids should be array"
/my-endpoint?ids=111&ids=222&ids=333
"Error while validating request: request.query.ids[0] should be integer"
And If I use "string" instead of "integer":
- name: ids
in: query
required: false
schema:
type: array
items:
type: string
/my-endpoint?ids=111
"Error while validating request: request.query.ids should be array"
/my-endpoint?ids=111&ids=222&ids=333
Valid!
How should I describe this ids parameter, which must be integer values?
UPDATE: I now understand that any query parameters will be a string when deserialized by an Express server (which I use). But I'm still unable to have a single element array to work!
After the comment from #Helen, I did try another validation library, express-openapi-validator and now it works well with:
- name: ids
in: query
required: false
style: form
explode: true
schema:
type: array
items:
type: integer
With express-openapi-validate, the only way I've been able to make it work is using:
- name: ids
in: query
required: false
schema:
anyOf:
- type: array
items:
type: string
pattern: '^\d+$'
- type: string
pattern: '^\d+$'
So I suggest you use express-openapi-validator with an Express server.
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
I have a series of parameters in Swagger like this
"parameters": [
{
"name": "username",
"description": "Fetch username by username/email",
"required": false,
"type": "string",
"paramType": "query"
},
{
"name": "site",
"description": "Fetch username by site",
"required": false,
"type": "string",
"paramType": "query"
},
{
"name": "survey",
"description": "Fetch username by survey",
"required": false,
"type": "string",
"paramType": "query"
}
],
One parameter MUST be filled out but it doesn't matter which one, the others can be left blank. Is there a way to represent this in Swagger?
Mutually exclusive parameters are possible (sort of) in OpenAPI 3.x:
Define the mutually exclusive parameters as object properties, and use oneOf or maxProperties to limit the object to just 1 property.
Use the parameter serialization method style: form and explode: true, so that the object is serialized as ?propName=value.
An example using the minProperties and maxProperties constraints:
openapi: 3.0.0
...
paths:
/foo:
get:
parameters:
- in: query
name: filter
required: true
style: form
explode: true
schema:
type: object
properties:
username:
type: string
site:
type: string
survey:
type: string
minProperties: 1
maxProperties: 1
additionalProperties: false
Using oneOf:
parameters:
- in: query
name: filter
required: true
style: form
explode: true
schema:
type: object
oneOf:
- properties:
username:
type: string
required: [username]
additionalProperties: false
- properties:
site:
type: string
required: [site]
additionalProperties: false
- properties:
survey:
type: string
required: [survey]
additionalProperties: false
Another version using oneOf:
parameters:
- in: query
name: filter
required: true
style: form
explode: true
schema:
type: object
properties:
username:
type: string
site:
type: string
survey:
type: string
additionalProperties: false
oneOf:
- required: [username]
- required: [site]
- required: [survey]
Note that Swagger UI and Swagger Editor do not support the examples above yet (as of March 2018). This issue seems to cover the parameter rendering part.
There's also an open proposal in the OpenAPI Specification repository to support interdependencies between query parameters so maybe future versions of the Specification will have a better way to define such scenarios.
Unfortunately this isn't possible currently. "required" is just a boolean and there's no way to represent interdependencies between parameters.
Best you can do is make clear the requirements in the parameter descriptions and also put in a custom 400 Bad Request description along the same lines.
There's a bit of discussion at https://github.com/OAI/OpenAPI-Specification/issues/256 about possible ways of implementing this in the next version of the OpenAPI Specification.
What about changing your API design ?
Currently you have one method, 3 parameters. If I understand well, user must always provide exactly one parameter, and two remaining ones must be unset.
For me, API would be more usable with three endpoints -like
/user/byName?name=
/user/bySite?name=
/user/bySurvey?name=
An alternative is to pass in a filter type parameter with an enum, and a filter value with the value to use.
Example at: https://app.swaggerhub.com/api/craig_bayley/PublicAPIDemo/v1
It can be required or not, as you choose. However if one is required, they should both be.