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.
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
...
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 got this swagger description:
APIExperimentProperties:
type: object
properties:
name:
type: "string"
experimentVariants:
type: array
items:
$ref: "#/definitions/APIExperimentVariantProperties"
owner:
type: "string"
label:
type: "string"
ticketId:
type: "string"
featureName:
type: "string"
APIExperimentVariantProperties:
type: object
required:
- id
- name
properties:
id:
type: "integer"
format: "int64"
name:
type: "string"
I wanted to make the fields id and name in APIExperimentVariantProperties to be marked as required in the swagger-ui.
As you can see in the picture, it looks like it worked.
It removed the optional from the field, and their labels are bolder
(the id field vs the featureName field for example).
But I am trying to make their placeholder say that it's required field, just like it did automatically for the field in the image below.
Any idea how to do that? the decimation didn't give any additional leads.
Suppose that I have this definition in a yaml OpenApi definition
definitions:
User:
description: "User"
type: "object"
properties:
firstname:
type: "string"
lastname:
type: "string"
password:
type: "string"
email:
type: "string"
username:
type: "string"
If in a parameters specification I need specific fields of a definition how can I refer them without defining another model as below?
definitions:
UserLogin:
description: "User"
type: "object"
properties:
password:
type: "string"
email:
type: "string"
In your question, you are using definitions keyword what hints that your question is about OpenAPI v2 aka. Swagger. For OpenAPI v3, definitions provided below should be defined inside appropriate Components Object sections.
In order to achieve this, you have to use Composition with keyword allOf. There is a great example that relates to your question here. First, you have to define a smaller object and then include it into the definition of a larger as follows:
definitions:
UserLogin:
description: User Login
type: object
properties:
password:
type: string
email:
type: string
User:
allOf:
- $ref: '#/definitions/UserLogin'
- description: User
type: object
properties:
firstname:
type: string
lastname:
type: string
username:
type: string
It is worth noting that:
Some lighter implementations may not support allOf keyword.
Using composition may increase or decrease the readability of the documentation, depending on the complexity and choice of words used to name the schemas.
In my API i would like to have a simple model for my collection and a more elaborate model for my individual resource. For example:
a GET request on /libraries should return
BaseLibrary:
type: object
properties:
library_id:
type: string
description: The id of the library
display_name:
type: string
description: Name of the library
href:
type: string
description: The URI linking to this library.
whilst a request to a specific library should return all of the above including an extra parameter books:
So a GET request to libraries/{library_id} should return:
ExtendedLibrary:
type: object
properties:
library_id:
type: string
description: The id of the library
display_name:
type: string
description: Name of the library
href:
type: string
description: The URI linking to this library.
books:
type: array
description: The books in this library
items:
$ref: "#/definitions/books"
I would very much like to not have to define a "BaseLibrary" twice and would want to simple model an additional "ExtendedLibrary" which contains all the responses of a base library and the additional books property.
I tried a lot of different things, with the closest to succes being the following definitions:
definitions:
BaseLibrary:
type: object
properties:
library_id:
type: string
description: The id of the library.
display_name:
type: string
description: Name of the library
href:
type: string
description: The URI linking to this library.
ExtendedLibrary:
type: object
properties:
$ref: "#/definitions/BaseLibrary/properties"
books:
type: array
description: The available books for this library.
items:
$ref: "#/definitions/Book"
However this gives me a "Extra JSON reference properties will be ignored: books" warning and the output seems to ignore this extra property. Is there a clean way to handle my problem? Or am I just going to have to copy paste my whole BaseLibrary model into my ExtendedLibrary model?
As mentioned in the comments section, this may be a duplicate of another question, but it's worth repeating the answer in the context of this particular example. The solution is to use the allOf property in the definition of ExtendedLibrary:
definitions:
Book:
type: object
properties:
title:
type: string
author:
type: string
BaseLibrary:
type: object
properties:
library_id:
type: string
description: The id of the library
display_name:
type: string
description: Name of the library
href:
type: string
description: The URI linking to this library.
ExtendedLibrary:
type: object
allOf:
- $ref: '#/definitions/BaseLibrary'
- properties:
books:
type: array
description: The books in this library
items:
$ref: "#/definitions/Book"
In my experience, Swagger UI visualizes this correctly. When I define an operation response to be ExtendedLibrary Swagger UI shows this example:
{
"library_id": "string",
"display_name": "string",
"href": "string",
"books": [
{
"title": "string",
"author": "string"
}
]
}
Also, Swagger Codegen does the right thing. At least when generating a Java client, it creates an ExtendedLibrary class that correctly extends BaseLibrary.