hq-graphql 2.1.2 → 2.1.3
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/lib/hq/graphql.rb +4 -0
- data/lib/hq/graphql/active_record_extensions.rb +1 -1
- data/lib/hq/graphql/config.rb +1 -0
- data/lib/hq/graphql/object.rb +0 -3
- data/lib/hq/graphql/object_association.rb +36 -19
- data/lib/hq/graphql/paginated_association_loader.rb +58 -23
- data/lib/hq/graphql/resource.rb +22 -9
- data/lib/hq/graphql/types.rb +4 -4
- data/lib/hq/graphql/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: 50ee2af7af4309a1fb549c15b88abdb8bda3454eacfdce5136f1997b814c52fb
|
4
|
+
data.tar.gz: 9dc0b893d4ad8bf8a999cba996224488be9c394e55f83ef979460ac19f96c381
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f4940761bdccfd01c867e21f69ab66a5459d8b151024ebd79679906ea62fa6c5d2208ce311a339ed720021c2b43221e51ebb092de8f4bff87a15bc3e3f07f376
|
7
|
+
data.tar.gz: 00a7683630411f065d954132918b3449ef6aec9419de30702dd43bf99ad34fb8dbaf5ee1ae248759f3029006db3863257fe86a99d7b89a7456123c82d52ef7b3
|
data/lib/hq/graphql.rb
CHANGED
data/lib/hq/graphql/config.rb
CHANGED
data/lib/hq/graphql/object.rb
CHANGED
@@ -63,9 +63,6 @@ module HQ
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def field_from_association(association, auto_nil:, internal_association: false, &block)
|
66
|
-
# The PaginationAssociationLoader doesn't support through associations yet
|
67
|
-
return if association.through_reflection? && ::HQ::GraphQL.use_experimental_associations?
|
68
|
-
|
69
66
|
association_klass = association.klass
|
70
67
|
name = association.name
|
71
68
|
klass = model_klass
|
@@ -3,6 +3,33 @@
|
|
3
3
|
module HQ
|
4
4
|
module GraphQL
|
5
5
|
module ObjectAssociation
|
6
|
+
def reflect_on_association(association)
|
7
|
+
resource_reflections[association.to_s]&.reflection(model_klass)
|
8
|
+
end
|
9
|
+
|
10
|
+
def update(name, &block)
|
11
|
+
resource_reflections[name.to_s] = UpdatedReflection.new(name, block)
|
12
|
+
end
|
13
|
+
|
14
|
+
def belongs_to(name, scope = nil, **options, &block)
|
15
|
+
add_reflection(name, scope, options, :belongs_to, block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_many(name, scope = nil, through: nil, **options, &block)
|
19
|
+
raise TypeError, "has_many through is unsupported" if through
|
20
|
+
add_reflection(name, scope, options, :has_many, block)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def resource_reflections
|
26
|
+
@resource_reflections ||= {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_reflection(name, scope, options, macro, block)
|
30
|
+
resource_reflections[name.to_s] = ResourceReflection.new(name, scope, options, macro, block)
|
31
|
+
end
|
32
|
+
|
6
33
|
class ResourceReflection
|
7
34
|
attr_reader :name, :scope, :options, :macro, :block
|
8
35
|
|
@@ -23,27 +50,17 @@ module HQ
|
|
23
50
|
end
|
24
51
|
end
|
25
52
|
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
def belongs_to(name, scope = nil, **options, &block)
|
31
|
-
add_reflection(name, scope, options, :belongs_to, block)
|
32
|
-
end
|
33
|
-
|
34
|
-
def has_many(name, scope = nil, through: nil, **options, &block)
|
35
|
-
raise TypeError, "has_many through is unsupported" if through
|
36
|
-
add_reflection(name, scope, options, :has_many, block)
|
37
|
-
end
|
53
|
+
class UpdatedReflection
|
54
|
+
attr_reader :name, :block
|
38
55
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
56
|
+
def initialize(name, block)
|
57
|
+
@name = name
|
58
|
+
@block = block
|
59
|
+
end
|
44
60
|
|
45
|
-
|
46
|
-
|
61
|
+
def reflection(model_klass)
|
62
|
+
model_klass.reflect_on_association(name)
|
63
|
+
end
|
47
64
|
end
|
48
65
|
end
|
49
66
|
end
|
@@ -39,7 +39,8 @@ module HQ
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def perform(records)
|
42
|
-
|
42
|
+
values = records.map { |r| source_value(r) }
|
43
|
+
scope =
|
43
44
|
if @limit || @offset
|
44
45
|
# If a limit or offset is added, then we need to transform the query
|
45
46
|
# into a lateral join so that we can limit on groups of data.
|
@@ -57,53 +58,71 @@ module HQ
|
|
57
58
|
# > ) a_top ON TRUE
|
58
59
|
# > WHERE addresses.user_id IN ($1, $2, ..., $N)
|
59
60
|
# > ORDER BY a_top.created_at DESC
|
60
|
-
inner_table
|
61
|
-
|
61
|
+
inner_table = association_class.arel_table
|
62
|
+
lateral_join_table = through_reflection? ? through_association.klass.arel_table : inner_table
|
63
|
+
from_table = lateral_join_table.alias("outer")
|
62
64
|
|
63
65
|
inside_scope = default_scope.
|
64
66
|
select(inner_table[::Arel.star]).
|
65
67
|
from(inner_table).
|
66
|
-
where(
|
68
|
+
where(lateral_join_table[target_join_key].eq(from_table[target_join_key])).
|
67
69
|
reorder(arel_order(inner_table)).
|
68
70
|
limit(@limit).
|
69
71
|
offset(@offset)
|
70
72
|
|
71
|
-
|
73
|
+
if through_reflection?
|
74
|
+
# expose the through_reflection key
|
75
|
+
inside_scope = inside_scope.select(lateral_join_table[target_join_key])
|
76
|
+
end
|
77
|
+
|
78
|
+
lateral_table = ::Arel::Table.new("top")
|
72
79
|
association_class.
|
73
|
-
select(
|
74
|
-
from(
|
75
|
-
|
76
|
-
|
77
|
-
reorder(arel_order(
|
80
|
+
select(lateral_table[::Arel.star]).distinct.
|
81
|
+
from(from_table).
|
82
|
+
where(from_table[target_join_key].in(values)).
|
83
|
+
joins("INNER JOIN LATERAL (#{inside_scope.to_sql}) #{lateral_table.name} ON TRUE").
|
84
|
+
reorder(arel_order(lateral_table))
|
78
85
|
else
|
79
|
-
default_scope.
|
80
|
-
|
81
|
-
|
86
|
+
scope = default_scope.reorder(arel_order(association_class.arel_table))
|
87
|
+
|
88
|
+
if through_reflection?
|
89
|
+
scope.where(through_association.name => { target_join_key => values }).
|
90
|
+
# expose the through_reflection key
|
91
|
+
select(association_class.arel_table[::Arel.star], through_association.klass.arel_table[target_join_key])
|
92
|
+
else
|
93
|
+
scope.where(target_join_key => values)
|
94
|
+
end
|
82
95
|
end
|
83
96
|
|
84
97
|
results = scope.to_a
|
85
98
|
records.each do |record|
|
86
|
-
fulfill(record,
|
99
|
+
fulfill(record, target_value(record, results)) unless fulfilled?(record)
|
87
100
|
end
|
88
101
|
end
|
89
102
|
|
90
103
|
private
|
91
104
|
|
92
|
-
def
|
93
|
-
belongs_to? ? association.
|
105
|
+
def source_join_key
|
106
|
+
belongs_to? ? association.foreign_key : association.association_primary_key
|
94
107
|
end
|
95
108
|
|
96
|
-
def
|
97
|
-
|
98
|
-
results.send(enumerator) { |r| r.send(association_key) == join_value(record) }
|
109
|
+
def source_value(record)
|
110
|
+
record.send(source_join_key)
|
99
111
|
end
|
100
112
|
|
101
|
-
def
|
102
|
-
|
113
|
+
def target_join_key
|
114
|
+
if through_reflection?
|
115
|
+
through_association.foreign_key
|
116
|
+
elsif belongs_to?
|
117
|
+
association.association_primary_key
|
118
|
+
else
|
119
|
+
association.foreign_key
|
120
|
+
end
|
103
121
|
end
|
104
122
|
|
105
|
-
def
|
106
|
-
|
123
|
+
def target_value(record, results)
|
124
|
+
enumerator = has_many? ? :select : :detect
|
125
|
+
results.send(enumerator) { |r| r.send(target_join_key) == source_value(record) }
|
107
126
|
end
|
108
127
|
|
109
128
|
def default_scope
|
@@ -111,6 +130,14 @@ module HQ
|
|
111
130
|
scope = association.scopes.reduce(scope, &:merge)
|
112
131
|
scope = association_class.default_scopes.reduce(scope, &:merge)
|
113
132
|
scope = scope.merge(@scope) if @scope
|
133
|
+
|
134
|
+
if through_reflection?
|
135
|
+
source = association_class.arel_table
|
136
|
+
target = through_association.klass.arel_table
|
137
|
+
join = source.join(target).on(target[association.foreign_key].eq(source[source_join_key]))
|
138
|
+
scope = scope.joins(join.join_sources)
|
139
|
+
end
|
140
|
+
|
114
141
|
scope
|
115
142
|
end
|
116
143
|
|
@@ -122,6 +149,14 @@ module HQ
|
|
122
149
|
association.macro == :has_many
|
123
150
|
end
|
124
151
|
|
152
|
+
def through_association
|
153
|
+
association.through_reflection
|
154
|
+
end
|
155
|
+
|
156
|
+
def through_reflection?
|
157
|
+
association.through_reflection?
|
158
|
+
end
|
159
|
+
|
125
160
|
def association
|
126
161
|
if @internal_association
|
127
162
|
Types[@model].reflect_on_association(@association_name)
|
data/lib/hq/graphql/resource.rb
CHANGED
@@ -59,12 +59,20 @@ module HQ
|
|
59
59
|
@input_klass ||= build_input_object
|
60
60
|
end
|
61
61
|
|
62
|
-
def
|
63
|
-
@
|
64
|
-
end
|
65
|
-
|
66
|
-
def
|
67
|
-
@
|
62
|
+
def nil_query_object
|
63
|
+
@nil_query_object ||= build_graphql_object(name: "#{graphql_name}Copy", auto_nil: false)
|
64
|
+
end
|
65
|
+
|
66
|
+
def query_object
|
67
|
+
@query_object ||= begin
|
68
|
+
if @query_object_options
|
69
|
+
options, block = @query_object_options
|
70
|
+
@query_object_options = nil
|
71
|
+
build_graphql_object(**options, &block)
|
72
|
+
else
|
73
|
+
build_graphql_object
|
74
|
+
end
|
75
|
+
end
|
68
76
|
end
|
69
77
|
|
70
78
|
def sort_fields_enum
|
@@ -89,7 +97,11 @@ module HQ
|
|
89
97
|
end
|
90
98
|
|
91
99
|
def query(**options, &block)
|
92
|
-
@
|
100
|
+
@query_object_options = [options, block]
|
101
|
+
end
|
102
|
+
|
103
|
+
def query_class(klass)
|
104
|
+
@query_class = klass
|
93
105
|
end
|
94
106
|
|
95
107
|
def sort_fields(*fields)
|
@@ -100,7 +112,7 @@ module HQ
|
|
100
112
|
resource = self
|
101
113
|
resolver = -> {
|
102
114
|
Class.new(::GraphQL::Schema::Resolver) do
|
103
|
-
type = is_array ? [resource.
|
115
|
+
type = is_array ? [resource.query_object] : resource.query_object
|
104
116
|
type type, null: null
|
105
117
|
class_eval(&block) if block
|
106
118
|
end
|
@@ -156,7 +168,8 @@ module HQ
|
|
156
168
|
def build_graphql_object(name: graphql_name, **options, &block)
|
157
169
|
scoped_graphql_name = name
|
158
170
|
scoped_model_name = model_name
|
159
|
-
|
171
|
+
object_class = @query_class || ::HQ::GraphQL.default_object_class || ::HQ::GraphQL::Object
|
172
|
+
Class.new(object_class) do
|
160
173
|
graphql_name scoped_graphql_name
|
161
174
|
|
162
175
|
with_model scoped_model_name, **options
|
data/lib/hq/graphql/types.rb
CHANGED
@@ -13,7 +13,7 @@ module HQ
|
|
13
13
|
def self.registry
|
14
14
|
@registry ||= Hash.new do |hash, options|
|
15
15
|
klass, nil_klass = Array(options)
|
16
|
-
hash[
|
16
|
+
hash[options] = nil_klass ? nil_query_object(klass) : klass_for(klass)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
@@ -61,12 +61,12 @@ module HQ
|
|
61
61
|
class << self
|
62
62
|
private
|
63
63
|
|
64
|
-
def
|
65
|
-
find_klass(klass_or_string, :
|
64
|
+
def nil_query_object(klass_or_string)
|
65
|
+
find_klass(klass_or_string, :nil_query_object)
|
66
66
|
end
|
67
67
|
|
68
68
|
def klass_for(klass_or_string)
|
69
|
-
find_klass(klass_or_string, :
|
69
|
+
find_klass(klass_or_string, :query_object)
|
70
70
|
end
|
71
71
|
|
72
72
|
def find_klass(klass_or_string, method)
|
data/lib/hq/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hq-graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.1.
|
4
|
+
version: 2.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Danny Jones
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-06-
|
11
|
+
date: 2020-06-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|