graphql 0.19.2 → 0.19.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/input_object_type.rb +1 -1
- data/lib/graphql/introspection/introspection_query.rb +16 -0
- data/lib/graphql/query.rb +0 -1
- data/lib/graphql/query/arguments.rb +12 -5
- data/lib/graphql/query/literal_input.rb +3 -3
- data/lib/graphql/relay/connection_type.rb +2 -1
- data/lib/graphql/relay/edge_type.rb +3 -2
- data/lib/graphql/relay/mutation.rb +81 -19
- data/lib/graphql/relay/node.rb +4 -3
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +4 -2
- data/lib/graphql/static_validation/type_stack.rb +21 -15
- data/lib/graphql/version.rb +1 -1
- data/readme.md +0 -1
- data/spec/graphql/introspection/introspection_query_spec.rb +48 -0
- data/spec/graphql/query/arguments_spec.rb +42 -1
- data/spec/graphql/relay/mutation_spec.rb +46 -0
- data/spec/graphql/static_validation/rules/variables_are_input_types_spec.rb +7 -0
- data/spec/support/star_wars_schema.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49959ced421e4d175a2ab09abb1f70046fbeecc0
|
4
|
+
data.tar.gz: e9eb998841dff1511ea8d06f6482d779e39c0d4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f595c5ff5a2b3062f17ec9bb038de9f2cffb550308a34a61cd8ab7c3f65a5cb878c557ebcc0aa50c31c48808be1a5a4d61ec6d609be7a14b62044809b5d4c46
|
7
|
+
data.tar.gz: 1a0452062ac5a5de8ef7102d28d2c1395e1e1cc16a379c87e844c35d6f7beaa3b036932695a8a55c506218185a05aa15425820fe34ef0a428e54ae95e828305c
|
data/lib/graphql/query.rb
CHANGED
@@ -6,10 +6,12 @@ module GraphQL
|
|
6
6
|
class Arguments
|
7
7
|
extend Forwardable
|
8
8
|
|
9
|
-
def initialize(values)
|
9
|
+
def initialize(values, argument_definitions:)
|
10
10
|
@original_values = values
|
11
11
|
@argument_values = values.inject({}) do |memo, (inner_key, inner_value)|
|
12
|
-
|
12
|
+
string_key = inner_key.to_s
|
13
|
+
arg_defn = argument_definitions[string_key]
|
14
|
+
memo[string_key] = wrap_value(inner_value, arg_defn.type)
|
13
15
|
memo
|
14
16
|
end
|
15
17
|
end
|
@@ -36,12 +38,17 @@ module GraphQL
|
|
36
38
|
|
37
39
|
private
|
38
40
|
|
39
|
-
def wrap_value(value)
|
41
|
+
def wrap_value(value, arg_defn_type)
|
40
42
|
case value
|
41
43
|
when Array
|
42
|
-
value.map { |item| wrap_value(item) }
|
44
|
+
value.map { |item| wrap_value(item, arg_defn_type.of_type) }
|
43
45
|
when Hash
|
44
|
-
|
46
|
+
if arg_defn_type.unwrap.kind.input_object?
|
47
|
+
self.class.new(value, argument_definitions: arg_defn_type.arguments)
|
48
|
+
else
|
49
|
+
# It may be a custom scalar that coerces to a Hash
|
50
|
+
value
|
51
|
+
end
|
45
52
|
else
|
46
53
|
value
|
47
54
|
end
|
@@ -36,7 +36,7 @@ module GraphQL
|
|
36
36
|
values_hash[arg_name] = arg_value
|
37
37
|
end
|
38
38
|
end
|
39
|
-
GraphQL::Query::Arguments.new(values_hash)
|
39
|
+
GraphQL::Query::Arguments.new(values_hash, argument_definitions: argument_defns)
|
40
40
|
end
|
41
41
|
|
42
42
|
module LiteralKindCoercers
|
@@ -60,7 +60,7 @@ module GraphQL
|
|
60
60
|
def self.coerce(value, type, variables)
|
61
61
|
hash = {}
|
62
62
|
value.arguments.each do |arg|
|
63
|
-
field_type = type.
|
63
|
+
field_type = type.arguments[arg.name].type
|
64
64
|
hash[arg.name] = LiteralInput.coerce(field_type, arg.value, variables)
|
65
65
|
end
|
66
66
|
type.input_fields.each do |arg_name, arg_defn|
|
@@ -71,7 +71,7 @@ module GraphQL
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
74
|
-
Arguments.new(hash)
|
74
|
+
Arguments.new(hash, argument_definitions: type.arguments)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -10,11 +10,12 @@ module GraphQL
|
|
10
10
|
connection_type = ObjectType.define do
|
11
11
|
name(connection_type_name)
|
12
12
|
field :edges, types[edge_type] do
|
13
|
+
description "A list of edges."
|
13
14
|
resolve -> (obj, args, ctx) {
|
14
15
|
obj.edge_nodes.map { |item| edge_class.new(item, obj) }
|
15
16
|
}
|
16
17
|
end
|
17
|
-
field :pageInfo, !PageInfo, property: :page_info
|
18
|
+
field :pageInfo, !PageInfo, "Information to aid in pagination.", property: :page_info
|
18
19
|
block && instance_eval(&block)
|
19
20
|
end
|
20
21
|
|
@@ -4,8 +4,9 @@ module GraphQL
|
|
4
4
|
def self.create_type(wrapped_type, name: nil, &block)
|
5
5
|
GraphQL::ObjectType.define do
|
6
6
|
name("#{wrapped_type.name}Edge")
|
7
|
-
|
8
|
-
field :
|
7
|
+
description "An edge in a connection."
|
8
|
+
field :node, wrapped_type, "The item at the end of the edge."
|
9
|
+
field :cursor, !types.String, "A cursor for use in pagination."
|
9
10
|
block && instance_eval(&block)
|
10
11
|
end
|
11
12
|
end
|
@@ -51,11 +51,12 @@ module GraphQL
|
|
51
51
|
include GraphQL::Define::InstanceDefinable
|
52
52
|
accepts_definitions(
|
53
53
|
:name, :description, :resolve,
|
54
|
+
:return_type,
|
54
55
|
input_field: GraphQL::Define::AssignArgument,
|
55
56
|
return_field: GraphQL::Define::AssignObjectField,
|
56
57
|
)
|
57
58
|
lazy_defined_attr_accessor :name, :description
|
58
|
-
lazy_defined_attr_accessor :fields, :arguments
|
59
|
+
lazy_defined_attr_accessor :fields, :arguments, :return_type
|
59
60
|
|
60
61
|
# For backwards compat, but do we need this separate API?
|
61
62
|
alias :return_fields :fields
|
@@ -64,21 +65,32 @@ module GraphQL
|
|
64
65
|
def initialize
|
65
66
|
@fields = {}
|
66
67
|
@arguments = {}
|
68
|
+
@has_generated_return_type = false
|
67
69
|
end
|
68
70
|
|
69
|
-
def
|
71
|
+
def has_generated_return_type?
|
72
|
+
# Trigger the generation of the return type, if it is dynamically generated:
|
73
|
+
return_type
|
74
|
+
@has_generated_return_type
|
75
|
+
end
|
76
|
+
|
77
|
+
def resolve=(new_resolve_proc)
|
70
78
|
ensure_defined
|
71
|
-
|
79
|
+
|
80
|
+
resolve_arity = get_arity(new_resolve_proc)
|
81
|
+
if resolve_arity == 2
|
82
|
+
warn("Mutation#resolve functions should be defined with three arguments: (root_obj, input, context). Two-argument mutation resolves are deprecated.")
|
83
|
+
new_resolve_proc = DeprecatedMutationResolve.new(new_resolve_proc)
|
84
|
+
end
|
85
|
+
|
86
|
+
@resolve_proc = MutationResolve.new(self, new_resolve_proc, wrap_result: has_generated_return_type?)
|
72
87
|
end
|
73
88
|
|
74
89
|
def field
|
75
90
|
@field ||= begin
|
76
91
|
ensure_defined
|
77
92
|
relay_mutation = self
|
78
|
-
field_resolve_proc =
|
79
|
-
results_hash = @resolve_proc.call(args[:input], ctx)
|
80
|
-
Result.new(arguments: args, result: results_hash)
|
81
|
-
}
|
93
|
+
field_resolve_proc = @resolve_proc
|
82
94
|
GraphQL::Field.define do
|
83
95
|
type(relay_mutation.return_type)
|
84
96
|
description(relay_mutation.description)
|
@@ -90,13 +102,14 @@ module GraphQL
|
|
90
102
|
end
|
91
103
|
|
92
104
|
def return_type
|
105
|
+
ensure_defined
|
93
106
|
@return_type ||= begin
|
94
|
-
|
107
|
+
@has_generated_return_type = true
|
95
108
|
relay_mutation = self
|
96
109
|
GraphQL::ObjectType.define do
|
97
110
|
name("#{relay_mutation.name}Payload")
|
98
111
|
description("Autogenerated return type of #{relay_mutation.name}")
|
99
|
-
field :clientMutationId, types.String, "A unique identifier for the client performing the mutation."
|
112
|
+
field :clientMutationId, types.String, "A unique identifier for the client performing the mutation.", property: :client_mutation_id
|
100
113
|
relay_mutation.return_fields.each do |name, field_obj|
|
101
114
|
field name, field: field_obj
|
102
115
|
end
|
@@ -121,22 +134,71 @@ module GraphQL
|
|
121
134
|
end
|
122
135
|
end
|
123
136
|
|
137
|
+
def result_class
|
138
|
+
@result_class ||= begin
|
139
|
+
ensure_defined
|
140
|
+
Result.define_subclass(self)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def get_arity(callable)
|
147
|
+
case callable
|
148
|
+
when Proc
|
149
|
+
callable.arity
|
150
|
+
else
|
151
|
+
callable.method(:call).arity
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Use this when the mutation's return type was generated from `return_field`s.
|
156
|
+
# It delegates field lookups to the hash returned from `resolve`.
|
124
157
|
class Result
|
125
|
-
attr_reader :
|
126
|
-
def initialize(
|
127
|
-
@
|
128
|
-
|
158
|
+
attr_reader :client_mutation_id
|
159
|
+
def initialize(client_mutation_id:, result:)
|
160
|
+
@client_mutation_id = client_mutation_id
|
161
|
+
result.each do |key, value|
|
162
|
+
self.public_send("#{key}=", value)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class << self
|
167
|
+
attr_accessor :mutation
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.define_subclass(mutation_defn)
|
171
|
+
subclass = Class.new(self) do
|
172
|
+
attr_accessor(*mutation_defn.return_type.all_fields.map(&:name))
|
173
|
+
self.mutation = mutation_defn
|
174
|
+
end
|
175
|
+
subclass
|
129
176
|
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class DeprecatedMutationResolve
|
180
|
+
def initialize(two_argument_resolve)
|
181
|
+
@two_argument_resolve = two_argument_resolve
|
182
|
+
end
|
183
|
+
|
184
|
+
def call(obj, args, ctx)
|
185
|
+
@two_argument_resolve.call(args[:input], ctx)
|
186
|
+
end
|
187
|
+
end
|
130
188
|
|
131
|
-
|
132
|
-
|
189
|
+
class MutationResolve
|
190
|
+
def initialize(mutation, resolve, wrap_result:)
|
191
|
+
@mutation = mutation
|
192
|
+
@resolve = resolve
|
193
|
+
@wrap_result = wrap_result
|
133
194
|
end
|
134
195
|
|
135
|
-
def
|
136
|
-
|
137
|
-
|
196
|
+
def call(obj, args, ctx)
|
197
|
+
mutation_result = @resolve.call(obj, args[:input], ctx)
|
198
|
+
if @wrap_result
|
199
|
+
@mutation.result_class.new(client_mutation_id: args[:input][:clientMutationId], result: mutation_result)
|
138
200
|
else
|
139
|
-
|
201
|
+
mutation_result
|
140
202
|
end
|
141
203
|
end
|
142
204
|
end
|
data/lib/graphql/relay/node.rb
CHANGED
@@ -9,8 +9,8 @@ module GraphQL
|
|
9
9
|
# _may_ be modified.
|
10
10
|
node_field = GraphQL::Field.define do
|
11
11
|
type(GraphQL::Relay::Node.interface)
|
12
|
-
description("Fetches an object given its ID")
|
13
|
-
argument(:id, !types.ID, "ID of the object")
|
12
|
+
description("Fetches an object given its ID.")
|
13
|
+
argument(:id, !types.ID, "ID of the object.")
|
14
14
|
resolve(GraphQL::Relay::Node::FindNode)
|
15
15
|
end
|
16
16
|
|
@@ -24,7 +24,8 @@ module GraphQL
|
|
24
24
|
def self.interface
|
25
25
|
@interface ||= GraphQL::InterfaceType.define do
|
26
26
|
name "Node"
|
27
|
-
|
27
|
+
description "An object with an ID."
|
28
|
+
field :id, !types.ID, "ID of the object."
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
@@ -3,7 +3,7 @@ module GraphQL
|
|
3
3
|
# Wrap a Connection and expose its page info
|
4
4
|
PageInfo = GraphQL::ObjectType.define do
|
5
5
|
name("PageInfo")
|
6
|
-
description("
|
6
|
+
description("Information about pagination in a connection.")
|
7
7
|
field :hasNextPage, !types.Boolean, "Indicates if there are more pages to fetch", property: :has_next_page
|
8
8
|
field :hasPreviousPage, !types.Boolean, "Indicates if there are any pages prior to the current page", property: :has_previous_page
|
9
9
|
field :startCursor, types.String, "When paginating backwards, the cursor to continue", property: :start_cursor
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
21
21
|
return GraphQL::Language::Visitor::SKIP
|
22
22
|
end
|
23
23
|
|
24
|
-
field =
|
24
|
+
field = context.schema.get_field(parent_type, ast_field.name)
|
25
25
|
if field.nil?
|
26
26
|
context.errors << message("Field '#{ast_field.name}' doesn't exist on type '#{parent_type.name}'", ast_field, context: context)
|
27
27
|
return GraphQL::Language::Visitor::SKIP
|
@@ -13,8 +13,10 @@ module GraphQL
|
|
13
13
|
|
14
14
|
def validate_is_input_type(node, context)
|
15
15
|
type_name = get_type_name(node.type)
|
16
|
-
type = context.schema.types
|
17
|
-
if
|
16
|
+
type = context.schema.types.fetch(type_name, nil)
|
17
|
+
if type.nil?
|
18
|
+
context.errors << message("#{type_name} isn't a defined input type (on $#{node.name})", node, context: context)
|
19
|
+
elsif !type.kind.input?
|
18
20
|
context.errors << message("#{type.name} isn't a valid input type (on $#{node.name})", node, context: context)
|
19
21
|
end
|
20
22
|
end
|
@@ -48,15 +48,13 @@ module GraphQL
|
|
48
48
|
private
|
49
49
|
|
50
50
|
# Look up strategies by name and use singleton instance to push and pop
|
51
|
-
PUSH_STRATEGIES = Hash.new
|
52
|
-
|
53
|
-
def self.get_strategy_for_node_class(node_class)
|
54
|
-
node_class_name = node_class.name.split("::").last
|
51
|
+
PUSH_STRATEGIES = Hash.new do |hash, key|
|
52
|
+
node_class_name = key.name.split("::").last
|
55
53
|
strategy_key = "#{node_class_name}Strategy"
|
56
|
-
const_defined?(strategy_key) ? const_get(strategy_key)
|
54
|
+
hash[key] = const_defined?(strategy_key) ? const_get(strategy_key) : NullStrategy
|
57
55
|
end
|
58
56
|
|
59
|
-
|
57
|
+
module FragmentWithTypeStrategy
|
60
58
|
def push(stack, node)
|
61
59
|
object_type = if node.type
|
62
60
|
stack.schema.types.fetch(node.type, nil)
|
@@ -76,19 +74,24 @@ module GraphQL
|
|
76
74
|
end
|
77
75
|
end
|
78
76
|
|
79
|
-
|
77
|
+
module FragmentDefinitionStrategy
|
78
|
+
extend FragmentWithTypeStrategy
|
79
|
+
module_function
|
80
80
|
def push_path_member(stack, node)
|
81
81
|
stack.path.push("fragment #{node.name}")
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
|
85
|
+
module InlineFragmentStrategy
|
86
|
+
extend FragmentWithTypeStrategy
|
87
|
+
module_function
|
86
88
|
def push_path_member(stack, node)
|
87
89
|
stack.path.push("...#{node.type ? " on #{node.type}" : ""}")
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
91
|
-
|
93
|
+
module OperationDefinitionStrategy
|
94
|
+
module_function
|
92
95
|
def push(stack, node)
|
93
96
|
# eg, QueryType, MutationType
|
94
97
|
object_type = stack.schema.root_type_for_operation(node.operation_type)
|
@@ -102,7 +105,8 @@ module GraphQL
|
|
102
105
|
end
|
103
106
|
end
|
104
107
|
|
105
|
-
|
108
|
+
module FieldStrategy
|
109
|
+
module_function
|
106
110
|
def push(stack, node)
|
107
111
|
parent_type = stack.object_types.last
|
108
112
|
parent_type = parent_type.unwrap
|
@@ -125,7 +129,8 @@ module GraphQL
|
|
125
129
|
end
|
126
130
|
end
|
127
131
|
|
128
|
-
|
132
|
+
module DirectiveStrategy
|
133
|
+
module_function
|
129
134
|
def push(stack, node)
|
130
135
|
directive_defn = stack.schema.directives[node.name]
|
131
136
|
stack.directive_definitions.push(directive_defn)
|
@@ -136,7 +141,8 @@ module GraphQL
|
|
136
141
|
end
|
137
142
|
end
|
138
143
|
|
139
|
-
|
144
|
+
module ArgumentStrategy
|
145
|
+
module_function
|
140
146
|
# Push `argument_defn` onto the stack.
|
141
147
|
# It's possible that `argument_defn` will be nil.
|
142
148
|
# Push it anyways so `pop` has something to pop.
|
@@ -165,7 +171,8 @@ module GraphQL
|
|
165
171
|
end
|
166
172
|
end
|
167
173
|
|
168
|
-
|
174
|
+
module FragmentSpreadStrategy
|
175
|
+
module_function
|
169
176
|
def push(stack, node)
|
170
177
|
stack.path.push("... #{node.name}")
|
171
178
|
end
|
@@ -176,8 +183,7 @@ module GraphQL
|
|
176
183
|
end
|
177
184
|
|
178
185
|
# A no-op strategy (don't handle this node)
|
179
|
-
|
180
|
-
def self.new; self; end
|
186
|
+
module NullStrategy
|
181
187
|
def self.push(stack, node); end
|
182
188
|
def self.pop(stack, node); end
|
183
189
|
end
|
data/lib/graphql/version.rb
CHANGED
data/readme.md
CHANGED
@@ -148,4 +148,3 @@ If you're building a backend for [Relay](http://facebook.github.io/relay/), you'
|
|
148
148
|
- Revisit guides, maybe split them into smaller, more specific pages
|
149
149
|
- Put guide titles into the `<title />`
|
150
150
|
- Document encrypted & versioned cursors
|
151
|
-
- Eager load `Schema#types` after `.define { ... }`
|
@@ -7,4 +7,52 @@ describe "GraphQL::Introspection::INTROSPECTION_QUERY" do
|
|
7
7
|
it "runs" do
|
8
8
|
assert(result["data"])
|
9
9
|
end
|
10
|
+
|
11
|
+
it "handles deeply nested (<= 7) schemas" do
|
12
|
+
query_type = GraphQL::ObjectType.define do
|
13
|
+
name "DeepQuery"
|
14
|
+
field :foo do
|
15
|
+
type !GraphQL::ListType.new(
|
16
|
+
of_type: !GraphQL::ListType.new(
|
17
|
+
of_type: !GraphQL::ListType.new(
|
18
|
+
of_type: GraphQL::FLOAT_TYPE
|
19
|
+
)
|
20
|
+
)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
deep_schema = GraphQL::Schema.define do
|
26
|
+
query query_type
|
27
|
+
end
|
28
|
+
|
29
|
+
result = deep_schema.execute(query_string)
|
30
|
+
assert(GraphQL::Schema::Loader.load(result))
|
31
|
+
end
|
32
|
+
|
33
|
+
it "doesn't handle too deeply nested (< 8) schemas" do
|
34
|
+
query_type = GraphQL::ObjectType.define do
|
35
|
+
name "DeepQuery"
|
36
|
+
field :foo do
|
37
|
+
type !GraphQL::ListType.new(
|
38
|
+
of_type: !GraphQL::ListType.new(
|
39
|
+
of_type: !GraphQL::ListType.new(
|
40
|
+
of_type: !GraphQL::ListType.new(
|
41
|
+
of_type: GraphQL::FLOAT_TYPE
|
42
|
+
)
|
43
|
+
)
|
44
|
+
)
|
45
|
+
)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
deep_schema = GraphQL::Schema.define do
|
50
|
+
query query_type
|
51
|
+
end
|
52
|
+
|
53
|
+
result = deep_schema.execute(query_string)
|
54
|
+
assert_raises(KeyError) {
|
55
|
+
GraphQL::Schema::Loader.load(result)
|
56
|
+
}
|
57
|
+
end
|
10
58
|
end
|
@@ -1,7 +1,29 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe GraphQL::Query::Arguments do
|
4
|
-
let(:arguments) {
|
4
|
+
let(:arguments) {
|
5
|
+
test_input_1 = GraphQL::InputObjectType.define do
|
6
|
+
name "TestInput1"
|
7
|
+
argument :d, types.Int
|
8
|
+
argument :e, types.Int
|
9
|
+
end
|
10
|
+
|
11
|
+
test_input_2 = GraphQL::InputObjectType.define do
|
12
|
+
name "TestInput2"
|
13
|
+
argument :a, types.Int
|
14
|
+
argument :b, types.Int
|
15
|
+
argument :c, !test_input_1
|
16
|
+
end
|
17
|
+
|
18
|
+
GraphQL::Query::Arguments.new({
|
19
|
+
a: 1,
|
20
|
+
b: 2,
|
21
|
+
c: GraphQL::Query::Arguments.new({
|
22
|
+
d: 3,
|
23
|
+
e: 4,
|
24
|
+
}, argument_definitions: test_input_1.arguments),
|
25
|
+
}, argument_definitions: test_input_2.arguments)
|
26
|
+
}
|
5
27
|
|
6
28
|
it "returns keys as strings" do
|
7
29
|
assert_equal(["a", "b", "c"], arguments.keys)
|
@@ -23,6 +45,25 @@ describe GraphQL::Query::Arguments do
|
|
23
45
|
assert_equal({ a: 1, b: 2, c: { d: 3, e: 4 } }, arguments.to_h)
|
24
46
|
end
|
25
47
|
|
48
|
+
describe "nested hashes" do
|
49
|
+
let(:input_type) {
|
50
|
+
test_input_type = GraphQL::InputObjectType.define do
|
51
|
+
name "TestInput"
|
52
|
+
argument :a, types.Int
|
53
|
+
argument :b, test_input_type
|
54
|
+
argument :c, types.Int # will be a hash
|
55
|
+
end
|
56
|
+
}
|
57
|
+
it "wraps input objects, but not other hashes" do
|
58
|
+
args = GraphQL::Query::Arguments.new(
|
59
|
+
{a: 1, b: {a: 2}, c: {a: 3}},
|
60
|
+
argument_definitions: input_type.arguments
|
61
|
+
)
|
62
|
+
assert args["b"].is_a?(GraphQL::Query::Arguments)
|
63
|
+
assert args["c"].is_a?(Hash)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
26
67
|
describe "#key?" do
|
27
68
|
let(:arg_values) { [] }
|
28
69
|
let(:schema) {
|
@@ -56,5 +56,51 @@ describe GraphQL::Relay::Mutation do
|
|
56
56
|
assert_equal IntroduceShipMutation, IntroduceShipMutation.field.mutation
|
57
57
|
assert_equal IntroduceShipMutation, IntroduceShipMutation.return_type.mutation
|
58
58
|
assert_equal IntroduceShipMutation, IntroduceShipMutation.input_type.mutation
|
59
|
+
assert_equal IntroduceShipMutation, IntroduceShipMutation.result_class.mutation
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "providing a return type" do
|
63
|
+
let(:custom_return_type) {
|
64
|
+
GraphQL::ObjectType.define do
|
65
|
+
name "CustomReturnType"
|
66
|
+
field :name, types.String
|
67
|
+
end
|
68
|
+
}
|
69
|
+
|
70
|
+
let(:mutation) {
|
71
|
+
custom_type = custom_return_type
|
72
|
+
GraphQL::Relay::Mutation.define do
|
73
|
+
name "CustomReturnTypeTest"
|
74
|
+
return_type custom_type
|
75
|
+
resolve -> (input, ctx) {
|
76
|
+
OpenStruct.new(name: "Custom Return Type Test")
|
77
|
+
}
|
78
|
+
end
|
79
|
+
}
|
80
|
+
|
81
|
+
let(:schema) {
|
82
|
+
mutation_field = mutation.field
|
83
|
+
|
84
|
+
mutation_root = GraphQL::ObjectType.define do
|
85
|
+
name "Mutation"
|
86
|
+
field :custom, mutation_field
|
87
|
+
end
|
88
|
+
|
89
|
+
GraphQL::Schema.define do
|
90
|
+
mutation(mutation_root)
|
91
|
+
end
|
92
|
+
}
|
93
|
+
|
94
|
+
it "uses the provided type" do
|
95
|
+
assert_equal custom_return_type, mutation.return_type
|
96
|
+
assert_equal custom_return_type, mutation.field.type
|
97
|
+
|
98
|
+
result = schema.execute("mutation { custom(input: {}) { name } }")
|
99
|
+
assert_equal "Custom Return Type Test", result["data"]["custom"]["name"]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "doesn't get a mutation in the metadata" do
|
103
|
+
assert_equal nil, custom_return_type.mutation
|
104
|
+
end
|
59
105
|
end
|
60
106
|
end
|
@@ -10,6 +10,7 @@ describe GraphQL::StaticValidation::VariablesAreInputTypes do
|
|
10
10
|
$interface: AnimalProduct!,
|
11
11
|
$object: Milk = 1,
|
12
12
|
$objects: [Cheese]!,
|
13
|
+
$unknownType: Nonsense,
|
13
14
|
) {
|
14
15
|
cheese(id: $id) { source }
|
15
16
|
__type(name: $str) { name }
|
@@ -34,5 +35,11 @@ describe GraphQL::StaticValidation::VariablesAreInputTypes do
|
|
34
35
|
"locations"=>[{"line"=>7, "column"=>7}],
|
35
36
|
"fields"=>["query getCheese"],
|
36
37
|
})
|
38
|
+
|
39
|
+
assert_includes(errors, {
|
40
|
+
"message"=>"Nonsense isn't a defined input type (on $unknownType)",
|
41
|
+
"locations"=>[{"line"=>8, "column"=>7}],
|
42
|
+
"fields"=>["query getCheese"],
|
43
|
+
})
|
37
44
|
end
|
38
45
|
end
|
@@ -155,7 +155,7 @@ IntroduceShipMutation = GraphQL::Relay::Mutation.define do
|
|
155
155
|
return_field :faction, Faction
|
156
156
|
|
157
157
|
# Here's the mutation operation:
|
158
|
-
resolve -> (inputs, ctx) {
|
158
|
+
resolve -> (root_obj, inputs, ctx) {
|
159
159
|
faction_id = inputs["factionId"]
|
160
160
|
ship = STAR_WARS_DATA.create_ship(inputs["shipName"], faction_id)
|
161
161
|
faction = STAR_WARS_DATA["Faction"][faction_id]
|
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.19.
|
4
|
+
version: 0.19.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-10-
|
11
|
+
date: 2016-10-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: codeclimate-test-reporter
|
@@ -550,7 +550,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
550
550
|
version: '0'
|
551
551
|
requirements: []
|
552
552
|
rubyforge_project:
|
553
|
-
rubygems_version: 2.
|
553
|
+
rubygems_version: 2.5.1
|
554
554
|
signing_key:
|
555
555
|
specification_version: 4
|
556
556
|
summary: A GraphQL server implementation for Ruby
|