deep_unrest 0.1.35 → 0.1.36

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: 5095cf1fc9c37e899a5dd31faf7c91f4a32abca7fd03eefccb7564d515df60d4
4
- data.tar.gz: 9a1e1acb9f55ba94e5c706c44cce09e0ecd25dcca2763d9761954eca09e2f603
3
+ metadata.gz: 49fe70a86a480793222a007af3de245afc7d2bba795d3e42ba0f2e74a5791c3b
4
+ data.tar.gz: 64ac3b1ffcb724b5bd5b8c15c37143da18241685cc36f76b3a7e63d9c568b461
5
5
  SHA512:
6
- metadata.gz: a070c77b4b55d24535fc0047f30ca8f8663a232eb02dc7e226ea10458fbfb3ada8a002ad261118286f1b403de71b790b8b9cfec95334eae0beb012870dd4210e
7
- data.tar.gz: e3af192ce56ed378e9ce197f104065079e21fd5a7ea3ae097d2b2d2298893f6360636d7db850e48c5ee78d66ec7fe7cd0d2585c100e3cad2d9ed69b4ad7d02c3
6
+ metadata.gz: 5b824d05e3a002abd22057c337c6b5b948c9c54c92c148873bf4239aa47a5319bcf68dd212d9dbb3dc9093addda891a3e2bc17329677e8ae05fee5c9a245b451
7
+ data.tar.gz: eb20d4d68c51fb7bb383f1e34996841195f1c44a880e2348f839c39968048032a1b2bccabfe3d1738af5e31a92f3e11a5ac84f39371182ac8d1116e1dc02ad62
@@ -4,8 +4,11 @@ module DeepUnrest
4
4
  class ApplicationController < ActionController::API
5
5
  include DeepUnrest.authentication_concern
6
6
 
7
+ around_action :allow_nested_arrays, only: :update
8
+
7
9
  @@temp_ids = {}
8
10
  @@destroyed_entities = []
11
+ @@changed_entities = []
9
12
 
10
13
  def context
11
14
  { current_user: current_user }
@@ -38,7 +41,32 @@ module DeepUnrest
38
41
  instance_eval &DeepUnrest.before_read if DeepUnrest.before_read
39
42
 
40
43
  results = DeepUnrest.perform_read(context, data, current_user)
41
- render json: results, status: 200
44
+ render json: results, status: :ok
45
+ rescue DeepUnrest::Unauthorized => err
46
+ render json: err.message, status: :forbidden
47
+ rescue DeepUnrest::UnpermittedParams => err
48
+ render json: err.message, status: :method_not_allowed
49
+ end
50
+
51
+ def write
52
+ repaired_params = params[:data]
53
+ data = repaired_params[:data]
54
+ context = repaired_params[:context] || {}
55
+ context[:uuid] = request.uuid
56
+ context[:current_user] = current_user
57
+
58
+ instance_eval &DeepUnrest.before_update if DeepUnrest.before_update
59
+
60
+ results = DeepUnrest.perform_write(context, data, current_user)
61
+ render json: results, status: :ok
62
+ rescue DeepUnrest::Unauthorized => err
63
+ render json: err.message, status: :forbidden
64
+ rescue DeepUnrest::UnpermittedParams => err
65
+ render json: err.message, status: :method_not_allowed
66
+ rescue DeepUnrest::Conflict => err
67
+ render json: err.message, status: :conflict
68
+ ensure
69
+ @@temp_ids.delete(request.uuid)
42
70
  end
43
71
 
44
72
  def update
@@ -46,23 +74,26 @@ module DeepUnrest
46
74
  context = allowed_write_params[:data][:context] || {}
47
75
  context[:uuid] = request.uuid
48
76
  context[:current_user] = current_user
49
- data = repair_nested_params(allowed_write_params)[:data][:data]
77
+ data = repair_nested_params(params)[:data][:data]
50
78
 
51
79
  instance_eval &DeepUnrest.before_update if DeepUnrest.before_update
52
80
 
