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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +147 -0
  3. data/Gemfile.lock +68 -39
  4. data/lib/brainstem/api_docs.rb +9 -4
  5. data/lib/brainstem/api_docs/atlas.rb +3 -3
  6. data/lib/brainstem/api_docs/controller.rb +12 -4
  7. data/lib/brainstem/api_docs/controller_collection.rb +11 -2
  8. data/lib/brainstem/api_docs/endpoint.rb +17 -7
  9. data/lib/brainstem/api_docs/endpoint_collection.rb +9 -1
  10. data/lib/brainstem/api_docs/formatters/open_api_specification/helper.rb +19 -16
  11. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter.rb +52 -80
  12. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_definitions_formatter.rb +64 -84
  13. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint_formatter.rb +1 -1
  14. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/endpoint_param_formatter.rb +39 -0
  15. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter.rb +147 -0
  16. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/response_field_formatter.rb +146 -0
  17. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter.rb +53 -55
  18. data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/tags_formatter.rb +1 -1
  19. data/lib/brainstem/api_docs/presenter.rb +16 -8
  20. data/lib/brainstem/api_docs/presenter_collection.rb +8 -5
  21. data/lib/brainstem/api_docs/sinks/open_api_specification_sink.rb +3 -1
  22. data/lib/brainstem/cli/generate_api_docs_command.rb +4 -0
  23. data/lib/brainstem/concerns/controller_dsl.rb +90 -20
  24. data/lib/brainstem/concerns/presenter_dsl.rb +16 -8
  25. data/lib/brainstem/dsl/association.rb +12 -0
  26. data/lib/brainstem/dsl/fields_block.rb +1 -1
  27. data/lib/brainstem/version.rb +1 -1
  28. data/spec/brainstem/api_docs/controller_spec.rb +127 -5
  29. data/spec/brainstem/api_docs/endpoint_spec.rb +489 -57
  30. data/spec/brainstem/api_docs/formatters/open_api_specification/helper_spec.rb +15 -4
  31. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter_spec.rb +112 -66
  32. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_definitions_formatter_spec.rb +404 -32
  33. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/endpoint_param_formatter_spec.rb +335 -0
  34. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter_spec.rb +237 -0
  35. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/response_field_formatter_spec.rb +413 -0
  36. data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter_spec.rb +116 -4
  37. data/spec/brainstem/api_docs/presenter_spec.rb +406 -24
  38. data/spec/brainstem/cli/generate_api_docs_command_spec.rb +8 -0
  39. data/spec/brainstem/concerns/controller_dsl_spec.rb +606 -45
  40. data/spec/brainstem/concerns/presenter_dsl_spec.rb +34 -2
  41. data/spec/brainstem/dsl/association_spec.rb +54 -3
  42. metadata +11 -2
