graphql-rb 0.0.2
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 +7 -0
- data/Rakefile +26 -0
- data/lib/graphql/configuration/configurable.rb +46 -0
- data/lib/graphql/configuration/configuration.rb +92 -0
- data/lib/graphql/configuration/slot.rb +124 -0
- data/lib/graphql/configuration.rb +3 -0
- data/lib/graphql/errors/error.rb +3 -0
- data/lib/graphql/errors.rb +1 -0
- data/lib/graphql/executor.rb +83 -0
- data/lib/graphql/introspection/meta_fields.rb +54 -0
- data/lib/graphql/introspection/query.rb +81 -0
- data/lib/graphql/introspection/schema.rb +158 -0
- data/lib/graphql/introspection.rb +3 -0
- data/lib/graphql/language/argument.rb +11 -0
- data/lib/graphql/language/directive.rb +5 -0
- data/lib/graphql/language/document.rb +23 -0
- data/lib/graphql/language/field.rb +55 -0
- data/lib/graphql/language/fragment_definition.rb +23 -0
- data/lib/graphql/language/fragment_spread.rb +5 -0
- data/lib/graphql/language/inline_fragment.rb +23 -0
- data/lib/graphql/language/list_type.rb +15 -0
- data/lib/graphql/language/name.rb +5 -0
- data/lib/graphql/language/named_type.rb +15 -0
- data/lib/graphql/language/non_null_type.rb +15 -0
- data/lib/graphql/language/operation_definition.rb +33 -0
- data/lib/graphql/language/parser.rb +331 -0
- data/lib/graphql/language/selection_set.rb +107 -0
- data/lib/graphql/language/transform.rb +101 -0
- data/lib/graphql/language/value.rb +24 -0
- data/lib/graphql/language/variable.rb +11 -0
- data/lib/graphql/language/variable_definition.rb +34 -0
- data/lib/graphql/language.rb +40 -0
- data/lib/graphql/type/argument.rb +16 -0
- data/lib/graphql/type/directive.rb +37 -0
- data/lib/graphql/type/directives.rb +25 -0
- data/lib/graphql/type/enum_type.rb +100 -0
- data/lib/graphql/type/field.rb +50 -0
- data/lib/graphql/type/input_object_type.rb +47 -0
- data/lib/graphql/type/interface_type.rb +64 -0
- data/lib/graphql/type/list.rb +23 -0
- data/lib/graphql/type/non_null.rb +25 -0
- data/lib/graphql/type/object_type.rb +57 -0
- data/lib/graphql/type/scalar_type.rb +137 -0
- data/lib/graphql/type/schema.rb +49 -0
- data/lib/graphql/type/union_type.rb +39 -0
- data/lib/graphql/type.rb +82 -0
- data/lib/graphql/validator.rb +43 -0
- data/lib/graphql/version.rb +3 -0
- data/lib/graphql.rb +21 -0
- data/spec/configuration/configuration_spec.rb +4 -0
- data/spec/data.rb +89 -0
- data/spec/introspection/full_spec.rb +12 -0
- data/spec/introspection/simple_spec.rb +153 -0
- data/spec/language/parser_spec.rb +73 -0
- data/spec/schema.rb +145 -0
- data/spec/spec_helper.rb +99 -0
- data/spec/type/enum_spec.rb +27 -0
- data/spec/type/input_object_spec.rb +21 -0
- data/spec/type/list_spec.rb +16 -0
- data/spec/type/non_null_spec.rb +22 -0
- data/spec/type/scalar_type_spec.rb +117 -0
- data/spec/type/schema_spec.rb +13 -0
- metadata +202 -0
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'graphql/configuration'
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
|
5
|
+
|
6
|
+
# GraphQL Scalar Type Configuration
|
7
|
+
#
|
8
|
+
class GraphQLScalarTypeConfiguration < GraphQL::Configuration::Base
|
9
|
+
slot :name, String
|
10
|
+
slot :description, String, null: true
|
11
|
+
slot :serialize, Proc
|
12
|
+
slot :parse_value, Proc
|
13
|
+
slot :parse_literal, Proc
|
14
|
+
end
|
15
|
+
|
16
|
+
# GraphQL Scalar Type
|
17
|
+
#
|
18
|
+
class GraphQLScalarType < GraphQL::Configuration::Configurable
|
19
|
+
|
20
|
+
include GraphQLType
|
21
|
+
include GraphQLInputType
|
22
|
+
include GraphQLOutputType
|
23
|
+
include GraphQLLeafType
|
24
|
+
include GraphQLNullableType
|
25
|
+
include GraphQLNamedType
|
26
|
+
|
27
|
+
configure_with GraphQLScalarTypeConfiguration
|
28
|
+
|
29
|
+
def initialize(configuration)
|
30
|
+
super
|
31
|
+
raise RuntimeError.new("Name should present in #{self.class}") if name.nil? || name.size == 0
|
32
|
+
end
|
33
|
+
|
34
|
+
def serialize(value)
|
35
|
+
@configuration.serialize.call(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_value(value)
|
39
|
+
@configuration.parse_value.call(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_literal(ast)
|
43
|
+
@configuration.parse_literal.call(ast)
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
name
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
# Int
|
53
|
+
#
|
54
|
+
GraphQLInt = GraphQLScalarType.new do
|
55
|
+
name 'Int'
|
56
|
+
|
57
|
+
serialize lambda { |value|
|
58
|
+
value = value.to_s
|
59
|
+
return nil unless value =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
|
60
|
+
value.to_f.round
|
61
|
+
}
|
62
|
+
|
63
|
+
parse_value serialize #-> (value) { serialize(value) }
|
64
|
+
|
65
|
+
parse_literal lambda { |ast|
|
66
|
+
ast[:kind] == :int ? ast[:value].to_i : nil
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
# Float
|
71
|
+
#
|
72
|
+
GraphQLFloat = GraphQLScalarType.new do
|
73
|
+
name 'Float'
|
74
|
+
|
75
|
+
serialize lambda { |value|
|
76
|
+
value = value.to_s
|
77
|
+
return nil unless value =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
|
78
|
+
value.to_f
|
79
|
+
}
|
80
|
+
|
81
|
+
parse_value serialize
|
82
|
+
|
83
|
+
parse_literal lambda { |ast|
|
84
|
+
ast[:kind] == :int || ast[:kind] == :float ? ast[:value].to_f : nil
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
# String
|
89
|
+
#
|
90
|
+
GraphQLString = GraphQLScalarType.new do
|
91
|
+
name 'String'
|
92
|
+
|
93
|
+
serialize lambda { |value|
|
94
|
+
value.to_s
|
95
|
+
}
|
96
|
+
|
97
|
+
parse_value serialize
|
98
|
+
|
99
|
+
parse_literal lambda { |ast|
|
100
|
+
ast[:kind] == :string ? ast[:value] : nil
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
# Boolean
|
105
|
+
#
|
106
|
+
GraphQLBoolean = GraphQLScalarType.new do
|
107
|
+
name 'Boolean'
|
108
|
+
|
109
|
+
serialize lambda { |value|
|
110
|
+
return false if value == 0
|
111
|
+
!!value
|
112
|
+
}
|
113
|
+
|
114
|
+
parse_value serialize
|
115
|
+
|
116
|
+
parse_literal lambda { |ast|
|
117
|
+
ast[:kind] == :boolean ? ast[:value] : nil
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
# ID
|
122
|
+
#
|
123
|
+
GraphQLID = GraphQLScalarType.new do
|
124
|
+
name 'ID'
|
125
|
+
|
126
|
+
serialize lambda { |value|
|
127
|
+
value.to_s
|
128
|
+
}
|
129
|
+
|
130
|
+
parse_value serialize
|
131
|
+
|
132
|
+
parse_literal lambda { |ast|
|
133
|
+
ast[:kind] == :string || ast[:kind] == :int ? ast[:value] : nil
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module GraphQL
|
2
|
+
|
3
|
+
class GraphQLSchemaConfiguration < GraphQL::Configuration::Base
|
4
|
+
|
5
|
+
slot :name, String
|
6
|
+
slot :query, GraphQLObjectType
|
7
|
+
slot :mutation, GraphQLObjectType, null: true
|
8
|
+
|
9
|
+
end
|
10
|
+
|
11
|
+
class GraphQLSchema < GraphQL::Configuration::Configurable
|
12
|
+
|
13
|
+
configure_with GraphQLSchemaConfiguration
|
14
|
+
|
15
|
+
def query_type
|
16
|
+
@configuration.query
|
17
|
+
end
|
18
|
+
|
19
|
+
def mutation_type
|
20
|
+
@configuration.mutation
|
21
|
+
end
|
22
|
+
|
23
|
+
def type_map
|
24
|
+
@type_map ||= [query_type, mutation_type, Introspection::Schema__].reduce({}, &TypeMapReducer)
|
25
|
+
end
|
26
|
+
|
27
|
+
def type_names
|
28
|
+
@type_names ||= type_map.keys
|
29
|
+
end
|
30
|
+
|
31
|
+
def types
|
32
|
+
@types ||= type_map.values
|
33
|
+
end
|
34
|
+
|
35
|
+
def type(name)
|
36
|
+
type_map[name]
|
37
|
+
end
|
38
|
+
|
39
|
+
def directives
|
40
|
+
@directives ||= [GraphQLSkipDirective, GraphQLIncludeDirective]
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
name
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module GraphQL
|
2
|
+
|
3
|
+
class GraphQLUnionTypeConfiguration < GraphQL::Configuration::Base
|
4
|
+
slot :name, String
|
5
|
+
slot :types, [-> { GraphQLObjectType }], singular: :type
|
6
|
+
slot :resolve_type, Proc, null: true
|
7
|
+
slot :description, String, null: true
|
8
|
+
end
|
9
|
+
|
10
|
+
class GraphQLUnionType < GraphQL::Configuration::Configurable
|
11
|
+
|
12
|
+
include GraphQLType
|
13
|
+
include GraphQLOutputType
|
14
|
+
include GraphQLCompositeType
|
15
|
+
include GraphQLAbstractType
|
16
|
+
include GraphQLNullableType
|
17
|
+
include GraphQLNamedType
|
18
|
+
|
19
|
+
configure_with GraphQLUnionTypeConfiguration
|
20
|
+
|
21
|
+
def possible_types
|
22
|
+
@configuration.types
|
23
|
+
end
|
24
|
+
|
25
|
+
def possible_type?(type)
|
26
|
+
@configuration.types.include?(type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def resolve_type(type)
|
30
|
+
@configuration.resolve_type.nil? ? type_of(type) : @configuration.resolve_type.call(type)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
name
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/lib/graphql/type.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module GraphQL
|
2
|
+
|
3
|
+
module GraphQLType
|
4
|
+
def +@
|
5
|
+
GraphQLList.new(self)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module GraphQLInputType
|
10
|
+
end
|
11
|
+
|
12
|
+
module GraphQLOutputType
|
13
|
+
end
|
14
|
+
|
15
|
+
module GraphQLLeafType
|
16
|
+
end
|
17
|
+
|
18
|
+
module GraphQLCompositeType
|
19
|
+
end
|
20
|
+
|
21
|
+
module GraphQLNamedType
|
22
|
+
end
|
23
|
+
|
24
|
+
module GraphQLAbstractType
|
25
|
+
|
26
|
+
def type_of(value)
|
27
|
+
raise "Not implemented. Yet."
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
module GraphQLNullableType
|
33
|
+
|
34
|
+
def !
|
35
|
+
GraphQLNonNull.new(self)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.named_type(type)
|
41
|
+
type = type.of_type while type.is_a?(GraphQLList) || type.is_a?(GraphQLNonNull)
|
42
|
+
end
|
43
|
+
|
44
|
+
TypeMapReducer = lambda do |memo, type|
|
45
|
+
return TypeMapReducer.call(memo, type.of_type) if type.is_a?(GraphQLNonNull) || type.is_a?(GraphQLList)
|
46
|
+
|
47
|
+
return memo if type.nil? || memo.keys.include?(type.name)
|
48
|
+
|
49
|
+
memo[type.name] = type
|
50
|
+
|
51
|
+
if type.is_a?(GraphQLUnionType) || type.is_a?(GraphQLInterfaceType)
|
52
|
+
memo = type.possible_types.reduce(memo, &TypeMapReducer)
|
53
|
+
end
|
54
|
+
|
55
|
+
if type.is_a?(GraphQLObjectType)
|
56
|
+
memo = type.interfaces.reduce(memo, &TypeMapReducer)
|
57
|
+
end
|
58
|
+
|
59
|
+
if type.is_a?(GraphQLObjectType) || type.is_a?(GraphQLInterfaceType)
|
60
|
+
type.field_map.each do |name, field|
|
61
|
+
memo = field.args.map(&:type).reduce(memo, &TypeMapReducer)
|
62
|
+
memo = TypeMapReducer.call(memo, field.type)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
memo
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
require_relative 'type/scalar_type'
|
72
|
+
require_relative 'type/object_type'
|
73
|
+
require_relative 'type/interface_type'
|
74
|
+
require_relative 'type/union_type'
|
75
|
+
require_relative 'type/enum_type'
|
76
|
+
require_relative 'type/input_object_type'
|
77
|
+
require_relative 'type/field'
|
78
|
+
require_relative 'type/argument'
|
79
|
+
require_relative 'type/list'
|
80
|
+
require_relative 'type/non_null'
|
81
|
+
require_relative 'type/schema'
|
82
|
+
require_relative 'type/directive'
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module GraphQL
|
2
|
+
class Validator
|
3
|
+
|
4
|
+
def self.coerce_value(value, type)
|
5
|
+
case type
|
6
|
+
when GraphQLNonNull
|
7
|
+
coerce_value(value, type.of_type)
|
8
|
+
when value.nil?
|
9
|
+
nil
|
10
|
+
when GraphQLList
|
11
|
+
values = value.is_a?(Array) ? value : [value]
|
12
|
+
values.map { |value| coerce_value(value, type.of_type) }
|
13
|
+
when GraphQLInputObjectType
|
14
|
+
raise "Not. Implemented. Yet."
|
15
|
+
when GraphQLScalarType, GraphQLEnumType
|
16
|
+
type.parse_value(value)
|
17
|
+
else
|
18
|
+
raise "Must be input type"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.valid_value?(value, type)
|
23
|
+
case type
|
24
|
+
when GraphQLNonNull
|
25
|
+
return false if value.nil?
|
26
|
+
valid_value?(value, type.of_type)
|
27
|
+
when value.nil?
|
28
|
+
true
|
29
|
+
when GraphQLList
|
30
|
+
values = value.is_a?(Array) ? value : [value]
|
31
|
+
values.all? { |value| valid_value?(value, type.of_type) }
|
32
|
+
when GraphQLInputObjectType
|
33
|
+
raise "Not. Implemented. Yet."
|
34
|
+
when GraphQLScalarType, GraphQLEnumType
|
35
|
+
!type.parse_value(value).nil?
|
36
|
+
else
|
37
|
+
raise "Must be input type"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
data/lib/graphql.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'graphql/errors'
|
2
|
+
require 'graphql/configuration'
|
3
|
+
require 'graphql/type'
|
4
|
+
require 'graphql/introspection'
|
5
|
+
require 'graphql/language'
|
6
|
+
require 'graphql/version'
|
7
|
+
require 'graphql/executor'
|
8
|
+
require 'graphql/validator'
|
9
|
+
|
10
|
+
module GraphQL
|
11
|
+
|
12
|
+
def self.graphql(schema, query, root, params, operation = nil)
|
13
|
+
document = GraphQL::Language.parse(query)
|
14
|
+
executor = GraphQL::Executor.new(document, schema)
|
15
|
+
result = executor.execute(root, params, operation)
|
16
|
+
{ data: result }
|
17
|
+
rescue StandardError => e
|
18
|
+
{ errors: [e] }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/spec/data.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'celluloid/current'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module StarWars
|
5
|
+
class Data
|
6
|
+
include Celluloid
|
7
|
+
|
8
|
+
Human = Struct.new('Human', :id, :name, :friends, :appears_in, :home_planet)
|
9
|
+
Droid = Struct.new('Droid', :id, :name, :friends, :appears_in, :primary_function)
|
10
|
+
|
11
|
+
Luke = Human.new('1000', 'Like Skywalker', ['1002', '1003', '2000', '2001'], [4, 5, 6], 'Tatooine')
|
12
|
+
Vader = Human.new('1001', 'Darth Vader', ['1004'], [4, 5, 6], 'Tatooine')
|
13
|
+
Han = Human.new('1002', 'Han Solo', ['1000', '1003', '2001'], [4, 5, 6])
|
14
|
+
Leia = Human.new('1003', 'Lea Organa', ['1000', '1002', '2000', '2001'], [4, 5, 6], 'Alderaan')
|
15
|
+
Tarkin = Human.new('1004', 'Wilhuff Tarkin', ['1001'], [4])
|
16
|
+
|
17
|
+
ThreePO = Droid.new('2000', 'C-3PO', ['1000', '1002', '1003', '2001'], [4, 5, 6], 'Protocol')
|
18
|
+
Artoo = Droid.new('2001', 'R2-D2', ['1000', '1002', '1003'], [4, 5, 6], 'Astromech')
|
19
|
+
|
20
|
+
Characters = [Luke, Vader, Han, Leia, Tarkin, ThreePO, Artoo]
|
21
|
+
|
22
|
+
def self.logger
|
23
|
+
@logger ||= Logger.new(STDOUT)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.get(id)
|
27
|
+
Fetcher.fetch(id)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.select(ids)
|
31
|
+
Fetcher.fetch(ids)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
class Fetcher
|
36
|
+
include Celluloid
|
37
|
+
|
38
|
+
def self.logger
|
39
|
+
@logger ||= Logger.new(STDOUT)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.fetch(id_or_ids)
|
43
|
+
logger.info "Fetching `#{id_or_ids}`"
|
44
|
+
pool.future.fetch(id_or_ids)
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.pool
|
48
|
+
@pool ||= new
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@timer = after(0.01) { perform_fetch }
|
53
|
+
@data = {}
|
54
|
+
end
|
55
|
+
|
56
|
+
def restart
|
57
|
+
@timer.reset
|
58
|
+
end
|
59
|
+
|
60
|
+
def fetch(id_or_ids)
|
61
|
+
condition = Celluloid::Condition.new
|
62
|
+
(@data[id_or_ids] ||= []) << lambda { |value| condition.signal(value) }
|
63
|
+
restart
|
64
|
+
condition.wait
|
65
|
+
end
|
66
|
+
|
67
|
+
def perform_fetch
|
68
|
+
self.class.logger.info "Performing fetch"
|
69
|
+
data = @data
|
70
|
+
@data = {}
|
71
|
+
ids = data.keys.flatten.uniq
|
72
|
+
self.class.logger.info "select * from table where id in #{ids}"
|
73
|
+
# sleep 1
|
74
|
+
characters = Data::Characters.select { |c| ids.include?(c.id) }
|
75
|
+
data.each do |key, blocks|
|
76
|
+
result = if key.is_a?(Array)
|
77
|
+
result = characters.select { |c| key.include?(c.id) }
|
78
|
+
else
|
79
|
+
result = characters.find { |c| key == (c.id) }
|
80
|
+
end
|
81
|
+
blocks.each { |block| block.call(result) }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'graphql'
|
2
|
+
require_relative '../schema'
|
3
|
+
|
4
|
+
RSpec.describe "Introspection - Full" do
|
5
|
+
|
6
|
+
it "Should allow querying the schema" do
|
7
|
+
expect {
|
8
|
+
GraphQL::graphql(StarWars::Schema, GraphQL::Introspection::Query, {}, {})
|
9
|
+
}.not_to raise_error
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'graphql'
|
2
|
+
require_relative '../schema'
|
3
|
+
|
4
|
+
RSpec.describe "Introspection - Simple" do
|
5
|
+
|
6
|
+
def q1
|
7
|
+
%Q(
|
8
|
+
query Introspection {
|
9
|
+
__schema {
|
10
|
+
types {
|
11
|
+
name
|
12
|
+
}
|
13
|
+
}
|
14
|
+
}
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def q2
|
20
|
+
%Q(
|
21
|
+
query Introspection {
|
22
|
+
__schema {
|
23
|
+
queryType {
|
24
|
+
name
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def q3
|
33
|
+
%Q(
|
34
|
+
query Introspection {
|
35
|
+
__type(name: "Droid") {
|
36
|
+
name
|
37
|
+
}
|
38
|
+
}
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def q4
|
44
|
+
%Q(
|
45
|
+
query Introspection {
|
46
|
+
__type(name: "Droid") {
|
47
|
+
name
|
48
|
+
kind
|
49
|
+
}
|
50
|
+
}
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def q5
|
56
|
+
%Q(
|
57
|
+
query Introspection {
|
58
|
+
__type(name: "Character") {
|
59
|
+
name
|
60
|
+
kind
|
61
|
+
}
|
62
|
+
}
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
def q6
|
68
|
+
%Q(
|
69
|
+
query Introspection {
|
70
|
+
__type(name: "Droid") {
|
71
|
+
name
|
72
|
+
fields {
|
73
|
+
name
|
74
|
+
type {
|
75
|
+
name
|
76
|
+
kind
|
77
|
+
}
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
it "Should allow querying the schema for types" do
|
86
|
+
expectation = {
|
87
|
+
data: {
|
88
|
+
__schema: {
|
89
|
+
types: [
|
90
|
+
{ name: "Query" },
|
91
|
+
{ name: "Episode" },
|
92
|
+
{ name: "Character" },
|
93
|
+
{ name: "Human" },
|
94
|
+
{ name: "String" },
|
95
|
+
{ name: "Droid" },
|
96
|
+
{ name: "__Schema" },
|
97
|
+
{ name: "__Type" },
|
98
|
+
{ name: "__TypeKind" },
|
99
|
+
{ name: "Boolean" },
|
100
|
+
{ name: "__Field" },
|
101
|
+
{ name: "__InputValue" },
|
102
|
+
{ name: "__EnumValue" }
|
103
|
+
]
|
104
|
+
}
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
result = GraphQL::graphql(StarWars::Schema, q1, {}, {})
|
109
|
+
|
110
|
+
expect(result).to eql(expectation)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "Should allow querying the schema for query type" do
|
114
|
+
expectation = {
|
115
|
+
data: { __schema: { queryType: { name: "Query" } } }
|
116
|
+
}
|
117
|
+
|
118
|
+
result = GraphQL::graphql(StarWars::Schema, q2, {}, {})
|
119
|
+
|
120
|
+
expect(result).to eql(expectation)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "Should allow querying the schema for a specific type" do
|
124
|
+
expectation = {
|
125
|
+
data: { __type: { name: 'Droid' } }
|
126
|
+
}
|
127
|
+
|
128
|
+
result = GraphQL::graphql(StarWars::Schema, q3, {}, {})
|
129
|
+
|
130
|
+
expect(result).to eql(expectation)
|
131
|
+
end
|
132
|
+
|
133
|
+
it "Should allow querying the schema for an object kind" do
|
134
|
+
expectation = {
|
135
|
+
data: { __type: { name: 'Droid', kind: 'OBJECT' } }
|
136
|
+
}
|
137
|
+
|
138
|
+
result = GraphQL::graphql(StarWars::Schema, q4, {}, {})
|
139
|
+
|
140
|
+
expect(result).to eql(expectation)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "Should allow querying the schema for an interface kind" do
|
144
|
+
expectation = {
|
145
|
+
data: { __type: { name: 'Character', kind: 'INTERFACE' } }
|
146
|
+
}
|
147
|
+
|
148
|
+
result = GraphQL::graphql(StarWars::Schema, q5, {}, {})
|
149
|
+
|
150
|
+
expect(result).to eql(expectation)
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
require 'graphql'
|
3
|
+
require_relative '../schema'
|
4
|
+
|
5
|
+
RSpec.describe GraphQL::Language do
|
6
|
+
|
7
|
+
|
8
|
+
def hero_query
|
9
|
+
%Q(
|
10
|
+
query getHero($episode: Episode!, $heroes: [String] = ["1001", "1002"]) {
|
11
|
+
hero(episode: JEDI) {
|
12
|
+
id
|
13
|
+
name
|
14
|
+
friends {
|
15
|
+
name
|
16
|
+
|
17
|
+
... humanFields
|
18
|
+
|
19
|
+
... on Droid {
|
20
|
+
name
|
21
|
+
primary_function
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
... humanFields
|
26
|
+
|
27
|
+
... on Droid {
|
28
|
+
name
|
29
|
+
primary_function
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
fragment humanFields on Human {
|
35
|
+
name
|
36
|
+
home_planet
|
37
|
+
}
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
it "Should parse query" do
|
43
|
+
|
44
|
+
expectation = {
|
45
|
+
data: {
|
46
|
+
hero: {
|
47
|
+
id: "1000",
|
48
|
+
name: "Like Skywalker",
|
49
|
+
friends: [
|
50
|
+
{
|
51
|
+
name: "Han Solo",
|
52
|
+
home_planet: nil
|
53
|
+
}, {
|
54
|
+
name: "Lea Organa",
|
55
|
+
home_planet: "Alderaan"
|
56
|
+
}, {
|
57
|
+
name: "C-3PO",
|
58
|
+
primary_function: "Protocol"
|
59
|
+
}, {
|
60
|
+
name: "R2-D2",
|
61
|
+
primary_function: "Astromech"
|
62
|
+
}
|
63
|
+
],
|
64
|
+
home_planet: "Tatooine"
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
result = GraphQL::graphql(StarWars::Schema, hero_query, nil, { episode: 'JEDI', heroes: ['1001'] })
|
70
|
+
expect(result).to eql(expectation)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|