'Templating' on Swagger API Description with yaml - json

Is it possible to use templating w/ swagger.
How is it done.
I do not want to repeat the three properties time, len and off each time.
Have a look at the end of this post where I made up a 'template' for explanation.
More Detail:
I have a JSON response structure which always returns a JSON always with the same properties but only the content of data is subject to change.
data could be an array, could be a string, a number, null or an object.
That depends on the Api's function handling.
{
time: "2019-02-01T12:12:324",
off: 13,
len: 14,
data: [
"Last item in the row of 14 items :-)"
]
}
See at the end of this post for my example of the Swagger definition.
It is a yaml which can be pasted into the swagger editor at https://editor.swagger.io/
In the swagger documentation (yaml) I do not want to repeat the statically reoccuring items, which will not change in their functionality for any other request.
Let me know, if the question is not precisely enough to understand.
swagger: "2.0"
info:
description: ""
version: 1.0.0
title: "Templating?"
contact:
email: "someone#somewhere.com"
host: localhost
basePath: /api
paths:
/items:
get:
summary: "list of items"
produces:
- application/json
responses:
200:
description: "successful operation"
schema:
$ref: "#/definitions/Items"
/item/{id}:
get:
summary: "specific item"
produces:
- application/json
parameters:
- name: id
in: path
description: "ID of the demanded item"
required: true
responses:
200:
description: "successful operation"
schema:
$ref: "#/definitions/Item"
definitions:
Items:
type: object
description: ""
properties:
time:
type: string
format: date-time
description: "date-time of the request"
off:
type: integer
description: "index 0 based offset of list data"
default: 0
len:
type: integer
description: "overall amount of items returned"
default: -1
data:
type: array
items:
$ref: "#/definitions/ListingItem"
Item:
type: object
description: ""
properties:
time:
type: string
format: date-time
description: "date-time of the request"
off:
type: integer
description: "index 0 based offset of list data"
default: 0
len:
type: integer
description: "overall amount of items returned"
default: -1
data:
$ref: "#/definitions/InfoItem"
ListingItem:
type: integer
description: "ID of the referenced item"
InfoItem:
type: object
properties:
id:
type: string
text:
type: string
Based on #Anthon's answer it came to my mind that this is somewhat the construct I would need. Actually it is inheriting from a 'template':
...
templates:
AbstractBasicResponse:
properties:
time:
type: string
format: date-time
description: "date-time of the request"
off:
type: integer
description: "index 0 based offset of list data"
default: 0
len:
type: integer
description: "overall amount of items returned"
default: -1
definitions:
Items:
type: object
extends: AbstractBasicResponse
properties:
data:
type: array
items:
$ref: "#/definitions/ListingItem"
Item:
type: object
extends: AbstractBasicResponse
properties:
data:
$ref: "#/definitions/InfoItem"
ListingItem:
type: integer
description: "ID of the referenced item"
InfoItem:
type: object
properties:
id:
type: string
text:
type: string
...

You might not have to revert to full templating, there are two things within YAML that help with "undoubling" recurring data: anchors/aliases and merge keys.
An example of an anchor (introduced by &) referenced by an alias (*) would be:
definitions:
Items:
type: object
description: ""
properties:
time:
type: string
format: date-time
description: "date-time of the request"
off: &index
type: integer
description: "index 0 based offset of list data"
default: 0
len: &amount
type: integer
description: "overall amount of items returned"
default: -1
data:
type: array
items:
$ref: "#/definitions/ListingItem"
Item:
type: object
description: ""
properties:
time:
type: string
format: date-time
description: "date-time of the request"
off: *index
len: *amount
data:
A YAML parser needs to be able to handle this, but since the alias points to the same object after loading, the code using the data might no longer work the same because of side effect in some cases depending on how the loaded data is processed.
You can have multiple aliases referring to the same anchor.
The merge key (<<) is a special key in a mapping with which you can pre-load that mapping where it occurs with a bunch of key-value pairs. This is most effective when used with a anchor/alias. With that you you some finer control and you could do:
len: &altlen
type: integer
description: "overall amount of items returned"
default: -1
and then
len:
<<: &altlen
default: 42
Which would then be the same doing:
len:
type: integer
description: "overall amount of items returned"
default: 42
Merge keys are normally resolved at load time by the YAML parser, so there are no potential side-effects when using those even though they involve anchors and aliases.

Related

Is the Ansible Inventory compatible with OpenAPI standards?

