brainstem 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9abf1184c78ea43d4e00ae5aa6f2a11fdb663013
4
- data.tar.gz: 421ffc9a7dd8cce73ad64bcadd40a4108b79c7bf
3
+ metadata.gz: e825c08d783b4cecd7da03879db5545efd3e0697
4
+ data.tar.gz: 1478c23f3186dd7d065585d102245e995e6d9df5
5
5
  SHA512:
6
- metadata.gz: 8a230a3bbe9bee9643bc96a6b894a8e86669483b1057cb6d7131a3c0ed34817b81739bb1cdc8bc15e64d6c3ca50a84a947133cb68bf3ebbdea702769f5542f59
7
- data.tar.gz: 0b20e2fc89ce9d3b5ac655b959ca56779d37fc2c22ed2e229666d65925932a46fae573e1ef56b7c15ead24c5fe1d8b8e4109523b531d2c273ee9eecc45549e81
6
+ metadata.gz: 27a6f03a61dcd1371ffe0b7860563fe8ba94b6be14814f21ae35f4230d2af65fdcf1790f8a547c5913d11611a4ded52e8f17136cd6b6efb4d8a32adf40b1680c
7
+ data.tar.gz: 501e3164fade58216384cc7ddfe32a07e009af99029a75ddd0f1db19d770e356983af1d4786953997ff18ce3ea03a55e376db05a784db06088e0e3090b131b84
@@ -1,5 +1,151 @@
1
1
  # Changelog
2
2
 