53
- results = DeepUnrest.perform_update(context, data)
81
+ results = DeepUnrest.perform_update(context, data, current_user)
54
82
  resp = { destroyed: results[:destroyed],
83
+ changed: results[:changed],
55
84
  tempIds: results[:temp_ids] }
56
85
  resp[:redirect] = results[:redirect_regex].call(redirect) if redirect
57
- render json: resp, status: 200
86
+ render json: resp, status: :ok
58
87
  rescue DeepUnrest::Unauthorized => err
59
- render json: err.message, status: 403
88
+ render json: err.message, status: :forbidden
60
89
  rescue DeepUnrest::UnpermittedParams => err
61
- render json: err.message, status: 405
90
+ render json: err.message, status: :method_not_allowed
62
91
  rescue DeepUnrest::Conflict => err
63
- render json: err.message, status: 409
92
+ render json: err.message, status: :conflict
64
93
  ensure
65
94
  @@temp_ids.delete(request.uuid)
95
+ @@destroyed_entities.clear
96
+ @@changed_entities.clear
66
97
  end
67
98
 
68
99
  def current_user
@@ -76,5 +107,11 @@ module DeepUnrest
76
107
  :errorPath,
77
108
  { attributes: {} }]])
78
109
  end
110
+
111
+ def allow_nested_arrays
112
+ ::ActionController::Parameters::PERMITTED_SCALAR_TYPES << Array
113
+ yield
114
+ ::ActionController::Parameters::PERMITTED_SCALAR_TYPES - [Array]
115
+ end
79
116
  end
80
117
  end
data/config/routes.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  DeepUnrest::Engine.routes.draw do
2
2
  patch 'update', to: 'application#update'
3
3
  get 'read', to: 'application#read'
4
+ patch 'write', to: 'application#write'
4
5
  end
@@ -31,12 +31,17 @@ module DeepUnrest
31
31
  def self.get_entity_authorization(scope, user)
32
32
  if %i[create update_all index destroy_all].include?(scope[:scope_type])
33
33
  target = scope[:klass]
34
- else
34
+ elsif scope[:scope]
35
+ # TODO: deprecate this part of the clause following write endpoint refactor
35
36
  target = scope[:scope][:base].send(scope[:scope][:method],
36
37
  *scope[:scope][:arguments])
38
+ else
39
+ target = scope[:klass].find(scope[:query][:id])
37
40
  end
38
41
 
39
42
  Pundit.policy!(user, target).send(get_policy_name(scope[:scope_type]))
43
+ rescue Pundit::NotDefinedError
44
+ false
40
45
  end
41
46
 
42
47
  def self.authorize(scopes, user)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module DeepUnrest
2
4
  module Concerns
3
5
  module MapTempIds
@@ -5,9 +7,15 @@ module DeepUnrest
5
7
 
6
8
  included do
7
9
  attr_accessor :deep_unrest_context
10
+ attr_accessor :deep_unrest_query_uuid
8
11
  attr_accessor :deep_unrest_temp_id
9
12
  after_create :map_temp_id
10
13
  after_destroy :track_destruction
14
+ after_save :track_changes
15
+ end
16
+
17
+ def pk
18
+ send self.class.primary_key
11
19
  end
12
20
 
13
21
  def map_temp_id
@@ -15,7 +23,7 @@ module DeepUnrest
15
23
  '@@temp_ids'
16
24
  )
17
25
  return unless temp_id_map && @deep_unrest_temp_id
18
- temp_id_map[@deep_unrest_context][@deep_unrest_temp_id] = id
26
+ temp_id_map[@deep_unrest_context][@deep_unrest_temp_id] = pk
19
27
  end
20
28
 
21
29
  # the client needs to know which items were destroyed so it can clean up
@@ -27,10 +35,31 @@ module DeepUnrest
27
35
  return unless destroyed
28
36
  destroyed << {
29
37
  type: self.class.to_s.pluralize.camelize(:lower),
30
- id: id,
38
+ id: pk,
31
39
  destroyed: true
32
40
  }
33
41
  end
