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