graphiti 2.1.1 → 2.1.2
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/Appraisals +5 -0
- data/CHANGELOG.md +8 -0
- data/gemfiles/rails_7_1.gemfile +1 -0
- data/gemfiles/rails_7_2.gemfile +1 -0
- data/gemfiles/rails_8_0.gemfile +1 -0
- data/lib/graphiti/filter_operators.rb +0 -1
- data/lib/graphiti/resource/configuration.rb +4 -2
- data/lib/graphiti/resource/dsl.rb +1 -1
- data/lib/graphiti/scoping/filter.rb +13 -1
- data/lib/graphiti/serializer.rb +1 -0
- data/lib/graphiti/sideload/has_many.rb +8 -3
- data/lib/graphiti/sideload/polymorphic_belongs_to.rb +0 -1
- data/lib/graphiti/stats/dsl.rb +0 -1
- data/lib/graphiti/util/public_id_sources.rb +31 -6
- data/lib/graphiti/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: af05146072fd78f20acdaa5b02efb3853936050991fdbec6b95c877e5d2135b5
|
|
4
|
+
data.tar.gz: 5edcc4716bb8e514aecd912e7ad5bc0c1e3db599b7048109060d3aee655ada9a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a343df743086251a11b6349f9a1a79b1cdcf370c57505c5fc65640e0a316524a98221c4b4dc34bbe51fca3357fff23a5fa03e9f3c26e37c478dd21e03669aec
|
|
7
|
+
data.tar.gz: 042a48e0dbf92f54d2b2cb2292bec09fbc4914ef7e2df4d2422401e0b93ee6b1078a15ec14d580f090c2aaf7fdaabb9b2b49ce623ea43480497598912ef9eb04
|
data/Appraisals
CHANGED
|
@@ -16,9 +16,12 @@
|
|
|
16
16
|
# Rails 8.0 / 8.1 -> gem "sqlite3", ">= 2.1"
|
|
17
17
|
# "~> 1.4" rather than "~> 1.4.0" is deliberate: it resolves to 1.7.x, which
|
|
18
18
|
# still builds on modern Rubies, where 1.4.x no longer does.
|
|
19
|
+
#
|
|
20
|
+
# Rails 7.1 through 8.0 pass quirks_mode to JSON.generate, which json 3 rejects.
|
|
19
21
|
|
|
20
22
|
appraise "rails-7-1" do
|
|
21
23
|
gem "rails", "~> 7.1.0"
|
|
24
|
+
gem "json", "< 3"
|
|
22
25
|
gem "rspec-rails"
|
|
23
26
|
gem "responders"
|
|
24
27
|
gem "sqlite3", "~> 1.4"
|
|
@@ -26,6 +29,7 @@ end
|
|
|
26
29
|
|
|
27
30
|
appraise "rails-7-2" do
|
|
28
31
|
gem "rails", "~> 7.2.0"
|
|
32
|
+
gem "json", "< 3"
|
|
29
33
|
gem "rspec-rails"
|
|
30
34
|
gem "responders"
|
|
31
35
|
gem "sqlite3", "~> 2.1"
|
|
@@ -33,6 +37,7 @@ end
|
|
|
33
37
|
|
|
34
38
|
appraise "rails-8-0" do
|
|
35
39
|
gem "rails", "~> 8.0.0"
|
|
40
|
+
gem "json", "< 3"
|
|
36
41
|
gem "rspec-rails"
|
|
37
42
|
gem "responders"
|
|
38
43
|
gem "sqlite3", "~> 2.1"
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
graphiti changelog
|
|
2
2
|
|
|
3
|
+
## [2.1.2](https://github.com/graphiti-api/graphiti/compare/v2.1.1...v2.1.2) (2026-10-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* a polymorphic resource renders each child that publishes a public id by that id instead of raising ([8d5e0fb](https://github.com/graphiti-api/graphiti/commit/8d5e0fbbdd71d706c3a57b11c866fc36727d6ef8))
|
|
9
|
+
* several resources can declare the same polymorphic_has_many while publishing public ids ([d6e332c](https://github.com/graphiti-api/graphiti/commit/d6e332c73e4ef7ed969e10c3444e802701f46232))
|
|
10
|
+
|
|
3
11
|
## [2.1.1](https://github.com/graphiti-api/graphiti/compare/v2.1.0...v2.1.1) (2026-09-07)
|
|
4
12
|
|
|
5
13
|
|
data/gemfiles/rails_7_1.gemfile
CHANGED
data/gemfiles/rails_7_2.gemfile
CHANGED
data/gemfiles/rails_8_0.gemfile
CHANGED
|
@@ -99,6 +99,7 @@ module Graphiti
|
|
|
99
99
|
def serializer=(val)
|
|
100
100
|
if val
|
|
101
101
|
if super(Class.new(val))
|
|
102
|
+
serializer.resource_class = self if serializer.respond_to?(:resource_class=)
|
|
102
103
|
apply_attributes_to_serializer
|
|
103
104
|
end
|
|
104
105
|
else
|
|
@@ -336,7 +337,7 @@ module Graphiti
|
|
|
336
337
|
attribute = attributes[attribute_name.to_sym]
|
|
337
338
|
return unless attribute && attribute[:readable] && !attribute[:proc]
|
|
338
339
|
|
|
339
|
-
source = public_id_source_for(attribute_name)
|
|
340
|
+
source = public_id_source_for(attribute_name) || (Graphiti.public_ids_declared? && Graphiti.public_id_sources.any(self, attribute_name))
|
|
340
341
|
raise Errors::PublicIdLeak.new(self, attribute_name, source) if source
|
|
341
342
|
end
|
|
342
343
|
|
|
@@ -348,10 +349,11 @@ module Graphiti
|
|
|
348
349
|
filter[:operators][:eq].nil? || Array(filter[:operators_taking_primary_keys]).include?(:eq)
|
|
349
350
|
end
|
|
350
351
|
|
|
351
|
-
def public_id_source_for(filter_name)
|
|
352
|
+
def public_id_source_for(filter_name, polymorphic_type = nil)
|
|
352
353
|
return unless Graphiti.public_ids_declared?
|
|
353
354
|
|
|
354
355
|
name = filter_name.to_sym
|
|
356
|
+
return Graphiti.public_id_sources[self, name, polymorphic_type] if polymorphic_type
|
|
355
357
|
return self if name == :id && config[:public_id_decode]
|
|
356
358
|
|
|
357
359
|
source = Graphiti.public_id_sources[self, name]
|
|
@@ -174,7 +174,7 @@ module Graphiti
|
|
|
174
174
|
|
|
175
175
|
# The hidden filter only ever sees keys Graphiti read off models, so its type is never used to cast anything.
|
|
176
176
|
def public_id_proc
|
|
177
|
-
proc { @resource.class.public_id_for(@object) }
|
|
177
|
+
proc { (self.class.resource_class || @resource.class).public_id_for(@object) }
|
|
178
178
|
end
|
|
179
179
|
|
|
180
180
|
def apply_public_id
|
|
@@ -65,7 +65,7 @@ module Graphiti
|
|
|
65
65
|
elsif filter.values[0][:internal]
|
|
66
66
|
raise Errors::InvalidAttributeAccess.new(resource, param_name, :filterable, request: true)
|
|
67
67
|
else
|
|
68
|
-
source = resource.class.public_id_source_for(filter.keys[0])
|
|
68
|
+
source = resource.class.public_id_source_for(filter.keys[0], requested_polymorphic_type(filter.keys[0]))
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
normalize_param(filter, param_value).each do |operator, value|
|
|
@@ -84,6 +84,18 @@ module Graphiti
|
|
|
84
84
|
end
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
+
def requested_polymorphic_type(filter_name)
|
|
88
|
+
return unless Graphiti.public_ids_declared?
|
|
89
|
+
|
|
90
|
+
type_filter = Graphiti.public_id_sources.type_filter(resource.class, filter_name)
|
|
91
|
+
return unless type_filter
|
|
92
|
+
|
|
93
|
+
requested = filter_param[type_filter]
|
|
94
|
+
requested = requested.values.first if requested.is_a?(Hash)
|
|
95
|
+
types = requested.is_a?(String) ? requested.split(",") : Array(requested)
|
|
96
|
+
types.first if types.one?
|
|
97
|
+
end
|
|
98
|
+
|
|
87
99
|
# A block asking for primary_keys: was sent public ids, which cast like the source's id rather than the key.
|
|
88
100
|
def validated_and_cast(filter, param_name, value, public_id_source)
|
|
89
101
|
type = Types[filter.values[0][:type]]
|
data/lib/graphiti/serializer.rb
CHANGED
|
@@ -18,6 +18,7 @@ module Graphiti
|
|
|
18
18
|
# relationship is told apart from one already applied, and from one the
|
|
19
19
|
# application wrote by hand.
|
|
20
20
|
class_attribute :relationship_sideloads
|
|
21
|
+
class_attribute :resource_class
|
|
21
22
|
self.attributes_applied_via_resource = []
|
|
22
23
|
self.extra_attributes_applied_via_resource = []
|
|
23
24
|
# See #requested_relationships
|
|
@@ -31,10 +31,14 @@ class Graphiti::Sideload::HasMany < Graphiti::Sideload
|
|
|
31
31
|
def link_hides_primary_key?
|
|
32
32
|
return true unless links_by_public_id?
|
|
33
33
|
|
|
34
|
-
resource_class.public_id_source_for(inverse_filter) == parent_resource_class &&
|
|
34
|
+
resource_class.public_id_source_for(inverse_filter, polymorphic_type) == parent_resource_class &&
|
|
35
35
|
resource_class.filter_accepts_public_ids?(inverse_filter)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
def polymorphic_type
|
|
39
|
+
parent_resource_class.model.name if polymorphic_has_many?
|
|
40
|
+
end
|
|
41
|
+
|
|
38
42
|
def links_by_public_id?
|
|
39
43
|
@links_by_public_id ||= Graphiti.public_ids_declared? && primary_key == :id && !!parent_resource_class&.publishes_public_id?
|
|
40
44
|
end
|
|
@@ -43,7 +47,8 @@ class Graphiti::Sideload::HasMany < Graphiti::Sideload
|
|
|
43
47
|
return unless links_by_public_id? && resource_class_loaded?
|
|
44
48
|
return if parent_resource_class.abstract_class? || !parent_resource_class.model_loaded?
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
type_filter = :"#{polymorphic_as}_type" if polymorphic_has_many?
|
|
51
|
+
Graphiti.public_id_sources.register(resource_class, inverse_filter, parent_resource_class, polymorphic_type: polymorphic_type, type_filter: type_filter)
|
|
47
52
|
resource_class.guard_public_id_leak!(inverse_filter)
|
|
48
53
|
end
|
|
49
54
|
|
|
@@ -54,7 +59,7 @@ class Graphiti::Sideload::HasMany < Graphiti::Sideload
|
|
|
54
59
|
end
|
|
55
60
|
|
|
56
61
|
def internal_parent_filter(parents, filter_name)
|
|
57
|
-
return parent_filter(parents) unless resource_class.public_id_source_for(filter_name)
|
|
62
|
+
return parent_filter(parents) unless resource_class.public_id_source_for(filter_name, polymorphic_type)
|
|
58
63
|
|
|
59
64
|
Graphiti::Util::InternalParam.new(ids_for_parents(parents))
|
|
60
65
|
end
|
data/lib/graphiti/stats/dsl.rb
CHANGED
|
@@ -3,24 +3,49 @@ module Graphiti
|
|
|
3
3
|
class PublicIdSources
|
|
4
4
|
def initialize
|
|
5
5
|
@sources = {}
|
|
6
|
+
@type_filters = {}
|
|
6
7
|
end
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
# Every parent of a polymorphic has_many shares one inverse filter, so each registers under the type its type filter names.
|
|
10
|
+
def register(child_resource_class, filter_name, parent_resource_class, polymorphic_type: nil, type_filter: nil)
|
|
11
|
+
key = [child_resource_class.name, filter_name.to_sym, polymorphic_type]
|
|
10
12
|
registered = @sources[key]
|
|
11
13
|
if registered && registered.name != parent_resource_class.name
|
|
12
14
|
raise Errors::ConflictingPublicIdSource.new(child_resource_class, filter_name, registered, parent_resource_class)
|
|
13
15
|
end
|
|
14
16
|
@sources[key] = parent_resource_class
|
|
17
|
+
@type_filters[[child_resource_class.name, filter_name.to_sym]] = type_filter.to_sym if type_filter
|
|
15
18
|
end
|
|
16
19
|
|
|
17
|
-
def [](child_resource_class, filter_name)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
source = @sources[[klass.name, filter_name.to_sym]]
|
|
20
|
+
def [](child_resource_class, filter_name, polymorphic_type = nil)
|
|
21
|
+
each_ancestor_name(child_resource_class) do |name|
|
|
22
|
+
source = @sources[[name, filter_name.to_sym, polymorphic_type]]
|
|
21
23
|
return source if source
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def type_filter(child_resource_class, filter_name)
|
|
28
|
+
each_ancestor_name(child_resource_class) do |name|
|
|
29
|
+
type_filter = @type_filters[[name, filter_name.to_sym]]
|
|
30
|
+
return type_filter if type_filter
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def any(child_resource_class, filter_name)
|
|
35
|
+
each_ancestor_name(child_resource_class) do |name|
|
|
36
|
+
entry = @sources.find { |(child, filter, _type), _source| child == name && filter == filter_name.to_sym }
|
|
37
|
+
return entry.last if entry
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def each_ancestor_name(klass)
|
|
44
|
+
while klass
|
|
45
|
+
yield klass.name
|
|
22
46
|
klass = klass.superclass
|
|
23
47
|
end
|
|
48
|
+
nil
|
|
24
49
|
end
|
|
25
50
|
end
|
|
26
51
|
end
|
data/lib/graphiti/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: graphiti
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.1.
|
|
4
|
+
version: 2.1.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Lee Richmond
|
|
@@ -439,7 +439,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
439
439
|
- !ruby/object:Gem::Version
|
|
440
440
|
version: '0'
|
|
441
441
|
requirements: []
|
|
442
|
-
rubygems_version: 4.0.
|
|
442
|
+
rubygems_version: 4.0.20
|
|
443
443
|
specification_version: 4
|
|
444
444
|
summary: Easily build jsonapi.org-compatible APIs
|
|
445
445
|
test_files: []
|