graphql 1.4.3 → 1.4.4

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: cda98acc4cef5d2d8e4c9c77cd9ac5f5139cc38a
4
- data.tar.gz: 184b72d444de078b443453bca6f0a4d64d8cf0b9
3
+ metadata.gz: 3e792ef4481683e6c02e730a49a55d514e3117bd
4
+ data.tar.gz: 60f5a4bd233004203b0a3249e91d1c1ac4c99fec
5
5
  SHA512:
6
- metadata.gz: cf7c368fd8d9ef704580aa2956e2813db1e3efbd748f1d9836d58b17ae5afaca3df2d623750dd9546688e42ab1cb5c0be26f6bbe46ea3f7d837788c8fc00fe92
7
- data.tar.gz: ce531eb4ad6cd87b947c9c5fbd68d5cd14fef266950a3448e02659d02e8bc7db10c7d9d206ea12751d059c01392ed952facf938c3cddfd53dfb21404b554d83f
6
+ metadata.gz: fc9cc9c7581fb346daf8eaff35d1b7183c4ed89fc9c8c077827e71f5a63418c9d717983d2af661c700a5aade7a2c262714e01b809bd53a241c018b807de169cd
7
+ data.tar.gz: 631b382abd20a9e9060cda878287ea87df1d227e55cb410038755e99c38697007ab188fce7ff4ca7ed935e64912e663fe10e63c935c2727f53b19cbceda71570
@@ -14,13 +14,17 @@ module GraphQL
14
14
  # GraphQL::InputObjectType.define do
15
15
  # argument :newName, !types.String
16
16
  # end
17
- #
17
+
18
18
  class Argument
19
19
  include GraphQL::Define::InstanceDefinable
20
- accepts_definitions :name, :type, :description, :default_value
21
- attr_accessor :type, :description, :default_value, :name
20
+ accepts_definitions :name, :type, :description, :default_value, :as
21
+ attr_accessor :type, :description, :default_value, :name, :as
22
+
23
+ ensure_defined(:name, :description, :default_value, :type=, :type, :as, :expose_as)
22
24
 
23
- ensure_defined(:name, :description, :default_value, :type=, :type)
25
+ def initialize_copy(other)
26
+ @expose_as = nil
27
+ end
24
28
 
25
29
  def default_value?
26
30
  !!@has_default_value
@@ -44,5 +48,10 @@ module GraphQL
44
48
  def type
45
49
  @clean_type ||= GraphQL::BaseType.resolve_related_type(@dirty_type)
46
50
  end
51
+
52
+ # @return [String] The name of this argument inside `resolve` functions
53
+ def expose_as
54
+ @expose_as ||= (@as || @name).to_s
55
+ end
47
56
  end
48
57
  end
@@ -158,5 +158,15 @@ module GraphQL
158
158
  def define_edge(**kwargs, &block)
159
159
  GraphQL::Relay::EdgeType.create_type(self, **kwargs, &block)
160
160
  end
161
+
162
+ # Return a GraphQL string for the type definition
163
+ # @param schema [GraphQL::Schema]
164
+ # @param printer [GraphQL::Schema::Printer]
165
+ # @see {GraphQL::Schema::Printer#initialize for additional options}
166
+ # @return [String] type definition
167
+ def to_definition(schema, printer: nil, **args)
168
+ printer ||= GraphQL::Schema::Printer.new(schema, **args)
169
+ printer.print_type(self)
170
+ end
161
171
  end
162
172
  end
@@ -10,7 +10,7 @@ module GraphQL
10
10
  GraphQL::Argument.new
11
11
  end
12
12
 
13
- unsupported_keys = rest.keys - [:default_value]
13
+ unsupported_keys = rest.keys - [:default_value, :as]
14
14
  if unsupported_keys.any?
15
15
  raise ArgumentError.new("unknown keyword#{unsupported_keys.length > 1 ? 's' : ''}: #{unsupported_keys.join(', ')}")
16
16
  end
@@ -19,6 +19,7 @@ module GraphQL
19
19
  type && argument.type = type
20
20
  description && argument.description = description
21
21
  rest.key?(:default_value) && argument.default_value = rest[:default_value]
22
+ argument.as = rest[:as]
22
23
 
23
24
  target.arguments[name.to_s] = argument
24
25
  end
@@ -213,15 +213,14 @@ module GraphQL
213
213
  @clean_type ||= GraphQL::BaseType.resolve_related_type(@dirty_type)
214
214
  end
215
215
 
216
- # You can only set a field's name _once_ -- this to prevent
217
- # passing the same {Field} to multiple `.field` calls.
218
- #
219
- # This is important because {#name} may be used by {#resolve}.
220
216
  def name=(new_name)
221
- if @name.nil?
222
- @name = new_name
223
- elsif @name != new_name
224
- raise("Can't rename an already-named field. (Tried to rename \"#{@name}\" to \"#{new_name}\".) If you're passing a field with the `field:` argument, make sure it's an unused instance of GraphQL::Field.")
217
+ old_name = @name
218
+ @name = new_name
219
+
220
+ if old_name != new_name && @resolve_proc.is_a?(Field::Resolve::NameResolve)
221
+ # Since the NameResolve would use the old field name,
222
+ # reset resolve proc when the name has changed
223
+ self.resolve = nil
225
224
  end