3
+ + **2.1.0** - _05/10/2019_
4
+ ### New Features
5
+ - Add the ability to mark properties as internal for Open API.
6
+ ```ruby
7
+ class ContactsController < ApiController
8
+ internal! "Only used internally"
9
+
10
+ brainstem_params do
11
+ actions :index do
12
+ response :hash do
13
+ field :count, :integer,
14
+ info: "Total count of contacts",
15
+ internal: "Internal eyes only"
16
+ fields :contacts, :array,
17
+ item_type: :hash,
18
+ nodoc: true,
19
+ info: "Array of contact details" do
20
+ field :full_name, :string,
21
+ info: "Full name of the contact",
22
+ internal: true
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ ```
29
+
30
+ - Add `--include-internal` option to CLI:
31
+ `brainstem generate --include-internal --open-api-specification=2` will generate documentation for
32
+ everything that is not marked as `nodoc`.
33
+
34
+ - Automatically generate documentation for associations
35
+ - `response_key` will be used if given, otherwise, it will use the name of the association appended
36
+ with `_id` or `_ids` for `has_many`.
37
+
38
+ Using the example below, the association for `PetCategory` will generate documentation for `shooby_id`,
39
+ while the `Owner` association will generate documentation for `owner_id`.
40
+ - The response will include references to all of the associations, including any `polymorphic_classes` added to
41
+ polymorphic associations.
42
+
43
+ In the example below, the documentation for the response will contain references to `PetCategory`, `Vaccine`,
44
+ `Owner`, `Dog`, and `Cat`.
45
+
46
+ ```ruby
47
+ class PetsPresenter < Brainstem::Presenter
48
+ presents Pet
49
+
50
+ associations do
51
+ association :pet_category, PetCategory,
52
+ response_key: :shooby_id,
53
+ type: :belongs_to
54
+ association :vaccines, Vaccine,
55
+ response_key: :vaccine_ids,
56
+ type: :has_many
57
+ association :owner, Owner,
58
+ response_key: :owner_id,
59
+ type: :has_one
60
+ association :pettable, :polymorphic,
61
+ response_key: :pettable_ref,
62
+ polymorphic_classes: [Dog, Cat]
63
+ end
64
+ end
65
+ ```
66
+
67
+ - Add support for non-static key fields in responses. When key is non-static, use the new DSL for response params.
68
+ See example below for usage.
69
+
70
+ ```ruby
71
+ {
72
+ :attributes => {
73
+ :"#{name}_field" => 'world',
74
+ :"#{name}_ids" => [1, 2, 3],
75
+ :name => name
76
+ },
77
+ :"#{model.class_name}_klass" => {
78
+ :"#{model.class_name}_identifier" => model.identifier,
79
+ :"#{model.association_name}_ids" => [1, 2, 3]
80
+ }
81
+ }
82
+ ```
83
+
84
+ A response with non-static key fields written above can be described as follows:
85
+
86
+ ```ruby
87
+ class ContactsController < ApiController
88
+ brainstem_params do
89
+ actions :show do
90
+ response :hash do |response_param|
91
+ response_param.fields :attributes, :hash do |attributes|
92
+ attributes.dynamic_key_field :string
93
+ attributes.field :some_ids, :array, dynamic: true
94
+ attributes.field :name, :string, required: true
95
+ end
96
+
97
+ response_param.dynamic_key_field :hash do |dk|
98
+ dk.dynamic_key_field :string
99
+ dk.field :other_ids, :array, dynamic: true
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ ```
106
+
107
+ - Add support for non-static key params. When key is non-static, use the new DSL for request params.
108
+ See example below for usage.
109
+
110
+ A param with non-static keys like `{ contact.id => 10, :"#{contact.friends_association_name}_ids" => [1, 2, 3] }`
111
+ can be described as follows:
112
+
113
+ ```ruby
114
+ class ContactsController < ApiController
115
+ brainstem_params do
116
+ actions :create do
117
+ valid :info, :hash, required: true do |param|
118
+ param.valid_dynamic_param :integer, required: true
119
+ param.valid :some_ids, :array, dynamic: true
120
+ end
121
+ end
122
+ end
123
+ end
124
+ ```
125
+
126
+ - Sort orders now default to directions `asc` and `desc`.
127
+ When direction is passed as false, no sort order is generated for the docs.
128
+ - ex.
129
+ ```ruby
130
+ presenter_class.sort_order :created_at, value, info: "sorts by creation time", direction: false
131
+ ```
132
+
133
+ - Add new DSL for nested arrays
134
+ ```ruby
135
+ response :array, nested_level: 2, item_type: :hash do
136
+ nested.field :a, :string
137
+ nested.field :b, :integer
138
+ end
139
+ ```
140
+ - An example of the response above: `[[{a: "string", b: 1}, "another string", 4]]`
141
+
142
+ ### Bugfixes
143
+ - Nested required fields will now no longer bubble up the required to its parent. A deeply nested field that is
144
+ required will now only be required on its direct parent.
145
+ - Required fields for a create / update (POST / PUT) request are explicitly called out in the OAS specification
146
+ - Default top-level query params like `only `, `order `, `page`, `per_page`, `include` & `optional` are not
147
+ displayed when the endpoint is not returning brainstem models.
148
+
3
149
  + **2.0.0** - _05/17/2018_
4
150
  - Introduce the capability to document custom response on endpoints
5
151
  ```ruby
@@ -20,6 +166,7 @@
20
166
  end
21
167
  end
22
168
  end
169
+ end
23
170
  ```
24
171
  - Add DSL on controllers to set Open API Specification 2.0 configurations
