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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/PROPOSAL_FOR_RESPONSE_DESCRIPTIONS.md +244 -0
- data/README.rst +320 -2
- data/lib/apipie-rails.rb +2 -0
- data/lib/apipie/application.rb +1 -1
- data/lib/apipie/configuration.rb +5 -2
- data/lib/apipie/dsl_definition.rb +69 -1
- data/lib/apipie/errors.rb +6 -1
- data/lib/apipie/extractor/writer.rb +65 -42
- data/lib/apipie/method_description.rb +32 -2
- data/lib/apipie/param_description.rb +19 -1
- data/lib/apipie/resource_description.rb +3 -1
- data/lib/apipie/response_description.rb +125 -0
- data/lib/apipie/response_description_adapter.rb +199 -0
- data/lib/apipie/swagger_generator.rb +106 -16
- data/lib/apipie/version.rb +1 -1
- data/spec/controllers/apipies_controller_spec.rb +1 -0
- data/spec/dummy/app/controllers/application_controller.rb +4 -0
- data/spec/dummy/app/controllers/pets_controller.rb +391 -0
- data/spec/dummy/app/controllers/pets_using_auto_views_controller.rb +73 -0
- data/spec/dummy/app/controllers/pets_using_self_describing_classes_controller.rb +95 -0
- data/spec/lib/extractor/writer_spec.rb +32 -4
- data/spec/lib/method_description_spec.rb +27 -0
- data/spec/lib/swagger/rake_swagger_spec.rb +4 -0
- data/spec/lib/swagger/swagger_dsl_spec.rb +489 -0
- data/spec/spec_helper.rb +94 -0
- metadata +13 -2
@@ -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
|
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
|
-
|
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
|
-
|
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
|
|
data/lib/apipie/version.rb
CHANGED
@@ -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
|
+
|