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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c25c28ed78a94aead6ff0547d559c15b3c00c01f
4
- data.tar.gz: 81c73c51e31e934391e9da8daa3d6915acaccb67
3
+ metadata.gz: e542df508ad2037e784439e944e47df3711dc7b7
4
+ data.tar.gz: 4623d2ac01705a25245a114aec72a71117172c77
5
5
  SHA512:
6
- metadata.gz: 3189f4d3b79efdf1082168bcf338159f6a89f04ba0c51d78ca35e64a1284438b08b3424d181db23d8e01c3505fa83cb8c915dc9d8a419cb1d91dbe0af0166d06
7
- data.tar.gz: f1a2c37b55214c42258b3f708d15a1dbe33d5bb0e01a8333d0818e494a96319f92d7e942c6efb71591596eafbd9c8e6f108042b928ea1c5631c295c483f86886
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, type}` can be cast to `other_type` when:
4
- # - `type == other_type`
5
- # - `type` is a union and it resolves `value` to `other_type`
6
- # - `other_type` is a union and `type` is a member
7
- # - `type` is an interface and it resolves `value` to `other_type`
8
- # - `other_type` is an interface and `type` implements that interface
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
- # @return [Boolean] Can `value` be evaluated as a `potential_type`?
16
- def self.compatible?(value, current_type, potential_type, query_ctx)
17
- if potential_type == current_type
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.resolve_type(value, query_ctx) == potential_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
- current_type.resolve_type(value, query_ctx) == potential_type
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, target)
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, type, target)
31
- irep_node.definitions.any? { |child_type, field_defn|
32
- GraphQL::Execution::Typecast.compatible?(target, child_type, type, execution_context.query.context)
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:, max_page_size: nil, parent: nil)
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
@@ -1,3 +1,3 @@
1
1
  module GraphQL
2
- VERSION = "0.18.2"
2
+ VERSION = "0.18.3"
3
3
  end
@@ -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
@@ -19,6 +19,7 @@ describe GraphQL::Introspection::SchemaType do
19
19
  "queryType"=>{
20
20
  "fields"=>[
21
21
  {"name"=>"allDairy"},
22
+ {"name"=>"allEdible"},
22
23
  {"name"=>"cheese"},
23
24
  {"name"=>"cow"},
24
25
  {"name"=>"dairy"},
@@ -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
- ship { name, id }
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
- "ship" => {
32
- "name" => "Bagel",
33
- "id" => NodeIdentification.to_global_id("Ship", "9"),
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
- expected = {"data" => {
44
- "introduceShip" => {
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) }
@@ -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 :ship, Ship
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
- { ship: ship, faction: faction }
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.2
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-17 00:00:00.000000000 Z
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