bullet_train-api 1.7.10 → 1.7.11
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/README.md +55 -1
- data/app/helpers/api/open_api_helper.rb +69 -22
- data/app/views/api/v1/open_api/shared/_paths.yaml.erb +3 -3
- data/app/views/api/v1/open_api/teams/_paths.yaml.erb +1 -1
- data/app/views/api/v1/open_api/users/_paths.yaml.erb +1 -1
- data/lib/bullet_train/api/example_bot.rb +1 -5
- data/lib/bullet_train/api/strong_parameters_reporter.rb +10 -2
- data/lib/bullet_train/api/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aef8c20ab58036263c4aedee16b5957f9794f6db67388b88f308ad2de6eddcd2
|
4
|
+
data.tar.gz: c97837332c5bf099547093477248223dd5efecc9dfe6327b10e27173bff8b981
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f7b9c1c254e7425b2a5a48b1badf2a6b22667b4724e761291ee4a577bd048b3bfe3663864eb0a34bf653d54a4c741f044f1d6ac7a44e8b8d581e6303f082f6dc
|
7
|
+
data.tar.gz: 81ff21bf84669a24a2972fcc6b6441661c18e3074b31f1c94945572b4e34dfe4923ef21985b3adaa5e1c5bdc70353d82567853e1da03f49da8c22a2053eeda55
|
data/README.md
CHANGED
@@ -20,6 +20,7 @@ Then, run `bundle` or install it manually using `gem install bullet_train-api`.
|
|
20
20
|
- [Documentation](#documentation)
|
21
21
|
- [Index file](#index-file)
|
22
22
|
- [Automatic Components](#automatic-components)
|
23
|
+
- [Manually Extending Component Schemas](#manually-extending-component-schemas)
|
23
24
|
- [Automatic Paths](#automatic-paths)
|
24
25
|
- [External Markdown files](#external-markdown-files)
|
25
26
|
- [Examples](#examples)
|
@@ -144,6 +145,59 @@ As you can see, it automatically sets required fields based on presence validato
|
|
144
145
|
field types based on the value found in Jbuilder file, descriptions and examples.
|
145
146
|
More on how it works and how you can customize the output in [`jbuilder-schema`](https://github.com/bullet-train-co/jbuilder-schema) documentation.
|
146
147
|
|
148
|
+
#### Manually Extending Component Schemas
|
149
|
+
|
150
|
+
While `automatic_components_for` automatically adds parameters and attributes from your application, sometimes it is
|
151
|
+
necessary to manually specify parameters and attributes in addition to the automatic ones, due to custom code in
|
152
|
+
your app.
|
153
|
+
|
154
|
+
`automatic_components_for` allows to you add or remove parameters and attributes. You can also specify that
|
155
|
+
parameters that are only available for create or update methods.
|
156
|
+
|
157
|
+
##### Adding or Removing Specific Attributes for a Component
|
158
|
+
|
159
|
+
To add or remove specific attributes for a component:
|
160
|
+
|
161
|
+
automatic_components_for User,
|
162
|
+
parameters: {
|
163
|
+
add: {
|
164
|
+
tag_ids: {
|
165
|
+
type: :array,
|
166
|
+
items: {type: :integer},
|
167
|
+
description: "Array of Tag IDs for the User."
|
168
|
+
}
|
169
|
+
}
|
170
|
+
},
|
171
|
+
attributes: {
|
172
|
+
remove: [:email, :time_zone],
|
173
|
+
add: {
|
174
|
+
url: {
|
175
|
+
type: :string,
|
176
|
+
description: "The URL of the User's image."
|
177
|
+
}
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
##### Specifying Parameters for Create or Update Methods
|
182
|
+
|
183
|
+
To specify parameters that only exist for the create or update methods, use the following format:
|
184
|
+
|
185
|
+
automatic_components_for Enrollment,
|
186
|
+
parameters: {
|
187
|
+
create: {
|
188
|
+
add: {
|
189
|
+
user_id: {
|
190
|
+
type: :integer,
|
191
|
+
description: "ID of the User who enrolled.",
|
192
|
+
example: 42
|
193
|
+
}
|
194
|
+
}
|
195
|
+
},
|
196
|
+
update: {
|
197
|
+
remove: [:time_zone]
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
147
201
|
#### Automatic Paths
|
148
202
|
|
149
203
|
Method `automatic_paths_for` generates basic REST paths. It accepts model as it's argument.
|
@@ -155,7 +209,7 @@ a write action (i.e. create or update), then doc generation uses the `strong_par
|
|
155
209
|
defined in the corresponding controller to generate the Parameters section in the schema.
|
156
210
|
|
157
211
|
Automatic paths are generated for basic REST actions. You can customize those paths or add your own by creating a
|
158
|
-
file at `app/views/api/<version>/open_api/<Model.underscore.plural>/_paths.yaml.erb`. For REST paths there's no need to
|
212
|
+
file at `app/views/api/<version>/open_api/<Model.underscore.plural>/_paths.yaml.erb`. For REST paths there's no need to
|
159
213
|
duplicate all the schema, you can specify only what differs from auto-generated code.
|
160
214
|
|
161
215
|
#### External Markdown files
|
@@ -100,24 +100,24 @@ module Api
|
|
100
100
|
end
|
101
101
|
|
102
102
|
if has_strong_parameters?("Api::#{@version.upcase}::#{model.name.pluralize}Controller".constantize)
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
103
|
+
strong_parameter_keys = strong_parameter_keys_for(model.name, @version)
|
104
|
+
|
105
|
+
# Create separate parameter schema for create and update methods
|
106
|
+
create_parameters_output = process_strong_parameters(model, strong_parameter_keys, schema_json, "create", **options)
|
107
|
+
update_parameters_output = process_strong_parameters(model, strong_parameter_keys, schema_json, "update", **options)
|
108
|
+
|
109
|
+
# We need to skip TeamParameters, UserParameters & InvitationParametersUpdate as they are not present in
|
110
|
+
# the bullet train api schema
|
111
|
+
if model.name == "Team" || model.name == "User"
|
112
|
+
create_parameters_output = nil
|
113
|
+
elsif model.name == "Invitation"
|
114
|
+
update_parameters_output = nil
|
107
115
|
end
|
108
116
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
# Allow customization of Parameters
|
115
|
-
customize_component!(parameters_output, options[:parameters]) if options[:parameters]
|
116
|
-
|
117
|
-
(
|
118
|
-
indent(attributes_output.to_yaml.gsub("---", "#{model.name.gsub("::", "")}Attributes:"), 3) +
|
119
|
-
indent(" " + parameters_output.to_yaml.gsub("---", "#{model.name.gsub("::", "")}Parameters:"), 3)
|
120
|
-
).html_safe
|
117
|
+
output = indent(attributes_output.to_yaml.gsub("---", "#{model.name.gsub("::", "")}Attributes:"), 3)
|
118
|
+
output += indent(" " + create_parameters_output.to_yaml.gsub("---", "#{model.name.gsub("::", "")}Parameters:"), 3) if create_parameters_output
|
119
|
+
output += indent(" " + update_parameters_output.to_yaml.gsub("---", "#{model.name.gsub("::", "")}ParametersUpdate:"), 3) if update_parameters_output
|
120
|
+
output.html_safe
|
121
121
|
else
|
122
122
|
|
123
123
|
indent(attributes_output.to_yaml.gsub("---", "#{model.name.gsub("::", "")}Attributes:"), 3)
|
@@ -125,6 +125,37 @@ module Api
|
|
125
125
|
end
|
126
126
|
end
|
127
127
|
|
128
|
+
def process_strong_parameters(model, strong_parameter_keys, schema_json, method_type, **options)
|
129
|
+
parameters_output = JSON.parse(schema_json)
|
130
|
+
parameters_output["required"].select! { |key| strong_parameter_keys.include?(key.to_sym) }
|
131
|
+
parameters_output["properties"].select! { |key| strong_parameter_keys.include?(key.to_sym) }
|
132
|
+
parameters_output["example"]&.select! { |key, value| strong_parameter_keys.include?(key.to_sym) }
|
133
|
+
|
134
|
+
# Allow customization of Parameters
|
135
|
+
parameters_custom = options[:parameters][method_type] if options[:parameters].is_a?(Hash) && options[:parameters].key?(method_type)
|
136
|
+
parameters_custom ||= options[:parameters]
|
137
|
+
customize_component!(parameters_output, parameters_custom, method_type) if parameters_custom
|
138
|
+
|
139
|
+
# We need to wrap the example parameters with the model name as expected by the API controllers
|
140
|
+
if parameters_output["example"]
|
141
|
+
parameters_output["example"] = {model.model_name.param_key => parameters_output["example"]}
|
142
|
+
end
|
143
|
+
|
144
|
+
parameters_output
|
145
|
+
end
|
146
|
+
|
147
|
+
def strong_parameter_keys_for(model_name, version)
|
148
|
+
strong_params_module = "::Api::#{version.upcase}::#{model_name.pluralize}Controller::StrongParameters".constantize
|
149
|
+
strong_params_reporter = BulletTrain::Api::StrongParametersReporter.new(model_name.constantize, strong_params_module)
|
150
|
+
strong_parameter_keys = strong_params_reporter.report
|
151
|
+
|
152
|
+
if strong_parameter_keys.last.is_a?(Hash)
|
153
|
+
strong_parameter_keys += strong_parameter_keys.pop.keys
|
154
|
+
end
|
155
|
+
|
156
|
+
strong_parameter_keys
|
157
|
+
end
|
158
|
+
|
128
159
|
def paths_for(model)
|
129
160
|
for_model model do
|
130
161
|
indent(render("api/#{@version}/open_api/#{model.name.underscore.pluralize}/paths"), 1)
|
@@ -154,9 +185,9 @@ module Api
|
|
154
185
|
methods.include?("create") || methods.include?("update")
|
155
186
|
end
|
156
187
|
|
157
|
-
def update_ref_values!(hash)
|
188
|
+
def update_ref_values!(hash, method_type = nil)
|
158
189
|
hash.each do |key, value|
|
159
|
-
if key == "$ref" && value.is_a?(String) && !value.include?("Attributes")
|
190
|
+
if key == "$ref" && value.is_a?(String) && !value.include?("Attributes#{method_type.to_s.camelize}")
|
160
191
|
# Extract the part after "#/components/schemas/"
|
161
192
|
schema_part = value.split("#/components/schemas/").last
|
162
193
|
|
@@ -164,14 +195,14 @@ module Api
|
|
164
195
|
camelized_schema = schema_part.split("/").map(&:camelize).join
|
165
196
|
|
166
197
|
# Update the value
|
167
|
-
hash[key] = "#/components/schemas/#{camelized_schema}Attributes"
|
198
|
+
hash[key] = "#/components/schemas/#{camelized_schema}Attributes#{method_type.to_s.camelize}"
|
168
199
|
elsif value.is_a?(Hash)
|
169
200
|
# Recursively call the method for nested hashes
|
170
|
-
update_ref_values!(value)
|
201
|
+
update_ref_values!(value, method_type)
|
171
202
|
elsif value.is_a?(Array)
|
172
203
|
# Recursively call the method for each hash in the array
|
173
204
|
value.each do |item|
|
174
|
-
update_ref_values!(item) if item.is_a?(Hash)
|
205
|
+
update_ref_values!(item, method_type) if item.is_a?(Hash)
|
175
206
|
end
|
176
207
|
end
|
177
208
|
end
|
@@ -189,9 +220,25 @@ module Api
|
|
189
220
|
end
|
190
221
|
end
|
191
222
|
|
192
|
-
|
223
|
+
# This allows for customization of the attribute and parameter components.
|
224
|
+
#
|
225
|
+
# General customizations for all methods:
|
226
|
+
# automatic_components_for User,
|
227
|
+
# attributes: {remove: [:email, :time_zone]}, parameters: {add: {email: {type: :string, required: true, example: "fred@example.com"}}
|
228
|
+
# automatic_components_for User,
|
229
|
+
# attributes: {remove: [:email, :time_zone]}, parameters: {remove: [:email, :time_zone, :locale]}
|
230
|
+
# Or specific customizations to parameters for create and update methods:
|
231
|
+
# automatic_components_for User,
|
232
|
+
# parameters: {update: {remove: [:email]}, create: {remove: [:time_zone]}},
|
233
|
+
# attributes: {remove: [:email, :time_zone]}
|
234
|
+
def customize_component!(original, custom, method_type = nil)
|
193
235
|
custom = custom.deep_stringify_keys.deep_transform_values { |v| v.is_a?(Symbol) ? v.to_s : v }
|
194
236
|
|
237
|
+
# Check if customizations are provided for specific HTTP methods
|
238
|
+
if custom.key?(method_type.to_s)
|
239
|
+
custom = custom[method_type.to_s]
|
240
|
+
end
|
241
|
+
|
195
242
|
if custom.key?("add")
|
196
243
|
custom["add"].each do |property, details|
|
197
244
|
if details["required"]
|
@@ -59,7 +59,7 @@
|
|
59
59
|
type: object
|
60
60
|
$ref: "#/components/schemas/ScaffoldingCompletelyConcreteTangibleThingParameters"
|
61
61
|
example:
|
62
|
-
|
62
|
+
$ref: "#/components/schemas/ScaffoldingCompletelyConcreteTangibleThingParameters/example"
|
63
63
|
responses:
|
64
64
|
"404":
|
65
65
|
description: "Not Found"
|
@@ -115,9 +115,9 @@
|
|
115
115
|
properties:
|
116
116
|
scaffolding_completely_concrete_tangible_thing:
|
117
117
|
type: object
|
118
|
-
$ref: "#/components/schemas/
|
118
|
+
$ref: "#/components/schemas/ScaffoldingCompletelyConcreteTangibleThingParametersUpdate"
|
119
119
|
example:
|
120
|
-
|
120
|
+
$ref: "#/components/schemas/ScaffoldingCompletelyConcreteTangibleThingParametersUpdate/example"
|
121
121
|
responses:
|
122
122
|
"404":
|
123
123
|
description: "Not Found"
|
@@ -43,11 +43,7 @@ module FactoryBot
|
|
43
43
|
|
44
44
|
if method.end_with?("parameters")
|
45
45
|
if has_strong_parameters?("::Api::#{version.upcase}::#{class_name.pluralize}Controller".constantize)
|
46
|
-
|
47
|
-
strong_parameter_keys = BulletTrain::Api::StrongParametersReporter.new(class_name.constantize, strong_params_module).report
|
48
|
-
if strong_parameter_keys.last.is_a?(Hash)
|
49
|
-
strong_parameter_keys += strong_parameter_keys.pop.keys
|
50
|
-
end
|
46
|
+
strong_parameter_keys = strong_parameter_keys_for(class_name, version)
|
51
47
|
|
52
48
|
output = _json_output(template, version, class_name, var_name, values)
|
53
49
|
|
@@ -41,8 +41,16 @@ module BulletTrain
|
|
41
41
|
# end
|
42
42
|
# end
|
43
43
|
|
44
|
-
def report
|
45
|
-
|
44
|
+
def report(method_type = nil)
|
45
|
+
method_type = ["create", "update"].include?(method_type) ? method_type : nil
|
46
|
+
base_method_name = @model.name.split("::").last.underscore
|
47
|
+
|
48
|
+
# if available in the controller, it will use the 'update' strong params instead of the default strong params.
|
49
|
+
@filters = if method_type == "update" && respond_to?("#{base_method_name}_#{method_type}_params".to_sym, true)
|
50
|
+
send(:"#{base_method_name}_#{method_type}_params")
|
51
|
+
else
|
52
|
+
send(:"#{base_method_name}_params")
|
53
|
+
end
|
46
54
|
|
47
55
|
# There's a reason I'm doing it this way.
|
48
56
|
@filters
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullet_train-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Culver
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-05
|
11
|
+
date: 2024-06-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: standard
|