@@ -0,0 +1,335 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/endpoint_param_formatter'
3
+
4
+ module Brainstem
5
+ module ApiDocs
6
+ module Formatters
7
+ module OpenApiSpecification
8
+ module Version2
9
+ module FieldDefinitions
10
+ describe EndpointParamFormatter do
11
+ describe '#format' do
12
+ let(:endpoint) { OpenStruct.new(controller_name: 'Test', action: 'create') }
13
+ let(:field_name) { 'sprocket' }
14
+ let(:configuration_tree) { field_configuration_tree.with_indifferent_access }
15
+
16
+ subject { described_class.new(endpoint, field_name, configuration_tree).format }
17
+
18
+ context 'when formatting nested field' do
19
+ context 'when formatting a nested array field with objects' do
20
+ let(:field_configuration_tree) do
21
+ {
22
+ _config: {
23
+ type: 'array',
24
+ nested_levels: 2,
25
+ item_type: 'hash',
26
+ },
27
+ widget_name: {
28
+ _config: {
29
+ required: true,
30
+ type: 'string',
31
+ info: 'the name of the widget',
32
+ nodoc: false
33
+ },
34
+ },
35
+ support_email: {
36
+ _config: {
37
+ required: true,
38
+ type: 'string',
39
+ info: 'contact support',
40
+ nodoc: false
41
+ },
42
+ },
43
+ version: {
44
+ _config: {
45
+ type: 'string',
46
+ info: 'the version of the widget',
47
+ nodoc: false
48
+ },
49
+ },
50
+ widget_permissions: {
51
+ _config: {
52
+ type: 'array',
53
+ item_type: 'hash',
54
+ info: 'the permissions of the widget',
55
+ nodoc: false
56
+ },
57
+ can_edit: {
58
+ _config: {
59
+ required: true,
60
+ type: 'array',
61
+ item_type: 'boolean',
62
+ nodoc: false
63
+ },
64
+ },
65
+ can_delete: {
66
+ _config: {
67
+ type: 'boolean',
68
+ nodoc: false
69
+ },
70
+ },
71
+ can_rename: {
72
+ _config: {
73
+ type: 'boolean',
74
+ info: 'can rename the widget',
75
+ nodoc: false
76
+ },
77
+ },
78
+ },
79
+ }
80
+ end
81
+
82
+ it 'formats a complicated tree with arrays and hashes as children' do
83
+ expect(subject).to eq(
84
+ 'type' => 'array',
85
+ 'items' => {
86
+ 'type' => 'array',
87
+ 'items' => {
88
+ 'type' => 'object',
89
+ 'required' => ['widget_name', 'support_email'],
90
+ 'properties' => {
91
+ 'widget_name' => {
92
+ 'type' => 'string',
93
+ 'description' => 'The name of the widget.',
94
+ },
95
+ 'support_email' => {
96
+ 'type' => 'string',
97
+ 'description' => 'Contact support.',
98
+ },
99
+ 'version' => {
100
+ 'type' => 'string',
101
+ 'description' => 'The version of the widget.',
102
+ },
103
+ 'widget_permissions' => {
104
+ 'type' => 'array',
105
+ 'description' => 'The permissions of the widget.',
106
+ 'items' => {
107
+ 'type' => 'object',
108
+ 'required' => ['can_edit'],
109
+ 'properties' => {
110
+ 'can_edit' => {
111
+ 'type' => 'array',
112
+ 'items' => {
113
+ 'type' => 'boolean',
114
+ }
115
+ },
116
+ 'can_delete' => {
117
+ 'type' => 'boolean',
118
+ },
119
+ 'can_rename' => {
120
+ 'type' => 'boolean',
121
+ 'description' => 'Can rename the widget.',
122
+ },
123
+ },
124
+ },
125
+ },
126
+ },
127
+ }
128
+ }
129
+ )
130
+ end
131
+ end
132
+
133
+ context 'when formatting a hash field' do
134
+ let(:field_configuration_tree) do
135
+ {
136
+ _config: {
137
+ type: 'hash',
138
+ info: 'Details about the widget',
139
+ },
140
+ widget_name: {
141
+ _config: {
142
+ required: true,
143
+ type: 'string',
144
+ info: 'the name of the widget',
145
+ nodoc: false
146
+ },
147
+ },
148
+ widget_permissions: {
149
+ _config: {
150
+ type: 'array',
151
+ item_type: 'string',
152
+ info: 'the permissions of the widget',
153
+ nodoc: false
154
+ },
155
+ },
156
+ }
157
+ end
158
+
159
+ it 'returns the formatted field schema' do
160
+ expect(subject).to eq(
161
+ 'type' => 'object',
162
+ 'description' => 'Details about the widget.',
163
+ 'required' => ['widget_name'],
164
+ 'properties' => {
165
+ 'widget_name' => {
166
+ 'type' => 'string',
167
+ 'description' => 'The name of the widget.',
168
+ },
169
+ 'widget_permissions' => {
170
+ 'type' => 'array',
171
+ 'description' => 'The permissions of the widget.',
172
+ 'items' => {
173
+ 'type' => 'string',
174
+ }
175
+ }
176
+ }
177
+ )
178
+ end
179
+ end
180
+ end
181
+
182
+ context 'when formatting params with dynamic keys' do
183
+ context 'when formatting a hash param' do
184
+ let(:field_configuration_tree) do
185
+ {
186
+ _config: {
187
+ type: 'hash',
188
+ info: 'Dynamic keys hash.',
189
+ },
190
+ _dynamic_key: {
191
+ _config: {
192
+ nodoc: false,
193
+ type: 'hash',
194
+ dynamic_key: true,
195
+ info: 'a dynamic description.'
196
+ },
197
+ blah: {
198
+ _config: {
199
+ nodoc: false,
200
+ type: 'string',
201
+ },
202
+ },
203
+ },
204
+ }
205
+ end
206
+
207
+ it 'returns the formatted field schema' do
208
+ expect(subject).to eq({
209
+ 'type' => 'object',
210
+ 'description' => 'Dynamic keys hash.',
211
+ 'additionalProperties' => {
212
+ 'type' => 'object',
213
+ 'description' => 'A dynamic description.',
214
+ 'properties' => {
215
+ 'blah' => {
216
+ 'type' => 'string',
217
+ },
218
+ },
219
+ },
220
+ })
221
+ end
222
+ end
223
+
224
+ context 'when formatting a nested hash field' do
225
+ let(:field_configuration_tree) do
226
+ {
227
+ _config: {
228
+ type: 'hash',
229
+ info: 'Dynamic keys hash.',
230
+ },
231
+ non_dynamic_key: {
232
+ _config: {
233
+ nodoc: false,
234
+ type: 'hash',
235
+ info: 'A non-dynamic description.'
236
+ },
237
+ non_dynamic_property: {
238
+ _config: {
239
+ nodoc: false,
240
+ type: 'string',
241
+ },
242
+ },
243
+ },
244
+ _dynamic_key: {
245
+ _config: {
246
+ nodoc: false,
247
+ type: 'hash',
248
+ dynamic_key: true,
249
+ required: true,
250
+ info: 'A dynamic description.'
251
+ },
252
+ dynamic_property1: {
253
+ _config: {
254
+ nodoc: false,
255
+ type: 'string',
256
+ required: true,
257
+ },
258
+ },
259
+ _dynamic_key: {
260
+ _config: {
261
+ nodoc: false,
262
+ type: 'hash',
263
+ dynamic_key: true,
264
+ info: 'A 2nd dynamic description.'
265
+ },
266
+ _dynamic_key: {
267
+ _config: {
268
+ nodoc: false,
269
+ type: 'string',
270
+ dynamic_key: true,
271
+ info: 'A dynamic string.',
272
+ required: true,
273
+ },
274
+ },
275
+ dynamic_property2: {
276
+ _config: {
277
+ nodoc: false,
278
+ type: 'string',
279
+ },
280
+ },
281
+ },
282
+ },
283
+ }
284
+ end
285
+
286
+ it 'returns the formatted field schema' do
287
+ expect(subject).to eq({
288
+ 'type' => 'object',
289
+ 'description' => 'Dynamic keys hash.',
290
+ 'properties' => {
291
+ 'non_dynamic_key' => {
292
+ 'type' => 'object',
293
+ 'description' => 'A non-dynamic description.',
294
+ 'properties' => {
295
+ 'non_dynamic_property' => {
296
+ 'type' => 'string',
297
+ }
298
+ }
299
+ },
300
+ },
301
+ 'additionalProperties' => {
302
+ 'type' => 'object',
303
+ 'description' => 'A dynamic description.',
304
+ 'required' => ['dynamic_property1'],
305
+ 'properties' => {
306
+ 'dynamic_property1' => {
307
+ 'type' => 'string',
308
+ },
309
+ },
310
+ 'additionalProperties' => {
311
+ 'type' => 'object',
312
+ 'description' => 'A 2nd dynamic description.',
313
+ 'properties' => {
314
+ 'dynamic_property2' => {
315
+ 'type' => 'string',
316
+ },
317
+ },
318
+ 'additionalProperties' => {
319
+ 'type' => 'string',
320
+ 'description' => 'A dynamic string.',
321
+ },
322
+ },
323
+ },
324
+ })
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end
330
+ end
331
+ end
332
+ end
333
+ end
334
+ end
335
+ end
@@ -0,0 +1,237 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/presenter'
3
+ require 'brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter'
4
+
5
+ module Brainstem
6
+ module ApiDocs
7
+ module Formatters
8
+ module OpenApiSpecification
9
+ module Version2
10
+ module FieldDefinitions
11
+ describe PresenterFieldFormatter do
12
+ describe '#format' do
13
+ let(:presenter_class) do
14
+ Class.new(Brainstem::Presenter) do
15
+ presents Workspace
16
+ end
17
+ end
18
+ let(:presenter) { Presenter.new(Object.new, const: presenter_class, target_class: 'Workspace') }
19
+ let(:conditionals) { {} }
20
+ let(:field) { presenter.valid_fields.values.first }
21
+
22
+
23
+ subject { described_class.new(presenter, field).format }
24
+
25
+ before do
26
+ stub(presenter).conditionals { conditionals }
27
+ end
28
+
29
+ context 'when formatting non-nested field' do
30
+ before do
31
+ presenter_class.fields do
32
+ field :sprocket_name, :string, via: :name, info: 'The name of the sprocket'
33
+ end
34
+ end
35
+
36
+ it 'returns the formatted field schema' do
37
+ expect(subject).to eq(
38
+ 'type' => 'string',
39
+ 'description' => 'The name of the sprocket.',
40
+ )
41
+ end
42
+ end
43
+
44
+ context 'when formatting nested field' do
45
+ context 'when formatting an array field' do
46
+ before do
47
+ presenter_class.fields do
48
+ field :sprocket_names, :array, info: 'All the names for the sprocket'
49
+ end
50
+ end
51
+
52
+ it 'returns the formatted field schema' do
53
+ expect(subject).to eq(
54
+ 'type' => 'array',
55
+ 'description' => 'All the names for the sprocket.',
56
+ 'items' => {
57
+ 'type' => 'string',
58
+ }
59
+ )
60
+ end
61
+ end
62
+
63
+ context 'when formatting a nested array field with non nested data type' do
64
+ before do
65
+ presenter_class.fields do
66
+ field :sprocket_usages, :array,
67
+ item_type: :decimal,
68
+ nested_levels: 3,
69
+ info: 'All the names for the sprocket'
70
+ end
71
+ end
72
+
73
+ it 'returns the formatted field schema' do
74
+ expect(subject).to eq(
75
+ 'type' => 'array',
76
+ 'description' => 'All the names for the sprocket.',
77
+ 'items' => {
78
+ 'type' => 'array',
79
+ 'items' => {
80
+ 'type' => 'array',
81
+ 'items' => {
82
+ 'type' => 'number',
83
+ 'format' => 'float'
84
+ }
85
+ }
86
+ }
87
+ )
88
+ end
89
+ end
90
+
91
+ context 'when formatting a nested array field with objects' do
92
+ before do
93
+ presenter_class.fields do
94
+ fields :sprockets, :array, nested_levels: 2, info: 'I am a sprocket' do
95
+ field :widget_name, :string,
96
+ info: 'the name of the widget'
97
+
98
+ fields :widget_permissions, :array, info: 'the permissions of the widget' do
99
+ field :can_edit, :array,
100
+ nested_levels: 3,
101
+ item_type: 'boolean',
102
+ info: 'the ethos of the widget'
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ it 'formats a complicated tree with arrays and hashes as children' do
109
+ expect(subject).to eq(
110
+ 'type' => 'array',
111
+ 'description' => 'I am a sprocket.',
112
+ 'items' => {
113
+ 'type' => 'array',
114
+ 'items' => {
115
+ 'type' => 'object',
116
+ 'properties' => {
117
+ 'widget_name' => {
118
+ 'type' => 'string',
119
+ 'description' => 'The name of the widget.',
120
+ },
121
+ 'widget_permissions' => {
122
+ 'type' => 'array',
123
+ 'description' => 'The permissions of the widget.',
124
+ 'items' => {
125
+ 'type' => 'object',
126
+ 'properties' => {
127
+ 'can_edit' => {
128
+ 'type' => 'array',
129
+ 'description' => 'The ethos of the widget.',
130
+ 'items' => {
131
+ 'type' => 'array',
132
+ 'items' => {
133
+ 'type' => 'array',
134
+ 'items' => {
135
+ 'type' => 'boolean'
136
+ }
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ )
147
+ end
148
+ end
149
+
150
+ context 'when formatting a hash field' do
151
+ before do
152
+ presenter_class.fields do
153
+ fields :sprockets, :hash, info: 'Details about the widget' do
154
+ field :widget_name, :string,
155
+ info: 'the name of the widget'
156
+
157
+ fields :widget_permissions, :array, info: 'the permissions of the widget' do
158
+ field :can_edit, :boolean,
159
+ info: 'can edit the widget'
160
+ end
161
+ end
162
+ end
163
+ end
164
+
165
+ it 'returns the formatted field schema' do
166
+ expect(subject).to eq(
167
+ 'type' => 'object',
168
+ 'description' => 'Details about the widget.',
169
+ 'properties' => {
170
+ 'widget_name' => {
171
+ 'type' => 'string',
172
+ 'description' => 'The name of the widget.',
173
+ },
174
+ 'widget_permissions' => {
175
+ 'type' => 'array',
176
+ 'description' => 'The permissions of the widget.',
177
+ 'items' => {
178
+ 'type' => 'object',
179
+ 'properties' => {
180
+ 'can_edit' => {
181
+ 'type' => 'boolean',
182
+ 'description' => 'Can edit the widget.'
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+ )
189
+ end
190
+ end
191
+
192
+ context 'when formatting a multi nested hash field' do
193
+ before do
194
+ presenter_class.fields do
195
+ fields :sprockets do
196
+ field :widget_name, :string,
197
+ info: 'the name of the widget'
198
+
199
+ fields :widget_permissions, :hash, info: 'the permissions of the widget' do
200
+ field :can_edit, :boolean,
201
+ info: 'can edit the widget'
202
+ end
203
+ end
204
+ end
205
+ end
206
+
207
+ it 'returns the formatted field schema' do
208
+ expect(subject).to eq(
209
+ 'type' => 'object',
210
+ 'properties' => {
211
+ 'widget_name' => {
212
+ 'type' => 'string',
213
+ 'description' => 'The name of the widget.',
214
+ },
215
+ 'widget_permissions' => {
216
+ 'type' => 'object',
217
+ 'description' => 'The permissions of the widget.',
218
+ 'properties' => {
219
+ 'can_edit' => {
220
+ 'type' => 'boolean',
221
+ 'description' => 'Can edit the widget.',
222
+ }
223
+ }
224
+ }
225
+ }
226
+ )
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end