brainstem 1.4.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +77 -0
  3. data/README.md +119 -0
  4. data/docs/api_doc_generator.markdown +45 -4
  5. data/docs/brainstem_executable.markdown +1 -1
  6. data/docs/oas_2_docgen.png +0 -0
  7. data/docs/oas_2_docgen_ascii.txt +78 -0
  8. data/lib/brainstem/api_docs.rb +23 -9
  9. data/lib/brainstem/api_docs/abstract_collection.rb +0 -13
  10. data/lib/brainstem/api_docs/atlas.rb +0 -14
  11. data/lib/brainstem/api_docs/builder.rb +0 -14
  12. data/lib/brainstem/api_docs/controller.rb +7 -16
  13. data/lib/brainstem/api_docs/controller_collection.rb +0 -3
  14. data/lib/brainstem/api_docs/endpoint.rb +73 -19
  15. data/lib/brainstem/api_docs/endpoint_collection.rb +0 -7
  16. data/lib/brainstem/api_docs/formatters/abstract_formatter.rb +0 -2
  17. data/lib/brainstem/api_docs/formatters/markdown/controller_formatter.rb +1 -9
  18. data/lib/brainstem/api_docs/formatters/markdown/endpoint_collection_formatter.rb +1 -9
  19. data/lib/brainstem/api_docs/formatters/markdown/endpoint_formatter.rb +39 -24
  20. data/lib/brainstem/api_docs/formatters/markdown/helper.rb +0 -13
  21. data/lib/brainstem/api_docs/formatters/markdown/presenter_formatter.rb +22 -35
  22. data/lib/brainstem/api_docs/formatters/open_api_specification/helper.rb +66 -0
  23. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/controller_formatter.rb +57 -0
  24. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter.rb +311 -0
  25. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_definitions_formatter.rb +197 -0
  26. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint_collection_formatter.rb +60 -0
  27. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint_formatter.rb +162 -0
  28. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/info_formatter.rb +126 -0
  29. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter.rb +132 -0
  30. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/security_definitions_formatter.rb +99 -0
  31. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/tags_formatter.rb +123 -0
  32. data/lib/brainstem/api_docs/introspectors/abstract_introspector.rb +0 -7
  33. data/lib/brainstem/api_docs/introspectors/rails_introspector.rb +1 -20
  34. data/lib/brainstem/api_docs/presenter.rb +21 -27
  35. data/lib/brainstem/api_docs/presenter_collection.rb +1 -11
  36. data/lib/brainstem/api_docs/resolver.rb +1 -8
  37. data/lib/brainstem/api_docs/sinks/abstract_sink.rb +0 -4
  38. data/lib/brainstem/api_docs/sinks/controller_presenter_multifile_sink.rb +0 -9
  39. data/lib/brainstem/api_docs/sinks/open_api_specification_sink.rb +234 -0
  40. data/lib/brainstem/api_docs/sinks/stdout_sink.rb +0 -5
  41. data/lib/brainstem/cli.rb +0 -13
  42. data/lib/brainstem/cli/abstract_command.rb +0 -7
  43. data/lib/brainstem/cli/generate_api_docs_command.rb +48 -24
  44. data/lib/brainstem/concerns/controller_dsl.rb +288 -145
  45. data/lib/brainstem/concerns/formattable.rb +0 -5
  46. data/lib/brainstem/concerns/optional.rb +0 -1
  47. data/lib/brainstem/concerns/presenter_dsl.rb +2 -21
  48. data/lib/brainstem/dsl/configuration.rb +0 -11
  49. data/lib/brainstem/presenter.rb +0 -4
  50. data/lib/brainstem/version.rb +1 -1
  51. data/spec/brainstem/api_docs/abstract_collection_spec.rb +0 -11
  52. data/spec/brainstem/api_docs/atlas_spec.rb +0 -6
  53. data/spec/brainstem/api_docs/builder_spec.rb +0 -4
  54. data/spec/brainstem/api_docs/controller_collection_spec.rb +0 -2
  55. data/spec/brainstem/api_docs/controller_spec.rb +29 -18
  56. data/spec/brainstem/api_docs/endpoint_collection_spec.rb +0 -6
  57. data/spec/brainstem/api_docs/endpoint_spec.rb +343 -13
  58. data/spec/brainstem/api_docs/formatters/abstract_formatter_spec.rb +0 -2
  59. data/spec/brainstem/api_docs/formatters/markdown/controller_formatter_spec.rb +0 -1
  60. data/spec/brainstem/api_docs/formatters/markdown/endpoint_collection_formatter_spec.rb +0 -5
  61. data/spec/brainstem/api_docs/formatters/markdown/endpoint_formatter_spec.rb +94 -8
  62. data/spec/brainstem/api_docs/formatters/markdown/helper_spec.rb +0 -8
  63. data/spec/brainstem/api_docs/formatters/markdown/presenter_formatter_spec.rb +0 -7
  64. data/spec/brainstem/api_docs/formatters/open_api_specification/helper_spec.rb +210 -0
  65. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/controller_formatter_spec.rb +81 -0
  66. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter_spec.rb +672 -0
  67. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_definitions_formatter_spec.rb +335 -0
  68. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint_collection_formatter_spec.rb +59 -0
  69. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint_formatter_spec.rb +308 -0
  70. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/info_formatter_spec.rb +89 -0
  71. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter_spec.rb +430 -0
  72. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/security_definitions_formatter_spec.rb +190 -0
  73. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/tags_formatter_spec.rb +217 -0
  74. data/spec/brainstem/api_docs/introspectors/abstract_introspector_spec.rb +0 -2
  75. data/spec/brainstem/api_docs/introspectors/rails_introspector_spec.rb +0 -2
  76. data/spec/brainstem/api_docs/presenter_collection_spec.rb +0 -2
  77. data/spec/brainstem/api_docs/presenter_spec.rb +58 -18
  78. data/spec/brainstem/api_docs/resolver_spec.rb +0 -1
  79. data/spec/brainstem/api_docs/sinks/controller_presenter_multifile_sink_spec.rb +0 -2
  80. data/spec/brainstem/api_docs/sinks/open_api_specification_sink_spec.rb +371 -0
  81. data/spec/brainstem/api_docs_spec.rb +2 -0
  82. data/spec/brainstem/cli/abstract_command_spec.rb +0 -4
  83. data/spec/brainstem/cli/generate_api_docs_command_spec.rb +53 -2
  84. data/spec/brainstem/concerns/controller_dsl_spec.rb +430 -64
  85. data/spec/brainstem/concerns/presenter_dsl_spec.rb +0 -20
  86. data/spec/brainstem/preloader_spec.rb +0 -7
  87. data/spec/brainstem/presenter_spec.rb +0 -1
  88. data/spec/dummy/rails.rb +0 -1
  89. data/spec/spec_helpers/db.rb +0 -1
  90. metadata +37 -2
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/formatters/open_api_specification/version_2/controller_formatter'
3
+ require 'brainstem/api_docs/controller'
4
+
5
+ module Brainstem
6
+ module ApiDocs
7
+ module Formatters
8
+ module OpenApiSpecification
9
+ module Version2
10
+ describe ControllerFormatter do
11
+ let(:const) { Object.new }
12
+ let(:atlas) { Object.new }
13
+ let(:controller) { Controller.new(atlas, const: const) }
14
+ let(:configuration) { {} }
15
+
16
+ let(:endpoint_1) { Object.new }
17
+ let(:endpoints) { [ endpoint_1 ] }
18
+ let(:nodoc) { false }
19
+ let(:options) { {} }
20
+
21
+ subject { described_class.new(controller, options) }
22
+
23
+ before do
24
+ stub(const).configuration { configuration }
25
+ end
26
+
27
+ describe "#call" do
28
+ let(:configuration) { { _default: { nodoc: nodoc } } }
29
+
30
+ context "when nodoc specified" do
31
+ let(:nodoc) { true }
32
+
33
+ before do
34
+ dont_allow(subject).format_actions!
35
+ end
36
+
37
+ it "returns an empty hash" do
38
+ expect(subject.call).to eq({})
39
+ end
40
+ end
41
+
42
+ context "when nodoc not specified" do
43
+ it "formats actions" do
44
+ mock(subject).format_actions!
45
+
46
+ subject.call
47
+ end
48
+ end
49
+ end
50
+
51
+ describe "formatting" do
52
+ let(:lorem) { "lorem ipsum dolor sit amet" }
53
+ let(:default_config) { {} }
54
+ let(:configuration) { { _default: default_config } }
55
+
56
+ describe "#format_actions!" do
57
+ context "if include actions" do
58
+ let(:options) { { include_actions: true } }
59
+
60
+ it "calls formatted_as with :oas_v2 on the sorted endpoints in the controller" do
61
+ stub(controller).valid_sorted_endpoints.stub!.formatted_as(:oas_v2) { { index: true } }
62
+ subject.send(:format_actions!)
63
+ end
64
+ end
65
+
66
+ context "if not include actions" do
67
+ let(:options) { { include_actions: false } }
68
+
69
+ it "shows nothing" do
70
+ subject.send(:format_actions!)
71
+ expect(subject.output).to eq({})
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,672 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter'
3
+ require 'brainstem/api_docs/endpoint'
4
+
5
+ module Brainstem
6
+ module ApiDocs
7
+ module Formatters
8
+ module OpenApiSpecification
9
+ module Version2
10
+ module Endpoint
11
+ describe ParamDefinitionsFormatter do
12
+ let(:controller) { Object.new }
13
+ let(:presenter) { Object.new }
14
+ let(:atlas) { Object.new }
15
+ let(:action) { 'show' }
16
+ let(:http_methods) { %w(GET) }
17
+ let(:endpoint) {
18
+ ::Brainstem::ApiDocs::Endpoint.new(
19
+ atlas,
20
+ {
21
+ http_methods: http_methods,
22
+ path: '/widgets(.:format)'
23
+ }.merge(endpoint_args)
24
+ )
25
+ }
26
+ let(:endpoint_args) { {} }
27
+ let(:nodoc) { false }
28
+
29
+ subject { described_class.new(endpoint) }
30
+
31
+ before do
32
+ stub(endpoint).presenter { presenter }
33
+ stub(endpoint).action { action }
34
+ end
35
+
36
+ describe '#call' do
37
+ context 'when request type is get' do
38
+ let(:http_methods) { %w(GET) }
39
+
40
+ context 'when action is index' do
41
+ let(:action) { 'index' }
42
+
43
+ it 'formats path, shared, query and body params for the endpoint' do
44
+ any_instance_of(described_class) do |instance|
45
+ mock(instance).format_path_params!
46
+ mock(instance).format_optional_params!
47
+ mock(instance).format_include_params!
48
+ mock(instance).format_query_params!
49
+ mock(instance).format_body_params!
50
+ mock(instance).format_pagination_params!
51
+ mock(instance).format_search_param!
52
+ mock(instance).format_only_param!
53
+ mock(instance).format_sort_order_params!
54
+ mock(instance).format_filter_params!
55
+ end
56
+
57
+ subject.call
58
+ end
59
+ end
60
+
61
+ context 'when action is show' do
62
+ let(:action) { 'show' }
63
+
64
+ it 'formats path, optional, query and body params for the endpoint' do
65
+ any_instance_of(described_class) do |instance|
66
+ mock(instance).format_path_params!
67
+ mock(instance).format_optional_params!
68
+ mock(instance).format_include_params!
69
+ mock(instance).format_query_params!
70
+ mock(instance).format_body_params!
71
+
72
+ dont_allow(instance).format_pagination_params!
73
+ dont_allow(instance).format_search_param!
74
+ dont_allow(instance).format_only_param!
75
+ dont_allow(instance).format_sort_order_params!
76
+ dont_allow(instance).format_filter_params!
77
+ end
78
+
79
+ subject.call
80
+ end
81
+ end
82
+ end
83
+
84
+ context 'when request type is `delete`' do
85
+ let(:http_methods) { %w(DELETE) }
86
+ let(:action) { 'destroy' }
87
+
88
+ it 'formats path, query and body param for the endpoint' do
89
+ any_instance_of(described_class) do |instance|
90
+ mock(instance).format_path_params!
91
+ mock(instance).format_query_params!
92
+ mock(instance).format_body_params!
93
+
94
+ dont_allow(instance).format_pagination_params!
95
+ dont_allow(instance).format_search_param!
96
+ dont_allow(instance).format_only_param!
97
+ dont_allow(instance).format_sort_order_params!
98
+ dont_allow(instance).format_optional_params!
99
+ dont_allow(instance).format_include_params!
100
+ dont_allow(instance).format_filter_params!
101
+ end
102
+
103
+ subject.call
104
+ end
105
+ 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
+ end
130
+
131
+ describe '#formatting' do
132
+ describe '#format_path_params' do
133
+ context 'when no path params' do
134
+ let(:endpoint_args) { { path: '/widgets(.:format)' } }
135
+
136
+ it 'does any add path params to the output' do
137
+ subject.send(:format_path_params!)
138
+
139
+ expect(subject.output).to eq([])
140
+ end
141
+ end
142
+
143
+ context 'when single path param is present' do
144
+ let(:endpoint_args) { { path: '/widgets/:id(.:format)' } }
145
+
146
+ it 'adds path params to the output' do
147
+ subject.send(:format_path_params!)
148
+
149
+ expect(subject.output).to eq([
150
+ {
151
+ 'in' => 'path',
152
+ 'name' => 'id',
153
+ 'required' => true,
154
+ 'type' => 'integer',
155
+ 'description' => 'The ID of the Model.'
156
+ }
157
+ ])
158
+ end
159
+ end
160
+
161
+ context 'when multiple path params are present' do
162
+ let(:endpoint_args) { { path: '/sprockets/:sprocket_id/widgets/:id(.:format)' } }
163
+
164
+ it 'adds path params to the output' do
165
+ subject.send(:format_path_params!)
166
+
167
+ expect(subject.output).to eq([
168
+ {
169
+ 'in' => 'path',
170
+ 'name' => 'sprocket_id',
171
+ 'required' => true,
172
+ 'type' => 'integer',
173
+ 'description' => 'The ID of the Sprocket.'
174
+ },
175
+ {
176
+ 'in' => 'path',
177
+ 'name' => 'id',
178
+ 'required' => true,
179
+ 'type' => 'integer',
180
+ 'description' => 'The ID of the Model.'
181
+ }
182
+ ])
183
+ end
184
+ end
185
+ end
186
+
187
+ describe '#format_query_params' do
188
+ let(:mocked_params_configuration_tree) do
189
+ {
190
+ sprocket_name: {
191
+ _config: {
192
+ type: 'string',
193
+ info: 'The name of the sprocket'
194
+ }
195
+ },
196
+ sprocket_ids: {
197
+ _config: {
198
+ type: 'array',
199
+ item_type: 'integer'
200
+ }
201
+ },
202
+ widget: {
203
+ _config: {
204
+ type: 'hash',
205
+ },
206
+ title: {
207
+ _config: {
208
+ type: 'string'
209
+ }
210
+ },
211
+ },
212
+ id: {
213
+ _config: {
214
+ type: 'integer',
215
+ info: 'The ID of the model ',
216
+ required: true
217
+ }
218
+ },
219
+ }.with_indifferent_access
220
+ end
221
+
222
+ before do
223
+ mock(endpoint).params_configuration_tree { mocked_params_configuration_tree }
224
+ end
225
+
226
+ it 'exclusively adds non-nested fields as query params ordered by it\'s required & name attributes' do
227
+ subject.send(:format_query_params!)
228
+
229
+ expect(subject.output).to eq([
230
+ {
231
+ 'in' => 'query',
232
+ 'name' => 'id',
233
+ 'required' => true,
234
+ 'type' => 'integer',
235
+ 'format' => 'int32',
236
+ 'description' => 'The ID of the model.'
237
+ },
238
+ {
239
+ 'in' => 'query',
240
+ 'name' => 'sprocket_ids',
241
+ 'type' => 'array',
242
+ 'items' => { 'type' => 'integer' }
243
+ },
244
+ {
245
+ 'in' => 'query',
246
+ 'name' => 'sprocket_name',
247
+ 'type' => 'string',
248
+ 'description' => 'The name of the sprocket.'
249
+ }
250
+ ])
251
+ end
252
+
253
+ context 'when type of the param is unknown' do
254
+ before do
255
+ mocked_params_configuration_tree[:id][:_config][:type] = 'invalid'
256
+ end
257
+
258
+ it 'raises an error' do
259
+ expect { subject.send(:format_query_params!) }.to raise_error(StandardError)
260
+ end
261
+ end
262
+ end
263
+
264
+ describe '#format_body_params' do
265
+ let(:mocked_params_configuration_tree) do
266
+ {
267
+ id: {
268
+ _config: {
269
+ type: 'integer'
270
+ }
271
+ },
272
+ task: {
273
+ _config: {
274
+ type: 'hash',
275
+ info: 'attributes for the task '
276
+ },
277
+ name: {
278
+ _config: {
279
+ type: 'string',
280
+ required: true,
281
+ info: 'name of the task '
282
+ }
283
+ },
284
+ subs: {
285
+ _config: {
286
+ type: 'hash',
287
+ info: 'sub tasks of the task'
288
+ },
289
+ name: {
290
+ _config: {
291
+ type: 'string',
292
+ required: true
293
+ }
294
+ },
295
+ },
296
+ checklist: {
297
+ _config: {
298
+ type: 'array',
299
+ item: 'hash'
300
+ },
301
+ name: {
302
+ _config: {
303
+ type: 'string'
304
+ }
305
+ },
306
+ },
307
+ },
308
+ creator: {
309
+ _config: {
310
+ type: 'hash',
311
+ info: 'attributes for the creator'
312
+ },
313
+ id: {
314
+ _config: {
315
+ type: 'integer',
316
+ info: 'ID of the creator'
317
+ }
318
+ },
319
+ },
320
+ assignees: {
321
+ _config: {
322
+ type: 'array',
323
+ info: 'attributes for the assignees'
324
+ },
325
+ id: {
326
+ _config: {
327
+ type: 'integer',
328
+ info: 'ID of the assignee'
329
+ }
330
+ },
331
+ active: {
332
+ _config: {
333
+ type: 'boolean',
334
+ info: 'activates the assignment'
335
+ }
336
+ }
337
+ }
338
+ }.with_indifferent_access
339
+ end
340
+
341
+ before do
342
+ mock(endpoint).params_configuration_tree { mocked_params_configuration_tree }
343
+ end
344
+
345
+ it 'adds nested fields to the query params' do
346
+ subject.send(:format_body_params!)
347
+
348
+ expect(subject.output).to eq([
349
+ {
350
+ 'in' => 'body',
351
+ 'required' => true,
352
+ 'name' => 'body',
353
+ 'schema' => {
354
+ 'type' => 'object',
355
+ 'properties' => {
356
+
357
+ 'task' => {
358
+ 'title' => 'task',
359
+ 'type' => 'object',
360
+ 'description' => 'Attributes for the task.',
361
+ 'properties' => {
362
+ 'name' => {
363
+ 'title' => 'name',
364
+ 'description' => 'Name of the task.',
365
+ 'type' => 'string'
366
+ },
367
+ 'subs' => {
368
+ 'title' => 'subs',
369
+ 'description' => 'Sub tasks of the task.',
370
+ 'type' => 'object',
371
+ 'properties' => {
372
+ 'name' => {
373
+ 'title' => 'name',
374
+ 'type' => 'string'
375
+ }
376
+ }
377
+ },
378
+ 'checklist' => {
379
+ 'title' => 'checklist',
380
+ 'type' => 'array',
381
+ 'items' => {
382
+ 'type' => 'object',
383
+ 'properties' => {
384
+ 'name' => {
385
+ 'title' => 'name',
386
+ 'type' => 'string'
387
+ }
388
+ }
389
+ }
390
+ }
391
+ }
392
+ },
393
+
394
+ 'creator' => {
395
+ 'title' => 'creator',
396
+ 'type' => 'object',
397
+ 'description' => 'Attributes for the creator.',
398
+ 'properties' => {
399
+ 'id' => {
400
+ 'title' => 'id',
401
+ 'description' => 'ID of the creator.',
402
+ 'type' => 'integer',
403
+ 'format' => 'int32'
404
+ },
405
+ }
406
+ },
407
+
408
+ 'assignees' => {
409
+ 'title' => 'assignees',
410
+ 'type' => 'array',
411
+ 'description' => 'Attributes for the assignees.',
412
+ 'items' => {
413
+ 'type' => 'object',
414
+ 'properties' => {
415
+ 'id' => {
416
+ 'title' => 'id',
417
+ 'description' => 'ID of the assignee.',
418
+ 'type' => 'integer',
419
+ 'format' => 'int32'
420
+ },
421
+ 'active' => {
422
+ 'title' => 'active',
423
+ 'description' => 'Activates the assignment.',
424
+ 'type' => 'boolean'
425
+ },
426
+ }
427
+ }
428
+ }
429
+ }
430
+ },
431
+ }
432
+ ])
433
+ end
434
+
435
+ context 'when type of the param is unknown' do
436
+ before do
437
+ mocked_params_configuration_tree[:task][:_config][:type] = 'invalid'
438
+ end
439
+
440
+ it 'raises an error' do
441
+ expect { subject.send(:format_body_params!) }.to raise_error(StandardError)
442
+ end
443
+ end
444
+
445
+ context 'when no body params are present' do
446
+ let(:mocked_params_configuration_tree) { {} }
447
+
448
+ it 'does not add any output' do
449
+ subject.send(:format_body_params!)
450
+
451
+ expect(subject.output).to be_empty
452
+ end
453
+ end
454
+ end
455
+
456
+ describe '#format_pagination_params!' do
457
+ it 'adds the page & per_page query params' do
458
+ subject.send(:format_pagination_params!)
459
+
460
+ expect(subject.output).to eq([
461
+ {
462
+ 'in' => 'query',
463
+ 'name' => 'page',
464
+ 'type' => 'integer',
465
+ 'format' => 'int32',
466
+ 'default' => 1
467
+ },
468
+ {
469
+ 'in' => 'query',
470
+ 'name' => 'per_page',
471
+ 'type' => 'integer',
472
+ 'format' => 'int32',
473
+ 'default' => 20,
474
+ 'maximum' => 200
475
+ }
476
+ ])
477
+ end
478
+ end
479
+
480
+ describe '#format_search_param!' do
481
+ before do
482
+ mock(presenter).searchable? { searchable }
483
+ end
484
+
485
+ context 'when presenter has search config' do
486
+ let(:searchable) { true }
487
+
488
+ it 'adds the search query params' do
489
+ subject.send(:format_search_param!)
490
+
491
+ expect(subject.output).to eq([
492
+ {
493
+ 'in' => 'query',
494
+ 'name' => 'search',
495
+ 'type' => 'string'
496
+ }
497
+ ])
498
+ end
499
+ end
500
+
501
+ context 'when presenter has no search config' do
502
+ let(:searchable) { false }
503
+
504
+ it 'adds the search query params' do
505
+ subject.send(:format_search_param!)
506
+
507
+ expect(subject.output).to eq([])
508
+ end
509
+ end
510
+ end
511
+
512
+ describe '#format_optional_params!' do
513
+ let(:optional_fields) { ['field_1', 'field_2'] }
514
+
515
+ before do
516
+ mock(presenter).optional_field_names { optional_fields }
517
+ end
518
+
519
+ it 'adds the optional fields query param' do
520
+ subject.send(:format_optional_params!)
521
+
522
+ expect(subject.output).to eq([
523
+ {
524
+ 'in' => 'query',
525
+ 'name' => 'optional_fields',
526
+ 'description' => 'Allows you to request one or more optional fields as an array.',
527
+ 'type' => 'array',
528
+ 'items' => {
529
+ 'type' => 'string',
530
+ 'enum' => optional_fields
531
+ }
532
+ }
533
+ ])
534
+ end
535
+ end
536
+
537
+ describe '#format_include_params!' do
538
+ let(:valid_associations) {
539
+ {
540
+ 'association_1' => OpenStruct.new(
541
+ name: 'association_1',
542
+ target_class: 'association_1_class',
543
+ description: 'Association_1 description.'
544
+ ),
545
+ 'association_2' => OpenStruct.new(
546
+ name: 'association_2',
547
+ target_class: 'association_2_class',
548
+ description: 'Association_2 description.',
549
+ options: { restrict_to_only: true }
550
+ )
551
+ }
552
+ }
553
+
554
+ before do
555
+ stub(presenter).valid_associations { valid_associations }
556
+ end
557
+
558
+ it 'adds the include filter as a query param' do
559
+ subject.send(:format_include_params!)
560
+
561
+ expect(subject.output.length).to eq(1)
562
+
563
+ param_def = subject.output[0]
564
+ expect(param_def.except('description')).to eq(
565
+ 'name' => 'include',
566
+ 'in' => 'query',
567
+ 'type' => 'string',
568
+ )
569
+ expect(param_def['description']).to include('e.g. `include=association1,association2`.')
570
+ expect(param_def['description']).to include('`association_1` (association_1_class) - Association_1 description')
571
+ association_2_desc = 'Association_2 description. Restricted to queries using the `only` parameter.'
572
+ expect(param_def['description']).to include("`association_2` (association_2_class) - #{association_2_desc}")
573
+ end
574
+ end
575
+
576
+ describe '#format_only_param!' do
577
+ it 'adds the only query params' do
578
+ subject.send(:format_only_param!)
579
+
580
+ expect(subject.output).to eq([
581
+ {
582
+ 'in' => 'query',
583
+ 'name' => 'only',
584
+ 'type' => 'string',
585
+ 'description' =>
586
+ "Allows you to request one or more resources directly by ID. Multiple IDs can be supplied\n" +
587
+ "in a comma separated list, like `GET /api/v1/workspaces.json?only=5,6,7`."
588
+ }
589
+ ])
590
+ end
591
+ end
592
+
593
+ describe '#format_sort_order_params!' do
594
+ before do
595
+ mock(presenter).default_sort_order { 'title:asc' }
596
+ mock(presenter).valid_sort_orders {
597
+ {
598
+ 'title' => { info: 'Order by title aphabetically' },
599
+ 'sprocket_name' => { info: 'Order by sprocket name aphabetically' },
600
+ }
601
+ }
602
+ end
603
+
604
+ it 'adds the sort order as query params' do
605
+ subject.send(:format_sort_order_params!)
606
+
607
+ expect(subject.output).to eq([
608
+ {
609
+ 'in' => 'query',
610
+ 'name' => 'order',
611
+ 'type' => 'string',
612
+ 'default' => 'title:asc',
613
+ 'description' => "Supply `order` with the name of a valid sort field for the endpoint and a direction.\n\n" +
614
+ "Valid values: `sprocket_name:asc`, `sprocket_name:desc`, `title:asc`, and `title:desc`."
615
+ }
616
+ ])
617
+ end
618
+ end
619
+
620
+ describe '#format_filters' do
621
+ let(:mocked_valid_filters) {
622
+ {
623
+ filter_1: { type: 'string', info: 'Filter by string' },
624
+ filter_2: { type: 'boolean', default: false },
625
+ filter_3: { type: 'array', item_type: 'string', items: ['Option 1', 'Option 2'], default: 'Option 1' }
626
+ }
627
+ }
628
+
629
+ before do
630
+ mock(presenter).valid_filters { mocked_valid_filters }
631
+ end
632
+
633
+ it 'adds filters to the output as query params' do
634
+ subject.send(:format_filter_params!)
635
+
636
+ expect(subject.output).to eq([
637
+ {
638
+ 'in' => 'query',
639
+ 'name' => 'filter_1',
640
+ 'type' => 'string',
641
+ 'description' => 'Filter by string.'
642
+ },
643
+ {
644
+ 'in' => 'query',
645
+ 'name' => 'filter_2',
646
+ 'type' => 'boolean',
647
+ 'default' => false
648
+ },
649
+ {
650
+ 'in' => 'query',
651
+ 'name' => 'filter_3',
652
+ 'type' => 'array',
653
+ 'items' => {
654
+ 'type' => 'string',
655
+ 'enum' => [
656
+ 'Option 1',
657
+ 'Option 2'
658
+ ],
659
+ 'default' => 'Option 1'
660
+ }
661
+ }
662
+ ])
663
+ end
664
+ end
665
+ end
666
+ end
667
+ end
668
+ end
669
+ end
670
+ end
671
+ end
672
+ end