graphql 1.10.0.pre1 → 1.10.0.pre2
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/generators/graphql/core.rb +1 -0
- data/lib/generators/graphql/install_generator.rb +1 -0
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/templates/base_field.erb +0 -4
- data/lib/generators/graphql/templates/base_mutation.erb +8 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +5 -0
- data/lib/generators/graphql/templates/mutation.erb +1 -1
- data/lib/generators/graphql/templates/schema.erb +1 -1
- data/lib/graphql.rb +4 -1
- data/lib/graphql/analysis/ast.rb +14 -13
- data/lib/graphql/analysis/ast/analyzer.rb +23 -4
- data/lib/graphql/analysis/ast/field_usage.rb +1 -1
- data/lib/graphql/analysis/ast/max_query_complexity.rb +3 -3
- data/lib/graphql/analysis/ast/max_query_depth.rb +7 -3
- data/lib/graphql/analysis/ast/query_complexity.rb +2 -2
- data/lib/graphql/analysis/ast/visitor.rb +3 -3
- data/lib/graphql/base_type.rb +1 -1
- data/lib/graphql/directive.rb +0 -1
- data/lib/graphql/directive/deprecated_directive.rb +1 -12
- data/lib/graphql/execution/errors.rb +4 -8
- data/lib/graphql/execution/interpreter.rb +5 -11
- data/lib/graphql/execution/interpreter/runtime.rb +56 -48
- data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
- data/lib/graphql/execution/lookahead.rb +5 -5
- data/lib/graphql/execution/multiplex.rb +10 -0
- data/lib/graphql/function.rb +1 -1
- data/lib/graphql/input_object_type.rb +3 -2
- data/lib/graphql/interface_type.rb +1 -1
- data/lib/graphql/introspection/base_object.rb +2 -5
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +6 -6
- data/lib/graphql/introspection/schema_type.rb +1 -6
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/language.rb +1 -1
- data/lib/graphql/language/block_string.rb +2 -2
- data/lib/graphql/language/definition_slice.rb +21 -10
- data/lib/graphql/language/document_from_schema_definition.rb +42 -42
- data/lib/graphql/language/lexer.rb +49 -48
- data/lib/graphql/language/lexer.rl +49 -48
- data/lib/graphql/language/nodes.rb +11 -8
- data/lib/graphql/language/parser.rb +4 -1
- data/lib/graphql/language/parser.y +4 -1
- data/lib/graphql/language/token.rb +1 -1
- data/lib/graphql/pagination/array_connection.rb +0 -1
- data/lib/graphql/pagination/connection.rb +31 -10
- data/lib/graphql/pagination/connections.rb +7 -2
- data/lib/graphql/pagination/relation_connection.rb +1 -7
- data/lib/graphql/query.rb +9 -4
- data/lib/graphql/query/arguments.rb +8 -1
- data/lib/graphql/query/literal_input.rb +2 -1
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/relay/base_connection.rb +3 -3
- data/lib/graphql/relay/relation_connection.rb +9 -5
- data/lib/graphql/schema.rb +699 -153
- data/lib/graphql/schema/argument.rb +20 -4
- data/lib/graphql/schema/build_from_definition.rb +64 -31
- data/lib/graphql/schema/built_in_types.rb +5 -5
- data/lib/graphql/schema/directive.rb +16 -1
- data/lib/graphql/schema/directive/deprecated.rb +18 -0
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/enum.rb +39 -3
- data/lib/graphql/schema/field.rb +39 -9
- data/lib/graphql/schema/field/connection_extension.rb +4 -4
- data/lib/graphql/schema/find_inherited_value.rb +13 -0
- data/lib/graphql/schema/finder.rb +13 -11
- data/lib/graphql/schema/input_object.rb +109 -1
- data/lib/graphql/schema/interface.rb +8 -7
- data/lib/graphql/schema/introspection_system.rb +104 -36
- data/lib/graphql/schema/late_bound_type.rb +1 -0
- data/lib/graphql/schema/list.rb +26 -0
- data/lib/graphql/schema/loader.rb +10 -4
- data/lib/graphql/schema/member.rb +3 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +23 -13
- data/lib/graphql/schema/member/build_type.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/member/has_fields.rb +15 -6
- data/lib/graphql/schema/member/instrumentation.rb +6 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member/validates_input.rb +33 -0
- data/lib/graphql/schema/non_null.rb +25 -0
- data/lib/graphql/schema/object.rb +14 -1
- data/lib/graphql/schema/printer.rb +4 -3
- data/lib/graphql/schema/relay_classic_mutation.rb +5 -1
- data/lib/graphql/schema/resolver.rb +20 -2
- data/lib/graphql/schema/scalar.rb +18 -3
- data/lib/graphql/schema/subscription.rb +1 -1
- data/lib/graphql/schema/timeout_middleware.rb +3 -2
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +22 -24
- data/lib/graphql/schema/validation.rb +17 -1
- data/lib/graphql/schema/warden.rb +46 -17
- data/lib/graphql/schema/wrapper.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +10 -6
- data/lib/graphql/static_validation/definition_dependencies.rb +21 -12
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/type_stack.rb +2 -2
- data/lib/graphql/static_validation/validator.rb +1 -1
- data/lib/graphql/subscriptions.rb +38 -13
- data/lib/graphql/subscriptions/event.rb +24 -7
- data/lib/graphql/subscriptions/instrumentation.rb +1 -1
- data/lib/graphql/subscriptions/subscription_root.rb +0 -1
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +10 -10
- data/lib/graphql/tracing/platform_tracing.rb +1 -2
- data/lib/graphql/tracing/skylight_tracing.rb +1 -0
- data/lib/graphql/unresolved_type_error.rb +2 -2
- data/lib/graphql/upgrader/member.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +5 -2
@@ -21,6 +21,7 @@ def initialize(query_string, filename:, tracer: Tracing::NullTracer)
|
|
21
21
|
@query_string = query_string
|
22
22
|
@filename = filename
|
23
23
|
@tracer = tracer
|
24
|
+
@reused_next_token = [nil, nil]
|
24
25
|
end
|
25
26
|
|
26
27
|
def parse_document
|
@@ -51,7 +52,9 @@ def next_token
|
|
51
52
|
if lexer_token.nil?
|
52
53
|
nil
|
53
54
|
else
|
54
|
-
[lexer_token.name
|
55
|
+
@reused_next_token[0] = lexer_token.name
|
56
|
+
@reused_next_token[1] = lexer_token
|
57
|
+
@reused_next_token
|
55
58
|
end
|
56
59
|
end
|
57
60
|
|
@@ -442,6 +442,7 @@ def initialize(query_string, filename:, tracer: Tracing::NullTracer)
|
|
442
442
|
@query_string = query_string
|
443
443
|
@filename = filename
|
444
444
|
@tracer = tracer
|
445
|
+
@reused_next_token = [nil, nil]
|
445
446
|
end
|
446
447
|
|
447
448
|
def parse_document
|
@@ -472,7 +473,9 @@ def next_token
|
|
472
473
|
if lexer_token.nil?
|
473
474
|
nil
|
474
475
|
else
|
475
|
-
[lexer_token.name
|
476
|
+
@reused_next_token[0] = lexer_token.name
|
477
|
+
@reused_next_token[1] = lexer_token
|
478
|
+
@reused_next_token
|
476
479
|
end
|
477
480
|
end
|
478
481
|
|
@@ -26,7 +26,18 @@ module GraphQL
|
|
26
26
|
# @return [GraphQL::Query::Context]
|
27
27
|
attr_accessor :context
|
28
28
|
|
29
|
-
|
29
|
+
# Raw access to client-provided values. (`max_page_size` not applied to first or last.)
|
30
|
+
attr_accessor :before_value, :after_value, :first_value, :last_value
|
31
|
+
|
32
|
+
# @return [String, nil] the client-provided cursor
|
33
|
+
def before
|
34
|
+
@before_value
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String, nil] the client-provided cursor
|
38
|
+
def after
|
39
|
+
@after_value
|
40
|
+
end
|
30
41
|
|
31
42
|
# @param items [Object] some unpaginated collection item, like an `Array` or `ActiveRecord::Relation`
|
32
43
|
# @param context [Query::Context]
|
@@ -34,13 +45,14 @@ module GraphQL
|
|
34
45
|
# @param after [String, nil] A cursor for pagination, if the client provided one
|
35
46
|
# @param last [Integer, nil] Limit parameter from the client, if provided
|
36
47
|
# @param before [String, nil] A cursor for pagination, if the client provided one.
|
48
|
+
# @param max_page_size [Integer, nil] A configured value to cap the result size. Applied as `first` if neither first or last are given.
|
37
49
|
def initialize(items, context: nil, first: nil, after: nil, max_page_size: nil, last: nil, before: nil)
|
38
50
|
@items = items
|
39
51
|
@context = context
|
40
|
-
@
|
41
|
-
@
|
42
|
-
@
|
43
|
-
@
|
52
|
+
@first_value = first
|
53
|
+
@after_value = after
|
54
|
+
@last_value = last
|
55
|
+
@before_value = before
|
44
56
|
@max_page_size = max_page_size
|
45
57
|
end
|
46
58
|
|
@@ -50,15 +62,24 @@ module GraphQL
|
|
50
62
|
end
|
51
63
|
|
52
64
|
attr_writer :first
|
53
|
-
# @return [Integer, nil]
|
65
|
+
# @return [Integer, nil]
|
66
|
+
# A clamped `first` value.
|
67
|
+
# (The underlying instance variable doesn't have limits on it.)
|
68
|
+
# If neither `first` nor `last` is given, but `max_page_size` is present, max_page_size is used for first.
|
54
69
|
def first
|
55
|
-
|
70
|
+
@first ||= begin
|
71
|
+
capped = limit_pagination_argument(@first_value, max_page_size)
|
72
|
+
if capped.nil? && last.nil?
|
73
|
+
capped = max_page_size
|
74
|
+
end
|
75
|
+
capped
|
76
|
+
end
|
56
77
|
end
|
57
78
|
|
58
79
|
attr_writer :last
|
59
|
-
# @return [Integer, nil]
|
80
|
+
# @return [Integer, nil] A clamped `last` value. (The underlying instance variable doesn't have limits on it)
|
60
81
|
def last
|
61
|
-
limit_pagination_argument(@
|
82
|
+
@last ||= limit_pagination_argument(@last_value, max_page_size)
|
62
83
|
end
|
63
84
|
|
64
85
|
# @return [Array<Edge>] {nodes}, but wrapped with Edge instances
|
@@ -66,7 +87,7 @@ module GraphQL
|
|
66
87
|
@edges ||= nodes.map { |n| self.class.edge_class.new(n, self) }
|
67
88
|
end
|
68
89
|
|
69
|
-
# @return [Array<Object>] A slice of {items}, constrained by {@
|
90
|
+
# @return [Array<Object>] A slice of {items}, constrained by {@first_value}/{@after_value}/{@last_value}/{@before_value}
|
70
91
|
def nodes
|
71
92
|
raise PaginationImplementationMissingError, "Implement #{self.class}#nodes to paginate `@items`"
|
72
93
|
end
|
@@ -29,8 +29,13 @@ module GraphQL
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def self.use(schema_defn)
|
32
|
-
|
33
|
-
|
32
|
+
if schema_defn.is_a?(Class)
|
33
|
+
schema_defn.connections = self.new
|
34
|
+
else
|
35
|
+
# Unwrap a `.define` object
|
36
|
+
schema_defn = schema_defn.target
|
37
|
+
schema_defn.connections = self.new
|
38
|
+
schema_defn.class.connections = schema_defn.connections
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
@@ -53,6 +53,7 @@ module GraphQL
|
|
53
53
|
raise "#{self.class}#null_relation(relation) must return an empty relation for a #{relation.class} (#{relation.inspect})"
|
54
54
|
end
|
55
55
|
|
56
|
+
# @return [Integer]
|
56
57
|
def offset_from_cursor(cursor)
|
57
58
|
decode(cursor).to_i
|
58
59
|
end
|
@@ -130,13 +131,6 @@ module GraphQL
|
|
130
131
|
end
|
131
132
|
end
|
132
133
|
|
133
|
-
# Apply max page size if nothing else was applied
|
134
|
-
if max_page_size && !first && !last
|
135
|
-
if relation_limit(paginated_nodes).nil? || relation_limit(paginated_nodes) > max_page_size
|
136
|
-
paginated_nodes = set_limit(paginated_nodes, max_page_size)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
134
|
@has_next_page = !!(
|
141
135
|
(before_offset && before_offset > 0) ||
|
142
136
|
(first && sliced_nodes_count > first)
|
data/lib/graphql/query.rb
CHANGED
@@ -78,12 +78,18 @@ module GraphQL
|
|
78
78
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
79
79
|
# @param except [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns truthy
|
80
80
|
# @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
|
81
|
-
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil)
|
81
|
+
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil, warden: nil)
|
82
82
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
83
83
|
variables ||= {}
|
84
|
+
|
85
|
+
# Use the `.graphql_definition` here which will return legacy types instead of classes
|
86
|
+
if schema.is_a?(Class) && !schema.interpreter?
|
87
|
+
schema = schema.graphql_definition
|
88
|
+
end
|
84
89
|
@schema = schema
|
85
90
|
@filter = schema.default_filter.merge(except: except, only: only)
|
86
91
|
@context = schema.context_class.new(query: self, object: root_value, values: context)
|
92
|
+
@warden = warden
|
87
93
|
@subscription_topic = subscription_topic
|
88
94
|
@root_value = root_value
|
89
95
|
@fragments = nil
|
@@ -157,7 +163,7 @@ module GraphQL
|
|
157
163
|
@lookahead ||= begin
|
158
164
|
ast_node = selected_operation
|
159
165
|
root_type = warden.root_type_for_operation(ast_node.operation_type || "query")
|
160
|
-
root_type = root_type.
|
166
|
+
root_type = root_type.type_class || raise("Invariant: `lookahead` only works with class-based types")
|
161
167
|
GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [ast_node])
|
162
168
|
end
|
163
169
|
end
|
@@ -314,8 +320,7 @@ module GraphQL
|
|
314
320
|
|
315
321
|
def prepare_ast
|
316
322
|
@prepared_ast = true
|
317
|
-
@warden
|
318
|
-
|
323
|
+
@warden ||= GraphQL::Schema::Warden.new(@filter, schema: @schema, context: @context)
|
319
324
|
parse_error = nil
|
320
325
|
@document ||= begin
|
321
326
|
if query_string
|
@@ -88,6 +88,10 @@ module GraphQL
|
|
88
88
|
|
89
89
|
def_delegators :to_h, :keys, :values, :each, :any?
|
90
90
|
|
91
|
+
def prepare
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
91
95
|
# Access each key, value and type for the arguments in this set.
|
92
96
|
# @yield [argument_value] The {ArgumentValue} for each argument
|
93
97
|
# @yieldparam argument_value [ArgumentValue]
|
@@ -119,6 +123,8 @@ module GraphQL
|
|
119
123
|
ruby_kwargs
|
120
124
|
end
|
121
125
|
|
126
|
+
alias :to_hash :to_kwargs
|
127
|
+
|
122
128
|
private
|
123
129
|
|
124
130
|
class ArgumentValue
|
@@ -151,7 +157,8 @@ module GraphQL
|
|
151
157
|
wrap_value(value, arg_defn_type.of_type, context)
|
152
158
|
when GraphQL::InputObjectType
|
153
159
|
if value.is_a?(Hash)
|
154
|
-
arg_defn_type.arguments_class.new(value, context: context, defaults_used: Set.new)
|
160
|
+
result = arg_defn_type.arguments_class.new(value, context: context, defaults_used: Set.new)
|
161
|
+
result.prepare
|
155
162
|
else
|
156
163
|
value
|
157
164
|
end
|
@@ -127,7 +127,8 @@ module GraphQL
|
|
127
127
|
ruby_kwargs
|
128
128
|
end
|
129
129
|
else
|
130
|
-
argument_owner.arguments_class.new(values_hash, context: context, defaults_used: defaults_used)
|
130
|
+
result = argument_owner.arguments_class.new(values_hash, context: context, defaults_used: defaults_used)
|
131
|
+
result.prepare
|
131
132
|
end
|
132
133
|
end
|
133
134
|
end
|
@@ -35,7 +35,11 @@ module GraphQL
|
|
35
35
|
if validation_result.valid?
|
36
36
|
if value_was_provided
|
37
37
|
# Add the variable if a value was provided
|
38
|
-
memo[variable_name] =
|
38
|
+
memo[variable_name] = if provided_value.nil?
|
39
|
+
nil
|
40
|
+
else
|
41
|
+
variable_type.coerce_input(provided_value, ctx)
|
42
|
+
end
|
39
43
|
elsif default_value != nil
|
40
44
|
# Add the variable if it wasn't provided but it has a default value (including `null`)
|
41
45
|
memo[variable_name] = GraphQL::Query::LiteralInput.coerce(variable_type, default_value, self)
|
@@ -143,7 +143,7 @@ module GraphQL
|
|
143
143
|
|
144
144
|
# An opaque operation which returns a connection-specific cursor.
|
145
145
|
def cursor_from_node(object)
|
146
|
-
raise
|
146
|
+
raise GraphQL::RequiredImplementationMissingError, "must return a cursor for this object/connection pair"
|
147
147
|
end
|
148
148
|
|
149
149
|
def inspect
|
@@ -165,11 +165,11 @@ module GraphQL
|
|
165
165
|
end
|
166
166
|
|
167
167
|
def paged_nodes
|
168
|
-
raise
|
168
|
+
raise GraphQL::RequiredImplementationMissingError, "must return nodes for this connection after paging"
|
169
169
|
end
|
170
170
|
|
171
171
|
def sliced_nodes
|
172
|
-
raise
|
172
|
+
raise GraphQL::RequiredImplementationMissingError, "must return all nodes for this connection after chopping off first and last"
|
173
173
|
end
|
174
174
|
end
|
175
175
|
end
|
@@ -25,12 +25,16 @@ module GraphQL
|
|
25
25
|
|
26
26
|
def has_next_page
|
27
27
|
if first
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
if defined?(ActiveRecord::Relation) && nodes.is_a?(ActiveRecord::Relation)
|
29
|
+
initial_offset = after ? offset_from_cursor(after) : 0
|
30
|
+
return paged_nodes.length >= first && nodes.offset(first + initial_offset).exists?
|
31
|
+
end
|
32
|
+
return paged_nodes.length >= first && sliced_nodes_count > first
|
33
|
+
end
|
34
|
+
if GraphQL::Relay::ConnectionType.bidirectional_pagination && last
|
35
|
+
return sliced_nodes_count >= last
|
33
36
|
end
|
37
|
+
false
|
34
38
|
end
|
35
39
|
|
36
40
|
def has_previous_page
|
data/lib/graphql/schema.rb
CHANGED
@@ -36,6 +36,7 @@ require "graphql/schema/scalar"
|
|
36
36
|
require "graphql/schema/object"
|
37
37
|
require "graphql/schema/union"
|
38
38
|
require "graphql/schema/directive"
|
39
|
+
require "graphql/schema/directive/deprecated"
|
39
40
|
require "graphql/schema/directive/include"
|
40
41
|
require "graphql/schema/directive/skip"
|
41
42
|
require "graphql/schema/directive/feature"
|
@@ -54,7 +55,6 @@ module GraphQL
|
|
54
55
|
# - types for exposing your application
|
55
56
|
# - query analyzers for assessing incoming queries (including max depth & max complexity restrictions)
|
56
57
|
# - execution strategies for running incoming queries
|
57
|
-
# - middleware for interacting with execution
|
58
58
|
#
|
59
59
|
# Schemas start with root types, {Schema#query}, {Schema#mutation} and {Schema#subscription}.
|
60
60
|
# The schema will traverse the tree of fields & types, using those as starting points.
|
@@ -66,14 +66,10 @@ module GraphQL
|
|
66
66
|
# Schemas can specify how queries should be executed against them.
|
67
67
|
# `query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy`
|
68
68
|
# each apply to corresponding root types.
|
69
|
-
#
|
70
|
-
# A schema accepts a `Relay::GlobalNodeIdentification` instance for use with Relay IDs.
|
71
|
-
#
|
69
|
+
# #
|
72
70
|
# @example defining a schema
|
73
|
-
# MySchema
|
71
|
+
# class MySchema < GraphQL::Schema
|
74
72
|
# query QueryType
|
75
|
-
# middleware PermissionMiddleware
|
76
|
-
# rescue_from(ActiveRecord::RecordNotFound) { "Not found" }
|
77
73
|
# # If types are only connected by way of interfaces, they must be added here
|
78
74
|
# orphan_types ImageType, AudioType
|
79
75
|
# end
|
@@ -85,6 +81,14 @@ module GraphQL
|
|
85
81
|
include GraphQL::Define::InstanceDefinable
|
86
82
|
extend GraphQL::Schema::FindInheritedValue
|
87
83
|
|
84
|
+
class UnresolvedLateBoundTypeError < GraphQL::Error
|
85
|
+
attr_reader :type
|
86
|
+
def initialize(type:)
|
87
|
+
@type = type
|
88
|
+
super("Late bound type was never found: #{type.inspect}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
88
92
|
accepts_definitions \
|
89
93
|
:query_execution_strategy, :mutation_execution_strategy, :subscription_execution_strategy,
|
90
94
|
:max_depth, :max_complexity, :default_max_page_size,
|
@@ -99,7 +103,7 @@ module GraphQL
|
|
99
103
|
mutation: ->(schema, t) { schema.mutation = t.respond_to?(:graphql_definition) ? t.graphql_definition : t },
|
100
104
|
subscription: ->(schema, t) { schema.subscription = t.respond_to?(:graphql_definition) ? t.graphql_definition : t },
|
101
105
|
disable_introspection_entry_points: ->(schema) { schema.disable_introspection_entry_points = true },
|
102
|
-
directives: ->(schema, directives) { schema.directives = directives.reduce({}) { |m, d| m[d.
|
106
|
+
directives: ->(schema, directives) { schema.directives = directives.reduce({}) { |m, d| m[d.graphql_name] = d; m } },
|
103
107
|
directive: ->(schema, directive) { schema.directives[directive.graphql_name] = directive },
|
104
108
|
instrument: ->(schema, type, instrumenter, after_built_ins: false) {
|
105
109
|
if type == :field && after_built_ins
|
@@ -154,6 +158,10 @@ module GraphQL
|
|
154
158
|
# [Boolean] True if this object disables the introspection entry point fields
|
155
159
|
attr_accessor :disable_introspection_entry_points
|
156
160
|
|
161
|
+
def disable_introspection_entry_points?
|
162
|
+
!!@disable_introspection_entry_points
|
163
|
+
end
|
164
|
+
|
157
165
|
class << self
|
158
166
|
attr_writer :default_execution_strategy
|
159
167
|
end
|
@@ -167,8 +175,6 @@ module GraphQL
|
|
167
175
|
attr_reader :tracers
|
168
176
|
|
169
177
|
DYNAMIC_FIELDS = ["__type", "__typename", "__schema"].freeze
|
170
|
-
EMPTY_ARRAY = [].freeze
|
171
|
-
EMPTY_HASH = {}.freeze
|
172
178
|
|
173
179
|
attr_reader :static_validator, :object_from_id_proc, :id_from_object_proc, :resolve_type_proc
|
174
180
|
|
@@ -176,7 +182,10 @@ module GraphQL
|
|
176
182
|
@tracers = []
|
177
183
|
@definition_error = nil
|
178
184
|
@orphan_types = []
|
179
|
-
@directives =
|
185
|
+
@directives = {}
|
186
|
+
self.class.default_directives.each do |name, dir|
|
187
|
+
@directives[name] = dir.graphql_definition
|
188
|
+
end
|
180
189
|
@static_validator = GraphQL::StaticValidation::Validator.new(schema: self)
|
181
190
|
@middleware = MiddlewareChain.new(final_step: GraphQL::Execution::Execute::FieldResolveStep)
|
182
191
|
@query_analyzers = []
|
@@ -282,13 +291,13 @@ module GraphQL
|
|
282
291
|
ensure_defined
|
283
292
|
# Assert that all necessary configs are present:
|
284
293
|
validation_error = Validation.validate(self)
|
285
|
-
validation_error && raise(
|
294
|
+
validation_error && raise(GraphQL::RequiredImplementationMissingError, validation_error)
|
286
295
|
rebuild_artifacts
|
287
296
|
|
288
297
|
@definition_error = nil
|
289
298
|
nil
|
290
299
|
rescue StandardError => err
|
291
|
-
if @raise_definition_error || err.is_a?(CyclicalDefinitionError)
|
300
|
+
if @raise_definition_error || err.is_a?(CyclicalDefinitionError) || err.is_a?(GraphQL::RequiredImplementationMissingError)
|
292
301
|
raise
|
293
302
|
else
|
294
303
|
# Raise this error _later_ to avoid messing with Rails constant loading
|
@@ -325,6 +334,10 @@ module GraphQL
|
|
325
334
|
end
|
326
335
|
end
|
327
336
|
|
337
|
+
def get_type(type_name)
|
338
|
+
@types[type_name]
|
339
|
+
end
|
340
|
+
|
328
341
|
# @api private
|
329
342
|
def introspection_system
|
330
343
|
@introspection_system ||= begin
|
@@ -336,9 +349,13 @@ module GraphQL
|
|
336
349
|
# Returns a list of Arguments and Fields referencing a certain type
|
337
350
|
# @param type_name [String]
|
338
351
|
# @return [Hash]
|
339
|
-
def references_to(type_name)
|
352
|
+
def references_to(type_name = nil)
|
340
353
|
rebuild_artifacts unless defined?(@type_reference_map)
|
341
|
-
|
354
|
+
if type_name
|
355
|
+
@type_reference_map.fetch(type_name, [])
|
356
|
+
else
|
357
|
+
@type_reference_map
|
358
|
+
end
|
342
359
|
end
|
343
360
|
|
344
361
|
# Returns a list of Union types in which a type is a member
|
@@ -416,8 +433,8 @@ module GraphQL
|
|
416
433
|
def get_field(parent_type, field_name)
|
417
434
|
with_definition_error_check do
|
418
435
|
parent_type_name = case parent_type
|
419
|
-
when GraphQL::BaseType
|
420
|
-
parent_type.
|
436
|
+
when GraphQL::BaseType, Class, Module
|
437
|
+
parent_type.graphql_name
|
421
438
|
when String
|
422
439
|
parent_type
|
423
440
|
else
|
@@ -444,7 +461,7 @@ module GraphQL
|
|
444
461
|
end
|
445
462
|
|
446
463
|
def type_from_ast(ast_node, context:)
|
447
|
-
GraphQL::Schema::TypeExpression.build_type(self
|
464
|
+
GraphQL::Schema::TypeExpression.build_type(self, ast_node)
|
448
465
|
end
|
449
466
|
|
450
467
|
# @see [GraphQL::Schema::Warden] Restricted access to members of a schema
|
@@ -493,7 +510,7 @@ module GraphQL
|
|
493
510
|
def resolve_type(type, object, ctx = :__undefined__)
|
494
511
|
check_resolved_type(type, object, ctx) do |ok_type, ok_object, ok_ctx|
|
495
512
|
if @resolve_type_proc.nil?
|
496
|
-
raise(
|
513
|
+
raise(GraphQL::RequiredImplementationMissingError, "Can't determine GraphQL type for: #{ok_object.inspect}, define `resolve_type (type, obj, ctx) -> { ... }` inside `Schema.define`.")
|
497
514
|
end
|
498
515
|
@resolve_type_proc.call(ok_type, ok_object, ok_ctx)
|
499
516
|
end
|
@@ -554,7 +571,7 @@ module GraphQL
|
|
554
571
|
# @return [Any] The application object identified by `id`
|
555
572
|
def object_from_id(id, ctx)
|
556
573
|
if @object_from_id_proc.nil?
|
557
|
-
raise(
|
574
|
+
raise(GraphQL::RequiredImplementationMissingError, "Can't fetch an object for id \"#{id}\" because the schema's `object_from_id (id, ctx) -> { ... }` function is not defined")
|
558
575
|
else
|
559
576
|
@object_from_id_proc.call(id, ctx)
|
560
577
|
end
|
@@ -596,9 +613,40 @@ module GraphQL
|
|
596
613
|
|
597
614
|
# Can't delegate to `class`
|
598
615
|
alias :_schema_class :class
|
599
|
-
def_delegators :_schema_class, :
|
616
|
+
def_delegators :_schema_class, :unauthorized_object, :unauthorized_field, :inaccessible_fields
|
600
617
|
def_delegators :_schema_class, :directive
|
601
618
|
|
619
|
+
|
620
|
+
# Given this schema member, find the class-based definition object
|
621
|
+
# whose `method_name` should be treated as an application hook
|
622
|
+
# @see {.visible?}
|
623
|
+
# @see {.accessible?}
|
624
|
+
def call_on_type_class(member, method_name, context, default:)
|
625
|
+
member = if member.respond_to?(:type_class)
|
626
|
+
member.type_class
|
627
|
+
else
|
628
|
+
member
|
629
|
+
end
|
630
|
+
|
631
|
+
if member.respond_to?(:relay_node_type) && (t = member.relay_node_type)
|
632
|
+
member = t
|
633
|
+
end
|
634
|
+
|
635
|
+
if member.respond_to?(method_name)
|
636
|
+
member.public_send(method_name, context)
|
637
|
+
else
|
638
|
+
default
|
639
|
+
end
|
640
|
+
end
|
641
|
+
|
642
|
+
def visible?(member, context)
|
643
|
+
call_on_type_class(member, :visible?, context, default: true)
|
644
|
+
end
|
645
|
+
|
646
|
+
def accessible?(member, context)
|
647
|
+
call_on_type_class(member, :accessible?, context, default: true)
|
648
|
+
end
|
649
|
+
|
602
650
|
# A function to call when {#execute} receives an invalid query string
|
603
651
|
#
|
604
652
|
# @see {DefaultParseError} is the default behavior.
|
@@ -621,7 +669,7 @@ module GraphQL
|
|
621
669
|
# @return [String] a unique identifier for `object` which clients can use to refetch it
|
622
670
|
def id_from_object(object, type, ctx)
|
623
671
|
if @id_from_object_proc.nil?
|
624
|
-
raise(
|
672
|
+
raise(GraphQL::RequiredImplementationMissingError, "Can't generate an ID for #{object.inspect} of type #{type}, schema's `id_from_object` must be defined")
|
625
673
|
else
|
626
674
|
@id_from_object_proc.call(object, type, ctx)
|
627
675
|
end
|
@@ -643,15 +691,17 @@ module GraphQL
|
|
643
691
|
# @param definition_or_path [String] A schema definition string, or a path to a file containing the definition
|
644
692
|
# @param default_resolve [<#call(type, field, obj, args, ctx)>] A callable for handling field resolution
|
645
693
|
# @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
|
646
|
-
# @
|
647
|
-
|
694
|
+
# @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
|
695
|
+
# @param interpreter [Boolean] If false, the legacy {Execution::Execute} runtime will be used
|
696
|
+
# @return [Class] the schema described by `document`
|
697
|
+
def self.from_definition(definition_or_path, default_resolve: BuildFromDefinition::DefaultResolve, interpreter: true, parser: BuildFromDefinition::DefaultParser, using: {})
|
648
698
|
# If the file ends in `.graphql`, treat it like a filepath
|
649
699
|
definition = if definition_or_path.end_with?(".graphql")
|
650
700
|
File.read(definition_or_path)
|
651
701
|
else
|
652
702
|
definition_or_path
|
653
703
|
end
|
654
|
-
GraphQL::Schema::BuildFromDefinition.from_definition(definition, default_resolve: default_resolve, parser: parser)
|
704
|
+
GraphQL::Schema::BuildFromDefinition.from_definition(definition, default_resolve: default_resolve, parser: parser, using: using, interpreter: interpreter)
|
655
705
|
end
|
656
706
|
|
657
707
|
# Error that is raised when [#Schema#from_definition] is passed an invalid schema definition string.
|
@@ -711,38 +761,83 @@ module GraphQL
|
|
711
761
|
# - Delegate to that instance
|
712
762
|
# Eventually, the methods will be moved into this class, removing the need for the singleton.
|
713
763
|
def_delegators :graphql_definition,
|
714
|
-
# Schema structure
|
715
|
-
:as_json, :to_json, :to_document, :to_definition,
|
716
764
|
# Execution
|
717
|
-
:execute, :multiplex,
|
718
|
-
:static_validator, :introspection_system,
|
719
|
-
:query_analyzers, :tracers, :instrumenters,
|
720
765
|
:execution_strategy_for_operation,
|
721
|
-
:validate, :multiplex_analyzers,
|
766
|
+
:validate, :multiplex_analyzers,
|
722
767
|
# Configuration
|
723
|
-
:
|
724
|
-
:max_complexity=, :max_depth=,
|
725
|
-
:error_bubbling=,
|
726
|
-
:metadata,
|
727
|
-
:default_mask,
|
728
|
-
:default_filter, :redefine,
|
768
|
+
:metadata, :redefine,
|
729
769
|
:id_from_object_proc, :object_from_id_proc,
|
730
770
|
:id_from_object=, :object_from_id=,
|
731
|
-
:remove_handler
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
771
|
+
:remove_handler
|
772
|
+
|
773
|
+
# @return [GraphQL::Subscriptions]
|
774
|
+
attr_accessor :subscriptions
|
775
|
+
|
776
|
+
# Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
|
777
|
+
# @see {#as_json}
|
778
|
+
# @return [String]
|
779
|
+
def to_json(*args)
|
780
|
+
JSON.pretty_generate(as_json(*args))
|
781
|
+
end
|
782
|
+
|
783
|
+
# Return the Hash response of {Introspection::INTROSPECTION_QUERY}.
|
784
|
+
# @param context [Hash]
|
785
|
+
# @param only [<#call(member, ctx)>]
|
786
|
+
# @param except [<#call(member, ctx)>]
|
787
|
+
# @return [Hash] GraphQL result
|
788
|
+
def as_json(only: nil, except: nil, context: {})
|
789
|
+
execute(Introspection::INTROSPECTION_QUERY, only: only, except: except, context: context).to_h
|
790
|
+
end
|
791
|
+
|
792
|
+
# Return the GraphQL IDL for the schema
|
793
|
+
# @param context [Hash]
|
794
|
+
# @param only [<#call(member, ctx)>]
|
795
|
+
# @param except [<#call(member, ctx)>]
|
796
|
+
# @return [String]
|
797
|
+
def to_definition(only: nil, except: nil, context: {})
|
798
|
+
GraphQL::Schema::Printer.print_schema(self, only: only, except: except, context: context)
|
799
|
+
end
|
800
|
+
|
801
|
+
# Return the GraphQL::Language::Document IDL AST for the schema
|
802
|
+
# @return [GraphQL::Language::Document]
|
803
|
+
def to_document
|
804
|
+
GraphQL::Language::DocumentFromSchemaDefinition.new(self).document
|
805
|
+
end
|
806
|
+
|
807
|
+
def find(path)
|
808
|
+
if !@finder
|
809
|
+
@find_cache = {}
|
810
|
+
@finder ||= GraphQL::Schema::Finder.new(self)
|
811
|
+
end
|
812
|
+
@find_cache[path] ||= @finder.find(path)
|
813
|
+
end
|
740
814
|
|
741
815
|
def graphql_definition
|
742
816
|
@graphql_definition ||= to_graphql
|
743
817
|
end
|
744
818
|
|
819
|
+
def default_filter
|
820
|
+
GraphQL::Filter.new(except: default_mask)
|
821
|
+
end
|
822
|
+
|
823
|
+
def default_mask(new_mask = nil)
|
824
|
+
if new_mask
|
825
|
+
@own_default_mask = new_mask
|
826
|
+
else
|
827
|
+
@own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
def static_validator
|
832
|
+
GraphQL::StaticValidation::Validator.new(schema: self)
|
833
|
+
end
|
834
|
+
|
745
835
|
def use(plugin, options = {})
|
836
|
+
if options.any?
|
837
|
+
plugin.use(self, options)
|
838
|
+
else
|
839
|
+
plugin.use(self)
|
840
|
+
end
|
746
841
|
own_plugins << [plugin, options]
|
747
842
|
end
|
748
843
|
|
@@ -753,14 +848,14 @@ module GraphQL
|
|
753
848
|
def to_graphql
|
754
849
|
schema_defn = self.new
|
755
850
|
schema_defn.raise_definition_error = true
|
756
|
-
schema_defn.query = query
|
757
|
-
schema_defn.mutation = mutation
|
758
|
-
schema_defn.subscription = subscription
|
851
|
+
schema_defn.query = query && query.graphql_definition
|
852
|
+
schema_defn.mutation = mutation && mutation.graphql_definition
|
853
|
+
schema_defn.subscription = subscription && subscription.graphql_definition
|
759
854
|
schema_defn.max_complexity = max_complexity
|
760
855
|
schema_defn.error_bubbling = error_bubbling
|
761
856
|
schema_defn.max_depth = max_depth
|
762
857
|
schema_defn.default_max_page_size = default_max_page_size
|
763
|
-
schema_defn.orphan_types = orphan_types
|
858
|
+
schema_defn.orphan_types = orphan_types.map(&:graphql_definition)
|
764
859
|
schema_defn.disable_introspection_entry_points = disable_introspection_entry_points?
|
765
860
|
|
766
861
|
prepped_dirs = {}
|
@@ -781,33 +876,24 @@ module GraphQL
|
|
781
876
|
schema_defn.query_execution_strategy = query_execution_strategy
|
782
877
|
schema_defn.mutation_execution_strategy = mutation_execution_strategy
|
783
878
|
schema_defn.subscription_execution_strategy = subscription_execution_strategy
|
784
|
-
|
879
|
+
schema_defn.default_mask = default_mask
|
880
|
+
instrumenters.each do |step, insts|
|
785
881
|
insts.each do |inst|
|
786
882
|
schema_defn.instrumenters[step] << inst
|
787
883
|
end
|
788
884
|
end
|
789
|
-
|
885
|
+
|
886
|
+
lazy_methods.each do |lazy_class, value_method|
|
790
887
|
schema_defn.lazy_methods.set(lazy_class, value_method)
|
791
888
|
end
|
889
|
+
|
792
890
|
rescues.each do |err_class, handler|
|
793
891
|
schema_defn.rescue_from(err_class, &handler)
|
794
892
|
end
|
795
893
|
|
796
|
-
|
797
|
-
|
798
|
-
|
799
|
-
schema_defn = schema_defn.redefine do
|
800
|
-
schema_plugins.each do |plugin, options|
|
801
|
-
if options.any?
|
802
|
-
use(plugin, **options)
|
803
|
-
else
|
804
|
-
use(plugin)
|
805
|
-
end
|
806
|
-
end
|
807
|
-
end
|
808
|
-
end
|
809
|
-
# Do this after `plugins` since Interpreter is a plugin
|
810
|
-
if schema_defn.query_execution_strategy != GraphQL::Execution::Interpreter
|
894
|
+
schema_defn.subscriptions ||= self.subscriptions
|
895
|
+
|
896
|
+
if !schema_defn.interpreter?
|
811
897
|
schema_defn.instrumenters[:query] << GraphQL::Schema::Member::Instrumentation
|
812
898
|
end
|
813
899
|
schema_defn.send(:rebuild_artifacts)
|
@@ -815,44 +901,196 @@ module GraphQL
|
|
815
901
|
schema_defn
|
816
902
|
end
|
817
903
|
|
904
|
+
# Build a map of `{ name => type }` and return it
|
905
|
+
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
906
|
+
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
907
|
+
def types
|
908
|
+
non_introspection_types.merge(introspection_system.types)
|
909
|
+
end
|
910
|
+
|
911
|
+
# @param type_name [String]
|
912
|
+
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
913
|
+
def get_type(type_name)
|
914
|
+
own_types[type_name] ||
|
915
|
+
introspection_system.types[type_name] ||
|
916
|
+
find_inherited_value(:types, EMPTY_HASH)[type_name]
|
917
|
+
end
|
918
|
+
|
818
919
|
# @return [GraphQL::Pagination::Connections] if installed
|
819
920
|
attr_accessor :connections
|
820
921
|
|
922
|
+
def new_connections?
|
923
|
+
!!connections
|
924
|
+
end
|
925
|
+
|
821
926
|
def query(new_query_object = nil)
|
822
927
|
if new_query_object
|
823
|
-
@query_object
|
928
|
+
if @query_object
|
929
|
+
raise GraphQL::Error, "Second definition of `query(...)` (#{new_query_object.inspect}) is invalid, already configured with #{@query_object.inspect}"
|
930
|
+
else
|
931
|
+
@query_object = new_query_object
|
932
|
+
add_type_and_traverse(new_query_object, root: true)
|
933
|
+
nil
|
934
|
+
end
|
824
935
|
else
|
825
|
-
|
826
|
-
query_object.respond_to?(:graphql_definition) ? query_object.graphql_definition : query_object
|
936
|
+
@query_object || find_inherited_value(:query)
|
827
937
|
end
|
828
938
|
end
|
829
939
|
|
830
940
|
def mutation(new_mutation_object = nil)
|
831
941
|
if new_mutation_object
|
832
|
-
@mutation_object
|
942
|
+
if @mutation_object
|
943
|
+
raise GraphQL::Error, "Second definition of `mutation(...)` (#{new_mutation_object.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
|
944
|
+
else
|
945
|
+
@mutation_object = new_mutation_object
|
946
|
+
add_type_and_traverse(new_mutation_object, root: true)
|
947
|
+
nil
|
948
|
+
end
|
833
949
|
else
|
834
|
-
|
835
|
-
mutation_object.respond_to?(:graphql_definition) ? mutation_object.graphql_definition : mutation_object
|
950
|
+
@mutation_object || find_inherited_value(:mutation)
|
836
951
|
end
|
837
952
|
end
|
838
953
|
|
839
954
|
def subscription(new_subscription_object = nil)
|
840
955
|
if new_subscription_object
|
841
|
-
@subscription_object
|
956
|
+
if @subscription_object
|
957
|
+
raise GraphQL::Error, "Second definition of `subscription(...)` (#{new_subscription_object.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
|
958
|
+
else
|
959
|
+
@subscription_object = new_subscription_object
|
960
|
+
add_type_and_traverse(new_subscription_object, root: true)
|
961
|
+
nil
|
962
|
+
end
|
963
|
+
else
|
964
|
+
@subscription_object || find_inherited_value(:subscription)
|
965
|
+
end
|
966
|
+
end
|
967
|
+
|
968
|
+
# @see [GraphQL::Schema::Warden] Resticted access to root types
|
969
|
+
# @return [GraphQL::ObjectType, nil]
|
970
|
+
def root_type_for_operation(operation)
|
971
|
+
case operation
|
972
|
+
when "query"
|
973
|
+
query
|
974
|
+
when "mutation"
|
975
|
+
mutation
|
976
|
+
when "subscription"
|
977
|
+
subscription
|
978
|
+
else
|
979
|
+
raise ArgumentError, "unknown operation type: #{operation}"
|
980
|
+
end
|
981
|
+
end
|
982
|
+
|
983
|
+
def root_types
|
984
|
+
@root_types
|
985
|
+
end
|
986
|
+
|
987
|
+
# @param type [Module] The type definition whose possible types you want to see
|
988
|
+
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
989
|
+
# @return [Array<Module>] Possible types for `type`, if it's given.
|
990
|
+
def possible_types(type = nil)
|
991
|
+
if type
|
992
|
+
own_possible_types[type.graphql_name] ||
|
993
|
+
find_inherited_value(:possible_types, EMPTY_HASH)[type.graphql_name] ||
|
994
|
+
EMPTY_ARRAY
|
995
|
+
else
|
996
|
+
find_inherited_value(:possible_types, EMPTY_HASH)
|
997
|
+
.merge(own_possible_types)
|
998
|
+
.merge(introspection_system.possible_types)
|
999
|
+
end
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
def union_memberships(type = nil)
|
1003
|
+
if type
|
1004
|
+
own_um = own_union_memberships.fetch(type.graphql_name, EMPTY_ARRAY)
|
1005
|
+
inherited_um = find_inherited_value(:union_memberships, EMPTY_HASH).fetch(type.graphql_name, EMPTY_ARRAY)
|
1006
|
+
own_um + inherited_um
|
1007
|
+
else
|
1008
|
+
joined_um = own_union_memberships.dup
|
1009
|
+
find_inherited_value(:union_memberhips, EMPTY_HASH).each do |k, v|
|
1010
|
+
um = joined_um[k] ||= []
|
1011
|
+
um.concat(v)
|
1012
|
+
end
|
1013
|
+
joined_um
|
1014
|
+
end
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
def references_to(to_type = nil, from: nil)
|
1018
|
+
@own_references_to ||= Hash.new { |h, k| h[k] = [] }
|
1019
|
+
if to_type
|
1020
|
+
if !to_type.is_a?(String)
|
1021
|
+
to_type = to_type.graphql_name
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
if from
|
1025
|
+
@own_references_to[to_type] << from
|
1026
|
+
else
|
1027
|
+
own_refs = @own_references_to[to_type]
|
1028
|
+
inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY
|
1029
|
+
own_refs + inherited_refs
|
1030
|
+
end
|
1031
|
+
else
|
1032
|
+
# `@own_references_to` can be quite large for big schemas,
|
1033
|
+
# and generally speaking, we won't inherit any values.
|
1034
|
+
# So optimize the most common case -- don't create a duplicate Hash.
|
1035
|
+
inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
|
1036
|
+
if inherited_value.any?
|
1037
|
+
inherited_value.merge(@own_references_to)
|
1038
|
+
else
|
1039
|
+
@own_references_to
|
1040
|
+
end
|
1041
|
+
end
|
1042
|
+
end
|
1043
|
+
|
1044
|
+
def type_from_ast(ast_node, context: nil)
|
1045
|
+
type_owner = context ? context.warden : self
|
1046
|
+
GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
def get_field(type_or_name, field_name)
|
1050
|
+
parent_type = case type_or_name
|
1051
|
+
when LateBoundType
|
1052
|
+
get_type(type_or_name.name)
|
1053
|
+
when String
|
1054
|
+
get_type(type_or_name)
|
1055
|
+
when Module
|
1056
|
+
type_or_name
|
842
1057
|
else
|
843
|
-
|
844
|
-
|
1058
|
+
raise ArgumentError, "unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
if parent_type.kind.fields? && (field = parent_type.get_field(field_name))
|
1062
|
+
field
|
1063
|
+
elsif parent_type == query && (entry_point_field = introspection_system.entry_point(name: field_name))
|
1064
|
+
entry_point_field
|
1065
|
+
elsif (dynamic_field = introspection_system.dynamic_field(name: field_name))
|
1066
|
+
dynamic_field
|
1067
|
+
else
|
1068
|
+
nil
|
845
1069
|
end
|
846
1070
|
end
|
847
1071
|
|
1072
|
+
def get_fields(type)
|
1073
|
+
type.fields
|
1074
|
+
end
|
1075
|
+
|
848
1076
|
def introspection(new_introspection_namespace = nil)
|
849
1077
|
if new_introspection_namespace
|
850
1078
|
@introspection = new_introspection_namespace
|
1079
|
+
# reset this cached value:
|
1080
|
+
@introspection_system = nil
|
851
1081
|
else
|
852
1082
|
@introspection || find_inherited_value(:introspection)
|
853
1083
|
end
|
854
1084
|
end
|
855
1085
|
|
1086
|
+
def introspection_system
|
1087
|
+
if !@introspection_system
|
1088
|
+
@introspection_system = Schema::IntrospectionSystem.new(self)
|
1089
|
+
@introspection_system.resolve_late_bindings
|
1090
|
+
end
|
1091
|
+
@introspection_system
|
1092
|
+
end
|
1093
|
+
|
856
1094
|
def cursor_encoder(new_encoder = nil)
|
857
1095
|
if new_encoder
|
858
1096
|
@cursor_encoder = new_encoder
|
@@ -892,14 +1130,38 @@ module GraphQL
|
|
892
1130
|
end
|
893
1131
|
end
|
894
1132
|
|
1133
|
+
attr_writer :max_complexity
|
1134
|
+
|
895
1135
|
def max_complexity(max_complexity = nil)
|
896
1136
|
if max_complexity
|
897
1137
|
@max_complexity = max_complexity
|
1138
|
+
elsif defined?(@max_complexity)
|
1139
|
+
@max_complexity
|
898
1140
|
else
|
899
|
-
|
1141
|
+
find_inherited_value(:max_complexity)
|
900
1142
|
end
|
901
1143
|
end
|
902
1144
|
|
1145
|
+
attr_writer :analysis_engine
|
1146
|
+
|
1147
|
+
def analysis_engine
|
1148
|
+
@analysis_engine || find_inherited_value(:analysis_engine, GraphQL::Analysis)
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
def using_ast_analysis?
|
1152
|
+
analysis_engine == GraphQL::Analysis::AST
|
1153
|
+
end
|
1154
|
+
|
1155
|
+
def interpreter?
|
1156
|
+
if defined?(@interpreter)
|
1157
|
+
@interpreter
|
1158
|
+
else
|
1159
|
+
find_inherited_value(:interpreter?, false)
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
attr_writer :interpreter
|
1164
|
+
|
903
1165
|
def error_bubbling(new_error_bubbling = nil)
|
904
1166
|
if !new_error_bubbling.nil?
|
905
1167
|
@error_bubbling = new_error_bubbling
|
@@ -908,16 +1170,24 @@ module GraphQL
|
|
908
1170
|
end
|
909
1171
|
end
|
910
1172
|
|
1173
|
+
attr_writer :error_bubbling
|
1174
|
+
|
1175
|
+
attr_writer :max_depth
|
1176
|
+
|
911
1177
|
def max_depth(new_max_depth = nil)
|
912
1178
|
if new_max_depth
|
913
1179
|
@max_depth = new_max_depth
|
1180
|
+
elsif defined?(@max_depth)
|
1181
|
+
@max_depth
|
914
1182
|
else
|
915
|
-
|
1183
|
+
find_inherited_value(:max_depth)
|
916
1184
|
end
|
917
1185
|
end
|
918
1186
|
|
919
1187
|
def disable_introspection_entry_points
|
920
1188
|
@disable_introspection_entry_points = true
|
1189
|
+
# TODO: this clears the cache made in `def types`. But this is not a great solution.
|
1190
|
+
@introspection_system = nil
|
921
1191
|
end
|
922
1192
|
|
923
1193
|
def disable_introspection_entry_points?
|
@@ -930,6 +1200,9 @@ module GraphQL
|
|
930
1200
|
|
931
1201
|
def orphan_types(*new_orphan_types)
|
932
1202
|
if new_orphan_types.any?
|
1203
|
+
new_orphan_types = new_orphan_types.flatten
|
1204
|
+
add_type_and_traverse(new_orphan_types, root: false)
|
1205
|
+
@orphan_types = new_orphan_types
|
933
1206
|
own_orphan_types.concat(new_orphan_types.flatten)
|
934
1207
|
end
|
935
1208
|
|
@@ -958,32 +1231,60 @@ module GraphQL
|
|
958
1231
|
end
|
959
1232
|
end
|
960
1233
|
|
961
|
-
|
962
|
-
|
1234
|
+
# rubocop:disable Lint/DuplicateMethods
|
1235
|
+
module ResolveTypeWithType
|
1236
|
+
def resolve_type(type, obj, ctx)
|
1237
|
+
first_resolved_type = if type.is_a?(Module) && type.respond_to?(:resolve_type)
|
1238
|
+
type.resolve_type(obj, ctx)
|
1239
|
+
else
|
1240
|
+
super
|
1241
|
+
end
|
1242
|
+
|
1243
|
+
after_lazy(first_resolved_type) do |resolved_type|
|
1244
|
+
if resolved_type.nil? || (resolved_type.is_a?(Module) && resolved_type.respond_to?(:kind)) || resolved_type.is_a?(GraphQL::BaseType)
|
1245
|
+
resolved_type
|
1246
|
+
else
|
1247
|
+
raise ".resolve_type should return a type definition, but got #{resolved_type.inspect} (#{resolved_type.class}) from `resolve_type(#{type}, #{obj}, #{ctx})`"
|
1248
|
+
end
|
1249
|
+
end
|
1250
|
+
end
|
963
1251
|
end
|
964
1252
|
|
965
1253
|
def resolve_type(type, obj, ctx)
|
966
1254
|
if type.kind.object?
|
967
1255
|
type
|
968
1256
|
else
|
969
|
-
raise
|
1257
|
+
raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(type, obj, ctx) must be implemented to use Union types or Interface types (tried to resolve: #{type.name})"
|
970
1258
|
end
|
971
1259
|
end
|
1260
|
+
# rubocop:enable Lint/DuplicateMethods
|
1261
|
+
|
1262
|
+
def inherited(child_class)
|
1263
|
+
if self == GraphQL::Schema
|
1264
|
+
child_class.directives(default_directives.values)
|
1265
|
+
end
|
1266
|
+
child_class.singleton_class.prepend(ResolveTypeWithType)
|
1267
|
+
super
|
1268
|
+
end
|
1269
|
+
|
1270
|
+
def rescues
|
1271
|
+
find_inherited_value(:rescues, EMPTY_HASH).merge(own_rescues)
|
1272
|
+
end
|
972
1273
|
|
973
1274
|
def object_from_id(node_id, ctx)
|
974
|
-
raise
|
1275
|
+
raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(node_id, ctx) must be implemented to load by ID (tried to load from id `#{node_id}`)"
|
975
1276
|
end
|
976
1277
|
|
977
1278
|
def id_from_object(object, type, ctx)
|
978
|
-
raise
|
1279
|
+
raise GraphQL::RequiredImplementationMissingError, "#{self.name}.id_from_object(object, type, ctx) must be implemented to create global ids (tried to create an id for `#{object.inspect}`)"
|
979
1280
|
end
|
980
1281
|
|
981
|
-
def visible?(member,
|
982
|
-
|
1282
|
+
def visible?(member, ctx)
|
1283
|
+
member.visible?(ctx)
|
983
1284
|
end
|
984
1285
|
|
985
|
-
def accessible?(member,
|
986
|
-
|
1286
|
+
def accessible?(member, ctx)
|
1287
|
+
member.accessible?(ctx)
|
987
1288
|
end
|
988
1289
|
|
989
1290
|
# This hook is called when a client tries to access one or more
|
@@ -1037,8 +1338,18 @@ module GraphQL
|
|
1037
1338
|
DefaultTypeError.call(type_err, ctx)
|
1038
1339
|
end
|
1039
1340
|
|
1341
|
+
# A function to call when {#execute} receives an invalid query string
|
1342
|
+
#
|
1343
|
+
# The default is to add the error to `context.errors`
|
1344
|
+
# @param err [GraphQL::ParseError] The error encountered during parsing
|
1345
|
+
# @param ctx [GraphQL::Query::Context] The context for the query where the error occurred
|
1346
|
+
# @return void
|
1347
|
+
def parse_error(parse_err, ctx)
|
1348
|
+
ctx.errors.push(parse_err)
|
1349
|
+
end
|
1350
|
+
|
1040
1351
|
def lazy_resolve(lazy_class, value_method)
|
1041
|
-
|
1352
|
+
lazy_methods.set(lazy_class, value_method)
|
1042
1353
|
end
|
1043
1354
|
|
1044
1355
|
def instrument(instrument_step, instrumenter, options = {})
|
@@ -1051,23 +1362,28 @@ module GraphQL
|
|
1051
1362
|
own_instrumenters[step] << instrumenter
|
1052
1363
|
end
|
1053
1364
|
|
1365
|
+
# Add several directives at once
|
1366
|
+
# @param new_directives [Class]
|
1054
1367
|
def directives(new_directives = nil)
|
1055
1368
|
if new_directives
|
1056
|
-
new_directives.each {|d| directive(d) }
|
1369
|
+
new_directives.each { |d| directive(d) }
|
1057
1370
|
end
|
1058
1371
|
|
1059
1372
|
find_inherited_value(:directives, default_directives).merge(own_directives)
|
1060
1373
|
end
|
1061
1374
|
|
1375
|
+
# Attach a single directive to this schema
|
1376
|
+
# @param new_directive [Class]
|
1062
1377
|
def directive(new_directive)
|
1378
|
+
add_type_and_traverse(new_directive, root: false)
|
1063
1379
|
own_directives[new_directive.graphql_name] = new_directive
|
1064
1380
|
end
|
1065
1381
|
|
1066
1382
|
def default_directives
|
1067
1383
|
{
|
1068
|
-
"include" => GraphQL::Directive::
|
1069
|
-
"skip" => GraphQL::Directive::
|
1070
|
-
"deprecated" => GraphQL::Directive::
|
1384
|
+
"include" => GraphQL::Schema::Directive::Include,
|
1385
|
+
"skip" => GraphQL::Schema::Directive::Skip,
|
1386
|
+
"deprecated" => GraphQL::Schema::Directive::Deprecated,
|
1071
1387
|
}
|
1072
1388
|
end
|
1073
1389
|
|
@@ -1094,7 +1410,8 @@ module GraphQL
|
|
1094
1410
|
if new_middleware
|
1095
1411
|
own_middleware << new_middleware
|
1096
1412
|
else
|
1097
|
-
|
1413
|
+
# TODO make sure this is cached when running a query
|
1414
|
+
MiddlewareChain.new(steps: all_middleware, final_step: GraphQL::Execution::Execute::FieldResolveStep)
|
1098
1415
|
end
|
1099
1416
|
end
|
1100
1417
|
|
@@ -1106,10 +1423,125 @@ module GraphQL
|
|
1106
1423
|
find_inherited_value(:multiplex_analyzers, EMPTY_ARRAY) + own_multiplex_analyzers
|
1107
1424
|
end
|
1108
1425
|
|
1426
|
+
# Execute a query on itself.
|
1427
|
+
# @see {Query#initialize} for arguments.
|
1428
|
+
# @return [Hash] query result, ready to be serialized as JSON
|
1429
|
+
def execute(query_str = nil, **kwargs)
|
1430
|
+
if query_str
|
1431
|
+
kwargs[:query] = query_str
|
1432
|
+
end
|
1433
|
+
# Some of the query context _should_ be passed to the multiplex, too
|
1434
|
+
multiplex_context = if (ctx = kwargs[:context])
|
1435
|
+
{
|
1436
|
+
backtrace: ctx[:backtrace],
|
1437
|
+
tracers: ctx[:tracers],
|
1438
|
+
}
|
1439
|
+
else
|
1440
|
+
{}
|
1441
|
+
end
|
1442
|
+
# Since we're running one query, don't run a multiplex-level complexity analyzer
|
1443
|
+
all_results = multiplex([kwargs], max_complexity: nil, context: multiplex_context)
|
1444
|
+
all_results[0]
|
1445
|
+
end
|
1446
|
+
|
1447
|
+
# Execute several queries on itself, concurrently.
|
1448
|
+
#
|
1449
|
+
# @example Run several queries at once
|
1450
|
+
# context = { ... }
|
1451
|
+
# queries = [
|
1452
|
+
# { query: params[:query_1], variables: params[:variables_1], context: context },
|
1453
|
+
# { query: params[:query_2], variables: params[:variables_2], context: context },
|
1454
|
+
# ]
|
1455
|
+
# results = MySchema.multiplex(queries)
|
1456
|
+
# render json: {
|
1457
|
+
# result_1: results[0],
|
1458
|
+
# result_2: results[1],
|
1459
|
+
# }
|
1460
|
+
#
|
1461
|
+
# @see {Query#initialize} for query keyword arguments
|
1462
|
+
# @see {Execution::Multiplex#run_queries} for multiplex keyword arguments
|
1463
|
+
# @param queries [Array<Hash>] Keyword arguments for each query
|
1464
|
+
# @param context [Hash] Multiplex-level context
|
1465
|
+
# @return [Array<Hash>] One result for each query in the input
|
1466
|
+
def multiplex(queries, **kwargs)
|
1467
|
+
schema = if interpreter?
|
1468
|
+
self
|
1469
|
+
else
|
1470
|
+
graphql_definition
|
1471
|
+
end
|
1472
|
+
GraphQL::Execution::Multiplex.run_all(schema, queries, **kwargs)
|
1473
|
+
end
|
1474
|
+
|
1475
|
+
def instrumenters
|
1476
|
+
inherited_instrumenters = find_inherited_value(:instrumenters) || Hash.new { |h,k| h[k] = [] }
|
1477
|
+
inherited_instrumenters.merge(own_instrumenters) do |_step, inherited, own|
|
1478
|
+
inherited + own
|
1479
|
+
end
|
1480
|
+
end
|
1481
|
+
|
1482
|
+
# Call the given block at the right time, either:
|
1483
|
+
# - Right away, if `value` is not registered with `lazy_resolve`
|
1484
|
+
# - After resolving `value`, if it's registered with `lazy_resolve` (eg, `Promise`)
|
1485
|
+
# @api private
|
1486
|
+
def after_lazy(value)
|
1487
|
+
if lazy?(value)
|
1488
|
+
GraphQL::Execution::Lazy.new do
|
1489
|
+
result = sync_lazy(value)
|
1490
|
+
# The returned result might also be lazy, so check it, too
|
1491
|
+
after_lazy(result) do |final_result|
|
1492
|
+
yield(final_result) if block_given?
|
1493
|
+
end
|
1494
|
+
end
|
1495
|
+
else
|
1496
|
+
yield(value) if block_given?
|
1497
|
+
end
|
1498
|
+
end
|
1499
|
+
|
1500
|
+
# Override this method to handle lazy objects in a custom way.
|
1501
|
+
# @param value [Object] an instance of a class registered with {.lazy_resolve}
|
1502
|
+
# @param ctx [GraphQL::Query::Context] the context for this query
|
1503
|
+
# @return [Object] A GraphQL-ready (non-lazy) object
|
1504
|
+
def sync_lazy(value)
|
1505
|
+
lazy_method = lazy_method_name(value)
|
1506
|
+
if lazy_method
|
1507
|
+
synced_value = value.public_send(lazy_method)
|
1508
|
+
sync_lazy(synced_value)
|
1509
|
+
else
|
1510
|
+
value
|
1511
|
+
end
|
1512
|
+
end
|
1513
|
+
|
1514
|
+
# @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered wtih {#lazy_resolve}.
|
1515
|
+
def lazy_method_name(obj)
|
1516
|
+
lazy_methods.get(obj)
|
1517
|
+
end
|
1518
|
+
|
1519
|
+
# @return [Boolean] True if this object should be lazily resolved
|
1520
|
+
def lazy?(obj)
|
1521
|
+
!!lazy_method_name(obj)
|
1522
|
+
end
|
1523
|
+
|
1109
1524
|
private
|
1110
1525
|
|
1111
|
-
def
|
1112
|
-
@
|
1526
|
+
def lazy_methods
|
1527
|
+
if !defined?(@lazy_methods)
|
1528
|
+
if inherited_map = find_inherited_value(:lazy_methods)
|
1529
|
+
# this isn't _completely_ inherited :S (Things added after `dup` won't work)
|
1530
|
+
@lazy_methods = inherited_map.dup
|
1531
|
+
else
|
1532
|
+
@lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
|
1533
|
+
@lazy_methods.set(GraphQL::Execution::Lazy, :value)
|
1534
|
+
end
|
1535
|
+
end
|
1536
|
+
@lazy_methods
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
def own_types
|
1540
|
+
@own_types ||= {}
|
1541
|
+
end
|
1542
|
+
|
1543
|
+
def non_introspection_types
|
1544
|
+
find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
|
1113
1545
|
end
|
1114
1546
|
|
1115
1547
|
def own_plugins
|
@@ -1124,15 +1556,16 @@ module GraphQL
|
|
1124
1556
|
@own_orphan_types ||= []
|
1125
1557
|
end
|
1126
1558
|
|
1127
|
-
def
|
1128
|
-
@
|
1559
|
+
def own_possible_types
|
1560
|
+
@own_possible_types ||= {}
|
1129
1561
|
end
|
1130
1562
|
|
1131
|
-
def
|
1132
|
-
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1563
|
+
def own_union_memberships
|
1564
|
+
@own_union_memberships ||= {}
|
1565
|
+
end
|
1566
|
+
|
1567
|
+
def own_directives
|
1568
|
+
@own_directives ||= {}
|
1136
1569
|
end
|
1137
1570
|
|
1138
1571
|
def own_instrumenters
|
@@ -1159,42 +1592,172 @@ module GraphQL
|
|
1159
1592
|
@own_multiplex_analyzers ||= []
|
1160
1593
|
end
|
1161
1594
|
|
1162
|
-
#
|
1163
|
-
#
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
member = if member.respond_to?(:metadata) && member.metadata
|
1169
|
-
member.metadata[:type_class] || member
|
1170
|
-
else
|
1171
|
-
member
|
1595
|
+
# @param t [Module, Array<Module>]
|
1596
|
+
# @return [void]
|
1597
|
+
def add_type_and_traverse(t, root:)
|
1598
|
+
if root
|
1599
|
+
@root_types ||= []
|
1600
|
+
@root_types << t
|
1172
1601
|
end
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1602
|
+
late_types = []
|
1603
|
+
new_types = Array(t)
|
1604
|
+
new_types.each { |t| add_type(t, owner: nil, late_types: late_types) }
|
1605
|
+
missed_late_types = 0
|
1606
|
+
while (late_type_vals = late_types.shift)
|
1607
|
+
type_owner, lt = late_type_vals
|
1608
|
+
if lt.is_a?(String)
|
1609
|
+
type = Member::BuildType.constantize(lt)
|
1610
|
+
# Reset the counter, since we might succeed next go-round
|
1611
|
+
missed_late_types = 0
|
1612
|
+
update_type_owner(type_owner, type)
|
1613
|
+
add_type(type, owner: type_owner, late_types: late_types)
|
1614
|
+
elsif lt.is_a?(LateBoundType)
|
1615
|
+
if (type = get_type(lt.graphql_name))
|
1616
|
+
# Reset the counter, since we might succeed next go-round
|
1617
|
+
missed_late_types = 0
|
1618
|
+
update_type_owner(type_owner, type)
|
1619
|
+
add_type(type, owner: type_owner, late_types: late_types)
|
1620
|
+
else
|
1621
|
+
missed_late_types += 1
|
1622
|
+
# Add it back to the list, maybe we'll be able to resolve it later.
|
1623
|
+
late_types << [type_owner, lt]
|
1624
|
+
if missed_late_types == late_types.size
|
1625
|
+
# We've looked at all of them and haven't resolved one.
|
1626
|
+
raise UnresolvedLateBoundTypeError.new(type: lt)
|
1627
|
+
else
|
1628
|
+
# Try the next one
|
1629
|
+
end
|
1630
|
+
end
|
1631
|
+
else
|
1632
|
+
raise ArgumentError, "Unexpected late type: #{lt.inspect}"
|
1633
|
+
end
|
1176
1634
|
end
|
1635
|
+
nil
|
1636
|
+
end
|
1177
1637
|
|
1178
|
-
|
1179
|
-
|
1638
|
+
def update_type_owner(owner, type)
|
1639
|
+
case owner
|
1640
|
+
when Class
|
1641
|
+
if owner.kind.union?
|
1642
|
+
# It's a union with possible_types
|
1643
|
+
# Replace the item by class name
|
1644
|
+
new_possible_types = owner.possible_types.map { |t|
|
1645
|
+
if t.is_a?(String) && (t == type.name)
|
1646
|
+
# This is a match of Ruby class names, not graphql names,
|
1647
|
+
# since strings are used to refer to constants.
|
1648
|
+
type
|
1649
|
+
elsif t.is_a?(LateBoundType) && t.graphql_name == type.graphql_name
|
1650
|
+
type
|
1651
|
+
else
|
1652
|
+
t
|
1653
|
+
end
|
1654
|
+
}
|
1655
|
+
owner.possible_types(*new_possible_types)
|
1656
|
+
own_possible_types[owner.graphql_name] = owner.possible_types
|
1657
|
+
elsif type.kind.interface? && owner.kind.object?
|
1658
|
+
new_interfaces = owner.interfaces.map do |t|
|
1659
|
+
if t.is_a?(String) && t == type.graphql_name
|
1660
|
+
type
|
1661
|
+
elsif t.is_a?(LateBoundType) && t.graphql_name == type.graphql_name
|
1662
|
+
type
|
1663
|
+
else
|
1664
|
+
t
|
1665
|
+
end
|
1666
|
+
end
|
1667
|
+
owner.implements(*new_interfaces)
|
1668
|
+
end
|
1669
|
+
|
1670
|
+
when nil
|
1671
|
+
# It's a root type
|
1672
|
+
own_types[type.graphql_name] = type
|
1673
|
+
when GraphQL::Schema::Field, GraphQL::Schema::Argument
|
1674
|
+
orig_type = owner.type
|
1675
|
+
# Apply list/non-null wrapper as needed
|
1676
|
+
if orig_type.respond_to?(:of_type)
|
1677
|
+
transforms = []
|
1678
|
+
while (orig_type.respond_to?(:of_type))
|
1679
|
+
if orig_type.kind.non_null?
|
1680
|
+
transforms << :to_non_null_type
|
1681
|
+
elsif orig_type.kind.list?
|
1682
|
+
transforms << :to_list_type
|
1683
|
+
else
|
1684
|
+
raise "Invariant: :of_type isn't non-null or list"
|
1685
|
+
end
|
1686
|
+
orig_type = orig_type.of_type
|
1687
|
+
end
|
1688
|
+
transforms.reverse_each { |t| type = type.public_send(t) }
|
1689
|
+
end
|
1690
|
+
owner.type = type
|
1180
1691
|
else
|
1181
|
-
|
1692
|
+
raise "Unexpected update: #{owner.inspect} #{type.inspect}"
|
1182
1693
|
end
|
1183
1694
|
end
|
1184
|
-
end
|
1185
1695
|
|
1696
|
+
def add_type(type, owner:, late_types:)
|
1697
|
+
if type.respond_to?(:metadata) && type.metadata.is_a?(Hash)
|
1698
|
+
type_class = type.metadata[:type_class]
|
1699
|
+
if type_class.nil?
|
1700
|
+
raise ArgumentError, "Can't add legacy type: #{type} (#{type.class})"
|
1701
|
+
else
|
1702
|
+
type = type_class
|
1703
|
+
end
|
1704
|
+
elsif type.is_a?(String) || type.is_a?(GraphQL::Schema::LateBoundType)
|
1705
|
+
late_types << [owner, type]
|
1706
|
+
return
|
1707
|
+
end
|
1186
1708
|
|
1187
|
-
|
1188
|
-
|
1189
|
-
|
1190
|
-
|
1191
|
-
end
|
1709
|
+
if owner.is_a?(Class) && owner < GraphQL::Schema::Union
|
1710
|
+
um = own_union_memberships[type.graphql_name] ||= []
|
1711
|
+
um << owner
|
1712
|
+
end
|
1192
1713
|
|
1193
|
-
|
1194
|
-
|
1195
|
-
|
1196
|
-
|
1197
|
-
|
1714
|
+
if (prev_type = own_types[type.graphql_name])
|
1715
|
+
if prev_type != type
|
1716
|
+
raise ArgumentError, "Conflicting type definitions for `#{type.graphql_name}`: #{prev_type} (#{prev_type.class}), #{type} #{type.class}"
|
1717
|
+
else
|
1718
|
+
# This type was already added
|
1719
|
+
end
|
1720
|
+
elsif type.is_a?(Class) && type < GraphQL::Schema::Directive
|
1721
|
+
type.arguments.each do |_name, arg|
|
1722
|
+
arg_type = arg.type.unwrap
|
1723
|
+
references_to(arg_type, from: arg)
|
1724
|
+
add_type(arg_type, owner: arg, late_types: late_types)
|
1725
|
+
end
|
1726
|
+
else
|
1727
|
+
own_types[type.graphql_name] = type
|
1728
|
+
if type.kind.fields?
|
1729
|
+
type.fields.each do |_name, field|
|
1730
|
+
field_type = field.type.unwrap
|
1731
|
+
references_to(field_type, from: field)
|
1732
|
+
add_type(field_type, owner: field, late_types: late_types)
|
1733
|
+
field.arguments.each do |_name, arg|
|
1734
|
+
arg_type = arg.type.unwrap
|
1735
|
+
references_to(arg_type, from: arg)
|
1736
|
+
add_type(arg_type, owner: arg, late_types: late_types)
|
1737
|
+
end
|
1738
|
+
end
|
1739
|
+
end
|
1740
|
+
if type.kind.input_object?
|
1741
|
+
type.arguments.each do |_name, arg|
|
1742
|
+
arg_type = arg.type.unwrap
|
1743
|
+
references_to(arg_type, from: arg)
|
1744
|
+
add_type(arg_type, owner: arg, late_types: late_types)
|
1745
|
+
end
|
1746
|
+
end
|
1747
|
+
if type.kind.union?
|
1748
|
+
own_possible_types[type.graphql_name] = type.possible_types
|
1749
|
+
type.possible_types.each do |t|
|
1750
|
+
add_type(t, owner: type, late_types: late_types)
|
1751
|
+
end
|
1752
|
+
end
|
1753
|
+
if type.kind.object?
|
1754
|
+
own_possible_types[type.graphql_name] = [type]
|
1755
|
+
type.interfaces.each do |i|
|
1756
|
+
implementers = own_possible_types[i.graphql_name] ||= []
|
1757
|
+
implementers << type
|
1758
|
+
add_type(i, owner: type, late_types: late_types)
|
1759
|
+
end
|
1760
|
+
end
|
1198
1761
|
end
|
1199
1762
|
end
|
1200
1763
|
end
|
@@ -1217,33 +1780,16 @@ module GraphQL
|
|
1217
1780
|
end
|
1218
1781
|
end
|
1219
1782
|
|
1220
|
-
# Override this method to handle lazy objects in a custom way.
|
1221
|
-
# @param value [Object] an instance of a class registered with {.lazy_resolve}
|
1222
|
-
# @param ctx [GraphQL::Query::Context] the context for this query
|
1223
|
-
# @return [Object] A GraphQL-ready (non-lazy) object
|
1224
|
-
def self.sync_lazy(value)
|
1225
|
-
if block_given?
|
1226
|
-
# This was already hit by the instance, just give it back
|
1227
|
-
yield(value)
|
1228
|
-
else
|
1229
|
-
# This was called directly on the class, hit the instance
|
1230
|
-
# which has the lazy method map
|
1231
|
-
self.graphql_definition.sync_lazy(value)
|
1232
|
-
end
|
1233
|
-
end
|
1234
|
-
|
1235
1783
|
# @see Schema.sync_lazy for a hook to override
|
1236
1784
|
# @api private
|
1237
1785
|
def sync_lazy(value)
|
1238
|
-
|
1239
|
-
|
1240
|
-
|
1241
|
-
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
end
|
1246
|
-
}
|
1786
|
+
lazy_method = lazy_method_name(value)
|
1787
|
+
if lazy_method
|
1788
|
+
synced_value = value.public_send(lazy_method)
|
1789
|
+
sync_lazy(synced_value)
|
1790
|
+
else
|
1791
|
+
value
|
1792
|
+
end
|
1247
1793
|
end
|
1248
1794
|
|
1249
1795
|
protected
|