How can I specify decimal schema formats in RSpec/RSwag when decimal responses are returned as strings? - ruby-on-rails

So my :decimal formatted DB field is being returned as a string, and making my tests fail.
I have a request spec, using RSwag, that I'm trying to get to pass with a format check, yet it's telling me the following:-
Rswag::Specs::UnexpectedResponse:
Expected response body to match schema: The property '#/budget' of type string did not match the following type: number in schema
My test:-
path '/api/v1/jobs/{id}' do
parameter name: 'id', in: :path, type: :string, description: 'Job id'
get('show job') do
tags 'Jobs'
response(200, 'successful') do
schema type: :object,
properties: {
title: { type: :string },
description: { type: :string },
date: { type: :string, format: 'date-time', nullable: true },
budget: { type: :number, format: 'double' },
awarded: { type: :boolean },
created_at: { type: :datetime},
updated_at: { type: :datetime}
}
let(:id) { #job.id }
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
run_test!
end
end
What is the best way around this? It's a little pointless going to the effort of using Swagger for my API docs if I have to make all the fields strings.

Related

rswag disallow properties not specified

I would like to specify in my rswag schema definition to only allow the properties that are defined in the schema, and strictly fail with a 500 similar to how it fails if required properties are missing.
So in this example it will fail with a 500 if foo or bar is not present since they are both required; baz is optional.
type: :object,
properties: {
foo: { type: :string },
bar: { type: :string },
baz: { type: :string },
},
required: %i[foo bar]
},
If other properties are in the payload (qux for example) rswag doesn't complain. I would like to strictly enforce that only specified properties are allowed to make it more obvious to future developers that the schema must be kept up to date with any API changes.
So something like the following.
type: :object,
properties: {
foo: { type: :string },
bar: { type: :string },
baz: { type: :string },
},
required: %i[foo bar],
strict: true # <-- Is something like this possible?
},

Which is the proper way to declare this kind of object for swagger?

I'm having a hard time trying to create this kind of object on swagger.json on the spec file
parameter name: :user, in: :body, schema: {
type: :object,
properties: {
username: { type: :string },
password: { type: :string },
},
required: %w[username password]
}
my objetive is to do this format
{
user:{
username: { type: :string },
password: { type: :string },
},
}
I've tried using other proprierties and formats such as changing the properties to include user: or the type, but none have worked so far
you need to configure it like this.
parameter name: :user, in: :body, schema: {
type: :object,
properties: {
user: {
type: :object,
properties: {
username: { type: :string },
password: { type: :string }
},
required: %w[username password]
}
},
required: %w[user]
}

How do I specify DECIMAL and DATE formats in rswag/swagger request specs for Rails?

post('create user') do
tags 'Jobs'
consumes 'application/json'
parameter name: :user, in: :body, schema: {
type: :object,
properties: {
title: { type: :string },
description: { type: :string },
date: { type: :datetime },
budget: { type: :decimal },
awarded: { type: :boolean }
},
required: [ 'title', 'description' ]
}
response(201, 'created') do
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
let(:user) { { title: 'foo', description: 'bar' } }
run_test!
end
end
I've implemented my request as above, but the output in the Swagger UI says
Unknown type: datetime
Unknown type: decimal
The rswag docs for Rails don't give a list of how to specify types - any ideas?
What you're looking for is part of swagger/openapi. Check the data types documentation; there are a limited number of data types.
You want something closer to this:
date: { type: :string, format: :datetime }
awarded: { type: :string, format: :decimal }
Note: decimal isn't a "real" format, but format is an open value so you can use anything supported by the tools you're using. You might use the format annotation along with a pattern match to ensure it's a decimal.

Using rswag in Rails - what is the syntax for index responses (arrays)?

