graphql 1.4.3 → 1.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql/argument.rb +13 -4
- data/lib/graphql/base_type.rb +10 -0
- data/lib/graphql/define/assign_argument.rb +2 -1
- data/lib/graphql/field.rb +7 -8
- data/lib/graphql/query/arguments.rb +3 -2
- data/lib/graphql/relay/base_connection.rb +1 -1
- data/lib/graphql/relay/connection_type.rb +7 -8
- data/lib/graphql/relay/mutation.rb +19 -2
- data/lib/graphql/relay/node.rb +4 -4
- data/lib/graphql/relay/relation_connection.rb +1 -1
- data/lib/graphql/schema/printer.rb +89 -37
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/argument_spec.rb +16 -0
- data/spec/graphql/base_type_spec.rb +36 -0
- data/spec/graphql/field_spec.rb +41 -9
- data/spec/graphql/query/arguments_spec.rb +17 -5
- data/spec/graphql/relay/array_connection_spec.rb +5 -0
- data/spec/graphql/relay/base_connection_spec.rb +16 -0
- data/spec/graphql/relay/mutation_spec.rb +94 -0
- data/spec/graphql/relay/node_spec.rb +88 -0
- data/spec/graphql/relay/relation_connection_spec.rb +5 -0
- data/spec/graphql/schema/printer_spec.rb +16 -0
- data/spec/support/dummy/schema.rb +2 -2
- data/spec/support/star_wars/schema.rb +8 -0
- data/spec/tmp/app/graphql/types/bird_type.rb +5 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e792ef4481683e6c02e730a49a55d514e3117bd
|
4
|
+
data.tar.gz: 60f5a4bd233004203b0a3249e91d1c1ac4c99fec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fc9cc9c7581fb346daf8eaff35d1b7183c4ed89fc9c8c077827e71f5a63418c9d717983d2af661c700a5aade7a2c262714e01b809bd53a241c018b807de169cd
|
7
|
+
data.tar.gz: 631b382abd20a9e9060cda878287ea87df1d227e55cb410038755e99c38697007ab188fce7ff4ca7ed935e64912e663fe10e63c935c2727f53b19cbceda71570
|
data/lib/graphql/argument.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/graphql/base_type.rb
CHANGED
@@ -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
|
data/lib/graphql/field.rb
CHANGED
@@ -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
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
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
|
-
|
14
|
-
|
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
|
-
|
16
|
-
connection_type_description = "The connection type for #{wrapped_type.name}."
|
14
|
+
edge_type ||= wrapped_type.edge_type
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
data/lib/graphql/relay/node.rb
CHANGED
@@ -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
|
@@ -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
|
-
|
11
|
-
|
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
|
-
#
|
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
|
18
|
-
def
|
19
|
-
|
20
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
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
|
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
|
|
data/lib/graphql/version.rb
CHANGED
@@ -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
|
data/spec/graphql/field_spec.rb
CHANGED
@@ -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
|
-
["
|
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
|
-
"
|
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["
|
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[:
|
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
|
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.
|
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-
|
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.
|
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
|