Reusing references to models but redefining required properties - swagger

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.

Related

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'

Swagger YAML specification, conditionally mandatory field

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

Creating an extendible model using Swagger/ OpenAPI

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.

How to declare a collection resource model in swagger 2.0?

I'm not sure about how I should do the declaration of the model for a collection resource on swagger.
The spec is missing some examples, so I'm guessing something like this:
Class:
required:
- id
- name
properties:
id:
type: string
name:
type: string
ClassList:
type: array
items:
type:
schema:
$ref: Class
Then swagger editor shows the following generated documentation:
Is it correct?
I think you got the answer to this one already, but even for the sake of documentation:
ClassList:
type: array
items:
$ref: #/definitions/Class

Resources