brainstem 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +147 -0
- data/Gemfile.lock +68 -39
- data/lib/brainstem/api_docs.rb +9 -4
- data/lib/brainstem/api_docs/atlas.rb +3 -3
- data/lib/brainstem/api_docs/controller.rb +12 -4
- data/lib/brainstem/api_docs/controller_collection.rb +11 -2
- data/lib/brainstem/api_docs/endpoint.rb +17 -7
- data/lib/brainstem/api_docs/endpoint_collection.rb +9 -1
- data/lib/brainstem/api_docs/formatters/open_api_specification/helper.rb +19 -16
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter.rb +52 -80
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_definitions_formatter.rb +64 -84
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint_formatter.rb +1 -1
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/endpoint_param_formatter.rb +39 -0
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter.rb +147 -0
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/response_field_formatter.rb +146 -0
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter.rb +53 -55
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/tags_formatter.rb +1 -1
- data/lib/brainstem/api_docs/presenter.rb +16 -8
- data/lib/brainstem/api_docs/presenter_collection.rb +8 -5
- data/lib/brainstem/api_docs/sinks/open_api_specification_sink.rb +3 -1
- data/lib/brainstem/cli/generate_api_docs_command.rb +4 -0
- data/lib/brainstem/concerns/controller_dsl.rb +90 -20
- data/lib/brainstem/concerns/presenter_dsl.rb +16 -8
- data/lib/brainstem/dsl/association.rb +12 -0
- data/lib/brainstem/dsl/fields_block.rb +1 -1
- data/lib/brainstem/version.rb +1 -1
- data/spec/brainstem/api_docs/controller_spec.rb +127 -5
- data/spec/brainstem/api_docs/endpoint_spec.rb +489 -57
- data/spec/brainstem/api_docs/formatters/open_api_specification/helper_spec.rb +15 -4
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter_spec.rb +112 -66
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_definitions_formatter_spec.rb +404 -32
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/endpoint_param_formatter_spec.rb +335 -0
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter_spec.rb +237 -0
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/response_field_formatter_spec.rb +413 -0
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter_spec.rb +116 -4
- data/spec/brainstem/api_docs/presenter_spec.rb +406 -24
- data/spec/brainstem/cli/generate_api_docs_command_spec.rb +8 -0
- data/spec/brainstem/concerns/controller_dsl_spec.rb +606 -45
- data/spec/brainstem/concerns/presenter_dsl_spec.rb +34 -2
- data/spec/brainstem/dsl/association_spec.rb +54 -3
- metadata +11 -2
@@ -54,12 +54,12 @@ module Brainstem
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
describe "
|
57
|
+
describe "format_sentence" do
|
58
58
|
context "when description is given" do
|
59
59
|
let(:description) { " lorem ipsum dolor sit amet " }
|
60
60
|
|
61
61
|
it "returns the formatted description" do
|
62
|
-
expect(subject.
|
62
|
+
expect(subject.format_sentence(description)).to eq("Lorem ipsum dolor sit amet.")
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
@@ -67,7 +67,7 @@ module Brainstem
|
|
67
67
|
let(:description) { "" }
|
68
68
|
|
69
69
|
it "returns nil" do
|
70
|
-
expect(subject.
|
70
|
+
expect(subject.format_sentence(description)).to eq('')
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
@@ -191,12 +191,23 @@ module Brainstem
|
|
191
191
|
it "returns type as `array` and given item type" do
|
192
192
|
expect(subject.send(:type_and_format, 'array', 'integer')).to eq({
|
193
193
|
'type' => 'array',
|
194
|
-
'items' => { 'type' => 'integer' }
|
194
|
+
'items' => { 'type' => 'integer', 'format' => 'int32' }
|
195
195
|
})
|
196
196
|
end
|
197
197
|
end
|
198
198
|
end
|
199
199
|
|
200
|
+
context "when dealing with delimited strings" do
|
201
|
+
it 'returns string as type and the collection format' do
|
202
|
+
%w(csv ssv tsv pipes).each do |delimited_string_type|
|
203
|
+
expect(subject.type_and_format(delimited_string_type)).to eq(
|
204
|
+
'type' => 'string',
|
205
|
+
'collectionFormat' => delimited_string_type,
|
206
|
+
)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
200
211
|
context "when type is unknown" do
|
201
212
|
it "returns nil" do
|
202
213
|
expect(subject.send(:type_and_format, 'invalid')).to be_nil
|
@@ -34,38 +34,86 @@ module Brainstem
|
|
34
34
|
end
|
35
35
|
|
36
36
|
describe '#call' do
|
37
|
-
context 'when
|
38
|
-
|
37
|
+
context 'when an endpoint presents a brainstem object' do
|
38
|
+
context 'when request type is get' do
|
39
|
+
let(:http_methods) { %w(GET) }
|
40
|
+
|
41
|
+
context 'when action is index' do
|
42
|
+
let(:action) { 'index' }
|
43
|
+
|
44
|
+
it 'formats path, shared, query and body params for the endpoint' do
|
45
|
+
any_instance_of(described_class) do |instance|
|
46
|
+
mock(instance).format_path_params!
|
47
|
+
mock(instance).format_optional_params!
|
48
|
+
mock(instance).format_include_params!
|
49
|
+
mock(instance).format_query_params!
|
50
|
+
mock(instance).format_body_params!
|
51
|
+
mock(instance).format_pagination_params!
|
52
|
+
mock(instance).format_search_param!
|
53
|
+
mock(instance).format_only_param!
|
54
|
+
mock(instance).format_sort_order_params!
|
55
|
+
mock(instance).format_filter_params!
|
56
|
+
end
|
57
|
+
|
58
|
+
subject.call
|
59
|
+
end
|
60
|
+
end
|
39
61
|
|
40
|
-
|
41
|
-
|
62
|
+
context 'when action is show' do
|
63
|
+
let(:action) { 'show' }
|
64
|
+
|
65
|
+
it 'formats path, optional, query and body params for the endpoint' do
|
66
|
+
any_instance_of(described_class) do |instance|
|
67
|
+
mock(instance).format_path_params!
|
68
|
+
mock(instance).format_optional_params!
|
69
|
+
mock(instance).format_include_params!
|
70
|
+
mock(instance).format_query_params!
|
71
|
+
mock(instance).format_body_params!
|
72
|
+
|
73
|
+
dont_allow(instance).format_pagination_params!
|
74
|
+
dont_allow(instance).format_search_param!
|
75
|
+
dont_allow(instance).format_only_param!
|
76
|
+
dont_allow(instance).format_sort_order_params!
|
77
|
+
dont_allow(instance).format_filter_params!
|
78
|
+
end
|
79
|
+
|
80
|
+
subject.call
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'when request type is `delete`' do
|
86
|
+
let(:http_methods) { %w(DELETE) }
|
87
|
+
let(:action) { 'destroy' }
|
42
88
|
|
43
|
-
it 'formats path,
|
89
|
+
it 'formats path, query and body param for the endpoint' do
|
44
90
|
any_instance_of(described_class) do |instance|
|
45
91
|
mock(instance).format_path_params!
|
46
|
-
mock(instance).format_optional_params!
|
47
|
-
mock(instance).format_include_params!
|
48
92
|
mock(instance).format_query_params!
|
49
93
|
mock(instance).format_body_params!
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
94
|
+
|
95
|
+
dont_allow(instance).format_pagination_params!
|
96
|
+
dont_allow(instance).format_search_param!
|
97
|
+
dont_allow(instance).format_only_param!
|
98
|
+
dont_allow(instance).format_sort_order_params!
|
99
|
+
dont_allow(instance).format_optional_params!
|
100
|
+
dont_allow(instance).format_include_params!
|
101
|
+
dont_allow(instance).format_filter_params!
|
55
102
|
end
|
56
103
|
|
57
104
|
subject.call
|
58
105
|
end
|
59
106
|
end
|
60
107
|
|
61
|
-
context 'when
|
62
|
-
let(:
|
108
|
+
context 'when request type is not delete' do
|
109
|
+
let(:http_methods) { %w(PATCH) }
|
110
|
+
let(:action) { 'update' }
|
63
111
|
|
64
|
-
it 'formats path,
|
112
|
+
it 'formats path, query and body param for the endpoint' do
|
65
113
|
any_instance_of(described_class) do |instance|
|
66
|
-
mock(instance).format_path_params!
|
67
114
|
mock(instance).format_optional_params!
|
68
115
|
mock(instance).format_include_params!
|
116
|
+
mock(instance).format_path_params!
|
69
117
|
mock(instance).format_query_params!
|
70
118
|
mock(instance).format_body_params!
|
71
119
|
|
@@ -81,9 +129,8 @@ module Brainstem
|
|
81
129
|
end
|
82
130
|
end
|
83
131
|
|
84
|
-
context 'when
|
85
|
-
let(:
|
86
|
-
let(:action) { 'destroy' }
|
132
|
+
context 'when an endpoint does not present a brainstem object' do
|
133
|
+
let(:presenter) { nil }
|
87
134
|
|
88
135
|
it 'formats path, query and body param for the endpoint' do
|
89
136
|
any_instance_of(described_class) do |instance|
|
@@ -103,29 +150,6 @@ module Brainstem
|
|
103
150
|
subject.call
|
104
151
|
end
|
105
152
|
end
|
106
|
-
|
107
|
-
context 'when request type is not delete' do
|
108
|
-
let(:http_methods) { %w(PATCH) }
|
109
|
-
let(:action) { 'update' }
|
110
|
-
|
111
|
-
it 'formats path, query and body param for the endpoint' do
|
112
|
-
any_instance_of(described_class) do |instance|
|
113
|
-
mock(instance).format_optional_params!
|
114
|
-
mock(instance).format_include_params!
|
115
|
-
mock(instance).format_path_params!
|
116
|
-
mock(instance).format_query_params!
|
117
|
-
mock(instance).format_body_params!
|
118
|
-
|
119
|
-
dont_allow(instance).format_pagination_params!
|
120
|
-
dont_allow(instance).format_search_param!
|
121
|
-
dont_allow(instance).format_only_param!
|
122
|
-
dont_allow(instance).format_sort_order_params!
|
123
|
-
dont_allow(instance).format_filter_params!
|
124
|
-
end
|
125
|
-
|
126
|
-
subject.call
|
127
|
-
end
|
128
|
-
end
|
129
153
|
end
|
130
154
|
|
131
155
|
describe '#formatting' do
|
@@ -239,7 +263,7 @@ module Brainstem
|
|
239
263
|
'in' => 'query',
|
240
264
|
'name' => 'sprocket_ids',
|
241
265
|
'type' => 'array',
|
242
|
-
'items' => { 'type' => 'integer' }
|
266
|
+
'items' => { 'type' => 'integer', 'format' => 'int32' }
|
243
267
|
},
|
244
268
|
{
|
245
269
|
'in' => 'query',
|
@@ -274,7 +298,7 @@ module Brainstem
|
|
274
298
|
type: 'hash',
|
275
299
|
info: 'attributes for the task '
|
276
300
|
},
|
277
|
-
|
301
|
+
title: {
|
278
302
|
_config: {
|
279
303
|
type: 'string',
|
280
304
|
required: true,
|
@@ -286,7 +310,7 @@ module Brainstem
|
|
286
310
|
type: 'hash',
|
287
311
|
info: 'sub tasks of the task'
|
288
312
|
},
|
289
|
-
|
313
|
+
title: {
|
290
314
|
_config: {
|
291
315
|
type: 'string',
|
292
316
|
required: true
|
@@ -300,7 +324,15 @@ module Brainstem
|
|
300
324
|
},
|
301
325
|
name: {
|
302
326
|
_config: {
|
303
|
-
type: 'string'
|
327
|
+
type: 'string',
|
328
|
+
required: true,
|
329
|
+
}
|
330
|
+
},
|
331
|
+
_dynamic_key: {
|
332
|
+
_config: {
|
333
|
+
type: 'boolean',
|
334
|
+
info: 'something',
|
335
|
+
dynamic_key: true,
|
304
336
|
}
|
305
337
|
},
|
306
338
|
},
|
@@ -324,6 +356,7 @@ module Brainstem
|
|
324
356
|
},
|
325
357
|
id: {
|
326
358
|
_config: {
|
359
|
+
required: true,
|
327
360
|
type: 'integer',
|
328
361
|
info: 'ID of the assignee'
|
329
362
|
}
|
@@ -334,6 +367,15 @@ module Brainstem
|
|
334
367
|
info: 'activates the assignment'
|
335
368
|
}
|
336
369
|
}
|
370
|
+
},
|
371
|
+
_dynamic_key: {
|
372
|
+
_config: {
|
373
|
+
required: true,
|
374
|
+
type: 'array',
|
375
|
+
item_type: 'integer',
|
376
|
+
info: 'IDs of assignees',
|
377
|
+
dynamic_key: true,
|
378
|
+
}
|
337
379
|
}
|
338
380
|
}.with_indifferent_access
|
339
381
|
end
|
@@ -353,38 +395,38 @@ module Brainstem
|
|
353
395
|
'schema' => {
|
354
396
|
'type' => 'object',
|
355
397
|
'properties' => {
|
356
|
-
|
357
398
|
'task' => {
|
358
|
-
'title' => 'task',
|
359
|
-
'type' => 'object',
|
360
399
|
'description' => 'Attributes for the task.',
|
400
|
+
'required' => ['title'],
|
401
|
+
'type' => 'object',
|
361
402
|
'properties' => {
|
362
|
-
'
|
363
|
-
'title' => 'name',
|
403
|
+
'title' => {
|
364
404
|
'description' => 'Name of the task.',
|
365
405
|
'type' => 'string'
|
366
406
|
},
|
367
407
|
'subs' => {
|
368
|
-
'title' => 'subs',
|
369
408
|
'description' => 'Sub tasks of the task.',
|
409
|
+
'required' => ['title'],
|
370
410
|
'type' => 'object',
|
371
411
|
'properties' => {
|
372
|
-
'
|
373
|
-
'
|
374
|
-
'type' => 'string'
|
412
|
+
'title' => {
|
413
|
+
'type' => 'string'
|
375
414
|
}
|
376
415
|
}
|
377
416
|
},
|
378
417
|
'checklist' => {
|
379
|
-
'title' => 'checklist',
|
380
418
|
'type' => 'array',
|
381
419
|
'items' => {
|
382
420
|
'type' => 'object',
|
421
|
+
'required' => ['name'],
|
383
422
|
'properties' => {
|
384
423
|
'name' => {
|
385
|
-
'
|
386
|
-
'type' => 'string'
|
424
|
+
'type' => 'string',
|
387
425
|
}
|
426
|
+
},
|
427
|
+
'additionalProperties' => {
|
428
|
+
'type' => 'boolean',
|
429
|
+
'description' => 'Something.',
|
388
430
|
}
|
389
431
|
}
|
390
432
|
}
|
@@ -392,12 +434,10 @@ module Brainstem
|
|
392
434
|
},
|
393
435
|
|
394
436
|
'creator' => {
|
395
|
-
'title' => 'creator',
|
396
437
|
'type' => 'object',
|
397
438
|
'description' => 'Attributes for the creator.',
|
398
439
|
'properties' => {
|
399
440
|
'id' => {
|
400
|
-
'title' => 'id',
|
401
441
|
'description' => 'ID of the creator.',
|
402
442
|
'type' => 'integer',
|
403
443
|
'format' => 'int32'
|
@@ -406,25 +446,31 @@ module Brainstem
|
|
406
446
|
},
|
407
447
|
|
408
448
|
'assignees' => {
|
409
|
-
'title' => 'assignees',
|
410
449
|
'type' => 'array',
|
411
450
|
'description' => 'Attributes for the assignees.',
|
412
451
|
'items' => {
|
413
452
|
'type' => 'object',
|
453
|
+
'required' => ['id'],
|
414
454
|
'properties' => {
|
415
455
|
'id' => {
|
416
|
-
'title' => 'id',
|
417
456
|
'description' => 'ID of the assignee.',
|
418
457
|
'type' => 'integer',
|
419
458
|
'format' => 'int32'
|
420
459
|
},
|
421
460
|
'active' => {
|
422
|
-
'title' => 'active',
|
423
461
|
'description' => 'Activates the assignment.',
|
424
462
|
'type' => 'boolean'
|
425
463
|
},
|
426
464
|
}
|
427
465
|
}
|
466
|
+
},
|
467
|
+
},
|
468
|
+
'additionalProperties' => {
|
469
|
+
'type' => 'array',
|
470
|
+
'description' => 'IDs of assignees.',
|
471
|
+
'items' => {
|
472
|
+
'type' => 'integer',
|
473
|
+
'format' => 'int32',
|
428
474
|
}
|
429
475
|
}
|
430
476
|
},
|
@@ -595,8 +641,8 @@ module Brainstem
|
|
595
641
|
mock(presenter).default_sort_order { 'title:asc' }
|
596
642
|
mock(presenter).valid_sort_orders {
|
597
643
|
{
|
598
|
-
'title' => { info: 'Order by title
|
599
|
-
'sprocket_name' => { info: 'Order by sprocket name
|
644
|
+
'title' => { info: 'Order by title alphabetically', direction: true },
|
645
|
+
'sprocket_name' => { info: 'Order by sprocket name alphabetically', direction: false },
|
600
646
|
}
|
601
647
|
}
|
602
648
|
end
|
@@ -611,7 +657,7 @@ module Brainstem
|
|
611
657
|
'type' => 'string',
|
612
658
|
'default' => 'title:asc',
|
613
659
|
'description' => "Supply `order` with the name of a valid sort field for the endpoint and a direction.\n\n" +
|
614
|
-
"Valid values: `sprocket_name
|
660
|
+
"Valid values: `sprocket_name`, `title:asc`, and `title:desc`."
|
615
661
|
}
|
616
662
|
])
|
617
663
|
end
|
@@ -114,6 +114,7 @@ module Brainstem
|
|
114
114
|
before do
|
115
115
|
stub(presenter).brainstem_keys { ['widgets'] }
|
116
116
|
stub(presenter).target_class { 'Widget' }
|
117
|
+
stub(presenter).valid_associations { {} }
|
117
118
|
end
|
118
119
|
|
119
120
|
it 'returns the structured response for an endpoint' do
|
@@ -154,6 +155,172 @@ module Brainstem
|
|
154
155
|
}
|
155
156
|
})
|
156
157
|
end
|
158
|
+
|
159
|
+
context 'when there are valid associations' do
|
160
|
+
let(:task_stubbed_presenter) { Object.new }
|
161
|
+
let(:user_stubbed_presenter) { Object.new }
|
162
|
+
let(:nodoc) { false }
|
163
|
+
|
164
|
+
before do
|
165
|
+
stub(task_stubbed_presenter).nodoc? { nodoc }
|
166
|
+
stub(task_stubbed_presenter).brainstem_keys { ['tasks'] }
|
167
|
+
stub(user_stubbed_presenter).nodoc? { nodoc }
|
168
|
+
stub(user_stubbed_presenter).brainstem_keys { ['users'] }
|
169
|
+
stub(presenter).find_by_class(Task) { task_stubbed_presenter }
|
170
|
+
stub(presenter).find_by_class(User) { user_stubbed_presenter }
|
171
|
+
end
|
172
|
+
|
173
|
+
context 'when association is pointing to a nodoc class' do
|
174
|
+
let(:nodoc) { true }
|
175
|
+
|
176
|
+
it 'does not generate references for the association' do
|
177
|
+
subject.send(:format_schema_response!)
|
178
|
+
|
179
|
+
expect(subject.output).to eq('200' => {
|
180
|
+
'description' => 'A list of Widgets have been retrieved.',
|
181
|
+
'schema' => {
|
182
|
+
'type' => 'object',
|
183
|
+
'properties' => {
|
184
|
+
'count' => { 'type' => 'integer', 'format' => 'int32' },
|
185
|
+
'meta' => {
|
186
|
+
'type' => 'object',
|
187
|
+
'properties' => {
|
188
|
+
'count' => { 'type' => 'integer', 'format' => 'int32' },
|
189
|
+
'page_count' => { 'type' => 'integer', 'format' => 'int32' },
|
190
|
+
'page_number' => { 'type' => 'integer', 'format' => 'int32' },
|
191
|
+
'page_size' => { 'type' => 'integer', 'format' => 'int32' },
|
192
|
+
}
|
193
|
+
},
|
194
|
+
'results' => {
|
195
|
+
'type' => 'array',
|
196
|
+
'items' => {
|
197
|
+
'type' => 'object',
|
198
|
+
'properties' => {
|
199
|
+
'key' => { 'type' => 'string' },
|
200
|
+
'id' => { 'type' => 'string' }
|
201
|
+
}
|
202
|
+
}
|
203
|
+
},
|
204
|
+
'widgets' => {
|
205
|
+
'type' => 'object',
|
206
|
+
'additionalProperties' => {
|
207
|
+
'$ref' => '#/definitions/Widget'
|
208
|
+
}
|
209
|
+
}
|
210
|
+
}
|
211
|
+
}
|
212
|
+
})
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'when the association is polymorphic' do
|
217
|
+
before do
|
218
|
+
stub(presenter).valid_associations { { 'tasks' => ::Brainstem::DSL::Association.new('polymorphic', :polymorphic, polymorphic_classes: [Task, User]) } }
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'returns the appropriate associations with their additional properties' do
|
222
|
+
subject.send(:format_schema_response!)
|
223
|
+
|
224
|
+
expect(subject.output).to eq('200' => {
|
225
|
+
'description' => 'A list of Widgets have been retrieved.',
|
226
|
+
'schema' => {
|
227
|
+
'type' => 'object',
|
228
|
+
'properties' => {
|
229
|
+
'count' => { 'type' => 'integer', 'format' => 'int32' },
|
230
|
+
'meta' => {
|
231
|
+
'type' => 'object',
|
232
|
+
'properties' => {
|
233
|
+
'count' => { 'type' => 'integer', 'format' => 'int32' },
|
234
|
+
'page_count' => { 'type' => 'integer', 'format' => 'int32' },
|
235
|
+
'page_number' => { 'type' => 'integer', 'format' => 'int32' },
|
236
|
+
'page_size' => { 'type' => 'integer', 'format' => 'int32' },
|
237
|
+
}
|
238
|
+
},
|
239
|
+
'results' => {
|
240
|
+
'type' => 'array',
|
241
|
+
'items' => {
|
242
|
+
'type' => 'object',
|
243
|
+
'properties' => {
|
244
|
+
'key' => { 'type' => 'string' },
|
245
|
+
'id' => { 'type' => 'string' }
|
246
|
+
}
|
247
|
+
}
|
248
|
+
},
|
249
|
+
'widgets' => {
|
250
|
+
'type' => 'object',
|
251
|
+
'additionalProperties' => {
|
252
|
+
'$ref' => '#/definitions/Widget'
|
253
|
+
}
|
254
|
+
},
|
255
|
+
'tasks' => {
|
256
|
+
'type' => 'object',
|
257
|
+
'additionalProperties' => {
|
258
|
+
'$ref' => '#/definitions/Task'
|
259
|
+
}
|
260
|
+
},
|
261
|
+
'users' => {
|
262
|
+
'type' => 'object',
|
263
|
+
'additionalProperties' => {
|
264
|
+
'$ref' => '#/definitions/User'
|
265
|
+
}
|
266
|
+
}
|
267
|
+
}
|
268
|
+
}
|
269
|
+
})
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
context 'when the association is not polymorphic' do
|
274
|
+
before do
|
275
|
+
stub(presenter).valid_associations { { 'tasks' => ::Brainstem::DSL::Association.new('Task', Task, {}) } }
|
276
|
+
end
|
277
|
+
|
278
|
+
it 'returns the appropriate association with its additional properties' do
|
279
|
+
subject.send(:format_schema_response!)
|
280
|
+
|
281
|
+
expect(subject.output).to eq('200' => {
|
282
|
+
'description' => 'A list of Widgets have been retrieved.',
|
283
|
+
'schema' => {
|
284
|
+
'type' => 'object',
|
285
|
+
'properties' => {
|
286
|
+
'count' => { 'type' => 'integer', 'format' => 'int32' },
|
287
|
+
'meta' => {
|
288
|
+
'type' => 'object',
|
289
|
+
'properties' => {
|
290
|
+
'count' => { 'type' => 'integer', 'format' => 'int32' },
|
291
|
+
'page_count' => { 'type' => 'integer', 'format' => 'int32' },
|
292
|
+
'page_number' => { 'type' => 'integer', 'format' => 'int32' },
|
293
|
+
'page_size' => { 'type' => 'integer', 'format' => 'int32' },
|
294
|
+
}
|
295
|
+
},
|
296
|
+
'results' => {
|
297
|
+
'type' => 'array',
|
298
|
+
'items' => {
|
299
|
+
'type' => 'object',
|
300
|
+
'properties' => {
|
301
|
+
'key' => { 'type' => 'string' },
|
302
|
+
'id' => { 'type' => 'string' }
|
303
|
+
}
|
304
|
+
}
|
305
|
+
},
|
306
|
+
'widgets' => {
|
307
|
+
'type' => 'object',
|
308
|
+
'additionalProperties' => {
|
309
|
+
'$ref' => '#/definitions/Widget'
|
310
|
+
}
|
311
|
+
},
|
312
|
+
'tasks' => {
|
313
|
+
'type' => 'object',
|
314
|
+
'additionalProperties' => {
|
315
|
+
'$ref' => '#/definitions/Task'
|
316
|
+
}
|
317
|
+
}
|
318
|
+
}
|
319
|
+
}
|
320
|
+
})
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
157
324
|
end
|
158
325
|
|
159
326
|
describe '#format_error_responses!' do
|
@@ -197,7 +364,25 @@ module Brainstem
|
|
197
364
|
'info' => 'Can edit the widget.',
|
198
365
|
'nodoc' => false
|
199
366
|
},
|
200
|
-
}
|
367
|
+
},
|
368
|
+
'_dynamic_key' => {
|
369
|
+
'_config' => {
|
370
|
+
'type' => 'array',
|
371
|
+
'item_type' => 'integer',
|
372
|
+
'info' => 'Viewable Widget Ids.',
|
373
|
+
'nodoc' => false,
|
374
|
+
'dynamic_key' => true
|
375
|
+
},
|
376
|
+
},
|
377
|
+
},
|
378
|
+
'_dynamic_key' => {
|
379
|
+
'_config' => {
|
380
|
+
'type' => 'array',
|
381
|
+
'item_type' => 'integer',
|
382
|
+
'info' => 'Association Ids.',
|
383
|
+
'nodoc' => false,
|
384
|
+
'dynamic_key' => true
|
385
|
+
},
|
201
386
|
},
|
202
387
|
}.with_indifferent_access
|
203
388
|
}
|
@@ -223,49 +408,93 @@ module Brainstem
|
|
223
408
|
'type' => 'boolean',
|
224
409
|
'description' => 'Can edit the widget.'
|
225
410
|
}
|
226
|
-
}
|
411
|
+
},
|
412
|
+
'additionalProperties' => {
|
413
|
+
'type' => 'array',
|
414
|
+
'description' => 'Viewable Widget Ids.',
|
415
|
+
'items' => {
|
416
|
+
'type' => 'integer',
|
417
|
+
'format' => 'int32',
|
418
|
+
}
|
419
|
+
},
|
227
420
|
}
|
228
|
-
}
|
421
|
+
},
|
422
|
+
'additionalProperties' => {
|
423
|
+
'type' => 'array',
|
424
|
+
'description' => 'Association Ids.',
|
425
|
+
'items' => {
|
426
|
+
'type' => 'integer',
|
427
|
+
'format' => 'int32',
|
428
|
+
}
|
429
|
+
},
|
229
430
|
}
|
230
431
|
})
|
231
432
|
end
|
232
433
|
end
|
233
434
|
|
234
435
|
context 'when the response is an array' do
|
235
|
-
|
236
|
-
|
237
|
-
{
|
238
|
-
|
239
|
-
'type' => 'array',
|
240
|
-
'item_type' => 'hash',
|
241
|
-
},
|
242
|
-
'widget_name' => {
|
436
|
+
context 'when array of string / number' do
|
437
|
+
before do
|
438
|
+
stub(endpoint).custom_response_configuration_tree {
|
439
|
+
{
|
243
440
|
'_config' => {
|
244
|
-
'type' => '
|
245
|
-
'
|
246
|
-
'nodoc' => false
|
441
|
+
'type' => 'array',
|
442
|
+
'item_type' => 'string',
|
247
443
|
},
|
248
|
-
}
|
249
|
-
|
444
|
+
}.with_indifferent_access
|
445
|
+
}
|
446
|
+
end
|
447
|
+
|
448
|
+
it 'returns the response structure' do
|
449
|
+
subject.send(:format_custom_response!)
|
450
|
+
|
451
|
+
expect(subject.output).to eq('200' => {
|
452
|
+
'description' => 'A list of Widgets have been retrieved.',
|
453
|
+
'schema' => {
|
454
|
+
'type' => 'array',
|
455
|
+
'items' => {
|
456
|
+
'type' => 'string',
|
457
|
+
}
|
458
|
+
}
|
459
|
+
})
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
context 'when array of hashes' do
|
464
|
+
before do
|
465
|
+
stub(endpoint).custom_response_configuration_tree {
|
466
|
+
{
|
250
467
|
'_config' => {
|
251
468
|
'type' => 'array',
|
252
469
|
'item_type' => 'hash',
|
253
|
-
'info' => 'The permissions of the widget.',
|
254
|
-
'nodoc' => false
|
255
470
|
},
|
256
|
-
'
|
471
|
+
'widget_name' => {
|
257
472
|
'_config' => {
|
258
|
-
'type' => '
|
259
|
-
'info' => '
|
473
|
+
'type' => 'string',
|
474
|
+
'info' => 'The name of the widget.',
|
260
475
|
'nodoc' => false
|
261
476
|
},
|
262
|
-
}
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
477
|
+
},
|
478
|
+
'widget_permissions' => {
|
479
|
+
'_config' => {
|
480
|
+
'type' => 'array',
|
481
|
+
'item_type' => 'hash',
|
482
|
+
'info' => 'The permissions of the widget.',
|
483
|
+
'nodoc' => false
|
484
|
+
},
|
485
|
+
'can_edit' => {
|
486
|
+
'_config' => {
|
487
|
+
'type' => 'boolean',
|
488
|
+
'info' => 'Can edit the widget.',
|
489
|
+
'nodoc' => false
|
490
|
+
},
|
491
|
+
}
|
492
|
+
},
|
493
|
+
}.with_indifferent_access
|
494
|
+
}
|
495
|
+
end
|
267
496
|
|
268
|
-
|
497
|
+
it 'returns the response structure' do
|
269
498
|
subject.send(:format_custom_response!)
|
270
499
|
|
271
500
|
expect(subject.output).to eq('200' => {
|
@@ -296,16 +525,158 @@ module Brainstem
|
|
296
525
|
}
|
297
526
|
}
|
298
527
|
})
|
528
|
+
end
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
context 'when the response is multi nested array' do
|
533
|
+
before do
|
534
|
+
stub(endpoint).custom_response_configuration_tree { response_config_tree }
|
535
|
+
end
|
536
|
+
|
537
|
+
# response :array, nested_level: 2, item_type: [:hash, :string, :integer] do
|
538
|
+
# nested.field :a, :string
|
539
|
+
# nested.field :b, :integer
|
540
|
+
# end
|
541
|
+
|
542
|
+
# response :array, nested_level: 2, item_type: :hash do
|
543
|
+
# nested.field :a, :string
|
544
|
+
# nested.field :a, :integer
|
545
|
+
# end
|
546
|
+
#
|
547
|
+
# [
|
548
|
+
# [
|
549
|
+
# { widget_name: 'Widget A', can_edit: false },
|
550
|
+
# { widget_name: 'Widget B', can_edit: true },
|
551
|
+
# ]
|
552
|
+
# ]
|
553
|
+
context 'when the leaf array is an array of objects' do
|
554
|
+
let(:response_config_tree) do
|
555
|
+
{
|
556
|
+
'_config' => {
|
557
|
+
'type' => 'array',
|
558
|
+
'nested_levels' => 2,
|
559
|
+
'item_type' => 'hash',
|
560
|
+
},
|
561
|
+
'widget_name' => {
|
562
|
+
'_config' => {
|
563
|
+
'type' => 'string',
|
564
|
+
'info' => 'The name of the widget.',
|
565
|
+
'nodoc' => false
|
566
|
+
},
|
567
|
+
},
|
568
|
+
'widget_permissions' => {
|
569
|
+
'_config' => {
|
570
|
+
'type' => 'array',
|
571
|
+
'item_type' => 'hash',
|
572
|
+
'info' => 'The permissions of the widget.',
|
573
|
+
'nodoc' => false
|
574
|
+
},
|
575
|
+
'can_edit' => {
|
576
|
+
'_config' => {
|
577
|
+
'type' => 'array',
|
578
|
+
'nested_levels' => 3,
|
579
|
+
'item_type' => 'boolean',
|
580
|
+
'nodoc' => false
|
581
|
+
},
|
582
|
+
}
|
583
|
+
},
|
584
|
+
}.with_indifferent_access
|
585
|
+
end
|
586
|
+
|
587
|
+
it 'returns the response structure' do
|
588
|
+
subject.send(:format_custom_response!)
|
589
|
+
|
590
|
+
expect(subject.output).to eq('200' => {
|
591
|
+
'description' => 'A list of Widgets have been retrieved.',
|
592
|
+
'schema' => {
|
593
|
+
'type' => 'array',
|
594
|
+
'items' => {
|
595
|
+
'type' => 'array',
|
596
|
+
'items' => {
|
597
|
+
'type' => 'object',
|
598
|
+
'properties' => {
|
599
|
+
'widget_name' => {
|
600
|
+
'type' => 'string',
|
601
|
+
'description' => 'The name of the widget.'
|
602
|
+
},
|
603
|
+
'widget_permissions' => {
|
604
|
+
'type' => 'array',
|
605
|
+
'description' => 'The permissions of the widget.',
|
606
|
+
'items' => {
|
607
|
+
'type' => 'object',
|
608
|
+
'properties' => {
|
609
|
+
'can_edit' => {
|
610
|
+
'type' => 'array',
|
611
|
+
'items' => {
|
612
|
+
'type' => 'array',
|
613
|
+
'items' => {
|
614
|
+
'type' => 'array',
|
615
|
+
'items' => {
|
616
|
+
'type' => 'boolean'
|
617
|
+
}
|
618
|
+
}
|
619
|
+
}
|
620
|
+
}
|
621
|
+
}
|
622
|
+
}
|
623
|
+
}
|
624
|
+
}
|
625
|
+
}
|
626
|
+
}
|
627
|
+
}
|
628
|
+
})
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
# response :array, nested_level: 3, item_type: :string
|
633
|
+
#
|
634
|
+
# [
|
635
|
+
# [
|
636
|
+
# [1,3,4,5],
|
637
|
+
# [6,7,8,9],
|
638
|
+
# ]
|
639
|
+
# ]
|
640
|
+
context 'when the leaf array is an array of strings' do
|
641
|
+
let(:response_config_tree) do
|
642
|
+
{
|
643
|
+
'_config' => {
|
644
|
+
'type' => 'array',
|
645
|
+
'nested_levels' => 3,
|
646
|
+
'item_type' => 'string',
|
647
|
+
},
|
648
|
+
}.with_indifferent_access
|
649
|
+
end
|
650
|
+
|
651
|
+
it 'returns the response structure' do
|
652
|
+
subject.send(:format_custom_response!)
|
653
|
+
|
654
|
+
expect(subject.output).to eq('200' => {
|
655
|
+
'description' => 'A list of Widgets have been retrieved.',
|
656
|
+
'schema' => {
|
657
|
+
'type' => 'array',
|
658
|
+
'items' => {
|
659
|
+
'type' => 'array',
|
660
|
+
'items' => {
|
661
|
+
'type' => 'array',
|
662
|
+
'items' => {
|
663
|
+
'type' => 'string',
|
664
|
+
}
|
665
|
+
}
|
666
|
+
}
|
667
|
+
}
|
668
|
+
})
|
669
|
+
end
|
299
670
|
end
|
300
671
|
end
|
301
672
|
|
302
|
-
context 'when the response is not a hash or array' do
|
673
|
+
context 'when the response is not a hash or an array' do
|
303
674
|
before do
|
304
675
|
stub(endpoint).custom_response_configuration_tree {
|
305
676
|
{
|
306
677
|
'_config' => {
|
307
|
-
'type' => '
|
308
|
-
'info' => '
|
678
|
+
'type' => 'integer',
|
679
|
+
'info' => 'Indicates the number of widgets',
|
309
680
|
'nodoc' => false
|
310
681
|
},
|
311
682
|
}.with_indifferent_access
|
@@ -318,8 +689,9 @@ module Brainstem
|
|
318
689
|
expect(subject.output).to eq('200' => {
|
319
690
|
'description' => 'A list of Widgets have been retrieved.',
|
320
691
|
'schema' => {
|
321
|
-
'type' => '
|
322
|
-
'
|
692
|
+
'type' => 'integer',
|
693
|
+
'format' => 'int32',
|
694
|
+
'description' => 'Indicates the number of widgets.'
|
323
695
|
}
|
324
696
|
})
|
325
697
|
end
|