42
+
43
+ # the client needs to know which items were charged so it can keep its
44
+ # local sync in store with the db
45
+ def track_changes
46
+ changed = DeepUnrest::ApplicationController.class_variable_get(
47
+ '@@changed_entities'
48
+ )
49
+ return unless changed && saved_changes?
50
+ changed << {
51
+ klass: self.class,
52
+ id: pk,
53
+ attributes: attribute_diff,
54
+ query_uuid: @deep_unrest_query_uuid
55
+ }
56
+ end
57
+
58
+ def attribute_diff
59
+ saved_changes.each_with_object({}) do |(attr_name, (_old, val)), diff|
60
+ diff[attr_name] = val
61
+ end
62
+ end
34
63
  end
35
64
  end
36
65
  end
@@ -2,10 +2,6 @@
2
2
 
3
3
  module DeepUnrest
4
4
  module Read
5
- # def self.map_included(params, addr)
6
- # params[:include].map { |k, v| create_read_mappings({ "#{k}": v }, [*addr, :include]) }
7
- # end
8
-
9
5
  def self.create_read_mappings(params, addr = [])
10
6
  return unless params
11
7
  params.map do |k, v|
@@ -19,7 +15,7 @@ module DeepUnrest
19
15
  addr: resource_addr,
20
16
  key: k.camelize(:lower),
21
17
  uuid: uuid,
22
- query: deep_underscore_keys(v) },
18
+ query: DeepUnrest.deep_underscore_keys(v) },
23
19
  *create_read_mappings(v[:include], [*resource_addr, :include])]
24
20
  end.flatten.compact
25
21
  end
@@ -28,31 +24,9 @@ module DeepUnrest
28
24
  str.pluralize == str && str.singularize != str
29
25
  end
30
26
 
31
- def self.serialize_result(ctx, item)
32
- JSONAPI::ResourceSerializer.new(item[:resource],
33
- fields: {
34
- "#{item[:key].pluralize}": item[:query][:fields].map(&:underscore).map(&:to_sym)
35
- }).serialize_to_hash(item[:resource].new(item[:record], ctx))[:data]
36
- end
37
-
38
27
  def self.serialize_results(ctx, data)
39
28
  data.each do |item|
40
- item[:serialized_result] = serialize_result(ctx, item)
41
- end
42
- end
43
-
44
- def self.deep_underscore_keys(query)
45
- query.deep_transform_keys! do |key|
46
- k = begin
47
- key.to_s.underscore
48
- rescue StandardError
49
- key
50
- end
51
- begin
52
- k.to_sym
53
- rescue StandardError
54
- key
55
- end
29
+ item[:serialized_result] = DeepUnrest.serialize_result(ctx, item)
56
30
  end
57
31
  end
58
32
 
@@ -184,53 +158,24 @@ module DeepUnrest
184
158
  [included, meta]
185
159
  end
186
160
 
187
- def self.set_attr(hash, path, val, cursor = nil)
188
- cursor ||= hash
189
- key = path.shift
190
-
191
- if path.empty?
192
- case cursor
193
- when Array
194
- cursor << val
195
- when Hash
196
- cursor[key] = val
197
- end
198
- return hash
199
- end
200
-
201
- next_cursor = case key
202
- when /\[\]$/
203
- cursor[key.gsub('[]', '')] ||= []
204
- else
205
- cursor[key] ||= {}
206
- end
207
-
208
- set_attr(hash, path, val, next_cursor)
209
- end
210
161
 
211
162
  def self.format_response(mappings)
212
163
  response = {}
213
164
  mappings.each do |mapping|
214
- set_attr(response, mapping[:addr], mapping[:serialized_result])
165
+ DeepUnrest.set_attr(response, mapping[:addr], mapping[:serialized_result])
215
166
  end
216
167
  response
217
168
  end
218
169
 
219
- def self.collect_authorized_scopes(mappings, user)
220
- mappings.each do |mapping|
221
- mapping[:scope] = DeepUnrest.authorization_strategy.get_authorized_scope(user, mapping[:klass])
222
- end
223
- end
224
-
225
170
  def self.read(ctx, params, user)
226
171
  # create mappings for assembly / disassembly
