rest_framework 0.10.0 → 0.11.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 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
  )