25
172
  ```ruby
@@ -1,70 +1,99 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- brainstem (1.4.1)
4
+ brainstem (2.2.0)
5
5
  activerecord (>= 4.1)
6
6
  activesupport (>= 4.1)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activemodel (5.1.4)
12
- activesupport (= 5.1.4)
13
- activerecord (5.1.4)
14
- activemodel (= 5.1.4)
15
- activesupport (= 5.1.4)
16
- arel (~> 8.0)
17
- activesupport (5.1.4)
11
+ actionpack (5.2.3)
12
+ actionview (= 5.2.3)
13
+ activesupport (= 5.2.3)
14
+ rack (~> 2.0)
15
+ rack-test (>= 0.6.3)
16
+ rails-dom-testing (~> 2.0)
17
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
18
+ actionview (5.2.3)
19
+ activesupport (= 5.2.3)
20
+ builder (~> 3.1)
21
+ erubi (~> 1.4)
22
+ rails-dom-testing (~> 2.0)
23
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
24
+ activemodel (5.2.3)
25
+ activesupport (= 5.2.3)
26
+ activerecord (5.2.3)
27
+ activemodel (= 5.2.3)
28
+ activesupport (= 5.2.3)
29
+ arel (>= 9.0)
30
+ activesupport (5.2.3)
18
31
  concurrent-ruby (~> 1.0, >= 1.0.2)
19
- i18n (~> 0.7)
32
+ i18n (>= 0.7, < 2)
20
33
  minitest (~> 5.1)
21
34
  tzinfo (~> 1.1)
22
- arel (8.0.0)
35
+ arel (9.0.0)
36
+ builder (3.2.3)
23
37
  coderay (1.1.2)
24
- concurrent-ruby (1.0.5)
25
- database_cleaner (1.6.1)
38
+ concurrent-ruby (1.1.5)
39
+ crass (1.0.4)
40
+ database_cleaner (1.7.0)
26
41
  db-query-matchers (0.9.0)
27
42
  activesupport (>= 4.0, <= 6.0)
28
43
  rspec (~> 3.0)
29
44
  diff-lcs (1.3)
30
- i18n (0.9.0)
45
+ erubi (1.8.0)
46
+ i18n (1.6.0)
31
47
  concurrent-ruby (~> 1.0)
32
- method_source (0.9.0)
33
- minitest (5.10.3)
48
+ loofah (2.2.3)
49
+ crass (~> 1.0.2)
50
+ nokogiri (>= 1.5.9)
51
+ method_source (0.9.2)
52
+ mini_portile2 (2.4.0)
53
+ minitest (5.11.3)
34
54
  mysql2 (0.4.10)
35
- pry (0.9.12.6)
36
- coderay (~> 1.0)
37
- method_source (~> 0.8)
38
- slop (~> 3.4)
39
- pry-nav (0.2.4)
40
- pry (>= 0.9.10, < 0.11.0)
41
- rake (12.2.1)
42
- redcarpet (3.4.0)
55
+ nokogiri (1.10.3)
56
+ mini_portile2 (~> 2.4.0)
57
+ pry (0.12.2)
58
+ coderay (~> 1.1.0)
59
+ method_source (~> 0.9.0)
60
+ pry-nav (0.3.0)
61
+ pry (>= 0.9.10, < 0.13.0)
62
+ rack (2.0.7)
63
+ rack-test (1.1.0)
64
+ rack (>= 1.0, < 3)
65
+ rails-dom-testing (2.0.3)
66
+ activesupport (>= 4.2.0)
67
+ nokogiri (>= 1.6)
68
+ rails-html-sanitizer (1.1.0)
69
+ loofah (~> 2.2, >= 2.2.2)
70
+ rake (12.3.3)
71
+ redcarpet (3.5.0)
43
72
  rr (1.2.1)
44
- rspec (3.7.0)
45
- rspec-core (~> 3.7.0)
46
- rspec-expectations (~> 3.7.0)
47
- rspec-mocks (~> 3.7.0)
48
- rspec-core (3.7.0)
49
- rspec-support (~> 3.7.0)
50
- rspec-expectations (3.7.0)
73
+ rspec (3.8.0)
74
+ rspec-core (~> 3.8.0)
75
+ rspec-expectations (~> 3.8.0)
76
+ rspec-mocks (~> 3.8.0)
77
+ rspec-core (3.8.2)
78
+ rspec-support (~> 3.8.0)
79
+ rspec-expectations (3.8.4)
51
80
  diff-lcs (>= 1.2.0, < 2.0)
52
- rspec-support (~> 3.7.0)
53
- rspec-mocks (3.7.0)
81
+ rspec-support (~> 3.8.0)
82
+ rspec-mocks (3.8.1)
54
83
  diff-lcs (>= 1.2.0, < 2.0)
55
- rspec-support (~> 3.7.0)
56
- rspec-support (3.7.0)
57
- slop (3.6.0)
58
- sqlite3 (1.3.13)
84
+ rspec-support (~> 3.8.0)
85
+ rspec-support (3.8.2)
86
+ sqlite3 (1.4.1)
59
87
  thread_safe (0.3.6)
60
- tzinfo (1.2.4)
88
+ tzinfo (1.2.5)
61
89
  thread_safe (~> 0.1)
62
- yard (0.9.9)
90
+ yard (0.9.20)
63
91
 
64
92
  PLATFORMS
65
93
  ruby
66
94
 
67
95
  DEPENDENCIES
96
+ actionpack
68
97
  brainstem!
69
98
  database_cleaner
70
99
  db-query-matchers
@@ -79,4 +108,4 @@ DEPENDENCIES
79
108
  yard
80
109
 
81
110
  BUNDLED WITH
82
- 1.13.6
111
+ 1.17.3
@@ -141,7 +141,7 @@ module Brainstem
141
141
  FORMATTERS = {
142
142
 
143
143
  # Formatter for entire response
144
- document: {},
144
+ document: {},
145
145
 
146
146
  # Formatters for collections
147
147
  controller_collection: {},
@@ -149,9 +149,9 @@ module Brainstem
149
149
  presenter_collection: {},
150
150
 
151
151
  # Formatters for individual entities
152
- controller: {},
153
- endpoint: {},
154
- presenter: {},
152
+ controller: {},
153
+ endpoint: {},
154
+ presenter: {},
155
155
 
156
156
  # Formatter for Open API Specifications
157
157
  info: {},
@@ -159,6 +159,11 @@ module Brainstem
159
159
  parameters: {},
160
160
  security: {},
161
161
  tags: {},
162
+
163
+ # Formatter for Open API Specifications Individual field / param definition
164
+ endpoint_param: {},
165
+ presenter_field: {},
166
+ response_field: {},
162
167
  }
163
168
  end
164
169
  end
@@ -18,9 +18,9 @@ module Brainstem
18
18
  include Concerns::Optional
19
19
 
20
20
  def initialize(introspector, options = {})
21
- self.endpoints = EndpointCollection.new(self)
22
- self.controllers = ControllerCollection.new(self)
23
- self.presenters = ::Brainstem::ApiDocs::PresenterCollection.new(self)
21
+ self.endpoints = EndpointCollection.new(self, options)
22
+ self.controllers = ControllerCollection.new(self, options)
23
+ self.presenters = ::Brainstem::ApiDocs::PresenterCollection.new(self, options)
24
24
  self.resolver = Resolver.new(self)
25
25
 
26
26
  self.controller_matches = []
@@ -22,7 +22,8 @@ module Brainstem
22
22
  :name,
23
23
  :endpoints,
24
24
  :filename_pattern,
25
- :atlas
25
+ :atlas,
26
+ :include_internal
26
27
 
27
28
  attr_writer :filename_pattern,
28
29
  :filename_link_pattern
@@ -33,7 +34,8 @@ module Brainstem
33
34
  :name,
34
35
  :formatters,
35
36
  :filename_pattern,
36
- :filename_link_pattern
37
+ :filename_link_pattern,
38
+ :include_internal
37
39
  ]
38
40
  end
39
41
 
@@ -77,7 +79,7 @@ module Brainstem
77
79
  end
78
80
 
79
81
  def nodoc?
80
- default_configuration[:nodoc]
82
+ nodoc_for?(default_configuration)
81
83
  end
82
84
 
83
85
  def title
@@ -101,13 +103,19 @@ module Brainstem
101
103
  #
102
104
  def contextual_documentation(key)
103
105
  default_configuration.has_key?(key) &&
104
- !default_configuration[key][:nodoc] &&
106
+ !nodoc_for?(default_configuration[key]) &&
105
107
  default_configuration[key][:info]
106
108
  end
107
109
 
108
110
  def valid_sorted_endpoints
109
111
  endpoints.sorted_with_actions_in_controller(const)
110
112
  end
113
+
114
+ private
115
+
116
+ def nodoc_for?(config)
117
+ !!(config[:nodoc] || (config[:internal] && !include_internal))
118
+ end
111
119
  end
112
120
  end
113
121
  end
@@ -5,13 +5,22 @@ module Brainstem
5
5
  module ApiDocs
6
6
  class ControllerCollection < AbstractCollection
7
7
 
8
+ attr_accessor :include_internal
9
+
10
+ def valid_options
11
+ super | [
12
+ :include_internal
13
+ ]
14
+ end
15
+
8
16
  #
9
17
  # Creates a new controller from a route object and appends it to the
10
18
  # collection.
11
19
  def create_from_route(route)
12
20
  Controller.new(atlas,
13
- const: route[:controller],
14
- name: route[:controller_name].split("/").last
21
+ const: route[:controller],
22
+ name: route[:controller_name].split("/").last,
23
+ include_internal: include_internal
15
24
  ).tap { |controller| self.<< controller }
16
25
  end
17
26
 
@@ -23,7 +23,8 @@ module Brainstem
23
23
  :controller,
24
24
  :controller_name,
25
25
  :action,
26
- :presenter
26
+ :presenter,
27
+ :include_internal
27
28
  ]
28
29
  end
29
30
 
@@ -38,7 +39,8 @@ module Brainstem
38
39
  :controller,
39
40
  :controller_name,
40
41
  :action,
41
- :atlas
42
+ :atlas,
43
+ :include_internal
42
44
 
43
45
  #
44
46
  # Pretty prints each endpoint.
@@ -94,7 +96,7 @@ module Brainstem
94
96
  # Is the entire endpoint undocumentable?
95
97
  #
96
98
  def nodoc?
97
- action_configuration[:nodoc]
99
+ nodoc_for?(action_configuration)
98
100
  end
99
101
 
100
102
  def title
@@ -156,7 +158,8 @@ module Brainstem
156
158
  .with_indifferent_access
157
159
  .inject(ActiveSupport::HashWithIndifferentAccess.new) do |result, (field_name_proc, field_config)|
158
160
 
159
- next result if field_config[:nodoc]
161
+ next result if nodoc_for?(field_config)
162
+ field_config = field_config.except(:nodoc, :internal)
160
163
 
161
164
  field_name = evaluate_field_name(field_name_proc)
162
165
  if field_config.has_key?(:ancestors)
@@ -199,7 +202,8 @@ module Brainstem
199
202
  .except(:_config)
200
203
  .inject(custom_config_tree) do |result, (field_name_proc, field_config)|
201
204
 
202
- next result if field_config[:nodoc]
205
+ next result if nodoc_for?(field_config)
206
+ field_config = field_config.except(:nodoc, :internal)
203
207
 
204
208
  field_name = evaluate_field_name(field_name_proc)
205
209
  if field_config.has_key?(:ancestors)
@@ -243,7 +247,7 @@ module Brainstem
243
247
  #
244
248
  def declared_presented_class
245
249
  valid_presents.has_key?(:target_class) &&
246
- !valid_presents[:nodoc] &&
250
+ !nodoc_for?(valid_presents) &&
247
251
  valid_presents[:target_class]
248
252
  end
249
253
 
@@ -281,7 +285,7 @@ module Brainstem
281
285
  #
282
286
  def contextual_documentation(key)
283
287
  action_configuration.has_key?(key) &&
284
- !action_configuration[key][:nodoc] &&
288
+ !nodoc_for?(action_configuration[key]) &&
285
289
  action_configuration[key][:info]
286
290
  end
287
291
 
@@ -305,6 +309,12 @@ module Brainstem
305
309
  presenter_path.relative_path_from(controller_path).to_s
306
310
  end
307
311
  end
312
+
313
+ private
314
+
315
+ def nodoc_for?(config)
316
+ !!(config[:nodoc] || (config[:internal] && !include_internal))
317
+ end
308
318
  end
309
319
  end
310
320
  end