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