graphql 2.2.5 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/ast/field_usage.rb +32 -7
- data/lib/graphql/analysis/ast/visitor.rb +8 -0
- data/lib/graphql/analysis/ast.rb +10 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/execution/interpreter/runtime.rb +9 -0
- data/lib/graphql/execution/interpreter.rb +90 -150
- data/lib/graphql/introspection/entry_points.rb +9 -3
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/document_from_schema_definition.rb +2 -3
- data/lib/graphql/language/lexer.rb +29 -28
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +12 -8
- data/lib/graphql/language/printer.rb +4 -0
- data/lib/graphql/language.rb +37 -0
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/query/context.rb +30 -33
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +2 -2
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +3 -1
- data/lib/graphql/schema/field.rb +33 -30
- data/lib/graphql/schema/interface.rb +5 -1
- data/lib/graphql/schema/loader.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/resolver.rb +9 -5
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema.rb +108 -28
- data/lib/graphql/static_validation/literal_validator.rb +1 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +1 -1
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +0 -3
- data/lib/graphql/testing/helpers.rb +8 -4
- data/lib/graphql/tracing/data_dog_trace.rb +21 -34
- data/lib/graphql/tracing/data_dog_tracing.rb +7 -21
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +3 -2
- metadata +38 -23
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 881a55a1017c82563e75cf9898d44be453c1329c849f60b9538fdbd0f0d4b630
|
|
4
|
+
data.tar.gz: e99efcbffe7cab713e9d5fa7156c1f3bb56752b10ae35f9c5b23e705a27f90da
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c82107ac040dd40a8bfcf09f0abf23d5a4a40ef8ed68b3e6ff0918e3b4ff02c6ae7f9637fed578bbd78032bde8d61622b65ab8efeb3bd4e2538fc809e626a92a
|
|
7
|
+
data.tar.gz: e0eddc7d0562f9637ecb1707d690b7d4579373c6d8f030e09565a150a60c4272ebb4e5f06cefc5165f6babfde01e5fb8930b4f678d6c1e146df56999634cbc1f
|
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
|
8
8
|
@used_fields = Set.new
|
|
9
9
|
@used_deprecated_fields = Set.new
|
|
10
10
|
@used_deprecated_arguments = Set.new
|
|
11
|
+
@used_deprecated_enum_values = Set.new
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def on_leave_field(node, parent, visitor)
|
|
@@ -15,7 +16,7 @@ module GraphQL
|
|
|
15
16
|
field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}"
|
|
16
17
|
@used_fields << field
|
|
17
18
|
@used_deprecated_fields << field if field_defn.deprecation_reason
|
|
18
|
-
arguments = visitor.query.arguments_for(node,
|
|
19
|
+
arguments = visitor.query.arguments_for(node, field_defn)
|
|
19
20
|
# If there was an error when preparing this argument object,
|
|
20
21
|
# then this might be an error or something:
|
|
21
22
|
if arguments.respond_to?(:argument_values)
|
|
@@ -28,6 +29,7 @@ module GraphQL
|
|
|
28
29
|
used_fields: @used_fields.to_a,
|
|
29
30
|
used_deprecated_fields: @used_deprecated_fields.to_a,
|
|
30
31
|
used_deprecated_arguments: @used_deprecated_arguments.to_a,
|
|
32
|
+
used_deprecated_enum_values: @used_deprecated_enum_values.to_a,
|
|
31
33
|
}
|
|
32
34
|
end
|
|
33
35
|
|
|
@@ -41,16 +43,39 @@ module GraphQL
|
|
|
41
43
|
|
|
42
44
|
next if argument.value.nil?
|
|
43
45
|
|
|
44
|
-
|
|
46
|
+
argument_type = argument.definition.type
|
|
47
|
+
if argument_type.non_null?
|
|
48
|
+
argument_type = argument_type.of_type
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if argument_type.kind.input_object?
|
|
45
52
|
extract_deprecated_arguments(argument.value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
|
|
46
|
-
elsif
|
|
47
|
-
argument
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
elsif argument_type.kind.enum?
|
|
54
|
+
extract_deprecated_enum_value(argument_type, argument.value)
|
|
55
|
+
elsif argument_type.list?
|
|
56
|
+
inner_type = argument_type.unwrap
|
|
57
|
+
case inner_type.kind
|
|
58
|
+
when TypeKinds::INPUT_OBJECT
|
|
59
|
+
argument.value.each do |value|
|
|
60
|
+
extract_deprecated_arguments(value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
|
|
61
|
+
end
|
|
62
|
+
when TypeKinds::ENUM
|
|
63
|
+
argument.value.each do |value|
|
|
64
|
+
extract_deprecated_enum_value(inner_type, value)
|
|
65
|
+
end
|
|
66
|
+
else
|
|
67
|
+
# Not a kind of input that we track
|
|
68
|
+
end
|
|
51
69
|
end
|
|
52
70
|
end
|
|
53
71
|
end
|
|
72
|
+
|
|
73
|
+
def extract_deprecated_enum_value(enum_type, value)
|
|
74
|
+
enum_value = @query.warden.enum_values(enum_type).find { |ev| ev.value == value }
|
|
75
|
+
if enum_value&.deprecation_reason
|
|
76
|
+
@used_deprecated_enum_values << enum_value.path
|
|
77
|
+
end
|
|
78
|
+
end
|
|
54
79
|
end
|
|
55
80
|
end
|
|
56
81
|
end
|
|
@@ -118,8 +118,12 @@ module GraphQL
|
|
|
118
118
|
def on_inline_fragment(node, parent)
|
|
119
119
|
on_fragment_with_type(node) do
|
|
120
120
|
@path.push("...#{node.type ? " on #{node.type.name}" : ""}")
|
|
121
|
+
@skipping = @skip_stack.last || skip?(node)
|
|
122
|
+
@skip_stack << @skipping
|
|
123
|
+
|
|
121
124
|
call_on_enter_inline_fragment(node, parent)
|
|
122
125
|
super
|
|
126
|
+
@skipping = @skip_stack.pop
|
|
123
127
|
call_on_leave_inline_fragment(node, parent)
|
|
124
128
|
end
|
|
125
129
|
end
|
|
@@ -187,9 +191,13 @@ module GraphQL
|
|
|
187
191
|
|
|
188
192
|
def on_fragment_spread(node, parent)
|
|
189
193
|
@path.push("... #{node.name}")
|
|
194
|
+
@skipping = @skip_stack.last || skip?(node)
|
|
195
|
+
@skip_stack << @skipping
|
|
196
|
+
|
|
190
197
|
call_on_enter_fragment_spread(node, parent)
|
|
191
198
|
enter_fragment_spread_inline(node)
|
|
192
199
|
super
|
|
200
|
+
@skipping = @skip_stack.pop
|
|
193
201
|
leave_fragment_spread_inline(node)
|
|
194
202
|
call_on_leave_fragment_spread(node, parent)
|
|
195
203
|
@path.pop
|
data/lib/graphql/analysis/ast.rb
CHANGED
|
@@ -6,6 +6,7 @@ require "graphql/analysis/ast/query_complexity"
|
|
|
6
6
|
require "graphql/analysis/ast/max_query_complexity"
|
|
7
7
|
require "graphql/analysis/ast/query_depth"
|
|
8
8
|
require "graphql/analysis/ast/max_query_depth"
|
|
9
|
+
require "timeout"
|
|
9
10
|
|
|
10
11
|
module GraphQL
|
|
11
12
|
module Analysis
|
|
@@ -63,7 +64,10 @@ module GraphQL
|
|
|
63
64
|
analyzers: analyzers_to_run
|
|
64
65
|
)
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
# `nil` or `0` causes no timeout
|
|
68
|
+
Timeout::timeout(query.validate_timeout_remaining) do
|
|
69
|
+
visitor.visit
|
|
70
|
+
end
|
|
67
71
|
|
|
68
72
|
if visitor.rescued_errors.any?
|
|
69
73
|
return visitor.rescued_errors
|
|
@@ -75,6 +79,11 @@ module GraphQL
|
|
|
75
79
|
[]
|
|
76
80
|
end
|
|
77
81
|
end
|
|
82
|
+
rescue Timeout::Error
|
|
83
|
+
[GraphQL::AnalysisError.new("Timeout on validation of query")]
|
|
84
|
+
rescue GraphQL::UnauthorizedError
|
|
85
|
+
# This error was raised during analysis and will be returned the client before execution
|
|
86
|
+
[]
|
|
78
87
|
end
|
|
79
88
|
|
|
80
89
|
def analysis_errors(results)
|
|
@@ -16,12 +16,6 @@ module GraphQL
|
|
|
16
16
|
"[" +
|
|
17
17
|
obj.map { |v| inspect_truncated(v) }.join(", ") +
|
|
18
18
|
"]"
|
|
19
|
-
when Query::Context::SharedMethods
|
|
20
|
-
if obj.invalid_null?
|
|
21
|
-
"nil"
|
|
22
|
-
else
|
|
23
|
-
inspect_truncated(obj.value)
|
|
24
|
-
end
|
|
25
19
|
else
|
|
26
20
|
inspect_truncated(obj)
|
|
27
21
|
end
|
|
@@ -33,12 +27,6 @@ module GraphQL
|
|
|
33
27
|
"{...}"
|
|
34
28
|
when Array
|
|
35
29
|
"[...]"
|
|
36
|
-
when Query::Context::SharedMethods
|
|
37
|
-
if obj.invalid_null?
|
|
38
|
-
"nil"
|
|
39
|
-
else
|
|
40
|
-
inspect_truncated(obj.value)
|
|
41
|
-
end
|
|
42
30
|
when GraphQL::Execution::Lazy
|
|
43
31
|
"(unresolved)"
|
|
44
32
|
else
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module GraphQL
|
|
3
|
-
class CoercionError < GraphQL::
|
|
4
|
-
# @return [Hash] Optional custom data for error objects which will be added
|
|
5
|
-
# under the `extensions` key.
|
|
6
|
-
attr_accessor :extensions
|
|
7
|
-
|
|
8
|
-
def initialize(message, extensions: nil)
|
|
9
|
-
@extensions = extensions
|
|
10
|
-
super(message)
|
|
11
|
-
end
|
|
3
|
+
class CoercionError < GraphQL::ExecutionError
|
|
12
4
|
end
|
|
13
5
|
end
|
|
@@ -14,6 +14,11 @@ module GraphQL
|
|
|
14
14
|
def load
|
|
15
15
|
@source.load(@key)
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
def load_with_deprecation_warning
|
|
19
|
+
warn("Returning `.request(...)` from GraphQL::Dataloader is deprecated, use `.load(...)` instead. (See usage of #{@source} with #{@key.inspect}).")
|
|
20
|
+
load
|
|
21
|
+
end
|
|
17
22
|
end
|
|
18
23
|
end
|
|
19
24
|
end
|
|
@@ -352,6 +352,15 @@ module GraphQL
|
|
|
352
352
|
end
|
|
353
353
|
|
|
354
354
|
field_result = call_method_on_directives(:resolve, object, directives) do
|
|
355
|
+
if directives.any?
|
|
356
|
+
# This might be executed in a different context; reset this info
|
|
357
|
+
runtime_state = get_current_runtime_state
|
|
358
|
+
runtime_state.current_field = field_defn
|
|
359
|
+
runtime_state.current_object = object
|
|
360
|
+
runtime_state.current_arguments = resolved_arguments
|
|
361
|
+
runtime_state.current_result_name = result_name
|
|
362
|
+
runtime_state.current_result = selection_result
|
|
363
|
+
end
|
|
355
364
|
# Actually call the field resolver and capture the result
|
|
356
365
|
app_result = begin
|
|
357
366
|
@current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
|
@@ -37,173 +37,113 @@ module GraphQL
|
|
|
37
37
|
multiplex.current_trace.execute_multiplex(multiplex: multiplex) do
|
|
38
38
|
schema = multiplex.schema
|
|
39
39
|
queries = multiplex.queries
|
|
40
|
-
query_instrumenters = schema.instrumenters[:query]
|
|
41
|
-
multiplex_instrumenters = schema.instrumenters[:multiplex]
|
|
42
40
|
lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
|
41
|
+
multiplex_analyzers = schema.multiplex_analyzers
|
|
42
|
+
if multiplex.max_complexity
|
|
43
|
+
multiplex_analyzers += [GraphQL::Analysis::AST::MaxQueryComplexity]
|
|
44
|
+
end
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
|
|
47
|
+
begin
|
|
48
|
+
# Since this is basically the batching context,
|
|
49
|
+
# share it for a whole multiplex
|
|
50
|
+
multiplex.context[:interpreter_instance] ||= multiplex.schema.query_execution_strategy(deprecation_warning: false).new
|
|
51
|
+
# Do as much eager evaluation of the query as possible
|
|
52
|
+
results = []
|
|
53
|
+
queries.each_with_index do |query, idx|
|
|
54
|
+
if query.subscription? && !query.subscription_update?
|
|
55
|
+
query.context.namespace(:subscriptions)[:events] = []
|
|
52
56
|
end
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
begin
|
|
68
|
-
# Although queries in a multiplex _share_ an Interpreter instance,
|
|
69
|
-
# they also have another item of state, which is private to that query
|
|
70
|
-
# in particular, assign it here:
|
|
71
|
-
runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
|
|
72
|
-
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
|
73
|
-
|
|
74
|
-
query.current_trace.execute_query(query: query) do
|
|
75
|
-
runtime.run_eager
|
|
76
|
-
end
|
|
77
|
-
rescue GraphQL::ExecutionError => err
|
|
78
|
-
query.context.errors << err
|
|
79
|
-
NO_OPERATION
|
|
80
|
-
end
|
|
57
|
+
multiplex.dataloader.append_job {
|
|
58
|
+
operation = query.selected_operation
|
|
59
|
+
result = if operation.nil? || !query.valid? || query.context.errors.any?
|
|
60
|
+
NO_OPERATION
|
|
61
|
+
else
|
|
62
|
+
begin
|
|
63
|
+
# Although queries in a multiplex _share_ an Interpreter instance,
|
|
64
|
+
# they also have another item of state, which is private to that query
|
|
65
|
+
# in particular, assign it here:
|
|
66
|
+
runtime = Runtime.new(query: query, lazies_at_depth: lazies_at_depth)
|
|
67
|
+
query.context.namespace(:interpreter_runtime)[:runtime] = runtime
|
|
68
|
+
|
|
69
|
+
query.current_trace.execute_query(query: query) do
|
|
70
|
+
runtime.run_eager
|
|
81
71
|
end
|
|
82
|
-
|
|
83
|
-
|
|
72
|
+
rescue GraphQL::ExecutionError => err
|
|
73
|
+
query.context.errors << err
|
|
74
|
+
NO_OPERATION
|
|
75
|
+
end
|
|
84
76
|
end
|
|
77
|
+
results[idx] = result
|
|
78
|
+
}
|
|
79
|
+
end
|
|
85
80
|
|
|
86
|
-
|
|
81
|
+
multiplex.dataloader.run
|
|
87
82
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
# Then, find all errors and assign the result to the query object
|
|
105
|
-
results.each_with_index do |data_result, idx|
|
|
106
|
-
query = queries[idx]
|
|
107
|
-
# Assign the result so that it can be accessed in instrumentation
|
|
108
|
-
query.result_values = if data_result.equal?(NO_OPERATION)
|
|
109
|
-
if !query.valid? || query.context.errors.any?
|
|
110
|
-
# A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
|
|
111
|
-
{ "errors" => query.static_errors.map(&:to_h) }
|
|
112
|
-
else
|
|
113
|
-
data_result
|
|
114
|
-
end
|
|
115
|
-
else
|
|
116
|
-
result = {
|
|
117
|
-
"data" => query.context.namespace(:interpreter_runtime)[:runtime].final_result
|
|
118
|
-
}
|
|
83
|
+
# Then, work through lazy results in a breadth-first way
|
|
84
|
+
multiplex.dataloader.append_job {
|
|
85
|
+
query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
|
|
86
|
+
queries = multiplex ? multiplex.queries : [query]
|
|
87
|
+
final_values = queries.map do |query|
|
|
88
|
+
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
|
89
|
+
# it might not be present if the query has an error
|
|
90
|
+
runtime ? runtime.final_result : nil
|
|
91
|
+
end
|
|
92
|
+
final_values.compact!
|
|
93
|
+
multiplex.current_trace.execute_query_lazy(multiplex: multiplex, query: query) do
|
|
94
|
+
Interpreter::Resolve.resolve_each_depth(lazies_at_depth, multiplex.dataloader)
|
|
95
|
+
end
|
|
96
|
+
}
|
|
97
|
+
multiplex.dataloader.run
|
|
119
98
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
99
|
+
# Then, find all errors and assign the result to the query object
|
|
100
|
+
results.each_with_index do |data_result, idx|
|
|
101
|
+
query = queries[idx]
|
|
102
|
+
if (events = query.context.namespace(:subscriptions)[:events]) && events.any?
|
|
103
|
+
schema.subscriptions.write_subscription(query, events)
|
|
104
|
+
end
|
|
105
|
+
# Assign the result so that it can be accessed in instrumentation
|
|
106
|
+
query.result_values = if data_result.equal?(NO_OPERATION)
|
|
107
|
+
if !query.valid? || query.context.errors.any?
|
|
108
|
+
# A bit weird, but `Query#static_errors` _includes_ `query.context.errors`
|
|
109
|
+
{ "errors" => query.static_errors.map(&:to_h) }
|
|
110
|
+
else
|
|
111
|
+
data_result
|
|
112
|
+
end
|
|
113
|
+
else
|
|
114
|
+
result = {}
|
|
124
115
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
|
|
129
|
-
end
|
|
130
|
-
# Get the Query::Result, not the Hash
|
|
131
|
-
results[idx] = query.result
|
|
116
|
+
if query.context.errors.any?
|
|
117
|
+
error_result = query.context.errors.map(&:to_h)
|
|
118
|
+
result["errors"] = error_result
|
|
132
119
|
end
|
|
133
120
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
ensure
|
|
141
|
-
queries.map { |query|
|
|
142
|
-
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
|
143
|
-
if runtime
|
|
144
|
-
runtime.delete_all_interpreter_context
|
|
145
|
-
end
|
|
146
|
-
}
|
|
121
|
+
result["data"] = query.context.namespace(:interpreter_runtime)[:runtime].final_result
|
|
122
|
+
|
|
123
|
+
result
|
|
124
|
+
end
|
|
125
|
+
if query.context.namespace?(:__query_result_extensions__)
|
|
126
|
+
query.result_values["extensions"] = query.context.namespace(:__query_result_extensions__)
|
|
147
127
|
end
|
|
128
|
+
# Get the Query::Result, not the Hash
|
|
129
|
+
results[idx] = query.result
|
|
148
130
|
end
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
131
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
yield
|
|
132
|
+
results
|
|
133
|
+
rescue Exception
|
|
134
|
+
# TODO rescue at a higher level so it will catch errors in analysis, too
|
|
135
|
+
# Assign values here so that the query's `@executed` becomes true
|
|
136
|
+
queries.map { |q| q.result_values ||= {} }
|
|
137
|
+
raise
|
|
138
|
+
ensure
|
|
139
|
+
queries.map { |query|
|
|
140
|
+
runtime = query.context.namespace(:interpreter_runtime)[:runtime]
|
|
141
|
+
if runtime
|
|
142
|
+
runtime.delete_all_interpreter_context
|
|
143
|
+
end
|
|
166
144
|
}
|
|
167
|
-
}
|
|
168
|
-
end
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
# Call each before hook, and if they all succeed, yield.
|
|
172
|
-
# If they don't all succeed, call after_ for each one that succeeded.
|
|
173
|
-
def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
|
|
174
|
-
begin
|
|
175
|
-
successful = []
|
|
176
|
-
instrumenters.each do |instrumenter|
|
|
177
|
-
instrumenter.public_send(before_hook_name, object)
|
|
178
|
-
successful << instrumenter
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
# if any before hooks raise an exception, quit calling before hooks,
|
|
182
|
-
# but call the after hooks on anything that succeeded but also
|
|
183
|
-
# raise the exception that came from the before hook.
|
|
184
|
-
rescue GraphQL::ExecutionError => err
|
|
185
|
-
object.context.errors << err
|
|
186
|
-
rescue => e
|
|
187
|
-
raise call_after_hooks(successful, object, after_hook_name, e)
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
begin
|
|
191
|
-
yield # Call the user code
|
|
192
|
-
ensure
|
|
193
|
-
ex = call_after_hooks(successful, object, after_hook_name, nil)
|
|
194
|
-
raise ex if ex
|
|
195
|
-
end
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
def call_after_hooks(instrumenters, object, after_hook_name, ex)
|
|
199
|
-
instrumenters.reverse_each do |instrumenter|
|
|
200
|
-
begin
|
|
201
|
-
instrumenter.public_send(after_hook_name, object)
|
|
202
|
-
rescue => e
|
|
203
|
-
ex = e
|
|
204
145
|
end
|
|
205
146
|
end
|
|
206
|
-
ex
|
|
207
147
|
end
|
|
208
148
|
end
|
|
209
149
|
|
|
@@ -9,13 +9,19 @@ module GraphQL
|
|
|
9
9
|
|
|
10
10
|
def __schema
|
|
11
11
|
# Apply wrapping manually since this field isn't wrapped by instrumentation
|
|
12
|
-
schema =
|
|
12
|
+
schema = context.schema
|
|
13
13
|
schema_type = schema.introspection_system.types["__Schema"]
|
|
14
|
-
schema_type.wrap(schema,
|
|
14
|
+
schema_type.wrap(schema, context)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def __type(name:)
|
|
18
|
-
context.warden.reachable_type?(name)
|
|
18
|
+
if context.warden.reachable_type?(name)
|
|
19
|
+
context.warden.get_type(name)
|
|
20
|
+
elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
|
|
21
|
+
type
|
|
22
|
+
else
|
|
23
|
+
nil
|
|
24
|
+
end
|
|
19
25
|
end
|
|
20
26
|
end
|
|
21
27
|
end
|
|
@@ -24,7 +24,7 @@ module GraphQL
|
|
|
24
24
|
@include_built_in_directives = include_built_in_directives
|
|
25
25
|
@include_one_of = false
|
|
26
26
|
|
|
27
|
-
schema_context = schema.context_class.new(query: nil,
|
|
27
|
+
schema_context = schema.context_class.new(query: nil, schema: schema, values: context)
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
@warden = @schema.warden_class.new(
|
|
@@ -266,8 +266,7 @@ module GraphQL
|
|
|
266
266
|
end
|
|
267
267
|
definitions = build_directive_nodes(dirs_to_build)
|
|
268
268
|
|
|
269
|
-
type_nodes = build_type_definition_nodes(warden.reachable_types)
|
|
270
|
-
|
|
269
|
+
type_nodes = build_type_definition_nodes(warden.reachable_types + schema.extra_types)
|
|
271
270
|
if @include_one_of
|
|
272
271
|
# This may have been set to true when iterating over all types
|
|
273
272
|
definitions.concat(build_directive_nodes([GraphQL::Schema::Directive::OneOf]))
|
|
@@ -89,6 +89,8 @@ module GraphQL
|
|
|
89
89
|
"..."
|
|
90
90
|
elsif token_name == :STRING
|
|
91
91
|
string_value
|
|
92
|
+
elsif @scanner.matched_size.nil?
|
|
93
|
+
@scanner.peek(1)
|
|
92
94
|
else
|
|
93
95
|
token_value
|
|
94
96
|
end
|
|
@@ -107,29 +109,27 @@ module GraphQL
|
|
|
107
109
|
}
|
|
108
110
|
UTF_8 = /\\u(?:([\dAa-f]{4})|\{([\da-f]{4,})\})(?:\\u([\dAa-f]{4}))?/i
|
|
109
111
|
VALID_STRING = /\A(?:[^\\]|#{ESCAPES}|#{UTF_8})*\z/o
|
|
112
|
+
ESCAPED = /(?:#{ESCAPES}|#{UTF_8})/o
|
|
110
113
|
|
|
111
114
|
def string_value
|
|
112
115
|
str = token_value
|
|
113
116
|
is_block = str.start_with?('"""')
|
|
114
117
|
if is_block
|
|
115
118
|
str.gsub!(/\A"""|"""\z/, '')
|
|
119
|
+
return Language::BlockString.trim_whitespace(str)
|
|
116
120
|
else
|
|
117
121
|
str.gsub!(/\A"|"\z/, '')
|
|
118
|
-
end
|
|
119
|
-
|
|
120
|
-
if is_block
|
|
121
|
-
str = Language::BlockString.trim_whitespace(str)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
if !str.valid_encoding? || !str.match?(VALID_STRING)
|
|
125
|
-
raise_parse_error("Bad unicode escape in #{str.inspect}")
|
|
126
|
-
else
|
|
127
|
-
Lexer.replace_escaped_characters_in_place(str)
|
|
128
122
|
|
|
129
|
-
if !str.valid_encoding?
|
|
123
|
+
if !str.valid_encoding? || !str.match?(VALID_STRING)
|
|
130
124
|
raise_parse_error("Bad unicode escape in #{str.inspect}")
|
|
131
125
|
else
|
|
132
|
-
str
|
|
126
|
+
Lexer.replace_escaped_characters_in_place(str)
|
|
127
|
+
|
|
128
|
+
if !str.valid_encoding?
|
|
129
|
+
raise_parse_error("Bad unicode escape in #{str.inspect}")
|
|
130
|
+
else
|
|
131
|
+
str
|
|
132
|
+
end
|
|
133
133
|
end
|
|
134
134
|
end
|
|
135
135
|
end
|
|
@@ -254,7 +254,7 @@ module GraphQL
|
|
|
254
254
|
STRING_ESCAPE = %r{[\\][\\/bfnrt]}
|
|
255
255
|
BLOCK_QUOTE = '"""'
|
|
256
256
|
ESCAPED_QUOTE = /\\"/;
|
|
257
|
-
STRING_CHAR = /#{ESCAPED_QUOTE}|[^"
|
|
257
|
+
STRING_CHAR = /#{ESCAPED_QUOTE}|[^"\\\n\r]|#{UNICODE_ESCAPE}|#{STRING_ESCAPE}/
|
|
258
258
|
QUOTED_STRING_REGEXP = %r{#{QUOTE} (?:#{STRING_CHAR})* #{QUOTE}}x
|
|
259
259
|
BLOCK_STRING_REGEXP = %r{
|
|
260
260
|
#{BLOCK_QUOTE}
|
|
@@ -299,24 +299,25 @@ module GraphQL
|
|
|
299
299
|
# Replace any escaped unicode or whitespace with the _actual_ characters
|
|
300
300
|
# To avoid allocating more strings, this modifies the string passed into it
|
|
301
301
|
def self.replace_escaped_characters_in_place(raw_string)
|
|
302
|
-
raw_string.gsub!(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
(
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
302
|
+
raw_string.gsub!(ESCAPED) do |matched_str|
|
|
303
|
+
if (point_str_1 = $1 || $2)
|
|
304
|
+
codepoint_1 = point_str_1.to_i(16)
|
|
305
|
+
if (codepoint_2 = $3)
|
|
306
|
+
codepoint_2 = codepoint_2.to_i(16)
|
|
307
|
+
if (codepoint_1 >= 0xD800 && codepoint_1 <= 0xDBFF) && # leading surrogate
|
|
308
|
+
(codepoint_2 >= 0xDC00 && codepoint_2 <= 0xDFFF) # trailing surrogate
|
|
309
|
+
# A surrogate pair
|
|
310
|
+
combined = ((codepoint_1 - 0xD800) * 0x400) + (codepoint_2 - 0xDC00) + 0x10000
|
|
311
|
+
[combined].pack('U'.freeze)
|
|
312
|
+
else
|
|
313
|
+
# Two separate code points
|
|
314
|
+
[codepoint_1].pack('U'.freeze) + [codepoint_2].pack('U'.freeze)
|
|
315
|
+
end
|
|
314
316
|
else
|
|
315
|
-
|
|
316
|
-
[codepoint_1].pack('U'.freeze) + [codepoint_2].pack('U'.freeze)
|
|
317
|
+
[codepoint_1].pack('U'.freeze)
|
|
317
318
|
end
|
|
318
319
|
else
|
|
319
|
-
[
|
|
320
|
+
ESCAPES_REPLACE[matched_str]
|
|
320
321
|
end
|
|
321
322
|
end
|
|
322
323
|
nil
|
|
@@ -512,7 +512,7 @@ module GraphQL
|
|
|
512
512
|
# An operation-level query variable
|
|
513
513
|
class VariableDefinition < AbstractNode
|
|
514
514
|
scalar_methods :name, :type, :default_value
|
|
515
|
-
children_methods
|
|
515
|
+
children_methods(directives: Directive)
|
|
516
516
|
# @!attribute default_value
|
|
517
517
|
# @return [String, Integer, Float, Boolean, Array, NullValue] A Ruby value to use if no other value is provided
|
|
518
518
|
|