deep_unrest 0.1.30 → 0.1.31

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
- SHA1:
3
- metadata.gz: 3395eda13825d0ecdfe337b515741edc5b5e25a6
4
- data.tar.gz: 0fec2b4c1003af2d6ed883fc1d0398bd457eb875
2
+ SHA256:
3
+ metadata.gz: 0c5e259eeb18f8ac855b773aec2b8eff84771ccf6f71df929d41511c6c4a17a8
4
+ data.tar.gz: 0d56e539b798f92749bf990ba555bba0d6b2a6c3f886333a772a4c82f7a24f86
5
5
  SHA512:
6
- metadata.gz: 441ebd1c53d3717286b4b30ef2ac201f5470d3d9f718a5757e3dae79b4152c1232b7c1d919f0ea9b554272137eea75c7d5454a7fc1dd2c01db355415c184f318
7
- data.tar.gz: 8b4066e6233a7dd6922e5e9aa55bd2f753d9ceae53714b31b0c3fcf87c1087cc26e95d787d52ced89e3be1c86622fee7ee0304d4c157c8fc4c6c1051b36ba1b4
6
+ metadata.gz: e7b8688e920dc630b4f6621a79bdbbef6a8fa6b60921a750136e11a22bf4a075567c20d80fec13f232b1515b5e13c08db974ecf6d244900fb45d37277455f21b
7
+ data.tar.gz: ae706986003c7a10bc541a32bf7f23e763dd63a4fde85bfea89e7eae33824efa0f03478ed7b870c7fa22a7c1056a2f45d8677935606407806a81288fa56fbd88
@@ -28,10 +28,29 @@ module DeepUnrest
28
28
  end
29
29
  end
30
30
 
31
+ def read
32
+ repaired_params = params[:data]
33
+ data = repaired_params[:data]
34
+ context = repaired_params[:context] || {}
35
+ context[:uuid] = request.uuid
36
+ context[:current_user] = current_user
37
+
38
+ instance_eval &DeepUnrest.before_read if DeepUnrest.before_read
39
+
40
+ results = DeepUnrest.perform_read(context, data, current_user)
41
+ render json: results, status: 200
42
+ end
43
+
31
44
  def update
32
- redirect = allowed_params[:data][:redirect]
33
- data = repair_nested_params(allowed_params)[:data][:data]
34
- results = DeepUnrest.perform_update(request.uuid, data, current_user)
45
+ redirect = allowed_write_params[:data][:redirect]
46
+ context = allowed_write_params[:data][:context] || {}
47
+ context[:uuid] = request.uuid
48
+ context[:current_user] = current_user
49
+ data = repair_nested_params(allowed_write_params)[:data][:data]
50
+
51
+ instance_eval &DeepUnrest.before_update if DeepUnrest.before_update
52
+
53
+ results = DeepUnrest.perform_update(context, data)
35
54
  resp = { destroyed: results[:destroyed],
36
55
  tempIds: results[:temp_ids] }
37
56
  resp[:redirect] = results[:redirect_regex].call(redirect) if redirect
@@ -50,7 +69,7 @@ module DeepUnrest
50
69
  instance_eval &DeepUnrest.get_user
51
70
  end
52
71
 
