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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +147 -0
- data/Gemfile.lock +68 -39
- data/lib/brainstem/api_docs.rb +9 -4
- data/lib/brainstem/api_docs/atlas.rb +3 -3
- data/lib/brainstem/api_docs/controller.rb +12 -4
- data/lib/brainstem/api_docs/controller_collection.rb +11 -2
- data/lib/brainstem/api_docs/endpoint.rb +17 -7
- data/lib/brainstem/api_docs/endpoint_collection.rb +9 -1
- data/lib/brainstem/api_docs/formatters/open_api_specification/helper.rb +19 -16
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter.rb +52 -80
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_definitions_formatter.rb +64 -84
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint_formatter.rb +1 -1
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/endpoint_param_formatter.rb +39 -0
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter.rb +147 -0
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/response_field_formatter.rb +146 -0
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter.rb +53 -55
- data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/tags_formatter.rb +1 -1
- data/lib/brainstem/api_docs/presenter.rb +16 -8
- data/lib/brainstem/api_docs/presenter_collection.rb +8 -5
- data/lib/brainstem/api_docs/sinks/open_api_specification_sink.rb +3 -1
- data/lib/brainstem/cli/generate_api_docs_command.rb +4 -0
- data/lib/brainstem/concerns/controller_dsl.rb +90 -20
- data/lib/brainstem/concerns/presenter_dsl.rb +16 -8
- data/lib/brainstem/dsl/association.rb +12 -0
- data/lib/brainstem/dsl/fields_block.rb +1 -1
- data/lib/brainstem/version.rb +1 -1
- data/spec/brainstem/api_docs/controller_spec.rb +127 -5
- data/spec/brainstem/api_docs/endpoint_spec.rb +489 -57
- data/spec/brainstem/api_docs/formatters/open_api_specification/helper_spec.rb +15 -4
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/param_definitions_formatter_spec.rb +112 -66
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/endpoint/response_definitions_formatter_spec.rb +404 -32
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/endpoint_param_formatter_spec.rb +335 -0
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter_spec.rb +237 -0
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/response_field_formatter_spec.rb +413 -0
- data/spec/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter_spec.rb +116 -4
- data/spec/brainstem/api_docs/presenter_spec.rb +406 -24
- data/spec/brainstem/cli/generate_api_docs_command_spec.rb +8 -0
- data/spec/brainstem/concerns/controller_dsl_spec.rb +606 -45
- data/spec/brainstem/concerns/presenter_dsl_spec.rb +34 -2
- data/spec/brainstem/dsl/association_spec.rb +54 -3
- metadata +11 -2
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/response_field_formatter'
|
2
|
+
|
3
|
+
module Brainstem
|
4
|
+
module ApiDocs
|
5
|
+
module Formatters
|
6
|
+
module OpenApiSpecification
|
7
|
+
module Version2
|
8
|
+
module FieldDefinitions
|
9
|
+
class EndpointParamFormatter < ResponseFieldFormatter
|
10
|
+
def initialize(endpoint, param_name, param_tree)
|
11
|
+
@endpoint = endpoint
|
12
|
+
@param_name = param_name
|
13
|
+
@param_tree = param_tree
|
14
|
+
end
|
15
|
+
|
16
|
+
def format_object_field(field_config, field_properties, include_description = true)
|
17
|
+
super(field_config, field_properties, include_description).tap do |field_schema|
|
18
|
+
if (required_props = required_properties(field_properties)).present?
|
19
|
+
field_schema[:required] = required_props
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def required_properties(field_properties)
|
25
|
+
field_properties.select do |_, property_data|
|
26
|
+
!property_data[:_config][:dynamic_key] && property_data[:_config][:required]
|
27
|
+
end.keys
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Brainstem::ApiDocs::FORMATTERS[:endpoint_param][:oas_v2] =
|
38
|
+
Brainstem::ApiDocs::Formatters::OpenApiSpecification::Version2::FieldDefinitions::EndpointParamFormatter.method(:call)
|
39
|
+
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'brainstem/api_docs'
|
2
|
+
require 'brainstem/api_docs/formatters/abstract_formatter'
|
3
|
+
require 'brainstem/api_docs/formatters/open_api_specification/helper'
|
4
|
+
|
5
|
+
module Brainstem
|
6
|
+
module ApiDocs
|
7
|
+
module Formatters
|
8
|
+
module OpenApiSpecification
|
9
|
+
module Version2
|
10
|
+
module FieldDefinitions
|
11
|
+
class PresenterFieldFormatter < AbstractFormatter
|
12
|
+
include Helper
|
13
|
+
|
14
|
+
def initialize(presenter, field)
|
15
|
+
@presenter = presenter
|
16
|
+
@field = field
|
17
|
+
end
|
18
|
+
|
19
|
+
def format
|
20
|
+
format_field(@field)
|
21
|
+
end
|
22
|
+
alias_method :call, :format
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
attr_reader :presenter
|
27
|
+
|
28
|
+
def has_properties?(field)
|
29
|
+
field.respond_to?(:configuration)
|
30
|
+
end
|
31
|
+
|
32
|
+
def format_field(field)
|
33
|
+
if field.options[:nested_levels]
|
34
|
+
format_nested_array_field(field)
|
35
|
+
elsif has_properties?(field)
|
36
|
+
format_nested_field(field)
|
37
|
+
else
|
38
|
+
format_simple_field(field)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def format_nested_array_field(field)
|
43
|
+
field_properties_data, nested_levels = format_array_items(field)
|
44
|
+
|
45
|
+
format_nested_array_parent(nested_levels, field_properties_data, format_description(field))
|
46
|
+
end
|
47
|
+
|
48
|
+
def format_array_items(field)
|
49
|
+
field_nested_levels = field.options[:nested_levels]
|
50
|
+
|
51
|
+
if has_properties?(field)
|
52
|
+
[format_nested_field(field, false), field_nested_levels - 1]
|
53
|
+
else
|
54
|
+
[type_and_format(field.options[:item_type]), field_nested_levels]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def format_nested_array_parent(nested_level, formatted_data, description = nil)
|
59
|
+
if nested_level == 1
|
60
|
+
{
|
61
|
+
'type' => 'array',
|
62
|
+
'description' => description,
|
63
|
+
'items' => formatted_data
|
64
|
+
}
|
65
|
+
else
|
66
|
+
{
|
67
|
+
'type' => 'array',
|
68
|
+
'description' => description,
|
69
|
+
'items' => format_nested_array_parent(nested_level - 1, formatted_data)
|
70
|
+
}
|
71
|
+
end.with_indifferent_access.reject { |_, v| v.blank? }
|
72
|
+
end
|
73
|
+
|
74
|
+
def format_nested_field(field, include_description = true)
|
75
|
+
case field.type
|
76
|
+
when 'hash'
|
77
|
+
format_object_field(field, include_description)
|
78
|
+
when 'array'
|
79
|
+
{
|
80
|
+
type: 'array',
|
81
|
+
description: include_description && format_description(field),
|
82
|
+
items: format_object_field(field, false)
|
83
|
+
}
|
84
|
+
end.with_indifferent_access.reject { |_, v| v.blank? }
|
85
|
+
end
|
86
|
+
|
87
|
+
def format_object_field(field, include_description = true)
|
88
|
+
{
|
89
|
+
type: 'object',
|
90
|
+
description: include_description && format_description(field),
|
91
|
+
properties: format_field_properties(field.to_h),
|
92
|
+
}.with_indifferent_access.reject { |_, v| v.blank? }
|
93
|
+
end
|
94
|
+
|
95
|
+
def format_simple_field(field)
|
96
|
+
field_data = type_and_format(field.type) || raise(invalid_type_error_message(field))
|
97
|
+
field_data.merge!(description: format_description(field))
|
98
|
+
field_data.with_indifferent_access.reject { |_, v| v.blank? }
|
99
|
+
end
|
100
|
+
|
101
|
+
def invalid_type_error_message(field)
|
102
|
+
<<-MSG.strip_heredoc
|
103
|
+
Unknown Brainstem Field type encountered(#{field.type}) for field #{field.name}
|
104
|
+
in #{presenter.target_class.to_s}.
|
105
|
+
MSG
|
106
|
+
end
|
107
|
+
|
108
|
+
def format_field_properties(branches)
|
109
|
+
branches.inject(ActiveSupport::HashWithIndifferentAccess.new) do |buffer, (field_name, field)|
|
110
|
+
buffer[field_name.to_s] = format_field(field)
|
111
|
+
buffer
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def format_description(field)
|
116
|
+
field_description = format_sentence(field.description) || ''
|
117
|
+
field_description << format_conditional_description(field.options)
|
118
|
+
if field.optional?
|
119
|
+
field_description << "\nOnly returned when requested through the optional_fields param.\n"
|
120
|
+
end
|
121
|
+
field_description.try(:chomp!)
|
122
|
+
field_description
|
123
|
+
end
|
124
|
+
|
125
|
+
def format_conditional_description(field_options)
|
126
|
+
return '' if field_options[:if].blank?
|
127
|
+
|
128
|
+
conditions = field_options[:if]
|
129
|
+
.reject { |cond| presenter.conditionals[cond].options[:nodoc] }
|
130
|
+
.map { |cond| uncapitalize(presenter.conditionals[cond].description) }
|
131
|
+
.delete_if(&:empty?)
|
132
|
+
.uniq
|
133
|
+
.to_sentence
|
134
|
+
|
135
|
+
conditions.present? ? "\nVisible when #{conditions}.\n" : ''
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
Brainstem::ApiDocs::FORMATTERS[:presenter_field][:oas_v2] =
|
146
|
+
Brainstem::ApiDocs::Formatters::OpenApiSpecification::Version2::FieldDefinitions::PresenterFieldFormatter.method(:call)
|
147
|
+
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'brainstem/api_docs'
|
2
|
+
require 'brainstem/api_docs/formatters/abstract_formatter'
|
3
|
+
require 'brainstem/api_docs/formatters/open_api_specification/helper'
|
4
|
+
|
5
|
+
module Brainstem
|
6
|
+
module ApiDocs
|
7
|
+
module Formatters
|
8
|
+
module OpenApiSpecification
|
9
|
+
module Version2
|
10
|
+
module FieldDefinitions
|
11
|
+
class ResponseFieldFormatter < AbstractFormatter
|
12
|
+
include Helper
|
13
|
+
|
14
|
+
def initialize(endpoint, param_name, param_tree)
|
15
|
+
@endpoint = endpoint
|
16
|
+
@param_name = param_name
|
17
|
+
@param_tree = param_tree
|
18
|
+
end
|
19
|
+
|
20
|
+
def format
|
21
|
+
field_config = @param_tree[:_config]
|
22
|
+
field_properties = @param_tree.except(:_config)
|
23
|
+
|
24
|
+
format_field(field_config, field_properties)
|
25
|
+
end
|
26
|
+
alias_method :call, :format
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def format_field(field_config, field_branches)
|
31
|
+
if field_config[:nested_levels]
|
32
|
+
format_nested_array_field(field_config, field_branches)
|
33
|
+
elsif field_branches.present?
|
34
|
+
format_nested_field(field_config, field_branches)
|
35
|
+
else
|
36
|
+
format_simple_field(field_config)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def format_nested_array_field(field_config, field_properties)
|
41
|
+
field_properties_data, nested_levels = format_array_items(field_config, field_properties)
|
42
|
+
|
43
|
+
format_nested_array_parent(nested_levels, field_properties_data)
|
44
|
+
end
|
45
|
+
|
46
|
+
def format_array_items(field_config, field_properties)
|
47
|
+
field_nested_levels = field_config[:nested_levels]
|
48
|
+
|
49
|
+
if field_properties.present?
|
50
|
+
[format_nested_field(field_config, field_properties), field_nested_levels - 1]
|
51
|
+
else
|
52
|
+
[type_and_format(field_config[:item_type]), field_nested_levels]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def format_nested_array_parent(nested_level, formatted_data)
|
57
|
+
if nested_level == 1
|
58
|
+
{
|
59
|
+
'type' => 'array',
|
60
|
+
'items' => formatted_data
|
61
|
+
}
|
62
|
+
else
|
63
|
+
{
|
64
|
+
'type' => 'array',
|
65
|
+
'items' => format_nested_array_parent(nested_level - 1, formatted_data)
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def format_nested_field(field_config, field_properties)
|
71
|
+
case field_config[:type]
|
72
|
+
when 'hash'
|
73
|
+
format_object_field(field_config, field_properties)
|
74
|
+
when 'array'
|
75
|
+
{
|
76
|
+
type: 'array',
|
77
|
+
description: format_description(field_config),
|
78
|
+
items: format_object_field(field_config, field_properties, false)
|
79
|
+
}
|
80
|
+
end.with_indifferent_access.reject { |_, v| v.blank? }
|
81
|
+
end
|
82
|
+
|
83
|
+
def format_object_field(field_config, field_properties, include_description = true)
|
84
|
+
properties, additional_properties = split_properties(field_properties)
|
85
|
+
|
86
|
+
{
|
87
|
+
type: 'object',
|
88
|
+
description: include_description && format_description(field_config),
|
89
|
+
properties: format_field_properties(properties),
|
90
|
+
additionalProperties: format_field_properties(additional_properties),
|
91
|
+
}.with_indifferent_access.reject { |_, v| v.blank? }
|
92
|
+
end
|
93
|
+
|
94
|
+
def split_properties(field_properties)
|
95
|
+
split_properties = field_properties.each_with_object({ properties: {}, additional_properties: {} }) do |(field_name, field_config), acc|
|
96
|
+
if field_config[:_config][:dynamic_key]
|
97
|
+
acc[:additional_properties][field_name] = field_config
|
98
|
+
else
|
99
|
+
acc[:properties][field_name] = field_config
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
[split_properties[:properties], split_properties[:additional_properties]]
|
104
|
+
end
|
105
|
+
|
106
|
+
def format_simple_field(field_config)
|
107
|
+
field_data = type_and_format(field_config[:type], field_config[:item_type])
|
108
|
+
raise(invalid_type_error_message(field_config)) unless field_data
|
109
|
+
field_data.merge!(description: format_sentence(field_config[:info])) if field_config[:info].present?
|
110
|
+
field_data
|
111
|
+
end
|
112
|
+
|
113
|
+
def invalid_type_error_message(field_config)
|
114
|
+
<<-MSG.strip_heredoc
|
115
|
+
Unknown Brainstem Field type encountered(#{field_config[:type]}) for field #{field_config[:name]}
|
116
|
+
in #{@endpoint.controller_name} for #{@endpoint.action} action.
|
117
|
+
MSG
|
118
|
+
end
|
119
|
+
|
120
|
+
def format_field_properties(branches)
|
121
|
+
branches.inject(ActiveSupport::HashWithIndifferentAccess.new) do |buffer, (field_name, field_config)|
|
122
|
+
config = field_config[:_config]
|
123
|
+
branches = field_config.except(:_config)
|
124
|
+
|
125
|
+
if config[:dynamic_key]
|
126
|
+
format_field(config, branches)
|
127
|
+
else
|
128
|
+
buffer[field_name.to_s] = format_field(config, branches)
|
129
|
+
buffer
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def format_description(field_config)
|
135
|
+
format_sentence(field_config[:info])
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
Brainstem::ApiDocs::FORMATTERS[:response_field][:oas_v2] =
|
146
|
+
Brainstem::ApiDocs::Formatters::OpenApiSpecification::Version2::FieldDefinitions::ResponseFieldFormatter.method(:call)
|
data/lib/brainstem/api_docs/formatters/open_api_specification/version_2/presenter_formatter.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'active_support/core_ext/string/inflections'
|
2
2
|
require 'brainstem/api_docs/formatters/abstract_formatter'
|
3
3
|
require 'brainstem/api_docs/formatters/open_api_specification/helper'
|
4
|
+
require 'brainstem/api_docs/formatters/open_api_specification/version_2/field_definitions/presenter_field_formatter'
|
4
5
|
|
5
6
|
module Brainstem
|
6
7
|
module ApiDocs
|
@@ -30,6 +31,7 @@ module Brainstem
|
|
30
31
|
format_description!
|
31
32
|
format_type!
|
32
33
|
format_fields!
|
34
|
+
sort_properties!
|
33
35
|
|
34
36
|
output.merge!(presenter.target_class => definition.reject {|_, v| v.blank?})
|
35
37
|
end
|
@@ -42,8 +44,16 @@ module Brainstem
|
|
42
44
|
definition.merge! title: presenter_title(presenter)
|
43
45
|
end
|
44
46
|
|
47
|
+
def sort_properties!
|
48
|
+
return if definition[:properties].blank?
|
49
|
+
|
50
|
+
definition[:properties] = definition[:properties].sort.each_with_object({}) do |(key, val), obj|
|
51
|
+
obj[key] = val
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
45
55
|
def format_description!
|
46
|
-
definition.merge! description:
|
56
|
+
definition.merge! description: format_sentence(presenter.description)
|
47
57
|
end
|
48
58
|
|
49
59
|
def format_type!
|
@@ -51,75 +61,63 @@ module Brainstem
|
|
51
61
|
end
|
52
62
|
|
53
63
|
def format_fields!
|
54
|
-
return unless presenter.valid_fields.any?
|
64
|
+
return unless presenter.valid_fields.any? || presenter.valid_associations.any?
|
55
65
|
|
56
|
-
|
66
|
+
properties = format_field_branch(presenter.valid_fields)
|
67
|
+
with_associations = format_field_associations(properties)
|
68
|
+
|
69
|
+
definition.merge! properties: with_associations
|
57
70
|
end
|
58
71
|
|
59
72
|
def format_field_branch(branch)
|
60
|
-
branch.
|
61
|
-
|
62
|
-
buffer[name.to_s] = case field.type
|
63
|
-
when 'hash'
|
64
|
-
{
|
65
|
-
type: 'object',
|
66
|
-
properties: format_field_branch(field.to_h)
|
67
|
-
}.with_indifferent_access
|
68
|
-
when 'array'
|
69
|
-
{
|
70
|
-
type: 'array',
|
71
|
-
items: {
|
72
|
-
type: 'object',
|
73
|
-
properties: format_field_branch(field.to_h)
|
74
|
-
}
|
75
|
-
}.with_indifferent_access
|
76
|
-
else
|
77
|
-
raise "Unknown Brainstem Field type encountered(#{field.type}) for field #{name}"
|
78
|
-
end
|
79
|
-
else
|
80
|
-
buffer[name.to_s] = format_field_leaf(field)
|
81
|
-
end
|
82
|
-
|
83
|
-
buffer
|
73
|
+
branch.each_with_object(ActiveSupport::HashWithIndifferentAccess.new) do |(name, field), buffer|
|
74
|
+
buffer[name.to_s] = format_field(field)
|
84
75
|
end
|
85
76
|
end
|
86
77
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
78
|
+
def format_field_associations(properties)
|
79
|
+
presenter.valid_associations.each_with_object(properties) do |(name, association), props|
|
80
|
+
if association.polymorphic?
|
81
|
+
key = association.name + "_ref"
|
82
|
+
props[key] = {
|
83
|
+
type: 'object',
|
84
|
+
description: association_description(key, name),
|
85
|
+
properties: {
|
86
|
+
key: type_and_format(:string),
|
87
|
+
id: type_and_format(:string)
|
88
|
+
}
|
89
|
+
}
|
90
|
+
next
|
91
|
+
end
|
90
92
|
|
91
|
-
|
92
|
-
|
93
|
+
key = association_key(association)
|
94
|
+
description = association_description(key, association.name)
|
95
|
+
formatted_type = if association.type == :has_many
|
96
|
+
type_and_format(:array, :string)
|
97
|
+
else
|
98
|
+
type_and_format(:string)
|
99
|
+
end.merge(description: description)
|
93
100
|
|
94
|
-
|
95
|
-
raise "Unknown Brainstem Field type encountered(#{field.type}) for field #{field.name}"
|
101
|
+
props[key] = formatted_type unless props[key]
|
96
102
|
end
|
97
|
-
|
98
|
-
field_data.merge!(description: format_description_for(field))
|
99
|
-
field_data.delete(:description) if field_data[:description].blank?
|
100
|
-
|
101
|
-
field_data
|
102
103
|
end
|
103
104
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
field_description << "\nOnly returned when requested through the optional_fields param.\n" if field.optional?
|
108
|
-
field_description.try(:chomp!)
|
109
|
-
field_description
|
105
|
+
def association_description(key, name)
|
106
|
+
["`#{key}` will only be included in the response if `#{name}` is in the list of included associations.",
|
107
|
+
"See <a href='#section/Includes'>include</a> section for usage."].join(' ')
|
110
108
|
end
|
111
109
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
.
|
118
|
-
|
119
|
-
|
120
|
-
.to_sentence
|
110
|
+
def association_key(association)
|
111
|
+
if association.response_key
|
112
|
+
association.response_key
|
113
|
+
else
|
114
|
+
key = association.name.singularize
|
115
|
+
association.type == :has_many ? "#{key}_ids" : "#{key}_id"
|
116
|
+
end
|
117
|
+
end
|
121
118
|
|
122
|
-
|
119
|
+
def format_field(field)
|
120
|
+
Brainstem::ApiDocs::FORMATTERS[:presenter_field][:oas_v2].call(presenter, field)
|
123
121
|
end
|
124
122
|
end
|
125
123
|
end
|