apipie-rails 0.5.7 → 0.5.8

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,199 @@
1
+ module Apipie
2
+
3
+ def self.prop(name, expected_type, options={}, sub_properties=[])
4
+ Apipie::ResponseDescriptionAdapter::PropDesc.new(name, expected_type, options, sub_properties)
5
+ end
6
+
7
+ def self.additional_properties(yesno)
8
+ Apipie::ResponseDescriptionAdapter::AdditionalPropertiesModifier.new(yesno)
9
+ end
10
+
11
+ class ResponseDescriptionAdapter
12
+ class Modifier
13
+ def apply(adapter)
14
+ raise "Modifer subclass must implement 'apply' method"
15
+ end
16
+ end
17
+
18
+ class AdditionalPropertiesModifier < Modifier
19
+ def initialize(additional_properties_allowed)
20
+ @additional_properties_allowed = additional_properties_allowed
21
+ end
22
+
23
+ def apply(adapter)
24
+ adapter.additional_properties = @additional_properties_allowed
25
+ end
26
+ end
27
+ end
28
+
29
+
30
+ class ResponseDescriptionAdapter
31
+
32
+ #
33
+ # A ResponseDescriptionAdapter::PropDesc object pretends to be an Apipie::Param in a ResponseDescription
34
+ #
35
+ # To successfully masquerade as such, it needs to:
36
+ # respond_to?('name') and/or ['name'] returning the name of the parameter
37
+ # respond_to?('required') and/or ['required'] returning boolean
38
+ # respond_to?('additional_properties') and/or ['additional_properties'] returning boolean
39
+ # respond_to?('validator') and/or ['validator'] returning 'nil' (so type is 'string'), or an object that:
40
+ # 1) describes a type. currently type is inferred as follows:
41
+ # if validator.is_a? Apipie::Validator::EnumValidator --> respond_to? 'values' (returns array). Type is enum or boolean
42
+ # else: use v.expected_type(). This is expected to be the swagger type, or:
43
+ # numeric ==> swagger type is 'number'
44
+ # hash ==> swagger type is 'object' and validator should respond_to? 'params_ordered'
45
+ # array ==> swagger type is array and validator (FUTURE) should indicate type of element
46
+
47
+ class PropDesc
48
+
49
+ def to_s
50
+ "PropDesc -- name: #{@name} type: #{@expected_type} required: #{@required} options: #{@options} subprop count: #{@sub_properties.length} additional properties: #{@additional_properties}"
51
+ end
52
+
53
+ #
54
+ # a ResponseDescriptionAdapter::PropDesc::Validator pretends to be an Apipie::Validator
55
+ #
56
+ class Validator
57
+ attr_reader :expected_type
58
+
59
+ def [](key)
60
+ return self.send(key) if self.respond_to?(key.to_s)
61
+ end
62
+
63
+ def initialize(expected_type, enum_values=nil, sub_properties=nil)
64
+ @expected_type = expected_type
65
+ @enum_values = enum_values
66
+ @is_enum = !!enum_values
67
+ @sub_properties = sub_properties
68
+ end
69
+
70
+ def is_enum?
71
+ !!@is_enum
72
+ end
73
+
74
+ def values
75
+ @enum_values
76
+ end
77
+
78
+ def params_ordered
79
+ raise "Only validators with expected_type 'object' can have sub-properties" unless @expected_type == 'object'
80
+ @sub_properties
81
+ end
82
+ end
83
+
84
+ #======================================================================
85
+
86
+
87
+ def initialize(name, expected_type, options={}, sub_properties=[])
88
+ @name = name
89
+ @required = true
90
+ @required = false if options[:required] == false
91
+ @expected_type = expected_type
92
+ @additional_properties = false
93
+
94
+ options[:desc] ||= options[:description]
95
+ @description = options[:desc]
96
+ @options = options
97
+ @is_array = options[:is_array] || false
98
+ @sub_properties = []
99
+ for prop in sub_properties do
100
+ add_sub_property(prop)
101
+ end
102
+ end
103
+
104
+ def [](key)
105
+ return self.send(key) if self.respond_to?(key.to_s)
106
+ end
107
+
108
+ def add_sub_property(prop_desc)
109
+ raise "Only properties with expected_type 'object' can have sub-properties" unless @expected_type == 'object'
110
+ if prop_desc.is_a? PropDesc
111
+ @sub_properties << prop_desc
112
+ elsif prop_desc.is_a? Modifier
113
+ prop_desc.apply(self)
114
+ else
115
+ raise "Unrecognized prop_desc type (#{prop_desc.class})"
116
+ end
117
+ end
118
+
119
+ def to_json(lang)
120
+ {
121
+ name: name,
122
+ required: required,
123
+ validator: validator,
124
+ description: description,
125
+ additional_properties: additional_properties,
126
+ is_array: is_array?,
127
+ options: options
128
+ }
129
+ end
130
+ attr_reader :name, :required, :expected_type, :options, :description
131
+ attr_accessor :additional_properties
132
+
133
+ alias_method :desc, :description
134
+
135
+ def is_array?
136
+ @is_array
137
+ end
138
+
139
+ def validator
140
+ Validator.new(@expected_type, options[:values], @sub_properties)
141
+ end
142
+ end
143
+ end
144
+
145
+ #======================================================================
146
+
147
+ class ResponseDescriptionAdapter
148
+
149
+ def self.from_self_describing_class(cls)
150
+ adapter = ResponseDescriptionAdapter.new
151
+ props = cls.describe_own_properties
152
+ adapter.add_property_descriptions(props)
153
+ adapter
154
+ end
155
+
156
+ def initialize
157
+ @property_descs = []
158
+ @additional_properties = false
159
+ end
160
+
161
+ attr_accessor :additional_properties
162
+
163
+ def allow_additional_properties
164
+ additional_properties
165
+ end
166
+
167
+ def to_json
168
+ params_ordered.to_json
169
+ end
170
+
171
+ def add(prop_desc)
172
+ if prop_desc.is_a? PropDesc
173
+ @property_descs << prop_desc
174
+ elsif prop_desc.is_a? Modifier
175
+ prop_desc.apply(self)
176
+ else
177
+ raise "Unrecognized prop_desc type (#{prop_desc.class})"
178
+ end
179
+ end
180
+
181
+ def add_property_descriptions(prop_descs)
182
+ for prop_desc in prop_descs
183
+ add(prop_desc)
184
+ end
185
+ end
186
+
187
+ def property(name, expected_type, options)
188
+ @property_descs << PropDesc.new(name, expected_type, options)
189
+ end
190
+
191
+ def params_ordered
192
+ @property_descs
193
+ end
194
+
195
+ def is_array?
196
+ false
197
+ end
198
+ end
199
+ end
@@ -13,6 +13,7 @@ module Apipie
13
13
 
