grape-swagger 0.7.2 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +33 -0
- data/.rubocop.yml +36 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +90 -0
- data/CONTRIBUTING.md +126 -0
- data/Gemfile +1 -21
- data/LICENSE.txt +1 -1
- data/README.md +397 -0
- data/RELEASING.md +80 -0
- data/Rakefile +6 -23
- data/UPGRADING.md +47 -0
- data/grape-swagger.gemspec +26 -84
- data/lib/grape-swagger.rb +237 -111
- data/lib/grape-swagger/errors.rb +9 -0
- data/lib/grape-swagger/markdown.rb +23 -0
- data/lib/grape-swagger/markdown/kramdown_adapter.rb +37 -0
- data/lib/grape-swagger/markdown/redcarpet_adapter.rb +89 -0
- data/lib/grape-swagger/version.rb +3 -0
- data/spec/api_description_spec.rb +41 -0
- data/spec/api_global_models_spec.rb +76 -0
- data/spec/api_models_spec.rb +190 -93
- data/spec/default_api_spec.rb +31 -36
- data/spec/form_params_spec.rb +56 -53
- data/spec/grape-swagger_helper_spec.rb +88 -49
- data/spec/grape-swagger_spec.rb +7 -5
- data/spec/hide_api_spec.rb +58 -55
- data/spec/markdown/kramdown_adapter_spec.rb +38 -0
- data/spec/markdown/markdown_spec.rb +27 -0
- data/spec/markdown/redcarpet_adapter_spec.rb +81 -0
- data/spec/namespaced_api_spec.rb +47 -0
- data/spec/non_default_api_spec.rb +372 -222
- data/spec/response_model_spec.rb +80 -0
- data/spec/simple_mounted_api_spec.rb +113 -118
- data/spec/spec_helper.rb +0 -1
- data/spec/version_spec.rb +8 -0
- data/test/api.rb +62 -0
- data/test/config.ru +10 -2
- data/test/splines.png +0 -0
- metadata +145 -91
- data/.rvmrc +0 -48
- data/CHANGELOG.markdown +0 -55
- data/Gemfile.lock +0 -94
- data/README.markdown +0 -168
- data/VERSION +0 -1
- data/test/nested_api.rb +0 -30
data/RELEASING.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Releasing Grape-Swagger
|
2
|
+
|
3
|
+
There're no particular rules about when to release grape-swagger. Release bug fixes frequenty, features not so frequently and breaking API changes rarely.
|
4
|
+
|
5
|
+
### Release
|
6
|
+
|
7
|
+
Run tests, check that all tests succeed locally.
|
8
|
+
|
9
|
+
```
|
10
|
+
bundle install
|
11
|
+
rake
|
12
|
+
```
|
13
|
+
|
14
|
+
Check that the last build succeeded in [Travis CI](https://travis-ci.org/tim-vandecasteele/grape-swagger) for all supported platforms.
|
15
|
+
|
16
|
+
Increment the version, modify [lib/grape-swagger/version.rb](lib/grape-swagger/version.rb).
|
17
|
+
|
18
|
+
* Increment the third number if the release has bug fixes and/or very minor features, only (eg. change `0.7.1` to `0.7.2`).
|
19
|
+
* Increment the second number if the release contains major features or breaking API changes (eg. change `0.7.1` to `0.8.0`).
|
20
|
+
|
21
|
+
Change "Next Release" in [CHANGELOG.md](CHANGELOG.md) to the new version.
|
22
|
+
|
23
|
+
```
|
24
|
+
### 0.7.2 (February 6, 2014)
|
25
|
+
```
|
26
|
+
|
27
|
+
Remove the line with "Your contribution here.", since there will be no more contributions to this release.
|
28
|
+
|
29
|
+
Commit your changes.
|
30
|
+
|
31
|
+
```
|
32
|
+
git add CHANGELOG.md lib/grape-swagger/version.rb
|
33
|
+
git commit -m "Preparing for release, 0.7.2."
|
34
|
+
git push origin master
|
35
|
+
```
|
36
|
+
|
37
|
+
Release.
|
38
|
+
|
39
|
+
```
|
40
|
+
$ rake release
|
41
|
+
|
42
|
+
grape-swagger 0.7.2 built to pkg/grape-swagger-0.7.2.gem.
|
43
|
+
Tagged v0.7.2.
|
44
|
+
Pushed git commits and tags.
|
45
|
+
Pushed grape-swagger 0.7.2 to rubygems.org.
|
46
|
+
```
|
47
|
+
|
48
|
+
### Prepare for the Next Version
|
49
|
+
|
50
|
+
Add the next release to [CHANGELOG.md](CHANGELOG.md).
|
51
|
+
|
52
|
+
```
|
53
|
+
Next Release
|
54
|
+
============
|
55
|
+
|
56
|
+
* Your contribution here.
|
57
|
+
```
|
58
|
+
|
59
|
+
Comit your changes.
|
60
|
+
|
61
|
+
```
|
62
|
+
git add CHANGELOG.md
|
63
|
+
git commit -m "Preparing for next release."
|
64
|
+
git push origin master
|
65
|
+
```
|
66
|
+
|
67
|
+
### Make an Announcement
|
68
|
+
|
69
|
+
Make an announcement on the [ruby-grape@googlegroups.com](mailto:ruby-grape@googlegroups.com) mailing list. The general format is as follows.
|
70
|
+
|
71
|
+
```
|
72
|
+
Grape-Swagger 0.7.2 has been released.
|
73
|
+
|
74
|
+
There were 8 contributors to this release, not counting documentation.
|
75
|
+
|
76
|
+
Please note the breaking API change in ...
|
77
|
+
|
78
|
+
[copy/paste CHANGELOG here]
|
79
|
+
|
80
|
+
```
|
data/Rakefile
CHANGED
@@ -2,30 +2,10 @@
|
|
2
2
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'bundler'
|
5
|
-
begin
|
6
|
-
Bundler.setup(:default, :development)
|
7
|
-
rescue Bundler::BundlerError => e
|
8
|
-
$stderr.puts e.message
|
9
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
-
exit e.status_code
|
11
|
-
end
|
12
|
-
require 'rake'
|
13
|
-
|
14
|
-
require 'jeweler'
|
15
|
-
Jeweler::Tasks.new do |gem|
|
16
|
-
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
-
gem.name = "grape-swagger"
|
18
|
-
gem.homepage = "http://github.com/tim-vandecasteele/grape-swagger"
|
19
|
-
gem.license = "MIT"
|
20
|
-
gem.summary = %Q{Add swagger compliant documentation to your grape API}
|
21
|
-
gem.description = %Q{A simple way to add proper auto generated documentation - that can be displayed with swagger - to your inline described grape API}
|
22
|
-
gem.email = "tim.vandecasteele@gmail.com"
|
23
|
-
gem.authors = ["Tim Vandecasteele"]
|
24
|
-
# dependencies defined in Gemfile
|
25
|
-
end
|
26
|
-
Jeweler::RubygemsDotOrgTasks.new
|
27
5
|
|
6
|
+
Bundler.setup(:default, :development)
|
28
7
|
|
8
|
+
require 'rake'
|
29
9
|
|
30
10
|
Bundler::GemHelper.install_tasks
|
31
11
|
|
@@ -34,4 +14,7 @@ require 'rspec/core/rake_task'
|
|
34
14
|
|
35
15
|
RSpec::Core::RakeTask.new(:spec)
|
36
16
|
|
37
|
-
|
17
|
+
require 'rubocop/rake_task'
|
18
|
+
RuboCop::RakeTask.new(:rubocop)
|
19
|
+
|
20
|
+
task default: [:rubocop, :spec]
|
data/UPGRADING.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
Upgrading Grape-swagger
|
2
|
+
=======================
|
3
|
+
|
4
|
+
### Upgrading to >= 0.8.0
|
5
|
+
|
6
|
+
#### Changes in Configuration
|
7
|
+
|
8
|
+
The following options have been added, removed or have been changed in the grape-swagger interface:
|
9
|
+
|
10
|
+
* `markdown: true/false` => `markdown: GrapeSwagger::Markdown::KramdownAdapter`
|
11
|
+
|
12
|
+
#### Markdown
|
13
|
+
|
14
|
+
You can now configure a markdown adapter. This was originally changed because of performance issues with Kramdown and the `markdown` option no longer takes a boolean argument. Built-in adapters include Kramdown and Redcarpet.
|
15
|
+
|
16
|
+
##### Kramdown
|
17
|
+
|
18
|
+
To configure the markdown with Kramdown, add the kramdown gem to your Gemfile:
|
19
|
+
|
20
|
+
`gem 'kramdown'`
|
21
|
+
|
22
|
+
Configure grape-swagger as follows:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
add_swagger_documentation (
|
26
|
+
markdown: GrapeSwagger::Markdown::KramdownAdapter
|
27
|
+
)
|
28
|
+
```
|
29
|
+
|
30
|
+
#### Redcarpet
|
31
|
+
|
32
|
+
To configure markdown with Redcarpet, add the redcarpet and the rouge gem to your Gemfile. Note that Redcarpet does not work with JRuby.
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
gem 'redcarpet'
|
36
|
+
gem 'rouge'
|
37
|
+
```
|
38
|
+
|
39
|
+
Configure grape-swagger as follows:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
add_swagger_documentation (
|
43
|
+
markdown: GrapeSwagger::Markdown::RedcarpetAdapter
|
44
|
+
)
|
45
|
+
```
|
46
|
+
|
47
|
+
See [#142](https://github.com/tim-vandecasteele/grape-swagger/pull/142) and documentation section [Markdown in Notes](https://github.com/tim-vandecasteele/grape-swagger#markdown-in-notes) for more information.
|
data/grape-swagger.gemspec
CHANGED
@@ -1,90 +1,32 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
-
# -*- encoding: utf-8 -*-
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
2
|
+
require 'grape-swagger/version'
|
5
3
|
|
6
4
|
Gem::Specification.new do |s|
|
7
|
-
s.name
|
8
|
-
s.version
|
5
|
+
s.name = 'grape-swagger'
|
6
|
+
s.version = GrapeSwagger::VERSION
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.authors = ['Tim Vandecasteele']
|
9
|
+
s.email = ['tim.vandecasteele@gmail.com']
|
10
|
+
s.homepage = 'https://github.com/tim-vandecasteele/grape-swagger'
|
11
|
+
s.summary = 'A simple way to add auto generated documentation to your Grape API that can be displayed with Swagger.'
|
12
|
+
s.license = 'MIT'
|
9
13
|
|
10
|
-
s.
|
11
|
-
s.
|
12
|
-
s.date = "2014-02-06"
|
13
|
-
s.description = "A simple way to add proper auto generated documentation - that can be displayed with swagger - to your inline described grape API"
|
14
|
-
s.email = "tim.vandecasteele@gmail.com"
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE.txt",
|
17
|
-
"README.markdown"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
".rspec",
|
22
|
-
".rvmrc",
|
23
|
-
".travis.yml",
|
24
|
-
"CHANGELOG.markdown",
|
25
|
-
"Gemfile",
|
26
|
-
"Gemfile.lock",
|
27
|
-
"LICENSE.txt",
|
28
|
-
"README.markdown",
|
29
|
-
"Rakefile",
|
30
|
-
"VERSION",
|
31
|
-
"grape-swagger.gemspec",
|
32
|
-
"lib/grape-swagger.rb",
|
33
|
-
"spec/api_models_spec.rb",
|
34
|
-
"spec/default_api_spec.rb",
|
35
|
-
"spec/form_params_spec.rb",
|
36
|
-
"spec/grape-swagger_helper_spec.rb",
|
37
|
-
"spec/grape-swagger_spec.rb",
|
38
|
-
"spec/hide_api_spec.rb",
|
39
|
-
"spec/non_default_api_spec.rb",
|
40
|
-
"spec/simple_mounted_api_spec.rb",
|
41
|
-
"spec/spec_helper.rb",
|
42
|
-
"test/config.ru",
|
43
|
-
"test/nested_api.rb"
|
44
|
-
]
|
45
|
-
s.homepage = "http://github.com/tim-vandecasteele/grape-swagger"
|
46
|
-
s.licenses = ["MIT"]
|
47
|
-
s.require_paths = ["lib"]
|
48
|
-
s.rubygems_version = "1.8.23"
|
49
|
-
s.summary = "Add swagger compliant documentation to your grape API"
|
14
|
+
s.add_runtime_dependency 'grape'
|
15
|
+
s.add_runtime_dependency 'grape-entity'
|
50
16
|
|
51
|
-
|
52
|
-
|
17
|
+
s.add_development_dependency 'rake'
|
18
|
+
s.add_development_dependency 'shoulda'
|
19
|
+
s.add_development_dependency 'rdoc'
|
20
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
21
|
+
s.add_development_dependency 'bundler'
|
22
|
+
s.add_development_dependency 'rack-test'
|
23
|
+
s.add_development_dependency 'rack-cors'
|
24
|
+
s.add_development_dependency 'rubocop', '0.24.1'
|
25
|
+
s.add_development_dependency 'kramdown', '~> 1.4.1'
|
26
|
+
s.add_development_dependency 'redcarpet', '~> 3.1.2' unless RUBY_PLATFORM.eql? 'java'
|
27
|
+
s.add_development_dependency 'rouge', '~> 1.6.1'
|
53
28
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
s.add_runtime_dependency(%q<kramdown>, [">= 1.3.1"])
|
58
|
-
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
59
|
-
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
60
|
-
s.add_development_dependency(%q<bundler>, ["> 1.0.0"])
|
61
|
-
s.add_development_dependency(%q<jeweler>, ["~> 1.8.4"])
|
62
|
-
s.add_development_dependency(%q<pry>, [">= 0"])
|
63
|
-
s.add_development_dependency(%q<rack-test>, [">= 0"])
|
64
|
-
s.add_development_dependency(%q<rspec>, [">= 0"])
|
65
|
-
else
|
66
|
-
s.add_dependency(%q<grape>, [">= 0.2.0"])
|
67
|
-
s.add_dependency(%q<grape-entity>, [">= 0.3.0"])
|
68
|
-
s.add_dependency(%q<kramdown>, [">= 1.3.1"])
|
69
|
-
s.add_dependency(%q<shoulda>, [">= 0"])
|
70
|
-
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
71
|
-
s.add_dependency(%q<bundler>, ["> 1.0.0"])
|
72
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
73
|
-
s.add_dependency(%q<pry>, [">= 0"])
|
74
|
-
s.add_dependency(%q<rack-test>, [">= 0"])
|
75
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
76
|
-
end
|
77
|
-
else
|
78
|
-
s.add_dependency(%q<grape>, [">= 0.2.0"])
|
79
|
-
s.add_dependency(%q<grape-entity>, [">= 0.3.0"])
|
80
|
-
s.add_dependency(%q<kramdown>, [">= 1.3.1"])
|
81
|
-
s.add_dependency(%q<shoulda>, [">= 0"])
|
82
|
-
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
83
|
-
s.add_dependency(%q<bundler>, ["> 1.0.0"])
|
84
|
-
s.add_dependency(%q<jeweler>, ["~> 1.8.4"])
|
85
|
-
s.add_dependency(%q<pry>, [">= 0"])
|
86
|
-
s.add_dependency(%q<rack-test>, [">= 0"])
|
87
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
88
|
-
end
|
29
|
+
s.files = `git ls-files`.split("\n")
|
30
|
+
s.test_files = `git ls-files -- {test,spec}/*`.split("\n")
|
31
|
+
s.require_paths = ['lib']
|
89
32
|
end
|
90
|
-
|
data/lib/grape-swagger.rb
CHANGED
@@ -1,38 +1,48 @@
|
|
1
|
-
require '
|
1
|
+
require 'grape-swagger/version'
|
2
|
+
require 'grape-swagger/errors'
|
3
|
+
require 'grape-swagger/markdown'
|
4
|
+
require 'grape-swagger/markdown/kramdown_adapter'
|
5
|
+
require 'grape-swagger/markdown/redcarpet_adapter'
|
2
6
|
|
3
7
|
module Grape
|
4
8
|
class API
|
5
9
|
class << self
|
6
|
-
attr_reader :combined_routes
|
10
|
+
attr_reader :combined_routes, :combined_namespaces
|
7
11
|
|
8
|
-
def add_swagger_documentation(options={})
|
12
|
+
def add_swagger_documentation(options = {})
|
9
13
|
documentation_class = create_documentation_class
|
10
14
|
|
11
|
-
documentation_class.setup({:
|
15
|
+
documentation_class.setup({ target_class: self }.merge(options))
|
12
16
|
mount(documentation_class)
|
13
17
|
|
14
18
|
@combined_routes = {}
|
15
|
-
|
16
19
|
routes.each do |route|
|
17
20
|
route_match = route.route_path.split(route.route_prefix).last.match('\/([\w|-]*?)[\.\/\(]')
|
18
21
|
next if route_match.nil?
|
19
22
|
resource = route_match.captures.first
|
20
23
|
next if resource.empty?
|
21
24
|
resource.downcase!
|
22
|
-
|
23
25
|
@combined_routes[resource] ||= []
|
24
|
-
|
25
|
-
|
26
|
-
@combined_routes[resource] << route
|
27
|
-
end
|
26
|
+
next if @@hide_documentation_path && route.route_path.include?(@@mount_path)
|
27
|
+
@combined_routes[resource] << route
|
28
28
|
end
|
29
29
|
|
30
|
+
@combined_namespaces = {}
|
31
|
+
combine_namespaces(self)
|
30
32
|
end
|
31
33
|
|
32
34
|
private
|
33
35
|
|
34
|
-
def
|
36
|
+
def combine_namespaces(app)
|
37
|
+
app.endpoints.each do |endpoint|
|
38
|
+
ns = endpoint.settings.stack.last[:namespace]
|
39
|
+
@combined_namespaces[ns.space] = ns if ns
|
35
40
|
|
41
|
+
combine_namespaces(endpoint.options[:app]) if endpoint.options[:app]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def create_documentation_class
|
36
46
|
Class.new(Grape::API) do
|
37
47
|
class << self
|
38
48
|
def name
|
@@ -42,19 +52,20 @@ module Grape
|
|
42
52
|
|
43
53
|
def self.setup(options)
|
44
54
|
defaults = {
|
45
|
-
:
|
46
|
-
:
|
47
|
-
:
|
48
|
-
:
|
49
|
-
:
|
50
|
-
:
|
51
|
-
:
|
52
|
-
:
|
53
|
-
:
|
54
|
-
:
|
55
|
-
:
|
56
|
-
:
|
57
|
-
:
|
55
|
+
target_class: nil,
|
56
|
+
mount_path: '/swagger_doc',
|
57
|
+
base_path: nil,
|
58
|
+
api_version: '0.1',
|
59
|
+
markdown: nil,
|
60
|
+
hide_documentation_path: false,
|
61
|
+
hide_format: false,
|
62
|
+
format: nil,
|
63
|
+
models: [],
|
64
|
+
info: {},
|
65
|
+
authorizations: nil,
|
66
|
+
root_base_path: true,
|
67
|
+
api_documentation: { desc: 'Swagger compatible API description' },
|
68
|
+
specific_api_documentation: { desc: 'Swagger compatible API description for specific API' }
|
58
69
|
}
|
59
70
|
|
60
71
|
options = defaults.merge(options)
|
@@ -62,14 +73,16 @@ module Grape
|
|
62
73
|
target_class = options[:target_class]
|
63
74
|
@@mount_path = options[:mount_path]
|
64
75
|
@@class_name = options[:class_name] || options[:mount_path].gsub('/', '')
|
65
|
-
@@markdown = options[:markdown]
|
76
|
+
@@markdown = options[:markdown] ? GrapeSwagger::Markdown.new(options[:markdown]) : nil
|
66
77
|
@@hide_format = options[:hide_format]
|
67
78
|
api_version = options[:api_version]
|
68
79
|
base_path = options[:base_path]
|
69
80
|
authorizations = options[:authorizations]
|
70
|
-
include_base_url = options[:include_base_url]
|
71
81
|
root_base_path = options[:root_base_path]
|
72
82
|
extra_info = options[:info]
|
83
|
+
api_doc = options[:api_documentation].dup
|
84
|
+
specific_api_doc = options[:specific_api_documentation].dup
|
85
|
+
@@models = options[:models] || []
|
73
86
|
|
74
87
|
@@hide_documentation_path = options[:hide_documentation_path]
|
75
88
|
|
@@ -79,82 +92,109 @@ module Grape
|
|
79
92
|
end
|
80
93
|
end
|
81
94
|
|
82
|
-
desc
|
95
|
+
desc api_doc.delete(:desc), params: api_doc.delete(:params)
|
96
|
+
@last_description.merge!(api_doc)
|
83
97
|
get @@mount_path do
|
84
98
|
header['Access-Control-Allow-Origin'] = '*'
|
85
99
|
header['Access-Control-Request-Method'] = '*'
|
86
100
|
|
87
|
-
routes = target_class
|
101
|
+
routes = target_class.combined_routes
|
102
|
+
namespaces = target_class.combined_namespaces
|
88
103
|
|
89
104
|
if @@hide_documentation_path
|
90
|
-
routes.reject!{ |route,
|
105
|
+
routes.reject! { |route, _value| "/#{route}/".index(parse_path(@@mount_path, nil) << '/') == 0 }
|
91
106
|
end
|
92
107
|
|
93
108
|
routes_array = routes.keys.map do |local_route|
|
94
109
|
next if routes[local_route].all?(&:route_hidden)
|
95
110
|
|
96
|
-
url_base = parse_path(route.route_path.gsub('(.:format)', ''), route.route_version) if include_base_url
|
97
111
|
url_format = '.{format}' unless @@hide_format
|
112
|
+
|
113
|
+
description = namespaces[local_route] && namespaces[local_route].options[:desc]
|
114
|
+
description ||= "Operations about #{local_route.pluralize}"
|
115
|
+
|
98
116
|
{
|
99
|
-
:
|
100
|
-
|
117
|
+
path: "/#{local_route}#{url_format}",
|
118
|
+
description: description
|
101
119
|
}
|
102
120
|
end.compact
|
103
121
|
|
104
122
|
output = {
|
105
123
|
apiVersion: api_version,
|
106
|
-
swaggerVersion:
|
124
|
+
swaggerVersion: '1.2',
|
107
125
|
produces: content_types_for(target_class),
|
108
|
-
operations: [],
|
109
126
|
apis: routes_array,
|
110
127
|
info: parse_info(extra_info)
|
111
128
|
}
|
112
129
|
|
113
|
-
|
114
|
-
output[:basePath] = basePath if basePath && basePath.size > 0 && root_base_path != false
|
115
|
-
output[:authorizations] = authorizations if authorizations
|
130
|
+
output[:authorizations] = authorizations unless authorizations.nil? || authorizations.empty?
|
116
131
|
|
117
132
|
output
|
118
133
|
end
|
119
134
|
|
120
|
-
desc
|
121
|
-
|
122
|
-
:
|
123
|
-
:
|
124
|
-
:
|
135
|
+
desc specific_api_doc.delete(:desc), params: {
|
136
|
+
'name' => {
|
137
|
+
desc: 'Resource name of mounted API',
|
138
|
+
type: 'string',
|
139
|
+
required: true
|
125
140
|
}
|
126
|
-
}
|
141
|
+
}.merge(specific_api_doc.delete(:params) || {})
|
142
|
+
@last_description.merge!(specific_api_doc)
|
127
143
|
get "#{@@mount_path}/:name" do
|
128
144
|
header['Access-Control-Allow-Origin'] = '*'
|
129
145
|
header['Access-Control-Request-Method'] = '*'
|
130
146
|
|
131
147
|
models = []
|
132
|
-
routes = target_class
|
148
|
+
routes = target_class.combined_routes[params[:name]]
|
149
|
+
error!('Not Found', 404) unless routes
|
133
150
|
|
134
151
|
ops = routes.reject(&:route_hidden).group_by do |route|
|
135
152
|
parse_path(route.route_path, api_version)
|
136
153
|
end
|
137
154
|
|
155
|
+
error!('Not Found', 404) unless ops.any?
|
156
|
+
|
138
157
|
apis = []
|
139
158
|
|
140
|
-
ops.each do |path,
|
141
|
-
operations =
|
159
|
+
ops.each do |path, op_routes|
|
160
|
+
operations = op_routes.map do |route|
|
142
161
|
notes = as_markdown(route.route_notes)
|
143
|
-
http_codes = parse_http_codes(route.route_http_codes)
|
144
162
|
|
145
|
-
|
163
|
+
http_codes = parse_http_codes(route.route_http_codes, models)
|
164
|
+
|
165
|
+
models << @@models if @@models.present?
|
166
|
+
|
167
|
+
models << route.route_entity if route.route_entity.present?
|
168
|
+
|
169
|
+
models = models_with_included_presenters(models.flatten.compact)
|
146
170
|
|
147
171
|
operation = {
|
148
|
-
:
|
149
|
-
:
|
150
|
-
:
|
151
|
-
:
|
152
|
-
:
|
153
|
-
:
|
154
|
-
parse_params(route.route_params, route.route_path, route.route_method)
|
172
|
+
notes: notes.to_s,
|
173
|
+
summary: route.route_description || '',
|
174
|
+
nickname: route.route_nickname || (route.route_method + route.route_path.gsub(/[\/:\(\)\.]/, '-')),
|
175
|
+
method: route.route_method,
|
176
|
+
parameters: parse_header_params(route.route_headers) + parse_params(route.route_params, route.route_path, route.route_method),
|
177
|
+
type: 'void'
|
155
178
|
}
|
156
|
-
operation
|
157
|
-
operation.
|
179
|
+
operation[:authorizations] = route.route_authorizations unless route.route_authorizations.nil? || route.route_authorizations.empty?
|
180
|
+
if operation[:parameters].any? { | param | param[:type] == 'File' }
|
181
|
+
operation.merge!(consumes: ['multipart/form-data'])
|
182
|
+
end
|
183
|
+
operation.merge!(responseMessages: http_codes) unless http_codes.empty?
|
184
|
+
|
185
|
+
if route.route_entity
|
186
|
+
type = parse_entity_name(route.route_entity)
|
187
|
+
if route.instance_variable_get(:@options)[:is_array]
|
188
|
+
operation.merge!(
|
189
|
+
'type' => 'array',
|
190
|
+
'items' => generate_typeref(type)
|
191
|
+
)
|
192
|
+
else
|
193
|
+
operation.merge!('type' => type)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
operation[:nickname] = route.route_nickname if route.route_nickname
|
158
198
|
operation
|
159
199
|
end.compact
|
160
200
|
apis << {
|
@@ -165,14 +205,16 @@ module Grape
|
|
165
205
|
|
166
206
|
api_description = {
|
167
207
|
apiVersion: api_version,
|
168
|
-
swaggerVersion:
|
169
|
-
resourcePath: "",
|
208
|
+
swaggerVersion: '1.2',
|
209
|
+
resourcePath: "/#{params[:name]}",
|
210
|
+
produces: content_types_for(target_class),
|
170
211
|
apis: apis
|
171
212
|
}
|
172
213
|
|
173
|
-
|
174
|
-
api_description[:basePath]
|
175
|
-
api_description[:models]
|
214
|
+
base_path = parse_base_path(base_path, request)
|
215
|
+
api_description[:basePath] = base_path if base_path && base_path.size > 0 && root_base_path != false
|
216
|
+
api_description[:models] = parse_entity_models(models) unless models.empty?
|
217
|
+
api_description[:authorizations] = authorizations if authorizations
|
176
218
|
|
177
219
|
api_description
|
178
220
|
end
|
@@ -181,37 +223,70 @@ module Grape
|
|
181
223
|
helpers do
|
182
224
|
|
183
225
|
def as_markdown(description)
|
184
|
-
description && @@markdown ?
|
226
|
+
description && @@markdown ? @@markdown.as_markdown(strip_heredoc(description)) : description
|
185
227
|
end
|
186
228
|
|
187
229
|
def parse_params(params, path, method)
|
188
230
|
params ||= []
|
189
|
-
|
190
231
|
params.map do |param, value|
|
191
|
-
value[:type] = '
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
232
|
+
value[:type] = 'File' if value.is_a?(Hash) && ['Rack::Multipart::UploadedFile', 'Hash'].include?(value[:type])
|
233
|
+
items = {}
|
234
|
+
|
235
|
+
raw_data_type = value.is_a?(Hash) ? (value[:type] || 'string').to_s : 'string'
|
236
|
+
data_type = case raw_data_type
|
237
|
+
when 'Boolean', 'Date', 'Integer', 'String'
|
238
|
+
raw_data_type.downcase
|
239
|
+
when 'BigDecimal'
|
240
|
+
'long'
|
241
|
+
when 'DateTime'
|
242
|
+
'dateTime'
|
243
|
+
when 'Numeric'
|
244
|
+
'double'
|
245
|
+
else
|
246
|
+
parse_entity_name(raw_data_type)
|
247
|
+
end
|
248
|
+
description = value.is_a?(Hash) ? value[:desc] || value[:description] : ''
|
249
|
+
required = value.is_a?(Hash) ? !!value[:required] : false
|
250
|
+
default_value = value.is_a?(Hash) ? value[:default] : nil
|
251
|
+
is_array = value.is_a?(Hash) ? (value[:is_array] || false) : false
|
252
|
+
enum_values = value.is_a?(Hash) ? value[:values] : nil
|
253
|
+
enum_values = enum_values.call if enum_values && enum_values.is_a?(Proc)
|
254
|
+
|
255
|
+
if value.is_a?(Hash) && value.key?(:param_type)
|
256
|
+
param_type = value[:param_type]
|
257
|
+
if is_array
|
258
|
+
items = { '$ref' => data_type }
|
259
|
+
data_type = 'array'
|
260
|
+
end
|
199
261
|
else
|
200
|
-
|
262
|
+
param_type = case
|
263
|
+
when path.include?(":#{param}")
|
264
|
+
'path'
|
265
|
+
when %w(POST PUT PATCH).include?(method)
|
266
|
+
if is_primitive?(data_type)
|
267
|
+
'form'
|
268
|
+
else
|
269
|
+
'body'
|
270
|
+
end
|
271
|
+
else
|
272
|
+
'query'
|
273
|
+
end
|
201
274
|
end
|
202
|
-
name
|
275
|
+
name = (value.is_a?(Hash) && value[:full_name]) || param
|
203
276
|
|
204
277
|
parsed_params = {
|
205
|
-
paramType:
|
206
|
-
name:
|
207
|
-
description:
|
208
|
-
type:
|
209
|
-
|
210
|
-
|
278
|
+
paramType: param_type,
|
279
|
+
name: name,
|
280
|
+
description: as_markdown(description),
|
281
|
+
type: data_type,
|
282
|
+
required: required,
|
283
|
+
allowMultiple: is_array
|
211
284
|
}
|
212
|
-
|
213
|
-
parsed_params.merge!(
|
214
|
-
|
285
|
+
parsed_params.merge!(format: 'int32') if data_type == 'integer'
|
286
|
+
parsed_params.merge!(format: 'int64') if data_type == 'long'
|
287
|
+
parsed_params.merge!(items: items) if items.present?
|
288
|
+
parsed_params.merge!(defaultValue: default_value) if default_value
|
289
|
+
parsed_params.merge!(enum: enum_values) if enum_values
|
215
290
|
parsed_params
|
216
291
|
end
|
217
292
|
end
|
@@ -222,7 +297,7 @@ module Grape
|
|
222
297
|
if content_types.empty?
|
223
298
|
formats = [target_class.settings[:format], target_class.settings[:default_format]].compact.uniq
|
224
299
|
formats = Grape::Formatter::Base.formatters({}).keys if formats.empty?
|
225
|
-
content_types = Grape::ContentTypes::CONTENT_TYPES.select{|content_type,
|
300
|
+
content_types = Grape::ContentTypes::CONTENT_TYPES.select { |content_type, _mime_type| formats.include? content_type }.values
|
226
301
|
end
|
227
302
|
|
228
303
|
content_types.uniq
|
@@ -231,34 +306,33 @@ module Grape
|
|
231
306
|
def parse_info(info)
|
232
307
|
{
|
233
308
|
contact: info[:contact],
|
234
|
-
description: info[:description],
|
309
|
+
description: as_markdown(info[:description]),
|
235
310
|
license: info[:license],
|
236
311
|
licenseUrl: info[:license_url],
|
237
312
|
termsOfServiceUrl: info[:terms_of_service_url],
|
238
313
|
title: info[:title]
|
239
|
-
}.delete_if{|_, value| value.blank?}
|
314
|
+
}.delete_if { |_, value| value.blank? }
|
240
315
|
end
|
241
316
|
|
242
317
|
def parse_header_params(params)
|
243
318
|
params ||= []
|
244
319
|
|
245
320
|
params.map do |param, value|
|
246
|
-
|
247
|
-
description
|
248
|
-
required
|
249
|
-
|
250
|
-
|
321
|
+
data_type = 'String'
|
322
|
+
description = value.is_a?(Hash) ? value[:description] : ''
|
323
|
+
required = value.is_a?(Hash) ? !!value[:required] : false
|
324
|
+
default_value = value.is_a?(Hash) ? value[:default] : nil
|
325
|
+
param_type = 'header'
|
251
326
|
|
252
327
|
parsed_params = {
|
253
|
-
paramType:
|
328
|
+
paramType: param_type,
|
254
329
|
name: param,
|
255
330
|
description: as_markdown(description),
|
256
|
-
type:
|
257
|
-
dataType: dataType,
|
331
|
+
type: data_type,
|
258
332
|
required: required
|
259
333
|
}
|
260
334
|
|
261
|
-
parsed_params.merge!(
|
335
|
+
parsed_params.merge!(defaultValue: default_value) if default_value
|
262
336
|
|
263
337
|
parsed_params
|
264
338
|
end
|
@@ -277,46 +351,98 @@ module Grape
|
|
277
351
|
version ? parsed_path.gsub('{version}', version) : parsed_path
|
278
352
|
end
|
279
353
|
|
280
|
-
def parse_entity_name(
|
281
|
-
|
282
|
-
|
283
|
-
|
354
|
+
def parse_entity_name(model)
|
355
|
+
if model.respond_to?(:entity_name)
|
356
|
+
model.entity_name
|
357
|
+
else
|
358
|
+
name = model.to_s
|
359
|
+
entity_parts = name.split('::')
|
360
|
+
entity_parts.reject! { |p| p == 'Entity' || p == 'Entities' }
|
361
|
+
entity_parts.join('::')
|
362
|
+
end
|
284
363
|
end
|
285
364
|
|
286
365
|
def parse_entity_models(models)
|
287
366
|
result = {}
|
288
|
-
|
289
367
|
models.each do |model|
|
290
|
-
name
|
291
|
-
properties
|
368
|
+
name = parse_entity_name(model)
|
369
|
+
properties = {}
|
370
|
+
required = []
|
292
371
|
|
293
372
|
model.documentation.each do |property_name, property_info|
|
294
|
-
|
373
|
+
p = property_info.dup
|
374
|
+
|
375
|
+
required << property_name.to_s if p.delete(:required)
|
376
|
+
|
377
|
+
if p.delete(:is_array)
|
378
|
+
p[:items] = generate_typeref(p[:type])
|
379
|
+
p[:type] = 'array'
|
380
|
+
else
|
381
|
+
p.merge! generate_typeref(p.delete(:type))
|
382
|
+
end
|
295
383
|
|
296
384
|
# rename Grape Entity's "desc" to "description"
|
297
|
-
|
298
|
-
|
385
|
+
property_description = p.delete(:desc)
|
386
|
+
p[:description] = property_description if property_description
|
387
|
+
|
388
|
+
# rename Grape's 'values' to 'enum'
|
389
|
+
select_values = p.delete(:values)
|
390
|
+
if select_values
|
391
|
+
select_values = select_values.call if select_values.is_a?(Proc)
|
392
|
+
p[:enum] = select_values
|
299
393
|
end
|
394
|
+
|
395
|
+
properties[property_name] = p
|
396
|
+
|
300
397
|
end
|
301
398
|
|
302
399
|
result[name] = {
|
303
400
|
id: model.instance_variable_get(:@root) || name,
|
304
|
-
name: model.instance_variable_get(:@root) || name,
|
305
401
|
properties: properties
|
306
402
|
}
|
403
|
+
result[name].merge!(required: required) unless required.empty?
|
307
404
|
end
|
308
405
|
|
309
406
|
result
|
310
407
|
end
|
311
408
|
|
312
|
-
def
|
409
|
+
def models_with_included_presenters(models)
|
410
|
+
all_models = models
|
411
|
+
|
412
|
+
models.each do |model|
|
413
|
+
# get model references from exposures with a documentation
|
414
|
+
additional_models = model.exposures.map do |_, config|
|
415
|
+
config[:using] if config.key?(:documentation)
|
416
|
+
end.compact
|
417
|
+
|
418
|
+
all_models += additional_models
|
419
|
+
end
|
420
|
+
|
421
|
+
all_models
|
422
|
+
end
|
423
|
+
|
424
|
+
def is_primitive?(type)
|
425
|
+
%w(integer long float double string byte boolean date dateTime).include? type
|
426
|
+
end
|
427
|
+
|
428
|
+
def generate_typeref(type)
|
429
|
+
if is_primitive? type
|
430
|
+
{ 'type' => type }
|
431
|
+
else
|
432
|
+
{ '$ref' => type }
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
def parse_http_codes(codes, models)
|
313
437
|
codes ||= {}
|
314
|
-
codes.map do |k, v|
|
315
|
-
|
438
|
+
codes.map do |k, v, m|
|
439
|
+
models << m if m
|
440
|
+
http_code_hash = {
|
316
441
|
code: k,
|
317
|
-
message: v
|
318
|
-
#responseModel: ...
|
442
|
+
message: v
|
319
443
|
}
|
444
|
+
http_code_hash[:responseModel] = parse_entity_name(m) if m
|
445
|
+
http_code_hash
|
320
446
|
end
|
321
447
|
end
|
322
448
|
|