brainstem 1.4.1 → 2.0.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.
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,89 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/formatters/open_api_specification/version_2/info_formatter'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ module Formatters
7
+ module OpenApiSpecification
8
+ module Version2
9
+ describe InfoFormatter do
10
+ let(:version) { 2 }
11
+
12
+ subject { described_class.new(version: version) }
13
+
14
+ describe 'call' do
15
+ before do
16
+ mock.proxy(subject).format_swagger_object!
17
+ mock.proxy(subject).format_info_object!
18
+ end
19
+
20
+ it 'includes the correct top level attributes' do
21
+ output = subject.call
22
+
23
+ expect(output.keys).to match_array(%w(swagger host basePath schemes consumes produces info))
24
+ end
25
+ end
26
+
27
+ describe 'format_swagger_object!' do
28
+ before do
29
+ mock.proxy(subject).host
30
+ mock.proxy(subject).schemes
31
+ mock.proxy(subject).consumes
32
+ mock.proxy(subject).produces
33
+ end
34
+
35
+ it 'includes the correct attributes' do
36
+ swagger_object = subject.send(:format_swagger_object!)
37
+
38
+ expect(swagger_object.keys).to match_array(%w(swagger host basePath schemes consumes produces))
39
+ end
40
+ end
41
+
42
+ describe 'format_info_object!' do
43
+ before do
44
+ mock.proxy(subject).version
45
+ mock.proxy(subject).title
46
+ mock.proxy(subject).description
47
+ mock.proxy(subject).terms_of_service
48
+ mock.proxy(subject).contact_object
49
+ mock.proxy(subject).license_object
50
+ end
51
+
52
+ it 'includes the top level info key' do
53
+ output = subject.send(:format_info_object!)
54
+
55
+ expect(output.keys).to eq(%w(info))
56
+ end
57
+
58
+ it 'includes the correct attributes' do
59
+ info_object = subject.send(:format_info_object!)['info']
60
+
61
+ expect(info_object.keys).to match_array(%w(version title description termsOfService contact license))
62
+ end
63
+
64
+ context 'when version is specified' do
65
+ let(:version) { 2 }
66
+
67
+ it 'sets the version attribute in the info object to the given value' do
68
+ info_object = subject.send(:format_info_object!)['info']
69
+
70
+ expect(info_object['version']).to eq(version)
71
+ end
72
+ end
73
+
74
+ context 'when version is not specified' do
75
+ let(:version) { nil }
76
+
77
+ it 'defaults the version attribute in the info object to "1.0"' do
78
+ info_object = subject.send(:format_info_object!)['info']
79
+
80
+ expect(info_object['version']).to eq('1.0')
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,430 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter'
3
+ require 'brainstem/api_docs/presenter'
4
+
5
+ module Brainstem
6
+ module ApiDocs
7
+ module Formatters
8
+ module OpenApiSpecification
9
+ module Version2
10
+ describe PresenterFormatter do
11
+ let(:presenter_args) { {} }
12
+ let(:extra_presenter_args) { {} }
13
+ let(:presenter) { Presenter.new(presenter_args.merge(extra_presenter_args)) }
14
+ let(:nodoc) { false }
15
+ let(:title) { 'Sprocket' }
16
+ let(:presented_class) { 'Sprocket' }
17
+ let(:lorem) { 'Best description ever' }
18
+ let(:valid_fields) { { title: OpenStruct.new(name: :title, type: :string, options: {}) } }
19
+ let(:fake_formatted_fields) { { "title" => {"type" => "string"}}}
20
+
21
+ subject { described_class.new(presenter) }
22
+
23
+ before do
24
+ stub(presenter).nodoc? { nodoc }
25
+ stub(presenter).target_class { presented_class }
26
+ stub(presenter).description { lorem }
27
+ stub(presenter).title { title }
28
+ stub(presenter).contextual_documentation(:title) { title }
29
+ end
30
+
31
+ describe '#call' do
32
+ before do
33
+ stub(presenter).format_title! { title }
34
+ stub(presenter).format_fields! { fake_formatted_fields }
35
+ stub(presenter).valid_fields { valid_fields }
36
+ end
37
+
38
+ context 'when nodoc' do
39
+ let(:nodoc) {true}
40
+
41
+ before do
42
+ any_instance_of(described_class) do |instance|
43
+ dont_allow(instance).format_title!
44
+ dont_allow(instance).format_description!
45
+ dont_allow(instance).format_type!
46
+ dont_allow(instance).format_fields!
47
+ end
48
+ end
49
+
50
+ it 'returns an empty output' do
51
+ expect(subject.call).to be {}
52
+ end
53
+ end
54
+
55
+ context 'when not nodoc' do
56
+ let(:nodoc) {false}
57
+
58
+ before do
59
+ any_instance_of(described_class) do |instance|
60
+ mock.proxy(instance).format_title!
61
+ mock.proxy(instance).format_description!
62
+ mock.proxy(instance).format_type!
63
+ mock.proxy(instance).format_fields!
64
+ end
65
+ end
66
+
67
+ it 'formats data' do
68
+ expect(subject.call).to_not be_empty
69
+ end
70
+
71
+ it 'top level key in the output is set to the presented class' do
72
+ subject.call
73
+
74
+ expect(subject.output.keys).to eq([presented_class])
75
+ expect(subject.output[presented_class].keys).to eq(%w(title description type properties))
76
+ expect(subject.output[presented_class]['title']).to eq(title)
77
+ expect(subject.output[presented_class]['description']).to eq("#{lorem}.")
78
+ expect(subject.output[presented_class]['type']).to eq('object')
79
+ expect(subject.output[presented_class]['properties']).to eq(fake_formatted_fields)
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#format_title!' do
85
+ before do
86
+ stub(presenter).valid_fields { valid_fields }
87
+ end
88
+
89
+ context 'when the presenter has a title' do
90
+ let(:title) { 'Sprockets are cool, I think' }
91
+
92
+ it 'returns the title' do
93
+ subject.send(:format_title!)
94
+
95
+ expect(subject.definition).to have_key(:title)
96
+ expect(subject.definition[:title]).to eq title
97
+ end
98
+ end
99
+
100
+ context 'when the presenter does not have a title' do
101
+ let(:title) { nil }
102
+
103
+ it 'returns formatted key' do
104
+ subject.send(:format_title!)
105
+
106
+ expect(subject.definition).to have_key(:title)
107
+ expect(subject.definition[:title]).to eq presented_class
108
+ end
109
+ end
110
+ end
111
+
112
+ describe '#format_description' do
113
+ context 'when the presenter has a description' do
114
+ let(:lorem) {' way over here '}
115
+
116
+ it 'returns the string-ized stripped description' do
117
+ subject.send(:format_description!)
118
+
119
+ expect(subject.definition).to have_key(:description)
120
+ expect(subject.definition[:description]).to eq 'Way over here.'
121
+ end
122
+ end
123
+
124
+ context 'when the presenter does not have a description' do
125
+ let(:lorem) { nil }
126
+
127
+ it 'returns an empy string' do
128
+ subject.send(:format_description!)
129
+
130
+ expect(subject.definition).to have_key(:description)
131
+ expect(subject.definition[:description]).to eq ''
132
+ end
133
+ end
134
+ end
135
+
136
+ describe '#format_fields!' do
137
+ let(:presenter_class) do
138
+ Class.new(Brainstem::Presenter) do
139
+ presents Workspace
140
+ end
141
+ end
142
+ let(:presenter) { Presenter.new(Object.new, const: presenter_class, target_class: 'Workspace') }
143
+ let(:conditionals) { {} }
144
+
145
+ before do
146
+ stub(presenter).conditionals { conditionals }
147
+ end
148
+
149
+ context 'with fields present' do
150
+ describe 'branch node' do
151
+ context 'with single branch' do
152
+ before do
153
+ presenter_class.fields do
154
+ fields :sprockets do
155
+ field :sprocket_name, :string, via: :name, info: 'Whatever.'
156
+ end
157
+ end
158
+ end
159
+
160
+ it 'outputs the name of sub-branch as a property of the parent' do
161
+ subject.send(:format_fields!)
162
+
163
+ expect(subject.definition).to have_key :properties
164
+ expect(subject.definition[:properties]).to eq({
165
+ 'sprockets' => {
166
+ 'type' => 'object',
167
+ 'properties' => {
168
+
169
+ 'sprocket_name' => { 'type' => 'string', 'description' => 'Whatever.' }
170
+ }
171
+ }
172
+ })
173
+ end
174
+ end
175
+
176
+ context 'with sub-branch of type hash' do
177
+ before do
178
+ presenter_class.fields do
179
+ fields :sprockets do
180
+ fields :sub_sprocket do
181
+ field :sprocket_name, :string, via: :name, info: 'Whatever.'
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ it 'outputs the name of sub-branches as a properties of the its parent' do
188
+ subject.send(:format_fields!)
189
+
190
+ expect(subject.definition).to have_key :properties
191
+ expect(subject.definition[:properties]).to eq({
192
+ 'sprockets' => {
193
+ 'type' => 'object',
194
+ 'properties' => {
195
+
196
+ 'sub_sprocket' => {
197
+ 'type' => 'object',
198
+ 'properties' => {
199
+
200
+ 'sprocket_name' => { 'type' => 'string', 'description' => 'Whatever.' }
201
+ }
202
+ }
203
+ }
204
+ }
205
+ })
206
+ end
207
+ end
208
+
209
+ context 'with sub branch of type array' do
210
+ xcontext 'when type of list items is an array'
211
+
212
+ context 'when type of list items is a hash' do
213
+ before do
214
+ presenter_class.fields do
215
+ fields :sprockets, :array, item_type: 'hash', info: 'parent' do
216
+ field :sprocket_name, :string, via: :name, info: 'Whatever.'
217
+ end
218
+ end
219
+ end
220
+
221
+ it 'outputs the name of sub-branches as a properties of the its parent' do
222
+ subject.send(:format_fields!)
223
+
224
+ expect(subject.definition).to have_key :properties
225
+ expect(subject.definition[:properties]).to eq({
226
+ 'sprockets' => {
227
+ 'type' => 'array',
228
+ 'items' => {
229
+ 'type' => 'object',
230
+ 'properties' => {
231
+
232
+ 'sprocket_name' => { 'type' => 'string', 'description' => 'Whatever.' }
233
+ }
234
+ }
235
+ }
236
+ })
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ describe 'leaf node' do
243
+ context 'if it is not conditional' do
244
+ before do
245
+ presenter_class.fields do
246
+ field :sprocket_name, :string, info: 'whatever'
247
+ field :sprocket_size, :integer
248
+ end
249
+ end
250
+
251
+ it 'outputs each field as a list item' do
252
+ subject.send(:format_fields!)
253
+
254
+ expect(subject.definition).to have_key :properties
255
+ expect(subject.definition[:properties]).to eq({
256
+ 'sprocket_name' => { 'type' => 'string', 'description' => 'Whatever.' },
257
+ 'sprocket_size' => { 'type' => 'integer', 'format' => 'int32' }
258
+ })
259
+ end
260
+
261
+ describe 'optional' do
262
+ let(:formatted_description) { subject.definition[:properties]['sprocket_name']['description'] }
263
+
264
+ context 'when true' do
265
+ before do
266
+ presenter_class.fields do
267
+ field :sprocket_name, :string, info: 'Whatever.', optional: true
268
+ field :sprocket_size, :integer
269
+ end
270
+ end
271
+
272
+ it 'says so' do
273
+ subject.send(:format_fields!)
274
+
275
+ expect(subject.definition).to have_key :properties
276
+ expect(formatted_description).to include 'Only returned when requested'
277
+ end
278
+ end
279
+
280
+ context 'when false' do
281
+ before do
282
+ presenter_class.fields do
283
+ field :sprocket_name, :string, info: 'Whatever.', optional: false
284
+ field :sprocket_size, :integer
285
+ end
286
+ end
287
+
288
+ it 'says nothing' do
289
+ subject.send(:format_fields!)
290
+
291
+ expect(subject.definition).to have_key :properties
292
+ expect(formatted_description).to_not include 'Only returned when requested'
293
+ end
294
+ end
295
+ end
296
+
297
+ describe 'description' do
298
+ context 'when present' do
299
+ before do
300
+ presenter_class.fields do
301
+ field :sprocket_name, :string, info: 'Whatever.'
302
+ field :sprocket_size, :integer
303
+ end
304
+ end
305
+
306
+ it 'outputs the description' do
307
+ subject.send(:format_fields!)
308
+
309
+ expect(subject.definition).to have_key :properties
310
+ expect(subject.definition[:properties]['sprocket_name']['description']).to eq('Whatever.')
311
+ end
312
+ end
313
+
314
+ context 'when absent' do
315
+ before do
316
+ presenter_class.fields do
317
+ field :sprocket_name, :string
318
+ field :sprocket_size, :integer
319
+ end
320
+ end
321
+
322
+ it 'does not include the description' do
323
+ subject.send(:format_fields!)
324
+
325
+ expect(subject.definition).to have_key :properties
326
+ expect(subject.definition[:properties]['sprocket_name']).to_not have_key('description')
327
+ end
328
+ end
329
+ end
330
+ end
331
+
332
+ context 'if it is conditional' do
333
+ let(:formatted_description) {subject.definition[:properties]['sprocket_name']['description']}
334
+
335
+ before do
336
+ presenter_class.fields do
337
+ field :sprocket_name, :string, info: 'whatever', if: [:it_is_a_friday]
338
+ end
339
+ end
340
+
341
+ context 'if nodoc' do
342
+ let(:conditionals) {
343
+ {
344
+ :it_is_a_friday => OpenStruct.new(
345
+ description: nil,
346
+ name: :it_is_a_friday,
347
+ type: :request,
348
+ options: { nodoc: true }
349
+ )
350
+ }
351
+ }
352
+
353
+ it 'does not include the conditional' do
354
+ subject.send(:format_fields!)
355
+
356
+ expect(formatted_description).not_to include 'Visible when'
357
+ end
358
+ end
359
+
360
+ context 'if not nodoc' do
361
+ context 'if the conditional has a description' do
362
+ let(:conditionals) {
363
+ {
364
+ :it_is_a_friday => OpenStruct.new(
365
+ description: 'It is a friday',
366
+ name: :it_is_a_friday,
367
+ type: :request,
368
+ options: {},
369
+ )
370
+ }
371
+ }
372
+
373
+ it 'includes the conditional' do
374
+ subject.send(:format_fields!)
375
+
376
+ expect(formatted_description).to include 'Visible when it is a friday.'
377
+ end
378
+ end
379
+
380
+ context 'if the condition doesn\'t have a description' do
381
+ let(:conditionals) {
382
+ {
383
+ :it_is_a_friday => OpenStruct.new(
384
+ description: nil,
385
+ name: :it_is_a_friday,
386
+ type: :request,
387
+ options: {},
388
+ )
389
+ }
390
+ }
391
+
392
+ it 'does not include the conditional' do
393
+ subject.send(:format_fields!)
394
+
395
+ expect(formatted_description).not_to include 'Visible when'
396
+ end
397
+ end
398
+ end
399
+ end
400
+
401
+ context 'when invalid type specified' do
402
+ before do
403
+ presenter_class.fields do
404
+ field :sprocket_name, :invalid
405
+ end
406
+ end
407
+
408
+ it 'raises an error' do
409
+ expect { subject.send(:format_fields!) }.to raise_error(StandardError)
410
+ end
411
+ end
412
+ end
413
+ end
414
+
415
+ context 'with no fields' do
416
+ let(:valid_fields) {{}}
417
+
418
+ it 'outputs that no fields were listed' do
419
+ subject.send(:format_fields!)
420
+
421
+ expect(subject.definition[:properties]).to be_nil
422
+ end
423
+ end
424
+ end
425
+ end
426
+ end
427
+ end
428
+ end
429
+ end
430
+ end