14
14
  def initialize(apipie)
15
15
  @apipie = apipie
16
+ @issued_warnings = []
16
17
  end
17
18
 
18
19
  def params_in_body?
@@ -220,10 +221,12 @@ module Apipie
220
221
 
221
222
  methods[method_key] = {
222
223
  tags: [tag_name_for_resource(ruby_method.resource)] + warning_tags,
224
+ consumes: params_in_body? ? ['application/json'] : ['application/x-www-form-urlencoded', 'multipart/form-data'],
223
225
  operationId: op_id,
224
226
  summary: Apipie.app.translate(api.short_description, @current_lang),
225
227
  parameters: swagger_params_array_for_method(ruby_method, api.path),
226
- responses: responses
228
+ responses: responses,
229
+ description: ruby_method.full_description
227
230
  }
228
231
 
229
232
  if methods[method_key][:summary].nil?
@@ -262,6 +265,45 @@ module Apipie
262
265
  http_method.downcase + path.gsub(/\//,'_').gsub(/:(\w+)/, '\1').gsub(/_$/,'')
263
266
  end
264
267
 
268
+ class SwaggerTypeWithFormat
269
+ attr_reader :str_format
270
+ def initialize(type, str_format)
271
+ @type = type
272
+ @str_format = str_format
273
+ end
274
+
275
+ def to_s
276
+ @type
277
+ end
278
+
279
+ def ==(other)
280
+ other.to_s == self.to_s
281
+ end
282
+ end
283
+
284
+ def lookup
285
+ @lookup ||= {
286
+ numeric: "number",
287
+ hash: "object",
288
+ array: "array",
289
+
290
+ # see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types
291
+ integer: SwaggerTypeWithFormat.new("integer", "int32"),
292
+ long: SwaggerTypeWithFormat.new("integer", "int64"),
293
+ number: SwaggerTypeWithFormat.new("number", nil), # here just for completeness
294
+ float: SwaggerTypeWithFormat.new("number", "float"),
295
+ double: SwaggerTypeWithFormat.new("number", "double"),
296
+ string: SwaggerTypeWithFormat.new("string", nil), # here just for completeness
297
+ byte: SwaggerTypeWithFormat.new("string", "byte"),
298
+ binary: SwaggerTypeWithFormat.new("string", "binary"),
299
+ boolean: SwaggerTypeWithFormat.new("boolean", nil), # here just for completeness
300
+ date: SwaggerTypeWithFormat.new("string", "date"),
301
+ dateTime: SwaggerTypeWithFormat.new("string", "date-time"),
302
+ password: SwaggerTypeWithFormat.new("string", "password"),
303
+ }
304
+ end
305
+
306
+
265
307
  def swagger_param_type(param_desc)
266
308
  if param_desc.nil?
267
309
  raise("problem")
@@ -272,7 +314,7 @@ module Apipie
272
314
  return "string"
273
315
  end
274
316
 
275
- if v.class == Apipie::Validator::EnumValidator
317
+ if v.class == Apipie::Validator::EnumValidator || (v.respond_to?(:is_enum?) && v.is_enum?)
276
318
  if v.values - [true, false] == [] && [true, false] - v.values == []
277
319
  warn_inferring_boolean(param_desc.name)
278
320
  return "boolean"
@@ -283,11 +325,6 @@ module Apipie
283
325
  # pp v
284
326
  end
285
327
 
286
- lookup = {
287
- numeric: "number",
288
- hash: "object",
289
- array: "array"
290
- }
291
328
 
292
329
  return lookup[v.expected_type.to_sym] || v.expected_type
293
330
  end
@@ -297,6 +334,30 @@ module Apipie
297
334
  # Responses
298
335
  #--------------------------------------------------------------------------
299
336
 
337
+ def response_schema(response)
338
+ begin
339
+ # no need to warn about "missing default value for optional param" when processing response definitions
340
+ prev_value = @disable_default_value_warning
341
+ @disable_default_value_warning = true
342
+ schema = json_schema_obj_from_params_array(response.params_ordered)
343
+ ensure
344
+ @disable_default_value_warning = prev_value
345
+ end
346
+
347
+ if response.is_array? && schema
348
+ schema = {
349
+ type: "array",
350
+ items: schema
351
+ }
352
+ end
353
+
354
+ if response.allow_additional_properties
355
+ schema[:additionalProperties] = true
356
+ end
357
+
358
+ schema
359
+ end
360
+
300
361
  def swagger_responses_hash_for_method(method)
301
362
  result = {}
302
363
 
@@ -305,6 +366,17 @@ module Apipie
305
366
  result[error.code] = error_block
306
367
  end
307
368
 
369
+ for response in method.returns
370
+ swagger_response_block = {
371
+ description: response.description
372
+ }
373
+
374
+ schema = response_schema(response)
375
+ swagger_response_block[:schema] = schema if schema
376
+
377
+ result[response.code] = swagger_response_block
378
+ end
379
+
308
380
  if result.length == 0
309
381
  warn_no_return_codes_specified
310
382
  result[200] = {description: 'ok'}
@@ -351,7 +423,7 @@ module Apipie
351
423
  # The core routine for creating a swagger parameter definition block.
352
424
  # The output is slightly different when the parameter is inside a schema block.
353
425
  #--------------------------------------------------------------------------
354
- def swagger_atomic_param(param_desc, in_schema, name=nil)
426
+ def swagger_atomic_param(param_desc, in_schema, name)
355
427
  def save_field(entry, openapi_key, v, apipie_key=openapi_key, translate=false)
356
428
  if v.key?(apipie_key)
357
429
  if translate
@@ -364,7 +436,12 @@ module Apipie
364
436
 
365
437
  swagger_def = {}
366
438
  swagger_def[:name] = name if !name.nil?
367
- swagger_def[:type] = swagger_param_type(param_desc)
439
+
440
+ swg_param_type = swagger_param_type(param_desc)
441
+ swagger_def[:type] = swg_param_type.to_s
442
+ if (swg_param_type.is_a? SwaggerTypeWithFormat) && !swg_param_type.str_format.nil?
443
+ swagger_def[:format] = swg_param_type.str_format
444
+ end
368
445
 
369
446
  if swagger_def[:type] == "array"
370
447
  swagger_def[:items] = {type: "string"} # TODO: add support for arrays of non-string items
@@ -385,7 +462,7 @@ module Apipie
385
462
  swagger_def[:required] = param_desc.required if param_desc.required
386
463
  end
387
464
 
388
- save_field(swagger_def, :description, param_desc.options, :desc, true)
465
+ save_field(swagger_def, :description, param_desc.options, :desc, true) unless param_desc.options[:desc].nil?
389
466
  save_field(swagger_def, :default, param_desc.options, :default_value)
390
467
 
391
468
  if param_desc.respond_to?(:_gen_added_from_path) && !param_desc.required
@@ -394,7 +471,7 @@ module Apipie
394
471
  end
395
472
 
396
473
  if !swagger_def[:required] && !swagger_def.key?(:default)
397
- warn_optional_without_default_value(param_desc.name)
474
+ warn_optional_without_default_value(param_desc.name) unless @disable_default_value_warning
398
475
  end
399
476
 
400
477
  swagger_def
@@ -415,6 +492,7 @@ module Apipie
415
492
 
416
493
  result = {type: "object"}
417
494
  result[:properties] = param_defs
495
+ result[:additionalProperties] = false unless Apipie.configuration.swagger_allow_additional_properties_in_response
418
496
  result[:required] = required_params if required_params.length > 0
419
497
 
420
498
  param_defs.length > 0 ? result : nil
@@ -426,8 +504,8 @@ module Apipie
426
504
  schema_obj = json_schema_obj_from_params_array(params_array)
427
505
  return nil if schema_obj.nil?
428
506
 
429
- @definitions[name] = schema_obj
430
- ref_to(name)
507
+ @definitions[name.to_sym] = schema_obj
508
+ ref_to(name.to_sym)
431
509
  end
432
510
 
433
511
  def json_schema_param_defs_from_params_array(params_array)
@@ -443,15 +521,27 @@ module Apipie
443
521
  raise ("unexpected param_desc format")
444
522
  end
445
523
 
446
- required_params.push(param_desc.name) if param_desc.required
524
+ required_params.push(param_desc.name.to_sym) if param_desc.required
447
525
 
448
526
  param_type = swagger_param_type(param_desc)
449
527
 
450
528
  if param_type == "object" && param_desc.validator.params_ordered
451
529
  schema = json_schema_obj_from_params_array(param_desc.validator.params_ordered)
452
- param_defs[param_desc.name] = schema if !schema.nil?
530
+ if param_desc.additional_properties
531
+ schema[:additionalProperties] = true
532
+ end
533
+
534
+ if param_desc.is_array?
535
+ new_schema = {
536
+ type: 'array',
537
+ items: schema
538
+ }
539
+ schema = new_schema
540
+ end
541
+
542
+ param_defs[param_desc.name.to_sym] = schema if !schema.nil?
453
543
  else
454
- param_defs[param_desc.name] = swagger_atomic_param(param_desc, true)
544
+ param_defs[param_desc.name.to_sym] = swagger_atomic_param(param_desc, true, nil)
455
545
  end
456
546
  end
457
547
 
@@ -1,3 +1,3 @@
1
1
  module Apipie
2
- VERSION = '0.5.7'
2
+ VERSION = '0.5.8'
3
3
  end
@@ -142,6 +142,7 @@ describe Apipie::ApipiesController do
142
142
 
143
143
  assert_response :success
144
144
  expect(response.body).to match(/"swagger":"2.0"/)
145
+ # puts response.body
145
146
  expect(JSON::Validator.validate(swagger_schema, response.body)).to be_truthy
146
147
  end
147
148
 
@@ -3,6 +3,10 @@ class ApplicationController < ActionController::Base
3
3
 
4
4
  resource_description do
5
5
  param :oauth, String, :desc => "Authorization", :required => false
6
+
7
+ returns :code => 401 do
8
+ property :reason, String, "Why authorization was denied"
9
+ end
6
10
  end
7
11
 
8
12
  def run_validations
@@ -0,0 +1,391 @@
1
+ #
2
+ # The PetsController defined here provides examples for different ways
3
+ # in which the 'returns' DSL directive can be used with the existing
4
+ # Apipie DSL elements 'param' and 'param_group'
5
+ #
6
+
7
+ class PetsController < ApplicationController
8
+ resource_description do
9
+ description 'A controller to test "returns"'
10
+ short 'Pets'
11
+ path '/pets'
12
+
13
+ param :common_param, Integer, :desc => "A param that can optionally be passed to all Pet methods", :required => false
14
+
15
+ returns :code => 404 do
16
+ property :error_message, String, "description of the error"
17
+ end
18
+
19
+ end
20
+
21
+ #-----------------------------------------------------------
22
+ # simple 'returns' example: a method that returns a pet record
23
+ #-----------------------------------------------------------
24
+ api :GET, "/pets/:id/as_properties", "Get a pet record"
25
+ returns :code => 200 do
26
+ property :pet_name, String, :desc => "Name of pet", :required => false
27
+ property :animal_type, ['dog','cat','iguana','kangaroo'], :desc => "Type of pet" # required by default, because this is a 'property'
28
+ end
29
+ returns :code => 404 do
30
+ property :another_error_message, String, :desc => "Overriding the response description from the Pets resource"
31
+ end
32
+ def show_as_properties
33
+ render :plain => "showing pet properties"
34
+ end
35
+
36
+
37
+ #-----------------------------------------------------------
38
+ # same example as above, but this time the properties are defined
39
+ # in a param group
40
+ #-----------------------------------------------------------
41
+ def_param_group :pet do
42
+ property :pet_name, String, :desc => "Name of pet", :required => false
43
+ property :animal_type, ['dog','cat','iguana','kangaroo'], :desc => "Type of pet" # required by default, because this is a 'property'
44
+ end
45
+
46
+ api :GET, "/pets/:id/as_param_group_of_properties", "Get a pet record"
47
+ returns :pet, :code => 200, :desc => "The pet"
48
+ def show_as_param_group_of_properties
49
+ render :plain => "showing pet properties defined as param groups"
50
+ end
51
+
52
+ #-----------------------------------------------------------
53
+ # Method returning an array of the :pet param_group
54
+ #-----------------------------------------------------------
55
+ api :GET, "/pets", "Get all pets"
56
+ returns :array_of => :pet, :desc => "list of pets"
57
+ def index
58
+ render :plain => "all pets"
59
+ end
60
+
61
+
62
+ #-----------------------------------------------------------
63
+ # mixing request/response and response-only parameters
64
+ #
65
+ # the param_group :pet_with_id has several parameters which are
66
+ # not expectd in the request, but would show up in the response
67
+ # and vice versa
68
+ #-----------------------------------------------------------
69
+ def_param_group :pet_with_id do
70
+ param :pet_id, Integer, :desc => "id of pet", :required => true
71
+ param :pet_name, String, :desc => "Name of pet", :required => false, :only_in => :response
72
+ param :partial_match_allowed, [true, false], :desc => "Partial match allowed?", :required => false, :only_in => :request
73
+ property :animal_type, ['dog','cat','iguana','kangaroo'], :desc => "Type of pet" # this is implicitly :only_in => :response
74
+ end
75
+
76
+ api :GET, "/pets/pet_by_id", "Get a pet record with the pet id in the body of the request"
77
+ param_group :pet_with_id # only the pet_id is expected to be in here
78
+ returns :param_group => :pet_with_id, :code => 200
79
+ def show_pet_by_id
80
+ render :plain => "returning a record with 3 fields"
81
+ end
82
+
83
+
84
+
85
+ #-----------------------------------------------------------
86
+ # example with multiple param groups
87
+ #-----------------------------------------------------------
88
+ def_param_group :owner do # a param group that can be used to describe inputs as well as outputs
89
+ param :owner_name, String
90
+ end
91
+ def_param_group :user_response do # a param group that can be used to describe outputs only
92
+ property :vote, [true,false]
93
+ end
94
+
95
+ api :GET, "/pets/by_owner_name/did_vote", "did any of the pets owned by the given user vote?"
96
+ param_group :owner, :desc => "look up the user by name"
97
+ returns :code => 200 do
98
+ param_group :owner
99
+ param_group :user_response
100
+ end
101
+ returns :code => 404 # no body
102
+ def get_vote_by_owner_name
103
+ render :plain => "no pets have voted"
104
+ end
105
+
106
+
107
+ #-----------------------------------------------------------
108
+ # a method returning multiple codes,
109
+ # some of which have a complex data structure
110
+ #-----------------------------------------------------------
111
+ def_param_group :pet_measurements do
112
+ property :pet_measurements, Hash do
113
+ property :weight, Integer, :desc => "Weight in pounds"
114
+ property :height, Integer, :desc => "Height in inches"
115
+ property :num_legs, Integer, :desc => "Number of legs", :required => false
116
+ end
117
+ end
118
+
119
+ def_param_group :pet_history do
120
+ property :did_visit_vet, [true,false], :desc => "Did the pet visit the veterinarian"
121
+ property :avg_meals_per_day, Float, :desc => "Average number of meals per day"
122
+ end
123
+
124
+ api :GET, "/pets/:id/extra_info", "Get extra information about a pet"
125
+ returns :code => 201, :desc => "Found a pet" do
126
+ param_group :pet
127
+ end
128
+ returns :code => 202 do
129
+ param_group :pet
130
+ param_group :pet_measurements
131
+ end
132
+ returns :code => 203 do
133
+ param_group :pet
134
+ param_group :pet_measurements
135
+ property 'pet_history', Hash do # the pet_history param_group does not contain a wrapping object,
136
+ # so create one manually
137
+ param_group :pet_history
138
+ end
139
+ property 'additional_histories', :array_of => Hash do
140
+ param_group :pet_history
141
+ end
142
+ end
143
+ returns :code => :unprocessable_entity, :desc => "Fleas were discovered on the pet" do
144
+ param_group :pet
145
+ property :num_fleas, Integer, :desc => "Number of fleas on this pet"
146
+ end
147
+ def show_extra_info
148
+ render :plain => "please disinfect your pet"
149
+ end
150
+
151
+
152
+ #=======================================================================
153
+ # Methods for testing response validation
154
+ #=======================================================================
155
+
156
+
157
+ #-----------------------------------------------------------
158
+ # A method which returns the response as described
159
+ #-----------------------------------------------------------
160
+ api!
161
+ returns :code => 200 do
162
+ property :a_number, Integer
163
+ property :an_optional_number, Integer, :required=>false
164
+ end
165
+ def return_and_validate_expected_response
166
+ result = {
167
+ a_number: 3
168
+ }
169
+ render :json => result
170
+ end
171
+
172
+ #-----------------------------------------------------------
173
+ # A method which returns a null value in the response
174
+ #-----------------------------------------------------------
175
+ api!
176
+ returns :code => 200 do
177
+ property :a_number, Integer
178
+ property :an_optional_number, Integer, :required=>false
179
+ end
180
+ def return_and_validate_expected_response_with_null
181
+ result = {
182
+ a_number: nil
183
+ }
184
+ render :json => result
185
+ end
186
+
187
+ #-----------------------------------------------------------
188
+ # A method which returns a null value in the response instead of an object
189
+ #-----------------------------------------------------------
190
+ api!
191
+ returns :code => 200 do
192
+ property :an_object, Hash do
193
+ property :an_optional_number, Integer, :required=>false
194
+ end
195
+ end
196
+ def return_and_validate_expected_response_with_null_object
197
+ result = {
198
+ an_object: nil
199
+ }
200
+ render :json => result
201
+ end
202
+
203
+ #-----------------------------------------------------------
204
+ # A method which returns an array response as described
205
+ #-----------------------------------------------------------
206
+ def_param_group :two_numbers do
207
+ property :a_number, Integer
208
+ property :an_optional_number, Integer, :required=>false
209
+ end
210
+
211
+ api!
212
+ returns :code => 200, :array_of => :two_numbers
213
+ def return_and_validate_expected_array_response
214
+ result = [{
215
+ a_number: 3
216
+ }]
217
+ render :json => result
218
+ end
219
+
220
+ #-----------------------------------------------------------
221
+ # A method which returns an array response when it is expected to return an object
222
+ # (note that response code is set here to 201)
223
+ #-----------------------------------------------------------
224
+ api!
225
+ returns :two_numbers, :code => 201
226
+ def return_and_validate_unexpected_array_response
227
+ result = [{
228
+ a_number: 3
229
+ }]
230
+ render :status => 201, :json => result
231
+ end
232
+
233
+ #-----------------------------------------------------------
234
+ # A method which has a response that does not match the output type
235
+ #-----------------------------------------------------------
236
+ api!
237
+ returns :code => 200 do
238
+ property :a_number, String
239
+ end
240
+ def return_and_validate_type_mismatch
241
+ result = {
242
+ a_number: 3
243
+ }
244
+ render :json => result
245
+ end
246
+
247
+
248
+ #-----------------------------------------------------------
249
+ # A method which has a response with a missing field
250
+ #-----------------------------------------------------------
251
+ api!
252
+ returns :code => 200 do
253
+ property :a_number, Integer
254
+ property :another_number, Integer
255
+ end
256
+ def return_and_validate_missing_field
257
+ result = {
258
+ a_number: 3
259
+ }
260
+ render :json => result
261
+ end
262
+
263
+
264
+ #-----------------------------------------------------------
265
+ # A method which has a response with an extra property
266
+ # (should raise a validation error by default)
267
+ #-----------------------------------------------------------
268
+ api!
269
+ returns :code => 200 do
270
+ property :a_number, Integer
271
+ end
272
+ def return_and_validate_extra_property
273
+ result = {
274
+ a_number: 3,
275
+ another_number: 4
276
+ }
277
+ render :json => result
278
+ end
279
+
280
+
281
+ #-----------------------------------------------------------
282
+ # A method which has a response with an extra property, but 'additional_properties' is specified
283
+ # (should not raise a validation error by default)
284
+ #-----------------------------------------------------------
285
+ api!
286
+ returns :code => 200, :additional_properties => true do
287
+ property :a_number, Integer
288
+ end
289
+ def return_and_validate_allowed_extra_property
290
+ result = {
291
+ a_number: 3,
292
+ another_number: 4
293
+ }
294
+ render :json => result
295
+ end
296
+
297
+ #-----------------------------------------------------------
298
+ # Sub-object in response has an extra property. 'additional_properties' is specified on the response, but not on the object
299
+ # (should raise a validation error by default)
300
+ #-----------------------------------------------------------
301
+ api!
302
+ returns :code => 200, :additional_properties => true do
303
+ property :an_object, Hash do
304
+ property :a_number, Integer
305
+ end
306
+ end
307
+ def sub_object_invalid_extra_property
308
+ result = {
309
+ an_object: {
310
+ a_number: 2,
311
+ an_extra_number: 3
312
+ }
313
+ }
314
+ render :json => result
315
+ end
316
+
317
+
318
+ #-----------------------------------------------------------
319
+ # Sub-object in response has an extra property. 'additional_properties' is specified on the object, but not on the response
320
+ # (should not raise a validation error by default)
321
+ #-----------------------------------------------------------
322
+ api!
323
+ returns :code => 200, :additional_properties => false do
324
+ property :an_object, Hash, :additional_properties => true do
325
+ property :a_number, Integer
326
+ end
327
+ end
328
+ def sub_object_allowed_extra_property
329
+ result = {
330
+ an_object: {
331
+ a_number: 2,
332
+ an_extra_number: 3
333
+ }
334
+ }
335
+ render :json => result
336
+ end
337
+
338
+
339
+ #=======================================================================
340
+ # Methods for testing array field responses
341
+ #=======================================================================
342
+
343
+ #-----------------------------------------------------------
344
+ # A method which returns the response as described (one field is an array of objects)
345
+ #-----------------------------------------------------------
346
+ api!
347
+ returns :code => 200 do
348
+ property :a_number, Integer
349
+ property :array_of_objects, :array_of => Hash do
350
+ property :number1, Integer, :required=>true
351
+ property :number2, Integer, :required=>true
352
+ end
353
+ end
354
+ def returns_response_with_valid_array
355
+ result = {
356
+ a_number: 3,
357
+ array_of_objects: [
358
+ {number1: 1, number2: 2},
359
+ {number1: 10, number2: 20}
360
+ ]
361
+ }
362
+ render :json => result
363
+ end
364
+
365
+
366
+ #-----------------------------------------------------------
367
+ # A method which returns an incorrect response (wrong field type inside array)
368
+ #-----------------------------------------------------------
369
+ api!
370
+ returns :code => 200 do
371
+ property :a_number, Integer
372
+ property :array_of_objects, :array_of => Hash do
373
+ property :number1, Integer, :required=>true
374
+ property :number2, Integer, :required=>true
375
+ end
376
+ end
377
+ def returns_response_with_invalid_array
378
+ result = {
379
+ a_number: 3,
380
+ array_of_objects: [
381
+ {number1: 1, number2: 2},
382
+ {number1: 10, number2: "this should have been a number"}
383
+ ]
384
+ }
385
+ render :json => result
386
+ end
387
+
388
+
389
+
390
+ end
391
+