rest_framework 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a916afbf32cf43553b8aa952024011639f311ab80db53685163e607e48e1ad0
4
- data.tar.gz: 1cc8aa7aed712a63b1ef9586c37b64a87de62fe5dfdcf659e982a1b77c20fd89
3
+ metadata.gz: 34653421f170160fbdd29c3119c728ba5fe7591fd969f9d50d2e20d18cecf8bb
4
+ data.tar.gz: ed35098916c941a897087d9b455f69e86f5f53b1e81442e22aa135c81877c45c
5
5
  SHA512:
6
- metadata.gz: bef1f7c3c52c7a42e8c307ccaf37e4e25f4e663359e26be433f20fd644fca0e59cd3b56613f965b084662046577576728981a320d1d5a5a6517d98556008fc99
7
- data.tar.gz: '080f96e20b330a391b896db89a192e415328aa320f8d669260d8d282c0eee709022afa7fa5dd484449eb5bba85544f8974d24764a5e5b45821803d1ccd3c23f0'
6
+ metadata.gz: b02af4cc8f15d804a8ece266a3678a5a0ae328cf505aa64478f4239392a2ccc1ddbcbdde333fc8d80c9ba8fbb59d2a057c8fed6150fe0758f068c7c3dca3670c
7
+ data.tar.gz: 33d87fa722fa5d0a23ccfd81ea6d5705cc37b49bb9b88ab62f504c76255429c77d0c75b2d83a8f86c3752c6fc7c9a60f3fa9078daebfca5461929d052f43d45f
data/README.md CHANGED
@@ -75,7 +75,7 @@ class Api::RootController < ApiController
75
75
  self.extra_actions = {test: :get}
76
76
 
77
77
  def root