I´m trying to specify the API used by ansibles dynamic inventory.
Does anyone have experience with solving the incompatibility?
For a single host it might work as following:
The Ansible json output will be like this:
{
"ansible_host": "172.16.19.123",
"proxy": "somehost.domain.fake"
}
The openapi.yml
paths:
/api/inventory/host/:
get:
summary: Gets One Ansible Host
parameters:
- in: query
name: hostname
schema:
type: string
required: true
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SingleHost'
components:
schemas:
SingleHost: # A Single Host for ansible-inventory --host [hostname] requests
type: object
properties:
ansible_host:
type: string
description: IP-Address of the host
ansible_port:
type: integer
description: ssh-port number of host
But when specifiyng the endpoint for ansible-inventory --list it gets tricky already..
example of a ansible inventory yml
{
"HostGroupName-A": {
"hosts": [
"Host-A"
]
},
"HostGroupName-B": {
"hosts": [
"Host-A",
"Host-B"
]
}
}
Should I just avoid using openapi to specify this?

Swagger error : Structural error What should I modify?

I am building an API documentation, I`m new to programming in general and this is the first time I work in Swagger.
This is my code:
/stareMesaj:
post:
tags:
- Metode disponibile
summary: Stare mesaj
description: Call de verificat stare mesaj. More soon.
operationId: stareMesaj
responses:
'200':
description: Ultimele 60 zile - Nu aveti drept de interogare stare pentru mesaj
headers:
X-Rate-Limit:
description: calls per hour allowed by the user
schema:
type: integer
format: int32
X-Expires-After:
$ref: '#/components/headers/ExpiresAfter'
content:
application/json:
schema:
type: string
examples:
response:
value: Hello world!
application/xml:
schema:
type: string
text/csv:
schema:
type: string
On the responses tag I get the following error:
Structural error at paths./stareMesaj.post.responses
should only have three-digit status codes, `default`, and vendor extensions (`x-*`) as properties
Jump to line 395
I don't know what to do. I googled the error but can't find it. What is the problem here?

OpenApi required property in nested objects not working

I need to describe an api having in request body an object with required fields and one of these fields it's an object itself having another set of required fields.
I'm using open api v3 and swagger editor (https://editor.swagger.io/)
After i put my .yaml file onto the editor I generate an html client (> generate client > html). Then I open the static page index.html generated in the .zip file obtaning this schema:
Table of Contents
body
secureoauthservicesv2Nested_nestedobj
body
id
Integer id of nested obj
nestedobj
secureoauthservicesv2Nested_nestedobj
secureoauthservicesv2Nested_nestedobj
nested object
field1 (optional)
String
field2 (optional)
String
I expect field1 to be required and field2 to be optional but it's not.
This is my .yaml file
openapi: 3.0.0
info:
title: Example API
description: Example API specification
version: 0.0.1
servers:
- url: https://example/api
paths:
/secure/oauth/services/v2/Nested:
post:
summary: Try nested
description: Used to post Nested obj
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- id
- nestedobj
properties:
id:
type: integer
description: id of nested obj
nestedobj:
type: object
required:
- field1
description: nested object
properties:
field1:
type: string
field2:
type: string
responses:
'200':
description: Nested object OK
Solved!
I used components and schemas, but I think this could be a bug, opened an issue on swagger editor repo:
https://github.com/swagger-api/swagger-editor/issues/1952
openapi: 3.0.0
info:
title: Example API
description: Example API specification
version: 0.0.2
servers:
- url: https://example/api
paths:
/secure/oauth/services/v2/Nested:
post:
summary: Try nested
description: Used to post Nested obj
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- id
- nestedobj
properties:
id:
type: integer
description: id of nested obj
nestedobj:
$ref: '#/components/schemas/nestedobj'
responses:
'200':
description: Nested object OK
components:
schemas:
element:
type: object
required:
- fieldArray1
properties:
fieldArray1:
type: string
description: field array
fieldArray2:
type: number
nestedobj:
type: object
required:
- field1
description: nested object
properties:
field1:
$ref: '#/components/schemas/woah'
field2:
type: string
woah:
type: object
required:
- woahthis
description: woah this
properties:
field3:
type: array
items:
$ref: '#/components/schemas/element'
woahthis:
type: number
description: numeber woah this
EDIT 23/08/21:
I opened a bug in swagger-codegen github in april 2019 but it still has no response whatsoever

Incorporate JSON into YAML with Indentation

I'm trying to incorporate a JSON into a YAML file.
The YAML looks like this:
filebeat.inputs:
- type: log
<incorporate here with a single level indent>
enabled: true
paths:
Suppose you have the following variable:
a = { processors: { drop_event: { when: { or: [ {equals: { status: 500 }},{equals: { status: -1 }}]}}}}
I want to incorporate it into an existing YAML.
I've tried to use:
JSON.parse((a).to_json).to_yaml
After applying this, I got a valid YAML but without indentation (all lines have to be indented) and with a "---" which is Ruby's new document in YAML.
The result:
filebeat.inputs:
- type: log
---
processors:
drop_event:
when:
or:
- equals:
status: 500
- equals:
status: -1
enabled: true
The result I'm looking for:
filebeat.inputs:
- type: log
processors:
drop_event:
when:
or:
- equals:
status: 500
- equals:
status: -1
enabled: true```
It’s easier to produce a valid ruby object by merging hashes and then serialize the result to YAML than vice versa.
puts(yaml.map do |hash|
hash.each_with_object({}) do |(k, v), acc|
# the trick: we insert before "enabled" key
acc.merge!(JSON.parse(a.to_json)) if k == "enabled"
# regular assignment for all hash elements
acc[k] = v
end
end.to_yaml)
Results in:
---
- type: log
processors:
drop_event:
when:
or:
- equals:
status: 500
- equals:
status: -1
enabled: true
JSON.parse(a.to_json) basically converts symbols to strings.
In order to do that first you need to convert your original YAML into JSON
original = YAML.load(File.read(File.join('...', 'filebeat.inputs')))
# => [
{
"type": "log",
"enabled": true,
"paths": null
}
]
Then you have to merge your JSON into this original variable
original[0].merge!(a.stringify_keys)
original.to_yaml
# =>
---
-
type: log
enabled: true
paths:
processors:
drop_event:
when:
or:
- equals:
status: 500
- equals:
status: -1