226
225
  end
227
226
 
@@ -10,9 +10,10 @@ module GraphQL
10
10
  def initialize(values, argument_definitions:)
11
11
  @original_values = values
12
12
  @argument_values = values.inject({}) do |memo, (inner_key, inner_value)|
13
- string_key = inner_key.to_s
14
- arg_defn = argument_definitions[string_key]
13
+ arg_defn = argument_definitions[inner_key.to_s]
14
+
15
15
  arg_value = wrap_value(inner_value, arg_defn.type)
16
+ string_key = arg_defn.expose_as
16
17
  memo[string_key] = ArgumentValue.new(string_key, arg_value, arg_defn)
17
18
  memo
18
19
  end
@@ -47,7 +47,7 @@ module GraphQL
47
47
  end
48
48
  end
49
49
 
50
- attr_reader :nodes, :arguments, :max_page_size, :parent, :field
50
+ attr_reader :nodes, :arguments, :max_page_size, :parent, :field, :context
51
51
 
52
52
  # Make a connection, wrapping `nodes`
53
53
  # @param nodes [Object] The collection of nodes
@@ -10,14 +10,15 @@ module GraphQL
10
10
 
11
11
  # Create a connection which exposes edges of this type
12
12
  def self.create_type(wrapped_type, edge_type: nil, edge_class: nil, nodes_field: ConnectionType.default_nodes_field, &block)
13
- edge_type ||= wrapped_type.edge_type
14
13
  edge_class ||= GraphQL::Relay::Edge
15
- connection_type_name = "#{wrapped_type.name}Connection"
16
- connection_type_description = "The connection type for #{wrapped_type.name}."
14
+ edge_type ||= wrapped_type.edge_type
17
15
 
18
- connection_type = ObjectType.define do
19
- name(connection_type_name)
20
- description(connection_type_description)
16
+ # Any call that would trigger `wrapped_type.ensure_defined`
17
+ # must be inside this lazy block, otherwise we get weird
18
+ # cyclical dependency errors :S
19
+ ObjectType.define do
20
+ name("#{wrapped_type.name}Connection")
21
+ description("The connection type for #{wrapped_type.name}.")
21
22
  field :edges, types[edge_type] do
22
23
  description "A list of edges."