78
- return api_response(
78
+ render_api(
79
79
  {
80
80
  message: "Welcome to the API.",
81
81
  how_to_authenticate: <<~END.lines.map(&:strip).join(" "),
@@ -88,7 +88,7 @@ class Api::RootController < ApiController
88
88
  end
89
89
 
90
90
  def test
91
- return api_response({message: "Hello, world!"})
91
+ render_api({message: "Hello, world!"})
92
92
  end
93
93
  end
94
94
  ```
@@ -105,7 +105,7 @@ class Api::MoviesController < ApiController
105
105
  def first
106
106
  # Always use the bang method, since the framework will rescue `RecordNotFound` and return a
107
107
  # sensible error response.
108
- return api_response(self.get_records.first!)
108
+ render_api(self.get_records.first!)
109
109
  end
110
110
 
111
111
  def get_recordset
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.0
1
+ 0.11.0
@@ -3,7 +3,7 @@
3
3
  <label class="form-label w-100">Route
4
4
  <select class="form-control form-control-sm" id="htmlFormRoute">
5
5
  <% @_rrf_form_routes_html.each do |route| %>
6
- <% path = @route_props[:with_path_args].call(route[:route]) %>
6
+ <% path = route[:path_with_params] %>
7
7
  <option value="<%= route[:verb] %>:<%= path %>"><%= route[:verb] %> <%= route[:relative_path] %></option>
8
8
  <% end %>
9
9
  </select>
@@ -21,16 +21,16 @@
21
21
  <% controller.get_fields.map(&:to_s).each do |f| %>
22
22
  <%
23
23
  # Don't provide form fields for associations or primary keys.
24
- metadata = controller.class.fields_metadata[f]
25
- next if !metadata || metadata[:kind] == "association" || metadata[:read_only]
24
+ cfg = controller.class.field_configuration[f]
25
+ next if !cfg || cfg[:kind] == "association" || cfg[:readonly]
26
26
  %>
27
27
  <div class="mb-2">
28
- <% if metadata[:kind] == "rich_text" %>
28
+ <% if cfg[:kind] == "rich_text" %>
29
29
  <label class="form-label w-100"><%= controller.class.label_for(f) %></label>
30
30
  <%= form.rich_text_area f %>
31
- <% elsif metadata[:kind] == "attachment" %>
31
+ <% elsif cfg[:kind] == "attachment" %>
32
32
  <label class="form-label w-100"><%= controller.class.label_for(f) %>
33
- <%= form.file_field f, multiple: metadata.dig(:attachment, :macro) == :has_many_attached %>
33
+ <%= form.file_field f, multiple: cfg[:attachment_type] == :has_many_attached %>
34
34
  </label>
35
35
  <% else %>
36
36
  <label class="form-label w-100"><%= controller.class.label_for(f) %>
@@ -3,7 +3,7 @@
3
3
  <label class="form-label w-100">Route
4
4
  <select class="form-control form-control-sm" id="rawFormRoute">
5
5
  <% @_rrf_form_routes_raw.each do |route| %>
6
- <% path = @route_props[:with_path_args].call(route[:route]) %>
6
+ <% path = route[:path_with_params] %>
7
7
  <option
8
8
  value="<%= route[:verb] %>:<%= path %>"
9
9
  data-supports-files="<%= !route[:action].in?(["update_all", "destroy", "destroy_all"]) ? "true" : "" %>"
@@ -1,7 +1,7 @@
1
1
  <tr>
2
2
  <td>
3
3
  <% if route[:verb] == "GET" && route[:matches_params] %>
4
- <%= link_to route[:relative_path], @route_props[:with_path_args].call(route[:route]) %>
4
+ <%= link_to route[:relative_path], route[:path_with_params] %>
5
5
  <% else %>
6
6
  <%= route[:relative_path] %>
7
7
  <% end %>
@@ -1,7 +1,7 @@
1
- class RESTFramework::Errors::NilPassedToAPIResponseError < RESTFramework::Errors::BaseError
1
+ class RESTFramework::Errors::NilPassedToRenderAPIError < RESTFramework::Errors::BaseError
2
2
  def message
3
3
  return <<~MSG.split("\n").join(" ")
4
- Payload of `nil` was passed to `api_response`; this is unsupported. If you want a blank
4
+ Payload of `nil` was passed to `render_api`; this is unsupported. If you want a blank
5
5
  response, pass `''` (an empty string) as the payload. If this was the result of a `find_by`
6
6
  (or similar Active Record method) not finding a record, you should use the bang version (e.g.,
7
7
  `find_by!`) to raise `ActiveRecord::RecordNotFound`, which the REST controller will catch and
@@ -11,4 +11,4 @@ class RESTFramework::Errors::NilPassedToAPIResponseError < RESTFramework::Errors
11
11
  end
12
12
 
13
13
  # Alias for convenience.
14
- RESTFramework::NilPassedToAPIResponseError = RESTFramework::Errors::NilPassedToAPIResponseError
14
+ RESTFramework::NilPassedToRenderAPIError = RESTFramework::Errors::NilPassedToRenderAPIError
@@ -3,5 +3,5 @@ end
3
3
 
4
4
  require_relative "errors/base_error"
5
5
 
6
- require_relative "errors/nil_passed_to_api_response_error"
6
+ require_relative "errors/nil_passed_to_render_api_error"
7
7
  require_relative "errors/unknown_model_error"
@@ -2,16 +2,16 @@
2
2
  class RESTFramework::Filters::OrderingFilter < RESTFramework::Filters::BaseFilter
3
3
  # Get a list of ordering fields for the current action.
4
4
  def _get_fields
5
- return @controller.ordering_fields&.map(&:to_s) || @controller.get_fields
5
+ return @controller.class.ordering_fields&.map(&:to_s) || @controller.get_fields
6
6
  end
7
7
 
8
8
  # Convert ordering string to an ordering configuration.
9
9
  def _get_ordering
10
- return nil if @controller.ordering_query_param.blank?
10
+ return nil unless param = @controller.class.ordering_query_param.presence
11
11
 
12
12
  # Ensure ordering_fields are strings since the split param will be strings.
13
13
  fields = self._get_fields
14
- order_string = @controller.params[@controller.ordering_query_param]
14
+ order_string = @controller.params[param]
15
15
 
16
16
  if order_string.present?
17
17
  ordering = {}.with_indifferent_access
@@ -40,7 +40,7 @@ class RESTFramework::Filters::OrderingFilter < RESTFramework::Filters::BaseFilte
40
40
  # Order data according to the request query parameters.
41
41
  def filter_data(data)
42
42
  ordering = self._get_ordering
43
- reorder = !@controller.ordering_no_reorder
43
+ reorder = !@controller.class.ordering_no_reorder
44
44
 
45
45
  if ordering && !ordering.empty?
46
46
  return data.send(reorder ? :reorder : :order, ordering)
@@ -52,6 +52,3 @@ end
52
52
 
53
53
  # Alias for convenience.
54
54
  RESTFramework::OrderingFilter = RESTFramework::Filters::OrderingFilter
55
-
56
- # TODO: Compatibility; remove in 1.0.
57
- RESTFramework::ModelOrderingFilter = RESTFramework::Filters::OrderingFilter
@@ -22,7 +22,7 @@ class RESTFramework::Filters::QueryFilter < RESTFramework::Filters::BaseFilter
22
22
  field, sub_field = match[1..2]
23
23
  next false unless field.in?(fields)
24
24
 
25
- sub_fields = @controller.class.field_config_for(field)[:sub_fields] || []
25
+ sub_fields = @controller.class.field_configuration[field][:sub_fields] || []
26
26
  if sub_field.in?(sub_fields)
27
27
  includes << field.to_sym
28
28
  next true
@@ -66,6 +66,3 @@ end
66
66
 
67
67
  # Alias for convenience.
68
68
  RESTFramework::QueryFilter = RESTFramework::Filters::QueryFilter
69
-
70
- # TODO: Compatibility; remove in 1.0.
71
- RESTFramework::ModelQueryFilter = RESTFramework::Filters::QueryFilter
@@ -2,13 +2,13 @@
2
2
  class RESTFramework::Filters::RansackFilter < RESTFramework::Filters::BaseFilter
3
3
  # Filter data according to the request query parameters.
4
4
  def filter_data(data)
5
- q = @controller.request.query_parameters[@controller.ransack_query_param]
5
+ q = @controller.request.query_parameters[@controller.class.ransack_query_param]
6
6
 
7
7
  if q.present?
8
- distinct = @controller.ransack_distinct
8
+ distinct = @controller.class.ransack_distinct
9
9
 
10
10
  # Determine if `distinct` is determined by query param.
11
- if distinct_query_param = @controller.ransack_distinct_query_param
11
+ if distinct_query_param = @controller.class.ransack_distinct_query_param
12
12
  if distinct_query = @controller.request.query_parameters[distinct_query_param].presence
13
13
  distinct_from_query = ActiveRecord::Type::Boolean.new.cast(distinct_query)
14
14
  unless distinct_from_query.nil?
@@ -17,7 +17,7 @@ class RESTFramework::Filters::RansackFilter < RESTFramework::Filters::BaseFilter
17
17
  end
18
18
  end
19
19
 
20
- return data.ransack(q, @controller.ransack_options || {}).result(distinct: distinct)
20
+ return data.ransack(q, @controller.class.ransack_options || {}).result(distinct: distinct)
21
21
  end
22
22
 
23
23
  return data
@@ -1,7 +1,7 @@
1
1
  class RESTFramework::Filters::SearchFilter < RESTFramework::Filters::BaseFilter
2
2
  # Get a list of search fields for the current action.
3
3
  def _get_fields
4
- if search_fields = @controller.search_fields
4
+ if search_fields = @controller.class.search_fields
5
5
  return search_fields&.map(&:to_s)
6
6
  end
7
7
 
@@ -13,7 +13,7 @@ class RESTFramework::Filters::SearchFilter < RESTFramework::Filters::BaseFilter
13
13
 
14
14
  # Filter data according to the request query parameters.
15
15
  def filter_data(data)
16
- search = @controller.request.query_parameters[@controller.search_query_param]
16
+ search = @controller.request.query_parameters[@controller.class.search_query_param]
17
17
 
18
18
  if search.present?
19
19
  if fields = self._get_fields.presence
@@ -28,7 +28,7 @@ class RESTFramework::Filters::SearchFilter < RESTFramework::Filters::BaseFilter
28
28
  # Ensure we pass user input as arguments to prevent SQL injection.
29
29
  return data.where(
30
30
  fields.map { |f|
31
- "CAST(#{f} AS #{data_type}) #{@controller.search_ilike ? "ILIKE" : "LIKE"} ?"
31
+ "CAST(#{f} AS #{data_type}) #{@controller.class.search_ilike ? "ILIKE" : "LIKE"} ?"
32
32
  }.join(" OR "),
33
33
  *(["%#{search}%"] * fields.length),
34
34
  )
@@ -41,6 +41,3 @@ end
41
41
 
42
42
  # Alias for convenience.
43
43
  RESTFramework::SearchFilter = RESTFramework::Filters::SearchFilter
44
-
45
- # TODO: Compatibility; remove in 1.0.
46
- RESTFramework::ModelSearchFilter = RESTFramework::Filters::SearchFilter
@@ -10,6 +10,7 @@ module RESTFramework::Mixins::BaseControllerMixin
10
10
  # Options related to metadata and display.
11
11
  title: nil,
12
12
  description: nil,
13
+ version: nil,
13
14
  inflect_acronyms: ["ID", "IDs", "REST", "API", "APIs"].freeze,
14
15
 
15
16
  # Options related to serialization.
@@ -18,11 +19,6 @@ module RESTFramework::Mixins::BaseControllerMixin
18
19
  serialize_to_json: true,
19
20
  serialize_to_xml: true,
20
21
 
21
- # Custom integrations (reduces serializer performance due to method calls).
22
- enable_action_text: false,
23
- enable_active_storage: false,
24
- }
25
- RRF_BASE_INSTANCE_CONFIG = {
26
22
  # Options related to pagination.
27
23
  paginator_class: nil,
28
24
  page_size: 20,
@@ -33,6 +29,10 @@ module RESTFramework::Mixins::BaseControllerMixin
33
29
  # Option to disable serializer adapters by default, mainly introduced because Active Model
34
30
  # Serializers will do things like serialize `[]` into `{"":[]}`.
35
31
  disable_adapters_by_default: true,
32
+
33
+ # Custom integrations (reduces serializer performance due to method calls).
34
+ enable_action_text: false,
35
+ enable_active_storage: false,
36
36
  }
37
37
 
38
38
  # Default action for API root.
@@ -58,78 +58,11 @@ module RESTFramework::Mixins::BaseControllerMixin
58
58
  )
59
59
  end
60
60
 
61
- # Collect actions (including extra actions) metadata for this controller.
62
- def actions_metadata
63
- actions = {}
64
-
65
- # Start with builtin actions.
66
- RESTFramework::BUILTIN_ACTIONS.merge(
67
- RESTFramework::RRF_BUILTIN_ACTIONS,
68
- ).each do |action, methods|
69
- next unless self.method_defined?(action)
70
-
71
- actions[action] = {
72
- path: "", methods: methods, type: :builtin, metadata: {label: self.label_for(action)}
73
- }
74
- end
75
-
76
- # Add builtin bulk actions.
77
- RESTFramework::RRF_BUILTIN_BULK_ACTIONS.each do |action, methods|
78
- next unless self.method_defined?(action)
79
-
80
- actions[action] = {
81
- path: "", methods: methods, type: :builtin, metadata: {label: self.label_for(action)}
82
- }
83
- end
84
-
85
- # Add extra actions.
86
- if extra_actions = self.try(:extra_actions)
87
- actions.merge!(RESTFramework::Utils.parse_extra_actions(extra_actions, controller: self))
88
- end
89
-
90
- return actions
91
- end
92
-
93
- # Collect member actions (including extra member actions) metadata for this controller.
94
- def member_actions_metadata
95
- actions = {}
96
-
97
- # Start with builtin actions.
98
- RESTFramework::BUILTIN_MEMBER_ACTIONS.each do |action, methods|
99
- next unless self.method_defined?(action)
100
-
101
- actions[action] = {
102
- path: "", methods: methods, type: :builtin, metadata: {label: self.label_for(action)}
103
- }
104
- end
105
-
106
- # Add extra actions.
107
- if extra_actions = self.try(:extra_member_actions)
108
- actions.merge!(RESTFramework::Utils.parse_extra_actions(extra_actions, controller: self))
109
- end
110
-
111
- return actions
112
- end
113
-
114
- def options_metadata
115
- return {
116
- title: self.get_title,
117
- description: self.description,
118
- renders: [
119
- "text/html",
120
- self.serialize_to_json ? "application/json" : nil,
121
- self.serialize_to_xml ? "application/xml" : nil,
122
- ].compact,
123
- actions: self.actions_metadata,
124
- member_actions: self.member_actions_metadata,
125
- }.compact
126
- end
127
-
128
61
  # Define any behavior to execute at the end of controller definition.
129
62
  # :nocov:
130
63
  def rrf_finalize
131
64
  if RESTFramework.config.freeze_config
132
- (self::RRF_BASE_CONFIG.keys + self::RRF_BASE_INSTANCE_CONFIG.keys).each { |k|
65
+ self::RRF_BASE_CONFIG.keys.each { |k|
133
66
  v = self.send(k)
134
67
  v.freeze if v.is_a?(Hash) || v.is_a?(Array)
135
68
  }
@@ -146,17 +79,13 @@ module RESTFramework::Mixins::BaseControllerMixin
146
79
  # By default, the layout should be set to `rest_framework`.
147
80
  base.layout("rest_framework")
148
81
 
149
- # Add class attributes (with defaults) unless they already exist.
82
+ # Add class attributes unless they already exist.
150
83
  RRF_BASE_CONFIG.each do |a, default|
151
84
  next if base.respond_to?(a)
152
85
 
86
+ # Don't leak class attributes to the instance to avoid conflicting with action methods.
153
87
  base.class_attribute(a, default: default, instance_accessor: false)
154
88
  end
155
- RRF_BASE_INSTANCE_CONFIG.each do |a, default|
156
- next if base.respond_to?(a)
157
-
158
- base.class_attribute(a, default: default)
159
- end
160
89
 
161
90
  # Alias `extra_actions` to `extra_collection_actions`.
162
91
  unless base.respond_to?(:extra_collection_actions)
@@ -204,26 +133,17 @@ module RESTFramework::Mixins::BaseControllerMixin
204
133
  end
205
134
  end
206
135
 
207
- def serializer_class
208
- # TODO: Compatibility; remove in 1.0.
209
- if klass = self.try(:get_serializer_class)
210
- return klass
211
- end
212
-
136
+ def get_serializer_class
213
137
  return self.class.serializer_class
214
138
  end
215
139
 
216
140
  # Serialize the given data using the `serializer_class`.
217
141
  def serialize(data, **kwargs)
218
- return RESTFramework::Utils.wrap_ams(self.serializer_class).new(
142
+ return RESTFramework::Utils.wrap_ams(self.get_serializer_class).new(
219
143
  data, controller: self, **kwargs
220
144
  ).serialize
221
145
  end
222
146
 
223
- def options_metadata
224
- return self.class.options_metadata
225
- end
226
-
227
147
  def rrf_error_handler(e)
228
148
  status = case e
229
149
  when ActiveRecord::RecordNotFound
@@ -232,7 +152,7 @@ module RESTFramework::Mixins::BaseControllerMixin
232
152
  400
233
153
  end
234
154
 
235
- return api_response(
155
+ render_api(
236
156
  {
237
157
  message: e.message,
238
158
  errors: e.try(:record).try(:errors),
@@ -242,19 +162,23 @@ module RESTFramework::Mixins::BaseControllerMixin
242
162
  )
243
163
  end
244
164
 
165
+ def route_groups
166
+ return @route_groups ||= RESTFramework::Utils.get_routes(Rails.application.routes, request)
167
+ end
168
+
245
169
  # Render a browsable API for `html` format, along with basic `json`/`xml` formats, and with
246
170
  # support or passing custom `kwargs` to the underlying `render` calls.
247
- def api_response(payload, **kwargs)
171
+ def render_api(payload, **kwargs)
248
172
  html_kwargs = kwargs.delete(:html_kwargs) || {}
249
173
  json_kwargs = kwargs.delete(:json_kwargs) || {}
250
174
  xml_kwargs = kwargs.delete(:xml_kwargs) || {}
251
175
 
252
176
  # Raise helpful error if payload is nil. Usually this happens when a record is not found (e.g.,
253
- # when passing something like `User.find_by(id: some_id)` to `api_response`). The caller should
177
+ # when passing something like `User.find_by(id: some_id)` to `render_api`). The caller should
254
178
  # actually be calling `find_by!` to raise ActiveRecord::RecordNotFound and allowing the REST
255
179
  # framework to catch this error and return an appropriate error response.
256
180
  if payload.nil?
257
- raise RESTFramework::NilPassedToAPIResponseError
181
+ raise RESTFramework::NilPassedToRenderAPIError
258
182
  end
259
183
 
260
184
  # If `payload` is an `ActiveRecord::Relation` or `ActiveRecord::Base`, then serialize it.
@@ -263,7 +187,7 @@ module RESTFramework::Mixins::BaseControllerMixin
263
187
  end
264
188
 
265
189
  # Do not use any adapters by default, if configured.
266
- if self.disable_adapters_by_default && !kwargs.key?(:adapter)
190
+ if self.class.disable_adapters_by_default && !kwargs.key?(:adapter)
267
191
  kwargs[:adapter] = nil
268
192
  end
269
193
 
@@ -295,9 +219,7 @@ module RESTFramework::Mixins::BaseControllerMixin
295
219
  end
296
220
  @title ||= self.class.get_title
297
221
  @description ||= self.class.description
298
- @route_props, @route_groups = RESTFramework::Utils.get_routes(
299
- Rails.application.routes, request
300
- )
222
+ self.route_groups
301
223
  begin
302
224
  render(**kwargs.merge(html_kwargs))
303
225
  rescue ActionView::MissingTemplate
@@ -318,10 +240,80 @@ module RESTFramework::Mixins::BaseControllerMixin
318
240
  end
319
241
 
320
242
  # TODO: Might make this the default render method in the future.
321
- alias_method :render_api, :api_response
243
+ alias_method :api_response, :render_api
244
+
245
+ def openapi_metadata
246
+ response_content_types = [
247
+ "text/html",
248
+ self.class.serialize_to_json ? "application/json" : nil,
249
+ self.class.serialize_to_xml ? "application/xml" : nil,
250
+ ].compact
251
+ request_content_types = [
252
+ "application/json",
253
+ "application/xml",
254
+ "application/x-www-form-urlencoded",
255
+ "multipart/form-data",
256
+ ].compact
257
+ routes = self.route_groups.values[0]
258
+ server = request.base_url + request.original_fullpath.gsub(/\?.*/, "")
259
+
260
+ return {
261
+ openapi: "3.1.1",
262
+ info: {
263
+ title: self.class.get_title,
264
+ description: self.class.description,
265
+ version: self.class.version.to_s,
266
+ }.compact,
267
+ servers: [{url: server}],
268
+ paths: routes.group_by { |r| r[:concat_path] }.map { |concat_path, routes|
269
+ [
270
+ concat_path.gsub(/:([0-9A-Za-z_-]+)/, "{\\1}"),
271
+ routes.map { |route|
272
+ metadata = route[:route].defaults[:metadata] || {}
273
+ summary = metadata[:label].presence || self.class.label_for(route[:action])
274
+ description = metadata[:description].presence
275
+ remaining_metadata = metadata.except(:label, :description).presence
276
+
277
+ [
278
+ route[:verb].downcase,
279
+ {
280
+ summary: summary,
281
+ description: description,
282
+ responses: {
283
+ 200 => {
284
+ content: response_content_types.map { |ct|
285
+ [ct, {}]
286
+ }.to_h,
287
+ description: "",
288
+ },
289
+ },
290
+ requestBody: route[:verb].in?(["GET", "DELETE", "OPTIONS", "TRACE"]) ? nil : {
291
+ content: request_content_types.map { |ct|
292
+ [ct, {}]
293
+ }.to_h,
294
+ },
295
+ "x-rrf-metadata": remaining_metadata,
296
+ }.compact,
297
+ ]
298
+ }.to_h.merge(
299
+ {
300
+ parameters: routes.first[:route].required_parts.map { |p|
301
+ {
302
+ name: p,
303
+ in: "path",
304
+ required: true,
305
+ schema: {type: "integer"},
306
+ }
307
+ },
308
+ },
309
+ ),
310
+ ]
311
+ }.to_h,
312
+ }.compact
313
+ end
322
314
 
323
315
  def options
324
- return api_response(self.options_metadata)
316
+ render_api(self.openapi_metadata)
325
317
  end
326
318
  end
327
319
 
@@ -4,17 +4,17 @@ require_relative "model_controller_mixin"
4
4
  # the existing `create` action to support bulk creation.
5
5
  module RESTFramework::Mixins::BulkCreateModelMixin
6
6
  # While bulk update/destroy are obvious because they create new router endpoints, bulk create
7
- # overloads the existing collection `POST` endpoint, so we add a special key to the options
7
+ # overloads the existing collection `POST` endpoint, so we add a special key to the OpenAPI
8
8
  # metadata to indicate bulk create is supported.
9
- def options_metadata
10
- return super.merge({bulk_create: true})
9
+ def openapi_metadata
10
+ return super.merge({"x-rrf-bulk-create": true})
11
11
  end
12
12
 
13
13
  def create
14
14
  if params[:_json].is_a?(Array)
15
15
  records = self.create_all!
16
16
  serialized_records = self.bulk_serialize(records)
17
- return api_response(serialized_records)
17
+ return render_api(serialized_records)
18
18
  end
19
19
 
20
20
  return super
@@ -36,7 +36,7 @@ module RESTFramework::Mixins::BulkUpdateModelMixin
36
36
  def update_all
37
37
  records = self.update_all!
38
38
  serialized_records = self.bulk_serialize(records)
39
- return api_response(serialized_records)
39
+ render_api(serialized_records)
40
40
  end
41
41
 
42
42
  # Perform the `update` call and return the collection of (possibly) updated records.
@@ -62,10 +62,10 @@ module RESTFramework::Mixins::BulkDestroyModelMixin
62
62
  if params[:_json].is_a?(Array)
63
63
  records = self.destroy_all!
64
64
  serialized_records = self.bulk_serialize(records)
65
- return api_response(serialized_records)
65
+ return render_api(serialized_records)
66
66
  end
67
67
 
68
- return api_response(
68
+ render_api(
69
69
  {message: "Bulk destroy requires an array of primary keys as input."},
70
70
  status: 400,
71
71
  )