apipie-rails 0.5.7 → 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,73 @@
1
+ #
2
+ # demonstration of how the 'describe_own_properties' method can be used
3
+ # to integrate Apipie response descriptions with view generation packages
4
+ # such as, for example, Grape::Entity
5
+ #
6
+
7
+ # Consider a hypothetical SelfDocumentingView class (a stand-in for Grape::Entity
8
+ # for demonstrational purposes). This is an abstract class, supporting the implementation
9
+ # of actual views as subclasses of SelfDocumentingView.
10
+ #
11
+ # A user of SelfDocumentingView would implement a subclass for each desired view. Each such
12
+ # view definition would include, for each field that should be returned in the JSON response,
13
+ # an instance method called v_<name>__<type>.
14
+ #
15
+ # SelfDocumentingView would then scan the subclass for such fields and:
16
+ # 1. Given an id: fetch the matching model and generated a view from it using the field definitions
17
+ # 2. Describe the structure of such views to Apipie using self.describe_own_properties
18
+ #
19
+ # (see the controller implementation below for how such a class might be used)
20
+
21
+ class SelfDocumentingView
22
+ # self.describe_own_properties (a class method) generates the meta-data
23
+ # (i.e., the type description) for the subclass.
24
+ def self.describe_own_properties
25
+ (self.instance_methods - self.class.instance_methods).map{|m|
26
+ if matchdata = /^v_(\w+)__(\w+)$/.match(m)
27
+ Apipie::prop(matchdata[1], matchdata[2])
28
+ end
29
+ }.compact
30
+ end
31
+
32
+ # to_json (an instance method) generates the actual view
33
+ def to_json
34
+ { note: "in an actual implementation of SelfDocumentingView, this method
35
+ would call each v_<name>__<type> method and include its output
36
+ in the response as a (<name>: <value>) pair"
37
+ }
38
+ end
39
+
40
+ def initialize(id)
41
+ load_from_model(id)
42
+ end
43
+
44
+ def load_from_model(id)
45
+ # in a real implementation of SelfDocumentingView, this
46
+ # method would load the fields to be displayed from the model
47
+ # instance identified by 'id'
48
+ end
49
+ end
50
+
51
+
52
+ #
53
+ # ViewOfPet extends SelfDocumentingView to include specific fields
54
+ #
55
+ class ViewOfPet < SelfDocumentingView
56
+ attr_accessor :v_pet_name__string
57
+ attr_accessor :v_animal_type__string
58
+ attr_accessor :v_age__number
59
+ end
60
+
61
+
62
+ class PetsUsingAutoViewsController < ApplicationController
63
+ #-----------------------------------------------------------
64
+ # Method returning an array of AutomatedViewOfPet (where
65
+ # AutomatedViewOfPet is an auto-generated self-describing class)
66
+ # -----------------------------------------------------------
67
+ api :GET, "/pet_described_using_automated_view/:id", "Get the measurements of a single pet"
68
+ param :id, String
69
+ returns ViewOfPet, :desc => "like Pet, but different"
70
+ def pet_described_using_automated_view
71
+ render :plain => ViewOfPet.new(params.id).to_json
72
+ end
73
+ end
@@ -0,0 +1,95 @@
1
+ #----------------------------------------------------------------------------------------------------
2
+ # A "self-describing class" is a class that respond_to? :describe_own_properties
3
+ # and returns an array of Property Descriptions.
4
+ # (The simple way to create Property Description objects is using the Apipie::prop helper function,
5
+ # which is a factory for Apipie::ResponseDescriptionAdapter::PropDesc instances)
6
+ #
7
+ #----------------------------------------------------------------------------------------------------
8
+
9
+
10
+ # in this example, Pet is a self-describing class with only two properties.
11
+ # In a real implementation, the Pet class would actually do something with these properties.
12
+ # Here, the class is defined to only include the describe_own_properties method.
13
+ class Pet
14
+ def self.describe_own_properties
15
+ [
16
+ Apipie::prop(:pet_name, 'string', {:description => 'Name of pet', :required => false}),
17
+ Apipie::prop(:animal_type, 'string', {:description => 'Type of pet', :values => ["dog", "cat", "iguana", "kangaroo"]}),
18
+ Apipie::additional_properties(false)
19
+ ]
20
+ end
21
+ end
22
+
23
+ #
24
+ # PetWithMeasurements is a self-describing class with an embedded object
25
+ #
26
+ class PetWithMeasurements
27
+ def self.describe_own_properties
28
+ [
29
+ Apipie::prop(:pet_name, 'string', {:description => 'Name of pet', :required => false}),
30
+ Apipie::prop('animal_type', 'string', {:description => 'Type of pet', :values => ["dog", "cat", "iguana", "kangaroo"]}),
31
+ Apipie::prop(:pet_measurements, 'object', {}, [
32
+ Apipie::prop(:weight, 'number', {:description => "Weight in pounds" }),
33
+ Apipie::prop(:height, 'number', {:description => "Height in inches" }),
34
+ Apipie::prop(:num_legs, 'number', {:description => "Number of legs", :required => false }),
35
+ Apipie::additional_properties(false)
36
+ ])
37
+ ]
38
+ end
39
+ end
40
+
41
+ #
42
+ # PetWithManyMeasurements is a self-describing class with an embedded object
43
+ #
44
+ class PetWithManyMeasurements
45
+ def self.describe_own_properties
46
+ [
47
+ Apipie::prop(:pet_name, 'string', {:description => 'Name of pet', :required => false}),
48
+ Apipie::prop(:many_pet_measurements, 'object', {is_array: true}, [
49
+ Apipie::prop(:weight, 'number', {:description => "Weight in pounds" }),
50
+ Apipie::prop(:height, 'number', {:description => "Height in inches" }),
51
+ ])
52
+ ]
53
+ end
54
+ end
55
+
56
+
57
+
58
+ class PetsUsingSelfDescribingClassesController < ApplicationController
59
+ resource_description do
60
+ description 'A controller to test "returns" using self-describing classes'
61
+ short 'Pets'
62
+ path '/pets2'
63
+ end
64
+
65
+ #-----------------------------------------------------------
66
+ # Method returning an array of Pet (a self-describing class)
67
+ # -----------------------------------------------------------
68
+ api :GET, "/pets_described_as_class", "Get all pets"
69
+ returns :array_of => Pet, :desc => "list of pets"
70
+ def pets_described_as_class
71
+ render :plain => "all pets"
72
+ end
73
+
74
+ #-----------------------------------------------------------
75
+ # Method returning an array of PetWithMeasurements (a self-describing class)
76
+ # -----------------------------------------------------------
77
+ api :GET, "/pets_with_measurements_described_as_class/:id", "Get the measurements of a single pet"
78
+ param :id, String
79
+ returns PetWithMeasurements, :desc => "measurements of the pet"
80
+ def pets_with_measurements_described_as_class
81
+ render :plain => "all pets"
82
+ end
83
+
84
+ #-----------------------------------------------------------
85
+ # Method returning an array of PetWithManyMeasurements (a self-describing class with array field)
86
+ # -----------------------------------------------------------
87
+ api :GET, "/pets_with_many_measurements_as_class/:id", "Get the measurements of a single pet"
88
+ param :id, String
89
+ returns PetWithManyMeasurements, :desc => "measurements of the pet"
90
+ def pets_with_many_measurements_as_class
91
+ render :plain => "all pets"
92
+ end
93
+
94
+ end
95
+
@@ -57,10 +57,38 @@ describe Apipie::Extractor::Writer do
57
57
  }
