deep_unrest 0.1.35 → 0.1.36

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: 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: