graphql 0.18.2 → 0.18.3

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 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