58
58
  }
59
59
 
60
- describe "with doc_path overriden in configuration" do
61
- it "should use the doc_path specified in configuration" do
62
- Apipie.configuration.doc_path = "user_specified_doc_path"
63
- expect(writer_class.examples_file).to eql(File.join(Rails.root, "user_specified_doc_path", "apipie_examples.json"))
60
+ context 'with doc_path overriden in configuration' do
61
+ around(:each) do |example|
62
+ standard_path = Apipie.configuration.doc_path
63
+ Apipie.configuration.doc_path = 'user_specified_doc_path'
64
+ example.run
65
+ Apipie.configuration.doc_path = standard_path
66
+ end
67
+
68
+ it 'should use the doc_path specified in configuration' do
69
+ expect(writer_class.examples_file).to eql(File.join(Rails.root, 'user_specified_doc_path', 'apipie_examples.json'))
70
+ end
71
+ end
72
+
73
+ context 'when compressing examples' do
74
+ around(:each) do |example|
75
+ Apipie.configuration.compress_examples = true
76
+ example.run
77
+ FileUtils.rm(writer_class.examples_file) if File.exist?(writer_class.examples_file)
78
+ Apipie.configuration.compress_examples = nil
79
+ end
80
+
81
+ it 'should write to a compressed file' do
82
+ expect(writer_class.examples_file).to match(/\.gz$/)
83
+ writer_class.write_recorded_examples(records)
84
+ expect(File.exist?(writer_class.examples_file))
85
+ end
86
+
87
+ it 'should read from a compressed file' do
88
+ writer_class.write_recorded_examples(records)
89
+ expected_string = writer_class.send(:serialize_examples, records)
90
+ expect(writer_class.load_recorded_examples)
91
+ .to eql(writer_class.send(:deserialize_examples, expected_string))
64
92
  end