23
24
  resolve ->(obj, args, ctx) {
@@ -35,8 +36,6 @@ module GraphQL
35
36
  field :pageInfo, !PageInfo, "Information to aid in pagination.", property: :page_info
36
37
  block && instance_eval(&block)
37
38
  end
38
-
39
- connection_type
40
39
  end
41
40
  end
42
41
  end
@@ -53,12 +53,19 @@ module GraphQL
53
53
  accepts_definitions(
54
54
  :name, :description, :resolve,
55
55
  :return_type,
56
+ :return_interfaces,
56
57
  input_field: GraphQL::Define::AssignArgument,
57
58
  return_field: GraphQL::Define::AssignObjectField,
58
59
  )
59
- attr_accessor :name, :description, :fields, :arguments, :return_type
60
+ attr_accessor :name, :description, :fields, :arguments, :return_type, :return_interfaces
61
+
62
+ ensure_defined(
63
+ :input_fields, :return_fields, :name, :description,
64
+ :fields, :arguments, :return_type,
65
+ :return_interfaces, :resolve=,
66
+ :field, :result_class, :input_type
67
+ )
60
68
 
61
- ensure_defined(:name, :description, :fields, :arguments, :return_type, :resolve=, :field, :result_class, :input_type)
62
69
  # For backwards compat, but do we need this separate API?
63
70
  alias :return_fields :fields
64
71
  alias :input_fields :arguments
@@ -93,6 +100,10 @@ module GraphQL
93
100
  end
94
101
  end
95
102
 
103
+ def return_interfaces
104
+ @return_interfaces ||= []
105
+ end
106
+
96
107
  def return_type
97
108
  @return_type ||= begin
98
109
  @has_generated_return_type = true
@@ -101,6 +112,7 @@ module GraphQL
101
112
  name("#{relay_mutation.name}Payload")
102
113
  description("Autogenerated return type of #{relay_mutation.name}")
103
114
  field :clientMutationId, types.String, "A unique identifier for the client performing the mutation.", property: :client_mutation_id
115
+ interfaces relay_mutation.return_interfaces
104
116
  relay_mutation.return_fields.each do |name, field_obj|
105
117
  field name, field: field_obj
106
118
  end
@@ -195,6 +207,11 @@ module GraphQL
195
207
  end
196
208
 
197
209
  if @wrap_result
210
+ if mutation_result && !mutation_result.is_a?(Hash)
211
+ raise StandardError, "Expected `#{mutation_result}` to be a Hash."\
212
+ " Return a hash when using `return_field` or specify a custom `return_type`."
213
+ end
214
+
198
215
  @mutation.result_class.new(client_mutation_id: args[:input][:clientMutationId], result: mutation_result)
199
216
  else
200
217
  mutation_result
@@ -4,7 +4,7 @@ module GraphQL
4
4
  # Helpers for working with Relay-specific Node objects.
5
5
  module Node
6
6
  # @return [GraphQL::Field] a field for finding objects by their global ID.
7
- def self.field
7
+ def self.field(resolve: nil)
8
8
  # We have to define it fresh each time because
9
9
  # its name will be modified and its description
10
10
  # _may_ be modified.
@@ -12,17 +12,17 @@ module GraphQL
12
12
  type(GraphQL::Relay::Node.interface)
13
13
  description("Fetches an object given its ID.")
14
14
  argument(:id, !types.ID, "ID of the object.")
15
- resolve(GraphQL::Relay::Node::FindNode)
15
+ resolve(resolve || GraphQL::Relay::Node::FindNode)
16
16
  relay_node_field(true)
17
17
  end
18
18
  end
19
19
 
20
- def self.plural_field
20
+ def self.plural_field(resolve: nil)
21
21
  GraphQL::Field.define do
22
22
  type(!types[GraphQL::Relay::Node.interface])
23
23
  description("Fetches a list of objects given a list of IDs.")
24
24
  argument(:ids, !types[!types.ID], "IDs of the objects.")
25
- resolve(GraphQL::Relay::Node::FindNodes)
25
+ resolve(resolve || GraphQL::Relay::Node::FindNodes)
26
26
  relay_nodes_field(true)
27
27
  end
28
28
  end
@@ -51,7 +51,7 @@ module GraphQL
51
51
  if before
52
52
  [previous_offset, 0].max
53
53
  elsif last
54
- count(nodes) - last
54
+ [count(nodes) - last, 0].max
55
55
  else
56
56
  previous_offset
57
57
  end
@@ -3,41 +3,91 @@ module GraphQL
3
3
  class Schema
4
4
  # Used to convert your {GraphQL::Schema} to a GraphQL schema string
5
5
  #
6
- # @example print your schema to standard output
6
+ # @example print your schema to standard output (via helper)
7
7
  # MySchema = GraphQL::Schema.define(query: QueryType)
8
8
  # puts GraphQL::Schema::Printer.print_schema(MySchema)
9
9
  #
10
- module Printer
11
- extend self
10
+ # @example print your schema to standard output
11
+ # MySchema = GraphQL::Schema.define(query: QueryType)
12
+ # puts GraphQL::Schema::Printer.new(MySchema).print_schema
13
+ #
14
+ # @example print a single type to standard output
15
+ # query_root = GraphQL::ObjectType.define do
16
+ # name "Query"
17
+ # description "The query root of this schema"
18
+ #
19
+ # field :post do
20
+ # type post_type
21
+ # resolve ->(obj, args, ctx) { Post.find(args["id"]) }
22
+ # end
23
+ # end
24
+ #
25
+ # post_type = GraphQL::ObjectType.define do
26
+ # name "Post"
27
+ # description "A blog post"
28
+ #
29
+ # field :id, !types.ID
30
+ # field :title, !types.String
31
+ # field :body, !types.String
32
+ # end
33
+ #
34
+ # MySchema = GraphQL::Schema.define(query: query_root)
35
+ #
36
+ # printer = GraphQL::Schema::Printer.new(MySchema)
37
+ # puts printer.print_type(post_type)
38
+ #
39
+ class Printer
40
+ attr_reader :schema, :warden
12
41
 
13
- # Return a GraphQL schema string for the defined types in the schema
42
+ # @param schema [GraphQL::Schema]
14
43
  # @param context [Hash]
15
44
  # @param only [<#call(member, ctx)>]
16
45
  # @param except [<#call(member, ctx)>]
17
- # @param schema [GraphQL::Schema]
18
- def print_schema(schema, context: nil, only: nil, except: nil)
19
- blacklist = if only
20
- ->(m, ctx) { !(IS_USER_DEFINED_MEMBER.call(m) && only.call(m, ctx)) }
21
- elsif except
22
- ->(m, ctx) { !IS_USER_DEFINED_MEMBER.call(m) || except.call(m, ctx) }
23
- else
24
- ->(m, ctx) { !IS_USER_DEFINED_MEMBER.call(m) }
25
- end
46
+ # @param introspection [Boolean] Should include the introspection types in the string?
47
+ def initialize(schema, context: nil, only: nil, except: nil, introspection: false)
48
+ @schema = schema
49
+ @context = context
26
50
 
27
- warden = GraphQL::Schema::Warden.new(blacklist, schema: schema, context: context)
28
-
29
- print_filtered_schema(schema, warden: warden)
51
+ blacklist = build_blacklist(only, except, introspection: introspection)
52
+ @warden = GraphQL::Schema::Warden.new(blacklist, schema: @schema, context: @context)
30
53
  end
31
54
 
32
55
  # Return the GraphQL schema string for the introspection type system
33
- def print_introspection_schema
56
+ def self.print_introspection_schema
34
57
  query_root = ObjectType.define(name: "Root")
35
58
  schema = GraphQL::Schema.define(query: query_root)
36
59
  blacklist = ->(m, ctx) { m == query_root }
60
+ printer = new(schema, except: blacklist, introspection: true)
61
+ printer.print_schema
62
+ end
63
+
64
+ # Return a GraphQL schema string for the defined types in the schema
65
+ # @param schema [GraphQL::Schema]
66
+ # @param context [Hash]
67
+ # @param only [<#call(member, ctx)>]
68
+ # @param except [<#call(member, ctx)>]
69
+ def self.print_schema(schema, **args)
70
+ printer = new(schema, **args)
71
+ printer.print_schema
72
+ end
37
73
 
38
- warden = GraphQL::Schema::Warden.new(blacklist, schema: schema, context: nil)
74
+ # Return a GraphQL schema string for the defined types in the schema
75
+ def print_schema
76
+ directive_definitions = warden.directives.map { |directive| print_directive(directive) }
77
+
78
+ printable_types = warden.types.reject(&:default_scalar?)
79
+
80
+ type_definitions = printable_types
81
+ .sort_by(&:name)
82
+ .map { |type| print_type(type) }
39
83
 
40
- print_filtered_schema(schema, warden: warden)
84
+ [print_schema_definition].compact
85
+ .concat(directive_definitions)
86
+ .concat(type_definitions).join("\n\n")
87
+ end
88
+
89
+ def print_type(type)
90
+ TypeKindPrinters::STRATEGIES.fetch(type.kind).print(warden, type)
41
91
  end
42
92
 
43
93
  private
@@ -56,21 +106,27 @@ module GraphQL
56
106
 
57
107
  private_constant :IS_USER_DEFINED_MEMBER
58
108
 
59
- def print_filtered_schema(schema, warden:)
60
- directive_definitions = warden.directives.map { |directive| print_directive(warden, directive) }
61
-
62
- printable_types = warden.types.reject(&:default_scalar?)
63
-
64
- type_definitions = printable_types
65
- .sort_by(&:name)
66
- .map { |type| print_type(warden, type) }
67
-
68
- [print_schema_definition(warden, schema)].compact
69
- .concat(directive_definitions)
70
- .concat(type_definitions).join("\n\n")
109
+ def build_blacklist(only, except, introspection:)
110
+ if introspection
111
+ if only
112
+ ->(m, ctx) { !only.call(m, ctx) }
113
+ elsif except
114
+ except
115
+ else
116
+ ->(m, ctx) { false }
117
+ end
118
+ else
119
+ if only
120
+ ->(m, ctx) { !(IS_USER_DEFINED_MEMBER.call(m) && only.call(m, ctx)) }
121
+ elsif except
122
+ ->(m, ctx) { !IS_USER_DEFINED_MEMBER.call(m) || except.call(m, ctx) }
123
+ else
124
+ ->(m, ctx) { !IS_USER_DEFINED_MEMBER.call(m) }
125
+ end
126
+ end
71
127
  end
72
128
 
73
- def print_schema_definition(warden, schema)
129
+ def print_schema_definition
74
130
  if (schema.query.nil? || schema.query.name == 'Query') &&
75
131
  (schema.mutation.nil? || schema.mutation.name == 'Mutation') &&
76
132
  (schema.subscription.nil? || schema.subscription.name == 'Subscription')
@@ -89,11 +145,7 @@ module GraphQL
89
145
  "schema {\n#{operations}}"
90
146
  end
91
147
 
92
- def print_type(warden, type)
93
- TypeKindPrinters::STRATEGIES.fetch(type.kind).print(warden, type)
94
- end
95
-
96
- def print_directive(warden, directive)
148
+ def print_directive(directive)
97
149
  TypeKindPrinters::DirectivePrinter.print(warden, directive)
98
150
  end
99
151
 
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.4.3"
3
+ VERSION = "1.4.4"
4
4
  end
@@ -41,4 +41,20 @@ describe GraphQL::Argument do
41
41
  assert argument.default_value.nil?
42
42
  assert !argument.default_value?
43
43
  end
44
+
45
+ describe "#as, #exposed_as" do
46
+ it "accepts a `as` property to define the arg name at resolve time" do
47
+ argument = GraphQL::Argument.define(name: :favoriteFood, type: GraphQL::STRING_TYPE, as: :favFood)
48
+ assert_equal argument.as, :favFood
49
+ end
50
+
51
+ it "uses `name` or `as` for `expose_as`" do
52
+ arg_1 = GraphQL::Argument.define(name: :favoriteFood, type: GraphQL::STRING_TYPE, as: :favFood)
53
+ assert_equal arg_1.expose_as, "favFood"
54
+ arg_2 = GraphQL::Argument.define(name: :favoriteFood, type: GraphQL::STRING_TYPE)
55
+ assert_equal arg_2.expose_as, "favoriteFood"
56
+ arg_3 = arg_2.redefine { as :ff }
57
+ assert_equal arg_3.expose_as, "ff"
58
+ end
59
+ end
44
60
  end
@@ -46,4 +46,40 @@ describe GraphQL::BaseType do
46
46
  refute_equal obj_edge, obj_2.connection_type
47
47
  end
48
48
  end
49
+
50
+ describe "#to_definition" do
51
+ post_type = GraphQL::ObjectType.define do
52
+ name "Post"
53
+ description "A blog post"
54
+
55
+ field :id, !types.ID
56
+ field :title, !types.String
57
+ field :body, !types.String
58
+ end
59
+
60
+ query_root = GraphQL::ObjectType.define do
61
+ name "Query"
62
+ description "The query root of this schema"
63
+
64
+ field :post do
65
+ type post_type
66
+ resolve ->(obj, args, ctx) { Post.find(args["id"]) }
67
+ end
68
+ end
69
+
70
+ schema = GraphQL::Schema.define(query: query_root)
71
+
72
+ expected = <<TYPE
73
+ # A blog post
74
+ type Post {
75
+ id: ID!
76
+ title: String!
77
+ body: String!
78
+ }
79
+ TYPE
80
+
81
+ it "prints the type definition" do
82
+ assert_equal expected.chomp, post_type.to_definition(schema)
83
+ end
84
+ end
49
85
  end
@@ -78,15 +78,6 @@ describe GraphQL::Field do
78
78
  end
79
79
 
80
80
  describe "#name" do
81
- it "can't be reassigned" do
82
- field = GraphQL::Field.define do
83
- name("something")
84
- end
85
- assert_equal "something", field.name
86
- assert_raises(RuntimeError) { field.name = "somethingelse" }
87
- assert_equal "something", field.name
88
- end
89
-
90
81
  it "must be a string" do
91
82
  dummy_query = GraphQL::ObjectType.define do
92
83
  name "QueryType"
@@ -174,6 +165,47 @@ describe GraphQL::Field do
174
165
  assert_equal 2, int_field_2.arguments.size
175
166
  end
176
167
 
168
+ it "rebuilds when the resolve_proc is default NameResolve" do
169
+ int_field = GraphQL::Field.define do
170
+ name "a"
171
+ end
172
+
173
+ int_field_2 = int_field.redefine(name: "b")
174
+
175
+ object = Struct.new(:a, :b).new(1, 2)
176
+
177
+ assert_equal 1, int_field.resolve_proc.call(object, nil, nil)
178
+ assert_equal 2, int_field_2.resolve_proc.call(object, nil, nil)
179
+ end
180
+
181
+ it "keeps the same resolve_proc when it is not a NameResolve" do
182
+ int_field = GraphQL::Field.define do
183
+ name "a"
184
+ resolve ->(obj, _, _) { 'GraphQL is Kool' }
185
+ end
186
+
187
+ int_field_2 = int_field.redefine(name: "b")
188
+
189
+ assert_equal(
190
+ int_field.resolve_proc.call(nil, nil, nil),
191
+ int_field_2.resolve_proc.call(nil, nil, nil)
192
+ )
193
+ end
194
+
195
+ it "keeps the same resolve_proc when it is a built in property resolve" do
196
+ int_field = GraphQL::Field.define do
197
+ name "a"
198
+ property :c
199
+ end
200
+
201
+ int_field_2 = int_field.redefine(name: "b")
202
+
203
+ object = Struct.new(:a, :b, :c).new(1, 2, 3)
204
+
205
+ assert_equal 3, int_field.resolve_proc.call(object, nil, nil)
206
+ assert_equal 3, int_field_2.resolve_proc.call(object, nil, nil)
207
+ end
208
+
177
209
  it "copies metadata, even out-of-bounds assignments" do
178
210
  int_field = GraphQL::Field.define do
179
211
  metadata(:a, 1)
@@ -13,7 +13,7 @@ describe GraphQL::Query::Arguments do
13
13
  name "TestInput2"
14
14
  argument :a, types.Int
15
15
  argument :b, types.Int
16
- argument :c, !test_input_1
16
+ argument :c, !test_input_1, as: :inputObject
17
17
  end
18
18
 
19
19
  GraphQL::Query::Arguments.new({
@@ -55,7 +55,7 @@ describe GraphQL::Query::Arguments do
55
55
  expected_type_info =[
56
56
  ["a", 1, "Int"],
57
57
  ["b", 2, "Int"],
58
- ["c", { d: 3, e: 4 }, "TestInput1"],
58
+ ["inputObject", { d: 3, e: 4 }, "TestInput1"],
59
59
  ]
60
60
  assert_equal expected_type_info, type_info
61
61
  end
@@ -72,7 +72,7 @@ describe GraphQL::Query::Arguments do
72
72
  expected_hash = {
73
73
  "A" => 1,
74
74
  "B" => 2,
75
- "C" => { d: 3 , e: 4 },
75
+ "INPUTOBJECT" => { d: 3 , e: 4 },
76
76
  }
77
77
  assert_equal expected_hash, new_arguments.to_h
78
78
  end
@@ -97,10 +97,14 @@ describe GraphQL::Query::Arguments do
97
97
  end
98
98
 
99
99
  describe "#[]" do
100
+ it "fetches using specified `as` keyword" do
101
+ assert arguments["inputObject"].is_a?(GraphQL::Query::Arguments)
102
+ end
103
+
100
104
  it "returns the value at that key" do
101
105
  assert_equal 1, arguments["a"]
102
106
  assert_equal 1, arguments[:a]
103
- assert arguments["c"].is_a?(GraphQL::Query::Arguments)
107
+ assert arguments["inputObject"].is_a?(GraphQL::Query::Arguments)
104
108
  end
105
109
 
106
110
  it "returns nil for missing keys" do
@@ -127,7 +131,7 @@ describe GraphQL::Query::Arguments do
127
131
  field :argTest, types.Int do
128
132
  argument :a, types.Int
129
133
  argument :b, types.Int, default_value: 2
130
- argument :c, types.Int
134
+ argument :c, types.Int, as: :specialKeyName
131
135
  argument :d, test_input_type
132
136
  resolve ->(obj, args, ctx) {
133
137
  arg_values_array << args
@@ -146,6 +150,14 @@ describe GraphQL::Query::Arguments do
146
150
  assert_equal false, arguments.key?("f")
147
151
  end
148
152
 
153
+ it "detects keys using `as` to rename an arg at resolve time" do
154
+ schema.execute("{ argTest(c: 1) }")
155
+
156
+ last_args = arg_values.last
157
+
158
+ assert_equal true, last_args.key?(:specialKeyName)
159
+ end
160
+
149
161
  it "works from query literals" do
150
162
  schema.execute("{ argTest(a: 1) }")
151
163
 
@@ -82,6 +82,11 @@ describe GraphQL::Relay::ArrayConnection do
82
82
 
83
83
  result = star_wars_query(query_string, "last" => 2)
84
84
  assert_equal(["Millenium Falcon", "Home One"], get_names(result))
85
+
86
+ result = star_wars_query(query_string, "last" => 10)
87
+ assert_equal(["X-Wing", "Y-Wing", "A-Wing", "Millenium Falcon", "Home One"], get_names(result))
88
+ assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasNextPage"])
89
+ assert_equal(false, result["data"]["rebels"]["ships"]["pageInfo"]["hasPreviousPage"])
85
90
  end
86
91
 
87
92
  it 'handles cursors beyond the bounds of the array' do
@@ -15,6 +15,22 @@ describe GraphQL::Relay::BaseConnection do
15
15
  end
16
16
  end
17
17
 
18
+ describe "#context" do
19
+ module Encoder
20
+ module_function
21
+ def encode(str, nonce: false); str; end
22
+ def decode(str, nonce: false); str; end
23
+ end
24
+
25
+ let(:schema) { OpenStruct.new(cursor_encoder: Encoder) }
26
+ let(:context) { OpenStruct.new(schema: schema) }
27
+
28
+ it "Has public access to the field context" do
29
+ conn = GraphQL::Relay::BaseConnection.new([], {}, context: context)
30
+ assert_equal context, conn.context
31
+ end
32
+ end
33
+
18
34
  describe "#encode / #decode" do
19
35
  module ReverseEncoder
20
36
  module_function
@@ -49,6 +49,36 @@ describe GraphQL::Relay::Mutation do
49
49
  assert_equal "Slave II", result["data"]["introduceShip"]["shipEdge"]["node"]["name"]
50
50
  end
51
51
 
52
+ it "raises when using a generated return type and resolve doesnt return a Hash" do
53
+ bad_mutation = GraphQL::Relay::Mutation.define do
54
+ name 'BadMutation'
55
+ description 'A mutation type that doesnt return a hash'
56
+
57
+ input_field :input, types.String
58
+ return_field :return, types.String
59
+
60
+ resolve ->(_, _, _) {
61
+ # Should have been { return: 'my_bad_return_value' }
62
+ 'my_bad_return_value'
63
+ }
64
+ end
65
+
66
+ root = GraphQL::ObjectType.define do
67
+ name "MutationRoot"
68
+ field :bad, bad_mutation.field
69
+ end
70
+
71
+ schema = GraphQL::Schema.define { mutation(root) }
72
+
73
+ exception = assert_raises do
74
+ puts schema.execute('mutation { bad(input: { input: "graphql" }) { return } }')
75
+ end
76
+
77
+ expected_message = "Expected `my_bad_return_value` to be a Hash."\
78
+ " Return a hash when using `return_field` or specify a custom `return_type`."
79
+ assert_equal expected_message, exception.message
80
+ end
81
+
52
82
  it "returns the result & clientMutationId" do
53
83
  result = star_wars_query(query_string, "clientMutationId" => "1234")
54
84
  expected = {"data" => {
@@ -167,6 +197,70 @@ describe GraphQL::Relay::Mutation do
167
197
  end
168
198
  end
169
199
 
200
+ describe "specifying return interfaces" do
201
+ let(:result_interface) {
202
+ GraphQL::InterfaceType.define do
203
+ name "ResultInterface"
204
+ field :success, !types.Boolean
205
+ field :notice, types.String
206
+ end
207
+ }
208
+
209
+ let(:error_interface) {
210
+ GraphQL::InterfaceType.define do
211
+ name "ErrorInterface"
212
+ field :error, types.String
213
+ end
214
+ }
215
+
216
+ let(:mutation) {
217
+ interfaces = [result_interface, error_interface]
218
+ GraphQL::Relay::Mutation.define do
219
+ name "ReturnTypeWithInterfaceTest"
220
+
221
+ return_field :name, types.String
222
+
223
+ return_interfaces interfaces
224
+
225
+ resolve ->(obj, input, ctx) {
226
+ {
227
+ name: "Type Specific Field",
228
+ success: true,
229
+ notice: "Success Interface Field",
230
+ error: "Error Interface Field"
231
+ }
232
+ }
233
+ end
234
+ }
235
+
236
+ let(:schema) {
237
+ mutation_field = mutation.field
238
+
239
+ mutation_root = GraphQL::ObjectType.define do
240
+ name "Mutation"
241
+ field :custom, mutation_field
242
+ end
243
+
244
+ GraphQL::Schema.define do
245
+ mutation(mutation_root)
246
+ resolve_type ->(obj, ctx) { "not really used" }
247
+ end
248
+ }
249
+
250
+ it 'makes the mutation type implement the interfaces' do
251
+ assert_equal [result_interface, error_interface], mutation.return_type.interfaces
252
+ end
253
+
254
+ it "returns interface values and specific ones" do
255
+ result = schema.execute('mutation { custom(input: {clientMutationId: "123"}) { name, success, notice, error, clientMutationId } }')
256
+ assert_equal "Type Specific Field", result["data"]["custom"]["name"]
257
+ assert_equal "Success Interface Field", result["data"]["custom"]["notice"]
258
+ assert_equal true, result["data"]["custom"]["success"]
259
+ assert_equal "Error Interface Field", result["data"]["custom"]["error"]
260
+ assert_equal "123", result["data"]["custom"]["clientMutationId"]
261
+ end
262
+ end
263
+
170
264
  describe "handling errors" do
171
265
  it "supports returning an error in resolve" do
172
266
  result = star_wars_query(query_string, "clientMutationId" => "5678", "shipName" => "Millennium Falcon")
@@ -9,6 +9,45 @@ describe GraphQL::Relay::Node do
9
9
  end
10
10
 
11
11
  describe ".field" do
12
+ describe "with custom resolver" do
13
+ it "executes the custom resolve instead of relay default" do
14
+ id = "resolver_is_hardcoded_so_this_does_not_matter"
15
+
16
+ result = star_wars_query(%|{
17
+ nodeWithCustomResolver(id: "#{id}") {
18
+ id,
19
+ ... on Faction {
20
+ name
21
+ ships(first: 1) {
22
+ edges {
23
+ node {
24
+ name
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }|)
31
+
32
+ expected = {"data" => {
33
+ "nodeWithCustomResolver"=>{
34
+ "id"=>"RmFjdGlvbi0x",
35
+ "name"=>"Alliance to Restore the Republic",
36
+ "ships"=>{
37
+ "edges"=>[
38
+ {"node"=>{
39
+ "name" => "X-Wing"
40
+ }
41
+ }
42
+ ]
43
+ }
44
+ }
45
+ }}
46
+
47
+ assert_equal(expected, result)
48
+ end
49
+ end
50
+
12
51
  describe "Custom global IDs" do
13
52
  before do
14
53
  # TODO: make the schema eager-load so we can remove this
@@ -92,6 +131,55 @@ describe GraphQL::Relay::Node do
92
131
  end
93
132
 
94
133
  describe ".plural_identifying_field" do
134
+ describe "with custom resolver" do
135
+ it "executes the custom resolve instead of relay default" do
136
+ id = ["resolver_is_hardcoded_so_this_does_not_matter", "another_id"]
137
+
138
+ result = star_wars_query(%|{
139
+ nodesWithCustomResolver(ids: ["#{id[0]}", "#{id[1]}"]) {
140
+ id,
141
+ ... on Faction {
142
+ name
143
+ ships(first: 1) {
144
+ edges {
145
+ node {
146
+ name
147
+ }
148
+ }
149
+ }
150
+ }
151
+ }
152
+ }|)
153
+
154
+ expected = {
155
+ "data" => {
156
+ "nodesWithCustomResolver" => [
157
+ {
158
+ "id" => "RmFjdGlvbi0x",
159
+ "name" => "Alliance to Restore the Republic",
160
+ "ships" => {
161
+ "edges"=>[
162
+ { "node" => { "name" => "X-Wing" } }
163
+ ]
164
+ }
165
+ },
166
+ {
167
+ "id" => "RmFjdGlvbi0y",
168
+ "name" => "Galactic Empire",
169
+ "ships" => {
170
+ "edges"=>[
171
+ { "node" => { "name" => "TIE Fighter" } }
172
+ ]
173
+ }
174
+ },
175
+ ]
176
+ }
177
+ }
178
+
179
+ assert_equal(expected, result)
180
+ end
181
+ end
182
+
95
183
  it 'finds objects by ids' do
96
184
  id = GraphQL::Schema::UniqueWithinType.encode("Faction", "1")
97
185
  id2 = GraphQL::Schema::UniqueWithinType.encode("Faction", "2")
@@ -97,6 +97,11 @@ describe GraphQL::Relay::RelationConnection do
97
97
 
98
98
  result = star_wars_query(query_string, "last" => 2)
99
99
  assert_equal(["Shield Generator", "Headquarters"], get_names(result))
100
+
101
+ result = star_wars_query(query_string, "last" => 10)
102
+ assert_equal(["Death Star", "Shield Generator", "Headquarters"], get_names(result))
103
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasNextPage"])
104
+ assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"])
100
105
  end
101
106
 
102
107
  it 'handles cursors beyond the bounds of the array' do
@@ -590,4 +590,20 @@ SCHEMA
590
590
  context = { names: ["Varied", "Image", "Sub"] }
591
591
  assert_equal expected.chomp, schema.to_definition(context: context, except: except_filter)
592
592
  end
593
+
594
+ describe "#print_type" do
595
+ it "returns the type schema as a string" do
596
+ expected = <<SCHEMA
597
+ # A blog post
598
+ type Post {
599
+ id: ID!
600
+ title: String!
601
+ body: String!
602
+ comments: [Comment!]
603
+ comments_count: Int! @deprecated(reason: \"Use \\\"comments\\\".\")
604
+ }
605
+ SCHEMA
606
+ assert_equal expected.chomp, GraphQL::Schema::Printer.new(schema).print_type(schema.types['Post'])
607
+ end
608
+ end
593
609
  end
@@ -328,9 +328,9 @@ module Dummy
328
328
  description "The root for mutations in this schema"
329
329
  field :pushValue, !types[!types.Int] do
330
330
  description("Push a value onto a global array :D")
331
- argument :value, !types.Int
331
+ argument :value, !types.Int, as: :val
332
332
  resolve ->(o, args, ctx) {
333
- GLOBAL_VALUES << args[:value]
333
+ GLOBAL_VALUES << args[:val]
334
334
  GLOBAL_VALUES
335
335
  }
336
336
  end
@@ -8,6 +8,8 @@ module StarWars
8
8
  interfaces [GraphQL::Relay::Node.interface]
9
9
  global_id_field :id
10
10
  field :name, types.String
11
+ # Test cyclical connection types:
12
+ connection :ships, Ship.connection_type
11
13
  end
12
14
 
13
15
  BaseType = GraphQL::ObjectType.define do
@@ -218,7 +220,13 @@ module StarWars
218
220
  end
219
221
 
220
222
  field :node, GraphQL::Relay::Node.field
223
+ field :nodeWithCustomResolver, GraphQL::Relay::Node.field(
224
+ resolve: ->(_, _, _) { StarWars::DATA["Faction"]["1"] }
225
+ )
221
226
  field :nodes, GraphQL::Relay::Node.plural_field
227
+ field :nodesWithCustomResolver, GraphQL::Relay::Node.plural_field(
228
+ resolve: ->(_, _, _) { [StarWars::DATA["Faction"]["1"], StarWars::DATA["Faction"]["2"]] }
229
+ )
222
230
  end
223
231
 
224
232
  MutationType = GraphQL::ObjectType.define do
@@ -0,0 +1,5 @@
1
+ Types::BirdType = GraphQL::InterfaceType.define do
2
+ name "Bird"
3
+ field :wingspan, !types.Int
4
+ field :foliage, types[Types::ColorType]
5
+ end
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: 1.4.3
4
+ version: 1.4.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-08 00:00:00.000000000 Z
11
+ date: 2017-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: codeclimate-test-reporter
@@ -553,6 +553,7 @@ files:
553
553
  - spec/support/star_wars/data.rb
554
554
  - spec/support/star_wars/schema.rb
555
555
  - spec/support/static_validation_helpers.rb
556
+ - spec/tmp/app/graphql/types/bird_type.rb
556
557
  homepage: http://github.com/rmosolgo/graphql-ruby
557
558
  licenses:
558
559
  - MIT
@@ -573,7 +574,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
573
574
  version: '0'
574
575
  requirements: []
575
576
  rubyforge_project:
576
- rubygems_version: 2.5.1
577
+ rubygems_version: 2.6.10
577
578
  signing_key:
578
579
  specification_version: 4
579
580
  summary: A GraphQL server implementation for Ruby
@@ -682,3 +683,4 @@ test_files:
682
683
  - spec/support/star_wars/data.rb
683
684
  - spec/support/star_wars/schema.rb
684
685
  - spec/support/static_validation_helpers.rb
686
+ - spec/tmp/app/graphql/types/bird_type.rb