Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Openapi 3.0 [DRAFT] #744

Merged
merged 47 commits into from
Jun 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
4872b54
wip: openapi 3.0
Oct 13, 2018
00753e0
move files to respective version folders
Oct 14, 2018
fa4c97d
begin to parse params
Oct 20, 2018
1efa3ec
fix failing tests
Nov 1, 2018
0cef1f3
Initial support for openapi3 requestBody
Nov 10, 2018
11ea074
OpenAPI3 parameters and request bodies are actually OpenAPI3 compliant
Nov 12, 2018
6f72951
Fix array parameter type (not custom types for now)
Nov 16, 2018
b071036
add tests
Nov 17, 2018
f5f6518
Initial support for openapi3 components/schemas
Nov 17, 2018
2a7b32b
Fix tests
Nov 17, 2018
798702e
Add test
Nov 18, 2018
13b04a5
Add test
Nov 25, 2018
c937cd2
Fix edge cases with ranges, floats and strings
Nov 25, 2018
0f5d491
Add test
Nov 25, 2018
0293a04
Add test
Nov 25, 2018
9885d05
Add test
Nov 25, 2018
dd948fc
Add test
Nov 25, 2018
be3cb58
Add test
Nov 25, 2018
ab54ce7
Better implementation of request body
Nov 30, 2018
96a0f14
Fix test
Nov 30, 2018
da11513
Add test
Nov 30, 2018
3c26c41
Add test
Nov 30, 2018
1c27600
Add test
Nov 30, 2018
f2732b3
fix array test
Dec 2, 2018
15d2215
Fix test
Dec 6, 2018
31917ae
Add test
Dec 6, 2018
2902005
Add test
Dec 6, 2018
76f4675
Add test
Dec 6, 2018
b7a2a85
Add test
Dec 6, 2018
148452a
Add test
Dec 6, 2018
d2c9c6e
Add test
Dec 6, 2018
c5f1e5b
Add test
Dec 6, 2018
4134ac2
Add test
Dec 6, 2018
da94b51
Add test
Dec 6, 2018
c478bf5
Fix tests
Dec 6, 2018
c42f359
Add test
Dec 7, 2018
545f629
Add test
Dec 7, 2018
4be176e
Add test
Dec 7, 2018
5076304
Add test
Dec 7, 2018
377f113
Fix file response body
Dec 11, 2018
fb17959
Add examples spec
Dec 17, 2018
29e2eff
Fix host test
Dec 27, 2018
fa61411
Add specs
Dec 27, 2018
49cb6b0
Fix guarded endpoint spec, after a rebase
Dec 28, 2018
8c333c9
Fix spec
Dec 28, 2018
5169a7b
Add spec
Dec 30, 2018
9591fb3
Add spec
Jan 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ get '/thing', failure: [
# ...
end
```
If no status code is defined [defaults](/lib/grape-swagger/endpoint.rb#L210) would be taken.
If no status code is defined [defaults](/lib/grape-swagger/swagger_2/endpoint.rb#L210) would be taken.

The result is then something like following:

Expand Down
34 changes: 25 additions & 9 deletions lib/grape-swagger.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

require 'grape-swagger/instance'

require 'grape-swagger/version'
require 'grape-swagger/endpoint'
require 'grape-swagger/errors'

require 'grape-swagger/doc_methods'
require 'grape-swagger/version'
require 'grape-swagger/model_parsers'
require 'grape-swagger/swagger_2/endpoint'
require 'grape-swagger/openapi_3/endpoint'
require 'grape-swagger/openapi_3/doc_methods'
require 'grape-swagger/swagger_2/doc_methods'

module GrapeSwagger
class << self
Expand Down Expand Up @@ -112,11 +113,10 @@ module SwaggerDocumentationAdder
include SwaggerRouting

def add_swagger_documentation(options = {})
documentation_class = create_documentation_class

version_for(options)
options = { target_class: self }.merge(options)
version_for(options)
@target_class = options[:target_class]
documentation_class = create_documentation_class(options[:openapi_version])
auth_wrapper = options[:endpoint_auth_wrapper] || Class.new

use auth_wrapper if auth_wrapper.method_defined?(:before) && !middleware.flatten.include?(auth_wrapper)
Expand All @@ -138,6 +138,11 @@ def add_swagger_documentation(options = {})
exclusive_route_keys.each do |key|
@target_class.combined_namespace_routes[key] = @target_class.combined_routes[key]
end

endpoint_type = options[:openapi_version] == '3.0' ? Grape::OpenAPI3Endpoint : Grape::Swagger2Endpoint
set_endpoint_type(@target_class, endpoint_type)
set_endpoint_type(documentation_class, endpoint_type)

documentation_class
end

Expand All @@ -147,6 +152,13 @@ def version_for(options)
options[:version] = version if version
end

def set_endpoint_type(app, klass)
app.endpoints.each do |endpoint|
endpoint.class.include(klass)
set_endpoint_type(endpoint.options[:app], klass) if endpoint.options[:app]
end
end

def combine_namespaces(app)
app.endpoints.each do |endpoint|
ns = endpoint.namespace_stackable(:namespace).last
Expand All @@ -161,9 +173,13 @@ def combine_namespaces(app)
end
end

def create_documentation_class
def create_documentation_class(openapi_version)
Class.new(GrapeInstance) do
extend GrapeSwagger::DocMethods
if openapi_version == '3.0'
extend GrapeOpenAPI::DocMethods
else
extend GrapeSwagger::DocMethods
end
end
end
end
Expand Down
13 changes: 11 additions & 2 deletions lib/grape-swagger/doc_methods/extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,17 @@ def find_definition(status, path)
response = path[method][:responses][status]
return if response.nil?

return response[:schema]['$ref'].split('/').last if response[:schema].key?('$ref')
return response[:schema]['items']['$ref'].split('/').last if response[:schema].key?('items')
# Swagger 2
if response[:schema]
return response[:schema]['$ref'].split('/').last if response[:schema].key?('$ref')
return response[:schema]['items']['$ref'].split('/').last if response[:schema].key?('items')
end

# OpenAPI 3
response[:content].each do |_,v|
return v[:schema]['$ref'].split('/').last if v[:schema].key?('$ref')
return v[:schema]['items']['$ref'].split('/').last if v[:schema].key?('items')
end
end

def add_extension_to(part, extensions)
Expand Down
137 changes: 137 additions & 0 deletions lib/grape-swagger/openapi_3/doc_methods.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# frozen_string_literal: true

require 'grape-swagger/doc_methods/status_codes'
require 'grape-swagger/doc_methods/produces_consumes'
require 'grape-swagger/doc_methods/data_type'
require 'grape-swagger/doc_methods/extensions'
require 'grape-swagger/doc_methods/operation_id'
require 'grape-swagger/doc_methods/optional_object'
require 'grape-swagger/doc_methods/path_string'
require 'grape-swagger/doc_methods/tag_name_description'
require 'grape-swagger/openapi_3/doc_methods/parse_params'
require 'grape-swagger/openapi_3/doc_methods/move_params'
require 'grape-swagger/doc_methods/headers'
require 'grape-swagger/doc_methods/build_model_definition'
require 'grape-swagger/doc_methods/version'

module GrapeOpenAPI
module DocMethods
def hide_documentation_path
@@hide_documentation_path
end

def mount_path
@@mount_path
end

def setup(options)
options = defaults.merge(options)

# options could be set on #add_swagger_documentation call,
# for available options see #defaults
target_class = options[:target_class]
guard = options[:swagger_endpoint_guard]
formatter = options[:format]
api_doc = options[:api_documentation].dup
specific_api_doc = options[:specific_api_documentation].dup

class_variables_from(options)

if formatter
%i[format default_format default_error_formatter].each do |method|
send(method, formatter)
end
end

desc api_doc.delete(:desc), api_doc

instance_eval(guard) unless guard.nil?

output_path_definitions = proc do |combi_routes, endpoint|
output = endpoint.swagger_object(
target_class,
endpoint.request,
options
)

paths, definitions = endpoint.path_and_definition_objects(combi_routes, target_class, options)
tags = tags_from(paths, options)

output[:tags] = tags unless tags.empty? || paths.blank?
output[:paths] = paths unless paths.blank?
unless definitions.blank?
output[:components] ||= {}
output[:components][:schemas] = definitions
end

output
end

get mount_path do
header['Access-Control-Allow-Origin'] = '*'
header['Access-Control-Request-Method'] = '*'

output_path_definitions.call(target_class.combined_namespace_routes, self)
end

desc specific_api_doc.delete(:desc), { params:
specific_api_doc.delete(:params) || {} }.merge(specific_api_doc)

params do
requires :name, type: String, desc: 'Resource name of mounted API'
optional :locale, type: Symbol, desc: 'Locale of API documentation'
end

get "#{mount_path}/:name" do
I18n.locale = params[:locale] || I18n.default_locale

combined_routes = target_class.combined_namespace_routes[params[:name]]
error!({ error: 'named resource not exist' }, 400) if combined_routes.nil?

output_path_definitions.call({ params[:name] => combined_routes }, self)
end
end

def defaults
{
info: {},
models: [],
doc_version: '0.0.1',
target_class: nil,
mount_path: '/swagger_doc',
host: nil,
base_path: nil,
add_base_path: false,
add_version: true,
hide_documentation_path: true,
format: :json,
authorizations: nil,
security_definitions: nil,
security: nil,
api_documentation: { desc: 'Swagger compatible API description' },
specific_api_documentation: { desc: 'Swagger compatible API description for specific API' },
endpoint_auth_wrapper: nil,
swagger_endpoint_guard: nil,
token_owner: nil
}
end

def class_variables_from(options)
@@mount_path = options[:mount_path]
@@class_name = options[:class_name] || options[:mount_path].delete('/')
@@hide_documentation_path = options[:hide_documentation_path]
end

def tags_from(paths, options)
tags = GrapeSwagger::DocMethods::TagNameDescription.build(paths)

if options[:tags]
names = options[:tags].map { |t| t[:name] }
tags.reject! { |t| names.include?(t[:name]) }
tags += options[:tags]
end

tags
end
end
end
Loading