graphiti 1.2.28 → 1.2.29
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 +4 -4
- data/.travis.yml +7 -1
- data/CHANGELOG.md +3 -0
- data/graphiti.gemspec +1 -1
- data/lib/graphiti/adapters/abstract.rb +10 -0
- data/lib/graphiti/adapters/active_record.rb +5 -1
- data/lib/graphiti/adapters/persistence/associations.rb +72 -0
- data/lib/graphiti/delegates/pagination.rb +11 -2
- data/lib/graphiti/hash_renderer.rb +2 -2
- data/lib/graphiti/request_validators/validator.rb +1 -1
- data/lib/graphiti/resource/configuration.rb +4 -4
- data/lib/graphiti/scope.rb +1 -3
- data/lib/graphiti/serializer.rb +2 -2
- data/lib/graphiti/stats/payload.rb +8 -5
- data/lib/graphiti/util/attribute_check.rb +1 -1
- data/lib/graphiti/util/persistence.rb +15 -78
- data/lib/graphiti/util/serializer_attributes.rb +4 -1
- data/lib/graphiti/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a1198667bd97f2aed53ee9652bf58e778c9423541e85c40122fcca1e504af6a
|
4
|
+
data.tar.gz: 58b4a16c2344147408b8f6bae93ba0f7268f45db8e1fdb0782f2eb452257f6af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4000d1e9f172c3cb1b973d8545dc02d6771951ed16efac8e21ce948de2a7ebb457115128d375dfda7f0d05bc334ab2851f6d69b0de8c23530bae8c47702d12f4
|
7
|
+
data.tar.gz: a10bfea53ddbd2af821d9467204fd4550a288fe6a5f57b02e63915e7d14230b00924ec20a0f3e350c1282bf9aeaf75a043ce5309ca83bf892c900f5add59f776
|
data/.travis.yml
CHANGED
@@ -19,7 +19,13 @@ env:
|
|
19
19
|
- COMMAND="standardrb --no-fix --format progress"
|
20
20
|
- COMMAND=rspec
|
21
21
|
- COMMAND=rspec APPRAISAL_INITIALIZED=true
|
22
|
-
|
22
|
+
jobs:
|
23
|
+
allow_failures:
|
24
|
+
- rvm: ruby-head
|
25
|
+
include:
|
26
|
+
- env: COMMAND=rspec
|
27
|
+
gemfile: Gemfile
|
28
|
+
rvm: ruby-head
|
23
29
|
exclude:
|
24
30
|
# Don't run the appraisal version of the specs for the base gemfile
|
25
31
|
- env: COMMAND=rspec APPRAISAL_INITIALIZED=true
|
data/CHANGELOG.md
CHANGED
@@ -8,6 +8,9 @@ Features:
|
|
8
8
|
- [157](https://github.com/graphiti-api/graphiti/pull/157) Using attribute option schema: false.
|
9
9
|
This option is default true and is not effected by only and except options. (@zeisler)
|
10
10
|
|
11
|
+
Fixes:
|
12
|
+
- [282] Support model names including "Resource"
|
13
|
+
|
11
14
|
## 1.1.0
|
12
15
|
|
13
16
|
Features:
|
data/graphiti.gemspec
CHANGED
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.bindir = "exe"
|
17
17
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
18
|
spec.require_paths = ["lib"]
|
19
|
-
spec.required_ruby_version = "
|
19
|
+
spec.required_ruby_version = [">= 2.3", "< 3.1"]
|
20
20
|
|
21
21
|
spec.add_dependency "jsonapi-serializable", "~> 0.3.0"
|
22
22
|
spec.add_dependency "jsonapi-renderer", "~> 0.2", ">= 0.2.2"
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module Graphiti
|
2
2
|
module Adapters
|
3
3
|
class Abstract
|
4
|
+
require "graphiti/adapters/persistence/associations.rb"
|
5
|
+
include Graphiti::Adapters::Persistence::Associations
|
6
|
+
|
4
7
|
attr_reader :resource
|
5
8
|
|
6
9
|
def initialize(resource)
|
@@ -400,6 +403,13 @@ module Graphiti
|
|
400
403
|
raise "you must override #destroy in an adapter subclass"
|
401
404
|
end
|
402
405
|
|
406
|
+
def close
|
407
|
+
end
|
408
|
+
|
409
|
+
def persistence_attributes(persistance, attributes)
|
410
|
+
attributes
|
411
|
+
end
|
412
|
+
|
403
413
|
def self.numerical_operators
|
404
414
|
[:eq, :not_eq, :gt, :gte, :lt, :lte].freeze
|
405
415
|
end
|
@@ -191,7 +191,7 @@ module Graphiti
|
|
191
191
|
# (see Adapters::Abstract#count)
|
192
192
|
def count(scope, attr)
|
193
193
|
if attr.to_sym == :total
|
194
|
-
scope.distinct.count
|
194
|
+
scope.distinct.count(:all)
|
195
195
|
else
|
196
196
|
scope.distinct.count(attr)
|
197
197
|
end
|
@@ -297,6 +297,10 @@ module Graphiti
|
|
297
297
|
model_instance
|
298
298
|
end
|
299
299
|
|
300
|
+
def close
|
301
|
+
ActiveRecord::Base.clear_active_connections!
|
302
|
+
end
|
303
|
+
|
300
304
|
private
|
301
305
|
|
302
306
|
def column_for(scope, name)
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Graphiti
|
2
|
+
module Adapters
|
3
|
+
module Persistence
|
4
|
+
module Associations
|
5
|
+
def process_belongs_to(persistence, attributes)
|
6
|
+
parents = [].tap do |processed|
|
7
|
+
persistence.iterate(only: [:polymorphic_belongs_to, :belongs_to]) do |x|
|
8
|
+
begin
|
9
|
+
id = x.dig(:attributes, :id)
|
10
|
+
x[:object] = x[:resource]
|
11
|
+
.persist_with_relationships(x[:meta], x[:attributes], x[:relationships])
|
12
|
+
processed << x
|
13
|
+
rescue Graphiti::Errors::RecordNotFound
|
14
|
+
path = "relationships/#{x.dig(:meta, :jsonapi_type)}"
|
15
|
+
raise Graphiti::Errors::RecordNotFound.new(x[:sideload].name, id, path)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
update_foreign_key_for_parents(parents, attributes)
|
21
|
+
parents
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_has_many(persistence, caller_model)
|
25
|
+
[].tap do |processed|
|
26
|
+
persistence.iterate(except: [:polymorphic_belongs_to, :belongs_to]) do |x|
|
27
|
+
update_foreign_key(caller_model, x[:attributes], x)
|
28
|
+
|
29
|
+
x[:object] = x[:resource]
|
30
|
+
.persist_with_relationships(x[:meta], x[:attributes], x[:relationships], caller_model, x[:foreign_key])
|
31
|
+
|
32
|
+
processed << x
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def update_foreign_key_for_parents(parents, attributes)
|
38
|
+
parents.each do |x|
|
39
|
+
update_foreign_key(x[:object], attributes, x)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# The child's attributes should be modified to nil-out the
|
44
|
+
# foreign_key when the parent is being destroyed or disassociated
|
45
|
+
#
|
46
|
+
# This is not the case for HABTM, whose "foreign key" is a join table
|
47
|
+
def update_foreign_key(parent_object, attrs, x)
|
48
|
+
return if x[:sideload].type == :many_to_many
|
49
|
+
|
50
|
+
if [:destroy, :disassociate].include?(x[:meta][:method])
|
51
|
+
if x[:sideload].polymorphic_has_one? || x[:sideload].polymorphic_has_many?
|
52
|
+
attrs[:"#{x[:sideload].polymorphic_as}_type"] = nil
|
53
|
+
end
|
54
|
+
attrs[x[:foreign_key]] = nil
|
55
|
+
update_foreign_type(attrs, x, null: true) if x[:is_polymorphic]
|
56
|
+
else
|
57
|
+
if x[:sideload].polymorphic_has_one? || x[:sideload].polymorphic_has_many?
|
58
|
+
attrs[:"#{x[:sideload].polymorphic_as}_type"] = parent_object.class.name
|
59
|
+
end
|
60
|
+
attrs[x[:foreign_key]] = parent_object.send(x[:primary_key])
|
61
|
+
update_foreign_type(attrs, x) if x[:is_polymorphic]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_foreign_type(attrs, x, null: false)
|
66
|
+
grouping_field = x[:sideload].parent.grouper.field_name
|
67
|
+
attrs[grouping_field] = null ? nil : x[:sideload].group_name
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -6,7 +6,7 @@ module Graphiti
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def links?
|
9
|
-
@proxy.query.pagination_links?
|
9
|
+
@proxy.query.pagination_links? && @proxy.data.present?
|
10
10
|
end
|
11
11
|
|
12
12
|
def links
|
@@ -53,7 +53,7 @@ module Graphiti
|
|
53
53
|
def item_count
|
54
54
|
begin
|
55
55
|
return @item_count if @item_count
|
56
|
-
@item_count =
|
56
|
+
@item_count = item_count_from_proxy || item_count_from_stats
|
57
57
|
unless @item_count.is_a?(Numeric)
|
58
58
|
raise TypeError, "#{@proxy.resource}.stat(:total, :count) returned an invalid value #{@item_count}"
|
59
59
|
end
|
@@ -68,6 +68,15 @@ module Graphiti
|
|
68
68
|
@item_count
|
69
69
|
end
|
70
70
|
|
71
|
+
def item_count_from_proxy
|
72
|
+
@proxy.stats.dig(:total, :count)
|
73
|
+
end
|
74
|
+
|
75
|
+
def item_count_from_stats
|
76
|
+
stats = Stats::Payload.new(@proxy.resource, @proxy.query, @proxy.scope.unpaginated_object, @proxy.data)
|
77
|
+
stats.calculate_stat(:total, @proxy.resource.stat(:total, :count))
|
78
|
+
end
|
79
|
+
|
71
80
|
def current_page
|
72
81
|
@current_page ||= (page_param[:number] || 1).to_i
|
73
82
|
end
|
@@ -129,7 +129,7 @@ module Graphiti
|
|
129
129
|
def get_attr(name, flag, opts = {})
|
130
130
|
defaults = {request: false}
|
131
131
|
opts = defaults.merge(opts)
|
132
|
-
new.get_attr(name, flag, opts)
|
132
|
+
new.get_attr(name, flag, **opts)
|
133
133
|
end
|
134
134
|
|
135
135
|
def abstract_class?
|
@@ -149,14 +149,14 @@ module Graphiti
|
|
149
149
|
|
150
150
|
def infer_type
|
151
151
|
if name.present?
|
152
|
-
name.demodulize.
|
152
|
+
name.demodulize.sub(/.*\KResource/, "").underscore.pluralize.to_sym
|
153
153
|
else
|
154
154
|
:undefined_jsonapi_type
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
158
158
|
def infer_model
|
159
|
-
name&.
|
159
|
+
name&.sub(/.*\KResource/, "")&.safe_constantize
|
160
160
|
end
|
161
161
|
|
162
162
|
# @api private
|
@@ -247,7 +247,7 @@ module Graphiti
|
|
247
247
|
|
248
248
|
def get_attr!(name, flag, options = {})
|
249
249
|
options[:raise_error] = true
|
250
|
-
get_attr(name, flag, options)
|
250
|
+
get_attr(name, flag, **options)
|
251
251
|
end
|
252
252
|
|
253
253
|
def get_attr(name, flag, request: false, raise_error: false)
|
data/lib/graphiti/scope.rb
CHANGED
@@ -45,9 +45,7 @@ module Graphiti
|
|
45
45
|
resolve_sideload = -> {
|
46
46
|
Graphiti.context = graphiti_context
|
47
47
|
sideload.resolve(results, q, parent_resource)
|
48
|
-
if concurrent
|
49
|
-
ActiveRecord::Base.clear_active_connections!
|
50
|
-
end
|
48
|
+
@resource.adapter.close if concurrent
|
51
49
|
}
|
52
50
|
if concurrent
|
53
51
|
promises << Concurrent::Promise.execute(&resolve_sideload)
|
data/lib/graphiti/serializer.rb
CHANGED
@@ -31,16 +31,19 @@ module Graphiti
|
|
31
31
|
stats[name] = {}
|
32
32
|
|
33
33
|
each_calculation(name, calculation) do |calc, function|
|
34
|
-
|
35
|
-
args << @resource.context if function.arity >= 3
|
36
|
-
args << @data if function.arity == 4
|
37
|
-
|
38
|
-
stats[name][calc] = function.call(*args)
|
34
|
+
stats[name][calc] = calculate_stat(name, function)
|
39
35
|
end
|
40
36
|
end
|
41
37
|
end
|
42
38
|
end
|
43
39
|
|
40
|
+
def calculate_stat(name, function)
|
41
|
+
args = [@scope, name]
|
42
|
+
args << @resource.context if function.arity >= 3
|
43
|
+
args << @data if function.arity == 4
|
44
|
+
function.call(*args)
|
45
|
+
end
|
46
|
+
|
44
47
|
private
|
45
48
|
|
46
49
|
def each_calculation(name, calculations)
|
@@ -14,6 +14,7 @@ class Graphiti::Util::Persistence
|
|
14
14
|
@relationships = relationships
|
15
15
|
@caller_model = caller_model
|
16
16
|
@foreign_key = foreign_key
|
17
|
+
@adapter = @resource.adapter
|
17
18
|
|
18
19
|
# Find the correct child resource for a given jsonapi type
|
19
20
|
if (meta_type = @meta[:type].try(:to_sym))
|
@@ -43,18 +44,16 @@ class Graphiti::Util::Persistence
|
|
43
44
|
#
|
44
45
|
# @return a model instance
|
45
46
|
def run
|
46
|
-
|
47
|
-
update_foreign_key_for_parents(parents)
|
47
|
+
attributes = @adapter.persistence_attributes(self, @attributes)
|
48
48
|
|
49
|
-
|
49
|
+
parents = @adapter.process_belongs_to(self, attributes)
|
50
|
+
persisted = persist_object(@meta[:method], attributes)
|
50
51
|
@resource.decorate_record(persisted)
|
51
52
|
assign_temp_id(persisted, @meta[:temp_id])
|
52
53
|
|
53
54
|
associate_parents(persisted, parents)
|
54
55
|
|
55
|
-
children = process_has_many(
|
56
|
-
update_foreign_key(persisted, x[:attributes], x)
|
57
|
-
}
|
56
|
+
children = @adapter.process_has_many(self, persisted)
|
58
57
|
|
59
58
|
associate_children(persisted, children) unless @meta[:method] == :destroy
|
60
59
|
|
@@ -69,43 +68,21 @@ class Graphiti::Util::Persistence
|
|
69
68
|
persisted
|
70
69
|
end
|
71
70
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
# The child's attributes should be modified to nil-out the
|
79
|
-
# foreign_key when the parent is being destroyed or disassociated
|
80
|
-
#
|
81
|
-
# This is not the case for HABTM, whose "foreign key" is a join table
|
82
|
-
def update_foreign_key(parent_object, attrs, x)
|
83
|
-
return if x[:sideload].type == :many_to_many
|
71
|
+
def iterate(only: [], except: [])
|
72
|
+
opts = {
|
73
|
+
resource: @resource,
|
74
|
+
relationships: @relationships
|
75
|
+
}.merge(only: only, except: except)
|
84
76
|
|
85
|
-
|
86
|
-
|
87
|
-
attrs[:"#{x[:sideload].polymorphic_as}_type"] = nil
|
88
|
-
end
|
89
|
-
attrs[x[:foreign_key]] = nil
|
90
|
-
update_foreign_type(attrs, x, null: true) if x[:is_polymorphic]
|
91
|
-
else
|
92
|
-
if x[:sideload].polymorphic_has_one? || x[:sideload].polymorphic_has_many?
|
93
|
-
attrs[:"#{x[:sideload].polymorphic_as}_type"] = parent_object.class.name
|
94
|
-
end
|
95
|
-
attrs[x[:foreign_key]] = parent_object.send(x[:primary_key])
|
96
|
-
update_foreign_type(attrs, x) if x[:is_polymorphic]
|
77
|
+
Graphiti::Util::RelationshipPayload.iterate(**opts) do |x|
|
78
|
+
yield x
|
97
79
|
end
|
98
80
|
end
|
99
81
|
|
100
|
-
|
101
|
-
grouping_field = x[:sideload].parent.grouper.field_name
|
102
|
-
attrs[grouping_field] = null ? nil : x[:sideload].group_name
|
103
|
-
end
|
82
|
+
private
|
104
83
|
|
105
|
-
def
|
106
|
-
|
107
|
-
update_foreign_key(x[:object], @attributes, x)
|
108
|
-
end
|
84
|
+
def add_hook(prc, lifecycle_event)
|
85
|
+
::Graphiti::Util::TransactionHooksRecorder.add(prc, lifecycle_event)
|
109
86
|
end
|
110
87
|
|
111
88
|
def associate_parents(object, parents)
|
@@ -161,35 +138,6 @@ class Graphiti::Util::Persistence
|
|
161
138
|
end
|
162
139
|
end
|
163
140
|
|
164
|
-
def process_has_many(relationships, caller_model)
|
165
|
-
[].tap do |processed|
|
166
|
-
iterate(except: [:polymorphic_belongs_to, :belongs_to]) do |x|
|
167
|
-
yield x
|
168
|
-
|
169
|
-
x[:object] = x[:resource]
|
170
|
-
.persist_with_relationships(x[:meta], x[:attributes], x[:relationships], caller_model, x[:foreign_key])
|
171
|
-
|
172
|
-
processed << x
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def process_belongs_to(relationships)
|
178
|
-
[].tap do |processed|
|
179
|
-
iterate(only: [:polymorphic_belongs_to, :belongs_to]) do |x|
|
180
|
-
begin
|
181
|
-
id = x.dig(:attributes, :id)
|
182
|
-
x[:object] = x[:resource]
|
183
|
-
.persist_with_relationships(x[:meta], x[:attributes], x[:relationships])
|
184
|
-
processed << x
|
185
|
-
rescue Graphiti::Errors::RecordNotFound
|
186
|
-
path = "relationships/#{x.dig(:meta, :jsonapi_type)}"
|
187
|
-
raise Graphiti::Errors::RecordNotFound.new(x[:sideload].name, id, path)
|
188
|
-
end
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
141
|
def post_process(caller_model, processed)
|
194
142
|
groups = processed.group_by { |x| x[:meta][:method] }
|
195
143
|
groups.each_pair do |method, group|
|
@@ -205,17 +153,6 @@ class Graphiti::Util::Persistence
|
|
205
153
|
object.instance_variable_set(:@_jsonapi_temp_id, temp_id)
|
206
154
|
end
|
207
155
|
|
208
|
-
def iterate(only: [], except: [])
|
209
|
-
opts = {
|
210
|
-
resource: @resource,
|
211
|
-
relationships: @relationships
|
212
|
-
}.merge(only: only, except: except)
|
213
|
-
|
214
|
-
Graphiti::Util::RelationshipPayload.iterate(opts) do |x|
|
215
|
-
yield x
|
216
|
-
end
|
217
|
-
end
|
218
|
-
|
219
156
|
def metadata
|
220
157
|
{
|
221
158
|
method: @meta[:method],
|
@@ -55,13 +55,16 @@ module Graphiti
|
|
55
55
|
def guard
|
56
56
|
method_name = @attr[:readable]
|
57
57
|
instance = @resource.new
|
58
|
+
attribute = @name.to_s
|
58
59
|
|
59
60
|
-> {
|
60
61
|
method = instance.method(method_name)
|
61
62
|
if method.arity.zero?
|
62
63
|
instance.instance_eval(&method_name)
|
63
|
-
|
64
|
+
elsif method.arity == 1
|
64
65
|
instance.instance_exec(@object, &method)
|
66
|
+
else
|
67
|
+
instance.instance_exec(@object, attribute, &method)
|
65
68
|
end
|
66
69
|
}
|
67
70
|
end
|
data/lib/graphiti/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphiti
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.29
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lee Richmond
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-10-
|
11
|
+
date: 2020-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jsonapi-serializable
|
@@ -266,6 +266,7 @@ files:
|
|
266
266
|
- lib/graphiti/adapters/active_record/many_to_many_sideload.rb
|
267
267
|
- lib/graphiti/adapters/graphiti_api.rb
|
268
268
|
- lib/graphiti/adapters/null.rb
|
269
|
+
- lib/graphiti/adapters/persistence/associations.rb
|
269
270
|
- lib/graphiti/cli.rb
|
270
271
|
- lib/graphiti/configuration.rb
|
271
272
|
- lib/graphiti/context.rb
|
@@ -347,9 +348,12 @@ require_paths:
|
|
347
348
|
- lib
|
348
349
|
required_ruby_version: !ruby/object:Gem::Requirement
|
349
350
|
requirements:
|
350
|
-
- - "
|
351
|
+
- - ">="
|
351
352
|
- !ruby/object:Gem::Version
|
352
353
|
version: '2.3'
|
354
|
+
- - "<"
|
355
|
+
- !ruby/object:Gem::Version
|
356
|
+
version: '3.1'
|
353
357
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
354
358
|
requirements:
|
355
359
|
- - ">="
|