graphql 0.19.2 → 0.19.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 +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
|