rswag disallow properties not specified - ruby-on-rails

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?
},

Related

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 can I specify decimal schema formats in RSpec/RSwag when decimal responses are returned as strings?

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.

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.

elasticsearch 5.X + searchkick(rails) configuration

I'm in the process of upgrading my elasticsearch instance from V1.7 to 5.3 and I'm running into some errors when starting my reindex. From what I can tell most models can index just fine but there are a couple I had that used more advanced settings that don't seem to be working. Here's an example of one of my models(brand.rb):
searchkick word_start: [:name],
merge_mappings: true,
mappings: searchkick_mappings,
settings: searchkick_settings
def search_data
attributes.merge(
geography: self.geography ? self.geography.name : "",
geography_breadcrumb: self.geography ? self.geography.breadcrumb : "",
producer_name: self.producer.name
)
end
the searchkick_mappings and searchkick_settings are defined in another file that is included in my model. Here's the code:
def searchkick_mappings
{
brand: {
properties: {
name: {
type: 'text',
analyzer: 'standard',
fields: {
autocomplete: {
type: 'text',
analyzer: 'autocomplete'
},
folded: {
type: 'text',
analyzer: 'folded'
},
delimited: {
type: 'text',
analyzer: 'delimited'
}
}
}
}
}
}
end
def searchkick_settings
{
analysis: {
filter: {
autocomplete_filter: {
type: 'edge_ngram',
min_gram: 1,
max_gram: 20
},
delimiter_filter: {
type: 'word_delimiter',
preserve_original: true
}
},
analyzer: {
folded: {
type: 'custom',
tokenizer: 'standard',
filter: ['standard','lowercase','asciifolding']
},
delimited: {
type: 'custom',
tokenizer: 'whitespace',
filter: ['lowercase','delimiter_filter']
},
autocomplete: {
type: 'custom',
tokenizer: 'standard',
filter: ['standard','lowercase', 'asciifolding',
'autocomplete_filter']
}
}
}
}
end
The only change I made from when it was working in V1.7 -> 5.3 is that I had to change the 'type' field from "String" to "text" as it seems they removed the string type in favor of a text and keyword type, where text is analyzed and keywords are not. The error i'm receiving when I run bundle exec searchkick:reindex:all is saying there is an unknown parameter 'ignore_above'. From reading the documentation it seems that parameter is only for keyword fields and not text but I am not adding that parameter in my custom mappings so I don't see why it would be there.
Let me know if you need to see more code/need to know more. I'll gladly edit OP/comment whatever is helpful.

Embedded object in loopback model schema for Swagger

In the context of a remote method, I'm trying to define a model schema of a parameter passed in the body. This object looks like this:
{
name: "Alex",
credentials: {
user: "alex",
pass: "pass"
}
}
So, I have this code in my remote method definition:
MyModel.remoteMethod("postSomething", {
accepts: [
{arg: 'person', type: {
"name": "string",
"credentials": {
"type": "object",
"properties": {
"user": "string",
"pass: "string"
}
}
}, http: {source: 'body'}, required: true
}
],
.....
Unfortunatelly, the details of this embedded object (credentials) are not shown in the generated Swagger explorer. This is what I see:
{
"user": "string",
"credentials": {}
}
I've tried many different ways but I could not show the properties of the credentials object.
Any ideas?
Loopback 2.x
Edit: Note the following only works for Loopback 2.x, as the type registry changed in 3.x.
The problem is that the data you are providing needs to be on the type property for the nested value. This should work:
MyModel.remoteMethod('postSomething', {
accepts: [
{
arg: 'person',
type: {
name: 'string',
credentials: {
type: {
user: 'string',
pass: 'string'
}
}
},
http: {
source: 'body'
},
required: true
}
],
//...
This also works with arrays:
accepts: [
{
arg: 'Book',
type: {
title: 'string',
author: 'string',
pages: [{
type: {
pageNo: 'number',
text: 'string'
}
}]
}
}
],
// ...
Loopback 3.x
Since the model registry and strong remoting changed in Loopback 3.x to only allow string or array types, you can't really avoid creating a new model. If you would like to quickly 'inline' a model without going through the full process of adding the model json file, adding it to model-config.json etc. you can register it directly on the app:
app.registry.createModel('Person', {
firstName: 'string',
lastName: 'string'
}, { base: 'Model' });
You can set the base to one of your other models if you want to extend an existing model (e.g, add another property that is only accepted in the given remote method)
If you want to create the model without cluttering up your model registry, you can do so by calling createModel on loobpack itself:
const loopback = require('loopback')
const modl = loopback.createModel({
name: 'Person',
base: null,
properties: {
firstName: {
type: 'string',
id: true // means it won't have an id property
}
}
});
In both of the above examples, you refer to the model by name to attach it to the remote method:
accepts: [
{
arg: 'Person',
type: 'Person'
}
],
// ...
Note you will need to create a sub-model for every sub-property (e.g. credentials)
Loopback swagger only picks up the outer object ignoring the properties of the object.
If you want to show a nested object in the swagger docs for the request body, you will have to make nested model.
Assuming you have a model called as person. You have to create another model named "credentials" having properties user and password. Then define the relationship in your person model's config
{
"name": "Person",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"name": {
"type": "string",
"required": true
}
},
"validations": [],
"relations": {
"credentials": {
"type": "embedsOne",
"model": "credentials",
"property": "credentials",
"options": {
"validate": true,
"forceId": false
}
}
},
"acls": [],
"methods": {}
}
And add a reference to this model where you define your remote method
MyModel.remoteMethod("postSomething", {
accepts: [
{arg: 'person', type: {Person},
http: {source: 'body'}, required: true
}
],
To avoid "Treating unknown remoting type" warning make sure your model is marked as "public" inside your "model-config.json"

Resources