graphiti 1.2.8 → 1.2.9
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/lib/graphiti.rb +6 -0
- data/lib/graphiti/errors.rb +3 -0
- data/lib/graphiti/request_validator.rb +13 -83
- data/lib/graphiti/request_validators/update_validator.rb +61 -0
- data/lib/graphiti/request_validators/validator.rb +96 -0
- data/lib/graphiti/resource_proxy.rb +1 -0
- data/lib/graphiti/sideload.rb +6 -0
- data/lib/graphiti/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb47455dd399241e2671b63e9087220c7319767a62df3a5ba4742a3f74a26725
|
4
|
+
data.tar.gz: 23900f4fd2949c8b3d3faa848dfa64c36690749906e4c8c8f99945cf48f72aa8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91883afe7f0a0cf03c7daf35098d5298673f69f52b69c0aa45c79cc24e20594890c8077a28e4448403616db2693f44893f346377cc31faa2e8644a99cc9dcc18
|
7
|
+
data.tar.gz: f5a371c0b6413c30a9c33080bec055d06eb5464a4b971557059b8db5e69b2c86c2c2a51d580cfe134a85eca483ce8fea61fcb4d924457b2cd2dd35388597f39e
|
data/lib/graphiti.rb
CHANGED
@@ -32,6 +32,10 @@ module Graphiti
|
|
32
32
|
yield
|
33
33
|
ensure
|
34
34
|
self.context = prior
|
35
|
+
|
36
|
+
resources.each { |resource_class|
|
37
|
+
resource_class.sideloads.values.each(&:clear_resources)
|
38
|
+
}
|
35
39
|
end
|
36
40
|
|
37
41
|
def self.config
|
@@ -124,6 +128,8 @@ require "graphiti/sideload/polymorphic_belongs_to"
|
|
124
128
|
require "graphiti/resource"
|
125
129
|
require "graphiti/resource_proxy"
|
126
130
|
require "graphiti/request_validator"
|
131
|
+
require "graphiti/request_validators/validator"
|
132
|
+
require "graphiti/request_validators/update_validator"
|
127
133
|
require "graphiti/query"
|
128
134
|
require "graphiti/scope"
|
129
135
|
require "graphiti/deserializer"
|
data/lib/graphiti/errors.rb
CHANGED
@@ -1,94 +1,24 @@
|
|
1
1
|
module Graphiti
|
2
2
|
class RequestValidator
|
3
|
-
|
3
|
+
delegate :validate,
|
4
|
+
:validate!,
|
5
|
+
:errors,
|
6
|
+
:deserialized_payload,
|
7
|
+
to: :@validator
|
4
8
|
|
5
9
|
def initialize(root_resource, raw_params)
|
6
|
-
@
|
7
|
-
@raw_params = raw_params
|
8
|
-
@errors = Graphiti::Util::SimpleErrors.new(raw_params)
|
10
|
+
@validator = ValidatorFactory.create(root_resource, raw_params)
|
9
11
|
end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
typecast_attributes(resource, deserialized_payload.attributes, deserialized_payload.meta[:payload_path])
|
20
|
-
process_relationships(resource, deserialized_payload.relationships, deserialized_payload.meta[:payload_path])
|
21
|
-
|
22
|
-
errors.blank?
|
23
|
-
end
|
24
|
-
|
25
|
-
def validate!
|
26
|
-
unless validate
|
27
|
-
raise Graphiti::Errors::InvalidRequest, self.errors
|
28
|
-
end
|
29
|
-
|
30
|
-
true
|
31
|
-
end
|
32
|
-
|
33
|
-
def deserialized_payload
|
34
|
-
@deserialized_payload ||= begin
|
35
|
-
payload = normalized_params
|
36
|
-
if payload[:data] && payload[:data][:type]
|
37
|
-
Graphiti::Deserializer.new(payload)
|
13
|
+
class ValidatorFactory
|
14
|
+
def self.create(root_resource, raw_params)
|
15
|
+
case raw_params["action"]
|
16
|
+
when "update" then
|
17
|
+
RequestValidators::UpdateValidator
|
38
18
|
else
|
39
|
-
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def process_relationships(resource, relationships, payload_path)
|
47
|
-
opts = {
|
48
|
-
resource: resource,
|
49
|
-
relationships: relationships,
|
50
|
-
}
|
51
|
-
|
52
|
-
Graphiti::Util::RelationshipPayload.iterate(opts) do |x|
|
53
|
-
sideload_def = x[:sideload]
|
54
|
-
|
55
|
-
unless sideload_def.writable?
|
56
|
-
full_key = fully_qualified_key(sideload_def.name, payload_path, :relationships)
|
57
|
-
unless @errors.added?(full_key, :unwritable_relationship)
|
58
|
-
@errors.add(full_key, :unwritable_relationship)
|
59
|
-
end
|
60
|
-
next
|
61
|
-
end
|
62
|
-
|
63
|
-
typecast_attributes(x[:resource], x[:attributes], x[:meta][:payload_path])
|
64
|
-
process_relationships(x[:resource], x[:relationships], x[:meta][:payload_path])
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def typecast_attributes(resource, attributes, payload_path)
|
69
|
-
attributes.each_pair do |key, value|
|
70
|
-
begin
|
71
|
-
attributes[key] = resource.typecast(key, value, :writable)
|
72
|
-
rescue Graphiti::Errors::UnknownAttribute
|
73
|
-
@errors.add(fully_qualified_key(key, payload_path), :unknown_attribute, message: "is an unknown attribute")
|
74
|
-
rescue Graphiti::Errors::InvalidAttributeAccess
|
75
|
-
@errors.add(fully_qualified_key(key, payload_path), :unwritable_attribute, message: "cannot be written")
|
76
|
-
rescue Graphiti::Errors::TypecastFailed => e
|
77
|
-
@errors.add(fully_qualified_key(key, payload_path), :type_error, message: "should be type #{e.type_name}")
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def normalized_params
|
83
|
-
normalized = @raw_params
|
84
|
-
if normalized.respond_to?(:to_unsafe_h)
|
85
|
-
normalized = normalized.to_unsafe_h.deep_symbolize_keys
|
19
|
+
RequestValidators::Validator
|
20
|
+
end.new(root_resource, raw_params)
|
86
21
|
end
|
87
|
-
normalized
|
88
|
-
end
|
89
|
-
|
90
|
-
def fully_qualified_key(key, path, attributes_or_relationships = :attributes)
|
91
|
-
(path + [attributes_or_relationships, key]).join(".")
|
92
22
|
end
|
93
23
|
end
|
94
24
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Graphiti
|
2
|
+
module RequestValidators
|
3
|
+
class UpdateValidator < Validator
|
4
|
+
def validate
|
5
|
+
if required_payload? && payload_matches_endpoint?
|
6
|
+
super
|
7
|
+
else
|
8
|
+
return false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def attribute_mismatch(attr_path)
|
15
|
+
@error_class = Graphiti::Errors::ConflictRequest
|
16
|
+
@errors.add(
|
17
|
+
attr_path.join("."),
|
18
|
+
:attribute_mismatch,
|
19
|
+
message: "does not match the server endpoint"
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def required_payload?
|
24
|
+
[
|
25
|
+
[:data],
|
26
|
+
[:data, :type],
|
27
|
+
[:data, :id]
|
28
|
+
].each do |required_attr|
|
29
|
+
attribute_mismatch(required_attr) unless @raw_params.dig(*required_attr)
|
30
|
+
end
|
31
|
+
errors.blank?
|
32
|
+
end
|
33
|
+
|
34
|
+
def payload_matches_endpoint?
|
35
|
+
unless @raw_params.dig(:data, :id) == @raw_params.dig(:filter, :id)
|
36
|
+
attribute_mismatch([:data, :id])
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
meta_type = @raw_params.dig(:data, :type)
|
41
|
+
|
42
|
+
# NOTE: calling #to_s and comparing 2 strings is slower than
|
43
|
+
# calling #to_sym and comparing 2 symbols. But pre ruby-2.2
|
44
|
+
# #to_sym on user supplied data would lead to a memory leak.
|
45
|
+
if @root_resource.type.to_s != meta_type
|
46
|
+
if @root_resource.polymorphic?
|
47
|
+
begin
|
48
|
+
@root_resource.class.resource_for_type(meta_type).new
|
49
|
+
rescue Errors::PolymorphicResourceChildNotFound
|
50
|
+
attribute_mismatch([:data, :type])
|
51
|
+
end
|
52
|
+
else
|
53
|
+
attribute_mismatch([:data, :type])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
errors.blank?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Graphiti
|
2
|
+
module RequestValidators
|
3
|
+
class Validator
|
4
|
+
attr_reader :errors
|
5
|
+
|
6
|
+
def initialize(root_resource, raw_params)
|
7
|
+
@root_resource = root_resource
|
8
|
+
@raw_params = raw_params
|
9
|
+
@errors = Graphiti::Util::SimpleErrors.new(raw_params)
|
10
|
+
end
|
11
|
+
|
12
|
+
def validate
|
13
|
+
resource = @root_resource
|
14
|
+
if (meta_type = deserialized_payload.meta[:type].try(:to_sym))
|
15
|
+
if @root_resource.type != meta_type && @root_resource.polymorphic?
|
16
|
+
resource = @root_resource.class.resource_for_type(meta_type).new
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
typecast_attributes(resource, deserialized_payload.attributes, deserialized_payload.meta[:payload_path])
|
21
|
+
process_relationships(resource, deserialized_payload.relationships, deserialized_payload.meta[:payload_path])
|
22
|
+
|
23
|
+
errors.blank?
|
24
|
+
end
|
25
|
+
|
26
|
+
def validate!
|
27
|
+
unless validate
|
28
|
+
raise @error_class || Graphiti::Errors::InvalidRequest, self.errors
|
29
|
+
end
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def deserialized_payload
|
35
|
+
@deserialized_payload ||= begin
|
36
|
+
payload = normalized_params
|
37
|
+
if payload[:data] && payload[:data][:type]
|
38
|
+
Graphiti::Deserializer.new(payload)
|
39
|
+
else
|
40
|
+
Graphiti::Deserializer.new({})
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def process_relationships(resource, relationships, payload_path)
|
48
|
+
opts = {
|
49
|
+
resource: resource,
|
50
|
+
relationships: relationships,
|
51
|
+
}
|
52
|
+
|
53
|
+
Graphiti::Util::RelationshipPayload.iterate(opts) do |x|
|
54
|
+
sideload_def = x[:sideload]
|
55
|
+
|
56
|
+
unless sideload_def.writable?
|
57
|
+
full_key = fully_qualified_key(sideload_def.name, payload_path, :relationships)
|
58
|
+
unless @errors.added?(full_key, :unwritable_relationship)
|
59
|
+
@errors.add(full_key, :unwritable_relationship)
|
60
|
+
end
|
61
|
+
next
|
62
|
+
end
|
63
|
+
|
64
|
+
typecast_attributes(x[:resource], x[:attributes], x[:meta][:payload_path])
|
65
|
+
process_relationships(x[:resource], x[:relationships], x[:meta][:payload_path])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def typecast_attributes(resource, attributes, payload_path)
|
70
|
+
attributes.each_pair do |key, value|
|
71
|
+
begin
|
72
|
+
attributes[key] = resource.typecast(key, value, :writable)
|
73
|
+
rescue Graphiti::Errors::UnknownAttribute
|
74
|
+
@errors.add(fully_qualified_key(key, payload_path), :unknown_attribute, message: "is an unknown attribute")
|
75
|
+
rescue Graphiti::Errors::InvalidAttributeAccess
|
76
|
+
@errors.add(fully_qualified_key(key, payload_path), :unwritable_attribute, message: "cannot be written")
|
77
|
+
rescue Graphiti::Errors::TypecastFailed => e
|
78
|
+
@errors.add(fully_qualified_key(key, payload_path), :type_error, message: "should be type #{e.type_name}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def normalized_params
|
84
|
+
normalized = @raw_params
|
85
|
+
if normalized.respond_to?(:to_unsafe_h)
|
86
|
+
normalized = normalized.to_unsafe_h.deep_symbolize_keys
|
87
|
+
end
|
88
|
+
normalized
|
89
|
+
end
|
90
|
+
|
91
|
+
def fully_qualified_key(key, path, attributes_or_relationships = :attributes)
|
92
|
+
(path + [attributes_or_relationships, key]).join(".")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -93,6 +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
97
|
validator = persist {
|
97
98
|
@resource.persist_with_relationships \
|
98
99
|
@payload.meta(action: action),
|
data/lib/graphiti/sideload.rb
CHANGED
@@ -224,6 +224,12 @@ module Graphiti
|
|
224
224
|
@parent_resource ||= parent_resource_class.new
|
225
225
|
end
|
226
226
|
|
227
|
+
# See https://github.com/graphiti-api/graphiti/issues/186
|
228
|
+
def clear_resources
|
229
|
+
@resource = nil
|
230
|
+
@parent_resource = nil
|
231
|
+
end
|
232
|
+
|
227
233
|
def assign(parents, children)
|
228
234
|
track_associated = type == :has_one
|
229
235
|
associated = [] if track_associated
|
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.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lee Richmond
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-10-
|
11
|
+
date: 2019-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: jsonapi-serializable
|
@@ -275,6 +275,8 @@ files:
|
|
275
275
|
- lib/graphiti/railtie.rb
|
276
276
|
- lib/graphiti/renderer.rb
|
277
277
|
- lib/graphiti/request_validator.rb
|
278
|
+
- lib/graphiti/request_validators/update_validator.rb
|
279
|
+
- lib/graphiti/request_validators/validator.rb
|
278
280
|
- lib/graphiti/resource.rb
|
279
281
|
- lib/graphiti/resource/configuration.rb
|
280
282
|
- lib/graphiti/resource/documentation.rb
|