graphql 0.16.1 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/analysis/query_complexity.rb +20 -13
- data/lib/graphql/analysis/query_depth.rb +2 -2
- data/lib/graphql/argument.rb +7 -2
- data/lib/graphql/base_type.rb +2 -1
- data/lib/graphql/boolean_type.rb +1 -0
- data/lib/graphql/define/assign_object_field.rb +19 -6
- data/lib/graphql/define/instance_definable.rb +44 -3
- data/lib/graphql/directive.rb +2 -6
- data/lib/graphql/enum_type.rb +11 -14
- data/lib/graphql/field.rb +47 -12
- data/lib/graphql/field/resolve.rb +57 -0
- data/lib/graphql/float_type.rb +2 -0
- data/lib/graphql/id_type.rb +2 -0
- data/lib/graphql/input_object_type.rb +5 -1
- data/lib/graphql/int_type.rb +2 -0
- data/lib/graphql/interface_type.rb +1 -1
- data/lib/graphql/internal_representation/node.rb +18 -29
- data/lib/graphql/internal_representation/rewrite.rb +7 -4
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/input_value_type.rb +1 -1
- data/lib/graphql/language/parser.rb +161 -136
- data/lib/graphql/language/parser.y +9 -1
- data/lib/graphql/object_type.rb +2 -2
- data/lib/graphql/query.rb +4 -4
- data/lib/graphql/query/directive_resolution.rb +2 -2
- data/lib/graphql/query/serial_execution/execution_context.rb +3 -2
- data/lib/graphql/query/serial_execution/field_resolution.rb +2 -5
- data/lib/graphql/query/serial_execution/selection_resolution.rb +1 -1
- data/lib/graphql/query/serial_execution/value_resolution.rb +2 -2
- data/lib/graphql/scalar_type.rb +2 -0
- data/lib/graphql/schema/timeout_middleware.rb +1 -0
- data/lib/graphql/string_type.rb +2 -0
- data/lib/graphql/union_type.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -3
- data/spec/graphql/analysis/query_complexity_spec.rb +41 -5
- data/spec/graphql/define/instance_definable_spec.rb +2 -2
- data/spec/graphql/field_spec.rb +18 -0
- data/spec/graphql/internal_representation/rewrite_spec.rb +7 -6
- data/spec/graphql/language/parser_spec.rb +5 -0
- data/spec/graphql/query/serial_execution/execution_context_spec.rb +2 -1
- data/spec/graphql/schema/timeout_middleware_spec.rb +29 -28
- data/spec/support/dairy_app.rb +12 -10
- metadata +3 -3
- data/lib/graphql/internal_representation/definition.rb +0 -0
@@ -169,7 +169,15 @@ rule
|
|
169
169
|
object_value_field:
|
170
170
|
name COLON input_value { return make_node(:Argument, name: val[0], value: val[2], position_source: val[0])}
|
171
171
|
|
172
|
-
enum_value:
|
172
|
+
enum_value: enum_name { return make_node(:Enum, name: val[0], position_source: val[0])}
|
173
|
+
|
174
|
+
enum_name: /* any identifier, but not "true", "false" or "null" */
|
175
|
+
IDENTIFIER
|
176
|
+
| FRAGMENT
|
177
|
+
| ON
|
178
|
+
| QUERY
|
179
|
+
| MUTATION
|
180
|
+
| SUBSCRIPTION
|
173
181
|
|
174
182
|
directives_list_opt:
|
175
183
|
/* none */ { return [] }
|
data/lib/graphql/object_type.rb
CHANGED
@@ -22,10 +22,9 @@ module GraphQL
|
|
22
22
|
#
|
23
23
|
class ObjectType < GraphQL::BaseType
|
24
24
|
accepts_definitions :interfaces, field: GraphQL::Define::AssignObjectField
|
25
|
-
attr_accessor :name, :description
|
26
25
|
|
27
26
|
# @return [Hash<String => GraphQL::Field>] Map String fieldnames to their {GraphQL::Field} implementations
|
28
|
-
|
27
|
+
lazy_defined_attr_accessor :fields
|
29
28
|
|
30
29
|
def initialize
|
31
30
|
@fields = {}
|
@@ -40,6 +39,7 @@ module GraphQL
|
|
40
39
|
|
41
40
|
def interfaces
|
42
41
|
@clean_interfaces ||= begin
|
42
|
+
ensure_defined
|
43
43
|
@dirty_interfaces.map { |i_type| GraphQL::BaseType.resolve_related_type(i_type) }
|
44
44
|
rescue
|
45
45
|
@dirty_interfaces
|
data/lib/graphql/query.rb
CHANGED
@@ -51,7 +51,7 @@ module GraphQL
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
@arguments_cache = {}
|
54
|
+
@arguments_cache = Hash.new { |h, k| h[k] = {} }
|
55
55
|
end
|
56
56
|
|
57
57
|
# Get the result for this query, executing it once
|
@@ -103,11 +103,11 @@ module GraphQL
|
|
103
103
|
|
104
104
|
# Node-level cache for calculating arguments. Used during execution and query analysis.
|
105
105
|
# @return [GraphQL::Query::Arguments] Arguments for this node, merging default values, literal values and query variables
|
106
|
-
def arguments_for(irep_node)
|
107
|
-
@arguments_cache[irep_node] ||= begin
|
106
|
+
def arguments_for(irep_node, definition)
|
107
|
+
@arguments_cache[irep_node][definition] ||= begin
|
108
108
|
GraphQL::Query::LiteralInput.from_arguments(
|
109
109
|
irep_node.ast_node.arguments,
|
110
|
-
|
110
|
+
definition.arguments,
|
111
111
|
self.variables
|
112
112
|
)
|
113
113
|
end
|
@@ -3,8 +3,8 @@ module GraphQL
|
|
3
3
|
module DirectiveResolution
|
4
4
|
def self.include_node?(irep_node, query)
|
5
5
|
irep_node.directives.each do |directive_node|
|
6
|
-
directive_defn = directive_node.
|
7
|
-
args = query.arguments_for(directive_node)
|
6
|
+
directive_defn = directive_node.definitions.first
|
7
|
+
args = query.arguments_for(directive_node, directive_defn)
|
8
8
|
if !directive_defn.include?(args)
|
9
9
|
return false
|
10
10
|
end
|
@@ -18,8 +18,9 @@ module GraphQL
|
|
18
18
|
@query.fragments[name]
|
19
19
|
end
|
20
20
|
|
21
|
-
def get_field(type,
|
22
|
-
|
21
|
+
def get_field(type, irep_node)
|
22
|
+
# fall back for dynamic fields (eg __typename)
|
23
|
+
irep_node.definitions[type] || @schema.get_field(type, irep_node.definition_name) || raise("No field found on #{type.name} for '#{irep_node.definition_name}' (#{irep_node.ast_node.name})")
|
23
24
|
end
|
24
25
|
|
25
26
|
def add_error(err)
|
@@ -9,11 +9,8 @@ module GraphQL
|
|
9
9
|
@parent_type = parent_type
|
10
10
|
@target = target
|
11
11
|
@execution_context = execution_context
|
12
|
-
@field = execution_context.get_field(parent_type, irep_node
|
13
|
-
|
14
|
-
raise("No field found on #{parent_type.name} '#{parent_type}' for '#{ast_node.name}'")
|
15
|
-
end
|
16
|
-
@arguments = execution_context.query.arguments_for(irep_node)
|
12
|
+
@field = execution_context.get_field(parent_type, irep_node)
|
13
|
+
@arguments = execution_context.query.arguments_for(irep_node, @field)
|
17
14
|
end
|
18
15
|
|
19
16
|
def result
|
@@ -32,7 +32,7 @@ module GraphQL
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def applies_to_type?(irep_node, type, target)
|
35
|
-
irep_node.
|
35
|
+
irep_node.definitions.any? { |child_type, field_defn|
|
36
36
|
GraphQL::Query::TypeResolver.new(target, child_type, type, execution_context.query.context).type
|
37
37
|
}
|
38
38
|
end
|
@@ -57,7 +57,7 @@ module GraphQL
|
|
57
57
|
resolved_type = field_type.resolve_type(value, execution_context)
|
58
58
|
|
59
59
|
unless resolved_type.is_a?(GraphQL::ObjectType)
|
60
|
-
raise GraphQL::ObjectType::UnresolvedTypeError.new(irep_node.
|
60
|
+
raise GraphQL::ObjectType::UnresolvedTypeError.new(irep_node.definition_name, field_type, parent_type)
|
61
61
|
end
|
62
62
|
|
63
63
|
strategy_class = get_strategy_for_kind(resolved_type.kind)
|
@@ -82,7 +82,7 @@ module GraphQL
|
|
82
82
|
# Get the "wrapped" type and resolve the value according to that type
|
83
83
|
def result
|
84
84
|
if value.nil? || value.is_a?(GraphQL::ExecutionError)
|
85
|
-
raise GraphQL::InvalidNullError.new(irep_node.
|
85
|
+
raise GraphQL::InvalidNullError.new(irep_node.definition_name, value)
|
86
86
|
else
|
87
87
|
wrapped_type = field_type.of_type
|
88
88
|
strategy_class = get_strategy_for_kind(wrapped_type.kind)
|
data/lib/graphql/scalar_type.rb
CHANGED
@@ -27,6 +27,7 @@ module GraphQL
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def coerce_non_null_input(value)
|
30
|
+
ensure_defined
|
30
31
|
@coerce_input_proc.call(value)
|
31
32
|
end
|
32
33
|
|
@@ -37,6 +38,7 @@ module GraphQL
|
|
37
38
|
end
|
38
39
|
|
39
40
|
def coerce_result(value)
|
41
|
+
ensure_defined
|
40
42
|
@coerce_result_proc.call(value)
|
41
43
|
end
|
42
44
|
|
@@ -33,6 +33,7 @@ module GraphQL
|
|
33
33
|
|
34
34
|
def call(parent_type, parent_object, field_definition, field_args, query_context, next_middleware)
|
35
35
|
timeout_at = query_context[@context_key] ||= Time.now + @max_seconds
|
36
|
+
|
36
37
|
if timeout_at < Time.now
|
37
38
|
on_timeout(parent_type, parent_object, field_definition, field_args, query_context)
|
38
39
|
else
|
data/lib/graphql/string_type.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
GraphQL::STRING_TYPE = GraphQL::ScalarType.define do
|
2
2
|
name "String"
|
3
|
+
description "Represents textual data as UTF-8 character sequences. This type is most often used by GraphQL to represent free-form human-readable text."
|
4
|
+
|
3
5
|
coerce_result -> (value) { value.to_s }
|
4
6
|
coerce_input -> (value) { value.is_a?(String) ? value : nil }
|
5
7
|
end
|
data/lib/graphql/union_type.rb
CHANGED
@@ -11,7 +11,6 @@ module GraphQL
|
|
11
11
|
#
|
12
12
|
class UnionType < GraphQL::BaseType
|
13
13
|
include GraphQL::BaseType::HasPossibleTypes
|
14
|
-
attr_accessor :name, :description
|
15
14
|
accepts_definitions :possible_types, :resolve_type
|
16
15
|
|
17
16
|
def kind
|
@@ -29,6 +28,7 @@ module GraphQL
|
|
29
28
|
|
30
29
|
def possible_types
|
31
30
|
@clean_possible_types ||= begin
|
31
|
+
ensure_defined
|
32
32
|
@dirty_possible_types.map { |type| GraphQL::BaseType.resolve_related_type(type) }
|
33
33
|
rescue
|
34
34
|
@dirty_possible_types
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -127,9 +127,7 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
|
|
127
127
|
- Problem: how does a field know which schema to look up the name from?
|
128
128
|
- Problem: how can we load types in Rails without accessing the constant?
|
129
129
|
- Maybe support by third-party library? `type("Post!")` could implement "type_missing", keeps `graphql-ruby` very simple
|
130
|
-
- If we eval'd `define { ... }` blocks _lazily_, would that work around all circular dependency issues?
|
131
|
-
- `QueryComplexity` improvements:
|
132
|
-
- Better detection of union / interface possibilities? Right now they're summed
|
133
130
|
- Type check improvements:
|
134
131
|
- Use catch-all type/field/argument definitions instead of terminating traversal
|
135
132
|
- Reduce ad-hoc traversals?
|
133
|
+
- Merge `graphql-relay-ruby` into this Repo so that they can stay in sync?
|
@@ -191,28 +191,47 @@ describe GraphQL::Analysis::QueryComplexity do
|
|
191
191
|
describe "custom complexities" do
|
192
192
|
let(:query) { GraphQL::Query.new(complexity_schema, query_string) }
|
193
193
|
let(:complexity_schema) {
|
194
|
-
|
195
|
-
name "
|
194
|
+
complexity_interface = GraphQL::InterfaceType.define do
|
195
|
+
name "ComplexityInterface"
|
196
|
+
field :value, types.Int
|
197
|
+
end
|
198
|
+
|
199
|
+
single_complexity_type = GraphQL::ObjectType.define do
|
200
|
+
name "SingleComplexity"
|
196
201
|
field :value, types.Int, complexity: 0.1 do
|
197
202
|
resolve -> (obj, args, ctx) { obj }
|
198
203
|
end
|
199
|
-
field :complexity,
|
204
|
+
field :complexity, single_complexity_type do
|
200
205
|
argument :value, types.Int
|
201
206
|
complexity -> (ctx, args, child_complexity) { args[:value] + child_complexity }
|
202
207
|
resolve -> (obj, args, ctx) { args[:value] }
|
203
208
|
end
|
209
|
+
interfaces [complexity_interface]
|
210
|
+
end
|
211
|
+
|
212
|
+
double_complexity_type = GraphQL::ObjectType.define do
|
213
|
+
name "DoubleComplexity"
|
214
|
+
field :value, types.Int, complexity: 4 do
|
215
|
+
resolve -> (obj, args, ctx) { obj }
|
216
|
+
end
|
217
|
+
interfaces [complexity_interface]
|
204
218
|
end
|
205
219
|
|
206
220
|
query_type = GraphQL::ObjectType.define do
|
207
221
|
name "Query"
|
208
|
-
field :complexity,
|
222
|
+
field :complexity, single_complexity_type do
|
209
223
|
argument :value, types.Int
|
210
224
|
complexity -> (ctx, args, child_complexity) { args[:value] + child_complexity }
|
211
225
|
resolve -> (obj, args, ctx) { args[:value] }
|
212
226
|
end
|
227
|
+
|
228
|
+
field :innerComplexity, complexity_interface do
|
229
|
+
argument :value, types.Int
|
230
|
+
resolve -> (obj, args, ctx) { args[:value] }
|
231
|
+
end
|
213
232
|
end
|
214
233
|
|
215
|
-
GraphQL::Schema.new(query: query_type)
|
234
|
+
GraphQL::Schema.new(query: query_type, types: [double_complexity_type])
|
216
235
|
}
|
217
236
|
let(:query_string) {%|
|
218
237
|
{
|
@@ -231,5 +250,22 @@ describe GraphQL::Analysis::QueryComplexity do
|
|
231
250
|
# 10 from `complexity`, `0.3` from `value`
|
232
251
|
assert_equal complexities, [query, 10.3]
|
233
252
|
end
|
253
|
+
|
254
|
+
describe "same field on multiple types" do
|
255
|
+
let(:query_string) {%|
|
256
|
+
{
|
257
|
+
innerComplexity(value: 2) {
|
258
|
+
... on SingleComplexity { value }
|
259
|
+
... on DoubleComplexity { value }
|
260
|
+
}
|
261
|
+
}
|
262
|
+
|}
|
263
|
+
|
264
|
+
it "picks them max for those fields" do
|
265
|
+
reduce_result
|
266
|
+
# 1 for innerComplexity + 4 for DoubleComplexity.value
|
267
|
+
assert_equal complexities, [query, 5]
|
268
|
+
end
|
269
|
+
end
|
234
270
|
end
|
235
271
|
end
|
@@ -10,12 +10,12 @@ module Garden
|
|
10
10
|
end
|
11
11
|
|
12
12
|
class Vegetable
|
13
|
-
attr_accessor :name, :start_planting_on, :end_planting_on
|
14
13
|
include GraphQL::Define::InstanceDefinable
|
14
|
+
lazy_defined_attr_accessor :name, :start_planting_on, :end_planting_on
|
15
15
|
accepts_definitions :name, plant_between: DefinePlantBetween
|
16
16
|
|
17
17
|
# definition added later:
|
18
|
-
|
18
|
+
lazy_defined_attr_accessor :height
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
data/spec/graphql/field_spec.rb
CHANGED
@@ -89,4 +89,22 @@ describe GraphQL::Field do
|
|
89
89
|
assert_equal "QueryType is invalid: field :symbol_name name must return String, not Symbol (:symbol_name)", err.message
|
90
90
|
end
|
91
91
|
end
|
92
|
+
|
93
|
+
describe "#hash_key" do
|
94
|
+
let(:source_field) { MilkType.get_field("source") }
|
95
|
+
after { source_field.hash_key = :source }
|
96
|
+
|
97
|
+
it "looks up a value with obj[hash_key]" do
|
98
|
+
resolved_source = source_field.resolve({source: "Abc", "source" => "Xyz"}, nil, nil)
|
99
|
+
assert_equal :source, source_field.hash_key
|
100
|
+
assert_equal "Abc", resolved_source
|
101
|
+
end
|
102
|
+
|
103
|
+
it "can be reassigned" do
|
104
|
+
source_field.hash_key = "source"
|
105
|
+
resolved_source = source_field.resolve({source: "Abc", "source" => "Xyz"}, nil, nil)
|
106
|
+
assert_equal "source", source_field.hash_key
|
107
|
+
assert_equal "Xyz", resolved_source
|
108
|
+
end
|
109
|
+
end
|
92
110
|
end
|
@@ -26,12 +26,12 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
26
26
|
assert_equal QueryType, op_node.return_type
|
27
27
|
first_field = op_node.children.values.first
|
28
28
|
assert_equal 3, first_field.children.length
|
29
|
-
assert_equal [QueryType], first_field.
|
29
|
+
assert_equal [QueryType], first_field.definitions.keys
|
30
30
|
assert_equal CheeseType, first_field.return_type
|
31
31
|
|
32
32
|
second_field = op_node.children.values.last
|
33
33
|
assert_equal 1, second_field.children.length
|
34
|
-
assert_equal [QueryType], second_field.
|
34
|
+
assert_equal [QueryType], second_field.definitions.keys
|
35
35
|
assert_equal CheeseType, second_field.return_type
|
36
36
|
end
|
37
37
|
end
|
@@ -48,7 +48,8 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
48
48
|
it "gets dynamic field definitions" do
|
49
49
|
cheese_field = rewrite_result[nil].children["cheese"]
|
50
50
|
typename_field = cheese_field.children["typename"]
|
51
|
-
assert_equal "__typename", typename_field.
|
51
|
+
assert_equal "__typename", typename_field.definitions.values.first.name
|
52
|
+
assert_equal "__typename", typename_field.definition_name
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
@@ -112,9 +113,9 @@ describe GraphQL::InternalRepresentation::Rewrite do
|
|
112
113
|
similar_sheep_field = similar_cow_field.children["similarCheese"]
|
113
114
|
assert_equal ["flavor", "source"], similar_sheep_field.children.keys
|
114
115
|
|
115
|
-
assert_equal
|
116
|
-
assert_equal
|
117
|
-
assert_equal
|
116
|
+
assert_equal [EdibleInterface], cheese_field.children["origin"].definitions.keys
|
117
|
+
assert_equal [CheeseType, EdibleInterface], cheese_field.children["fatContent"].definitions.keys
|
118
|
+
assert_equal [CheeseType], cheese_field.children["flavor"].definitions.keys
|
118
119
|
end
|
119
120
|
end
|
120
121
|
end
|
@@ -190,6 +190,7 @@ describe GraphQL::Language::Parser do
|
|
190
190
|
array: [7, 8, 9]
|
191
191
|
object: {a: [1,2,3], b: {c: "4"}}
|
192
192
|
unicode_bom: "\xef\xbb\xbfquery"
|
193
|
+
keywordEnum: on
|
193
194
|
)
|
194
195
|
}
|
195
196
|
|}
|
@@ -234,6 +235,10 @@ describe GraphQL::Language::Parser do
|
|
234
235
|
obj = inputs[7].value
|
235
236
|
assert_equal %|\xef\xbb\xbfquery|, inputs[7].value
|
236
237
|
end
|
238
|
+
|
239
|
+
it "parses enum 'on''" do
|
240
|
+
assert_equal "on", inputs[8].value.name
|
241
|
+
end
|
237
242
|
end
|
238
243
|
end
|
239
244
|
|
@@ -39,7 +39,8 @@ describe GraphQL::Query::SerialExecution::ExecutionContext do
|
|
39
39
|
|
40
40
|
describe "get_field" do
|
41
41
|
it "returns the respective field from the schema" do
|
42
|
-
|
42
|
+
irep_node = OpenStruct.new(definition_name: "cheese", definitions: {DairyType => DairyType.fields["cheese"]})
|
43
|
+
field = execution_context.get_field(DairyType, irep_node)
|
43
44
|
assert_equal("cheese", field.name)
|
44
45
|
end
|
45
46
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe GraphQL::Schema::TimeoutMiddleware do
|
4
|
-
let(:max_seconds) {
|
5
|
-
let(:timeout_middleware) { GraphQL::Schema::TimeoutMiddleware.new(max_seconds:
|
4
|
+
let(:max_seconds) { 1 }
|
5
|
+
let(:timeout_middleware) { GraphQL::Schema::TimeoutMiddleware.new(max_seconds: max_seconds) }
|
6
6
|
let(:timeout_schema) {
|
7
7
|
|
8
8
|
sleep_for_seconds_resolve = -> (obj, args, ctx) {
|
@@ -45,18 +45,18 @@ describe GraphQL::Schema::TimeoutMiddleware do
|
|
45
45
|
describe "timeout part-way through" do
|
46
46
|
let(:query_string) {%|
|
47
47
|
{
|
48
|
-
a: sleepFor(seconds: 0.
|
49
|
-
b: sleepFor(seconds: 0.
|
50
|
-
c: sleepFor(seconds: 0.
|
51
|
-
d: sleepFor(seconds: 0.
|
52
|
-
e: sleepFor(seconds: 0.
|
48
|
+
a: sleepFor(seconds: 0.4)
|
49
|
+
b: sleepFor(seconds: 0.4)
|
50
|
+
c: sleepFor(seconds: 0.4)
|
51
|
+
d: sleepFor(seconds: 0.4)
|
52
|
+
e: sleepFor(seconds: 0.4)
|
53
53
|
}
|
54
54
|
|}
|
55
55
|
it "returns a partial response and error messages" do
|
56
56
|
expected_data = {
|
57
|
-
"a"=>0.
|
58
|
-
"b"=>0.
|
59
|
-
"c"=>0.
|
57
|
+
"a"=>0.4,
|
58
|
+
"b"=>0.4,
|
59
|
+
"c"=>0.4,
|
60
60
|
"d"=>nil,
|
61
61
|
"e"=>nil,
|
62
62
|
}
|
@@ -79,11 +79,11 @@ describe GraphQL::Schema::TimeoutMiddleware do
|
|
79
79
|
describe "timeout in nested fields" do
|
80
80
|
let(:query_string) {%|
|
81
81
|
{
|
82
|
-
a: nestedSleep(seconds:
|
82
|
+
a: nestedSleep(seconds: 0.3) {
|
83
83
|
seconds
|
84
|
-
b: nestedSleep(seconds: 0.
|
84
|
+
b: nestedSleep(seconds: 0.3) {
|
85
85
|
seconds
|
86
|
-
c: nestedSleep(seconds: 0.
|
86
|
+
c: nestedSleep(seconds: 0.3) {
|
87
87
|
seconds
|
88
88
|
d: nestedSleep(seconds: 0.4) {
|
89
89
|
seconds
|
@@ -96,14 +96,15 @@ describe GraphQL::Schema::TimeoutMiddleware do
|
|
96
96
|
}
|
97
97
|
}
|
98
98
|
|}
|
99
|
+
|
99
100
|
it "returns a partial response and error messages" do
|
100
101
|
expected_data = {
|
101
102
|
"a" => {
|
102
|
-
"seconds" =>
|
103
|
+
"seconds" => 0.3,
|
103
104
|
"b" => {
|
104
|
-
"seconds" => 0.
|
105
|
+
"seconds" => 0.3,
|
105
106
|
"c" => {
|
106
|
-
"seconds"=>0.
|
107
|
+
"seconds"=>0.3,
|
107
108
|
"d" => {
|
108
109
|
"seconds"=>nil,
|
109
110
|
"e"=>nil
|
@@ -131,17 +132,17 @@ describe GraphQL::Schema::TimeoutMiddleware do
|
|
131
132
|
describe "long-running fields" do
|
132
133
|
let(:query_string) {%|
|
133
134
|
{
|
134
|
-
a: sleepFor(seconds: 0.
|
135
|
-
b: sleepFor(seconds: 0.
|
136
|
-
c: sleepFor(seconds:
|
135
|
+
a: sleepFor(seconds: 0.2)
|
136
|
+
b: sleepFor(seconds: 0.2)
|
137
|
+
c: sleepFor(seconds: 0.8)
|
137
138
|
d: sleepFor(seconds: 0.1)
|
138
139
|
}
|
139
140
|
|}
|
140
141
|
it "doesn't terminate long-running field execution" do
|
141
142
|
expected_data = {
|
142
|
-
"a"=>0.
|
143
|
-
"b"=>0.
|
144
|
-
"c"=>
|
143
|
+
"a"=>0.2,
|
144
|
+
"b"=>0.2,
|
145
|
+
"c"=>0.8,
|
145
146
|
"d"=>nil,
|
146
147
|
}
|
147
148
|
|
@@ -159,17 +160,17 @@ describe GraphQL::Schema::TimeoutMiddleware do
|
|
159
160
|
|
160
161
|
describe "with a custom block" do
|
161
162
|
let(:timeout_middleware) {
|
162
|
-
GraphQL::Schema::TimeoutMiddleware.new(max_seconds:
|
163
|
+
GraphQL::Schema::TimeoutMiddleware.new(max_seconds: max_seconds) do |err, query|
|
163
164
|
raise("Query timed out after 2s: #{query.class.name}")
|
164
165
|
end
|
165
166
|
}
|
166
167
|
let(:query_string) {%|
|
167
168
|
{
|
168
|
-
a: sleepFor(seconds: 0.
|
169
|
-
b: sleepFor(seconds: 0.
|
170
|
-
c: sleepFor(seconds: 0.
|
171
|
-
d: sleepFor(seconds: 0.
|
172
|
-
e: sleepFor(seconds: 0.
|
169
|
+
a: sleepFor(seconds: 0.4)
|
170
|
+
b: sleepFor(seconds: 0.4)
|
171
|
+
c: sleepFor(seconds: 0.4)
|
172
|
+
d: sleepFor(seconds: 0.4)
|
173
|
+
e: sleepFor(seconds: 0.4)
|
173
174
|
}
|
174
175
|
|}
|
175
176
|
|