53
- def allowed_params
72
+ def allowed_write_params
54
73
  params.permit(data: [:redirect,
55
74
  data: [:destroy,
56
75
  :path,
data/config/routes.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  DeepUnrest::Engine.routes.draw do
2
2
  patch 'update', to: 'application#update'
3
+ get 'read', to: 'application#read'
3
4
  end
@@ -1,6 +1,7 @@
1
1
  require 'deep_unrest/authorization/base_strategy'
2
2
  require 'deep_unrest/authorization/none_strategy'
3
3
  require 'deep_unrest/authorization/pundit_strategy'
4
+ require 'deep_unrest/paginators/basic'
4
5
  require 'deep_unrest/concerns/null_concern'
5
6
  require 'deep_unrest/concerns/map_temp_ids'
6
7
 
@@ -10,12 +11,20 @@ module DeepUnrest
10
11
  end
11
12
 
12
13
  mattr_accessor :authorization_strategy
14
+ mattr_accessor :pagination_strategy
15
+ mattr_accessor :page_size
13
16
  mattr_accessor :authentication_concern
14
17
  mattr_accessor :get_user
18
+ mattr_accessor :before_read
19
+ mattr_accessor :before_update
15
20
 
16
21
  self.authorization_strategy = DeepUnrest::Authorization::PunditStrategy
22
+ self.pagination_strategy = DeepUnrest::Paginators::Basic
23
+ self.page_size = 25
17
24
  self.authentication_concern = DeepUnrest::Concerns::NullConcern
18
25
  self.get_user = proc { current_user }
26
+ self.before_read = nil
27
+ self.before_update = nil
19
28
 
20
29
  def self.configure(&_block)
21
30
  yield self
@@ -0,0 +1,11 @@
1
+ module DeepUnrest
2
+ module Paginators
3
+ module Basic
4
+ def self.get_page(item, results)
5
+ page = [item[:query][:page] || 1].max
6
+ page_size = item[:query][:pageSize] || DeepUnrest.page_size
7
+ results.limit(page_size).offset(page - 1)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,245 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DeepUnrest
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
+ def self.create_read_mappings(params, addr = [])
10
+ return unless params
11
+ params.map do |k, v|
12
+ resource_addr = [*addr, k]
13
+ uuid = SecureRandom.uuid
14
+ v[:uuid] = uuid
15
+ [{ klass: k.singularize.classify.constantize,
16
+ policy: "#{k.singularize.classify}Policy".constantize,
17
+ resource: "#{k.singularize.classify}Resource".constantize,
18
+ scope_type: :index,
19
+ addr: resource_addr,
20
+ key: k.camelize(:lower),
21
+ uuid: uuid,
22
+ query: deep_underscore_keys(v) },
23
+ *create_read_mappings(v[:include], [*resource_addr, :include])]
24
+ end.flatten.compact
25
+ end
26
+
27
+ def self.plural?(str)
28
+ str.pluralize == str && str.singularize != str
29
+ end
30
+
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
+ def self.serialize_results(ctx, data)
39
+ 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
56
+ end
57
+ end
58
+
59
+ def self.resolve_conditions(query, parent_context)
60
+ if query.is_a? Array
61
+ query.each { |item| resolve_conditions(item, parent_context) }
62
+ elsif query.is_a? Hash
63
+ query.each do |k, v|
64
+ next unless v.is_a? Hash
65
+ if v[:from_context]
66
+ name, attr = v[:from_context].split('.')
67
+ next unless parent_context[name]
68
+ query[k] = parent_context[name].send(attr.underscore)
69
+ else
70
+ resolve_conditions(v, parent_context)
71
+ end
72
+ end
73
+ end
74
+ query
75
+ end
76
+
77
+ def self.recurse_included_queries(item, mappings, parent_context, included, meta, addr)
78
+ return unless item[:query].key?(:include)
79
+ item[:query][:include].each do |_k, v|
80
+ next_context = parent_context.clone
81
+ next_context[item[:key].singularize] = item[:record]
82
+ next_mapping = mappings.find { |m| m[:uuid] == v[:uuid] }.clone
83
+ execute_query(next_mapping, mappings, next_context, included, meta, addr, item)
84
+ end
85
+ end
86
+
87
+ def self.query_item(mapping, mappings, parent_context, included, meta, addr, _parent)
88
+ query = resolve_conditions(mapping[:query].deep_dup, parent_context)
89
+ raise DeepUnrest::InvalidQuery unless query[:id] || query[:find]
90
+ record = if query.key?(:id)
91
+ mapping[:scope].find(query[:id]) if query.key?(:id)
92
+ else
93
+ mapping[:scope].find_by!(query[:find])
94
+ end
95
+
96
+ next_addr = [*addr, mapping[:key]]
97
+
98
+ result = {
99
+ **mapping,
100
+ addr: next_addr,
101
+ record: record
102
+ }
103
+
104
+ included << result
105
+
106
+ recurse_included_queries(result, mappings, parent_context, included, meta, [*next_addr, :included])
107
+ end
108
+
109
+ def self.get_paginator(query, _parent)
110
+ opts = query.dig(:paginate) || {}
111
+ params = ActionController::Parameters.new(opts)
112
+
113
+ case params[:type]
114
+ when :offset
115
+ OffsetPaginator.new(params)
116
+ else
117
+ PagedPaginator.new(params)
118
+ end
119
+ end
120
+
121
+ def self.query_list(item, mappings, parent_context, included, meta, addr, parent)
122
+ base_query = item[:query].deep_dup
123
+ extension = base_query.dig(:extend, parent&.fetch(:record)&.id&.to_s) || {}
124
+ query = resolve_conditions(base_query.deep_merge(extension),
125
+ parent_context)
126
+
127
+ paginator = get_paginator(query, parent)
128
+ resource = item[:resource]
129
+
130
+ # monkey patch the resource to only show authorized records
131
+ def resource.records_base(_opts)
132
+ item[:scope]
133
+ end
134
+
135
+ # results = resource.find(query[:filter], paginator: paginator,
136
+ # sort_criteria: query[:sort])
137
+
138
+ processor = JSONAPI::Processor.new(item[:resource],
139
+ :find,
140
+ filters: query[:filter] || {},
141
+ sort_criteria: query[:sort],
142
+ paginator: paginator)
143
+
144
+ jsonapi_result = processor.process
145
+
146
+ meta << {
147
+ addr: [*addr, item[:key], 'meta'],
148
+ serialized_result: {
149
+ paginationParams: jsonapi_result.pagination_params,
150
+ recordCount: jsonapi_result.record_count,
151
+ }
152
+ }
153
+
154
+ jsonapi_result.resources.each_with_index do |record, i|
155
+ next_addr = [*addr, item[:key], 'data[]', i]
156
+ result = {
157
+ **item,
158
+ addr: next_addr,
159
+ record: record._model
160
+ }
161
+
162
+ included << result
163
+ recurse_included_queries(result, mappings, parent_context, included, meta, [*next_addr, :included])
164
+ end
165
+ end
166
+
167
+ def self.get_query_type(item)
168
+ return :detail unless plural?(item[:key])
169
+ :list
170
+ end
171
+
172
+ def self.execute_query(item, mappings, parent_context, included, meta, addr, parent = nil)
173
+ if get_query_type(item) == :list
174
+ query_list(item, mappings, parent_context, included, meta, addr, parent)
175
+ else
176
+ query_item(item, mappings, parent_context, included, meta, addr, parent)
177
+ end
178
+ end
179
+
180
+ def self.execute_queries(mappings, parent_context = {}, included = [], meta = [], addr = [])
181
+ mappings.select { |m| m[:addr].size == 1 }.each do |item|
182
+ item[:results] = execute_query(item, mappings, parent_context, included, meta, addr)
183
+ end
184
+ [included, meta]
185
+ end
186
+
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
+
211
+ def self.format_response(mappings)
212
+ response = {}
213
+ mappings.each do |mapping|
214
+ set_attr(response, mapping[:addr], mapping[:serialized_result])
215
+ end
216
+ response
217
+ end
218
+
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
+ def self.read(ctx, params, user)
226
+ # create mappings for assembly / disassembly
227
+ mappings = create_read_mappings(params.to_unsafe_h)
228
+
229
+ # authorize user for requested scope(s)
230
+ DeepUnrest.authorization_strategy.authorize(mappings, user).flatten
231
+
232
+ # collect authorized scopes
233
+ collect_authorized_scopes(mappings, user)
234
+
235
+ # read data
236
+ data, meta = execute_queries(mappings)
237
+
238
+ # serialize using JSONAPI resource serializers
239
+ serialize_results(ctx, data)
240
+
241
+ # assemble results into something resembling shape of request
242
+ format_response([*data, *meta])
243
+ end
244
+ end
245
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeepUnrest
4
- VERSION = '0.1.30'
4
+ VERSION = '0.1.31'
5
5
  end
data/lib/deep_unrest.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'deep_unrest/engine'
4
+ require 'deep_unrest/read'
4
5
 
5
6
  # workaronud for rails bug with association indices.
6
7
  # see https://github.com/rails/rails/pull/24728
@@ -48,6 +49,9 @@ module DeepUnrest
48
49
  class Unauthorized < ::StandardError
49
50
  end
50
51
 
52
+ class InvalidQuery < ::StandardError
53
+ end
54
+
51
55
  def self.to_class(str)
52
56
  str.classify.constantize
53
57
  end
@@ -377,7 +381,8 @@ module DeepUnrest
377
381
  end
378
382
  end
379
383
 
380
- def self.mutate(mutation, user)
384
+ def self.mutate(mutation, context)
385
+ user = context[:current_user]
381
386
  ActiveRecord::Base.transaction do
382
387
  mutation.map do |_, item|
383
388
  item[:operations].map do |id, ops|
@@ -396,7 +401,7 @@ module DeepUnrest
396
401
  when :update
397
402
  model = item[:klass].find(id)
398
403
  model.assign_attributes(action[:body])
399
- resource = item[:resource].new(model, current_user: user)
404
+ resource = item[:resource].new(model, context)
400
405
  resource.run_callbacks :save do
401
406
  resource.run_callbacks :update do
402
407
  model.save
@@ -405,17 +410,16 @@ module DeepUnrest
405
410
  end
406
411
  when :create
407
412
  model = item[:klass].new(action[:body])
408
- resource = item[:resource].new(model, current_user: user)
413
+ resource = item[:resource].new(model, context)
409
414
  resource.run_callbacks :save do
410
415
  resource.run_callbacks :create do
411
- # item[:klass].create(action[:body])
412
416
  resource._model.save
413
417
  resource._model
414
418
  end
415
419
  end
416
420
  when :destroy
417
421
  model = item[:klass].find(id)
418
- resource = item[:resource].new(model, current_user: user)
422
+ resource = item[:resource].new(model, context)
419
423
  resource.run_callbacks :remove do
420
424
  item[:klass].destroy(id)
421
425
  end
@@ -508,12 +512,19 @@ module DeepUnrest
508
512
  record&.errors&.messages
509
513
  end
510
514
 
511
- def self.perform_update(ctx, params, user)
515
+ def self.perform_read(ctx, params, user)
516
+ DeepUnrest::Read.read(ctx, params, user)
517
+ end
518
+
519
+ def self.perform_update(ctx, params)
512
520
  temp_id_map = DeepUnrest::ApplicationController.class_variable_get(
513
521
  '@@temp_ids'
514
522
  )
515
523
 
516
- temp_id_map[ctx] ||= {}
524
+ user = ctx[:current_user]
525
+ uuid = ctx[:uuid]
526
+
527
+ temp_id_map[uuid] ||= {}
517
528
 
518
529
  # reject new resources marked for destruction
519
530
  viable_params = params.reject do |param|
@@ -530,10 +541,10 @@ module DeepUnrest
530
541
  mutations = build_mutation_body(viable_params, scopes, user)
531
542
 
532
543
  # convert temp_ids from ids to non-activerecord attributes
533
- convert_temp_ids!(ctx, mutations)
544
+ convert_temp_ids!(uuid, mutations)
534
545
 
535
546
  # perform update
536
- results = mutate(mutations, user).flatten
547
+ results = mutate(mutations, ctx).flatten
537
548
 
538
549
  # check results for errors
539
550
  errors = results.map { |res| format_error_keys(res) }
@@ -546,8 +557,8 @@ module DeepUnrest
546
557
  '@@destroyed_entities'
547
558
  )
548
559
  return {
549
- redirect_regex: build_redirect_regex(temp_id_map[ctx]),
550
- temp_ids: temp_id_map[ctx],
560
+ redirect_regex: build_redirect_regex(temp_id_map[uuid]),
561
+ temp_ids: temp_id_map[uuid],
551
562
  destroyed: destroyed
552
563
  }
553
564
  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.30
4
+ version: 0.1.31
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lynn Hurley
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-11 00:00:00.000000000 Z
11
+ date: 2019-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 5.2.0
19
+ version: 5.2.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 5.2.0
26
+ version: 5.2.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: jsonapi-utils
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -162,6 +162,8 @@ files:
162
162
  - lib/deep_unrest/concerns/map_temp_ids.rb
163
163
  - lib/deep_unrest/concerns/null_concern.rb
164
164
  - lib/deep_unrest/engine.rb
165
+ - lib/deep_unrest/paginators/basic.rb
166
+ - lib/deep_unrest/read.rb
165
167
  - lib/deep_unrest/version.rb
166
168
  - lib/tasks/deep_unrest_tasks.rake
167
169
  homepage: https://github.com/graveflex/deep_unrest
@@ -184,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
184
186
  version: '0'
185
187
  requirements: []
186
188
  rubyforge_project:
187
- rubygems_version: 2.6.6
189
+ rubygems_version: 2.7.3
188
190
  signing_key:
189
191
  specification_version: 4
190
192
  summary: Update multiple or deeply nested JSONAPI resources