I'm working on a schema in OpenAPI with Swagger, and I'm not sure if I'm misusing the $ref element. I have a User model and a Project model, similar to something like
User:
type: object
properties:
id:
type: string
format: uuid
name:
type: string
...
Project:
type: object
properties:
id:
type: string
user_id:
$ref: "#/components/schemas/User"
...
I don't see much in the Open API Spec documentation as to what the $ref element is specifically, but in the JSON Schema documentation—of which Open API extends the $ref element—I've found the following description of the item:
The easiest way to describe $ref is that it gets logically replaced with the thing that it points to.
In the case above, I just want to refer to the user who posted a project. It seems unnecessary to essentially include all of the information about the User in the Project model if that is what it's doing. Would it be better practice to just have a string element of the uuid of the user_id? Or is it correct as is? Is it more common to name the field user rather than user_id if that is the case?
Edit:
I realize the heart of what was bothering me is if there were to be recursive references. If a User has an array of $ref to Projects, but also a Project has an array of $ref to Users, the replacement (if that's what it's doing) would infinitely embed each model in the other. I assume this wouldn't happen in practice, assuming that a $ref is just a pointer to the Model?
In your example it might make sense to extract the userId definition into its own schema (assuming it is the userId only that appears, not an entire User object), then it is clearer what's going on:
components:
schemas:
User:
type: object
properties:
id:
$ref: '#/components/schemas/userId'
name:
type: string
...
Project:
type: object
properties:
id:
type: string
user_id:
$ref: "#/components/schemas/userId"
...
userId:
type: string
format: uuid
But there is nothing stopping you creating a direct $ref to #/components/schemas/User/properties/id instead, as long as what is pointed to is a valid OpenAPI schemaObject.
Circular references are allowed by the JSON Reference and OpenAPI specifications, so your analogy of a pointer is a sound one.
Related
I'm building a web api using asp.net webapi 2 and for my documentation i've chosen to go with swagger.
The problem i'm having is that one of my api methods takes a nested array (array of strings inside another array). When testing manually in postman it works great when i type something like this:
http:/localhost:port/setup?array[0]=key1&array[0]=value1&array[1]=key2&array[1]=value2
But in swagger-ui i get one field to type in, with only one parameter per row. And that makes me unable to enter two values for each nested array.
Result should look like this (json):
[["key1","value1"],["key2","value2"]]
Which i have been able to achieve with the query parameters above. While i can only achieve the following in the swagger-ui text field:
[["key1"],["value1"],["key2"],["value2"]]
I have vacuumed the internet for 1-2 hours for an answare but can only find posts asking how to define a nested array in the yaml file. Some errands i've read on the swagger github leaves me thinking that it isn't possible at all. And while it's not a critical feature it would be really nice if all of the tests worked as they should.
So the question is, if possible, how do i type two separate strings in a nested array in swagger-ui.
I am not a pro in any of this stuff. I learned how to use json api's and swagger, all of it this week so please take that in mind while reviewing this.
Thanks in advance!
A nested array is simple:
type: array
items:
type: array
items:
type: string
But OpenAPI/Swagger does not have a way to serialize nested arrays in the query string like in your example.
If the array has a fixed number of items, a possible workaround is to define each nested array as a separate query parameter:
paths:
/setup:
get:
parameters:
- in: query
name: array[0]
required: true
type: array
items:
type: string
collectionFormat: multi # array[0]=key1&array[0]=value1
- in: query
name: array[1]
required: true
type: array
items:
type: string
collectionFormat: multi # array[1]=key2&array[1]=value2
A better approach would be to use a POST request and pass the array in the request body:
paths:
/setup:
post:
consumes:
- application/json
parameters:
- in: body
name: body
required: true
schema:
type: array
items:
type: array
items:
type: string
I'm trying to create an example in the request portion of my Swagger file. In the (heavily simplified) snippet below my request is describing the relationship between a patient and a clinician. Both these entities use the same Identifier model.
However, I can't seem to work out how to pass in example data that would be different for the patientIdentifier and the clinicianIdentifier while still making using of the common Identifier model. The snippet I've posted is syntactically correct, but obviously in example data will be the same for both Identifiers, which is far from ideal.
I realise I can just extract the fields from the Identifer model and copy them to the patientIdentifier and clinicianIdentfier without too much effort in this case, but I'd like to know if there is a more elegant way to achieve this.
Relationship:
properties:
patientIdentifier:
$ref: '#/definitions/Identifier'
clinicianIdentifier:
$ref: '#/definitions/Identifier'
Identifier:
type: object
properties:
id:
type: string
example: "Jane Doe"
group:
type: string
example: "WD7"
If someone could point me in the right direction with something along the lines of documentation or an example that does this, it would be really appreciated.
Thanks!
You'll need to provide a schema-level example for Relationship. Schema-level examples take precedence over property-level examples.
Relationship:
type: object
properties:
patientIdentifier:
$ref: '#/definitions/Identifier'
clinicianIdentifier:
$ref: '#/definitions/Identifier'
example:
patientIdentifier:
id: Jane Doe
group: WD7
clinicianIdentifier:
id: Bob Smith
group: ABCDE
Note that property-level examples for patientIdentifier and clinicianIdentifier won't work because when using a $ref, any siblings of a $ref are ignored.
# This won't work - examples will be ignored
Relationship:
type: object
properties:
patientIdentifier:
$ref: '#/definitions/Identifier'
example:
id: Jane Doe
group: WD7
clinicianIdentifier:
$ref: '#/definitions/Identifier'
example:
id: Bob Smith
group: ABCDE
I'm using Swagger online editor. I create some model in definition
Object:
type: object
properties:
id:
type: integer
format: int32
name:
type: string
some_variable_to_exclude:
type: string
This is complete model and i use it in different responses. But in one of they i want my model dont present property "some_variable_to_exclude". How can i exclude it ? Is it possible ?
Okay, maybe not exclude, maybe some comment near property, but only for that response.
In order to extend the Swagger Schema with custom objects, the field must begin with x-
See more here: http://swagger.io/specification/#vendorExtensions
For example, swagger-node uses x-swagger-router-controller to specify which controller to use for a particular API path. But you can use it for whatever you need, e.g., x-custom-property
Create another definition without field you don't need
Create definition without field you don't need, like
components:
schemas:
Object:
type: object
properties:
id:
type: integer
format: int32
name:
type: string
then, on describe new full scheme, write this:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/Object'
- properties:
some_variable_to_exclude:
type: string
I'm beginner of Swagger, and I'm trying to define endpoints which have:
Some read-only properties that isn't allowed in request but show in response
Some white-only properties and hidden that allowed in request but not show in response
Some properties only on collection level at /resources, but some other additional details on /resources/resource-id
What I'm doing is defining the following models:
ResourceBaseModel: this saves all the shared properties of all
ResourceBaseModel:
type: object
properties:
shared_properties:
type: string
ResourceCollectionResponse: this is wrapping the response additional properties
ResourceCollectionResponse:
type: array
items:
type: object
allOf:
- $ref: ResourceBaseModel
- type: object
properties:
collection_normal_properties:
type: string
collection_read_only_properties:
type: string
readOnly: true
ResourceDetailResponse: this is adding different properties for response
ResourceDetailResponse:
type: object
allOf:
- $ref: ResourceBaseModel
- type: object
properties:
detail_normal_properties:
type: string
detail_read_only_properties:
type: string
readOnly: true
ResourceRequest: same, add additional and write-only properties
ResourceRequest:
type: object
allOf:
- $ref: ResourceBaseModel
- type: object
properties:
request_write_only_properties:
type: string
This is making every model defined 4 times and I feel it's not efficient.
So here are my questions:
I saw there is a discriminator in Swagger Spec. Should I use this with "allOf" of these extended models? From result, using of not using this discriminator, the result looks the same as long as "allOf" used.
the "readOnly", if defined in base level, still shows in Swagger UI and needs special handling or filtering when using or generating docs. The demo data in request is also showing these readOnly properties in Swagger UI request (but only the model added a label of "read-only"). Is there any better solution besides what I'm trying.
the "white-only", as far as I know, is not supported. Is defining a new model the only way?
I wonder if there will be one day I can define only one model to describe all of the models, or do you think an innovative language that can compile to Swagger YAML can benefit the whole community? Like how Sass/LESS builds CSS?
Thanks for your help and insightes!
OpenAPI 3.0.x supports writeOnly as well as readOnly schema properties. This should allow you to simplify your models, the allOf / discriminator should not be necessary.
I am defining my response as per:
responses:
200:
description: 'An authProvider object'
schema:
type: 'array'
items:
$ref: '#/definitions/AuthProvider'
Is it possible for me to directly attach another field or 2 in the response? I would like to add something that I don't want to put into AuthProvider
Whenever $ref is used, it's basically using JSON Reference. By definition (see linked spec), anything defined within the same object as the reference itself is ignored. As such, it is not possible to directly add additional properties alongside with the $ref definition.
That said, you can use composition to 'combine' two objects into one. This is done using the allOf JSON Schema directive. A simple example for such a definition would look like this:
allOf:
-
$ref: "..."
-
properties:
property1:
type: "string"
property2:
type: "boolean"
Note 1: The various Swagger tools out there may not fully support composition at the moment.
Note 2: As always, it's normally better to externalize the definition (even if used only once) rather than being defined inline.