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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fe75f46165881699109207f26430e8f6b5801ecdc193e552cda56b65bfc5ed5b
4
- data.tar.gz: a9fea18c76fb38cbe0911169487072db6b916dfde540ee206f48ed5eee638996
3
+ metadata.gz: eb47455dd399241e2671b63e9087220c7319767a62df3a5ba4742a3f74a26725
4
+ data.tar.gz: 23900f4fd2949c8b3d3faa848dfa64c36690749906e4c8c8f99945cf48f72aa8
5
5
  SHA512:
6
- metadata.gz: db681e9031c0765a07993ec7f1d2fb994faafb4a61bb35e59c1053d04c73b30d10094b579cafb7d7ca2181eee05b806cebb17bafeaf085426596ee5049041950
7
- data.tar.gz: 2e1f029281562e117501fb53e130fac33a18430f5e9f42e443e62f578892a704a455471c43daeaa4041998da50aa80d2bc096b225c558434ad0397ad4af066a7
6
+ metadata.gz: 91883afe7f0a0cf03c7daf35098d5298673f69f52b69c0aa45c79cc24e20594890c8077a28e4448403616db2693f44893f346377cc31faa2e8644a99cc9dcc18
7
+ data.tar.gz: f5a371c0b6413c30a9c33080bec055d06eb5464a4b971557059b8db5e69b2c86c2c2a51d580cfe134a85eca483ce8fea61fcb4d924457b2cd2dd35388597f39e
@@ -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"
@@ -754,5 +754,8 @@ module Graphiti
754
754
  MSG
755
755
  end
756
756
  end
757
+
758
+ class ConflictRequest < InvalidRequest
759
+ end
757
760
  end
758
761
  end
@@ -1,94 +1,24 @@
1
1
  module Graphiti
2
2
  class RequestValidator
3
- attr_reader :errors
3
+ delegate :validate,
4
+ :validate!,
5
+ :errors,
6
+ :deserialized_payload,
7
+ to: :@validator
4
8
 
5
9
  def initialize(root_resource, raw_params)
6
- @root_resource = root_resource
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
- def validate
12
- resource = @root_resource
13
- if (meta_type = deserialized_payload.meta[:type].try(:to_sym))
14
- if @root_resource.type != meta_type && @root_resource.polymorphic?
15
- resource = @root_resource.class.resource_for_type(meta_type).new
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
- Graphiti::Deserializer.new({})
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),
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Graphiti
2
- VERSION = "1.2.8"
2
+ VERSION = "1.2.9"
3
3
  end
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.8
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-21 00:00:00.000000000 Z
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