graphiti 1.2.20 → 1.2.28

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: db3bae18176af83f5ac1b88c50546cefcf57f38245e69a5fcbb45f3028ca2273
4
- data.tar.gz: 6ee077531421990da7d4fe80bedfada136b299420d7b82566e9261b7f1a2800a
3
+ metadata.gz: 62001af4fe0f26688b07d4f9e0565b2f2cd1e875fa299c982ef5986e9db66836
4
+ data.tar.gz: 28ba11c786c4e7eb7be0612c2bd678434156e667110623046438d6e1a1c9b67d
5
5
  SHA512:
6
- metadata.gz: 0ceeabdfe64a03c4d709353a763a49224a14eb068633dcbd67c4ef2ee370dbdcd47781b0a4c15f19fe02956712aaca3dda42a17eb514bf44b821725261dc7ddf
7
- data.tar.gz: d329a82610a68dec935625edbd502e85c39907a64261f170d342f632c82dc211c0fab440edbb16210483eb2154f44d403a2f13d5f3b9b7e41d4aafe68c66bf4a
6
+ metadata.gz: f5ed46d521816170aec5448ee816a5ea336faf791034c63c19a8bce64982d42ed9e19420b3d9e4f4782a78b20f8ba475636d920574b360d947991201eeb50bc8
7
+ data.tar.gz: f8578aa1f893d531146204cc43c142ddc58b4cc98a272e08c33ab13fd308530f04d4bdd273ef638ff88017f88051c44a9c76b0e1335a47c80ec9eee6b412d87b
data/Gemfile CHANGED
@@ -3,8 +3,6 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in graphiti.gemspec
4
4
  gemspec
5
5
 
6
- gem "standard", "0.4.7"
7
-
8
6
  group :test do
9
7
  gem "pry"
10
8
  gem "pry-byebug", platform: [:mri]
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 4.1"
5
+ gem "rails", "~> 4.2"
6
6
  gem "rspec-rails"
7
7
  gem "sqlite3", "~> 1.3.6"
8
8
  gem "database_cleaner"
@@ -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,7 +1,7 @@
1
1
  module Graphiti
2
2
  module Adapters
3
3
  class ActiveRecord < ::Graphiti::Adapters::Abstract
4
- require "graphiti/adapters/active_record/inferrence"
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"
@@ -1,5 +1,5 @@
1
1
  class Graphiti::Adapters::ActiveRecord::BelongsToSideload < Graphiti::Sideload::BelongsTo
2
- include Graphiti::Adapters::ActiveRecord::Inferrence
2
+ include Graphiti::Adapters::ActiveRecord::Inference
3
3
 
4
4
  def default_base_scope
5
5
  resource_class.model.all
@@ -1,5 +1,5 @@
1
1
  class Graphiti::Adapters::ActiveRecord::HasManySideload < Graphiti::Sideload::HasMany
2
- include Graphiti::Adapters::ActiveRecord::Inferrence
2
+ include Graphiti::Adapters::ActiveRecord::Inference
3
3
 
4
4
  def default_base_scope
5
5
  resource_class.model.all
@@ -1,5 +1,5 @@
1
1
  class Graphiti::Adapters::ActiveRecord::HasOneSideload < Graphiti::Sideload::HasOne
2
- include Graphiti::Adapters::ActiveRecord::Inferrence
2
+ include Graphiti::Adapters::ActiveRecord::Inference
3
3
 
4
4
  def default_base_scope
5
5
  resource_class.model.all
@@ -1,6 +1,6 @@
1
- module Graphiti::Adapters::ActiveRecord::Inferrence
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 inferrence
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
- .preload(through_relationship_name)
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
@@ -10,6 +10,8 @@ module Graphiti
10
10
 
11
11
  class << self
12
12
  def on_data(name, start, stop, id, payload)
13
+ return [] unless enabled
14
+
13
15
  took = ((stop - start) * 1000.0).round(2)
14
16
  params = scrub_params(payload[:params])
15
17
 
@@ -24,7 +26,7 @@ module Graphiti
24
26
  end
25
27
  end
26
28
 
27
- def on_data_exception(payload, params)
29
+ private def on_data_exception(payload, params)
28
30
  unless payload[:exception_object].instance_variable_get(:@__graphiti_debug)
29
31
  add_chunk do |logs, json|
30
32
  logs << ["\n=== Graphiti Debug ERROR", :red, true]
@@ -49,11 +51,11 @@ module Graphiti
49
51
  end
50
52
  end
51
53
 
52
- def results(raw_results)
54
+ private def results(raw_results)
53
55
  raw_results.map { |r| "[#{r.class.name}, #{r.id.inspect}]" }.join(", ")
54
56
  end
55
57
 
56
- def on_sideload_data(payload, params, took)
58
+ private def on_sideload_data(payload, params, took)
57
59
  sideload = payload[:sideload]
58
60
  results = results(payload[:results])
59
61
  add_chunk(payload[:resource], payload[:parent]) do |logs, json|
@@ -72,7 +74,7 @@ module Graphiti
72
74
  end
73
75
  end
74
76
 
75
- def on_primary_data(payload, params, took)
77
+ private def on_primary_data(payload, params, took)
76
78
  results = results(payload[:results])
77
79
  add_chunk(payload[:resource], payload[:parent]) do |logs, json|
78
80
  logs << [""]
@@ -90,6 +92,8 @@ module Graphiti
90
92
  end
91
93
 
92
94
  def on_render(name, start, stop, id, payload)
95
+ return [] unless enabled
96
+
93
97
  add_chunk do |logs|
94
98
  took = ((stop - start) * 1000.0).round(2)
95
99
  logs << [""]
@@ -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 raw_params["action"]
16
- when "update" then
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
@@ -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
@@ -5,6 +5,7 @@ module Graphiti
5
5
 
6
6
  class_methods do
7
7
  def filter(name, *args, &blk)
8
+ name = name.to_sym
8
9
  opts = args.extract_options!
9
10
  type_override = args[0]
10
11
 
@@ -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
 
@@ -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
@@ -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 = evaluate_flag(opts[:readable])
30
- @writable = evaluate_flag(opts[: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
 
@@ -1,3 +1,3 @@
1
1
  module Graphiti
2
- VERSION = "1.2.20"
2
+ VERSION = "1.2.28"
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.20
4
+ version: 1.2.28
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-07-17 00:00:00.000000000 Z
11
+ date: 2020-10-19 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,7 +262,7 @@ 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/inferrence.rb
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
@@ -327,7 +341,7 @@ homepage: https://github.com/graphiti-api/graphiti
327
341
  licenses:
328
342
  - MIT
329
343
  metadata: {}
330
- post_install_message:
344
+ post_install_message:
331
345
  rdoc_options: []
332
346
  require_paths:
333
347
  - lib
@@ -343,7 +357,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
343
357
  version: '0'
344
358
  requirements: []
345
359
  rubygems_version: 3.0.6
346
- signing_key:
360
+ signing_key:
347
361
  specification_version: 4
348
362
  summary: Easily build jsonapi.org-compatible APIs
349
363
  test_files: []