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,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
+