227
172
  mappings = create_read_mappings(params.to_unsafe_h)
228
173
 
229
174
  # authorize user for requested scope(s)
230
- DeepUnrest.authorization_strategy.authorize(mappings, user).flatten
175
+ DeepUnrest.authorization_strategy.authorize(mappings, user)
231
176
 
232
177
  # collect authorized scopes
233
- collect_authorized_scopes(mappings, user)
178
+ DeepUnrest.collect_authorized_scopes(mappings, user)
234
179
 
235
180
  # read data
236
181
  data, meta = execute_queries(mappings)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeepUnrest
4
- VERSION = '0.1.35'
4
+ VERSION = '0.1.36'
5
5
  end
@@ -0,0 +1,210 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepUnrest
4
+ module Write
5
+ def self.get_scope_type(item)
6
+ return :destroy if item[:destroy]
7
+ return :show if item[:readOnly] || item[:attributes].blank?
8
+ return :create if item[:id] && DeepUnrest.temp_id?(item[:id].to_s)
9
+ :update
10
+ end
11
+
12
+ def self.create_write_mappings(params, addr = [])
13
+ return unless params
14
+ params.map do |k, v|
15
+ resource_addr = [*addr, k]
16
+ uuid = SecureRandom.uuid
17
+ v[:uuid] = uuid
18
+ [{ klass: k.singularize.classify.constantize,
19
+ policy: "#{k.singularize.classify}Policy".constantize,
20
+ resource: "#{k.singularize.classify}Resource".constantize,
21
+ scope_type: get_scope_type(v),
22
+ addr: resource_addr,
23
+ key: k.camelize(:lower),
24
+ uuid: uuid,
25
+ query: DeepUnrest.deep_underscore_keys(v) },
26
+ *create_write_mappings(v[:included], [*resource_addr, :include])]
27
+ end.flatten.compact
28
+ end
29
+
30
+ def self.append_ar_paths(mappings)
31
+ mappings.each do |item|
32
+ item[:ar_addr] = []
33
+ item[:addr].each_with_index do |segment, i|
34
+ next if segment == :include
35
+ item[:ar_addr] << if item[:addr][i - 1] == :include
36
+ "#{segment}_attributes".to_sym
37
+ else
38
+ segment
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ def self.authorize_attributes(mappings, ctx)
45
+ unauthorized_items = []
46
+
47
+ mappings.reject { |m| m[:scope_type] == :show }
48
+ .reject { |m| m[:destroy] }
49
+ .each do |item|
50
+ attributes = item.dig(:query, :attributes) || {}
51
+ resource = item[:resource]
52
+ p = JSONAPI::RequestParser.new
53
+ p.resource_klass = resource
54
+ opts = if item[:scope_type] == :create
55
+ resource.creatable_fields(ctx)
56
+ else
57
+ resource.updatable_fields(ctx)
58
+ end
59
+
60
+ p.parse_params({ attributes: attributes }, opts)[:attributes]
61
+ rescue JSONAPI::Exceptions::ParameterNotAllowed
62
+ unpermitted_keys = attributes.keys.map(&:to_sym) - opts
63
+ item[:errors] = unpermitted_keys.each_with_object({}) do |attr_key, memo|
64
+ memo[attr_key] = 'Unpermitted parameter'
65
+ end
66
+ unauthorized_items << item
67
+ end
68
+
69
+ return if unauthorized_items.blank?
70
+
71
+ msg = serialize_errors(unauthorized_items)
72
+ raise DeepUnrest::UnpermittedParams, msg
73
+ end
74
+
75
+ def self.build_mutation_bodies(mappings)
76
+ mappings.reject { |m| m[:scope_type] == :show }
77
+ .each_with_object({}) do |item, memo|
78
+ # TODO: use pkey instead of "id"
79
+ next_attrs = item.dig(:query, :attributes || {})
80
+ .deep_symbolize_keys
81
+ update_body = { id: item.dig(:query, :id),
82
+ deep_unrest_query_uuid: item.dig(:query, :uuid),
83
+ **next_attrs }
84
+ update_body[:_destroy] = true if item[:scope_type] == :destroy
85
+ DeepUnrest.set_attr(memo, item[:ar_addr].clone, update_body)
86
+
87
+ item[:mutate] = memo.fetch(*item[:ar_addr]) if item[:ar_addr].size == 1
88
+ end
89
+ end
90
+
91
+ def self.execute_queries(mappings, context)
92
+ ActiveRecord::Base.transaction do
93
+ mappings.select { |m| m[:mutate] }.map do |item|
94
+ record = case item[:scope_type]
95
+ when :update
96
+ model = item[:klass].find(item.dig(:query, :id))
97
+ model.assign_attributes(item[:mutate])
98
+ resource = item[:resource].new(model, context)
99
+ resource.run_callbacks :save do
100
+ resource.run_callbacks :update do
101
+ model.save
102
+ model
103
+ end
104
+ end
105
+ when :create
106
+ model = item[:klass].new(item[:mutate])
107
+ resource = item[:resource].new(model, context)
108
+ resource.run_callbacks :save do
109
+ resource.run_callbacks :create do
110
+ resource._model.save
111
+ resource._model
112
+ end
113
+ end
114
+ when :destroy
115
+ model = item[:klass].find(id)
116
+ resource = item[:resource].new(model, context)
117
+ resource.run_callbacks :remove do
118
+ item[:klass].destroy(id)
119
+ end
120
+ end
121
+
122
+ result = { record: record }
123
+ if item[:temp_id]
124
+ result[:temp_ids] = {}
125
+ result[:temp_ids][item[:temp_id]] = record.id
126
+ end
127
+ result
128
+ end
129
+ end
130
+ end
131
+
132
+ def self.serialize_changes(ctx, mappings, changed)
133
+ changed.select { |c| c[:query_uuid] }
134
+ .each_with_object({}) do |c, memo|
135
+ mapping = mappings.find { |m| m.dig(:query, :uuid) == c[:query_uuid] }
136
+ mapping[:query][:fields] = c[:attributes].keys
137
+ mapping[:record] = c[:klass].new(id: c[:id])
138
+ mapping[:record].assign_attributes(c[:attributes])
139
+ result = DeepUnrest.serialize_result(ctx, mapping)
140
+ DeepUnrest.set_attr(memo, mapping[:addr], result)
141
+ end
142
+ end
143
+
144
+ def self.serialize_errors(mappings)
145
+ { errors: mappings.each_with_object({}) do |item, memo|
146
+ err = {
147
+ id: item.dig(:query, :id),
148
+ type: item.dig(:query, :type),
149
+ attributes: item[:errors,]
150
+ }
151
+ DeepUnrest.set_attr(memo, [*item[:addr]], err)
152
+ end }.to_json
153
+ end
154
+
155
+ def self.write(ctx, params, user)
156
+ temp_id_map = DeepUnrest::ApplicationController.class_variable_get(
157
+ '@@temp_ids'
158
+ )
159
+
160
+ # create mappings for assembly / disassembly
161
+ mappings = create_write_mappings(params.to_unsafe_h)
162
+
163
+ # authorize user for requested scope(s)
164
+ DeepUnrest.authorization_strategy.authorize(mappings, user)
165
+
166
+ authorize_attributes(mappings, ctx)
167
+
168
+ # collect authorized scopes
169
+ # DeepUnrest.collect_authorized_scopes(mappings, user)
170
+ append_ar_paths(mappings)
171
+
172
+ # bulid update arguments
173
+ build_mutation_bodies(mappings)
174
+
175
+ # convert temp_ids from ids to non-activerecord attributes
176
+ DeepUnrest.convert_temp_ids!(ctx[:uuid], mappings)
177
+
178
+ # save data, run callbaks
179
+ results = execute_queries(mappings, ctx)
180
+
181
+ # check results for errors
182
+ errors = results.map { |res| DeepUnrest.format_error_keys(res) }
183
+ .compact
184
+ .reject(&:empty?)
185
+ .compact
186
+
187
+ if errors.empty?
188
+ destroyed = DeepUnrest::ApplicationController.class_variable_get(
189
+ '@@destroyed_entities'
190
+ )
191
+
192
+ changed = DeepUnrest::ApplicationController.class_variable_get(
193
+ '@@changed_entities'
194
+ )
195
+
196
+ return {
197
+ temp_ids: temp_id_map[ctx[:uuid]],
198
+ destroyed: destroyed,
199
+ changed: serialize_changes(ctx, mappings, changed)
200
+ }
201
+ end
202
+
203
+ # map errors to their sources
204
+ formatted_errors = { errors: map_errors_to_param_keys(scopes, errors) }
205
+
206
+ # raise error if there are any errors
207
+ raise DeepUnrest::Conflict, formatted_errors.to_json
208
+ end
209
+ end
210
+ end
data/lib/deep_unrest.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'deep_unrest/engine'
4
4
  require 'deep_unrest/read'
