bullet_train-api 1.7.10 → 1.7.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|