graphql 2.0.14 → 2.0.15
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/backtrace/table.rb +2 -2
- data/lib/graphql/dataloader.rb +4 -1
- data/lib/graphql/execution/interpreter/runtime.rb +23 -10
- data/lib/graphql/execution/interpreter.rb +4 -4
- data/lib/graphql/execution/lookahead.rb +15 -4
- data/lib/graphql/introspection.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +18 -18
- data/lib/graphql/language/printer.rb +20 -11
- data/lib/graphql/query/context.rb +19 -5
- data/lib/graphql/schema/build_from_definition.rb +31 -15
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/field.rb +3 -3
- data/lib/graphql/schema/member/build_type.rb +1 -1
- data/lib/graphql/schema/member/has_directives.rb +71 -56
- data/lib/graphql/schema/resolver/has_payload_type.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +3 -0
- data/lib/graphql/schema.rb +16 -6
- data/lib/graphql/tracing/instrumentation_tracing.rb +83 -0
- data/lib/graphql/types/relay/node_behaviors.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46b43157a331eae41dd54897d24dbc3adac190910237c7c1c158fc2bc32b6616
|
4
|
+
data.tar.gz: 118ac97b740ba1340d3e6b9aee5fa2f81ca25dc8769a98ec0d8e25d63e839711
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be38356ecdff7f8a9627bb021e83d13ea92e951b287386e76fd6e42ff05ef0a5a37ce9c98e6e4bf1834459989558f6c8b512c98e7642bef2c624578c1db24932
|
7
|
+
data.tar.gz: b0b75f342dca7a731b1b59e6399d364184152ceb0c5deb9fb4338a3582b3b6e38bf789a53839f6def9eb4b9c079c45e74675fb608a184dff4e6aeaf0ce052dfd
|
@@ -83,7 +83,7 @@ module GraphQL
|
|
83
83
|
value = if top && @override_value
|
84
84
|
@override_value
|
85
85
|
else
|
86
|
-
value_at(@context.query.context.namespace(:
|
86
|
+
value_at(@context.query.context.namespace(:interpreter_runtime)[:runtime], context_entry.path)
|
87
87
|
end
|
88
88
|
rows << [
|
89
89
|
"#{context_entry.ast_node ? context_entry.ast_node.position.join(":") : ""}",
|
@@ -112,7 +112,7 @@ module GraphQL
|
|
112
112
|
if object.is_a?(GraphQL::Schema::Object)
|
113
113
|
object = object.object
|
114
114
|
end
|
115
|
-
value = value_at(context_entry.namespace(:
|
115
|
+
value = value_at(context_entry.namespace(:interpreter_runtime)[:runtime], [])
|
116
116
|
rows << [
|
117
117
|
"#{position}",
|
118
118
|
"#{op_type}#{op_name ? " #{op_name}" : ""}",
|
data/lib/graphql/dataloader.rb
CHANGED
@@ -289,7 +289,10 @@ module GraphQL
|
|
289
289
|
fiber_locals = {}
|
290
290
|
|
291
291
|
Thread.current.keys.each do |fiber_var_key|
|
292
|
-
|
292
|
+
# This variable should be fresh in each new fiber
|
293
|
+
if fiber_var_key != :__graphql_runtime_info
|
294
|
+
fiber_locals[fiber_var_key] = Thread.current[fiber_var_key]
|
295
|
+
end
|
293
296
|
end
|
294
297
|
|
295
298
|
if @nonblocking
|
@@ -148,13 +148,23 @@ module GraphQL
|
|
148
148
|
# @return [GraphQL::Query::Context]
|
149
149
|
attr_reader :context
|
150
150
|
|
151
|
+
def thread_info
|
152
|
+
info = Thread.current[:__graphql_runtime_info]
|
153
|
+
if !info
|
154
|
+
new_ti = {}
|
155
|
+
info = Thread.current[:__graphql_runtime_info] = new_ti
|
156
|
+
end
|
157
|
+
info
|
158
|
+
end
|
159
|
+
|
151
160
|
def initialize(query:)
|
152
161
|
@query = query
|
153
162
|
@dataloader = query.multiplex.dataloader
|
154
163
|
@schema = query.schema
|
155
164
|
@context = query.context
|
156
165
|
@multiplex_context = query.multiplex.context
|
157
|
-
|
166
|
+
# Start this off empty:
|
167
|
+
Thread.current[:__graphql_runtime_info] = nil
|
158
168
|
@response = GraphQLResultHash.new(nil, nil)
|
159
169
|
# Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
|
160
170
|
@runtime_directive_names = []
|
@@ -680,7 +690,11 @@ module GraphQL
|
|
680
690
|
|
681
691
|
case current_type.kind.name
|
682
692
|
when "SCALAR", "ENUM"
|
683
|
-
r =
|
693
|
+
r = begin
|
694
|
+
current_type.coerce_result(value, context)
|
695
|
+
rescue StandardError => err
|
696
|
+
schema.handle_or_reraise(context, err)
|
697
|
+
end
|
684
698
|
set_result(selection_result, result_name, r)
|
685
699
|
r
|
686
700
|
when "UNION", "INTERFACE"
|
@@ -870,17 +884,18 @@ module GraphQL
|
|
870
884
|
end
|
871
885
|
|
872
886
|
def set_all_interpreter_context(object, field, arguments, path)
|
887
|
+
ti = thread_info
|
873
888
|
if object
|
874
|
-
|
889
|
+
ti[:current_object] = object
|
875
890
|
end
|
876
891
|
if field
|
877
|
-
|
892
|
+
ti[:current_field] = field
|
878
893
|
end
|
879
894
|
if arguments
|
880
|
-
|
895
|
+
ti[:current_arguments] = arguments
|
881
896
|
end
|
882
897
|
if path
|
883
|
-
|
898
|
+
ti[:current_path] = path
|
884
899
|
end
|
885
900
|
end
|
886
901
|
|
@@ -940,13 +955,11 @@ module GraphQL
|
|
940
955
|
# Set this pair in the Query context, but also in the interpeter namespace,
|
941
956
|
# for compatibility.
|
942
957
|
def set_interpreter_context(key, value)
|
943
|
-
|
944
|
-
@context[key] = value
|
958
|
+
thread_info[key] = value
|
945
959
|
end
|
946
960
|
|
947
961
|
def delete_interpreter_context(key)
|
948
|
-
|
949
|
-
@context.delete(key)
|
962
|
+
(ti = thread_info) && ti.delete(key)
|
950
963
|
end
|
951
964
|
|
952
965
|
def resolve_type(type, value, path)
|
@@ -68,7 +68,7 @@ module GraphQL
|
|
68
68
|
# they also have another item of state, which is private to that query
|
69
69
|
# in particular, assign it here:
|
70
70
|
runtime = Runtime.new(query: query)
|
71
|
-
query.context.namespace(:
|
71
|
+
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
72
72
|
|
73
73
|
query.trace("execute_query", {query: query}) do
|
74
74
|
runtime.run_eager
|
@@ -90,7 +90,7 @@ module GraphQL
|
|
90
90
|
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
91
91
|
queries = multiplex ? multiplex.queries : [query]
|
92
92
|
final_values = queries.map do |query|
|
93
|
-
runtime = query.context.namespace(:
|
93
|
+
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
94
94
|
# it might not be present if the query has an error
|
95
95
|
runtime ? runtime.final_result : nil
|
96
96
|
end
|
@@ -99,7 +99,7 @@ module GraphQL
|
|
99
99
|
Interpreter::Resolve.resolve_all(final_values, multiplex.dataloader)
|
100
100
|
end
|
101
101
|
queries.each do |query|
|
102
|
-
runtime = query.context.namespace(:
|
102
|
+
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
103
103
|
if runtime
|
104
104
|
runtime.delete_interpreter_context(:current_path)
|
105
105
|
runtime.delete_interpreter_context(:current_field)
|
@@ -123,7 +123,7 @@ module GraphQL
|
|
123
123
|
end
|
124
124
|
else
|
125
125
|
result = {
|
126
|
-
"data" => query.context.namespace(:
|
126
|
+
"data" => query.context.namespace(:interpreter_runtime)[:runtime].final_result
|
127
127
|
}
|
128
128
|
|
129
129
|
if query.context.errors.any?
|
@@ -76,8 +76,8 @@ module GraphQL
|
|
76
76
|
# @param field_name [String, Symbol]
|
77
77
|
# @param arguments [Hash] Arguments which must match in the selection
|
78
78
|
# @return [Boolean]
|
79
|
-
def selects?(field_name, arguments: nil)
|
80
|
-
selection(field_name, arguments: arguments).selected?
|
79
|
+
def selects?(field_name, selected_type: @selected_type, arguments: nil)
|
80
|
+
selection(field_name, selected_type: selected_type, arguments: arguments).selected?
|
81
81
|
end
|
82
82
|
|
83
83
|
# @return [Boolean] True if this lookahead represents a field that was requested
|
@@ -95,11 +95,22 @@ module GraphQL
|
|
95
95
|
@query.get_field(selected_type, field_name)
|
96
96
|
when Symbol
|
97
97
|
# Try to avoid the `.to_s` below, if possible
|
98
|
-
all_fields =
|
98
|
+
all_fields = if selected_type.kind.fields?
|
99
|
+
@query.warden.fields(selected_type)
|
100
|
+
else
|
101
|
+
# Handle unions by checking possible
|
102
|
+
@query.warden
|
103
|
+
.possible_types(selected_type)
|
104
|
+
.map { |t| @query.warden.fields(t) }
|
105
|
+
.flatten
|
106
|
+
end
|
107
|
+
|
99
108
|
if (match_by_orig_name = all_fields.find { |f| f.original_name == field_name })
|
100
109
|
match_by_orig_name
|
101
110
|
else
|
102
|
-
|
111
|
+
# Symbol#name is only present on 3.0+
|
112
|
+
sym_s = field_name.respond_to?(:name) ? field_name.name : field_name.to_s
|
113
|
+
guessed_name = Schema::Member::BuildType.camelize(sym_s)
|
103
114
|
@query.get_field(selected_type, guessed_name)
|
104
115
|
end
|
105
116
|
end
|
@@ -4,7 +4,7 @@ module GraphQL
|
|
4
4
|
def self.query(include_deprecated_args: false, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
|
5
5
|
# The introspection query to end all introspection queries, copied from
|
6
6
|
# https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js
|
7
|
-
<<-QUERY
|
7
|
+
<<-QUERY.gsub(/\n{2,}/, "\n")
|
8
8
|
query IntrospectionQuery {
|
9
9
|
__schema {
|
10
10
|
#{include_schema_description ? "description" : ""}
|
@@ -44,16 +44,18 @@ module GraphQL
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def build_schema_node
|
47
|
-
|
48
|
-
query: (q = warden.root_type_for_operation("query")) && q.graphql_name,
|
49
|
-
mutation: (m = warden.root_type_for_operation("mutation")) && m.graphql_name,
|
50
|
-
subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
|
51
|
-
# This only supports directives from parsing,
|
52
|
-
# use a custom printer to add to this list.
|
53
|
-
#
|
47
|
+
schema_options = {
|
54
48
|
# `@schema.directives` is covered by `build_definition_nodes`
|
55
|
-
directives:
|
56
|
-
|
49
|
+
directives: definition_directives(@schema, :schema_directives),
|
50
|
+
}
|
51
|
+
if !schema_respects_root_name_conventions?(@schema)
|
52
|
+
schema_options.merge!({
|
53
|
+
query: (q = warden.root_type_for_operation("query")) && q.graphql_name,
|
54
|
+
mutation: (m = warden.root_type_for_operation("mutation")) && m.graphql_name,
|
55
|
+
subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
|
56
|
+
})
|
57
|
+
end
|
58
|
+
GraphQL::Language::Nodes::SchemaDefinition.new(schema_options)
|
57
59
|
end
|
58
60
|
|
59
61
|
def build_object_type_node(object_type)
|
@@ -283,7 +285,9 @@ module GraphQL
|
|
283
285
|
private
|
284
286
|
|
285
287
|
def include_schema_node?
|
286
|
-
always_include_schema ||
|
288
|
+
always_include_schema ||
|
289
|
+
!schema_respects_root_name_conventions?(schema) ||
|
290
|
+
!schema.schema_directives.empty?
|
287
291
|
end
|
288
292
|
|
289
293
|
def schema_respects_root_name_conventions?(schema)
|
@@ -293,14 +297,14 @@ module GraphQL
|
|
293
297
|
end
|
294
298
|
|
295
299
|
def directives(member)
|
296
|
-
definition_directives(member)
|
300
|
+
definition_directives(member, :directives)
|
297
301
|
end
|
298
302
|
|
299
|
-
def definition_directives(member)
|
300
|
-
dirs = if !member.respond_to?(
|
303
|
+
def definition_directives(member, directives_method)
|
304
|
+
dirs = if !member.respond_to?(directives_method) || member.directives.empty?
|
301
305
|
[]
|
302
306
|
else
|
303
|
-
member.
|
307
|
+
member.public_send(directives_method).map do |dir|
|
304
308
|
args = []
|
305
309
|
dir.arguments.argument_values.each_value do |arg_value| # rubocop:disable Development/ContextIsPassedCop -- directive instance method
|
306
310
|
arg_defn = arg_value.definition
|
@@ -324,10 +328,6 @@ module GraphQL
|
|
324
328
|
dirs
|
325
329
|
end
|
326
330
|
|
327
|
-
def ast_directives(member)
|
328
|
-
member.ast_node ? member.ast_node.directives : []
|
329
|
-
end
|
330
|
-
|
331
331
|
attr_reader :schema, :warden, :always_include_schema,
|
332
332
|
:include_introspection_types, :include_built_in_directives, :include_built_in_scalars
|
333
333
|
end
|
@@ -132,10 +132,11 @@ module GraphQL
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def print_schema_definition(schema)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
135
|
+
has_conventional_names = (schema.query.nil? || schema.query == 'Query') &&
|
136
|
+
(schema.mutation.nil? || schema.mutation == 'Mutation') &&
|
137
|
+
(schema.subscription.nil? || schema.subscription == 'Subscription')
|
138
|
+
|
139
|
+
if has_conventional_names && schema.directives.empty?
|
139
140
|
return
|
140
141
|
end
|
141
142
|
|
@@ -145,14 +146,22 @@ module GraphQL
|
|
145
146
|
out << "\n "
|
146
147
|
out << print_node(dir)
|
147
148
|
end
|
148
|
-
|
149
|
-
|
150
|
-
|
149
|
+
if !has_conventional_names
|
150
|
+
out << "\n"
|
151
|
+
end
|
151
152
|
end
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
153
|
+
|
154
|
+
if !has_conventional_names
|
155
|
+
if schema.directives.empty?
|
156
|
+
out << " "
|
157
|
+
end
|
158
|
+
out << "{\n"
|
159
|
+
out << " query: #{schema.query}\n" if schema.query
|
160
|
+
out << " mutation: #{schema.mutation}\n" if schema.mutation
|
161
|
+
out << " subscription: #{schema.subscription}\n" if schema.subscription
|
162
|
+
out << "}"
|
163
|
+
end
|
164
|
+
out
|
156
165
|
end
|
157
166
|
|
158
167
|
def print_scalar_type_definition(scalar_type)
|
@@ -111,7 +111,7 @@ module GraphQL
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def current_path
|
114
|
-
@query_context
|
114
|
+
@query_context[:current_path] || @no_path
|
115
115
|
end
|
116
116
|
|
117
117
|
def key?(key)
|
@@ -189,12 +189,16 @@ module GraphQL
|
|
189
189
|
|
190
190
|
def_delegators :@query, :trace, :interpreter?
|
191
191
|
|
192
|
+
RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path])
|
192
193
|
# @!method []=(key, value)
|
193
194
|
# Reassign `key` to the hash passed to {Schema#execute} as `context:`
|
194
195
|
|
195
196
|
# Lookup `key` from the hash passed to {Schema#execute} as `context:`
|
196
197
|
def [](key)
|
197
|
-
if
|
198
|
+
if RUNTIME_METADATA_KEYS.include?(key)
|
199
|
+
thread_info = Thread.current[:__graphql_runtime_info]
|
200
|
+
thread_info && thread_info[key]
|
201
|
+
elsif @scoped_context.key?(key)
|
198
202
|
@scoped_context[key]
|
199
203
|
else
|
200
204
|
@provided_values[key]
|
@@ -212,7 +216,10 @@ module GraphQL
|
|
212
216
|
UNSPECIFIED_FETCH_DEFAULT = Object.new
|
213
217
|
|
214
218
|
def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
|
215
|
-
if
|
219
|
+
if RUNTIME_METADATA_KEYS.include?(key)
|
220
|
+
(thread_info = Thread.current[:__graphql_runtime_info]) &&
|
221
|
+
thread_info[key]
|
222
|
+
elsif @scoped_context.key?(key)
|
216
223
|
scoped_context[key]
|
217
224
|
elsif @provided_values.key?(key)
|
218
225
|
@provided_values[key]
|
@@ -226,7 +233,10 @@ module GraphQL
|
|
226
233
|
end
|
227
234
|
|
228
235
|
def dig(key, *other_keys)
|
229
|
-
if
|
236
|
+
if RUNTIME_METADATA_KEYS.include?(key)
|
237
|
+
(thread_info = Thread.current[:__graphql_runtime_info]).key?(key) &&
|
238
|
+
thread_info.dig(key)
|
239
|
+
elsif @scoped_context.key?(key)
|
230
240
|
@scoped_context.dig(key, *other_keys)
|
231
241
|
else
|
232
242
|
@provided_values.dig(key, *other_keys)
|
@@ -259,7 +269,11 @@ module GraphQL
|
|
259
269
|
# @param ns [Object] a usage-specific namespace identifier
|
260
270
|
# @return [Hash] namespaced storage
|
261
271
|
def namespace(ns)
|
262
|
-
|
272
|
+
if ns == :interpreter
|
273
|
+
self
|
274
|
+
else
|
275
|
+
@storage[ns]
|
276
|
+
end
|
263
277
|
end
|
264
278
|
|
265
279
|
# @return [Boolean] true if this namespace was accessed before
|
@@ -6,16 +6,16 @@ module GraphQL
|
|
6
6
|
module BuildFromDefinition
|
7
7
|
class << self
|
8
8
|
# @see {Schema.from_definition}
|
9
|
-
def from_definition(definition_string, parser: GraphQL.default_parser, **kwargs)
|
10
|
-
from_document(parser.parse(definition_string), **kwargs)
|
9
|
+
def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs)
|
10
|
+
from_document(schema_superclass, parser.parse(definition_string), **kwargs)
|
11
11
|
end
|
12
12
|
|
13
|
-
def from_definition_path(definition_path, parser: GraphQL.default_parser, **kwargs)
|
14
|
-
from_document(parser.parse_file(definition_path), **kwargs)
|
13
|
+
def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs)
|
14
|
+
from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
|
15
15
|
end
|
16
16
|
|
17
|
-
def from_document(document, default_resolve:, using: {}, relay: false)
|
18
|
-
Builder.build(document, default_resolve: default_resolve || {}, relay: relay, using: using)
|
17
|
+
def from_document(schema_superclass, document, default_resolve:, using: {}, relay: false)
|
18
|
+
Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -23,7 +23,7 @@ module GraphQL
|
|
23
23
|
module Builder
|
24
24
|
extend self
|
25
25
|
|
26
|
-
def build(document, default_resolve:, using: {}, relay:)
|
26
|
+
def build(schema_superclass, document, default_resolve:, using: {}, relay:)
|
27
27
|
raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
|
28
28
|
|
29
29
|
if default_resolve.is_a?(Hash)
|
@@ -36,7 +36,7 @@ module GraphQL
|
|
36
36
|
end
|
37
37
|
schema_definition = schema_defns.first
|
38
38
|
types = {}
|
39
|
-
directives =
|
39
|
+
directives = schema_superclass.directives.dup
|
40
40
|
type_resolver = build_resolve_type(types, directives, ->(type_name) { types[type_name] ||= Schema::LateBoundType.new(type_name)})
|
41
41
|
# Make a different type resolver because we need to coerce directive arguments
|
42
42
|
# _while_ building the schema.
|
@@ -65,10 +65,14 @@ module GraphQL
|
|
65
65
|
# In case any directives referenced built-in types for their arguments:
|
66
66
|
replace_late_bound_types_with_built_in(types)
|
67
67
|
|
68
|
+
schema_extensions = nil
|
68
69
|
document.definitions.each do |definition|
|
69
70
|
case definition
|
70
71
|
when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
|
71
72
|
nil # already handled
|
73
|
+
when GraphQL::Language::Nodes::SchemaExtension
|
74
|
+
schema_extensions ||= []
|
75
|
+
schema_extensions << definition
|
72
76
|
else
|
73
77
|
# It's possible that this was already loaded by the directives
|
74
78
|
prev_type = types[definition.name]
|
@@ -103,7 +107,7 @@ module GraphQL
|
|
103
107
|
|
104
108
|
raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
|
105
109
|
|
106
|
-
Class.new(
|
110
|
+
schema_class = Class.new(schema_superclass) do
|
107
111
|
begin
|
108
112
|
# Add these first so that there's some chance of resolving late-bound types
|
109
113
|
orphan_types types.values
|
@@ -157,6 +161,14 @@ module GraphQL
|
|
157
161
|
child_class.definition_default_resolve = self.definition_default_resolve
|
158
162
|
end
|
159
163
|
end
|
164
|
+
|
165
|
+
if schema_extensions
|
166
|
+
schema_extensions.each do |ext|
|
167
|
+
build_directives(schema_class, ext, type_resolver)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
schema_class
|
160
172
|
end
|
161
173
|
|
162
174
|
NullResolveType = ->(type, obj, ctx) {
|
@@ -196,13 +208,18 @@ module GraphQL
|
|
196
208
|
|
197
209
|
def build_directives(definition, ast_node, type_resolver)
|
198
210
|
dirs = prepare_directives(ast_node, type_resolver)
|
199
|
-
dirs.each do |dir_class, options|
|
200
|
-
definition.
|
211
|
+
dirs.each do |(dir_class, options)|
|
212
|
+
if definition.respond_to?(:schema_directive)
|
213
|
+
# it's a schema
|
214
|
+
definition.schema_directive(dir_class, **options)
|
215
|
+
else
|
216
|
+
definition.directive(dir_class, **options)
|
217
|
+
end
|
201
218
|
end
|
202
219
|
end
|
203
220
|
|
204
221
|
def prepare_directives(ast_node, type_resolver)
|
205
|
-
dirs =
|
222
|
+
dirs = []
|
206
223
|
ast_node.directives.each do |dir_node|
|
207
224
|
if dir_node.name == "deprecated"
|
208
225
|
# This is handled using `deprecation_reason`
|
@@ -210,10 +227,10 @@ module GraphQL
|
|
210
227
|
else
|
211
228
|
dir_class = type_resolver.call(dir_node.name)
|
212
229
|
if dir_class.nil?
|
213
|
-
raise ArgumentError, "No definition for @#{dir_node.name} on #{ast_node.name} at #{ast_node.line}:#{ast_node.col}"
|
230
|
+
raise ArgumentError, "No definition for @#{dir_node.name} #{ast_node.respond_to?(:name) ? "on #{ast_node.name} " : ""}at #{ast_node.line}:#{ast_node.col}"
|
214
231
|
end
|
215
232
|
options = args_to_kwargs(dir_class, dir_node)
|
216
|
-
dirs[dir_class
|
233
|
+
dirs << [dir_class, options]
|
217
234
|
end
|
218
235
|
end
|
219
236
|
dirs
|
@@ -389,7 +406,6 @@ module GraphQL
|
|
389
406
|
graphql_name(interface_type_definition.name)
|
390
407
|
description(interface_type_definition.description)
|
391
408
|
interface_type_definition.interfaces.each do |interface_name|
|
392
|
-
"Implements: #{interface_type_definition} -> #{interface_name}"
|
393
409
|
interface_defn = type_resolver.call(interface_name)
|
394
410
|
implements(interface_defn)
|
395
411
|
end
|
@@ -39,7 +39,7 @@ module GraphQL
|
|
39
39
|
transform_name = arguments[:by]
|
40
40
|
if TRANSFORMS.include?(transform_name) && return_value.respond_to?(transform_name)
|
41
41
|
return_value = return_value.public_send(transform_name)
|
42
|
-
response = context.namespace(:
|
42
|
+
response = context.namespace(:interpreter_runtime)[:runtime].final_result
|
43
43
|
*keys, last = path
|
44
44
|
keys.each do |key|
|
45
45
|
if response && (response = response[key])
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -129,10 +129,10 @@ module GraphQL
|
|
129
129
|
def connection?
|
130
130
|
if @connection.nil?
|
131
131
|
# Provide default based on type name
|
132
|
-
return_type_name = if @
|
133
|
-
Member::BuildType.to_type_name(@resolver_class.type)
|
134
|
-
elsif @return_type_expr
|
132
|
+
return_type_name = if @return_type_expr
|
135
133
|
Member::BuildType.to_type_name(@return_type_expr)
|
134
|
+
elsif @resolver_class && @resolver_class.type
|
135
|
+
Member::BuildType.to_type_name(@resolver_class.type)
|
136
136
|
else
|
137
137
|
# As a last ditch, try to force loading the return type:
|
138
138
|
type.unwrap.name
|
@@ -120,7 +120,7 @@ module GraphQL
|
|
120
120
|
def camelize(string)
|
121
121
|
return string if string == '_'
|
122
122
|
return string unless string.include?("_")
|
123
|
-
camelized = string.split('_').
|
123
|
+
camelized = string.split('_').each(&:capitalize!).join
|
124
124
|
camelized[0] = camelized[0].downcase
|
125
125
|
if (match_data = string.match(/\A(_+)/))
|
126
126
|
camelized = "#{match_data[0]}#{camelized}"
|
@@ -11,8 +11,7 @@ module GraphQL
|
|
11
11
|
# @return [void]
|
12
12
|
def directive(dir_class, **options)
|
13
13
|
@own_directives ||= []
|
14
|
-
|
15
|
-
@own_directives << dir_class.new(self, **options)
|
14
|
+
HasDirectives.add_directive(self, @own_directives, dir_class, options)
|
16
15
|
nil
|
17
16
|
end
|
18
17
|
|
@@ -20,78 +19,94 @@ module GraphQL
|
|
20
19
|
# @param dir_class [Class<GraphQL::Schema::Directive>]
|
21
20
|
# @return [viod]
|
22
21
|
def remove_directive(dir_class)
|
23
|
-
@own_directives
|
22
|
+
HasDirectives.remove_directive(@own_directives, dir_class)
|
24
23
|
nil
|
25
24
|
end
|
26
25
|
|
27
26
|
NO_DIRECTIVES = [].freeze
|
28
27
|
|
29
28
|
def directives
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
29
|
+
HasDirectives.get_directives(self, @own_directives, :directives)
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
def add_directive(schema_member, directives, directive_class, directive_options)
|
34
|
+
remove_directive(directives, directive_class) unless directive_class.repeatable?
|
35
|
+
directives << directive_class.new(schema_member, **directive_options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove_directive(directives, directive_class)
|
39
|
+
directives && directives.reject! { |d| d.is_a?(directive_class) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_directives(schema_member, directives, directives_method)
|
43
|
+
case schema_member
|
44
|
+
when Class
|
45
|
+
inherited_directives = if schema_member.superclass.respond_to?(directives_method)
|
46
|
+
get_directives(schema_member.superclass, schema_member.superclass.public_send(directives_method), directives_method)
|
47
|
+
else
|
48
|
+
NO_DIRECTIVES
|
49
|
+
end
|
50
|
+
if inherited_directives.any? && directives
|
51
|
+
dirs = []
|
52
|
+
merge_directives(dirs, inherited_directives)
|
53
|
+
merge_directives(dirs, directives)
|
54
|
+
dirs
|
55
|
+
elsif directives
|
56
|
+
directives
|
57
|
+
elsif inherited_directives.any?
|
58
|
+
inherited_directives
|
59
|
+
else
|
60
|
+
NO_DIRECTIVES
|
61
|
+
end
|
62
|
+
when Module
|
63
|
+
dirs = nil
|
64
|
+
schema_member.ancestors.reverse_each do |ancestor|
|
65
|
+
if ancestor.respond_to?(:own_directives) &&
|
66
|
+
(anc_dirs = ancestor.own_directives).any?
|
67
|
+
dirs ||= []
|
68
|
+
merge_directives(dirs, anc_dirs)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
if directives
|
54
72
|
dirs ||= []
|
55
|
-
merge_directives(dirs,
|
73
|
+
merge_directives(dirs, directives)
|
56
74
|
end
|
75
|
+
dirs || NO_DIRECTIVES
|
76
|
+
when HasDirectives
|
77
|
+
directives || NO_DIRECTIVES
|
78
|
+
else
|
79
|
+
raise "Invariant: how could #{schema_member} not be a Class, Module, or instance of HasDirectives?"
|
57
80
|
end
|
58
|
-
|
59
|
-
|
60
|
-
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Modify `target` by adding items from `dirs` such that:
|
86
|
+
# - Any name conflict is overriden by the incoming member of `dirs`
|
87
|
+
# - Any other member of `dirs` is appended
|
88
|
+
# @param target [Array<GraphQL::Schema::Directive>]
|
89
|
+
# @param dirs [Array<GraphQL::Schema::Directive>]
|
90
|
+
# @return [void]
|
91
|
+
def merge_directives(target, dirs)
|
92
|
+
dirs.each do |dir|
|
93
|
+
if (idx = target.find_index { |d| d.graphql_name == dir.graphql_name })
|
94
|
+
target.slice!(idx)
|
95
|
+
target.insert(idx, dir)
|
96
|
+
else
|
97
|
+
target << dir
|
98
|
+
end
|
61
99
|
end
|
62
|
-
|
63
|
-
when HasDirectives
|
64
|
-
@own_directives || NO_DIRECTIVES
|
65
|
-
else
|
66
|
-
raise "Invariant: how could #{self} not be a Class, Module, or instance of HasDirectives?"
|
100
|
+
nil
|
67
101
|
end
|
68
102
|
end
|
69
103
|
|
104
|
+
|
70
105
|
protected
|
71
106
|
|
72
107
|
def own_directives
|
73
108
|
@own_directives
|
74
109
|
end
|
75
|
-
|
76
|
-
private
|
77
|
-
|
78
|
-
# Modify `target` by adding items from `dirs` such that:
|
79
|
-
# - Any name conflict is overriden by the incoming member of `dirs`
|
80
|
-
# - Any other member of `dirs` is appended
|
81
|
-
# @param target [Array<GraphQL::Schema::Directive>]
|
82
|
-
# @param dirs [Array<GraphQL::Schema::Directive>]
|
83
|
-
# @return [void]
|
84
|
-
def merge_directives(target, dirs)
|
85
|
-
dirs.each do |dir|
|
86
|
-
if (idx = target.find_index { |d| d.graphql_name == dir.graphql_name })
|
87
|
-
target.slice!(idx)
|
88
|
-
target.insert(idx, dir)
|
89
|
-
else
|
90
|
-
target << dir
|
91
|
-
end
|
92
|
-
end
|
93
|
-
nil
|
94
|
-
end
|
95
110
|
end
|
96
111
|
end
|
97
112
|
end
|
@@ -91,7 +91,7 @@ module GraphQL
|
|
91
91
|
resolver_fields = all_field_definitions
|
92
92
|
Class.new(object_class) do
|
93
93
|
graphql_name("#{resolver_name}Payload")
|
94
|
-
description("Autogenerated return type of #{resolver_name}")
|
94
|
+
description("Autogenerated return type of #{resolver_name}.")
|
95
95
|
resolver_fields.each do |f|
|
96
96
|
# Reattach the already-defined field here
|
97
97
|
# (The field's `.owner` will still point to the mutation, not the object type, I think)
|
@@ -11,6 +11,9 @@ module GraphQL
|
|
11
11
|
# @return [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
|
12
12
|
attr_reader :abstract_type
|
13
13
|
|
14
|
+
# @return [Hash]
|
15
|
+
attr_reader :options
|
16
|
+
|
14
17
|
# Called when an object is hooked up to an abstract type, such as {Schema::Union.possible_types}
|
15
18
|
# or {Schema::Object.implements} (for interfaces).
|
16
19
|
#
|
data/lib/graphql/schema.rb
CHANGED
@@ -110,9 +110,10 @@ module GraphQL
|
|
110
110
|
# @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
|
111
111
|
# @return [Class] the schema described by `document`
|
112
112
|
def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
|
113
|
-
# If the file ends in `.graphql`, treat it like a filepath
|
114
|
-
if definition_or_path.end_with?(".graphql")
|
113
|
+
# If the file ends in `.graphql` or `.graphqls`, treat it like a filepath
|
114
|
+
if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls")
|
115
115
|
GraphQL::Schema::BuildFromDefinition.from_definition_path(
|
116
|
+
self,
|
116
117
|
definition_or_path,
|
117
118
|
default_resolve: default_resolve,
|
118
119
|
parser: parser,
|
@@ -120,6 +121,7 @@ module GraphQL
|
|
120
121
|
)
|
121
122
|
else
|
122
123
|
GraphQL::Schema::BuildFromDefinition.from_definition(
|
124
|
+
self,
|
123
125
|
definition_or_path,
|
124
126
|
default_resolve: default_resolve,
|
125
127
|
parser: parser,
|
@@ -738,11 +740,10 @@ module GraphQL
|
|
738
740
|
def handle_or_reraise(context, err)
|
739
741
|
handler = Execution::Errors.find_handler_for(self, err.class)
|
740
742
|
if handler
|
741
|
-
|
742
|
-
|
743
|
-
args = runtime_info[:current_arguments]
|
743
|
+
obj = context[:current_object]
|
744
|
+
args = context[:current_arguments]
|
744
745
|
args = args && args.keyword_arguments
|
745
|
-
field =
|
746
|
+
field = context[:current_field]
|
746
747
|
if obj.is_a?(GraphQL::Schema::Object)
|
747
748
|
obj = obj.object
|
748
749
|
end
|
@@ -816,6 +817,15 @@ module GraphQL
|
|
816
817
|
member.accessible?(ctx)
|
817
818
|
end
|
818
819
|
|
820
|
+
def schema_directive(dir_class, **options)
|
821
|
+
@own_schema_directives ||= []
|
822
|
+
Member::HasDirectives.add_directive(self, @own_schema_directives, dir_class, options)
|
823
|
+
end
|
824
|
+
|
825
|
+
def schema_directives
|
826
|
+
Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives)
|
827
|
+
end
|
828
|
+
|
819
829
|
# This hook is called when a client tries to access one or more
|
820
830
|
# fields that fail the `accessible?` check.
|
821
831
|
#
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module GraphQL
|
2
|
+
module Tracing
|
3
|
+
class InstrumentationTracing
|
4
|
+
def initialize(schema)
|
5
|
+
@schema = schema
|
6
|
+
end
|
7
|
+
|
8
|
+
def trace(event, data)
|
9
|
+
instrumenters = nil
|
10
|
+
query_instrumenters = nil
|
11
|
+
case event
|
12
|
+
when "execute_multiplex"
|
13
|
+
instrumenters = @schema.instrumenters
|
14
|
+
multiplex_instrumenters = instrumenters[:multiplex]
|
15
|
+
query_instrumenters = instrumenters[:query]
|
16
|
+
call_hooks(multiplex_instrumenters, data[:multiplex], :before_multiplex, :after_multiplex) {
|
17
|
+
each_query_call_hooks(query_instrumenters, data[:multiplex].queries) {
|
18
|
+
yield
|
19
|
+
}
|
20
|
+
}
|
21
|
+
else
|
22
|
+
yield
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Call the before_ hooks of each query,
|
29
|
+
# Then yield if no errors.
|
30
|
+
# `call_hooks` takes care of appropriate cleanup.
|
31
|
+
def each_query_call_hooks(instrumenters, queries, i = 0)
|
32
|
+
if i >= queries.length
|
33
|
+
yield
|
34
|
+
else
|
35
|
+
query = queries[i]
|
36
|
+
call_hooks(instrumenters, query, :before_query, :after_query) {
|
37
|
+
each_query_call_hooks(instrumenters, queries, i + 1) {
|
38
|
+
yield
|
39
|
+
}
|
40
|
+
}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Call each before hook, and if they all succeed, yield.
|
45
|
+
# If they don't all succeed, call after_ for each one that succeeded.
|
46
|
+
def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
|
47
|
+
begin
|
48
|
+
successful = []
|
49
|
+
instrumenters.each do |instrumenter|
|
50
|
+
instrumenter.public_send(before_hook_name, object)
|
51
|
+
successful << instrumenter
|
52
|
+
end
|
53
|
+
|
54
|
+
# if any before hooks raise an exception, quit calling before hooks,
|
55
|
+
# but call the after hooks on anything that succeeded but also
|
56
|
+
# raise the exception that came from the before hook.
|
57
|
+
rescue GraphQL::ExecutionError => err
|
58
|
+
object.context.errors << err
|
59
|
+
rescue => e
|
60
|
+
raise call_after_hooks(successful, object, after_hook_name, e)
|
61
|
+
end
|
62
|
+
|
63
|
+
begin
|
64
|
+
yield # Call the user code
|
65
|
+
ensure
|
66
|
+
ex = call_after_hooks(successful, object, after_hook_name, nil)
|
67
|
+
raise ex if ex
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def call_after_hooks(instrumenters, object, after_hook_name, ex)
|
72
|
+
instrumenters.reverse_each do |instrumenter|
|
73
|
+
begin
|
74
|
+
instrumenter.public_send(after_hook_name, object)
|
75
|
+
rescue => e
|
76
|
+
ex = e
|
77
|
+
end
|
78
|
+
end
|
79
|
+
ex
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.15
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -538,6 +538,7 @@ files:
|
|
538
538
|
- lib/graphql/tracing/appoptics_tracing.rb
|
539
539
|
- lib/graphql/tracing/appsignal_tracing.rb
|
540
540
|
- lib/graphql/tracing/data_dog_tracing.rb
|
541
|
+
- lib/graphql/tracing/instrumentation_tracing.rb
|
541
542
|
- lib/graphql/tracing/new_relic_tracing.rb
|
542
543
|
- lib/graphql/tracing/notifications_tracing.rb
|
543
544
|
- lib/graphql/tracing/platform_tracing.rb
|