graphql 1.4.0 → 1.4.1
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/introspection/schema_type.rb +3 -3
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/query.rb +39 -27
- data/lib/graphql/query/executor.rb +1 -1
- data/lib/graphql/query/variables.rb +21 -28
- data/lib/graphql/relay/connection_type.rb +2 -0
- data/lib/graphql/relay/relation_connection.rb +8 -2
- data/lib/graphql/schema.rb +3 -0
- data/lib/graphql/schema/warden.rb +9 -0
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/spec/graphql/analysis/analyze_query_spec.rb +15 -15
- data/spec/graphql/analysis/field_usage_spec.rb +1 -1
- data/spec/graphql/analysis/max_query_complexity_spec.rb +8 -8
- data/spec/graphql/analysis/max_query_depth_spec.rb +7 -7
- data/spec/graphql/analysis/query_complexity_spec.rb +2 -2
- data/spec/graphql/analysis/query_depth_spec.rb +1 -1
- data/spec/graphql/base_type_spec.rb +19 -11
- data/spec/graphql/directive_spec.rb +1 -1
- data/spec/graphql/enum_type_spec.rb +2 -2
- data/spec/graphql/execution/typecast_spec.rb +19 -19
- data/spec/graphql/execution_error_spec.rb +1 -1
- data/spec/graphql/field_spec.rb +15 -7
- data/spec/graphql/id_type_spec.rb +1 -1
- data/spec/graphql/input_object_type_spec.rb +16 -16
- data/spec/graphql/interface_type_spec.rb +6 -6
- data/spec/graphql/internal_representation/rewrite_spec.rb +34 -34
- data/spec/graphql/introspection/directive_type_spec.rb +1 -1
- data/spec/graphql/introspection/input_value_type_spec.rb +2 -2
- data/spec/graphql/introspection/introspection_query_spec.rb +1 -1
- data/spec/graphql/introspection/schema_type_spec.rb +2 -2
- data/spec/graphql/introspection/type_type_spec.rb +1 -1
- data/spec/graphql/language/parser_spec.rb +1 -1
- data/spec/graphql/non_null_type_spec.rb +3 -3
- data/spec/graphql/object_type_spec.rb +8 -8
- data/spec/graphql/query/executor_spec.rb +4 -4
- data/spec/graphql/query/variables_spec.rb +20 -4
- data/spec/graphql/query_spec.rb +20 -2
- data/spec/graphql/relay/connection_type_spec.rb +1 -1
- data/spec/graphql/relay/mutation_spec.rb +9 -9
- data/spec/graphql/relay/node_spec.rb +8 -8
- data/spec/graphql/relay/relation_connection_spec.rb +24 -6
- data/spec/graphql/schema/catchall_middleware_spec.rb +3 -3
- data/spec/graphql/schema/reduce_types_spec.rb +9 -9
- data/spec/graphql/schema/type_expression_spec.rb +3 -3
- data/spec/graphql/schema/validation_spec.rb +1 -1
- data/spec/graphql/schema/warden_spec.rb +79 -0
- data/spec/graphql/schema_spec.rb +2 -2
- data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
- data/spec/graphql/static_validation/type_stack_spec.rb +2 -2
- data/spec/graphql/static_validation/validator_spec.rb +2 -2
- data/spec/graphql/union_type_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/support/dummy/data.rb +27 -0
- data/spec/support/dummy/schema.rb +369 -0
- data/spec/support/star_wars/data.rb +81 -0
- data/spec/support/star_wars/schema.rb +250 -0
- data/spec/support/static_validation_helpers.rb +2 -2
- metadata +10 -10
- data/spec/support/dairy_app.rb +0 -369
- data/spec/support/dairy_data.rb +0 -26
- data/spec/support/star_wars_data.rb +0 -80
- data/spec/support/star_wars_schema.rb +0 -242
data/spec/support/dairy_data.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'ostruct'
|
3
|
-
|
4
|
-
Cheese = Struct.new(:id, :flavor, :origin, :fat_content, :source)
|
5
|
-
CHEESES = {
|
6
|
-
1 => Cheese.new(1, "Brie", "France", 0.19, 1),
|
7
|
-
2 => Cheese.new(2, "Gouda", "Netherlands", 0.3, 1),
|
8
|
-
3 => Cheese.new(3, "Manchego", "Spain", 0.065, "SHEEP")
|
9
|
-
}
|
10
|
-
|
11
|
-
Milk = Struct.new(:id, :fatContent, :origin, :source, :flavors)
|
12
|
-
MILKS = {
|
13
|
-
1 => Milk.new(1, 0.04, "Antiquity", 1, ["Natural", "Chocolate", "Strawberry"]),
|
14
|
-
}
|
15
|
-
|
16
|
-
DAIRY = OpenStruct.new(
|
17
|
-
id: 1,
|
18
|
-
cheese: CHEESES[1],
|
19
|
-
milks: [MILKS[1]]
|
20
|
-
)
|
21
|
-
|
22
|
-
COW = OpenStruct.new(
|
23
|
-
id: 1,
|
24
|
-
name: "Billy",
|
25
|
-
last_produced_dairy: MILKS[1]
|
26
|
-
)
|
@@ -1,80 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'ostruct'
|
3
|
-
|
4
|
-
names = [
|
5
|
-
'X-Wing',
|
6
|
-
'Y-Wing',
|
7
|
-
'A-Wing',
|
8
|
-
'Millenium Falcon',
|
9
|
-
'Home One',
|
10
|
-
'TIE Fighter',
|
11
|
-
'TIE Interceptor',
|
12
|
-
'Executor',
|
13
|
-
]
|
14
|
-
|
15
|
-
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
16
|
-
`rm -f ./_test_.db`
|
17
|
-
# Set up "Bases" in ActiveRecord
|
18
|
-
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: "./_test_.db")
|
19
|
-
|
20
|
-
ActiveRecord::Schema.define do
|
21
|
-
self.verbose = false
|
22
|
-
create_table :bases do |t|
|
23
|
-
t.column :name, :string
|
24
|
-
t.column :planet, :string
|
25
|
-
t.column :faction_id, :integer
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class Base < ActiveRecord::Base
|
30
|
-
end
|
31
|
-
|
32
|
-
Base.create!(name: "Yavin", planet: "Yavin 4", faction_id: 1)
|
33
|
-
Base.create!(name: "Echo Base", planet: "Hoth", faction_id: 1)
|
34
|
-
Base.create!(name: "Secret Hideout", planet: "Dantooine", faction_id: 1)
|
35
|
-
Base.create!(name: "Death Star", planet: nil, faction_id: 2)
|
36
|
-
Base.create!(name: "Shield Generator", planet: "Endor", faction_id: 2)
|
37
|
-
Base.create!(name: "Headquarters", planet: "Coruscant", faction_id: 2)
|
38
|
-
|
39
|
-
# Also, set up Bases with Sequel
|
40
|
-
DB = Sequel.sqlite("./_test_.db")
|
41
|
-
class SequelBase < Sequel::Model(:bases)
|
42
|
-
end
|
43
|
-
|
44
|
-
rebels = OpenStruct.new({
|
45
|
-
id: '1',
|
46
|
-
name: 'Alliance to Restore the Republic',
|
47
|
-
ships: ['1', '2', '3', '4', '5'],
|
48
|
-
bases: Base.where(faction_id: 1),
|
49
|
-
basesClone: Base.where(faction_id: 1),
|
50
|
-
})
|
51
|
-
|
52
|
-
|
53
|
-
empire = OpenStruct.new({
|
54
|
-
id: '2',
|
55
|
-
name: 'Galactic Empire',
|
56
|
-
ships: ['6', '7', '8'],
|
57
|
-
bases: Base.where(faction_id: 2),
|
58
|
-
basesClone: Base.where(faction_id: 2),
|
59
|
-
})
|
60
|
-
|
61
|
-
STAR_WARS_DATA = {
|
62
|
-
"Faction" => {
|
63
|
-
"1" => rebels,
|
64
|
-
"2" => empire,
|
65
|
-
},
|
66
|
-
"Ship" => names.each_with_index.reduce({}) do |memo, (name, idx)|
|
67
|
-
id = (idx + 1).to_s
|
68
|
-
memo[id] = OpenStruct.new(name: name, id: id)
|
69
|
-
memo
|
70
|
-
end,
|
71
|
-
"Base" => Hash.new { |h, k| h[k] = Base.find(k) }
|
72
|
-
}
|
73
|
-
|
74
|
-
def STAR_WARS_DATA.create_ship(name, faction_id)
|
75
|
-
new_id = (self["Ship"].keys.map(&:to_i).max + 1).to_s
|
76
|
-
new_ship = OpenStruct.new(id: new_id, name: name)
|
77
|
-
self["Ship"][new_id] = new_ship
|
78
|
-
self["Faction"][faction_id].ships << new_id
|
79
|
-
new_ship
|
80
|
-
end
|
@@ -1,242 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
# Adapted from graphql-relay-js
|
3
|
-
# https://github.com/graphql/graphql-relay-js/blob/master/src/__tests__/starWarsSchema.js
|
4
|
-
|
5
|
-
Ship = GraphQL::ObjectType.define do
|
6
|
-
name "Ship"
|
7
|
-
interfaces [GraphQL::Relay::Node.interface]
|
8
|
-
global_id_field :id
|
9
|
-
field :name, types.String
|
10
|
-
end
|
11
|
-
|
12
|
-
BaseType = GraphQL::ObjectType.define do
|
13
|
-
name "Base"
|
14
|
-
interfaces [GraphQL::Relay::Node.interface]
|
15
|
-
global_id_field :id
|
16
|
-
field :name, types.String
|
17
|
-
field :planet, types.String
|
18
|
-
end
|
19
|
-
|
20
|
-
# Use an optional block to add fields to the connection type:
|
21
|
-
BaseConnectionWithTotalCountType = BaseType.define_connection(nodes_field: true) do
|
22
|
-
name "BasesConnectionWithTotalCount"
|
23
|
-
field :totalCount do
|
24
|
-
type types.Int
|
25
|
-
resolve ->(obj, args, ctx) { obj.nodes.count }
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
class CustomBaseEdge < GraphQL::Relay::Edge
|
30
|
-
def upcased_name
|
31
|
-
node.name.upcase
|
32
|
-
end
|
33
|
-
|
34
|
-
def upcased_parent_name
|
35
|
-
parent.name.upcase
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
CustomBaseEdgeType = BaseType.define_edge do
|
40
|
-
name "CustomBaseEdge"
|
41
|
-
field :upcasedName, types.String, property: :upcased_name
|
42
|
-
field :upcasedParentName, types.String, property: :upcased_parent_name
|
43
|
-
field :edgeClassName, types.String do
|
44
|
-
resolve ->(obj, args, ctx) { obj.class.name }
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
CustomEdgeBaseConnectionType = BaseType.define_connection(edge_class: CustomBaseEdge, edge_type: CustomBaseEdgeType, nodes_field: true) do
|
49
|
-
name "CustomEdgeBaseConnection"
|
50
|
-
|
51
|
-
field :totalCountTimes100 do
|
52
|
-
type types.Int
|
53
|
-
resolve ->(obj, args, ctx) { obj.nodes.count * 100 }
|
54
|
-
end
|
55
|
-
|
56
|
-
field :fieldName, types.String, resolve: ->(obj, args, ctx) { obj.field.name }
|
57
|
-
end
|
58
|
-
|
59
|
-
Faction = GraphQL::ObjectType.define do
|
60
|
-
name "Faction"
|
61
|
-
interfaces [GraphQL::Relay::Node.interface]
|
62
|
-
|
63
|
-
field :id, !types.ID, resolve: GraphQL::Relay::GlobalIdResolve.new(type: Faction)
|
64
|
-
field :name, types.String
|
65
|
-
connection :ships, Ship.connection_type do
|
66
|
-
resolve ->(obj, args, ctx) {
|
67
|
-
all_ships = obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
|
68
|
-
if args[:nameIncludes]
|
69
|
-
all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
|
70
|
-
end
|
71
|
-
all_ships
|
72
|
-
}
|
73
|
-
# You can define arguments here and use them in the connection
|
74
|
-
argument :nameIncludes, types.String
|
75
|
-
end
|
76
|
-
connection :shipsWithMaxPageSize, Ship.connection_type, max_page_size: 2 do
|
77
|
-
resolve ->(obj, args, ctx) {
|
78
|
-
all_ships = obj.ships.map {|ship_id| STAR_WARS_DATA["Ship"][ship_id] }
|
79
|
-
if args[:nameIncludes]
|
80
|
-
all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
|
81
|
-
end
|
82
|
-
all_ships
|
83
|
-
}
|
84
|
-
# You can define arguments here and use them in the connection
|
85
|
-
argument :nameIncludes, types.String
|
86
|
-
end
|
87
|
-
|
88
|
-
connection :bases, BaseConnectionWithTotalCountType do
|
89
|
-
# Resolve field should return an Array, the Connection
|
90
|
-
# will do the rest!
|
91
|
-
resolve ->(obj, args, ctx) {
|
92
|
-
all_bases = Base.where(id: obj.bases)
|
93
|
-
if args[:nameIncludes]
|
94
|
-
all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
|
95
|
-
end
|
96
|
-
all_bases
|
97
|
-
}
|
98
|
-
argument :nameIncludes, types.String
|
99
|
-
end
|
100
|
-
|
101
|
-
connection :basesClone, BaseType.connection_type
|
102
|
-
connection :basesByName, BaseType.connection_type, property: :bases do
|
103
|
-
argument :order, types.String, default_value: "name"
|
104
|
-
resolve ->(obj, args, ctx) {
|
105
|
-
if args[:order].present?
|
106
|
-
obj.bases.order(args[:order])
|
107
|
-
else
|
108
|
-
obj.bases
|
109
|
-
end
|
110
|
-
}
|
111
|
-
end
|
112
|
-
|
113
|
-
connection :basesWithMaxLimitRelation, BaseType.connection_type, max_page_size: 2 do
|
114
|
-
resolve ->(object, args, context) { Base.all }
|
115
|
-
end
|
116
|
-
|
117
|
-
connection :basesWithMaxLimitArray, BaseType.connection_type, max_page_size: 2 do
|
118
|
-
resolve ->(object, args, context) { Base.all.to_a }
|
119
|
-
end
|
120
|
-
|
121
|
-
connection :basesAsSequelDataset, BaseConnectionWithTotalCountType do
|
122
|
-
argument :nameIncludes, types.String
|
123
|
-
resolve ->(obj, args, ctx) {
|
124
|
-
all_bases = SequelBase.where(faction_id: obj.id)
|
125
|
-
if args[:nameIncludes]
|
126
|
-
all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
|
127
|
-
end
|
128
|
-
all_bases
|
129
|
-
}
|
130
|
-
end
|
131
|
-
|
132
|
-
connection :basesWithCustomEdge, CustomEdgeBaseConnectionType, property: :bases
|
133
|
-
end
|
134
|
-
|
135
|
-
# Define a mutation. It will also:
|
136
|
-
# - define a derived InputObjectType
|
137
|
-
# - define a derived ObjectType (for return)
|
138
|
-
# - define a field, accessible from {Mutation#field}
|
139
|
-
#
|
140
|
-
# The resolve proc takes `inputs, ctx`, where:
|
141
|
-
# - `inputs` has the keys defined with `input_field`
|
142
|
-
# - `ctx` is the Query context (like normal fields)
|
143
|
-
#
|
144
|
-
# Notice that you leave out clientMutationId.
|
145
|
-
IntroduceShipMutation = GraphQL::Relay::Mutation.define do
|
146
|
-
# Used as the root for derived types:
|
147
|
-
name "IntroduceShip"
|
148
|
-
description "Add a ship to this faction"
|
149
|
-
|
150
|
-
# Nested under `input` in the query:
|
151
|
-
input_field :shipName, types.String
|
152
|
-
input_field :factionId, !types.ID
|
153
|
-
|
154
|
-
# Result may have access to these fields:
|
155
|
-
return_field :shipEdge, Ship.edge_type
|
156
|
-
return_field :faction, Faction
|
157
|
-
|
158
|
-
# Here's the mutation operation:
|
159
|
-
resolve ->(root_obj, inputs, ctx) {
|
160
|
-
faction_id = inputs["factionId"]
|
161
|
-
if inputs["shipName"] == 'Millennium Falcon'
|
162
|
-
GraphQL::ExecutionError.new("Sorry, Millennium Falcon ship is reserved")
|
163
|
-
|
164
|
-
else
|
165
|
-
ship = STAR_WARS_DATA.create_ship(inputs["shipName"], faction_id)
|
166
|
-
faction = STAR_WARS_DATA["Faction"][faction_id]
|
167
|
-
connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(faction.ships)
|
168
|
-
ships_connection = connection_class.new(faction.ships, inputs)
|
169
|
-
ship_edge = GraphQL::Relay::Edge.new(ship, ships_connection)
|
170
|
-
result = {
|
171
|
-
shipEdge: ship_edge,
|
172
|
-
faction: faction
|
173
|
-
}
|
174
|
-
if inputs["shipName"] == "Slave II"
|
175
|
-
LazyWrapper.new(result)
|
176
|
-
else
|
177
|
-
result
|
178
|
-
end
|
179
|
-
end
|
180
|
-
}
|
181
|
-
end
|
182
|
-
|
183
|
-
|
184
|
-
class LazyWrapper
|
185
|
-
attr_reader :value
|
186
|
-
def initialize(value)
|
187
|
-
@value = value
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
QueryType = GraphQL::ObjectType.define do
|
192
|
-
name "Query"
|
193
|
-
field :rebels, Faction do
|
194
|
-
resolve ->(obj, args, ctx) { STAR_WARS_DATA["Faction"]["1"]}
|
195
|
-
end
|
196
|
-
|
197
|
-
field :empire, Faction do
|
198
|
-
resolve ->(obj, args, ctx) { STAR_WARS_DATA["Faction"]["2"]}
|
199
|
-
end
|
200
|
-
|
201
|
-
field :largestBase, BaseType do
|
202
|
-
resolve ->(obj, args, ctx) { Base.find(3) }
|
203
|
-
end
|
204
|
-
|
205
|
-
field :node, GraphQL::Relay::Node.field
|
206
|
-
end
|
207
|
-
|
208
|
-
MutationType = GraphQL::ObjectType.define do
|
209
|
-
name "Mutation"
|
210
|
-
# The mutation object exposes a field:
|
211
|
-
field :introduceShip, field: IntroduceShipMutation.field
|
212
|
-
end
|
213
|
-
|
214
|
-
StarWarsSchema = GraphQL::Schema.define do
|
215
|
-
query(QueryType)
|
216
|
-
mutation(MutationType)
|
217
|
-
|
218
|
-
resolve_type ->(object, ctx) {
|
219
|
-
if object == :test_error
|
220
|
-
:not_a_type
|
221
|
-
elsif object.is_a?(Base)
|
222
|
-
BaseType
|
223
|
-
elsif STAR_WARS_DATA["Faction"].values.include?(object)
|
224
|
-
Faction
|
225
|
-
elsif STAR_WARS_DATA["Ship"].values.include?(object)
|
226
|
-
Ship
|
227
|
-
else
|
228
|
-
nil
|
229
|
-
end
|
230
|
-
}
|
231
|
-
|
232
|
-
object_from_id ->(node_id, ctx) do
|
233
|
-
type_name, id = GraphQL::Schema::UniqueWithinType.decode(node_id)
|
234
|
-
STAR_WARS_DATA[type_name][id]
|
235
|
-
end
|
236
|
-
|
237
|
-
id_from_object ->(object, type, ctx) do
|
238
|
-
GraphQL::Schema::UniqueWithinType.encode(type.name, object.id)
|
239
|
-
end
|
240
|
-
|
241
|
-
lazy_resolve(LazyWrapper, :value)
|
242
|
-
end
|