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 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