5
+ require 'deep_unrest/write'
5
6
 
6
7
  # workaronud for rails bug with association indices.
7
8
  # see https://github.com/rails/rails/pull/24728
@@ -15,7 +16,7 @@ module ActiveRecord
15
16
  new_record,
16
17
  autosave)
17
18
  if new_record || autosave
18
- association && association.target
19
+ association&.target
19
20
  else
20
21
  association.target.find_all(&:new_record?)
21
22
  end
@@ -167,7 +168,7 @@ module DeepUnrest
167
168
  end
168
169
 
169
170
  p.parse_params({ attributes: attributes }, opts)[:attributes]
170
- rescue JSONAPI::Exceptions::ParametersNotAllowed
171
+ rescue JSONAPI::Exceptions::ParameterNotAllowed
171
172
  unpermitted_keys = attributes.keys.map(&:to_sym) - opts
172
173
  msg = "Attributes #{unpermitted_keys} of #{type.classify} not allowed"
173
174
  msg += " to #{user.class} with id '#{user.id}'" if user
@@ -213,6 +214,7 @@ module DeepUnrest
213
214
 
214
215
  def self.parse_id(id_str)
215
216
  return false if id_str.nil?
217
+ return id_str if id_str.is_a? Integer
216
218
  id_match = id_str.match(/^\.?(?<id>[\w\-]+)$/)
