mirror of
https://github.com/ditkrg/rswag.git
synced 2026-01-22 22:06:43 +00:00
Adds support for proper requestBody examples. Adds mechanism to allow for adding additional ways to add request body examples
Can add externalValue or it will work and produce valid swagger spec. The Symbol name matching the let parameter is always required
This commit is contained in:
parent
4c2097e017
commit
b8dcc8fe30
@ -43,7 +43,7 @@ module Rswag
|
||||
# TODO: setup travis CI?
|
||||
|
||||
# MUST HAVES
|
||||
# TODO: look at handling different ways schemas can be defined in 3.0 for requestBody and response
|
||||
# TODO: *** look at handling different ways schemas can be defined in 3.0 for requestBody and response
|
||||
# can we handle all of them?
|
||||
# Then can look at handling different request_body things like $ref, etc
|
||||
# TODO: look at adding request_body method to handle diffs in Open API 2.0 to 3.0
|
||||
@ -75,12 +75,20 @@ module Rswag
|
||||
# it can contain a 'value' key which is a direct hash (easiest)
|
||||
# it can contain a 'external_value' key which makes an external call to load the json
|
||||
# it can contain a '$ref' key. Which points to #/components/examples/blog
|
||||
if passed_examples.first.is_a?(Symbol)
|
||||
example_key_name = passed_examples.first # can come up with better scheme here
|
||||
# TODO: write more tests around this adding to the parameter
|
||||
# if symbol try and use save_request_example
|
||||
param_attributes = { name: example_key_name, in: :body, required: required, param_value: example_key_name, schema: schema }
|
||||
parameter(param_attributes)
|
||||
passed_examples.each do |passed_example|
|
||||
if passed_example.is_a?(Symbol)
|
||||
example_key_name = passed_example
|
||||
# TODO: write more tests around this adding to the parameter
|
||||
# if symbol try and use save_request_example
|
||||
param_attributes = { name: example_key_name, in: :body, required: required, param_value: example_key_name, schema: schema }
|
||||
parameter(param_attributes)
|
||||
elsif passed_example.is_a?(Hash) && passed_example[:externalValue]
|
||||
param_attributes = { name: passed_example, in: :body, required: required, param_value: passed_example[:externalValue], schema: schema }
|
||||
parameter(param_attributes)
|
||||
elsif passed_example.is_a?(Hash) && passed_example['$ref']
|
||||
param_attributes = { name: passed_example, in: :body, required: required, param_value: passed_example['$ref'], schema: schema }
|
||||
parameter(param_attributes)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -169,6 +177,45 @@ module Rswag
|
||||
metadata[:response][:examples] = example
|
||||
end
|
||||
|
||||
# checks the examples in the parameters should be able to add $ref and externalValue examples.
|
||||
# This syntax would look something like this in the integration _spec.rb file
|
||||
#
|
||||
# request_body_json schema: { '$ref' => '#/components/schemas/blog' },
|
||||
# examples: [:blog, {name: :external_blog,
|
||||
# externalValue: 'http://api.sample.org/myjson_example'},
|
||||
# {name: :another_example,
|
||||
# '$ref' => '#/components/examples/flexible_blog_example'}]
|
||||
# The first value :blog, points to a let param of the same name, and is used to make the request in the
|
||||
# integration test (it is used to build the request payload)
|
||||
#
|
||||
# The second item in the array shows how to add an externalValue for the examples in the requestBody section
|
||||
# The third item shows how to add a $ref item that points to the components/examples section of the swagger spec.
|
||||
#
|
||||
# NOTE: that the externalValue will produce valid example syntax in the swagger output, but swagger-ui
|
||||
# will not show it yet
|
||||
def merge_other_examples!(example_metadata)
|
||||
# example.metadata[:operation][:requestBody][:content]['application/json'][:examples]
|
||||
content_node = example_metadata[:operation][:requestBody][:content]['application/json']
|
||||
return unless content_node
|
||||
|
||||
external_example = example_metadata[:operation]&.dig(:parameters)&.detect { |p| p[:in] == :body && p[:name].is_a?(Hash) && p[:name][:externalValue] } || {}
|
||||
ref_example = example_metadata[:operation]&.dig(:parameters)&.detect { |p| p[:in] == :body && p[:name].is_a?(Hash) && p[:name]['$ref'] } || {}
|
||||
examples_node = content_node[:examples] ||= {}
|
||||
|
||||
nodes_to_add = []
|
||||
nodes_to_add << external_example unless external_example.empty?
|
||||
nodes_to_add << ref_example unless ref_example.empty?
|
||||
|
||||
nodes_to_add.each do |node|
|
||||
json_request_examples = examples_node ||= {}
|
||||
other_name = node[:name][:name]
|
||||
other_key = node[:name][:externalValue] ? :externalValue : '$ref'
|
||||
if other_name
|
||||
json_request_examples.merge!(other_name => {other_key => node[:param_value]})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run_test!(&block)
|
||||
# NOTE: rspec 2.x support
|
||||
if RSPEC_VERSION < 3
|
||||
@ -202,9 +249,13 @@ module Rswag
|
||||
example.metadata[:operation][:requestBody][:content]['application/json'] = { examples: {} } unless example.metadata[:operation][:requestBody][:content]['application/json'][:examples]
|
||||
json_request_examples = example.metadata[:operation][:requestBody][:content]['application/json'][:examples]
|
||||
json_request_examples[body_parameter[:name]] = { value: send(body_parameter[:name]) }
|
||||
|
||||
example.metadata[:operation][:requestBody][:content]['application/json'][:examples] = json_request_examples
|
||||
end
|
||||
end
|
||||
|
||||
self.class.merge_other_examples!(example.metadata) if example.metadata[:operation][:requestBody]
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -152,12 +152,9 @@ module Rswag
|
||||
end
|
||||
|
||||
def build_json_payload(parameters, example)
|
||||
body_param = parameters.select { |p| p[:in] == :body }.first
|
||||
body_param = parameters.select { |p| p[:in] == :body && p[:name].is_a?(Symbol) }.first
|
||||
return nil unless body_param
|
||||
|
||||
# p "example is #{example.send(body_param[:name]).to_json} ** AND body param is #{body_param}" if body_param
|
||||
# body_param ? example.send(body_param[:name]).to_json : nil
|
||||
|
||||
source_body_param = example.send(body_param[:name]) if body_param[:name] && example.respond_to?(body_param[:name])
|
||||
source_body_param ||= body_param[:param_value]
|
||||
source_body_param ? source_body_param.to_json : nil
|
||||
|
||||
@ -19,6 +19,14 @@ class BlogsController < ApplicationController
|
||||
respond_with @blog
|
||||
end
|
||||
|
||||
# POST /blogs/alternate
|
||||
def alternate_create
|
||||
|
||||
# contrived example to show different :examples in the requestBody section
|
||||
@blog = Blog.create(params.require(:blog).permit(:title, :content))
|
||||
respond_with @blog
|
||||
end
|
||||
|
||||
# Put /blogs/1
|
||||
def upload
|
||||
@blog = Blog.find_by_id(params[:id])
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
TestApp::Application.routes.draw do
|
||||
|
||||
post '/blogs/flexible', to: 'blogs#flexible_create'
|
||||
post '/blogs/alternate', to: 'blogs#alternate_create'
|
||||
resources :blogs
|
||||
put '/blogs/:id/upload', to: 'blogs#upload'
|
||||
|
||||
|
||||
@ -80,6 +80,32 @@ describe 'Blogs API', type: :request, swagger_doc: 'v1/swagger.json' do
|
||||
end
|
||||
end
|
||||
|
||||
path '/blogs/alternate' do
|
||||
post 'Creates a blog - different :examples in requestBody' do
|
||||
tags 'Blogs'
|
||||
description 'Creates a new blog from provided data'
|
||||
operationId 'createAlternateBlog'
|
||||
consumes 'application/json'
|
||||
produces 'application/json'
|
||||
|
||||
# NOTE: the externalValue: http://... is valid 3.0 spec, but swagger-UI does NOT support it yet
|
||||
# https://github.com/swagger-api/swagger-ui/issues/5433
|
||||
request_body_json schema: { '$ref' => '#/components/schemas/blog' },
|
||||
examples: [:blog, {name: :external_blog,
|
||||
externalValue: 'http://api.sample.org/myjson_example'},
|
||||
{name: :another_example,
|
||||
'$ref' => '#/components/examples/flexible_blog_example'}]
|
||||
|
||||
let(:blog) { { blog: { title: 'alt title', content: 'alt bar' } } }
|
||||
|
||||
response '201', 'blog created' do
|
||||
schema '$ref' => '#/components/schemas/blog'
|
||||
run_test!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
path '/blogs/{id}' do
|
||||
|
||||
|
||||
@ -69,6 +69,16 @@ RSpec.configure do |config|
|
||||
required: %w[id headline]
|
||||
}
|
||||
},
|
||||
examples: {
|
||||
flexible_blog_example: {
|
||||
summary: 'Sample example of a flexible blog',
|
||||
value: {
|
||||
id: 1,
|
||||
headline: 'This is a headline',
|
||||
text: 'Some sample text'
|
||||
}
|
||||
}
|
||||
},
|
||||
securitySchemes: {
|
||||
basic_auth: {
|
||||
type: :http,
|
||||
|
||||
@ -258,6 +258,63 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/blogs/alternate": {
|
||||
"post": {
|
||||
"summary": "Creates a blog - different :examples in requestBody",
|
||||
"tags": [
|
||||
"Blogs"
|
||||
],
|
||||
"description": "Creates a new blog from provided data",
|
||||
"operationId": "createAlternateBlog",
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"examples": {
|
||||
"blog": {
|
||||
"value": {
|
||||
"blog": {
|
||||
"title": "alt title",
|
||||
"content": "alt bar"
|
||||
}
|
||||
}
|
||||
},
|
||||
"external_blog": {
|
||||
"externalValue": "http://api.sample.org/myjson_example"
|
||||
},
|
||||
"another_example": {
|
||||
"$ref": "#/components/examples/flexible_blog_example"
|
||||
}
|
||||
},
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/blog"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "blog created",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"example": {
|
||||
"id": 1,
|
||||
"title": "alt title",
|
||||
"content": "alt bar",
|
||||
"thumbnail": null
|
||||
},
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/blog"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/blogs/{id}": {
|
||||
"get": {
|
||||
"summary": "Retrieves a blog",
|
||||
@ -435,6 +492,16 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"examples": {
|
||||
"flexible_blog_example": {
|
||||
"summary": "Sample example of a flexible blog",
|
||||
"value": {
|
||||
"id": 1,
|
||||
"headline": "This is a headline",
|
||||
"text": "Some sample text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"securitySchemes": {
|
||||
"basic_auth": {
|
||||
"type": "http",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user