grape-swagger 0.6.0 → 0.7.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.
- data/.rspec +2 -0
- data/.rvmrc +1 -1
- data/.travis.yml +2 -0
- data/CHANGELOG.markdown +9 -0
- data/Gemfile +2 -1
- data/Gemfile.lock +24 -10
- data/README.markdown +58 -6
- data/VERSION +1 -1
- data/grape-swagger.gemspec +14 -7
- data/lib/grape-swagger.rb +235 -93
- data/spec/api_models_spec.rb +132 -0
- data/spec/default_api_spec.rb +80 -10
- data/spec/form_params_spec.rb +83 -0
- data/spec/grape-swagger_helper_spec.rb +107 -0
- data/spec/grape-swagger_spec.rb +0 -2
- data/spec/hide_api_spec.rb +106 -0
- data/spec/non_default_api_spec.rb +318 -78
- data/spec/simple_mounted_api_spec.rb +139 -19
- data/spec/spec_helper.rb +2 -10
- metadata +26 -6
- data/spec/grape-swagger-helper_spec.rb +0 -88
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "API Models" do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
module Entities
|
7
|
+
class Something < Grape::Entity
|
8
|
+
expose :text, :documentation => { :type => "string", :desc => "Content of something." }
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Entities
|
13
|
+
module Some
|
14
|
+
class Thing < Grape::Entity
|
15
|
+
expose :text, :documentation => { :type => "string", :desc => "Content of something." }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class ModelsApi < Grape::API
|
21
|
+
format :json
|
22
|
+
desc 'This gets something.', {
|
23
|
+
entity: Entities::Something
|
24
|
+
}
|
25
|
+
get '/something' do
|
26
|
+
something = OpenStruct.new text: 'something'
|
27
|
+
present something, with: Entities::Something
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'This gets thing.', {
|
31
|
+
entity: Entities::Some::Thing
|
32
|
+
}
|
33
|
+
get "/thing" do
|
34
|
+
thing = OpenStruct.new text: 'thing'
|
35
|
+
present thing, with: Entities::Some::Thing
|
36
|
+
end
|
37
|
+
add_swagger_documentation
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def app; ModelsApi; end
|
42
|
+
|
43
|
+
it "should document specified models" do
|
44
|
+
get '/swagger_doc'
|
45
|
+
JSON.parse(last_response.body).should == {
|
46
|
+
"apiVersion" => "0.1",
|
47
|
+
"swaggerVersion" => "1.2",
|
48
|
+
"basePath" => "http://example.org",
|
49
|
+
"info" => {},
|
50
|
+
"produces" => ["application/json"],
|
51
|
+
"operations" => [],
|
52
|
+
"apis" => [
|
53
|
+
{ "path" => "/something.{format}" },
|
54
|
+
{ "path" => "/thing.{format}" },
|
55
|
+
{ "path" => "/swagger_doc.{format}" }
|
56
|
+
]
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should include type when specified" do
|
61
|
+
get '/swagger_doc/something.json'
|
62
|
+
JSON.parse(last_response.body).should == {
|
63
|
+
"apiVersion" => "0.1",
|
64
|
+
"swaggerVersion" => "1.2",
|
65
|
+
"basePath" => "http://example.org",
|
66
|
+
"resourcePath" => "",
|
67
|
+
"apis" => [{
|
68
|
+
"path" => "/something.{format}",
|
69
|
+
"operations" => [{
|
70
|
+
"produces" => [
|
71
|
+
"application/json"
|
72
|
+
],
|
73
|
+
"notes" => "",
|
74
|
+
"type" => "Something",
|
75
|
+
"summary" => "This gets something.",
|
76
|
+
"nickname" => "GET-something---format-",
|
77
|
+
"httpMethod" => "GET",
|
78
|
+
"parameters" => []
|
79
|
+
}]
|
80
|
+
}],
|
81
|
+
"models" => {
|
82
|
+
"Something" => {
|
83
|
+
"id" => "Something",
|
84
|
+
"name" => "Something",
|
85
|
+
"properties" => {
|
86
|
+
"text" => {
|
87
|
+
"type" => "string",
|
88
|
+
"description" => "Content of something."
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should include nested type when specified" do
|
97
|
+
get '/swagger_doc/thing.json'
|
98
|
+
JSON.parse(last_response.body).should == {
|
99
|
+
"apiVersion" => "0.1",
|
100
|
+
"swaggerVersion" => "1.2",
|
101
|
+
"basePath" => "http://example.org",
|
102
|
+
"resourcePath" => "",
|
103
|
+
"apis" => [{
|
104
|
+
"path" => "/thing.{format}",
|
105
|
+
"operations" => [{
|
106
|
+
"produces" => [
|
107
|
+
"application/json"
|
108
|
+
],
|
109
|
+
"notes" => "",
|
110
|
+
"type" => "Some::Thing",
|
111
|
+
"summary" => "This gets thing.",
|
112
|
+
"nickname" => "GET-thing---format-",
|
113
|
+
"httpMethod" => "GET",
|
114
|
+
"parameters" => []
|
115
|
+
}]
|
116
|
+
}],
|
117
|
+
"models" => {
|
118
|
+
"Some::Thing" => {
|
119
|
+
"id" => "Some::Thing",
|
120
|
+
"name" => "Some::Thing",
|
121
|
+
"properties" => {
|
122
|
+
"text" => {
|
123
|
+
"type" => "string",
|
124
|
+
"description" => "Content of something."
|
125
|
+
}
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
data/spec/default_api_spec.rb
CHANGED
@@ -2,21 +2,91 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "Default API" do
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
context 'with no additional options' do
|
6
|
+
before :all do
|
7
|
+
class NotAMountedApi < Grape::API
|
8
|
+
format :json
|
9
|
+
desc 'This gets something.'
|
10
|
+
get '/something' do
|
11
|
+
{ bla: 'something' }
|
12
|
+
end
|
13
|
+
add_swagger_documentation
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def app; NotAMountedApi; end
|
18
|
+
|
19
|
+
it "should document something" do
|
20
|
+
get '/swagger_doc'
|
21
|
+
JSON.parse(last_response.body).should == {
|
22
|
+
"apiVersion" => "0.1",
|
23
|
+
"swaggerVersion" => "1.2",
|
24
|
+
"basePath" => "http://example.org",
|
25
|
+
"info" => {},
|
26
|
+
"produces" => ["application/json"],
|
27
|
+
"operations" => [],
|
28
|
+
"apis" => [
|
29
|
+
{ "path" => "/something.{format}" },
|
30
|
+
{ "path" => "/swagger_doc.{format}" }
|
31
|
+
]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
context "path inside the apis array" do
|
36
|
+
it "should start with a forward slash" do
|
37
|
+
get '/swagger_doc'
|
38
|
+
JSON.parse(last_response.body)['apis'].each do |api|
|
39
|
+
api['path'].should start_with "/"
|
40
|
+
end
|
10
41
|
end
|
11
|
-
add_swagger_documentation
|
12
42
|
end
|
13
43
|
end
|
14
44
|
|
15
|
-
|
45
|
+
context 'with API info' do
|
46
|
+
before :all do
|
47
|
+
class ApiInfoTest < Grape::API
|
48
|
+
format :json
|
49
|
+
add_swagger_documentation info: {
|
50
|
+
title: 'My API Title',
|
51
|
+
description: 'A description of my API',
|
52
|
+
license: 'Apache 2',
|
53
|
+
license_url: 'http://test.com',
|
54
|
+
terms_of_service_url: 'http://terms.com',
|
55
|
+
contact: 'support@test.com'
|
56
|
+
}
|
57
|
+
end
|
58
|
+
get '/swagger_doc'
|
59
|
+
end
|
60
|
+
|
61
|
+
def app; ApiInfoTest; end
|
16
62
|
|
17
|
-
|
18
|
-
|
19
|
-
|
63
|
+
subject do
|
64
|
+
JSON.parse(last_response.body)['info']
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should document API title' do
|
68
|
+
expect(subject['title']).to eql('My API Title')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should document API description' do
|
72
|
+
expect(subject['description']).to eql('A description of my API')
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should document the license' do
|
76
|
+
expect(subject['license']).to eql('Apache 2')
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should document the license url' do
|
80
|
+
expect(subject['licenseUrl']).to eql('http://test.com')
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should document the terms of service url' do
|
84
|
+
expect(subject['termsOfServiceUrl']).to eql('http://terms.com')
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'should document the contact email' do
|
88
|
+
expect(subject['contact']).to eql('support@test.com')
|
89
|
+
end
|
20
90
|
end
|
21
91
|
|
22
92
|
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Form Params" do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
class FormParamApi < Grape::API
|
7
|
+
format :json
|
8
|
+
|
9
|
+
params do
|
10
|
+
requires :name, type: String, desc: "name of item"
|
11
|
+
end
|
12
|
+
post '/items' do
|
13
|
+
{}
|
14
|
+
end
|
15
|
+
|
16
|
+
params do
|
17
|
+
requires :id, type: Integer, desc: "id of item"
|
18
|
+
requires :name, type: String, desc: "name of item"
|
19
|
+
end
|
20
|
+
put '/items/:id' do
|
21
|
+
{}
|
22
|
+
end
|
23
|
+
|
24
|
+
params do
|
25
|
+
requires :id, type: Integer, desc: "id of item"
|
26
|
+
requires :name, type: String, desc: "name of item"
|
27
|
+
end
|
28
|
+
patch '/items/:id' do
|
29
|
+
{}
|
30
|
+
end
|
31
|
+
|
32
|
+
add_swagger_documentation
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def app; FormParamApi; end
|
37
|
+
|
38
|
+
it "retrieves the documentation form params" do
|
39
|
+
get '/swagger_doc/items.json'
|
40
|
+
|
41
|
+
JSON.parse(last_response.body).should == {
|
42
|
+
"apiVersion" => "0.1",
|
43
|
+
"swaggerVersion" => "1.2",
|
44
|
+
"resourcePath" => "",
|
45
|
+
"basePath"=>"http://example.org",
|
46
|
+
"apis" => [
|
47
|
+
{
|
48
|
+
"path" => "/items.{format}",
|
49
|
+
"operations" => [
|
50
|
+
{
|
51
|
+
"produces" => ["application/json"],
|
52
|
+
"notes" => "",
|
53
|
+
"summary" => "",
|
54
|
+
"nickname" => "POST-items---format-",
|
55
|
+
"httpMethod" => "POST",
|
56
|
+
"parameters" => [ { "paramType" => "form", "name" => "name", "description" => "name of item", "type" => "String", "dataType" => "String", "required" => true } ]
|
57
|
+
}
|
58
|
+
]
|
59
|
+
}, {
|
60
|
+
"path" => "/items/{id}.{format}",
|
61
|
+
"operations" => [
|
62
|
+
{
|
63
|
+
"produces" => ["application/json"],
|
64
|
+
"notes" => "",
|
65
|
+
"summary" => "",
|
66
|
+
"nickname" => "PUT-items--id---format-",
|
67
|
+
"httpMethod" => "PUT",
|
68
|
+
"parameters" => [ { "paramType" => "path", "name" => "id", "description" => "id of item", "type" => "Integer", "dataType" => "Integer", "required" => true }, { "paramType" => "form", "name" => "name", "description" => "name of item", "type" => "String", "dataType" => "String", "required" => true } ]
|
69
|
+
},
|
70
|
+
{
|
71
|
+
"produces" => ["application/json"],
|
72
|
+
"notes" => "",
|
73
|
+
"summary" => "",
|
74
|
+
"nickname" => "PATCH-items--id---format-",
|
75
|
+
"httpMethod" => "PATCH",
|
76
|
+
"parameters" => [ { "paramType" => "path", "name" => "id", "description" => "id of item", "type" => "Integer", "dataType" => "Integer", "required" => true }, { "paramType" => "form", "name" => "name", "description" => "name of item", "type" => "String", "dataType" => "String", "required" => true } ]
|
77
|
+
}
|
78
|
+
]
|
79
|
+
}
|
80
|
+
]
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "helpers" do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
class HelperTestAPI < Grape::API
|
7
|
+
add_swagger_documentation
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
before :each do
|
12
|
+
@api = Object.new
|
13
|
+
|
14
|
+
# after injecting grape-swagger into the Test API we get the helper methods
|
15
|
+
# back from the first endpoint's class (the API mounted by grape-swagger
|
16
|
+
# to serve the swagger_doc
|
17
|
+
|
18
|
+
@api.extend HelperTestAPI.endpoints.first.options[:app].helpers
|
19
|
+
end
|
20
|
+
|
21
|
+
context "parsing parameters" do
|
22
|
+
it "parses params as query strings for a GET" do
|
23
|
+
params = {
|
24
|
+
name: { type: 'String', desc: "A name", required: true, defaultValue: 'default' },
|
25
|
+
level: 'max'
|
26
|
+
}
|
27
|
+
path = "/coolness"
|
28
|
+
method = "GET"
|
29
|
+
@api.parse_params(params, path, method).should == [
|
30
|
+
{ paramType: "query", name: :name, description: "A name", type: "String", dataType: "String", required: true, defaultValue: 'default' },
|
31
|
+
{ paramType: "query", name: :level, description: "", type: "String", dataType: "String", required: false }
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "parses params as form for a POST" do
|
36
|
+
params = {
|
37
|
+
name: { type: 'String', :desc => "A name", required: true },
|
38
|
+
level: 'max'
|
39
|
+
}
|
40
|
+
path = "/coolness"
|
41
|
+
method = "POST"
|
42
|
+
@api.parse_params(params, path, method).should == [
|
43
|
+
{ paramType: "form", name: :name, description: "A name", type: "String", dataType: "String", required: true },
|
44
|
+
{ paramType: "form", name: :level, description: "", type: "String", dataType: "String", required: false }
|
45
|
+
]
|
46
|
+
end
|
47
|
+
|
48
|
+
context "custom type" do
|
49
|
+
before :all do
|
50
|
+
class CustomType
|
51
|
+
end
|
52
|
+
end
|
53
|
+
it "parses a custom parameters" do
|
54
|
+
params = {
|
55
|
+
option: { type: CustomType, desc: "Custom option" }
|
56
|
+
}
|
57
|
+
path = "/coolness"
|
58
|
+
method = "GET"
|
59
|
+
@api.parse_params(params, path, method).should == [
|
60
|
+
{ paramType: "query", name: :option, description: "Custom option", type: "CustomType", dataType: "CustomType", required: false }
|
61
|
+
]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
context "parsing the path" do
|
68
|
+
it "parses the path" do
|
69
|
+
path = ":abc/def(.:format)"
|
70
|
+
@api.parse_path(path, nil).should == "{abc}/def.{format}"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "parses a path that has vars with underscores in the name" do
|
74
|
+
path = "abc/:def_g(.:format)"
|
75
|
+
@api.parse_path(path, nil).should == "abc/{def_g}.{format}"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "parses a path that has vars with numbers in the name" do
|
79
|
+
path = "abc/:sha1(.:format)"
|
80
|
+
@api.parse_path(path, nil).should == "abc/{sha1}.{format}"
|
81
|
+
end
|
82
|
+
|
83
|
+
it "parses a path that has multiple variables" do
|
84
|
+
path1 = "abc/:def/:geh(.:format)"
|
85
|
+
path2 = "abc/:def:geh(.:format)"
|
86
|
+
@api.parse_path(path1, nil).should == "abc/{def}/{geh}.{format}"
|
87
|
+
@api.parse_path(path2, nil).should == "abc/{def}{geh}.{format}"
|
88
|
+
end
|
89
|
+
|
90
|
+
it "parses the path with a specified version" do
|
91
|
+
path = ":abc/{version}/def(.:format)"
|
92
|
+
@api.parse_path(path, 'v1').should == "{abc}/v1/def.{format}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "parsing header parameters" do
|
97
|
+
it "parses params for the header" do
|
98
|
+
params = {
|
99
|
+
"XAuthToken" => { description: "A required header.", required: true, defaultValue: 'default' }
|
100
|
+
}
|
101
|
+
@api.parse_header_params(params).should == [
|
102
|
+
{ paramType: "header", name: "XAuthToken", description: "A required header.", type: "String", dataType: "String", required: true, defaultValue: 'default' }
|
103
|
+
]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
data/spec/grape-swagger_spec.rb
CHANGED
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "a hide mounted api" do
|
4
|
+
before :all do
|
5
|
+
class HideMountedApi < Grape::API
|
6
|
+
desc 'Show this endpoint'
|
7
|
+
get '/simple' do
|
8
|
+
{ :foo => 'bar' }
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'Hide this endpoint', {
|
12
|
+
:hidden => true
|
13
|
+
}
|
14
|
+
get '/hide' do
|
15
|
+
{ :foo => 'bar' }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class HideApi < Grape::API
|
20
|
+
mount HideMountedApi
|
21
|
+
add_swagger_documentation
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def app; HideApi end
|
26
|
+
|
27
|
+
it "retrieves swagger-documentation that doesn't include hidden endpoints" do
|
28
|
+
get '/swagger_doc.json'
|
29
|
+
JSON.parse(last_response.body).should == {
|
30
|
+
"apiVersion" => "0.1",
|
31
|
+
"swaggerVersion" => "1.2",
|
32
|
+
"basePath" => "http://example.org",
|
33
|
+
"info" => {},
|
34
|
+
"produces" => ["application/xml", "application/json", "text/plain"],
|
35
|
+
"operations" => [],
|
36
|
+
"apis" => [
|
37
|
+
{ "path" => "/simple.{format}" },
|
38
|
+
{ "path" => "/swagger_doc.{format}" }
|
39
|
+
]
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
describe "a hide mounted api with same namespace" do
|
46
|
+
before :all do
|
47
|
+
class HideNamespaceMountedApi < Grape::API
|
48
|
+
desc 'Show this endpoint'
|
49
|
+
get '/simple/show' do
|
50
|
+
{ :foo => 'bar' }
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'Hide this endpoint', {
|
54
|
+
:hidden => true
|
55
|
+
}
|
56
|
+
get '/simple/hide' do
|
57
|
+
{ :foo => 'bar' }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class HideNamespaceApi < Grape::API
|
62
|
+
mount HideNamespaceMountedApi
|
63
|
+
add_swagger_documentation
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def app; HideNamespaceApi end
|
68
|
+
|
69
|
+
it "retrieves swagger-documentation on /swagger_doc" do
|
70
|
+
get '/swagger_doc.json'
|
71
|
+
JSON.parse(last_response.body).should == {
|
72
|
+
"apiVersion" => "0.1",
|
73
|
+
"swaggerVersion" => "1.2",
|
74
|
+
"basePath" => "http://example.org",
|
75
|
+
"info" => {},
|
76
|
+
"produces" => ["application/xml", "application/json", "text/plain"],
|
77
|
+
"operations" => [],
|
78
|
+
"apis" => [
|
79
|
+
{ "path" => "/simple.{format}" },
|
80
|
+
{ "path" => "/swagger_doc.{format}" }
|
81
|
+
]
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
it "retrieves the documentation for mounted-api that doesn't include hidden endpoints" do
|
86
|
+
get '/swagger_doc/simple.json'
|
87
|
+
JSON.parse(last_response.body).should == {
|
88
|
+
"apiVersion" => "0.1",
|
89
|
+
"swaggerVersion" => "1.2",
|
90
|
+
"basePath" => "http://example.org",
|
91
|
+
"resourcePath" => "",
|
92
|
+
"apis" => [{
|
93
|
+
"path" => "/simple/show.{format}",
|
94
|
+
"operations" => [{
|
95
|
+
"produces" => ["application/xml", "application/json", "text/plain"],
|
96
|
+
"notes" => nil,
|
97
|
+
"notes" => "",
|
98
|
+
"summary" => "Show this endpoint",
|
99
|
+
"nickname" => "GET-simple-show---format-",
|
100
|
+
"httpMethod" => "GET",
|
101
|
+
"parameters" => []
|
102
|
+
}]
|
103
|
+
}]
|
104
|
+
}
|
105
|
+
end
|
106
|
+
end
|