65
93
  end
66
94
 
@@ -68,4 +68,31 @@ describe Apipie::MethodDescription do
68
68
 
69
69
  end
70
70
 
71
+ describe "response-only properties" do
72
+ before(:each) do
73
+ @resource = Apipie::ResourceDescription.new(ApplicationController, "dummy")
74
+ dsl_data[:params] = [[:a, String, nil, {:only_in => :request}, nil],
75
+ [:b, String, nil, {:only_in => :response}, nil],
76
+ [:c, String, nil, {}, nil]]
77
+ @method = Apipie::MethodDescription.new(:a, @resource, dsl_data)
78
+ @resource.add_method_description @method
79
+ end
80
+
81
+ it "should ignore response-only parameters" do
82
+ expect(@method.params.keys).to eq([:a, :c])
83
+ expect(@method.to_json[:params].map{|h| h[:name]}).to eq(['a', 'c'])
84
+ end
85
+ end
86
+
87
+
88
+ describe "'returns' properties" do
89
+ it "should raise an error if both :param_group and :array_of are specified in 'returns'" do
90
+ @resource = Apipie::ResourceDescription.new(ApplicationController, "dummy")
91
+ dsl_data[:returns] = { 200 => [{:param_group => 'pet', :array_of => 'pet'}, nil, nil] }
92
+
93
+ expect {Apipie::MethodDescription.new(:a, @resource, dsl_data)}.to raise_error(Apipie::ReturnsMultipleDefinitionError)
94
+ end
95
+ end
96
+
97
+
71
98
  end
@@ -1,6 +1,10 @@
1
1
  require 'spec_helper'
2
2
  require "json-schema"
3
3
 
4
+ require File.expand_path("../../../dummy/app/controllers/twitter_example_controller.rb", __FILE__)
5
+ require File.expand_path("../../../dummy/app/controllers/users_controller.rb", __FILE__)
6
+ require File.expand_path("../../../dummy/app/controllers/pets_controller.rb", __FILE__)
7
+
4
8
  describe 'rake tasks' do
5
9
  include_context "rake"
6
10
 