Unfortunately the "documentation" for rswag seems pretty lacking and doesn't give an example of how to implement an index action. My "create" spec displays the Schema and Example Value in the Swagger UI, but my "index" method isn't displaying either of those in the UI.
What do I need to change here? I've played around with it based on the limited examples I've found and none of them seem to work.
path '/api/v1/users' do
get('list users') do
tags 'Users'
response(200, 'successful') do
schema type: :array,
properties: {
id: { type: :integer },
title: { type: :string },
created_at: { type: :datetime},
updated_at: { type: :datetime}
}
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
run_test!
end
end
post('create user') do
tags 'Users'
consumes 'application/json'
parameter name: :user, in: :body, schema: {
type: :object,
properties: {
title: { type: :string }
},
required: [ 'title', 'description' ]
}
response(200, 'successful') do
after do |example|
example.metadata[:response][:content] = {
'application/json' => {
example: JSON.parse(response.body, symbolize_names: true)
}
}
end
run_test!
end
end
end
I have also tried to format the schema like so, based on another example I found, which didn't do anything either (schema/example just aren't displaying):-
schema type: :object,
properties: {
collection: {
type: :array,
items: {
type: :object,
properties: {
id: { type: :integer },
title: { type: :string },
created_at: { type: :datetime},
updated_at: { type: :datetime}
}
}
}
}
Check your swagger_helper file. If you've followed the documentation, it's probably something like that:
RSpec.configure do |config|
config.swagger_root = Rails.root.to_s + '/swagger'
config.swagger_docs = {
'v1/swagger.json' => {
openapi: '3.0.1',
info: {
title: 'API V1',
version: 'v1',
description: 'This is the first version of my API'
},
servers: [
{
url: 'https://{defaultHost}',
variables: {
defaultHost: {
default: 'www.example.com'
}
}
}
]
}
}
end
Just replace opeanapi: '3.0.1' for swagger: '2.0'. I've faced this same issue and this is the only workaround I've found so far.
What worked for me was using the produces method, like:
get 'Retrieves the lists' do
tags 'Lists'
produces 'application/json'
response '200', 'Lists found' do
schema type: :array,
items: {
type: :object,
properties: {
id: { type: :integer },
title: { type: :string },
created_at: { type: :datetime},
updated_at: { type: :datetime}
}
}
run_test!
end
end
#s89_

Rails Rswag Swagger UI Not Sending Headers Properly

I'm using gem rswag for documentation for my Rails API. My rails API requires that all requests include headers['Content-Type']='my-fancy-content-type' and headers['Accept']='my-fancy-accept. So far I've done testing using POSTMAN and all are working fine. My Rspec tests also all pass.
When using the swagger UI at http://localhost:3002/api-docs/index.html. I input the correct param (just id in this case), and the correct headers as shown in the screenshot:
swagger UI
It seems that none of the headers were sent. The console shows it was doing curl -X GET "http://localhost:3002/segments/122" -H "accept: */*" without any headers.
I set up my spec/swagger_helper.rb like:
require 'rails_helper'
RSpec.configure do |config|
config.swagger_root = Rails.root.join('swagger').to_s
config.swagger_docs = {
'v1/swagger.yml' => {
openapi: '3.0.1',
info: {
title: 'API V1',
version: 'v1'
},
paths: {},
servers: [
{
url: 'http://{defaultHost}',
variables: {
defaultHost: {
default: "localhost:#{ENV['PORT']}"
}
}
}
]
}
}
config.swagger_format = :yaml
end
My swagger.yaml looks like:
---
openapi: 3.0.1
info:
title: API V1
version: v1
paths:
"/segments/{id}":
get:
summary: Find segment by id
tags:
- Segments
parameters:
- name: Accept
in: header
required: true
schema:
type: string
- name: Content-Type
in: header
required: true
schema:
type: string
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: success
content: {}
'404':
description: not_found
content: {}
servers:
- url: http://{defaultHost}
variables:
defaultHost:
default: localhost:3002
securityDefinitions:
Content-Type:
type: string
description: Content-Type
name: Content-Type
in: header
A snippet of a test used to generate the swagger documentation is(it passes normally):
path '/segments/{id}' do
get 'Find segment by id' do
tags 'Segments'
parameter name: :Accept, in: :header, type: :string, required: true
parameter name: 'Content-Type', in: :header, type: :string, required: true
parameter name: :id, in: :path, type: :integer
context 'when record exists' do
response '200', :success do
schema type: :object,
properties: {
content: {
id: {type: :string},
type: {type: :string},
attributes: {
id: :integer,
...
}
}
}
run_test! do |response|
expect(response.status).to eq(200)
end
end
I tried adding c.swagger_headers = { 'Content-Type' => 'my-fancy-content-type', 'Accept' => 'my-fancy-accept' } inside Rswag::Api.config inside config/initializers/rswag_api.rb but it did not work (i.e. not sending any of the headers).
Thanks in advance for your help!
In my spec/swagger_helper.rb, I no longer use Open API but I use Swagger 2.0. So change
config.swagger_docs = {
'v1/swagger.yml' => {
openapi: '3.0.1',
to
config.swagger_docs = {
'v1/swagger.json' => {
swagger: '2.0',
And now it works.

Resources