217
219
  id_match && id_match[:id]
218
220
  end
@@ -368,10 +370,34 @@ module DeepUnrest
368
370
  (non_dupes + merged).flatten
369
371
  end
370
372
 
373
+ def self.set_attr(hash, path, val, cursor = nil)
374
+ cursor ||= hash
375
+ key = path.shift
376
+
377
+ if path.empty?
378
+ case cursor
379
+ when Array
380
+ cursor << val
381
+ when Hash
382
+ cursor[key] = val
383
+ end
384
+ return hash
385
+ end
386
+
387
+ next_cursor = case key
388
+ when /\[\]$/
389
+ cursor[key.gsub('[]', '')] ||= []
390
+ else
391
+ cursor[key] ||= {}
392
+ end
393
+
394
+ set_attr(hash, path, val, next_cursor)
395
+ end
396
+
371
397
  def self.build_mutation_body(ops, scopes, user)
372
398
  err_path_memo = {}
373
399
  ops.each_with_object(HashWithIndifferentAccess.new({})) do |op, memo|
374
- memo.deep_merge!(build_mutation_fragment(op, scopes, user, err_path_memo)) do |key, a, b|
400
+ memo.deep_merge!(build_mutation_fragment(op, scopes, user, err_path_memo)) do |_key, a, b|
375
401
  if a.is_a? Array
376
402
  combine_arrays(a, b)
377
403
  else
@@ -516,7 +542,43 @@ module DeepUnrest
516
542
  DeepUnrest::Read.read(ctx, params, user)
517
543
  end
518
544
 