@@ -0,0 +1,489 @@
1
+ require 'spec_helper'
2
+ require 'rack/utils'
3
+ require 'rspec/expectations'
4
+
5
+ describe "Swagger Responses" do
6
+ let(:desc) { Apipie.get_resource_description(controller_class, Apipie.configuration.default_version) }
7
+
8
+ let(:swagger) {
9
+ Apipie.configuration.swagger_suppress_warnings = true
10
+ Apipie.to_swagger_json(Apipie.configuration.default_version, controller_class.to_s.underscore.sub("_controller", ""))
11
+ }
12
+
13
+ let(:controller_class ) { described_class }
14
+
15
+ def swagger_response_for(path, code=200, method='get')
16
+ swagger[:paths][path][method][:responses][code]
17
+ end
18
+
19
+ def swagger_params_for(path, method='get')
20
+ swagger[:paths][path][method][:parameters]
21
+ end
22
+
23
+ def swagger_param_by_name(param_name, path, method='get')
24
+ params = swagger_params_for(path, method)
25
+ matching = params.select{|p| p[:name] == param_name }
26
+ raise "multiple params named [#{param_name}] in swagger definition for [#{method } #{path}]" if matching.length > 1
27
+
28
+ nil if matching.length == 0
29
+
30
+ matching[0]
31
+ end
32
+
33
+
34
+
35
+ describe PetsController do
36
+
37
+
38
+ describe "PetsController#index" do
39
+ subject do
40
+ desc._methods[:index]
41
+ end
42
+
43
+ it "should return code 200 with array of entries of the format {'pet_name', 'animal_type'}" do
44
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
45
+
46
+ puts returns_obj.to_json
47
+ expect(returns_obj.code).to eq(200)
48
+ expect(returns_obj.is_array?).to eq(true)
49
+
50
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type])
51
+ end
52
+
53
+ it 'should have the response described in the swagger' do
54
+ response = swagger_response_for('/pets')
55
+ expect(response[:description]).to eq("list of pets")
56
+
57
+ schema = response[:schema]
58
+ expect(schema[:type]).to eq("array")
59
+
60
+ a_schema = schema[:items]
61
+ expect(a_schema).to have_field(:pet_name, 'string', {:description => 'Name of pet', :required => false})
62
+ expect(a_schema).to have_field(:animal_type, 'string', {:description => 'Type of pet', :enum => ['dog','cat','iguana','kangaroo']})
63
+ end
64
+
65
+
66
+ it "should return code 401 with a String description field" do
67
+ returns_obj = subject.returns.detect{|e| e.code == 404 }
68
+
69
+ expect(returns_obj.code).to eq(404)
70
+ expect(returns_obj.is_array?).to eq(false)
71
+
72
+ expect(returns_obj).to match_field_structure([:error_message])
73
+ end
74
+
75
+
76
+ it "should return code 401 with a :reason field (defined in the superclass)" do
77
+ returns_obj = subject.returns.detect{|e| e.code == 401 }
78
+
79
+ expect(returns_obj.code).to eq(401)
80
+ expect(returns_obj.is_array?).to eq(false)
81
+
82
+ expect(returns_obj).to match_field_structure([:reason])
83
+ end
84
+
85
+ it 'should have the 404 response described in the swagger' do
86
+ response = swagger_response_for('/pets', 404)
87
+ expect(response[:description]).to eq("Not Found")
88
+
89
+ schema = response[:schema]
90
+ expect(schema[:type]).to eq("object")
91
+
92
+ expect(schema).to have_field(:error_message, 'string', {:description => 'description of the error', :required => true})
93
+ end
94
+
95
+ end
96
+
97
+ describe "PetsController#show_as_properties" do
98
+ subject do
99
+ desc._methods[:show_as_properties]
100
+ end
101
+
102
+ it "should return code 200 with 'pet_name' and 'animal_type'" do
103
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
104
+
105
+ puts returns_obj.to_json
106
+ expect(returns_obj.code).to eq(200)
107
+ expect(returns_obj.is_array?).to eq(false)
108
+
109
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type])
110
+ end
111
+
112
+ it 'should have the response described in the swagger' do
113
+ response = swagger_response_for('/pets/{id}/as_properties')
114
+ expect(response[:description]).to eq("OK")
115
+
116
+ schema = response[:schema]
117
+ expect(schema).to have_field(:pet_name, 'string', {:description => 'Name of pet', :required => false})
118
+ expect(schema).to have_field(:animal_type, 'string', {:description => 'Type of pet', :enum => ['dog','cat','iguana','kangaroo']})
119
+ end
120
+
121
+ it 'should have the 404 response description overridden' do
122
+ returns_obj = subject.returns.detect{|e| e.code == 404 }
123
+
124
+ # puts returns_obj.to_json
125
+ expect(returns_obj.code).to eq(404)
126
+ expect(returns_obj.is_array?).to eq(false)
127
+
128
+ expect(returns_obj).to match_field_structure([:another_error_message])
129
+ end
130
+ end
131
+
132
+ describe "PetsController#show_as_param_group_of_properties" do
133
+ subject do
134
+ desc._methods[:show_as_param_group_of_properties]
135
+ end
136
+
137
+ it "should return code 200 with 'pet_name' and 'animal_type'" do
138
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
139
+
140
+ puts returns_obj.to_json
141
+ expect(returns_obj.code).to eq(200)
142
+ expect(returns_obj.is_array?).to eq(false)
143
+
144
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type])
145
+ expect(returns_obj.params_ordered[0].is_required?).to be_falsey
146
+ expect(returns_obj.params_ordered[1].is_required?).to be_truthy
147
+ end
148
+
149
+ it 'should have the response described in the swagger' do
150
+ response = swagger_response_for('/pets/{id}/as_param_group_of_properties')
151
+ expect(response[:description]).to eq("The pet")
152
+
153
+ schema = response[:schema]
154
+ expect(schema).to have_field(:pet_name, 'string', {:description => 'Name of pet', :required => false})
155
+ expect(schema).to have_field(:animal_type, 'string', {:description => 'Type of pet', :enum => ['dog','cat','iguana','kangaroo']})
156
+ end
157
+ end
158
+
159
+ describe "PetsController#show_pet_by_id" do
160
+ subject do
161
+ desc._methods[:show_pet_by_id]
162
+ end
163
+
164
+ it "should have only oauth (from ApplicationController), common_param (from resource) and pet_id as an input parameters" do
165
+ params_obj = subject.params_ordered
166
+
167
+ expect(params_obj[0].name).to eq(:oauth)
168
+ expect(params_obj[1].name).to eq(:common_param)
169
+ expect(params_obj[2].name).to eq(:pet_id)
170
+ end
171
+
172
+ it "should return code 200 with 'pet_id', pet_name' and 'animal_type'" do
173
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
174
+
175
+ puts returns_obj.to_json
176
+ expect(returns_obj.code).to eq(200)
177
+ expect(returns_obj.is_array?).to eq(false)
178
+
179
+ # note that the response is expected NOT to return the parameters marked ':only_in => :request'
180
+ expect(returns_obj).to match_field_structure([:pet_id, :pet_name, :animal_type])
181
+ end
182
+
183
+ it 'should have the response described in the swagger' do
184
+ response = swagger_response_for('/pets/pet_by_id')
185
+ expect(response[:description]).to eq("OK")
186
+
187
+ schema = response[:schema]
188
+ expect(schema).to have_field(:pet_id, 'number', {:description => 'id of pet'})
189
+ expect(schema).to have_field(:pet_name, 'string', {:description => 'Name of pet', :required => false})
190
+ expect(schema).to have_field(:animal_type, 'string', {:description => 'Type of pet', :enum => ['dog','cat','iguana','kangaroo']})
191
+ expect(schema).not_to have_field(:partial_match_allowed, 'boolean', {:required => false})
192
+ end
193
+
194
+ it "creates a swagger definition with all input parameters" do
195
+ # a parameter defined for this method
196
+ expect(swagger_param_by_name(:pet_id, '/pets/pet_by_id')[:type]).to eq('number')
197
+
198
+ # a parameter defined for the resource
199
+ expect(swagger_param_by_name(:common_param, '/pets/pet_by_id')[:type]).to eq('number')
200
+
201
+ # a parameter defined in the controller's superclass
202
+ expect(swagger_param_by_name(:oauth, '/pets/pet_by_id')[:type]).to eq('string')
203
+ end
204
+
205
+ end
206
+
207
+ describe "PetsController#get_vote_by_owner_name" do
208
+ subject do
209
+ desc._methods[:get_vote_by_owner_name]
210
+ end
211
+
212
+ it "should return code 200 with 'owner_name' and 'vote'" do
213
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
214
+
215
+ puts returns_obj.to_json
216
+ expect(returns_obj.code).to eq(200)
217
+ expect(returns_obj.is_array?).to eq(false)
218
+
219
+ expect(returns_obj).to match_field_structure([:owner_name, :vote])
220
+ end
221
+
222
+ it 'should have the response described in the swagger' do
223
+ response = swagger_response_for('/pets/by_owner_name/did_vote')
224
+ expect(response[:description]).to eq("OK")
225
+
226
+ schema = response[:schema]
227
+ expect(schema).to have_field(:owner_name, 'string', {:required => false}) # optional because defined using 'param', not 'property'
228
+ expect(schema).to have_field(:vote, 'boolean')
229
+ end
230
+ end
231
+
232
+ describe "PetsController#show_extra_info" do
233
+ subject do
234
+ desc._methods[:show_extra_info]
235
+ end
236
+
237
+ it "should return code 201 with 'pet_name' and 'animal_type'" do
238
+ returns_obj = subject.returns.detect{|e| e.code == 201 }
239
+
240
+ puts returns_obj.to_json
241
+ expect(returns_obj.code).to eq(201)
242
+ expect(returns_obj.is_array?).to eq(false)
243
+
244
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type])
245
+ end
246
+ it 'should have the 201 response described in the swagger' do
247
+ response = swagger_response_for('/pets/{id}/extra_info', 201)
248
+ expect(response[:description]).to eq("Found a pet")
249
+
250
+ schema = response[:schema]
251
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
252
+ expect(schema).to have_field(:animal_type, 'string')
253
+ end
254
+
255
+ it "should return code 202 with spread out 'pet' and encapsulated 'pet_measurements'" do
256
+ returns_obj = subject.returns.detect{|e| e.code == 202 }
257
+
258
+ puts returns_obj.to_json
259
+ expect(returns_obj.code).to eq(202)
260
+ expect(returns_obj.is_array?).to eq(false)
261
+
262
+ expect(returns_obj).to match_field_structure([:pet_name,
263
+ :animal_type,
264
+ {:pet_measurements => [:weight, :height, :num_legs]}
265
+ ])
266
+ end
267
+ it 'should have the 202 response described in the swagger' do
268
+ response = swagger_response_for('/pets/{id}/extra_info', 202)
269
+ expect(response[:description]).to eq('Accepted')
270
+
271
+ schema = response[:schema]
272
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
273
+ expect(schema).to have_field(:animal_type, 'string')
274
+ expect(schema).to have_field(:pet_measurements, 'object')
275
+
276
+ pm_schema = schema[:properties][:pet_measurements]
277
+ expect(pm_schema).to have_field(:weight, 'number', {:description => "Weight in pounds"})
278
+ expect(pm_schema).to have_field(:height, 'number', {:description => "Height in inches"})
279
+ expect(pm_schema).to have_field(:num_legs, 'number', {:description => "Number of legs", :required => false})
280
+ end
281
+
282
+ it "should return code 203 with spread out 'pet', encapsulated 'pet_measurements' and encapsulated 'pet_history'" do
283
+ returns_obj = subject.returns.detect{|e| e.code == 203 }
284
+
285
+ puts returns_obj.to_json
286
+ expect(returns_obj.code).to eq(203)
287
+ expect(returns_obj.is_array?).to eq(false)
288
+
289
+ expect(returns_obj).to match_field_structure([:pet_name,
290
+ :animal_type,
291
+ {:pet_measurements => [:weight, :height,:num_legs]},
292
+ {:pet_history => [:did_visit_vet, :avg_meals_per_day]},
293
+ {:additional_histories => [:did_visit_vet, :avg_meals_per_day]}
294
+ ])
295
+ end
296
+ it 'should have the 203 response described in the swagger' do
297
+ response = swagger_response_for('/pets/{id}/extra_info', 203)
298
+ expect(response[:description]).to eq('Non-Authoritative Information')
299
+
300
+ schema = response[:schema]
301
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
302
+ expect(schema).to have_field(:animal_type, 'string')
303
+ expect(schema).to have_field(:pet_measurements, 'object')
304
+ expect(schema).to have_field(:pet_history, 'object')
305
+ expect(schema).to have_field(:additional_histories, 'array')
306
+
307
+ pm_schema = schema[:properties][:pet_measurements]
308
+ expect(pm_schema).to have_field(:weight, 'number', {:description => "Weight in pounds"})
309
+ expect(pm_schema).to have_field(:height, 'number', {:description => "Height in inches"})
310
+ expect(pm_schema).to have_field(:num_legs, 'number', {:description => "Number of legs", :required => false})
311
+
312
+ ph_schema = schema[:properties][:pet_history]
313
+ expect(ph_schema).to have_field(:did_visit_vet, 'boolean')
314
+ expect(ph_schema).to have_field(:avg_meals_per_day, 'number')
315
+
316
+ pa_schema = schema[:properties][:additional_histories]
317
+ expect(pa_schema[:type]).to eq('array')
318
+ pai_schema = schema[:properties][:additional_histories][:items]
319
+ expect(pai_schema).to have_field(:did_visit_vet, 'boolean')
320
+ expect(pai_schema).to have_field(:avg_meals_per_day, 'number')
321
+ end
322
+
323
+ it "should return code matching :unprocessable_entity (422) with spread out 'pet' and 'num_fleas'" do
324
+ returns_obj = subject.returns.detect{|e| e.code == 422 }
325
+
326
+ puts returns_obj.to_json
327
+ expect(returns_obj.code).to eq(422)
328
+
329
+ expect(returns_obj).to match_field_structure([:pet_name,
330
+ :animal_type,
331
+ :num_fleas
332
+ ])
333
+ end
334
+ it 'should have the 422 response described in the swagger' do
335
+ response = swagger_response_for('/pets/{id}/extra_info', 422)
336
+ expect(response[:description]).to eq('Fleas were discovered on the pet')
337
+
338
+ schema = response[:schema]
339
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
340
+ expect(schema).to have_field(:animal_type, 'string')
341
+ expect(schema).to have_field(:num_fleas, 'number')
342
+ end
343
+
344
+ end
345
+
346
+ end
347
+
348
+ #==============================================================================
349
+ # PetsUsingSelfDescribingClassesController is a demonstration of how
350
+ # responses can be described using manual generation of a property description
351
+ # array
352
+ #==============================================================================
353
+
354
+
355
+ describe PetsUsingSelfDescribingClassesController do
356
+
357
+ describe "PetsController#pets_described_as_class" do
358
+ subject do
359
+ desc._methods[:pets_described_as_class]
360
+ end
361
+
362
+ it "should return code 200 with array of entries of the format {'pet_name', 'animal_type'}" do
363
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
364
+
365
+ puts returns_obj.to_json
366
+ expect(returns_obj.code).to eq(200)
367
+ expect(returns_obj.is_array?).to eq(true)
368
+
369
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type])
370
+ end
371
+
372
+ it 'should have the response described in the swagger' do
373
+ response = swagger_response_for('/pets_described_as_class')
374
+ expect(response[:description]).to eq("list of pets")
375
+
376
+ schema = response[:schema]
377
+ expect(schema[:type]).to eq("array")
378
+
379
+ a_schema = schema[:items]
380
+ expect(a_schema).to have_field(:pet_name, 'string', {:description => 'Name of pet', :required => false})
381
+ expect(a_schema).to have_field(:animal_type, 'string', {:description => 'Type of pet', :enum => ['dog','cat','iguana','kangaroo']})
382
+ end
383
+ end
384
+
385
+
386
+ describe "PetsController#pets_with_measurements_described_as_class" do
387
+ subject do
388
+ desc._methods[:pets_with_measurements_described_as_class]
389
+ end
390
+
391
+ it "should return code 200 with spread out 'pet' and encapsulated 'pet_measurements'" do
392
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
393
+
394
+ puts returns_obj.to_json
395
+ expect(returns_obj.code).to eq(200)
396
+ expect(returns_obj.is_array?).to eq(false)
397
+
398
+ expect(returns_obj).to match_field_structure([:pet_name,
399
+ :animal_type,
400
+ {:pet_measurements => [:weight, :height, :num_legs]}
401
+ ])
402
+ end
403
+ it 'should have the 200 response described in the swagger' do
404
+ response = swagger_response_for('/pets_with_measurements_described_as_class/{id}', 200)
405
+ expect(response[:description]).to eq('measurements of the pet')
406
+
407
+ schema = response[:schema]
408
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
409
+ expect(schema).to have_field(:animal_type, 'string')
410
+ expect(schema).to have_field(:pet_measurements, 'object')
411
+
412
+ pm_schema = schema[:properties][:pet_measurements]
413
+ expect(pm_schema).to have_field(:weight, 'number', {:description => "Weight in pounds"})
414
+ expect(pm_schema).to have_field(:height, 'number', {:description => "Height in inches"})
415
+ expect(pm_schema).to have_field(:num_legs, 'number', {:description => "Number of legs", :required => false})
416
+ end
417
+ end
418
+
419
+ describe "PetsController#pets_with_many_measurements_as_class" do
420
+ subject do
421
+ desc._methods[:pets_with_many_measurements_as_class]
422
+ end
423
+
424
+ it "should return code 200 with pet_name (string) and many_pet_measurements (array of objects)" do
425
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
426
+
427
+ puts returns_obj.to_json
428
+ expect(returns_obj.code).to eq(200)
429
+ expect(returns_obj.is_array?).to eq(false)
430
+
431
+ expect(returns_obj).to match_field_structure([:pet_name,
432
+ {:many_pet_measurements => [:weight, :height]}
433
+ ])
434
+ end
435
+
436
+
437
+ it 'should have the 200 response described in the swagger' do
438
+ response = swagger_response_for('/pets_with_many_measurements_as_class/{id}', 200)
439
+ expect(response[:description]).to eq('measurements of the pet')
440
+
441
+ schema = response[:schema]
442
+ expect(schema).to have_field(:pet_name, 'string', {:required => false})
443
+ expect(schema).to have_field(:many_pet_measurements, 'array')
444
+
445
+ pm_schema = schema[:properties][:many_pet_measurements][:items]
446
+ expect(pm_schema).to have_field(:weight, 'number', {:description => "Weight in pounds"})
447
+ expect(pm_schema).to have_field(:height, 'number', {:description => "Height in inches"})
448
+ end
449
+ end
450
+
451
+ end
452
+
453
+
454
+ #=========================================================
455
+ # PetsUsingAutoViewsController is a demonstration of how
456
+ # responses can be described using logic
457
+ #=========================================================
458
+
459
+ describe PetsUsingAutoViewsController do
460
+
461
+ describe "PetsController#pet_described_using_automated_view" do
462
+ subject do
463
+ desc._methods[:pet_described_using_automated_view]
464
+ end
465
+
466
+ it "should return code 200 with array of entries of the format {'pet_name', 'animal_type'}" do
467
+ returns_obj = subject.returns.detect{|e| e.code == 200 }
468
+
469
+ expect(returns_obj.code).to eq(200)
470
+ expect(returns_obj.is_array?).to eq(false)
471
+ expect(returns_obj).to match_field_structure([:pet_name, :animal_type, :age])
472
+ end
473
+
474
+ it 'should have the response described in the swagger' do
475
+ response = swagger_response_for('/pet_described_using_automated_view/{id}')
476
+ expect(response[:description]).to eq("like Pet, but different")
477
+
478
+ schema = response[:schema]
479
+ expect(schema[:type]).to eq("object")
480
+
481
+ expect(schema).to have_field(:pet_name, 'string', {:required => true})
482
+ expect(schema).to have_field(:animal_type, 'string', {:required => true})
483
+ expect(schema).to have_field(:age, 'number', {:required => true})
484
+ end
485
+ end
486
+ end
487
+
488
+
489
+ end