Reputation: 1270
My resource has an ID (typical case).
It has also a slug, a human-readable but still unique identifier (to beautify the URLs, mainly).
This slug is optional when the resource is created. If the client provides one, it is being used; otherwise, the server generated one.
This slug is however required when the resource is being read.
We do want that distinction to be clear, so any tooling reading that OpenAPI specification knows what to expect exactly.
This could of course be achieved using a mix of different schemas linked with allOf
modifiers (see example below), but I would like to avoid having to perform this composition (assuming it works with tooling in the first place).
So my question is:
Is there a way in OpenAPI >= 3.0.2 to declare a property required-readOnly and optional-writeOnly?
Solution using composition:
openapi: 3.0.2
info:
title: Person API
version: 1.0.0
paths:
'/persons/{person-slug}':
get:
parameters:
- $ref: '#/components/parameters/PersonSlug'
responses:
200:
description: Information on a person.
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/SlugRead'
- $ref: '#/components/schemas/Person'
post:
parameters:
- $ref: '#/components/parameters/PersonSlug'
responses:
200:
description: Information on a person.
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/SlugWrite'
- $ref: '#/components/schemas/Person'
components:
parameters:
PersonSlug:
name: 'person-slug'
description: Human readable unique ID of a person.
required: true
in: path
schema:
type: string
schemas:
SlugRead: # required
required:
- slug
properties:
slug:
type: string
readOnly: true
SlugWrite: # not required
properties:
slug:
type: string
Person:
required:
- first_name
- last_name
- birth_date
properties:
first_name:
type: string
last_name:
type: string
birth_date:
type: string
format: date
Upvotes: 4
Views: 8732
Reputation: 1635
For whomever stumbles upon this question in the future, I solved this using a base schema and derived schemas for specific operations.
Person:
properties:
id:
type: integer
readOnly: true
name:
type: string
slug:
type: string
PersonWRITE:
allOf: # name is required, slug is optional and id is not available
- $ref: '#/components/schemas/Person'
- required: [name]
PersonREAD:
allOf: # everything is available and required
- $ref: '#/components/schemas/Person'
- required: [id, name, slug]
It is essentially the OP's solution, but moved into the components level so that you don't have to use the 'allOf' in the request/response definition.
EDIT: Please note that thhe following will NOT work:
PersonWRITE: #wrong
$ref: '#/components/schemas/Person'
required: [name]
As the documentatin states that Any sibling elements of a $ref are ignored. This is because $ref works by replacing itself and everything on its level with the definition it is pointing at.
The key is the allOf
, which combines all properties of the objects in the list. Upon $ref expansion, the schema looks like this (still a valid yaml, I just jsonified it a bit to point out the that the array elements are, in fact, two objects whose propetries will be combined):
PersonWRITE:
allOf: [
{
properties: {
id: { type: integer, readOnly: true },
name: { type: string },
slug: { type: string }
}
},
{
required: [name]
}
]
Upvotes: 10
Reputation: 2935
Can't see how to achieve exactly what you're after is possible but I think the effect may be definable by using separate fields for the desired (write only) vs assigned (read only) slug:
Person:
required:
- first_name
- last_name
- birth_date
- assigned_slug
properties:
first_name:
type: string
last_name:
type: string
birth_date:
type: string
format: date
assigned_slug:
type: string
readOnly: true
desired_slug:
type: string
writeOnly: true
No idea if any tooling would do the right thing with it though!
Upvotes: 0