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,335 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_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 ResponseDefinitionsFormatter 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
+ endpoint_args
21
+ )
22
+ }
23
+ let(:endpoint_args) { {} }
24
+
25
+ subject { described_class.new(endpoint) }
26
+
27
+ before do
28
+ stub(presenter).contextual_documentation(:title) { 'Widget' }
29
+ stub(endpoint).presenter { presenter }
30
+ stub(endpoint).action { action }
31
+ stub(endpoint).custom_response_configuration_tree { {} }
32
+ stub(endpoint).http_methods { http_methods }
33
+ end
34
+
35
+ describe '#call' do
36
+ context 'when delete request' do
37
+ let(:http_methods) { %w(DELETE) }
38
+
39
+ it 'formats the delete response and error response' do
40
+ any_instance_of(described_class) do |instance|
41
+ mock(instance).format_delete_response!
42
+ mock(instance).format_error_responses!
43
+
44
+ dont_allow(instance).format_schema_response!
45
+ end
46
+
47
+ subject.call
48
+ end
49
+ end
50
+
51
+ context 'when request is not delete' do
52
+ let(:http_methods) { %w(GET) }
53
+
54
+ it 'formats the schema response and error response' do
55
+ any_instance_of(described_class) do |instance|
56
+ mock(instance).format_schema_response!
57
+ mock(instance).format_error_responses!
58
+
59
+ dont_allow(instance).format_delete_response!
60
+ end
61
+
62
+ subject.call
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ describe '#formatting' do
69
+ describe '#success_response_description' do
70
+ subject { described_class.new(endpoint).send(:success_response_description) }
71
+
72
+ context 'when `GET` request' do
73
+ let(:http_methods) { %w(GET) }
74
+
75
+ it { is_expected.to eq('A list of Widgets have been retrieved.') }
76
+ end
77
+
78
+ context 'when `POST` request' do
79
+ let(:http_methods) { %w(POST) }
80
+
81
+ it { is_expected.to eq('Widget has been created.') }
82
+ end
83
+
84
+ context 'when `PUT` request' do
85
+ let(:http_methods) { %w(PUT) }
86
+
87
+ it { is_expected.to eq('Widget has been updated.') }
88
+ end
89
+
90
+ context 'when `PATCH` request' do
91
+ let(:http_methods) { %w(PATCH) }
92
+
93
+ it { is_expected.to eq('Widget has been updated.') }
94
+ end
95
+
96
+ context 'when `DELETE` request' do
97
+ let(:http_methods) { %w(DELETE) }
98
+
99
+ it { is_expected.to eq('Widget has been deleted.') }
100
+ end
101
+ end
102
+
103
+ describe '#format_delete_response!' do
104
+ let(:http_methods) { %w(DELETE) }
105
+
106
+ it 'returns the structure response for a destroy action' do
107
+ subject.send(:format_delete_response!)
108
+
109
+ expect(subject.output).to eq('204' => { 'description' => 'Widget has been deleted.' })
110
+ end
111
+ end
112
+
113
+ describe '#format_schema_response!' do
114
+ before do
115
+ stub(presenter).brainstem_keys { ['widgets'] }
116
+ stub(presenter).target_class { 'Widget' }
117
+ end
118
+
119
+ it 'returns the structured response for an endpoint' do
120
+ subject.send(:format_schema_response!)
121
+
122
+ expect(subject.output).to eq('200' => {
123
+ 'description' => 'A list of Widgets have been retrieved.',
124
+ 'schema' => {
125
+ 'type' => 'object',
126
+ 'properties' => {
127
+ 'count' => { 'type' => 'integer', 'format' => 'int32' },
128
+ 'meta' => {
129
+ 'type' => 'object',
130
+ 'properties' => {
131
+ 'count' => { 'type' => 'integer', 'format' => 'int32' },
132
+ 'page_count' => { 'type' => 'integer', 'format' => 'int32' },
133
+ 'page_number' => { 'type' => 'integer', 'format' => 'int32' },
134
+ 'page_size' => { 'type' => 'integer', 'format' => 'int32' },
135
+ }
136
+ },
137
+ 'results' => {
138
+ 'type' => 'array',
139
+ 'items' => {
140
+ 'type' => 'object',
141
+ 'properties' => {
142
+ 'key' => { 'type' => 'string' },
143
+ 'id' => { 'type' => 'string' }
144
+ }
145
+ }
146
+ },
147
+ 'widgets' => {
148
+ 'type' => 'object',
149
+ 'additionalProperties' => {
150
+ '$ref' => '#/definitions/Widget'
151
+ }
152
+ }
153
+ }
154
+ }
155
+ })
156
+ end
157
+ end
158
+
159
+ describe '#format_error_responses!' do
160
+ it 'returns the structured errors responses for an endpoint' do
161
+ subject.send(:format_error_responses!)
162
+
163
+ expect(subject.output).to eq(
164
+ '400' => { 'description' => 'Bad Request', 'schema' => { '$ref' => '#/definitions/Errors' } },
165
+ '401' => { 'description' => 'Unauthorized request', 'schema' => { '$ref' => '#/definitions/Errors' } },
166
+ '403' => { 'description' => 'Forbidden request', 'schema' => { '$ref' => '#/definitions/Errors' } },
167
+ '404' => { 'description' => 'Page Not Found', 'schema' => { '$ref' => '#/definitions/Errors' } },
168
+ '503' => { 'description' => 'Service is unavailable', 'schema' => { '$ref' => '#/definitions/Errors' } }
169
+ )
170
+ end
171
+ end
172
+
173
+ describe '#format_custom_response!' do
174
+ context 'when the response is a hash' do
175
+ before do
176
+ stub(endpoint).custom_response_configuration_tree {
177
+ {
178
+ '_config' => {
179
+ 'type' => 'hash',
180
+ },
181
+ 'widget_name' => {
182
+ '_config' => {
183
+ 'type' => 'string',
184
+ 'info' => 'The name of the widget.',
185
+ 'nodoc' => false
186
+ },
187
+ },
188
+ 'widget_permission' => {
189
+ '_config' => {
190
+ 'type' => 'hash',
191
+ 'info' => 'The permissions of the widget.',
192
+ 'nodoc' => false
193
+ },
194
+ 'can_edit' => {
195
+ '_config' => {
196
+ 'type' => 'boolean',
197
+ 'info' => 'Can edit the widget.',
198
+ 'nodoc' => false
199
+ },
200
+ }
201
+ },
202
+ }.with_indifferent_access
203
+ }
204
+ end
205
+
206
+ it 'returns the response structure' do
207
+ subject.send(:format_custom_response!)
208
+
209
+ expect(subject.output).to eq('200' => {
210
+ 'description' => 'A list of Widgets have been retrieved.',
211
+ 'schema' => {
212
+ 'type' => 'object',
213
+ 'properties' => {
214
+ 'widget_name' => {
215
+ 'type' => 'string',
216
+ 'description' => 'The name of the widget.'
217
+ },
218
+ 'widget_permission' => {
219
+ 'type' => 'object',
220
+ 'description' => 'The permissions of the widget.',
221
+ 'properties' => {
222
+ 'can_edit' => {
223
+ 'type' => 'boolean',
224
+ 'description' => 'Can edit the widget.'
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+ })
231
+ end
232
+ end
233
+
234
+ context 'when the response is an array' do
235
+ before do
236
+ stub(endpoint).custom_response_configuration_tree {
237
+ {
238
+ '_config' => {
239
+ 'type' => 'array',
240
+ 'item_type' => 'hash',
241
+ },
242
+ 'widget_name' => {
243
+ '_config' => {
244
+ 'type' => 'string',
245
+ 'info' => 'The name of the widget.',
246
+ 'nodoc' => false
247
+ },
248
+ },
249
+ 'widget_permissions' => {
250
+ '_config' => {
251
+ 'type' => 'array',
252
+ 'item_type' => 'hash',
253
+ 'info' => 'The permissions of the widget.',
254
+ 'nodoc' => false
255
+ },
256
+ 'can_edit' => {
257
+ '_config' => {
258
+ 'type' => 'boolean',
259
+ 'info' => 'Can edit the widget.',
260
+ 'nodoc' => false
261
+ },
262
+ }
263
+ },
264
+ }.with_indifferent_access
265
+ }
266
+ end
267
+
268
+ it 'returns the response structure' do
269
+ subject.send(:format_custom_response!)
270
+
271
+ expect(subject.output).to eq('200' => {
272
+ 'description' => 'A list of Widgets have been retrieved.',
273
+ 'schema' => {
274
+ 'type' => 'array',
275
+ 'items' => {
276
+ 'type' => 'object',
277
+ 'properties' => {
278
+ 'widget_name' => {
279
+ 'type' => 'string',
280
+ 'description' => 'The name of the widget.'
281
+ },
282
+ 'widget_permissions' => {
283
+ 'type' => 'array',
284
+ 'description' => 'The permissions of the widget.',
285
+ 'items' => {
286
+ 'type' => 'object',
287
+ 'properties' => {
288
+ 'can_edit' => {
289
+ 'type' => 'boolean',
290
+ 'description' => 'Can edit the widget.'
291
+ }
292
+ }
293
+ }
294
+ }
295
+ }
296
+ }
297
+ }
298
+ })
299
+ end
300
+ end
301
+
302
+ context 'when the response is not a hash or array' do
303
+ before do
304
+ stub(endpoint).custom_response_configuration_tree {
305
+ {
306
+ '_config' => {
307
+ 'type' => 'boolean',
308
+ 'info' => 'whether the widget exists',
309
+ 'nodoc' => false
310
+ },
311
+ }.with_indifferent_access
312
+ }
313
+ end
314
+
315
+ it 'returns the response structure' do
316
+ subject.send(:format_custom_response!)
317
+
318
+ expect(subject.output).to eq('200' => {
319
+ 'description' => 'A list of Widgets have been retrieved.',
320
+ 'schema' => {
321
+ 'type' => 'boolean',
322
+ 'description' => 'Whether the widget exists.'
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,59 @@
1
+ require 'spec_helper'
2
+ require 'brainstem/api_docs/formatters/open_api_specification/version_2/endpoint_collection_formatter'
3
+ require 'brainstem/api_docs/endpoint_collection'
4
+
5
+ module Brainstem
6
+ module ApiDocs
7
+ module Formatters
8
+ module OpenApiSpecification
9
+ module Version2
10
+ describe EndpointCollectionFormatter do
11
+ let(:atlas) { Object.new }
12
+ let(:endpoint_collection) { EndpointCollection.new(atlas) }
13
+ let(:nodoc) { false }
14
+
15
+ subject { described_class.new(endpoint_collection) }
16
+
17
+ describe "#call" do
18
+ it "formats each endpoint" do
19
+ mock(subject).format_endpoints!
20
+ subject.call
21
+ end
22
+ end
23
+
24
+ describe "formatting" do
25
+ describe "#format_endpoints!" do
26
+ let(:endpoint_1) { Object.new }
27
+ let(:endpoint_2) { Object.new }
28
+ let(:endpoint_3) { Object.new }
29
+ let(:endpoint_4) { Object.new }
30
+
31
+ before do
32
+ mock(endpoint_collection).only_documentable { [endpoint_1, endpoint_2, endpoint_3, endpoint_4] }
33
+
34
+ mock(endpoint_1).formatted_as(:oas_v2) { { "/sprockets" => { "get" => { info: "Get all sprockets" } } } }
35
+ mock(endpoint_2).formatted_as(:oas_v2) { { "/sprockets" => { "post" => { info: "Create a sprocket" } } } }
36
+ mock(endpoint_3).formatted_as(:oas_v2) { { "/sprockets/{id}" => { "patch" => { info: "Update a sprocket" } } } }
37
+ mock(endpoint_4).formatted_as(:oas_v2) { {} }
38
+ end
39
+
40
+ it "joins each documentable endpoint" do
41
+ subject.send(:format_endpoints!)
42
+ expect(subject.output).to eq({
43
+ "/sprockets" => {
44
+ "get" => { info: "Get all sprockets" },
45
+ "post" => { info: "Create a sprocket" },
46
+ },
47
+ "/sprockets/{id}" => {
48
+ "patch" => { info: "Update a sprocket" }
49
+ }
50
+ })
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,308 @@
1
+ require "spec_helper"
2
+ require "brainstem/api_docs/formatters/open_api_specification/version_2/endpoint_formatter"
3
+ require "brainstem/api_docs/endpoint"
4
+
5
+ module Brainstem
6
+ module ApiDocs
7
+ module Formatters
8
+ module OpenApiSpecification
9
+ module Version2
10
+ describe EndpointFormatter do
11
+ let(:controller) { Object.new }
12
+ let(:presenter) { Object.new }
13
+ let(:atlas) { Object.new }
14
+ let(:endpoint) {
15
+ ::Brainstem::ApiDocs::Endpoint.new(
16
+ atlas,
17
+ {
18
+ controller: controller,
19
+ http_methods: %w(get post),
20
+ path: "/v2/widgets(.:format)"
21
+ }.merge(endpoint_args)
22
+ )
23
+ }
24
+ let(:endpoint_args) { {} }
25
+ let(:nodoc) { false }
26
+ let(:controller_tag) { nil }
27
+ let(:security) { nil }
28
+
29
+ subject { described_class.new(endpoint) }
30
+
31
+ before do
32
+ stub(endpoint).presenter { presenter }
33
+ stub(endpoint).security { security }
34
+ stub(controller).tag { controller_tag }
35
+ end
36
+
37
+ describe "#call" do
38
+ before do
39
+ stub(endpoint).nodoc? { nodoc }
40
+ end
41
+
42
+ context "when endpoint has no presenter associated with it" do
43
+ let(:nodoc) { true }
44
+ let(:presenter) { nil }
45
+
46
+ before do
47
+ any_instance_of(described_class) do |instance|
48
+ dont_allow(instance).format_summary!
49
+ dont_allow(instance).format_optional_info!
50
+ dont_allow(instance).format_security!
51
+ dont_allow(instance).format_parameters!
52
+ dont_allow(instance).format_response!
53
+ dont_allow(instance).format_tags!
54
+ end
55
+ end
56
+
57
+ it "returns an empty output" do
58
+ expect(subject.call).to eq({})
59
+ end
60
+ end
61
+
62
+ context "when it is nodoc" do
63
+ let(:nodoc) { true }
64
+
65
+ before do
66
+ any_instance_of(described_class) do |instance|
67
+ dont_allow(instance).format_summary!
68
+ dont_allow(instance).format_optional_info!
69
+ dont_allow(instance).format_security!
70
+ dont_allow(instance).format_parameters!
71
+ dont_allow(instance).format_response!
72
+ dont_allow(instance).format_tags!
73
+ end
74
+ end
75
+
76
+ it "returns an empty output" do
77
+ expect(subject.call).to eq({})
78
+ end
79
+ end
80
+
81
+ context "when it is not nodoc" do
82
+ before do
83
+ stub(controller).tag { "CRUD: Widgets" }
84
+ stub(endpoint).security { "CRUD: Widgets" }
85
+ end
86
+
87
+ it "formats endpoint properties" do
88
+ any_instance_of(described_class) do |instance|
89
+ mock(instance).format_summary!
90
+ mock(instance).format_optional_info!
91
+ mock(instance).format_security!
92
+ mock(instance).format_parameters!
93
+ mock(instance).format_response!
94
+ mock(instance).format_tags!
95
+ end
96
+
97
+ subject.call
98
+ end
99
+ end
100
+ end
101
+
102
+ describe "formatting" do
103
+ let(:lorem) { "lorem ipsum dolor sit amet" }
104
+ let(:default_config) { {} }
105
+ let(:show_config) { {} }
106
+
107
+ let(:configuration) { {
108
+ :_default => default_config,
109
+ :show => show_config,
110
+ } }
111
+
112
+ let(:endpoint_args) { { action: :show } }
113
+
114
+ before do
115
+ stub(controller).configuration { configuration }
116
+ end
117
+
118
+ describe "#formatted_url" do
119
+ let(:endpoint_args) { { path: "/widgets(.:format)" } }
120
+
121
+ it "returns the url without the format" do
122
+ expect(subject.send(:formatted_url)).to eq("/widgets")
123
+ end
124
+
125
+ context "when :id is present in the url" do
126
+ let(:endpoint_args) { { path: "/widgets/:id(.:format)" } }
127
+
128
+ it "replace the :id param with {id}" do
129
+ expect(subject.send(:formatted_url)).to eq("/widgets/{id}")
130
+ end
131
+ end
132
+
133
+ context "when :some_id is present in the url" do
134
+ let(:endpoint_args) { { path: "/widgets/:some_id/blah/:id(.:format)" } }
135
+
136
+ it "replace the :some_id param with {some_id}" do
137
+ expect(subject.send(:formatted_url)).to eq("/widgets/{some_id}/blah/{id}")
138
+ end
139
+ end
140
+ end
141
+
142
+ describe "#format_summary!" do
143
+ it "sets the title in the output" do
144
+ stub(endpoint).title { lorem }
145
+ subject.send(:format_summary!)
146
+ expect(subject.output["/widgets"]["get"]["summary"]).to eq(lorem)
147
+ end
148
+ end
149
+
150
+ describe "#format_tags!" do
151
+ let(:controller_name) { "awesome_sauce" }
152
+
153
+ before do
154
+ stub(controller).name { controller_name }
155
+ end
156
+
157
+ context "when controller is not assigned a tag" do
158
+ before do
159
+ stub(controller).tag { nil }
160
+ end
161
+
162
+ it "sets the title in the output" do
163
+ subject.send(:format_tags!)
164
+ expect(subject.output["/widgets"]["get"]["tags"]).to eq(["Awesome Sauce"])
165
+ end
166
+ end
167
+
168
+ context "when controller is assigned a tag" do
169
+ before do
170
+ stub(controller).tag { "Breaking Bad" }
171
+ end
172
+
173
+ it "sets the assigned tag name in the output" do
174
+ subject.send(:format_tags!)
175
+ expect(subject.output["/widgets"]["get"]["tags"]).to eq(["Breaking Bad"])
176
+ end
177
+ end
178
+ end
179
+
180
+ describe "#format_optional_info!" do
181
+ context "when present" do
182
+ let(:description) { " lorem ipsum dolor sit amet " }
183
+ let(:operation_id) { "Operation 1" }
184
+ let(:consumes) { %w(application/json) }
185
+ let(:produces) { %w(application/json) }
186
+ let(:schemes) { %w(http https) }
187
+ let(:external_docs) { { url: "/", description: "Blah" } }
188
+ let(:deprecated) { true }
189
+
190
+ before do
191
+ stub(endpoint).description { lorem }
192
+ stub(endpoint).operation_id { operation_id }
193
+ stub(endpoint).consumes { consumes }
194
+ stub(endpoint).produces { produces }
195
+ stub(endpoint).schemes { schemes }
196
+ stub(endpoint).external_docs { external_docs }
197
+ stub(endpoint).deprecated { deprecated }
198
+ end
199
+
200
+ it "includes the description key with the given description" do
201
+ subject.send(:format_optional_info!)
202
+
203
+ output = subject.output["/widgets"]["get"]
204
+ expect(output["description"]).to eq("Lorem ipsum dolor sit amet.")
205
+ expect(output["operation_id"]).to eq(operation_id)
206
+ expect(output["consumes"]).to eq(consumes)
207
+ expect(output["produces"]).to eq(produces)
208
+ expect(output["schemes"]).to eq(schemes)
209
+ expect(output["external_docs"]).to eq(external_docs.with_indifferent_access)
210
+ expect(output["deprecated"]).to eq(deprecated)
211
+ end
212
+ end
213
+
214
+ context "when absent" do
215
+ before do
216
+ stub(endpoint).description { nil }
217
+ stub(endpoint).operation_id { nil }
218
+ stub(endpoint).consumes { nil }
219
+ stub(endpoint).produces { nil }
220
+ stub(endpoint).schemes { nil }
221
+ stub(endpoint).external_docs { nil }
222
+ stub(endpoint).deprecated { nil }
223
+ end
224
+
225
+ it "does not include the description key" do
226
+ subject.send(:format_optional_info!)
227
+
228
+ output = subject.output["/widgets"]["get"].keys
229
+ expect(output).to_not include(
230
+ "description",
231
+ "operation_id",
232
+ "consumes",
233
+ "produces",
234
+ "schemes",
235
+ "external_docs",
236
+ "deprecated"
237
+ )
238
+ end
239
+ end
240
+ end
241
+
242
+ describe "#format_security!" do
243
+ before do
244
+ stub(endpoint).security { security }
245
+ end
246
+
247
+ context "when specified" do
248
+ let(:security) { { petstore_auth: ["write:pets", "read:pets"] } }
249
+
250
+ it "sets the security property in the output" do
251
+ subject.send(:format_security!)
252
+
253
+ expect(subject.output["/widgets"]["get"]["security"]).to eq(security.with_indifferent_access)
254
+ end
255
+ end
256
+
257
+ # NOTE: Empty Array removes all security restrictions on the endpoint.
258
+ context "when empty array is specified" do
259
+ let(:security) { [] }
260
+
261
+ it "sets the security property as an emtpy array" do
262
+ subject.send(:format_security!)
263
+
264
+ expect(subject.output["/widgets"]["get"]["security"]).to eq([])
265
+ end
266
+ end
267
+
268
+ context "when nothing is specified" do
269
+ let(:security) { nil }
270
+
271
+ it "does not add the security property" do
272
+ subject.send(:format_security!)
273
+
274
+ expect(subject.output["/widgets"]["get"]).to_not have_key("security")
275
+ end
276
+ end
277
+ end
278
+
279
+ describe "#format_parameters!" do
280
+ let(:formatted_params) { { "params_for_endpoint" => true } }
281
+
282
+ it "calls EndpointParamsFormatter" do
283
+ mock(Brainstem::ApiDocs::FORMATTERS[:parameters][:oas_v2])
284
+ .call(endpoint) { formatted_params }
285
+ subject.send(:format_parameters!)
286
+
287
+ expect(subject.output["/widgets"]["get"]["parameters"]).to eq(formatted_params)
288
+ end
289
+ end
290
+
291
+ describe "#format_response!" do
292
+ let(:formatted_response) { { "response_for_endpoint" => true } }
293
+
294
+ it "calls EndpointResponseFormatter" do
295
+ mock(Brainstem::ApiDocs::FORMATTERS[:response][:oas_v2])
296
+ .call(endpoint) { formatted_response }
297
+ subject.send(:format_response!)
298
+
299
+ expect(subject.output["/widgets"]["get"]["responses"]).to eq(formatted_response)
300
+ end
301
+ end
302
+ end
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
308
+ end