Swagger POST Json Body Parameter Schema YAML

i'm working on a RESTful API using swagger-api and swagger-editor for routes.
I can't figure out why the JSON i am sending through body, never reaches my controller.
here is my YAML
schemes:
- http
- https
produces: [application/json, multipart/form-data, application/x-www-form-urlencoded]
paths:
/projects:
x-swagger-router-controller: project
post:
description: create a new project
operationId: postProjects
consumes:
- application/json
parameters:
- name: param1
in: body
description: description
required: false
schema:
$ref: "#/definitions/Project"
responses:
"200":
description: Success
schema:
$ref: "#/definitions/Project"
default:
description: Error
schema:
$ref: "#/definitions/ErrorResponse"
definitions:
Project:
properties:
name:
type: string
required:
- name
an example of the post request i'm sending.
curl -v -X POST -H "Content-Type: application/json" -d '{"name":"test"}' http://127.0.0.1:10010/projects
and the response
{"message":"Request validation failed: Parameter (param1) failed schema validation","code":"SCHEMA_VALIDATION_FAILED","failedValidation":true,"results":{"errors":[{"code":"OBJECT_MISSING_REQUIRED_PROPERTY","message":"Missing required property: name","path":[]}],"warnings":[]},"path":["paths","/projects","post","parameters","0"],"paramName":"param1"}
If i set the parameter "name" as not required, i just received an empty response like this
{ param1:
{ path: [ 'paths', '/projects', 'post', 'parameters', '0' ],
schema:
{ name: 'param1',
in: 'body',
description: 'description',
required: false,
schema: [Object] },
originalValue: {},
value: {} } }
I have no clue why since other format such as header, path or formdata works fine.
I always receive an empty object. req.swagger.params has no value.
I tried several schema but even the simplest is not working.
i can tell from the header that 'content-type': 'application/json'.
So the content type is set, the schema validates a simple string argument named "name". Everything should be ok. but still not.
This issue has been fixed.
It wasn't swagger related.
I built an API with nodeJs and i realized i didn't have a middleware to handle body parameter.
So because i missed a step before enabled the swagger middleware, i couldn't do anything with body parameter.
The main reason behind getting null values when sending json data to the API backend is the paramater path most of the time you give and the naming you give the parameter.
You also have to declare explicitly the type of schema you expect
You have to set parameter name to body and set in: body so that it picks the body object as a JSON
An example is here. You can try it out
/auth/register:
post:
tags:
- Auth
parameters:
- in: body
name: user
description: Create a new user.
schema:
type: object
required:
- firstName
- lastName
- email
- password
- confirmPassword
properties:
firstName:
type: string
lastName:
type: string
email:
type: string
password:
type: string
confirmPassword:
type: string
example:
firstName: Jane
lastName: Doe
email: janedoe#gmail.com
password: pass
confirmPassword: pass
responses:
"200":
description: OK