519
- def self.perform_update(ctx, params)
545
+ def self.perform_write(ctx, params, user)
546
+ DeepUnrest::Write.write(ctx, params, user)
547
+ end
548
+
549
+ def self.serialize_changes(diffs, user)
550
+ ctx = { current_user: user }
551
+ diffs.each do |diff|
552
+ diff[:resource] = diff[:attributes]
553
+ pk = diff[:klass].primary_key
554
+ diff[:resource][pk] = diff[:id]
555
+ diff[:model] = diff[:klass].new(diff[:resource])
556
+ end
557
+
558
+ allowed_models = diffs.select do |diff|
559
+ scope = DeepUnrest.authorization_strategy
560
+ .get_authorized_scope(user,
561
+ diff[:klass])
562
+ scope.exists?(diff[:id])
563
+ rescue NameError
564
+ false
565
+ end
566
+
567
+ resources = allowed_models.map do |diff|
568
+ resource_klass = get_resource(diff[:klass].to_s)
569
+ fields = {}
570
+ keys = diff[:resource].keys.map(&:to_sym)
571
+ fields[to_assoc(diff[:klass].to_s.pluralize)] = keys
572
+
573
+ JSONAPI::ResourceSerializer.new(
574
+ resource_klass,
575
+ fields: fields
576
+ ).serialize_to_hash(resource_klass.new(diff[:model], ctx))[:data]
577
+ end
578
+ resources.select { |item| item.dig('attributes') }.compact
579
+ end
580
+
581
+ def self.perform_update(ctx, params, user)
520
582
  temp_id_map = DeepUnrest::ApplicationController.class_variable_get(
521
583
  '@@temp_ids'
522
584
  )
@@ -556,10 +618,18 @@ module DeepUnrest
556
618
  destroyed = DeepUnrest::ApplicationController.class_variable_get(
557
619
  '@@destroyed_entities'
558
620
  )
621
+
622
+ changed = DeepUnrest::ApplicationController.class_variable_get(
623
+ '@@changed_entities'
624
+ )
625
+
626
+ diff = serialize_changes(changed, user)
627
+
559
628
  return {
560
629
  redirect_regex: build_redirect_regex(temp_id_map[uuid]),
561
630
  temp_ids: temp_id_map[uuid],
562
- destroyed: destroyed
631
+ destroyed: destroyed,
632
+ changed: diff
563
633
  }
564
634
  end
565
635
 
@@ -569,4 +639,38 @@ module DeepUnrest
569
639
  # raise error if there are any errors
570
640
  raise Conflict, formatted_errors.to_json unless formatted_errors.empty?
571
641
  end
642
+
643
+ ### SHARED ###
644
+ def self.deep_underscore_keys(query)
645
+ query.deep_transform_keys! do |key|
646
+ k = begin
647
+ key.to_s.underscore
648
+ rescue StandardError
649
+ key
650
+ end
651
+ begin
652
+ k.to_sym
653
+ rescue StandardError
654
+ key
655
+ end
656
+ end
657
+ end
658
+
659
+ def self.collect_authorized_scopes(mappings, user)
660
+ mappings.each do |mapping|
661
+ mapping[:scope] = DeepUnrest.authorization_strategy.get_authorized_scope(user, mapping[:klass])
662
+ end
663
+ end
664
+
665
+ def self.serialize_result(ctx, item)
666
+ # item[:resource].exclude_links :none
667
+ resource_instance = item[:resource].new(item[:record], ctx)
668
+ JSONAPI::ResourceSerializer.new(
669
+ item[:resource],
670
+ fields: {
671
+ "#{item[:key].pluralize}": item[:query][:fields].map(&:underscore)
672
+ .map(&:to_sym)
673
+ }
674
+ ).serialize_to_hash(resource_instance)[:data]
675
+ end
572
676
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deep_unrest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.35
4
+ version: 0.1.36
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lynn Hurley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-02 00:00:00.000000000 Z
11
+ date: 2019-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -151,6 +151,7 @@ files:
151
151
  - lib/deep_unrest/paginators/basic.rb
152
152
  - lib/deep_unrest/read.rb
153
153
  - lib/deep_unrest/version.rb
154
+ - lib/deep_unrest/write.rb
154
155
  - lib/tasks/deep_unrest_tasks.rake
155
156
  homepage: https://github.com/graveflex/deep_unrest
156
157
  licenses: