graphql_includable 0.3.1 → 0.4.0.alpha.1
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/graphql_includable/concern.rb +5 -11
- data/lib/graphql_includable/relay/edge_with_node.rb +1 -2
- data/lib/graphql_includable/relay/edge_with_node_connection.rb +26 -4
- data/lib/graphql_includable/relay/edge_with_node_connection_type.rb +5 -2
- data/lib/graphql_includable/relay/instrumentation/connection.rb +46 -10
- data/lib/graphql_includable/resolver.rb +9 -5
- data/lib/graphql_includable.rb +5 -3
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b4931965d11fc59002d2f514361dc3d30e0a17b
|
4
|
+
data.tar.gz: 2d0d4f23a686bd46d19d3447d989402364f16849
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2be6bc900f6e21eadc3eafda2cf236a35b9d1366e67ace38d392ff4499b3a7bfb50576541edc994e5aa22d0a05018a1d9ba3dae2eef56e28323d8139f27e2ca9
|
7
|
+
data.tar.gz: c42a7b0b009ed5078e786bdbac8e2c25bcd6f192333bc40c02e3d74f96fa373809ddf3aa419be5e28f080a1b9e325574653284921d107a29b578cb56aa3dab23
|
@@ -6,17 +6,11 @@ module GraphQLIncludable
|
|
6
6
|
|
7
7
|
module ClassMethods
|
8
8
|
def includes_from_graphql(ctx)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
Resolver.includes_for_node(node, manager)
|
15
|
-
|
16
|
-
generated_includes = manager.includes
|
17
|
-
instrument[:includes] = generated_includes
|
18
|
-
includes(generated_includes)
|
19
|
-
end
|
9
|
+
node = Resolver.find_node_by_return_type(ctx.irep_node, name)
|
10
|
+
manager = IncludesManager.new(nil)
|
11
|
+
Resolver.includes_for_node(node, manager)
|
12
|
+
puts "INCLUDES!!! #{manager.includes}"
|
13
|
+
includes(manager.includes)
|
20
14
|
rescue => e
|
21
15
|
Rails.logger.error(e)
|
22
16
|
self
|
@@ -3,8 +3,7 @@ module GraphQLIncludable
|
|
3
3
|
class EdgeWithNode < GraphQL::Relay::Edge
|
4
4
|
def initialize(node, connection)
|
5
5
|
@edge = node
|
6
|
-
|
7
|
-
node = @edge.public_send(edge_to_node_property)
|
6
|
+
node = connection.edge_to_node(@edge) # TODO: Make lazy
|
8
7
|
super(node, connection)
|
9
8
|
end
|
10
9
|
|
@@ -1,12 +1,17 @@
|
|
1
1
|
module GraphQLIncludable
|
2
2
|
module Relay
|
3
3
|
class ConnectionEdgesAndNodes
|
4
|
-
attr_reader :parent, :edges_property, :nodes_property
|
4
|
+
attr_reader :parent, :args, :ctx, :edges_property, :nodes_property, :edge_to_node_property, :edges_resolver, :nodes_resolver
|
5
5
|
|
6
|
-
def initialize(parent, edges_property, nodes_property)
|
6
|
+
def initialize(parent, args, ctx, edges_property, nodes_property, edge_to_node_property, edges_resolver, nodes_resolver)
|
7
7
|
@parent = parent
|
8
|
+
@args = args
|
9
|
+
@ctx = ctx
|
8
10
|
@edges_property = edges_property
|
9
11
|
@nodes_property = nodes_property
|
12
|
+
@edge_to_node_property = edge_to_node_property
|
13
|
+
@edges_resolver = edges_resolver
|
14
|
+
@nodes_resolver = nodes_resolver
|
10
15
|
end
|
11
16
|
end
|
12
17
|
|
@@ -23,14 +28,14 @@ module GraphQLIncludable
|
|
23
28
|
end
|
24
29
|
|
25
30
|
def fetch_edges
|
26
|
-
@loaded_edges ||= @connection_edges_and_nodes.
|
31
|
+
@loaded_edges ||= @connection_edges_and_nodes.edges_resolver.call(@connection_edges_and_nodes.parent, args, ctx)
|
27
32
|
# Set nodes to make underlying BaseConnection work
|
28
33
|
@nodes = @loaded_edges
|
29
34
|
@loaded_edges
|
30
35
|
end
|
31
36
|
|
32
37
|
def fetch_nodes
|
33
|
-
@loaded_nodes ||= @connection_edges_and_nodes.
|
38
|
+
@loaded_nodes ||= @connection_edges_and_nodes.nodes_resolver.call(@connection_edges_and_nodes.parent, args, ctx)
|
34
39
|
# Set nodes to make underlying BaseConnection work
|
35
40
|
@nodes = @loaded_nodes
|
36
41
|
@loaded_nodes
|
@@ -41,8 +46,25 @@ module GraphQLIncludable
|
|
41
46
|
super
|
42
47
|
end
|
43
48
|
|
49
|
+
def edge_to_node(edge)
|
50
|
+
edge.public_send(@connection_edges_and_nodes.edge_to_node_property)
|
51
|
+
end
|
52
|
+
|
53
|
+
def total_count
|
54
|
+
@nodes = determin_page_info_nodes
|
55
|
+
@nodes.size
|
56
|
+
end
|
57
|
+
|
44
58
|
private
|
45
59
|
|
60
|
+
def args
|
61
|
+
@connection_edges_and_nodes.args
|
62
|
+
end
|
63
|
+
|
64
|
+
def ctx
|
65
|
+
@connection_edges_and_nodes.ctx
|
66
|
+
end
|
67
|
+
|
46
68
|
def determin_page_info_nodes
|
47
69
|
# If the query asks for `pageInfo` before `edges` or `nodes`, we dont directly know which to use most efficently.
|
48
70
|
# We can have a guess by checking if either of the associations are preloaded
|
@@ -2,7 +2,7 @@ module GraphQLIncludable
|
|
2
2
|
module Relay
|
3
3
|
class EdgeWithNodeConnectionType
|
4
4
|
def self.create_type(
|
5
|
-
wrapped_type,
|
5
|
+
wrapped_type,
|
6
6
|
edge_type: wrapped_type.edge_type, edge_class: EdgeWithNode, nodes_field: GraphQL::Relay::ConnectionType.default_nodes_field, &block
|
7
7
|
)
|
8
8
|
custom_edge_class = edge_class
|
@@ -10,7 +10,10 @@ module GraphQLIncludable
|
|
10
10
|
GraphQL::ObjectType.define do
|
11
11
|
name("#{wrapped_type.name}Connection")
|
12
12
|
description("The connection type for #{wrapped_type.name}.")
|
13
|
-
|
13
|
+
|
14
|
+
field :totalCount, types.Int, "Total count.", property: :total_count
|
15
|
+
|
16
|
+
field :edges, types[edge_type], "A list of edges.", edge_class: custom_edge_class, property: :fetch_edges, _includable_connection_marker: true
|
14
17
|
|
15
18
|
if nodes_field
|
16
19
|
field :nodes, types[wrapped_type], "A list of nodes.", property: :fetch_nodes
|
@@ -3,29 +3,65 @@ module GraphQLIncludable
|
|
3
3
|
module Instrumentation
|
4
4
|
class Connection
|
5
5
|
def instrument(_type, field)
|
6
|
-
return field unless field
|
6
|
+
return field unless is_edge_with_node_connection?(field)
|
7
7
|
|
8
|
-
|
9
|
-
requires_instrumentation = required_metadata.any? { |key| field.metadata.key?(key) }
|
8
|
+
raise ArgumentError.new('Connection does not support fetching using :property') if field.property.present?
|
10
9
|
|
11
|
-
|
12
|
-
|
10
|
+
validate!(field)
|
11
|
+
edge_to_node_property = field.metadata[:edge_to_node_property]
|
12
|
+
explicit_includes = field.metadata[:includes]
|
13
|
+
edges_prop = explicit_includes[:edges]
|
14
|
+
nodes_prop = explicit_includes[:nodes]
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
if is_proc_based?(field)
|
17
|
+
edges_resolver = field.metadata[:resolve_edges]
|
18
|
+
nodes_resolver = field.metadata[:resolve_nodes]
|
19
|
+
else
|
20
|
+
# Use the edges and nodes symbols from the incldues pattern as the propeties to fetch
|
21
|
+
edges_resolver = ->(obj, args, ctx) { obj.public_send(edges_prop) }
|
22
|
+
nodes_resolver = ->(obj, args, ctx) { obj.public_send(nodes_prop) }
|
23
|
+
end
|
18
24
|
|
19
25
|
_original_resolve = field.resolve_proc
|
20
26
|
new_resolve_proc = ->(obj, args, ctx) do
|
21
|
-
ConnectionEdgesAndNodes.new(obj, edges_prop, nodes_prop)
|
27
|
+
ConnectionEdgesAndNodes.new(obj, args, ctx, edges_prop, nodes_prop, edge_to_node_property, edges_resolver, nodes_resolver)
|
22
28
|
end
|
23
29
|
|
24
30
|
field.redefine { resolve(new_resolve_proc) }
|
25
31
|
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def is_edge_with_node_connection?(field)
|
36
|
+
field.connection? && field.type.fields['edges'].metadata.key?(:_includable_connection_marker)
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_proc_based?(field)
|
40
|
+
required_metadata = [:resolve_edges, :resolve_nodes]
|
41
|
+
has_a_resolver = required_metadata.any? { |key| field.metadata.key?(key) }
|
42
|
+
|
43
|
+
return false unless has_a_resolver
|
44
|
+
raise ArgumentError.new("Missing one of #{required_metadata}") unless required_metadata.all? { |key| field.metadata.key?(key) }
|
45
|
+
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate!(field)
|
50
|
+
raise ArgumentError.new('Missing edge_to_node_property definition for field') unless field.metadata.key?(:edge_to_node_property)
|
51
|
+
raise ArgumentError.new(':edge_to_node_property must be a symbol') unless field.metadata[:edge_to_node_property].is_a?(Symbol)
|
52
|
+
|
53
|
+
raise ArgumentError.new('Missing includes definition for field') unless field.metadata.key?(:includes)
|
54
|
+
includes = field.metadata[:includes]
|
55
|
+
raise ArgumentError.new('Connection includes must be a hash containing :edges and :nodes keys') unless includes.is_a?(Hash)
|
56
|
+
raise ArgumentError.new('Missing :nodes includes') unless includes.key?(:nodes)
|
57
|
+
raise ArgumentError.new('Missing :edges includes') unless includes.key?(:edges)
|
58
|
+
raise ArgumentError.new(':edges must be a symbol') unless includes[:edges].is_a?(Symbol)
|
59
|
+
raise ArgumentError.new(':nodes must be a symbol') unless includes[:edges].is_a?(Symbol)
|
60
|
+
end
|
26
61
|
end
|
27
62
|
|
28
63
|
GraphQL::Relay::BaseConnection.register_connection_implementation(GraphQLIncludable::Relay::ConnectionEdgesAndNodes, GraphQLIncludable::Relay::EdgeWithNodeConnection)
|
29
64
|
end
|
65
|
+
|
30
66
|
end
|
31
67
|
end
|
@@ -80,7 +80,7 @@ module GraphQLIncludable
|
|
80
80
|
edges_association = associations_from_parent_model[:edges]
|
81
81
|
nodes_association = associations_from_parent_model[:nodes]
|
82
82
|
|
83
|
-
edge_node_attribute = node.
|
83
|
+
edge_node_attribute = node.definition.metadata[:edge_to_node_property]
|
84
84
|
edge_model = edges_association.klass
|
85
85
|
edge_to_node_association = edge_model.reflect_on_association(edge_node_attribute)
|
86
86
|
node_model = edge_to_node_association.klass
|
@@ -88,6 +88,7 @@ module GraphQLIncludable
|
|
88
88
|
connection_children = node.scoped_children[node.return_type.unwrap]
|
89
89
|
connection_children.each_value do |connection_node|
|
90
90
|
# connection_field {
|
91
|
+
# totalCount
|
91
92
|
# pageInfo {...}
|
92
93
|
# nodes {
|
93
94
|
# node_model_field ...
|
@@ -122,6 +123,8 @@ module GraphQLIncludable
|
|
122
123
|
node_children.each_value do |node_child_node|
|
123
124
|
includes_for_child(node_child_node, node_model, nodes_includes_manager)
|
124
125
|
end
|
126
|
+
elsif connection_node.name == 'totalCount'
|
127
|
+
# Handled using `.size` - if includes() grabbed edges/nodes it will .length else, a COUNT query saving memory.
|
125
128
|
end
|
126
129
|
end
|
127
130
|
end
|
@@ -167,15 +170,16 @@ module GraphQLIncludable
|
|
167
170
|
|
168
171
|
def self.node_possible_association_names(node)
|
169
172
|
definition = node.definitions.first
|
173
|
+
user_includes = definition.metadata[:includes]
|
170
174
|
|
171
175
|
association_names = {}
|
172
176
|
if node_is_relay_connection?(node)
|
173
|
-
association_names[:edges] =
|
174
|
-
association_names[:nodes] =
|
175
|
-
return association_names if association_names.present? # This
|
177
|
+
association_names[:edges] = user_includes[:edges] if user_includes.key?(:edges)
|
178
|
+
association_names[:nodes] = user_includes[:nodes] if user_includes.key?(:nodes)
|
179
|
+
return association_names if association_names.present? # This will have resolve procs for nodes and edges
|
176
180
|
end
|
177
181
|
|
178
|
-
association_names[:default] =
|
182
|
+
association_names[:default] = user_includes || (definition.property || definition.name).to_sym
|
179
183
|
association_names
|
180
184
|
end
|
181
185
|
|
data/lib/graphql_includable.rb
CHANGED
@@ -9,9 +9,11 @@ require 'graphql_includable/relay/instrumentation/connection'
|
|
9
9
|
|
10
10
|
GraphQL::Field.accepts_definitions(
|
11
11
|
includes: GraphQL::Define.assign_metadata_key(:includes),
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
edge_to_node_property: GraphQL::Define.assign_metadata_key(:edge_to_node_property),
|
13
|
+
resolve_edges: GraphQL::Define.assign_metadata_key(:resolve_edges),
|
14
|
+
resolve_nodes: GraphQL::Define.assign_metadata_key(:resolve_nodes),
|
15
|
+
# Internal use
|
16
|
+
_includable_connection_marker: GraphQL::Define.assign_metadata_key(:_includable_connection_marker),
|
15
17
|
)
|
16
18
|
|
17
19
|
module GraphQL
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql_includable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0.alpha.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Rouse
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2019-08-
|
13
|
+
date: 2019-08-06 00:00:00.000000000 Z
|
14
14
|
dependencies: []
|
15
15
|
description:
|
16
16
|
email:
|
@@ -44,9 +44,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
44
|
version: '0'
|
45
45
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- - "
|
47
|
+
- - ">"
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version:
|
49
|
+
version: 1.3.1
|
50
50
|
requirements: []
|
51
51
|
rubyforge_project:
|
52
52
|
rubygems_version: 2.6.14.1
|