graphiti 1.2.21 → 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/Gemfile +0 -2
- data/gemfiles/rails_4.gemfile +1 -1
- data/graphiti.gemspec +2 -1
- data/lib/graphiti/adapters/abstract.rb +10 -0
- data/lib/graphiti/adapters/active_record.rb +6 -2
- data/lib/graphiti/adapters/active_record/belongs_to_sideload.rb +1 -1
- data/lib/graphiti/adapters/active_record/has_many_sideload.rb +1 -1
- data/lib/graphiti/adapters/active_record/has_one_sideload.rb +1 -1
- data/lib/graphiti/adapters/active_record/{inferrence.rb → inference.rb} +2 -2
- data/lib/graphiti/adapters/active_record/many_to_many_sideload.rb +20 -2
- data/lib/graphiti/adapters/persistence/associations.rb +72 -0
- data/lib/graphiti/delegates/pagination.rb +11 -2
- data/lib/graphiti/errors.rb +14 -0
- data/lib/graphiti/hash_renderer.rb +2 -2
- data/lib/graphiti/request_validator.rb +6 -6
- data/lib/graphiti/request_validators/validator.rb +8 -2
- data/lib/graphiti/resource/configuration.rb +4 -4
- data/lib/graphiti/resource/dsl.rb +1 -0
- data/lib/graphiti/resource/interface.rb +10 -0
- data/lib/graphiti/resource_proxy.rb +3 -1
- data/lib/graphiti/runner.rb +1 -2
- data/lib/graphiti/scope.rb +1 -3
- data/lib/graphiti/serializer.rb +2 -2
- data/lib/graphiti/sideload.rb +3 -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 +26 -8
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/Gemfile
CHANGED
data/gemfiles/rails_4.gemfile
CHANGED
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"
|
|
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
|
|
|
29
29
|
spec.add_development_dependency "kaminari", "~> 0.17"
|
|
30
30
|
spec.add_development_dependency "bundler"
|
|
31
31
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
32
|
+
spec.add_development_dependency "standard", "0.4.7"
|
|
32
33
|
spec.add_development_dependency "activemodel", ">= 4.1"
|
|
33
34
|
spec.add_development_dependency "graphiti_spec_helpers", "1.0.beta.4"
|
|
34
35
|
end
|
|
@@ -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
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Graphiti
|
|
2
2
|
module Adapters
|
|
3
3
|
class ActiveRecord < ::Graphiti::Adapters::Abstract
|
|
4
|
-
require "graphiti/adapters/active_record/
|
|
4
|
+
require "graphiti/adapters/active_record/inference"
|
|
5
5
|
require "graphiti/adapters/active_record/has_many_sideload"
|
|
6
6
|
require "graphiti/adapters/active_record/belongs_to_sideload"
|
|
7
7
|
require "graphiti/adapters/active_record/has_one_sideload"
|
|
@@ -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)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
module Graphiti::Adapters::ActiveRecord::
|
|
1
|
+
module Graphiti::Adapters::ActiveRecord::Inference
|
|
2
2
|
# If going AR to AR, use AR introspection
|
|
3
|
-
# If going AR to PORO, fall back to normal
|
|
3
|
+
# If going AR to PORO, fall back to normal inference
|
|
4
4
|
def infer_foreign_key
|
|
5
5
|
parent_model = parent_resource_class.model
|
|
6
6
|
reflection = parent_model.reflections[association_name.to_s]
|
|
@@ -8,6 +8,18 @@ class Graphiti::Adapters::ActiveRecord::ManyToManySideload < Graphiti::Sideload:
|
|
|
8
8
|
foreign_key.keys.first
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
def inverse_filter
|
|
12
|
+
return @inverse_filter if @inverse_filter
|
|
13
|
+
|
|
14
|
+
inferred_name = infer_inverse_association
|
|
15
|
+
|
|
16
|
+
if inferred_name
|
|
17
|
+
"#{inferred_name.to_s.singularize}_id"
|
|
18
|
+
else
|
|
19
|
+
super
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
11
23
|
def belongs_to_many_filter(scope, value)
|
|
12
24
|
if polymorphic?
|
|
13
25
|
clauses = value.group_by { |v| v["type"] }.map { |group|
|
|
@@ -36,8 +48,7 @@ class Graphiti::Adapters::ActiveRecord::ManyToManySideload < Graphiti::Sideload:
|
|
|
36
48
|
|
|
37
49
|
def filter_for(scope, value, type = nil)
|
|
38
50
|
scope
|
|
39
|
-
.
|
|
40
|
-
.joins(through_relationship_name)
|
|
51
|
+
.includes(through_relationship_name)
|
|
41
52
|
.where(belongs_to_many_clause(value, type))
|
|
42
53
|
end
|
|
43
54
|
|
|
@@ -76,4 +87,11 @@ class Graphiti::Adapters::ActiveRecord::ManyToManySideload < Graphiti::Sideload:
|
|
|
76
87
|
value = through_reflection.foreign_key.to_sym
|
|
77
88
|
{key => value}
|
|
78
89
|
end
|
|
90
|
+
|
|
91
|
+
def infer_inverse_association
|
|
92
|
+
through_class = through_reflection.klass
|
|
93
|
+
|
|
94
|
+
foreign_reflection = through_class.reflections[name.to_s.singularize]
|
|
95
|
+
foreign_reflection && foreign_reflection.options[:inverse_of]
|
|
96
|
+
end
|
|
79
97
|
end
|
|
@@ -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
|
data/lib/graphiti/errors.rb
CHANGED
|
@@ -366,6 +366,20 @@ module Graphiti
|
|
|
366
366
|
end
|
|
367
367
|
end
|
|
368
368
|
|
|
369
|
+
class UndefinedIDLookup < Base
|
|
370
|
+
def initialize(resource_class)
|
|
371
|
+
@resource_class = resource_class
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def message
|
|
375
|
+
<<~MSG
|
|
376
|
+
Tried to resolve #{@resource_class} with an :id filter, but the filter was nil.
|
|
377
|
+
This can result in unscoping a query, which can cause incorrect values to be
|
|
378
|
+
returned which may or may not bypass standard access controls.
|
|
379
|
+
MSG
|
|
380
|
+
end
|
|
381
|
+
end
|
|
382
|
+
|
|
369
383
|
class UnknownAttribute < AttributeError
|
|
370
384
|
def message
|
|
371
385
|
"#{super}, but could not find an attribute with that name."
|
|
@@ -6,18 +6,18 @@ module Graphiti
|
|
|
6
6
|
:deserialized_payload,
|
|
7
7
|
to: :@validator
|
|
8
8
|
|
|
9
|
-
def initialize(root_resource, raw_params)
|
|
10
|
-
@validator = ValidatorFactory.create(root_resource, raw_params)
|
|
9
|
+
def initialize(root_resource, raw_params, action)
|
|
10
|
+
@validator = ValidatorFactory.create(root_resource, raw_params, action)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
class ValidatorFactory
|
|
14
|
-
def self.create(root_resource, raw_params)
|
|
15
|
-
case
|
|
16
|
-
when
|
|
14
|
+
def self.create(root_resource, raw_params, action)
|
|
15
|
+
case action
|
|
16
|
+
when :update then
|
|
17
17
|
RequestValidators::UpdateValidator
|
|
18
18
|
else
|
|
19
19
|
RequestValidators::Validator
|
|
20
|
-
end.new(root_resource, raw_params)
|
|
20
|
+
end.new(root_resource, raw_params, action)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -3,10 +3,11 @@ module Graphiti
|
|
|
3
3
|
class Validator
|
|
4
4
|
attr_reader :errors
|
|
5
5
|
|
|
6
|
-
def initialize(root_resource, raw_params)
|
|
6
|
+
def initialize(root_resource, raw_params, action)
|
|
7
7
|
@root_resource = root_resource
|
|
8
8
|
@raw_params = raw_params
|
|
9
9
|
@errors = Graphiti::Util::SimpleErrors.new(raw_params)
|
|
10
|
+
@action = action
|
|
10
11
|
end
|
|
11
12
|
|
|
12
13
|
def validate
|
|
@@ -50,7 +51,7 @@ module Graphiti
|
|
|
50
51
|
relationships: relationships
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
Graphiti::Util::RelationshipPayload.iterate(opts) do |x|
|
|
54
|
+
Graphiti::Util::RelationshipPayload.iterate(**opts) do |x|
|
|
54
55
|
sideload_def = x[:sideload]
|
|
55
56
|
|
|
56
57
|
unless sideload_def.writable?
|
|
@@ -68,6 +69,11 @@ module Graphiti
|
|
|
68
69
|
|
|
69
70
|
def typecast_attributes(resource, attributes, payload_path)
|
|
70
71
|
attributes.each_pair do |key, value|
|
|
72
|
+
# Only validate id if create action, otherwise it's only used for lookup
|
|
73
|
+
next if @action != :create &&
|
|
74
|
+
key == :id &&
|
|
75
|
+
resource.class.config[:attributes][:id][:writable] == false
|
|
76
|
+
|
|
71
77
|
begin
|
|
72
78
|
attributes[key] = resource.typecast(key, value, :writable)
|
|
73
79
|
rescue Graphiti::Errors::UnknownAttribute
|
|
@@ -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)
|
|
@@ -23,6 +23,9 @@ module Graphiti
|
|
|
23
23
|
|
|
24
24
|
# @api private
|
|
25
25
|
def _find(params = {}, base_scope = nil)
|
|
26
|
+
guard_nil_id!(params[:data])
|
|
27
|
+
guard_nil_id!(params)
|
|
28
|
+
|
|
26
29
|
id = params[:data].try(:[], :id) || params.delete(:id)
|
|
27
30
|
params[:filter] ||= {}
|
|
28
31
|
params[:filter][:id] = id if id
|
|
@@ -52,6 +55,13 @@ module Graphiti
|
|
|
52
55
|
end
|
|
53
56
|
end
|
|
54
57
|
end
|
|
58
|
+
|
|
59
|
+
def guard_nil_id!(params)
|
|
60
|
+
return unless params
|
|
61
|
+
if params.key?(:id) && params[:id].nil?
|
|
62
|
+
raise Errors::UndefinedIDLookup.new(self)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
55
65
|
end
|
|
56
66
|
end
|
|
57
67
|
end
|
|
@@ -93,7 +93,7 @@ module Graphiti
|
|
|
93
93
|
original = Graphiti.context[:namespace]
|
|
94
94
|
begin
|
|
95
95
|
Graphiti.context[:namespace] = action
|
|
96
|
-
::Graphiti::RequestValidator.new(@resource, @payload.params).validate!
|
|
96
|
+
::Graphiti::RequestValidator.new(@resource, @payload.params, action).validate!
|
|
97
97
|
validator = persist {
|
|
98
98
|
@resource.persist_with_relationships \
|
|
99
99
|
@payload.meta(action: action),
|
|
@@ -118,6 +118,7 @@ module Graphiti
|
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
def destroy
|
|
121
|
+
data
|
|
121
122
|
transaction_response = @resource.transaction do
|
|
122
123
|
metadata = {method: :destroy}
|
|
123
124
|
model = @resource.destroy(@query.filters[:id], metadata)
|
|
@@ -135,6 +136,7 @@ module Graphiti
|
|
|
135
136
|
end
|
|
136
137
|
|
|
137
138
|
def update_attributes
|
|
139
|
+
data
|
|
138
140
|
save(action: :update)
|
|
139
141
|
end
|
|
140
142
|
|
data/lib/graphiti/runner.rb
CHANGED
|
@@ -9,8 +9,7 @@ module Graphiti
|
|
|
9
9
|
@query = query
|
|
10
10
|
@action = action
|
|
11
11
|
|
|
12
|
-
validator = RequestValidator.new(jsonapi_resource, params)
|
|
13
|
-
|
|
12
|
+
validator = RequestValidator.new(jsonapi_resource, params, action)
|
|
14
13
|
validator.validate!
|
|
15
14
|
|
|
16
15
|
@deserialized_payload = validator.deserialized_payload
|
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
data/lib/graphiti/sideload.rb
CHANGED
|
@@ -26,8 +26,8 @@ module Graphiti
|
|
|
26
26
|
@foreign_key = opts[:foreign_key]
|
|
27
27
|
@type = opts[:type]
|
|
28
28
|
@base_scope = opts[:base_scope]
|
|
29
|
-
@readable =
|
|
30
|
-
@writable =
|
|
29
|
+
@readable = opts[:readable]
|
|
30
|
+
@writable = opts[:writable]
|
|
31
31
|
@as = opts[:as]
|
|
32
32
|
@link = opts[:link]
|
|
33
33
|
@single = opts[:single]
|
|
@@ -426,6 +426,7 @@ module Graphiti
|
|
|
426
426
|
Util::Class.namespace_for(klass)
|
|
427
427
|
end
|
|
428
428
|
|
|
429
|
+
# TODO: call this at runtime to support procs
|
|
429
430
|
def evaluate_flag(flag)
|
|
430
431
|
return false if flag.blank?
|
|
431
432
|
|
|
@@ -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
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2020-
|
|
11
|
+
date: 2020-10-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jsonapi-serializable
|
|
@@ -162,6 +162,20 @@ dependencies:
|
|
|
162
162
|
- - "~>"
|
|
163
163
|
- !ruby/object:Gem::Version
|
|
164
164
|
version: '10.0'
|
|
165
|
+
- !ruby/object:Gem::Dependency
|
|
166
|
+
name: standard
|
|
167
|
+
requirement: !ruby/object:Gem::Requirement
|
|
168
|
+
requirements:
|
|
169
|
+
- - '='
|
|
170
|
+
- !ruby/object:Gem::Version
|
|
171
|
+
version: 0.4.7
|
|
172
|
+
type: :development
|
|
173
|
+
prerelease: false
|
|
174
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
175
|
+
requirements:
|
|
176
|
+
- - '='
|
|
177
|
+
- !ruby/object:Gem::Version
|
|
178
|
+
version: 0.4.7
|
|
165
179
|
- !ruby/object:Gem::Dependency
|
|
166
180
|
name: activemodel
|
|
167
181
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -190,7 +204,7 @@ dependencies:
|
|
|
190
204
|
- - '='
|
|
191
205
|
- !ruby/object:Gem::Version
|
|
192
206
|
version: 1.0.beta.4
|
|
193
|
-
description:
|
|
207
|
+
description:
|
|
194
208
|
email:
|
|
195
209
|
- richmolj@gmail.com
|
|
196
210
|
executables:
|
|
@@ -248,10 +262,11 @@ files:
|
|
|
248
262
|
- lib/graphiti/adapters/active_record/belongs_to_sideload.rb
|
|
249
263
|
- lib/graphiti/adapters/active_record/has_many_sideload.rb
|
|
250
264
|
- lib/graphiti/adapters/active_record/has_one_sideload.rb
|
|
251
|
-
- lib/graphiti/adapters/active_record/
|
|
265
|
+
- lib/graphiti/adapters/active_record/inference.rb
|
|
252
266
|
- lib/graphiti/adapters/active_record/many_to_many_sideload.rb
|
|
253
267
|
- lib/graphiti/adapters/graphiti_api.rb
|
|
254
268
|
- lib/graphiti/adapters/null.rb
|
|
269
|
+
- lib/graphiti/adapters/persistence/associations.rb
|
|
255
270
|
- lib/graphiti/cli.rb
|
|
256
271
|
- lib/graphiti/configuration.rb
|
|
257
272
|
- lib/graphiti/context.rb
|
|
@@ -327,15 +342,18 @@ homepage: https://github.com/graphiti-api/graphiti
|
|
|
327
342
|
licenses:
|
|
328
343
|
- MIT
|
|
329
344
|
metadata: {}
|
|
330
|
-
post_install_message:
|
|
345
|
+
post_install_message:
|
|
331
346
|
rdoc_options: []
|
|
332
347
|
require_paths:
|
|
333
348
|
- lib
|
|
334
349
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
335
350
|
requirements:
|
|
336
|
-
- - "
|
|
351
|
+
- - ">="
|
|
337
352
|
- !ruby/object:Gem::Version
|
|
338
353
|
version: '2.3'
|
|
354
|
+
- - "<"
|
|
355
|
+
- !ruby/object:Gem::Version
|
|
356
|
+
version: '3.1'
|
|
339
357
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
340
358
|
requirements:
|
|
341
359
|
- - ">="
|
|
@@ -343,7 +361,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
343
361
|
version: '0'
|
|
344
362
|
requirements: []
|
|
345
363
|
rubygems_version: 3.0.6
|
|
346
|
-
signing_key:
|
|
364
|
+
signing_key:
|
|
347
365
|
specification_version: 4
|
|
348
366
|
summary: Easily build jsonapi.org-compatible APIs
|
|
349
367
|
test_files: []
|