swaggable 0.4.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.
@@ -0,0 +1,3 @@
1
+ module Swaggable
2
+ VERSION = '0.4.0'
3
+ end
@@ -0,0 +1 @@
1
+ {"swagger":"2.0","info":{"description":"This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.","version":"1.0.0","title":"Swagger Petstore","termsOfService":"http://swagger.io/terms/","contact":{"email":"apiteam@swagger.io"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"}},"host":"petstore.swagger.io","basePath":"/v2","tags":[{"name":"pet","description":"Everything about your Pets","externalDocs":{"description":"Find out more","url":"http://swagger.io"}},{"name":"store","description":"Access to Petstore orders"},{"name":"user","description":"Operations about user","externalDocs":{"description":"Find out more about our store","url":"http://swagger.io"}}],"schemes":["http"],"paths":{"/pet":{"post":{"tags":["pet"],"summary":"Add a new pet to the store","description":"","operationId":"addPet","consumes":["application/json","application/xml"],"produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"Pet object that needs to be added to the store","required":true,"schema":{"$ref":"#/definitions/Pet"}}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"put":{"tags":["pet"],"summary":"Update an existing pet","description":"","operationId":"updatePet","consumes":["application/json","application/xml"],"produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"Pet object that needs to be added to the store","required":true,"schema":{"$ref":"#/definitions/Pet"}}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"},"405":{"description":"Validation exception"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByStatus":{"get":{"tags":["pet"],"summary":"Finds Pets by status","description":"Multiple status values can be provided with comma seperated strings","operationId":"findPetsByStatus","produces":["application/xml","application/json"],"parameters":[{"name":"status","in":"query","description":"Status values that need to be considered for filter","required":true,"type":"array","items":{"type":"string","enum":["available","pending","sold"],"default":"available"},"collectionFormat":"csv"}],"responses":{"200":{"description":"successful operation","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}},"400":{"description":"Invalid status value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByTags":{"get":{"tags":["pet"],"summary":"Finds Pets by tags","description":"Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.","operationId":"findPetsByTags","produces":["application/xml","application/json"],"parameters":[{"name":"tags","in":"query","description":"Tags to filter by","required":true,"type":"array","items":{"type":"string"},"collectionFormat":"csv"}],"responses":{"200":{"description":"successful operation","schema":{"type":"array","items":{"$ref":"#/definitions/Pet"}}},"400":{"description":"Invalid tag value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}":{"get":{"tags":["pet"],"summary":"Find pet by ID","description":"Returns a single pet","operationId":"getPetById","produces":["application/xml","application/json"],"parameters":[{"name":"petId","in":"path","description":"ID of pet to return","required":true,"type":"integer","format":"int64"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Pet"}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"}},"security":[{"api_key":[]}]},"post":{"tags":["pet"],"summary":"Updates a pet in the store with form data","description":"","operationId":"updatePetWithForm","consumes":["application/x-www-form-urlencoded"],"produces":["application/xml","application/json"],"parameters":[{"name":"petId","in":"path","description":"ID of pet that needs to be updated","required":true,"type":"integer","format":"int64"},{"name":"name","in":"formData","description":"Updated name of the pet","required":false,"type":"string"},{"name":"status","in":"formData","description":"Updated status of the pet","required":false,"type":"string"}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"delete":{"tags":["pet"],"summary":"Deletes a pet","description":"","operationId":"deletePet","produces":["application/xml","application/json"],"parameters":[{"name":"api_key","in":"header","required":false,"type":"string"},{"name":"petId","in":"path","description":"Pet id to delete","required":true,"type":"integer","format":"int64"}],"responses":{"400":{"description":"Invalid pet value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}/uploadImage":{"post":{"tags":["pet"],"summary":"uploads an image","description":"","operationId":"uploadFile","consumes":["multipart/form-data"],"produces":["application/json"],"parameters":[{"name":"petId","in":"path","description":"ID of pet to update","required":true,"type":"integer","format":"int64"},{"name":"additionalMetadata","in":"formData","description":"Additional data to pass to server","required":false,"type":"string"},{"name":"file","in":"formData","description":"file to upload","required":false,"type":"file"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/ApiResponse"}}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/store/inventory":{"get":{"tags":["store"],"summary":"Returns pet inventories by status","description":"Returns a map of status codes to quantities","operationId":"getInventory","produces":["application/json"],"parameters":[],"responses":{"200":{"description":"successful operation","schema":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}},"security":[{"api_key":[]}]}},"/store/order":{"post":{"tags":["store"],"summary":"Place an order for a pet","description":"","operationId":"placeOrder","produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"order placed for purchasing the pet","required":true,"schema":{"$ref":"#/definitions/Order"}}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Order"}},"400":{"description":"Invalid Order"}}}},"/store/order/{orderId}":{"get":{"tags":["store"],"summary":"Find purchase order by ID","description":"For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions","operationId":"getOrderById","produces":["application/xml","application/json"],"parameters":[{"name":"orderId","in":"path","description":"ID of pet that needs to be fetched","required":true,"type":"integer","maximum":5.0,"minimum":1.0,"format":"int64"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/Order"}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}},"delete":{"tags":["store"],"summary":"Delete purchase order by ID","description":"For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors","operationId":"deleteOrder","produces":["application/xml","application/json"],"parameters":[{"name":"orderId","in":"path","description":"ID of the order that needs to be deleted","required":true,"type":"string","minimum":1.0}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}}},"/user":{"post":{"tags":["user"],"summary":"Create user","description":"This can only be done by the logged in user.","operationId":"createUser","produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"Created user object","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"default":{"description":"successful operation"}}}},"/user/createWithArray":{"post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"","operationId":"createUsersWithArrayInput","produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"List of user object","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/User"}}}],"responses":{"default":{"description":"successful operation"}}}},"/user/createWithList":{"post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"","operationId":"createUsersWithListInput","produces":["application/xml","application/json"],"parameters":[{"in":"body","name":"body","description":"List of user object","required":true,"schema":{"type":"array","items":{"$ref":"#/definitions/User"}}}],"responses":{"default":{"description":"successful operation"}}}},"/user/login":{"get":{"tags":["user"],"summary":"Logs user into the system","description":"","operationId":"loginUser","produces":["application/xml","application/json"],"parameters":[{"name":"username","in":"query","description":"The user name for login","required":true,"type":"string"},{"name":"password","in":"query","description":"The password for login in clear text","required":true,"type":"string"}],"responses":{"200":{"description":"successful operation","schema":{"type":"string"},"headers":{"X-Rate-Limit":{"type":"integer","format":"int32","description":"calls per hour allowed by the user"},"X-Expires-After":{"type":"string","format":"date-time","description":"date in UTC when toekn expires"}}},"400":{"description":"Invalid username/password supplied"}}}},"/user/logout":{"get":{"tags":["user"],"summary":"Logs out current logged in user session","description":"","operationId":"logoutUser","produces":["application/xml","application/json"],"parameters":[],"responses":{"default":{"description":"successful operation"}}}},"/user/{username}":{"get":{"tags":["user"],"summary":"Get user by user name","description":"","operationId":"getUserByName","produces":["application/xml","application/json"],"parameters":[{"name":"username","in":"path","description":"The name that needs to be fetched. Use user1 for testing. ","required":true,"type":"string"}],"responses":{"200":{"description":"successful operation","schema":{"$ref":"#/definitions/User"}},"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}},"put":{"tags":["user"],"summary":"Updated user","description":"This can only be done by the logged in user.","operationId":"updateUser","produces":["application/xml","application/json"],"parameters":[{"name":"username","in":"path","description":"name that need to be deleted","required":true,"type":"string"},{"in":"body","name":"body","description":"Updated user object","required":true,"schema":{"$ref":"#/definitions/User"}}],"responses":{"400":{"description":"Invalid user supplied"},"404":{"description":"User not found"}}},"delete":{"tags":["user"],"summary":"Delete user","description":"This can only be done by the logged in user.","operationId":"deleteUser","produces":["application/xml","application/json"],"parameters":[{"name":"username","in":"path","description":"The name that needs to be deleted","required":true,"type":"string"}],"responses":{"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}}}},"securityDefinitions":{"petstore_auth":{"type":"oauth2","authorizationUrl":"http://petstore.swagger.io/api/oauth/dialog","flow":"implicit","scopes":{"write:pets":"modify pets in your account","read:pets":"read your pets"}},"api_key":{"type":"apiKey","name":"api_key","in":"header"}},"definitions":{"Order":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"petId":{"type":"integer","format":"int64"},"quantity":{"type":"integer","format":"int32"},"shipDate":{"type":"string","format":"date-time"},"status":{"type":"string","description":"Order Status","enum":["placed","approved","delivered"]},"complete":{"type":"boolean","default":false}},"xml":{"name":"Order"}},"Category":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"Category"}},"User":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"username":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"},"phone":{"type":"string"},"userStatus":{"type":"integer","format":"int32","description":"User Status"}},"xml":{"name":"User"}},"Tag":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"Tag"}},"Pet":{"type":"object","required":["name","photoUrls"],"properties":{"id":{"type":"integer","format":"int64"},"category":{"$ref":"#/definitions/Category"},"name":{"type":"string","example":"doggie"},"photoUrls":{"type":"array","xml":{"name":"photoUrl","wrapped":true},"items":{"type":"string"}},"tags":{"type":"array","xml":{"name":"tag","wrapped":true},"items":{"$ref":"#/definitions/Tag"}},"status":{"type":"string","description":"pet status in the store","enum":["available","pending","sold"]}},"xml":{"name":"Pet"}},"ApiResponse":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"type":{"type":"string"},"message":{"type":"string"}}}},"externalDocs":{"description":"Find out more about Swagger","url":"http://swagger.io"}}
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'rspec'
3
+ require 'pry'
4
+ require 'webmock/rspec'
5
+
6
+ RSpec.configure do |config|
7
+ config.color = true
8
+ config.tty = true
9
+ config.disable_monkey_patching!
10
+ # config.full_backtrace = true
11
+ # config.formatter = :documentation # :documentation, :progress, :html, :textmate
12
+ end
13
+
14
+ $LOAD_PATH.unshift File.expand_path('lib')
15
+ require 'swaggable'
16
+
17
+ $LOAD_PATH.unshift File.expand_path('spec/support')
18
+
@@ -0,0 +1,169 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe 'Swaggable::ApiDefinition' do
4
+ let(:subject_class) { Swaggable::ApiDefinition }
5
+ let(:subject_instance) { Swaggable::ApiDefinition.new }
6
+ subject { subject_instance }
7
+
8
+ it 'has a version' do
9
+ subject.version = 'v2.0'
10
+ expect(subject.version).to eq 'v2.0'
11
+ end
12
+
13
+ it 'has a title' do
14
+ subject.title = 'a new title'
15
+ expect(subject.title).to eq 'a new title'
16
+ end
17
+
18
+ it 'has a description' do
19
+ subject.description = 'a new description'
20
+ expect(subject.description).to eq 'a new description'
21
+ end
22
+
23
+ it 'has a base_path' do
24
+ subject.base_path = 'a new base_path'
25
+ expect(subject.base_path).to eq 'a new base_path'
26
+ end
27
+
28
+ it 'has endpoints' do
29
+ endpoint = Swaggable::EndpointDefinition.new verb: :get, path: '/'
30
+ subject.endpoints << endpoint
31
+ expect(subject.endpoints.first).to eq endpoint
32
+ end
33
+
34
+ describe '#endpoints' do
35
+ subject { subject_instance.endpoints }
36
+
37
+ it 'accumulates endpoints with <<' do
38
+ endpoint = Swaggable::EndpointDefinition.new verb: :get, path: '/'
39
+ subject << endpoint
40
+ expect(subject['GET /']).to be endpoint
41
+ end
42
+
43
+ it 'accumulates endpoints with add' do
44
+ endpoint = Swaggable::EndpointDefinition.new verb: :get, path: '/'
45
+ subject.add endpoint
46
+ expect(subject['GET /']).to be endpoint
47
+ end
48
+
49
+ it 'adds new endpoints' do
50
+ endpoint = subject.add_new do |e|
51
+ e.verb = :post
52
+ e.path = '/users'
53
+ end
54
+
55
+ expect(subject['POST /users']).to be endpoint
56
+ end
57
+
58
+ it 'iterates through endpoints' do
59
+ has_run = false
60
+
61
+ endpoint = subject.add_new do |e|
62
+ e.verb = :post
63
+ e.path = '/users'
64
+ end
65
+
66
+ subject.each do |e|
67
+ has_run = true
68
+ expect(e).to be endpoint
69
+ end
70
+ end
71
+
72
+ it 'converts to array' do
73
+ endpoint = subject.add_new do |e|
74
+ e.verb = :post
75
+ e.path = '/users'
76
+ end
77
+
78
+ expect(subject.to_a).to eq [endpoint]
79
+ end
80
+
81
+ it 'clears' do
82
+ endpoint = subject.add_new do |e|
83
+ e.verb = :post
84
+ e.path = '/users'
85
+ end
86
+
87
+ subject.clear
88
+
89
+ expect(subject.to_a).to eq []
90
+ end
91
+ end
92
+
93
+ describe '#tags' do
94
+ it 'is collected from endpoints' do
95
+ tag = instance_double(Swaggable::TagDefinition, name: 'A tag')
96
+ endpoint = Swaggable::EndpointDefinition.new verb: :get, path: '/'
97
+ endpoint.tags << tag
98
+ subject.endpoints << endpoint
99
+ expect(subject.tags.first).to eq tag
100
+ end
101
+
102
+ it 'avoids duplicates' do
103
+ tag_1 = Swaggable::TagDefinition.new name: 'tag_1'
104
+ tag_1_again = Swaggable::TagDefinition.new name: 'tag_1'
105
+ tag_2 = Swaggable::TagDefinition.new name: 'tag_2'
106
+
107
+ endpoint_a = Swaggable::EndpointDefinition.new
108
+ endpoint_b = Swaggable::EndpointDefinition.new
109
+
110
+ endpoint_a.tags << tag_1
111
+ endpoint_b.tags << tag_1_again
112
+ endpoint_b.tags << tag_2
113
+
114
+ subject.endpoints << endpoint_a
115
+ subject.endpoints << endpoint_b
116
+
117
+ expect(subject.tags.to_a).to eq [tag_1_again, tag_2]
118
+ end
119
+
120
+ it 'is empty array when no tags are present' do
121
+ subject.endpoints.clear
122
+ expect(subject.tags).to eq []
123
+ end
124
+
125
+ it 'is frozen to avoid giving the false impression that it can be modified' do
126
+ expect{ subject.tags << instance_double(Swaggable::TagDefinition) }.to raise_error
127
+ end
128
+ end
129
+
130
+ it 'yields itself on initialize' do
131
+ yielded = false
132
+
133
+ subject_class.new do |s|
134
+ expect(s).to be_a subject_class
135
+ yielded = true
136
+ end
137
+
138
+ expect(yielded).to be true
139
+ end
140
+
141
+ it 'builds from a Grape API' do
142
+ allow(subject_class.grape_adapter).to receive(:import) { |grape, api| api.title = 'A test'; api }
143
+ result = subject_class.from_grape_api double('grape')
144
+ expect(result.title).to eq 'A test'
145
+ end
146
+
147
+ it 'has a dsl' do
148
+ api = Swaggable::ApiDefinition.new
149
+
150
+ api.configure do
151
+ version 'v1.0'
152
+ title 'My API'
153
+ description 'My cool API'
154
+ base_path '/a/path'
155
+
156
+ endpoints do
157
+ add_new do
158
+ path '/users'
159
+ verb :post
160
+ description 'Creates users'
161
+ summary 'Allows to create an user'
162
+ end
163
+ end
164
+ end
165
+
166
+ expect(api.version).to eq 'v1.0'
167
+ expect(api.endpoints.first.path).to eq '/users'
168
+ end
169
+ end
@@ -0,0 +1,77 @@
1
+ require_relative '../spec_helper'
2
+
3
+ RSpec.describe 'Swaggable::EndpointDefinition' do
4
+ let(:subject_class) { Swaggable::EndpointDefinition }
5
+ let(:subject_instance) { Swaggable::EndpointDefinition.new }
6
+ subject { subject_instance }
7
+
8
+ it 'has a path' do
9
+ subject.path = '/'
10
+ expect(subject.path).to eq '/'
11
+ end
12
+
13
+ it 'has a verb' do
14
+ subject.verb = 'POST'
15
+ expect(subject.verb).to eq 'POST'
16
+ end
17
+
18
+ it 'has a description' do
19
+ subject.description = 'a new desc'
20
+ expect(subject.description).to eq 'a new desc'
21
+ end
22
+
23
+ it 'has a summary' do
24
+ subject.summary = 'a new summary'
25
+ expect(subject.summary).to eq 'a new summary'
26
+ end
27
+
28
+ it 'has tags' do
29
+ tag = instance_double(Swaggable::TagDefinition, name: 'A Tag')
30
+ subject.tags << tag
31
+ expect(subject.tags.to_a).to eq [tag]
32
+ end
33
+
34
+ it 'has consumes' do
35
+ format = double('format')
36
+ subject.consumes << format
37
+ expect(subject.consumes).to eq [format]
38
+ end
39
+
40
+ it 'has produces' do
41
+ format = double('format')
42
+ subject.produces << format
43
+ expect(subject.produces).to eq [format]
44
+ end
45
+
46
+ it 'has parameters' do
47
+ parameter = Swaggable::ParameterDefinition.new name: 'some_parameter'
48
+ subject.parameters << parameter
49
+ expect(subject.parameters.to_a).to eq [parameter]
50
+ end
51
+
52
+ it 'yields itself on initialize' do
53
+ yielded = false
54
+
55
+ subject_class.new do |s|
56
+ expect(s).to be_a subject_class
57
+ yielded = true
58
+ end
59
+
60
+ expect(yielded).to be true
61
+ end
62
+
63
+ it 'accepts attributes on initialize' do
64
+ endpoint = subject_class.new path: '/a/path', verb: 'GET'
65
+ expect(endpoint.path).to eq '/a/path'
66
+ expect(endpoint.verb).to eq 'GET'
67
+ end
68
+
69
+ it 'has responses' do
70
+ subject.responses.add_new do
71
+ status 418
72
+ description 'Teapot'
73
+ end
74
+
75
+ expect(subject.responses[418].description).to eq 'Teapot'
76
+ end
77
+ end
@@ -0,0 +1,198 @@
1
+ require_relative '../spec_helper'
2
+ require 'grape'
3
+
4
+ RSpec.describe 'Swaggable::GrapeAdapter' do
5
+ let(:subject_class) { Swaggable::GrapeAdapter }
6
+ let(:subject_instance) { Swaggable::GrapeAdapter.new }
7
+ subject { subject_instance }
8
+
9
+ describe '#import(grape_api, api_definition)' do
10
+ let(:api) { Swaggable::ApiDefinition.new }
11
+ let(:grape) { Class.new(Grape::API) }
12
+
13
+ def do_import
14
+ subject.import grape, api
15
+ end
16
+
17
+ it 'returns the api' do
18
+ api = do_import
19
+ expect(api).to be_a Swaggable::ApiDefinition
20
+ end
21
+
22
+ it 'sets version' do
23
+ grape.version 'v2.0'
24
+ do_import
25
+ expect(api.version).to eq 'v2.0'
26
+ end
27
+
28
+ it 'sets title' do
29
+ allow(grape).to receive(:name).and_return('MyAPI')
30
+ do_import
31
+ expect(api.title).to eq 'MyAPI'
32
+ end
33
+
34
+ it 'sets base path' do
35
+ do_import
36
+ expect(api.base_path).to eq '/'
37
+ end
38
+
39
+ describe 'endpoints' do
40
+ it 'sets verb' do
41
+ grape.post { }
42
+ do_import
43
+ expect(api.endpoints.first.verb).to eq 'post'
44
+ end
45
+
46
+ it 'sets description as summary' do
47
+ grape.desc 'My endpoint'
48
+ grape.post { }
49
+ do_import
50
+ expect(api.endpoints.first.summary).to eq 'My endpoint'
51
+ end
52
+
53
+ it 'sets format' do
54
+ grape.format :json
55
+ grape.post { }
56
+ do_import
57
+ expect(api.endpoints.first.produces).to eq ['application/json']
58
+ end
59
+
60
+ describe 'path' do
61
+ it 'has no (.:format)' do
62
+ grape.post('/a/path') { }
63
+ do_import
64
+ expect(api.endpoints.first.path).to eq '/a/path'
65
+ end
66
+
67
+ it 'has no (/.:format)' do
68
+ grape.version 'v1.0'
69
+ grape.get('/') { }
70
+ do_import
71
+ expect(api.endpoints.first.path).to eq '/v1.0'
72
+ end
73
+
74
+ it 'has version number' do
75
+ grape.version 'v3.0'
76
+ grape.post('/a/path') { }
77
+ do_import
78
+ expect(api.endpoints.first.path).to eq '/v3.0/a/path'
79
+ end
80
+
81
+ it 'has version number if present with prefix too' do
82
+ grape.version 'v3.0'
83
+ grape.prefix '/api'
84
+ grape.post('/a/path') { }
85
+ do_import
86
+ expect(api.endpoints.first.path).to eq '/api/v3.0/a/path'
87
+ end
88
+
89
+ it 'has version number if present with prefix not beginning with / too' do
90
+ grape.version 'v3.0'
91
+ grape.prefix 'api'
92
+ grape.post('/a/path') { }
93
+ do_import
94
+ expect(api.endpoints.first.path).to eq '/api/v3.0/a/path'
95
+ end
96
+
97
+ it 'has parameters' do
98
+ grape.version 'v3.0'
99
+ grape.prefix '/api'
100
+ grape.post('/a/path/:with/:parameters') { }
101
+ do_import
102
+ expect(api.endpoints.first.path).to eq '/api/v3.0/a/path/{with}/{parameters}'
103
+ end
104
+
105
+ it 'has tags' do
106
+ grape.post('/a/path') { }
107
+ do_import
108
+ tag = api.endpoints.first.tags.first
109
+ expect(tag.name).to eq grape.name
110
+ end
111
+ end
112
+
113
+ describe 'parameters' do
114
+ it 'have name' do
115
+ grape.params do
116
+ requires :user_uuid
117
+ end
118
+
119
+ grape.post('/a/path') { }
120
+
121
+ do_import
122
+ expect(api.endpoints.first.parameters.first.name).to eq 'user_uuid'
123
+ end
124
+
125
+ it 'have type' do
126
+ grape.params do
127
+ requires :user_uuid, type: String
128
+ end
129
+
130
+ grape.post('/a/path') { }
131
+
132
+ do_import
133
+ expect(api.endpoints.first.parameters.first.type).to eq :string
134
+ end
135
+
136
+ it 'have required' do
137
+ grape.params do
138
+ requires :required_param
139
+ end
140
+
141
+ grape.post('/a/path') { }
142
+
143
+ do_import
144
+ expect(api.endpoints.first.parameters.first.required).to eq true
145
+ end
146
+
147
+ it 'have description' do
148
+ grape.params do
149
+ requires :param, desc: 'A param'
150
+ end
151
+
152
+ grape.post('/a/path') { }
153
+
154
+ do_import
155
+ expect(api.endpoints.first.parameters.first.description).to eq 'A param'
156
+ end
157
+
158
+ it 'have location path if path param' do
159
+ grape.params do
160
+ requires :required_param
161
+ end
162
+
163
+ grape.post('/a/:required_param') { }
164
+
165
+ do_import
166
+ expect(api.endpoints.first.parameters.first.location).to eq :path
167
+ end
168
+
169
+ it 'have location query if non-path param' do
170
+ grape.params do
171
+ requires :required_param
172
+ end
173
+
174
+ grape.post('/a') { }
175
+
176
+ do_import
177
+ expect(api.endpoints.first.parameters.first.location).to eq :query
178
+ end
179
+ end
180
+
181
+ describe 'responses' do
182
+ it 'have status' do
183
+ grape.post('/', http_codes: [201, 'Created']) { }
184
+
185
+ do_import
186
+ expect(api.endpoints.first.responses.first.status).to eq 201
187
+ end
188
+
189
+ it 'have description' do
190
+ grape.post('/', http_codes: [[201, 'Created']]) { }
191
+
192
+ do_import
193
+ expect(api.endpoints.first.responses.first.description).to eq 'Created'
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end