graphql 0.18.2 → 0.18.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/execution/typecast.rb +17 -13
- data/lib/graphql/query/serial_execution/selection_resolution.rb +4 -4
- data/lib/graphql/relay/base_connection.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/execution/typecast_spec.rb +51 -0
- data/spec/graphql/interface_type_spec.rb +49 -0
- data/spec/graphql/introspection/schema_type_spec.rb +1 -0
- data/spec/graphql/introspection/type_type_spec.rb +2 -1
- data/spec/graphql/object_type_spec.rb +1 -1
- data/spec/graphql/relay/mutation_spec.rb +10 -15
- data/spec/graphql/schema/reduce_types_spec.rb +1 -0
- data/spec/graphql/union_type_spec.rb +28 -1
- data/spec/support/dairy_app.rb +24 -3
- data/spec/support/star_wars_schema.rb +5 -2
- metadata +4 -3
- data/lib/graphql/schema/middleware.rb +0 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e542df508ad2037e784439e944e47df3711dc7b7
|
4
|
+
data.tar.gz: 4623d2ac01705a25245a114aec72a71117172c77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2b6f96474c81050d2f626e62ab96c784459662f3676c6b4a28ea8d36def062e5de6b1cefa097705f6c93be2b026dfb6cea1184b9e736d9fd0618024442b8d83
|
7
|
+
data.tar.gz: de4ea313f70e012ad7152a71dbfe459d62fa4988b43f6947791d00c1fa45eb377962331e8db7f504a96ca331d27d962933874af4afae405a4794be2c82c2d714
|
@@ -1,28 +1,32 @@
|
|
1
1
|
module GraphQL
|
2
2
|
module Execution
|
3
|
-
# GraphQL object `{value,
|
4
|
-
# - `
|
5
|
-
# - `
|
6
|
-
# - `
|
7
|
-
# - `
|
8
|
-
# - `
|
3
|
+
# GraphQL object `{value, current_type}` can be cast to `potential_type` when:
|
4
|
+
# - `current_type == potential_type`
|
5
|
+
# - `current_type` is a union and it contains `potential_type`
|
6
|
+
# - `potential_type` is a union and it contains `current_type`
|
7
|
+
# - `current_type` is an interface and `potential_type` implements it
|
8
|
+
# - `potential_type` is an interface and `current_type` implements it
|
9
9
|
module Typecast
|
10
10
|
# While `value` is exposed by GraphQL as an instance of `current_type`,
|
11
11
|
# should it _also_ be treated as an instance of `potential_type`?
|
12
12
|
#
|
13
13
|
# This is used for checking whether fragments apply to an object.
|
14
14
|
#
|
15
|
-
# @
|
16
|
-
|
17
|
-
|
15
|
+
# @param [Object] the value which GraphQL is currently exposing
|
16
|
+
# @param [GraphQL::BaseType] the type which GraphQL is using for `value` now
|
17
|
+
# @param [GraphQL::BaseType] can `value` be exposed using this type?
|
18
|
+
# @param [GraphQL::Query::Context] the context for the current query
|
19
|
+
# @return [Boolean] true if `value` be evaluated as a `potential_type`
|
20
|
+
def self.compatible?(current_type, potential_type, query_ctx)
|
21
|
+
if current_type == potential_type
|
18
22
|
true
|
19
23
|
elsif current_type.kind.union?
|
20
|
-
current_type.
|
24
|
+
current_type.possible_types.include?(potential_type)
|
21
25
|
elsif potential_type.kind.union?
|
22
26
|
potential_type.include?(current_type)
|
23
|
-
elsif current_type.kind.interface?
|
24
|
-
|
25
|
-
elsif potential_type.kind.interface?
|
27
|
+
elsif current_type.kind.interface? && potential_type.kind.object?
|
28
|
+
potential_type.interfaces.include?(current_type)
|
29
|
+
elsif potential_type.kind.interface? && current_type.kind.object?
|
26
30
|
current_type.interfaces.include?(potential_type)
|
27
31
|
else
|
28
32
|
false
|
@@ -13,7 +13,7 @@ module GraphQL
|
|
13
13
|
|
14
14
|
def result
|
15
15
|
irep_node.children.each_with_object({}) do |(name, irep_node), memo|
|
16
|
-
if GraphQL::Execution::DirectiveChecks.include?(irep_node, execution_context.query) && applies_to_type?(irep_node, type
|
16
|
+
if GraphQL::Execution::DirectiveChecks.include?(irep_node, execution_context.query) && applies_to_type?(irep_node, type)
|
17
17
|
field_result = execution_context.strategy.field_resolution.new(
|
18
18
|
irep_node,
|
19
19
|
type,
|
@@ -27,9 +27,9 @@ module GraphQL
|
|
27
27
|
|
28
28
|
private
|
29
29
|
|
30
|
-
def applies_to_type?(irep_node,
|
31
|
-
irep_node.definitions.any? { |
|
32
|
-
GraphQL::Execution::Typecast.compatible?(
|
30
|
+
def applies_to_type?(irep_node, current_type)
|
31
|
+
irep_node.definitions.any? { |potential_type, field_defn|
|
32
|
+
GraphQL::Execution::Typecast.compatible?(current_type, potential_type, execution_context.query.context)
|
33
33
|
}
|
34
34
|
end
|
35
35
|
end
|
@@ -61,7 +61,7 @@ module GraphQL
|
|
61
61
|
# @param field [Object] The underlying field
|
62
62
|
# @param max_page_size [Int] The maximum number of results to return
|
63
63
|
# @param parent [Object] The object which this collection belongs to
|
64
|
-
def initialize(nodes, arguments, field
|
64
|
+
def initialize(nodes, arguments, field: nil, max_page_size: nil, parent: nil)
|
65
65
|
@nodes = nodes
|
66
66
|
@arguments = arguments
|
67
67
|
@max_page_size = max_page_size
|
data/lib/graphql/version.rb
CHANGED
@@ -0,0 +1,51 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GraphQL::Execution::Typecast do
|
4
|
+
let(:milk_value) { MILKS[1] }
|
5
|
+
let(:cheese_value) { CHEESES[1] }
|
6
|
+
|
7
|
+
let(:schema) { DummySchema }
|
8
|
+
let(:context) { GraphQL::Query::Context.new(query: OpenStruct.new(schema: schema), values: nil) }
|
9
|
+
|
10
|
+
def compatible?(*args)
|
11
|
+
GraphQL::Execution::Typecast.compatible?(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "resolves correctly when both types are the same" do
|
15
|
+
assert compatible?(MilkType, MilkType, context)
|
16
|
+
|
17
|
+
assert !compatible?(MilkType, CheeseType, context)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "resolves a union type to a matching member" do
|
21
|
+
assert compatible?(DairyProductUnion, MilkType, context)
|
22
|
+
assert compatible?(DairyProductUnion, CheeseType, context)
|
23
|
+
|
24
|
+
assert !compatible?(DairyProductUnion, GraphQL::INT_TYPE, context)
|
25
|
+
assert !compatible?(DairyProductUnion, HoneyType, context)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "resolves correcty when potential type is UnionType and current type is a member of that union" do
|
29
|
+
assert compatible?(MilkType, DairyProductUnion, context)
|
30
|
+
assert compatible?(CheeseType, DairyProductUnion, context)
|
31
|
+
|
32
|
+
assert !compatible?(QueryType, DairyProductUnion, context)
|
33
|
+
assert !compatible?(EdibleInterface, DairyProductUnion, context)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "resolves an object type to one of its interfaces" do
|
37
|
+
assert compatible?(CheeseType, EdibleInterface, context)
|
38
|
+
assert compatible?(MilkType, EdibleInterface, context)
|
39
|
+
|
40
|
+
assert !compatible?(QueryType, EdibleInterface, context)
|
41
|
+
assert !compatible?(LocalProductInterface, EdibleInterface, context)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "resolves an interface to a matching member" do
|
45
|
+
assert compatible?(EdibleInterface, CheeseType, context)
|
46
|
+
assert compatible?(EdibleInterface, MilkType, context)
|
47
|
+
|
48
|
+
assert !compatible?(EdibleInterface, GraphQL::STRING_TYPE, context)
|
49
|
+
assert !compatible?(EdibleInterface, DairyProductInputType, context)
|
50
|
+
end
|
51
|
+
end
|
@@ -53,4 +53,53 @@ describe GraphQL::InterfaceType do
|
|
53
53
|
assert_equal(interface.resolve_type(123, nil), :custom_resolve)
|
54
54
|
end
|
55
55
|
end
|
56
|
+
|
57
|
+
describe "fragments" do
|
58
|
+
let(:query_string) {%|
|
59
|
+
{
|
60
|
+
favoriteEdible {
|
61
|
+
fatContent
|
62
|
+
... on LocalProduct {
|
63
|
+
origin
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|}
|
68
|
+
let(:result) { DummySchema.execute(query_string) }
|
69
|
+
|
70
|
+
it "can apply interface fragments to an interface" do
|
71
|
+
expected_result = { "data" => {
|
72
|
+
"favoriteEdible" => {
|
73
|
+
"fatContent" => 0.04,
|
74
|
+
"origin" => "Antiquity",
|
75
|
+
}
|
76
|
+
} }
|
77
|
+
|
78
|
+
assert_equal(expected_result, result)
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "filtering members by type" do
|
82
|
+
let(:query_string) {%|
|
83
|
+
{
|
84
|
+
allEdible {
|
85
|
+
__typename
|
86
|
+
... on LocalProduct {
|
87
|
+
origin
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|}
|
92
|
+
|
93
|
+
it "only applies fields to the right object" do
|
94
|
+
expected_data = [
|
95
|
+
{"__typename"=>"Cheese", "origin"=>"France"},
|
96
|
+
{"__typename"=>"Cheese", "origin"=>"Netherlands"},
|
97
|
+
{"__typename"=>"Cheese", "origin"=>"Spain"},
|
98
|
+
{"__typename"=>"Milk", "origin"=>"Antiquity"},
|
99
|
+
]
|
100
|
+
|
101
|
+
assert_equal expected_data, result["data"]["allEdible"]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
56
105
|
end
|
@@ -36,7 +36,8 @@ describe GraphQL::Introspection::TypeType do
|
|
36
36
|
"milkType"=>{
|
37
37
|
"interfaces"=>[
|
38
38
|
{"name"=>"Edible"},
|
39
|
-
{"name"=>"AnimalProduct"}
|
39
|
+
{"name"=>"AnimalProduct"},
|
40
|
+
{"name"=>"LocalProduct"},
|
40
41
|
],
|
41
42
|
"fields"=>[
|
42
43
|
{"type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"Float"}}},
|
@@ -15,7 +15,7 @@ describe GraphQL::ObjectType do
|
|
15
15
|
end
|
16
16
|
|
17
17
|
it "may have interfaces" do
|
18
|
-
assert_equal([EdibleInterface, AnimalProductInterface], type.interfaces)
|
18
|
+
assert_equal([EdibleInterface, AnimalProductInterface, LocalProductInterface], type.interfaces)
|
19
19
|
end
|
20
20
|
|
21
21
|
describe '#get_field ' do
|
@@ -5,7 +5,9 @@ describe GraphQL::Relay::Mutation do
|
|
5
5
|
mutation addBagel($clientMutationId: String) {
|
6
6
|
introduceShip(input: {shipName: "Bagel", factionId: "1", clientMutationId: $clientMutationId}) {
|
7
7
|
clientMutationId
|
8
|
-
|
8
|
+
shipEdge {
|
9
|
+
node { name, id }
|
10
|
+
}
|
9
11
|
faction { name }
|
10
12
|
}
|
11
13
|
}
|
@@ -28,9 +30,11 @@ describe GraphQL::Relay::Mutation do
|
|
28
30
|
expected = {"data" => {
|
29
31
|
"introduceShip" => {
|
30
32
|
"clientMutationId" => "1234",
|
31
|
-
"
|
32
|
-
"
|
33
|
-
|
33
|
+
"shipEdge" => {
|
34
|
+
"node" => {
|
35
|
+
"name" => "Bagel",
|
36
|
+
"id" => NodeIdentification.to_global_id("Ship", "9"),
|
37
|
+
},
|
34
38
|
},
|
35
39
|
"faction" => {"name" => STAR_WARS_DATA["Faction"]["1"].name }
|
36
40
|
}
|
@@ -40,16 +44,7 @@ describe GraphQL::Relay::Mutation do
|
|
40
44
|
|
41
45
|
it "doesn't require a clientMutationId to perform mutations" do
|
42
46
|
result = query(query_string)
|
43
|
-
|
44
|
-
|
45
|
-
"clientMutationId" => nil,
|
46
|
-
"ship" => {
|
47
|
-
"name" => "Bagel",
|
48
|
-
"id" => NodeIdentification.to_global_id("Ship", "9"),
|
49
|
-
},
|
50
|
-
"faction" => {"name" => STAR_WARS_DATA["Faction"]["1"].name }
|
51
|
-
}
|
52
|
-
}}
|
53
|
-
assert_equal(expected, result)
|
47
|
+
new_ship_name = result["data"]["introduceShip"]["shipEdge"]["node"]["name"]
|
48
|
+
assert_equal("Bagel", new_ship_name)
|
54
49
|
end
|
55
50
|
end
|
@@ -14,6 +14,7 @@ describe GraphQL::Schema::ReduceTypes do
|
|
14
14
|
"Int" => GraphQL::INT_TYPE,
|
15
15
|
"Edible" => EdibleInterface,
|
16
16
|
"AnimalProduct" => AnimalProductInterface,
|
17
|
+
"LocalProduct" => LocalProductInterface,
|
17
18
|
}
|
18
19
|
result = reduce_types([CheeseType])
|
19
20
|
assert_equal(expected.keys, result.keys)
|
@@ -12,11 +12,11 @@ describe GraphQL::UnionType do
|
|
12
12
|
possible_types(types)
|
13
13
|
}
|
14
14
|
}
|
15
|
+
|
15
16
|
it "has a name" do
|
16
17
|
assert_equal("MyUnion", union.name)
|
17
18
|
end
|
18
19
|
|
19
|
-
|
20
20
|
it "infers type from an object" do
|
21
21
|
assert_equal(CheeseType, DairyProductUnion.resolve_type(CHEESES[1], OpenStruct.new(schema: DummySchema)))
|
22
22
|
end
|
@@ -29,6 +29,33 @@ describe GraphQL::UnionType do
|
|
29
29
|
assert_equal(false, union.include?(type_3))
|
30
30
|
end
|
31
31
|
|
32
|
+
describe "typecasting from union to union" do
|
33
|
+
let(:result) { DummySchema.execute(query_string) }
|
34
|
+
let(:query_string) {%|
|
35
|
+
{
|
36
|
+
allDairy {
|
37
|
+
dairyName: __typename
|
38
|
+
... on Beverage {
|
39
|
+
bevName: __typename
|
40
|
+
... on Milk {
|
41
|
+
flavors
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|}
|
47
|
+
|
48
|
+
it "casts if the object belongs to both unions" do
|
49
|
+
expected_result = [
|
50
|
+
{"dairyName"=>"Cheese"},
|
51
|
+
{"dairyName"=>"Cheese"},
|
52
|
+
{"dairyName"=>"Cheese"},
|
53
|
+
{"dairyName"=>"Milk", "bevName"=>"Milk", "flavors"=>["Natural", "Chocolate", "Strawberry"]},
|
54
|
+
]
|
55
|
+
assert_equal expected_result, result["data"]["allDairy"]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
32
59
|
describe "list of union type" do
|
33
60
|
describe "fragment spreads" do
|
34
61
|
let(:result) { DummySchema.execute(query_string) }
|
data/spec/support/dairy_app.rb
CHANGED
@@ -5,6 +5,17 @@ class NoSuchDairyError < StandardError; end
|
|
5
5
|
GraphQL::Field.accepts_definitions(joins: GraphQL::Define.assign_metadata_key(:joins))
|
6
6
|
GraphQL::BaseType.accepts_definitions(class_names: GraphQL::Define.assign_metadata_key(:class_names))
|
7
7
|
|
8
|
+
LocalProductInterface = GraphQL::InterfaceType.define do
|
9
|
+
name "LocalProduct"
|
10
|
+
description "Something that comes from somewhere"
|
11
|
+
field :origin, !types.String, "Place the thing comes from"
|
12
|
+
# This is a "bug" in the dummy app:
|
13
|
+
# it should actually check the incoming object to determine the type.
|
14
|
+
# But this is here so we can check _where_ misbehaving resolve_type
|
15
|
+
# functions wreak their havoc.
|
16
|
+
resolve_type -> (o, c) { MilkType }
|
17
|
+
end
|
18
|
+
|
8
19
|
EdibleInterface = GraphQL::InterfaceType.define do
|
9
20
|
name "Edible"
|
10
21
|
description "Something you can eat, yum"
|
@@ -18,6 +29,12 @@ AnimalProductInterface = GraphQL::InterfaceType.define do
|
|
18
29
|
field :source, !types.String, "Animal which produced this product"
|
19
30
|
end
|
20
31
|
|
32
|
+
BeverageUnion = GraphQL::UnionType.define do
|
33
|
+
name "Beverage"
|
34
|
+
description "Something you can drink"
|
35
|
+
possible_types [MilkType]
|
36
|
+
end
|
37
|
+
|
21
38
|
DairyAnimalEnum = GraphQL::EnumType.define do
|
22
39
|
name "DairyAnimal"
|
23
40
|
description "An animal which can yield milk"
|
@@ -31,7 +48,7 @@ CheeseType = GraphQL::ObjectType.define do
|
|
31
48
|
name "Cheese"
|
32
49
|
class_names ["Cheese"]
|
33
50
|
description "Cultured dairy product"
|
34
|
-
interfaces [EdibleInterface, AnimalProductInterface]
|
51
|
+
interfaces [EdibleInterface, AnimalProductInterface, LocalProductInterface]
|
35
52
|
|
36
53
|
# Can have (name, type, desc)
|
37
54
|
field :id, !types.Int, "Unique identifier"
|
@@ -78,7 +95,7 @@ end
|
|
78
95
|
MilkType = GraphQL::ObjectType.define do
|
79
96
|
name "Milk"
|
80
97
|
description "Dairy beverage"
|
81
|
-
interfaces [EdibleInterface, AnimalProductInterface]
|
98
|
+
interfaces [EdibleInterface, AnimalProductInterface, LocalProductInterface]
|
82
99
|
field :id, !types.ID
|
83
100
|
field :source, DairyAnimalEnum, "Animal which produced this milk", hash_key: :source
|
84
101
|
field :origin, !types.String, "Place the milk comes from"
|
@@ -254,6 +271,10 @@ DairyAppQueryType = GraphQL::ObjectType.define do
|
|
254
271
|
resolve -> (obj, args, ctx) { CHEESES.values + MILKS.values }
|
255
272
|
end
|
256
273
|
|
274
|
+
field :allEdible, types[EdibleInterface] do
|
275
|
+
resolve -> (obj, args, ctx) { CHEESES.values + MILKS.values }
|
276
|
+
end
|
277
|
+
|
257
278
|
field :error do
|
258
279
|
description "Raise an error"
|
259
280
|
type GraphQL::STRING_TYPE
|
@@ -317,6 +338,6 @@ DummySchema = GraphQL::Schema.new(
|
|
317
338
|
mutation: DairyAppMutationType,
|
318
339
|
subscription: SubscriptionType,
|
319
340
|
max_depth: 5,
|
320
|
-
types: [HoneyType],
|
341
|
+
types: [HoneyType, BeverageUnion],
|
321
342
|
)
|
322
343
|
DummySchema.rescue_from(NoSuchDairyError) { |err| err.message }
|
@@ -177,7 +177,7 @@ IntroduceShipMutation = GraphQL::Relay::Mutation.define do
|
|
177
177
|
input_field :factionId, !types.ID
|
178
178
|
|
179
179
|
# Result may have access to these fields:
|
180
|
-
return_field :
|
180
|
+
return_field :shipEdge, Ship.edge_type
|
181
181
|
return_field :faction, Faction
|
182
182
|
|
183
183
|
# Here's the mutation operation:
|
@@ -185,7 +185,10 @@ IntroduceShipMutation = GraphQL::Relay::Mutation.define do
|
|
185
185
|
faction_id = inputs["factionId"]
|
186
186
|
ship = STAR_WARS_DATA.create_ship(inputs["shipName"], faction_id)
|
187
187
|
faction = STAR_WARS_DATA["Faction"][faction_id]
|
188
|
-
|
188
|
+
connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(faction.ships)
|
189
|
+
ships_connection = connection_class.new(faction.ships, inputs)
|
190
|
+
ship_edge = GraphQL::Relay::Edge.new(ship, ships_connection)
|
191
|
+
{ shipEdge: ship_edge, faction: faction }
|
189
192
|
}
|
190
193
|
end
|
191
194
|
|
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: 0.18.
|
4
|
+
version: 0.18.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: codeclimate-test-reporter
|
@@ -346,7 +346,6 @@ files:
|
|
346
346
|
- lib/graphql/schema.rb
|
347
347
|
- lib/graphql/schema/catchall_middleware.rb
|
348
348
|
- lib/graphql/schema/invalid_type_error.rb
|
349
|
-
- lib/graphql/schema/middleware.rb
|
350
349
|
- lib/graphql/schema/middleware_chain.rb
|
351
350
|
- lib/graphql/schema/possible_types.rb
|
352
351
|
- lib/graphql/schema/printer.rb
|
@@ -397,6 +396,7 @@ files:
|
|
397
396
|
- spec/graphql/define/instance_definable_spec.rb
|
398
397
|
- spec/graphql/directive_spec.rb
|
399
398
|
- spec/graphql/enum_type_spec.rb
|
399
|
+
- spec/graphql/execution/typecast_spec.rb
|
400
400
|
- spec/graphql/execution_error_spec.rb
|
401
401
|
- spec/graphql/field_spec.rb
|
402
402
|
- spec/graphql/float_type_spec.rb
|
@@ -502,6 +502,7 @@ test_files:
|
|
502
502
|
- spec/graphql/define/instance_definable_spec.rb
|
503
503
|
- spec/graphql/directive_spec.rb
|
504
504
|
- spec/graphql/enum_type_spec.rb
|
505
|
+
- spec/graphql/execution/typecast_spec.rb
|
505
506
|
- spec/graphql/execution_error_spec.rb
|
506
507
|
- spec/graphql/field_spec.rb
|
507
508
|
- spec/graphql/float_type_spec.rb
|
@@ -1,75 +0,0 @@
|
|
1
|
-
module GraphQL
|
2
|
-
class Schema
|
3
|
-
# - query_initialize
|
4
|
-
# - query_finalize
|
5
|
-
# - field_resolve
|
6
|
-
#
|
7
|
-
# @example Closing a socket when the query is finished
|
8
|
-
# SocketClosingMiddleware = GraphQL::Schema::Middleware.define do
|
9
|
-
# finalize_query -> (memo, env) {
|
10
|
-
# query.context[:socket].close
|
11
|
-
# }
|
12
|
-
# end
|
13
|
-
#
|
14
|
-
# @example Timing a query
|
15
|
-
# QueryTimerMiddleware = GraphQL::Schema::Middleware.define do
|
16
|
-
# initialize_query -> (env) {
|
17
|
-
# env.merge!({
|
18
|
-
# query_start_time: Time.now,
|
19
|
-
# field_times: [],
|
20
|
-
# })
|
21
|
-
# }
|
22
|
-
#
|
23
|
-
# resolve_field -> (env) {
|
24
|
-
# field_time = {
|
25
|
-
# start_time: Time.now,
|
26
|
-
# own_elapsed: nil,
|
27
|
-
# child_elapsed: 0,
|
28
|
-
# }
|
29
|
-
#
|
30
|
-
# memo[:field_times] << field_time
|
31
|
-
# memo
|
32
|
-
# }
|
33
|
-
#
|
34
|
-
# after_field { |memo, env|
|
35
|
-
# field_time = memo[:field_times].pop
|
36
|
-
# total_elapsed = Time.now - field_time[:start_time]
|
37
|
-
# own_elapsed = total_elapsed - field_time[:child_elapsed]
|
38
|
-
#
|
39
|
-
# field_name = "#{env[:parent_type].name}.#{env[:field_definition].name}(#{env[:arguments].to_h.to_json})"
|
40
|
-
# Logger.info("[GraphQL Field] #{field_name} #{own_elapsed}")
|
41
|
-
#
|
42
|
-
# parent_field_time = memo[:field_times].last
|
43
|
-
# if parent_field_time
|
44
|
-
# parent_field_time[:child_elapsed] += total_elapsed
|
45
|
-
# end
|
46
|
-
# }
|
47
|
-
#
|
48
|
-
# after_query { |memo, env|
|
49
|
-
# elapsed = Time.now - memo[:query_start_time]
|
50
|
-
# Logger.info("[GraphQL Elapsed] #{elapsed}")
|
51
|
-
# }
|
52
|
-
# end
|
53
|
-
class Middleware
|
54
|
-
EVENTS = [:before_query, :after_query, :before_field, :after_field]
|
55
|
-
class << self
|
56
|
-
attr_reader :handlers
|
57
|
-
def handlers
|
58
|
-
@handlers ||= {}
|
59
|
-
end
|
60
|
-
|
61
|
-
def on(event_name, &handler)
|
62
|
-
if EVENTS.include?(event_name)
|
63
|
-
handlers[event_name] = handler
|
64
|
-
else
|
65
|
-
raise("Can't attach handler to #{event_name} (must be one of: #{EVENTS})")
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def trigger(event_name, *args)
|
71
|
-
self.class.handlers[event_name].call(*args)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|