grape-swagger 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -33,7 +33,15 @@ module API
33
33
  end
34
34
  ```
35
35
 
36
- To explore your API, either download [Swagger UI](https://github.com/wordnik/swagger-ui) and set it up yourself or go to the [online swagger demo](http://petstore.swagger.wordnik.com/) and enter your localhost url documentation root in the url field (probably something in the line of http://localhost:3000/swagger_doc.json)
36
+ To explore your API, either download [Swagger UI](https://github.com/wordnik/swagger-ui) and set it up yourself or go to the [online swagger demo](http://petstore.swagger.wordnik.com/) and enter your localhost url documentation root in the url field (probably something in the line of http://localhost:3000/swagger_doc.json).
37
+ If you use the online demo, make sure your API supports foreign requests by enabling CORS in grape, otherwise you'll see the API description, but requests on the API won't return. You can do this by putting below code in your Grape API definition:
38
+
39
+ ```` ruby
40
+ before do
41
+ header['Access-Control-Allow-Origin'] = '*'
42
+ header['Access-Control-Request-Method'] = '*'
43
+ end
44
+ ````
37
45
 
38
46
  ## Configure
39
47
  You can pass a hash with some configuration possibilities to ```add_swagger_documentation```, all of these are optional:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 0.4.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "grape-swagger"
8
- s.version = "0.3.0"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tim Vandecasteele"]
12
- s.date = "2012-10-19"
12
+ s.date = "2013-03-28"
13
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
14
  s.email = "tim.vandecasteele@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -11,7 +11,8 @@ module Grape
11
11
  original_mount mounts
12
12
  @combined_routes ||= {}
13
13
  mounts::routes.each do |route|
14
- resource = route.route_path.match('\/(\w*?)[\.\/\(]').captures.first || '/'
14
+ resource = route.route_path.match('\/(\w*?)[\.\/\(]').captures.first
15
+ next if resource.empty?
15
16
  @combined_routes[resource.downcase] ||= []
16
17
  @combined_routes[resource.downcase] << route
17
18
  end
@@ -64,13 +65,13 @@ module Grape
64
65
  routes.reject!{ |route, value| "/#{route}/".index(parse_path(@@mount_path, nil) << '/') == 0 }
65
66
  end
66
67
 
67
- routes_array = routes.keys.map do |route|
68
- { :path => "#{@@mount_path}/#{route}.{format}" }
68
+ routes_array = routes.keys.map do |local_route|
69
+ { :path => "#{parse_path(route.route_path.gsub('(.:format)', ''),route.route_version)}/#{local_route}.{format}" }
69
70
  end
70
71
  {
71
72
  apiVersion: api_version,
72
73
  swaggerVersion: "1.1",
73
- basePath: base_path || "http://#{env['HTTP_HOST']}",
74
+ basePath: base_path || request.base_url,
74
75
  operations:[],
75
76
  apis: routes_array
76
77
  }
@@ -86,23 +87,26 @@ module Grape
86
87
  routes = @@target_class::combined_routes[params[:name]]
87
88
  routes_array = routes.map do |route|
88
89
  notes = route.route_notes && @@markdown ? Kramdown::Document.new(route.route_notes.strip_heredoc).to_html : route.route_notes
89
- {
90
- :path => parse_path(route.route_path, api_version),
91
- :operations => [{
90
+ http_codes = parse_http_codes route.route_http_codes
91
+ operations = {
92
92
  :notes => notes,
93
93
  :summary => route.route_description || '',
94
94
  :nickname => route.route_method + route.route_path.gsub(/[\/:\(\)\.]/,'-'),
95
95
  :httpMethod => route.route_method,
96
96
  :parameters => parse_header_params(route.route_headers) +
97
97
  parse_params(route.route_params, route.route_path, route.route_method)
98
- }]
98
+ }
99
+ operations.merge!({:errorResponses => http_codes}) unless http_codes.empty?
100
+ {
101
+ :path => parse_path(route.route_path, api_version),
102
+ :operations => [operations]
99
103
  }
100
104
  end
101
105
 
102
106
  {
103
107
  apiVersion: api_version,
104
108
  swaggerVersion: "1.1",
105
- basePath: base_path || "http://#{env['HTTP_HOST']}",
109
+ basePath: base_path || request.base_url,
106
110
  resourcePath: "",
107
111
  apis: routes_array
108
112
  }
@@ -158,16 +162,22 @@ module Grape
158
162
  def parse_path(path, version)
159
163
  # adapt format to swagger format
160
164
  parsed_path = path.gsub('(.:format)', '.{format}')
161
- # This is attempting to emulate the behavior of
162
- # Rack::Mount::Strexp. We cannot use Strexp directly because
163
- # all it does is generate regular expressions for parsing URLs.
164
- # TODO: Implement a Racc tokenizer to properly generate the
165
+ # This is attempting to emulate the behavior of
166
+ # Rack::Mount::Strexp. We cannot use Strexp directly because
167
+ # all it does is generate regular expressions for parsing URLs.
168
+ # TODO: Implement a Racc tokenizer to properly generate the
165
169
  # parsed path.
166
170
  parsed_path = parsed_path.gsub(/:([a-zA-Z_]\w*)/, '{\1}')
167
171
  # add the version
168
172
  parsed_path = parsed_path.gsub('{version}', version) if version
169
173
  parsed_path
170
174
  end
175
+ def parse_http_codes codes
176
+ codes ||= {}
177
+ codes.collect do |k, v|
178
+ {:code => k, :reason => v}
179
+ end
180
+ end
171
181
  end
172
182
  end
173
183
  end
@@ -2,87 +2,87 @@ require 'spec_helper'
2
2
 
3
3
  describe "helpers" do
4
4
 
5
- before(:all) do
6
- class HelperTestAPI < Grape::API
7
- add_swagger_documentation
8
- end
5
+ before(:all) do
6
+ class HelperTestAPI < Grape::API
7
+ add_swagger_documentation
8
+ end
9
9
 
10
- @api = Object.new
11
- # after injecting grape-swagger into the Test API we get the helper methods
12
- # back from the first endpoint's class (the API mounted by grape-swagger
13
- # to serve the swagger_doc
14
- @api.extend HelperTestAPI.endpoints.first.options[:app].helpers
10
+ @api = Object.new
11
+ # after injecting grape-swagger into the Test API we get the helper methods
12
+ # back from the first endpoint's class (the API mounted by grape-swagger
13
+ # to serve the swagger_doc
14
+ @api.extend HelperTestAPI.endpoints.first.options[:app].helpers
15
15
 
16
- end
16
+ end
17
17
 
18
- context "parsing parameters" do
19
- it "should parse params as query strings for a GET" do
20
- params = {
21
- name: {type: 'String', :desc => "A name", required: true },
22
- level: 'max'
23
- }
24
- path = "/coolness"
25
- method = "GET"
26
- @api.parse_params(params, path, method).should ==
27
- [
28
- {paramType: "query", name: :name, description:"A name", dataType: "String", required: true},
29
- {paramType: "query", name: :level, description:"", dataType: "String", required: false}
30
- ]
31
- end
18
+ context "parsing parameters" do
19
+ it "should parse params as query strings for a GET" do
20
+ params = {
21
+ name: {type: 'String', :desc => "A name", required: true },
22
+ level: 'max'
23
+ }
24
+ path = "/coolness"
25
+ method = "GET"
26
+ @api.parse_params(params, path, method).should ==
27
+ [
28
+ {paramType: "query", name: :name, description:"A name", dataType: "String", required: true},
29
+ {paramType: "query", name: :level, description:"", dataType: "String", required: false}
30
+ ]
31
+ end
32
32
 
33
- it "should parse params as body for a POST" do
34
- params = {
35
- name: {type: 'String', :desc =>"A name", required: true },
36
- level: 'max'
37
- }
38
- path = "/coolness"
39
- method = "POST"
40
- @api.parse_params(params, path, method).should ==
41
- [
42
- {paramType: "body", name: :name, description:"A name", dataType: "String", required: true},
43
- {paramType: "body", name: :level, description:"", dataType: "String", required: false}
44
- ]
45
- end
46
- end
33
+ it "should parse params as body for a POST" do
34
+ params = {
35
+ name: {type: 'String', :desc =>"A name", required: true },
36
+ level: 'max'
37
+ }
38
+ path = "/coolness"
39
+ method = "POST"
40
+ @api.parse_params(params, path, method).should ==
41
+ [
42
+ {paramType: "body", name: :name, description:"A name", dataType: "String", required: true},
43
+ {paramType: "body", name: :level, description:"", dataType: "String", required: false}
44
+ ]
45
+ end
46
+ end
47
47
 
48
- context "parsing the path" do
49
- it "should parse the path" do
50
- path = ":abc/def(.:format)"
51
- @api.parse_path(path, nil).should == "{abc}/def.{format}"
52
- end
48
+ context "parsing the path" do
49
+ it "should parse the path" do
50
+ path = ":abc/def(.:format)"
51
+ @api.parse_path(path, nil).should == "{abc}/def.{format}"
52
+ end
53
53
 
54
54
  it "should parse a path that has vars with underscores in the name" do
55
55
  path = "abc/:def_g(.:format)"
56
- @api.parse_path(path, nil).should == "abc/{def_g}.{format}"
57
-
56
+ @api.parse_path(path, nil).should == "abc/{def_g}.{format}"
57
+
58
58
  end
59
59
 
60
60
  it "should parse a path that has vars with numbers in the name" do
61
61
  path = "abc/:sha1(.:format)"
62
- @api.parse_path(path, nil).should == "abc/{sha1}.{format}"
62
+ @api.parse_path(path, nil).should == "abc/{sha1}.{format}"
63
63
  end
64
64
 
65
65
  it "should parse a path that has multiple variables" do
66
66
  path1 = "abc/:def/:geh(.:format)"
67
67
  path2 = "abc/:def:geh(.:format)"
68
- @api.parse_path(path1, nil).should == "abc/{def}/{geh}.{format}"
69
- @api.parse_path(path2, nil).should == "abc/{def}{geh}.{format}"
68
+ @api.parse_path(path1, nil).should == "abc/{def}/{geh}.{format}"
69
+ @api.parse_path(path2, nil).should == "abc/{def}{geh}.{format}"
70
70
  end
71
71
 
72
- it "should parse the path with a specified version" do
73
- path = ":abc/{version}/def(.:format)"
74
- @api.parse_path(path, 'v1').should == "{abc}/v1/def.{format}"
75
- end
76
- end
72
+ it "should parse the path with a specified version" do
73
+ path = ":abc/{version}/def(.:format)"
74
+ @api.parse_path(path, 'v1').should == "{abc}/v1/def.{format}"
75
+ end
76
+ end
77
77
 
78
- context "parsing header parameters" do
79
- it "should parse params for the header" do
80
- params = {"XAuthToken" => { description: "A required header.", required: true}}
81
- @api.parse_header_params(params).should ==
82
- [
83
- {paramType: "header", name: "XAuthToken", description:"A required header.", dataType: "String", required: true}
84
- ]
85
- end
86
- end
78
+ context "parsing header parameters" do
79
+ it "should parse params for the header" do
80
+ params = {"XAuthToken" => { description: "A required header.", required: true}}
81
+ @api.parse_header_params(params).should ==
82
+ [
83
+ {paramType: "header", name: "XAuthToken", description:"A required header.", dataType: "String", required: true}
84
+ ]
85
+ end
86
+ end
87
87
 
88
88
  end
@@ -61,6 +61,38 @@ describe "options: " do
61
61
  end
62
62
  end
63
63
 
64
+ context "mounting in a versioned api" do
65
+ before(:all) do
66
+ class SimpleApiToMountInVersionedApi < Grape::API
67
+ desc 'this gets something'
68
+ get '/something' do
69
+ {:bla => 'something'}
70
+ end
71
+ end
72
+
73
+ class SimpleApiWithVersionInPath < Grape::API
74
+ version 'v1', :using => :path
75
+
76
+ mount SimpleApiToMountInVersionedApi
77
+ add_swagger_documentation
78
+ end
79
+ end
80
+
81
+ def app; SimpleApiWithVersionInPath end
82
+
83
+ it "should get the documentation on a versioned path /v1/swagger_doc" do
84
+ get '/v1/swagger_doc'
85
+ last_response.body.should == "{:apiVersion=>\"0.1\", :swaggerVersion=>\"1.1\", :basePath=>\"http://example.org\", :operations=>[], :apis=>[{:path=>\"/v1/swagger_doc/something.{format}\"}, {:path=>\"/v1/swagger_doc/swagger_doc.{format}\"}]}"
86
+ end
87
+
88
+ it "should get the resource specific documentation on a versioned path /v1/swagger_doc/something" do
89
+ get '/v1/swagger_doc/something'
90
+ last_response.status.should == 200
91
+ end
92
+
93
+ end
94
+
95
+
64
96
  context "overruling hiding the documentation paths" do
65
97
  before(:all) do
66
98
  class HideDocumentationPathMountedApi < Grape::API
@@ -80,7 +112,7 @@ describe "options: " do
80
112
 
81
113
  it "it doesn't show the documentation path on /swagger_doc" do
82
114
  get '/swagger_doc'
83
- last_response.body.should == "{:apiVersion=>\"0.1\", :swaggerVersion=>\"1.1\", :basePath=>\"http://example.org\", :operations=>[], :apis=>[{:path=>\"/swagger_doc/something.{format}\"}]}"
115
+ last_response.body.should == "{:apiVersion=>\"0.1\", :swaggerVersion=>\"1.1\", :basePath=>\"http://example.org\", :operations=>[], :apis=>[{:path=>\"/swagger_doc/something.{format}\"}]}"
84
116
  end
85
117
  end
86
118
 
@@ -143,32 +175,58 @@ describe "options: " do
143
175
  last_response.body.should == "{:apiVersion=>\"0.1\", :swaggerVersion=>\"1.1\", :basePath=>\"http://example.org\", :resourcePath=>\"\", :apis=>[{:path=>\"/something.{format}\", :operations=>[{:notes=>\"<p><em>test</em></p>\\n\", :summary=>\"this gets something\", :nickname=>\"GET-something---format-\", :httpMethod=>\"GET\", :parameters=>[]}]}]}"
144
176
  end
145
177
  end
146
-
178
+
147
179
  context "versioned API" do
148
180
  before(:all) do
149
181
  class VersionedMountedApi < Grape::API
150
182
  prefix 'api'
151
183
  version 'v1'
152
-
184
+
153
185
  desc 'this gets something'
154
186
  get '/something' do
155
187
  {:bla => 'something'}
156
188
  end
157
189
  end
158
-
190
+
159
191
  class SimpleApiWithVersion < Grape::API
160
192
  mount VersionedMountedApi
161
193
  add_swagger_documentation :api_version => "v1"
162
194
  end
163
195
  end
164
-
196
+
165
197
  def app; SimpleApiWithVersion end
166
-
198
+
167
199
  it "parses version and places it in the path" do
168
200
  get '/swagger_doc/api'
169
201
  last_response.body.should == "{:apiVersion=>\"v1\", :swaggerVersion=>\"1.1\", :basePath=>\"http://example.org\", :resourcePath=>\"\", :apis=>[{:path=>\"/api/v1/something.{format}\", :operations=>[{:notes=>nil, :summary=>\"this gets something\", :nickname=>\"GET-api--version-something---format-\", :httpMethod=>\"GET\", :parameters=>[]}]}]}"
170
202
  end
171
203
  end
172
204
 
205
+ context "protected API" do
206
+ before(:all) do
207
+ class ProtectedApi < Grape::API
208
+ desc 'this gets something'
209
+ get '/something' do
210
+ {:bla => 'something'}
211
+ end
212
+ end
213
+
214
+ class SimpleApiWithProtection < Grape::API
215
+ mount ProtectedApi
216
+ add_swagger_documentation
217
+ end
218
+ end
219
+
220
+ def app; SimpleApiWithProtection; end
221
+
222
+ it "should use https schema in mount point" do
223
+ get '/swagger_doc', {}, 'rack.url_scheme' => 'https'
224
+ last_response.body.should == "{:apiVersion=>\"0.1\", :swaggerVersion=>\"1.1\", :basePath=>\"https://example.org\", :operations=>[], :apis=>[{:path=>\"/swagger_doc/something.{format}\"}, {:path=>\"/swagger_doc/swagger_doc.{format}\"}]}"
225
+ end
173
226
 
227
+ it "should use https schema in endpoint doc" do
228
+ get '/swagger_doc/something', {}, 'rack.url_scheme' => 'https'
229
+ last_response.body.should == "{:apiVersion=>\"0.1\", :swaggerVersion=>\"1.1\", :basePath=>\"https://example.org\", :resourcePath=>\"\", :apis=>[{:path=>\"/something.{format}\", :operations=>[{:notes=>nil, :summary=>\"this gets something\", :nickname=>\"GET-something---format-\", :httpMethod=>\"GET\", :parameters=>[]}]}]}"
230
+ end
231
+ end
174
232
  end
@@ -3,18 +3,27 @@ require 'spec_helper'
3
3
  describe "a simple mounted api" do
4
4
  before(:all) do
5
5
  class SimpleMountedApi < Grape::API
6
+ desc "Document root"
7
+ get do
8
+ end
9
+
6
10
  desc 'this gets something', {
7
11
  :notes => '_test_'
8
12
  }
13
+
9
14
  get '/simple' do
10
15
  {:bla => 'something'}
11
16
  end
12
-
17
+
13
18
  desc 'this gets something else', {
14
19
  :headers => {
15
- "XAuthToken" => {description: "A required header.", required: true},
20
+ "XAuthToken" => {description: "A required header.", required: true},
16
21
  "XOtherHeader" => {description: "An optional header.", required: false}
17
- }
22
+ },
23
+ :http_codes => {
24
+ 403 => "invalid pony",
25
+ 405 => "no ponies left!"
26
+ }
18
27
  }
19
28
  get '/simple_with_headers' do
20
29
  {:bla => 'something_else'}
@@ -41,6 +50,6 @@ describe "a simple mounted api" do
41
50
 
42
51
  it "retrieves the documentation for mounted-api that includes headers" do
43
52
  get '/swagger_doc/simple_with_headers'
44
- last_response.body.should == "{:apiVersion=>\"0.1\", :swaggerVersion=>\"1.1\", :basePath=>\"http://example.org\", :resourcePath=>\"\", :apis=>[{:path=>\"/simple_with_headers.{format}\", :operations=>[{:notes=>nil, :summary=>\"this gets something else\", :nickname=>\"GET-simple_with_headers---format-\", :httpMethod=>\"GET\", :parameters=>[{:paramType=>\"header\", :name=>\"XAuthToken\", :description=>\"A required header.\", :dataType=>\"String\", :required=>true}, {:paramType=>\"header\", :name=>\"XOtherHeader\", :description=>\"An optional header.\", :dataType=>\"String\", :required=>false}]}]}]}"
53
+ last_response.body.should == "{:apiVersion=>\"0.1\", :swaggerVersion=>\"1.1\", :basePath=>\"http://example.org\", :resourcePath=>\"\", :apis=>[{:path=>\"/simple_with_headers.{format}\", :operations=>[{:notes=>nil, :summary=>\"this gets something else\", :nickname=>\"GET-simple_with_headers---format-\", :httpMethod=>\"GET\", :parameters=>[{:paramType=>\"header\", :name=>\"XAuthToken\", :description=>\"A required header.\", :dataType=>\"String\", :required=>true}, {:paramType=>\"header\", :name=>\"XOtherHeader\", :description=>\"An optional header.\", :dataType=>\"String\", :required=>false}], :errorResponses=>[{:code=>403, :reason=>\"invalid pony\"}, {:code=>405, :reason=>\"no ponies left!\"}]}]}]}"
45
54
  end
46
55
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grape-swagger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-19 00:00:00.000000000 Z
12
+ date: 2013-03-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: grape
@@ -194,7 +194,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
194
194
  version: '0'
195
195
  segments:
196
196
  - 0
197
- hash: 3307258424823840533
197
+ hash: -2543404597308803068
198
198
  required_rubygems_version: !ruby/object:Gem::Requirement
199
199
  none: false
200
200
  requirements: