graphiti 1.2.8 → 1.2.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|