graphql 1.7.14 → 1.8.0.pre1
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 +5 -5
- data/lib/generators/graphql/function_generator.rb +1 -1
- data/lib/generators/graphql/loader_generator.rb +1 -1
- data/lib/generators/graphql/mutation_generator.rb +1 -6
- data/lib/generators/graphql/templates/function.erb +2 -2
- data/lib/generators/graphql/templates/loader.erb +2 -2
- data/lib/graphql.rb +2 -0
- data/lib/graphql/argument.rb +0 -1
- data/lib/graphql/backwards_compatibility.rb +2 -3
- data/lib/graphql/base_type.rb +18 -16
- data/lib/graphql/compatibility/query_parser_specification.rb +0 -117
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -14
- data/lib/graphql/define/assign_object_field.rb +5 -12
- data/lib/graphql/deprecated_dsl.rb +28 -0
- data/lib/graphql/directive.rb +0 -1
- data/lib/graphql/enum_type.rb +1 -3
- data/lib/graphql/execution.rb +0 -1
- data/lib/graphql/execution/multiplex.rb +29 -12
- data/lib/graphql/field.rb +5 -20
- data/lib/graphql/function.rb +12 -0
- data/lib/graphql/input_object_type.rb +1 -3
- data/lib/graphql/internal_representation/node.rb +14 -26
- data/lib/graphql/internal_representation/visit.rb +6 -3
- data/lib/graphql/introspection/arguments_field.rb +0 -1
- data/lib/graphql/introspection/enum_values_field.rb +0 -1
- data/lib/graphql/introspection/fields_field.rb +0 -1
- data/lib/graphql/introspection/input_fields_field.rb +0 -1
- data/lib/graphql/introspection/interfaces_field.rb +0 -1
- data/lib/graphql/introspection/of_type_field.rb +0 -1
- data/lib/graphql/introspection/possible_types_field.rb +0 -1
- data/lib/graphql/introspection/schema_field.rb +0 -1
- data/lib/graphql/introspection/type_by_name_field.rb +0 -1
- data/lib/graphql/introspection/typename_field.rb +0 -1
- data/lib/graphql/language.rb +0 -3
- data/lib/graphql/language/generation.rb +182 -3
- data/lib/graphql/language/lexer.rb +69 -144
- data/lib/graphql/language/lexer.rl +4 -15
- data/lib/graphql/language/nodes.rb +76 -136
- data/lib/graphql/language/parser.rb +621 -668
- data/lib/graphql/language/parser.y +11 -17
- data/lib/graphql/language/token.rb +3 -10
- data/lib/graphql/object_type.rb +6 -1
- data/lib/graphql/query.rb +13 -8
- data/lib/graphql/query/arguments.rb +33 -48
- data/lib/graphql/query/context.rb +1 -0
- data/lib/graphql/query/literal_input.rb +1 -4
- data/lib/graphql/relay/connection_resolve.rb +3 -0
- data/lib/graphql/relay/global_id_resolve.rb +5 -1
- data/lib/graphql/relay/relation_connection.rb +19 -14
- data/lib/graphql/schema.rb +219 -12
- data/lib/graphql/schema/argument.rb +33 -0
- data/lib/graphql/schema/build_from_definition.rb +18 -64
- data/lib/graphql/schema/enum.rb +76 -0
- data/lib/graphql/schema/field.rb +127 -0
- data/lib/graphql/schema/field/dynamic_resolve.rb +63 -0
- data/lib/graphql/schema/field/unwrapped_resolve.rb +20 -0
- data/lib/graphql/schema/input_object.rb +61 -0
- data/lib/graphql/schema/interface.rb +32 -0
- data/lib/graphql/schema/loader.rb +2 -2
- data/lib/graphql/schema/member.rb +97 -0
- data/lib/graphql/schema/member/build_type.rb +106 -0
- data/lib/graphql/schema/member/has_fields.rb +56 -0
- data/lib/graphql/schema/member/instrumentation.rb +113 -0
- data/lib/graphql/schema/member/list_type_proxy.rb +21 -0
- data/lib/graphql/schema/member/non_null_type_proxy.rb +21 -0
- data/lib/graphql/schema/object.rb +65 -0
- data/lib/graphql/schema/printer.rb +266 -33
- data/lib/graphql/schema/scalar.rb +25 -0
- data/lib/graphql/schema/traversal.rb +26 -17
- data/lib/graphql/schema/union.rb +48 -0
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -5
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -15
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +1 -11
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +5 -7
- data/lib/graphql/tracing.rb +0 -1
- data/lib/graphql/tracing/platform_tracing.rb +7 -20
- data/lib/graphql/tracing/scout_tracing.rb +2 -2
- data/lib/graphql/unresolved_type_error.rb +2 -3
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -1
- data/spec/dummy/app/channels/graphql_channel.rb +1 -22
- data/spec/dummy/log/development.log +0 -239
- data/spec/dummy/log/test.log +0 -204
- data/spec/dummy/test/system/action_cable_subscription_test.rb +0 -4
- data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
- data/spec/generators/graphql/function_generator_spec.rb +0 -26
- data/spec/generators/graphql/loader_generator_spec.rb +0 -24
- data/spec/graphql/analysis/max_query_complexity_spec.rb +3 -3
- data/spec/graphql/analysis/max_query_depth_spec.rb +3 -3
- data/spec/graphql/backtrace_spec.rb +0 -10
- data/spec/graphql/base_type_spec.rb +5 -19
- data/spec/graphql/boolean_type_spec.rb +3 -3
- data/spec/graphql/directive_spec.rb +1 -3
- data/spec/graphql/enum_type_spec.rb +5 -18
- data/spec/graphql/execution/execute_spec.rb +1 -1
- data/spec/graphql/execution/multiplex_spec.rb +2 -2
- data/spec/graphql/float_type_spec.rb +2 -2
- data/spec/graphql/id_type_spec.rb +1 -1
- data/spec/graphql/input_object_type_spec.rb +2 -15
- data/spec/graphql/int_type_spec.rb +2 -2
- data/spec/graphql/internal_representation/rewrite_spec.rb +2 -2
- data/spec/graphql/introspection/schema_type_spec.rb +0 -1
- data/spec/graphql/language/generation_spec.rb +186 -21
- data/spec/graphql/language/lexer_spec.rb +1 -21
- data/spec/graphql/language/nodes_spec.rb +12 -21
- data/spec/graphql/language/parser_spec.rb +1 -1
- data/spec/graphql/query/arguments_spec.rb +15 -37
- data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -2
- data/spec/graphql/query/variables_spec.rb +1 -1
- data/spec/graphql/query_spec.rb +5 -31
- data/spec/graphql/rake_task_spec.rb +1 -3
- data/spec/graphql/relay/base_connection_spec.rb +1 -1
- data/spec/graphql/relay/connection_instrumentation_spec.rb +2 -2
- data/spec/graphql/relay/connection_resolve_spec.rb +1 -1
- data/spec/graphql/relay/connection_type_spec.rb +1 -1
- data/spec/graphql/relay/mutation_spec.rb +3 -3
- data/spec/graphql/relay/relation_connection_spec.rb +1 -65
- data/spec/graphql/schema/build_from_definition_spec.rb +4 -86
- data/spec/graphql/schema/enum_spec.rb +60 -0
- data/spec/graphql/schema/field_spec.rb +14 -0
- data/spec/graphql/schema/input_object_spec.rb +43 -0
- data/spec/graphql/schema/interface_spec.rb +98 -0
- data/spec/graphql/schema/object_spec.rb +119 -0
- data/spec/graphql/schema/printer_spec.rb +15 -92
- data/spec/graphql/schema/scalar_spec.rb +40 -0
- data/spec/graphql/schema/union_spec.rb +35 -0
- data/spec/graphql/schema/validation_spec.rb +1 -1
- data/spec/graphql/schema/warden_spec.rb +11 -11
- data/spec/graphql/schema_spec.rb +25 -23
- data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +2 -10
- data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
- data/spec/graphql/string_type_spec.rb +3 -3
- data/spec/graphql/subscriptions_spec.rb +1 -1
- data/spec/graphql/tracing/platform_tracing_spec.rb +1 -60
- data/spec/support/dummy/schema.rb +25 -39
- data/spec/support/jazz.rb +334 -0
- data/spec/support/lazy_helpers.rb +21 -23
- data/spec/support/star_wars/data.rb +7 -6
- data/spec/support/star_wars/schema.rb +109 -142
- metadata +39 -33
- data/lib/graphql/execution/instrumentation.rb +0 -82
- data/lib/graphql/language/block_string.rb +0 -47
- data/lib/graphql/language/document_from_schema_definition.rb +0 -277
- data/lib/graphql/language/printer.rb +0 -351
- data/lib/graphql/tracing/data_dog_tracing.rb +0 -49
- data/spec/graphql/execution/instrumentation_spec.rb +0 -165
- data/spec/graphql/language/block_string_spec.rb +0 -70
- data/spec/graphql/language/document_from_schema_definition_spec.rb +0 -770
- data/spec/graphql/language/printer_spec.rb +0 -203
@@ -0,0 +1,334 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Here's the "application"
|
4
|
+
module Jazz
|
5
|
+
module Models
|
6
|
+
Instrument = Struct.new(:name, :family)
|
7
|
+
Ensemble = Struct.new(:name)
|
8
|
+
Musician = Struct.new(:name, :favorite_key)
|
9
|
+
Key = Struct.new(:root, :sharp, :flat) do
|
10
|
+
def self.from_notation(key_str)
|
11
|
+
key, sharp_or_flat = key_str.split("")
|
12
|
+
sharp = sharp_or_flat == "♯"
|
13
|
+
flat = sharp_or_flat == "♭"
|
14
|
+
Models::Key.new(key, sharp, flat)
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_notation
|
18
|
+
"#{root}#{sharp ? "♯" : ""}#{flat ? "♭" : ""}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.reset
|
23
|
+
@data = {
|
24
|
+
"Instrument" => [
|
25
|
+
Models::Instrument.new("Banjo", :str),
|
26
|
+
Models::Instrument.new("Flute", "WOODWIND"),
|
27
|
+
Models::Instrument.new("Trumpet", "BRASS"),
|
28
|
+
Models::Instrument.new("Piano", "KEYS"),
|
29
|
+
Models::Instrument.new("Organ", "KEYS"),
|
30
|
+
Models::Instrument.new("Drum Kit", "PERCUSSION"),
|
31
|
+
],
|
32
|
+
"Ensemble" => [
|
33
|
+
Models::Ensemble.new("Bela Fleck and the Flecktones"),
|
34
|
+
],
|
35
|
+
"Musician" => [
|
36
|
+
Models::Musician.new("Herbie Hancock", Models::Key.from_notation("B♭")),
|
37
|
+
]
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.data
|
42
|
+
@data || reset
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class BaseArgument < GraphQL::Schema::Argument
|
47
|
+
def initialize(name, type, desc = nil, custom: nil, **kwargs)
|
48
|
+
@custom = custom
|
49
|
+
super(name, type, desc, **kwargs)
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_graphql
|
53
|
+
arg_defn = super
|
54
|
+
arg_defn.metadata[:custom] = @custom
|
55
|
+
arg_defn
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# A custom field class that supports the `upcase:` option
|
60
|
+
class BaseField < GraphQL::Schema::Field
|
61
|
+
argument_class BaseArgument
|
62
|
+
def initialize(*args, options, &block)
|
63
|
+
@upcase = options.delete(:upcase)
|
64
|
+
super(*args, options, &block)
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_graphql
|
68
|
+
field_defn = super
|
69
|
+
if @upcase
|
70
|
+
inner_resolve = field_defn.resolve_proc
|
71
|
+
field_defn.resolve = ->(obj, args, ctx) {
|
72
|
+
inner_resolve.call(obj, args, ctx).upcase
|
73
|
+
}
|
74
|
+
end
|
75
|
+
field_defn
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class BaseObject < GraphQL::Schema::Object
|
80
|
+
# Use this overridden field class
|
81
|
+
field_class BaseField
|
82
|
+
|
83
|
+
class << self
|
84
|
+
def config(key, value)
|
85
|
+
configs[key] = value
|
86
|
+
end
|
87
|
+
|
88
|
+
def configs
|
89
|
+
@configs ||= {}
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_graphql
|
93
|
+
type_defn = super
|
94
|
+
configs.each do |k,v|
|
95
|
+
type_defn.metadata[k] = v
|
96
|
+
end
|
97
|
+
type_defn
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class BaseInterface < GraphQL::Schema::Interface
|
103
|
+
# Use this overridden field class
|
104
|
+
field_class BaseField
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
# Some arbitrary global ID scheme
|
109
|
+
# *Type suffix is removed automatically
|
110
|
+
class GloballyIdentifiableType < BaseInterface
|
111
|
+
description "A fetchable object in the system"
|
112
|
+
field :id, ID, "A unique identifier for this object", null: false
|
113
|
+
field :upcasedId, ID, null: false, upcase: true, method: :id
|
114
|
+
|
115
|
+
module Implementation
|
116
|
+
def id
|
117
|
+
GloballyIdentifiableType.to_id(@object)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.to_id(object)
|
122
|
+
"#{object.class.name.split("::").last}/#{object.name}"
|
123
|
+
end
|
124
|
+
|
125
|
+
def self.find(id)
|
126
|
+
class_name, object_name = id.split("/")
|
127
|
+
Models.data[class_name].find { |obj| obj.name == object_name }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# A legacy-style interface used by new-style types
|
132
|
+
NamedEntity = GraphQL::InterfaceType.define do
|
133
|
+
name "NamedEntity"
|
134
|
+
field :name, !types.String
|
135
|
+
end
|
136
|
+
|
137
|
+
# test field inheritance
|
138
|
+
class ObjectWithUpcasedName < BaseObject
|
139
|
+
# Test extra arguments:
|
140
|
+
field :upcaseName, String, null: false, upcase: true
|
141
|
+
|
142
|
+
def upcase_name
|
143
|
+
@object.name # upcase is applied by the superclass
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Here's a new-style GraphQL type definition
|
148
|
+
class Ensemble < ObjectWithUpcasedName
|
149
|
+
implements GloballyIdentifiableType, NamedEntity
|
150
|
+
description "A group of musicians playing together"
|
151
|
+
config :config, :configged
|
152
|
+
# Test string type names:
|
153
|
+
field :name, "String", null: false
|
154
|
+
field :musicians, "[Jazz::Musician]", null: false
|
155
|
+
end
|
156
|
+
|
157
|
+
class Family < GraphQL::Schema::Enum
|
158
|
+
description "Groups of musical instruments"
|
159
|
+
# support string and symbol
|
160
|
+
value "STRING", "Makes a sound by vibrating strings", value: :str
|
161
|
+
value :WOODWIND, "Makes a sound by vibrating air in a pipe"
|
162
|
+
value :BRASS, "Makes a sound by amplifying the sound of buzzing lips"
|
163
|
+
value "PERCUSSION", "Makes a sound by hitting something that vibrates"
|
164
|
+
value "KEYS", "Neither here nor there, really"
|
165
|
+
value "DIDGERIDOO", "Makes a sound by amplifying the sound of buzzing lips", deprecation_reason: "Merged into BRASS"
|
166
|
+
end
|
167
|
+
|
168
|
+
# Lives side-by-side with an old-style definition
|
169
|
+
using GraphQL::DeprecatedDSL # for ! and types[]
|
170
|
+
InstrumentType = GraphQL::ObjectType.define do
|
171
|
+
name "Instrument"
|
172
|
+
interfaces [NamedEntity]
|
173
|
+
implements GloballyIdentifiableType
|
174
|
+
|
175
|
+
field :id, !types.ID, "A unique identifier for this object", resolve: ->(obj, args, ctx) { GloballyIdentifiableType.to_id(obj) }
|
176
|
+
field :upcasedId, !types.ID, resolve: ->(obj, args, ctx) { GloballyIdentifiableType.to_id(obj).upcase }
|
177
|
+
if RUBY_ENGINE == "jruby"
|
178
|
+
# JRuby doesn't support refinements, so the `using` above won't work
|
179
|
+
field :family, Family.to_non_null_type
|
180
|
+
else
|
181
|
+
field :family, !Family
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class Key < GraphQL::Schema::Scalar
|
186
|
+
description "A musical key"
|
187
|
+
def self.coerce_input(val, ctx)
|
188
|
+
Models::Key.from_notation(val)
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.coerce_result(val, ctx)
|
192
|
+
val.to_notation
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class Musician < BaseObject
|
197
|
+
implements GloballyIdentifiableType
|
198
|
+
implements NamedEntity
|
199
|
+
description "Someone who plays an instrument"
|
200
|
+
field :instrument, InstrumentType, null: false
|
201
|
+
field :favoriteKey, Key, null: true
|
202
|
+
end
|
203
|
+
|
204
|
+
LegacyInputType = GraphQL::InputObjectType.define do
|
205
|
+
name "LegacyInput"
|
206
|
+
argument :intValue, !types.Int
|
207
|
+
end
|
208
|
+
|
209
|
+
class InspectableInput < GraphQL::Schema::InputObject
|
210
|
+
argument :stringValue, String, required: true
|
211
|
+
argument :nestedInput, InspectableInput, required: false
|
212
|
+
argument :legacyInput, LegacyInputType, required: false
|
213
|
+
def helper_method
|
214
|
+
[
|
215
|
+
# Context is available in the InputObject
|
216
|
+
@context[:message],
|
217
|
+
# A GraphQL::Query::Arguments instance is available
|
218
|
+
@arguments[:stringValue],
|
219
|
+
# Legacy inputs have underscored method access too
|
220
|
+
legacy_input ? legacy_input.int_value : "-",
|
221
|
+
# Access by method call is available
|
222
|
+
"(#{nested_input ? nested_input.helper_method : "-"})",
|
223
|
+
].join(", ")
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
class InspectableKey < BaseObject
|
228
|
+
field :root, String, null: false
|
229
|
+
field :isSharp, Boolean, null: false, method: :sharp
|
230
|
+
field :isFlat, Boolean, null: false, method: :flat
|
231
|
+
end
|
232
|
+
|
233
|
+
class PerformingAct < GraphQL::Schema::Union
|
234
|
+
possible_types Musician, Ensemble
|
235
|
+
|
236
|
+
def resolve_type
|
237
|
+
if @object.is_a?(Models::Ensemble)
|
238
|
+
Ensemble
|
239
|
+
else
|
240
|
+
Musician
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Another new-style definition, with method overrides
|
246
|
+
class Query < BaseObject
|
247
|
+
field :ensembles, [Ensemble], null: false
|
248
|
+
field :find, GloballyIdentifiableType, null: true do
|
249
|
+
argument :id, ID, required: true, custom: :ok
|
250
|
+
end
|
251
|
+
field :instruments, [InstrumentType], null: false do
|
252
|
+
argument :family, Family, required: false
|
253
|
+
end
|
254
|
+
field :inspectInput, [String], null: false do
|
255
|
+
argument :input, InspectableInput, required: true
|
256
|
+
end
|
257
|
+
field :inspectKey, InspectableKey, null: false do
|
258
|
+
argument :key, Key, required: true
|
259
|
+
end
|
260
|
+
field :nowPlaying, PerformingAct, null: false, resolve: ->(o, a, c) { Models.data["Ensemble"].first }
|
261
|
+
# For asserting that the object is initialized once:
|
262
|
+
field :objectId, Integer, null: false
|
263
|
+
|
264
|
+
def ensembles
|
265
|
+
Models.data["Ensemble"]
|
266
|
+
end
|
267
|
+
|
268
|
+
def find(id:)
|
269
|
+
if id == "MagicalSkipId"
|
270
|
+
@context.skip
|
271
|
+
else
|
272
|
+
GloballyIdentifiableType.find(id)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def instruments(family: nil)
|
277
|
+
objs = Models.data["Instrument"]
|
278
|
+
if family
|
279
|
+
objs = objs.select { |i| i.family == family }
|
280
|
+
end
|
281
|
+
objs
|
282
|
+
end
|
283
|
+
|
284
|
+
# This is for testing input object behavior
|
285
|
+
def inspect_input(input:)
|
286
|
+
[
|
287
|
+
input.class.name,
|
288
|
+
input.helper_method,
|
289
|
+
# Access by method
|
290
|
+
input.string_value,
|
291
|
+
# Access by key:
|
292
|
+
input["stringValue"],
|
293
|
+
input[:stringValue],
|
294
|
+
]
|
295
|
+
end
|
296
|
+
|
297
|
+
def inspect_key(key:)
|
298
|
+
key
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
class EnsembleInput < GraphQL::Schema::InputObject
|
303
|
+
argument :name, String, required: true
|
304
|
+
end
|
305
|
+
|
306
|
+
class Mutation < BaseObject
|
307
|
+
field :addEnsemble, Ensemble, null: false do
|
308
|
+
argument :input, EnsembleInput, required: true
|
309
|
+
end
|
310
|
+
|
311
|
+
def add_ensemble(input:)
|
312
|
+
ens = Models::Ensemble.new(input.name)
|
313
|
+
Models.data["Ensemble"] << ens
|
314
|
+
ens
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
class MetadataPlugin
|
319
|
+
def self.use(schema_defn, value:)
|
320
|
+
schema_defn.target.metadata[:plugin_key] = value
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# New-style Schema definition
|
325
|
+
class Schema < GraphQL::Schema
|
326
|
+
query(Query)
|
327
|
+
mutation(Mutation)
|
328
|
+
use MetadataPlugin, value: "xyz"
|
329
|
+
def self.resolve_type(type, obj, ctx)
|
330
|
+
class_name = obj.class.name.split("::").last
|
331
|
+
ctx.schema.types[class_name] || raise("No type for #{obj.inspect}")
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
@@ -46,32 +46,30 @@ module LazyHelpers
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
LazySum
|
50
|
-
|
51
|
-
field :
|
52
|
-
|
49
|
+
class LazySum < GraphQL::Schema::Object
|
50
|
+
field :value, Integer, null: true, resolve: ->(o, a, c) { o == 13 ? nil : o }
|
51
|
+
field :nestedSum, LazySum, null: false do
|
52
|
+
argument :value, Integer, required: true
|
53
53
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
62
|
-
}
|
54
|
+
|
55
|
+
def nested_sum(value:)
|
56
|
+
if value == 13
|
57
|
+
Wrapper.new(nil)
|
58
|
+
else
|
59
|
+
SumAll.new(@context, @object + value)
|
60
|
+
end
|
63
61
|
end
|
64
62
|
|
65
|
-
field :nullableNestedSum, LazySum do
|
66
|
-
argument :value,
|
67
|
-
resolve ->(o, args, c) {
|
68
|
-
if args[:value] == 13
|
69
|
-
Wrapper.new(nil)
|
70
|
-
else
|
71
|
-
SumAll.new(c, o + args[:value])
|
72
|
-
end
|
73
|
-
}
|
63
|
+
field :nullableNestedSum, LazySum, null: true do
|
64
|
+
argument :value, Integer, required: true
|
74
65
|
end
|
66
|
+
alias :nullable_nested_sum :nested_sum
|
67
|
+
end
|
68
|
+
|
69
|
+
using GraphQL::DeprecatedDSL
|
70
|
+
if RUBY_ENGINE == "jruby"
|
71
|
+
# JRuby doesn't support refinements, so the `using` above won't work
|
72
|
+
GraphQL::DeprecatedDSL.activate
|
75
73
|
end
|
76
74
|
|
77
75
|
LazyQuery = GraphQL::ObjectType.define do
|
@@ -136,7 +134,7 @@ module LazyHelpers
|
|
136
134
|
end
|
137
135
|
end
|
138
136
|
|
139
|
-
LazySchema
|
137
|
+
class LazySchema < GraphQL::Schema
|
140
138
|
query(LazyQuery)
|
141
139
|
mutation(LazyQuery)
|
142
140
|
lazy_resolve(Wrapper, :item)
|
@@ -18,12 +18,13 @@ module StarWars
|
|
18
18
|
'Executor',
|
19
19
|
]
|
20
20
|
|
21
|
+
# ActiveRecord::Base.logger = Logger.new(STDOUT)
|
21
22
|
`rm -f ./_test_.db`
|
22
23
|
# Set up "Bases" in ActiveRecord
|
23
24
|
|
24
25
|
if jruby?
|
25
26
|
ActiveRecord::Base.establish_connection(adapter: "jdbcsqlite3", database: "./_test_.db")
|
26
|
-
|
27
|
+
Sequel.connect('jdbc:sqlite:./_test_.db')
|
27
28
|
elsif ENV['DATABASE'] == 'POSTGRESQL'
|
28
29
|
ActiveRecord::Base.establish_connection(
|
29
30
|
adapter: "postgresql",
|
@@ -59,13 +60,13 @@ module StarWars
|
|
59
60
|
end
|
60
61
|
|
61
62
|
class FactionRecord
|
62
|
-
attr_reader :id, :name, :ships, :bases, :
|
63
|
-
def initialize(id:, name:, ships:, bases:,
|
63
|
+
attr_reader :id, :name, :ships, :bases, :bases_clone
|
64
|
+
def initialize(id:, name:, ships:, bases:, bases_clone:)
|
64
65
|
@id = id
|
65
66
|
@name = name
|
66
67
|
@ships = ships
|
67
68
|
@bases = bases
|
68
|
-
@
|
69
|
+
@bases_clone = bases_clone
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
@@ -74,7 +75,7 @@ module StarWars
|
|
74
75
|
name: 'Alliance to Restore the Republic',
|
75
76
|
ships: ['1', '2', '3', '4', '5'],
|
76
77
|
bases: Base.where(faction_id: 1),
|
77
|
-
|
78
|
+
bases_clone: Base.where(faction_id: 1),
|
78
79
|
})
|
79
80
|
|
80
81
|
|
@@ -83,7 +84,7 @@ module StarWars
|
|
83
84
|
name: 'Galactic Empire',
|
84
85
|
ships: ['6', '7', '8'],
|
85
86
|
bases: Base.where(faction_id: 2),
|
86
|
-
|
87
|
+
bases_clone: Base.where(faction_id: 2),
|
87
88
|
})
|
88
89
|
|
89
90
|
DATA = {
|
@@ -3,31 +3,28 @@ module StarWars
|
|
3
3
|
# Adapted from graphql-relay-js
|
4
4
|
# https://github.com/graphql/graphql-relay-js/blob/master/src/__tests__/starWarsSchema.js
|
5
5
|
|
6
|
-
Ship
|
7
|
-
|
8
|
-
interfaces [GraphQL::Relay::Node.interface]
|
6
|
+
class Ship < GraphQL::Schema::Object
|
7
|
+
implements GraphQL::Relay::Node.interface
|
9
8
|
global_id_field :id
|
10
|
-
field :name,
|
9
|
+
field :name, String, null: true
|
11
10
|
# Test cyclical connection types:
|
12
|
-
|
11
|
+
field :ships, Ship.connection_type, null: false
|
13
12
|
end
|
14
13
|
|
15
|
-
BaseType
|
16
|
-
|
17
|
-
|
14
|
+
class BaseType < GraphQL::Schema::Object
|
15
|
+
graphql_name "Base"
|
16
|
+
implements GraphQL::Relay::Node.interface
|
18
17
|
global_id_field :id
|
19
|
-
field :name,
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
27
|
-
}
|
18
|
+
field :name, String, null: false, resolve: ->(obj, args, ctx) {
|
19
|
+
LazyWrapper.new {
|
20
|
+
if obj.id.nil?
|
21
|
+
raise GraphQL::ExecutionError, "Boom!"
|
22
|
+
else
|
23
|
+
obj.name
|
24
|
+
end
|
28
25
|
}
|
29
|
-
|
30
|
-
field :planet,
|
26
|
+
}
|
27
|
+
field :planet, String, null: true
|
31
28
|
end
|
32
29
|
|
33
30
|
# Use an optional block to add fields to the connection type:
|
@@ -90,103 +87,81 @@ module StarWars
|
|
90
87
|
end
|
91
88
|
end
|
92
89
|
|
93
|
-
Faction
|
94
|
-
|
95
|
-
interfaces [GraphQL::Relay::Node.interface]
|
96
|
-
|
97
|
-
field :id, !types.ID, resolve: GraphQL::Relay::GlobalIdResolve.new(type: Faction)
|
98
|
-
field :name, types.String
|
99
|
-
connection :ships, ShipConnectionWithParentType, max_page_size: 1000 do
|
100
|
-
resolve ->(obj, args, ctx) {
|
101
|
-
all_ships = obj.ships.map {|ship_id| StarWars::DATA["Ship"][ship_id] }
|
102
|
-
if args[:nameIncludes]
|
103
|
-
case args[:nameIncludes]
|
104
|
-
when "error"
|
105
|
-
all_ships = GraphQL::ExecutionError.new("error from within connection")
|
106
|
-
when "raisedError"
|
107
|
-
raise GraphQL::ExecutionError.new("error raised from within connection")
|
108
|
-
when "lazyError"
|
109
|
-
all_ships = LazyWrapper.new { GraphQL::ExecutionError.new("lazy error from within connection") }
|
110
|
-
when "lazyRaisedError"
|
111
|
-
all_ships = LazyWrapper.new { raise GraphQL::ExecutionError.new("lazy raised error from within connection") }
|
112
|
-
when "null"
|
113
|
-
all_ships = nil
|
114
|
-
when "lazyObject"
|
115
|
-
prev_all_ships = all_ships
|
116
|
-
all_ships = LazyWrapper.new { prev_all_ships }
|
117
|
-
else
|
118
|
-
all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
|
119
|
-
end
|
120
|
-
end
|
121
|
-
all_ships
|
122
|
-
}
|
123
|
-
# You can define arguments here and use them in the connection
|
124
|
-
argument :nameIncludes, types.String
|
125
|
-
end
|
126
|
-
|
127
|
-
connection :shipsWithMaxPageSize, max_page_size: 2, function: ShipsWithMaxPageSize.new
|
90
|
+
class Faction < GraphQL::Schema::Object
|
91
|
+
implements GraphQL::Relay::Node.interface
|
128
92
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
93
|
+
field :id, ID, null: false, resolve: GraphQL::Relay::GlobalIdResolve.new(type: Faction)
|
94
|
+
field :name, String, null: true
|
95
|
+
field :ships, ShipConnectionWithParentType, connection: true, max_page_size: 1000, null: true, resolve: ->(obj, args, ctx) {
|
96
|
+
all_ships = obj.ships.map {|ship_id| StarWars::DATA["Ship"][ship_id] }
|
97
|
+
if args[:nameIncludes]
|
98
|
+
case args[:nameIncludes]
|
99
|
+
when "error"
|
100
|
+
all_ships = GraphQL::ExecutionError.new("error from within connection")
|
101
|
+
when "raisedError"
|
102
|
+
raise GraphQL::ExecutionError.new("error raised from within connection")
|
103
|
+
when "lazyError"
|
104
|
+
all_ships = LazyWrapper.new { GraphQL::ExecutionError.new("lazy error from within connection") }
|
105
|
+
when "lazyRaisedError"
|
106
|
+
all_ships = LazyWrapper.new { raise GraphQL::ExecutionError.new("lazy raised error from within connection") }
|
107
|
+
when "null"
|
108
|
+
all_ships = nil
|
109
|
+
when "lazyObject"
|
110
|
+
prev_all_ships = all_ships
|
111
|
+
all_ships = LazyWrapper.new { prev_all_ships }
|
148
112
|
else
|
149
|
-
|
113
|
+
all_ships = all_ships.select { |ship| ship.name.include?(args[:nameIncludes])}
|
150
114
|
end
|
151
|
-
|
115
|
+
end
|
116
|
+
all_ships
|
117
|
+
} do
|
118
|
+
# You can define arguments here and use them in the connection
|
119
|
+
argument :nameIncludes, String, required: false
|
152
120
|
end
|
153
121
|
|
154
|
-
|
155
|
-
resolve ->(object, args, context) { Base.all }
|
156
|
-
end
|
122
|
+
field :shipsWithMaxPageSize, max_page_size: 2, function: ShipsWithMaxPageSize.new
|
157
123
|
|
158
|
-
|
159
|
-
|
124
|
+
field :bases, BaseConnectionWithTotalCountType, null: true, connection: true, resolve: ->(obj, args, ctx) {
|
125
|
+
all_bases = Base.where(id: obj.bases)
|
126
|
+
if args[:nameIncludes]
|
127
|
+
all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
|
128
|
+
end
|
129
|
+
all_bases
|
130
|
+
} do
|
131
|
+
argument :nameIncludes, String, required: false
|
160
132
|
end
|
161
133
|
|
162
|
-
|
163
|
-
|
134
|
+
field :basesClone, BaseType.connection_type, null: true
|
135
|
+
field :basesByName, BaseType.connection_type, null: true do
|
136
|
+
argument :order, String, default_value: "name", required: false
|
164
137
|
end
|
165
|
-
|
166
|
-
|
167
|
-
|
138
|
+
def bases_by_name(order: nil)
|
139
|
+
if order.present?
|
140
|
+
@object.bases.order(order)
|
141
|
+
else
|
142
|
+
@object.bases
|
143
|
+
end
|
168
144
|
end
|
169
145
|
|
170
|
-
|
171
|
-
|
172
|
-
|
146
|
+
field :basesWithMaxLimitRelation, BaseType.connection_type, null: true, max_page_size: 2, resolve: Proc.new { Base.all}
|
147
|
+
field :basesWithMaxLimitArray, BaseType.connection_type, null: true, max_page_size: 2, resolve: Proc.new { Base.all.to_a }
|
148
|
+
field :basesWithDefaultMaxLimitRelation, BaseType.connection_type, null: true, resolve: Proc.new { Base.all }
|
149
|
+
field :basesWithDefaultMaxLimitArray, BaseType.connection_type, null: true, resolve: Proc.new { Base.all.to_a }
|
150
|
+
field :basesWithLargeMaxLimitRelation, BaseType.connection_type, null: true, max_page_size: 1000, resolve: Proc.new { Base.all }
|
173
151
|
|
174
|
-
|
175
|
-
argument :nameIncludes,
|
176
|
-
resolve ->(obj, args, ctx) {
|
177
|
-
all_bases = SequelBase.where(faction_id: obj.id)
|
178
|
-
if args[:nameIncludes]
|
179
|
-
all_bases = all_bases.where(Sequel.like(:name, "%#{args[:nameIncludes]}%"))
|
180
|
-
end
|
181
|
-
all_bases
|
182
|
-
}
|
152
|
+
field :basesAsSequelDataset, BaseConnectionWithTotalCountType, null: true, connection: true, max_page_size: 1000 do
|
153
|
+
argument :nameIncludes, String, required: false
|
183
154
|
end
|
184
155
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
156
|
+
def bases_as_sequel_dataset(name_includes: nil)
|
157
|
+
all_bases = SequelBase.where(faction_id: @object.id)
|
158
|
+
if name_includes
|
159
|
+
all_bases = all_bases.where(Sequel.like(:name, "%#{name_includes}%"))
|
160
|
+
end
|
161
|
+
all_bases
|
189
162
|
end
|
163
|
+
|
164
|
+
field :basesWithCustomEdge, CustomEdgeBaseConnectionType, null: true, connection: true, resolve: ->(o, a, c) { LazyNodesWrapper.new(o.bases) }
|
190
165
|
end
|
191
166
|
|
192
167
|
# Define a mutation. It will also:
|
@@ -323,60 +298,52 @@ module StarWars
|
|
323
298
|
|
324
299
|
GraphQL::Relay::BaseConnection.register_connection_implementation(LazyNodesWrapper, LazyNodesRelationConnection)
|
325
300
|
|
326
|
-
QueryType
|
327
|
-
|
328
|
-
field :rebels, Faction do
|
329
|
-
resolve ->(obj, args, ctx) { StarWars::DATA["Faction"]["1"]}
|
330
|
-
end
|
301
|
+
class QueryType < GraphQL::Schema::Object
|
302
|
+
graphql_name "Query"
|
331
303
|
|
332
|
-
field :
|
333
|
-
resolve ->(obj, args, ctx) { StarWars::DATA["Faction"]["2"]}
|
334
|
-
end
|
304
|
+
field :rebels, Faction, null: true, resolve: ->(obj, args, ctx) { StarWars::DATA["Faction"]["1"]}
|
335
305
|
|
336
|
-
field :
|
337
|
-
resolve ->(obj, args, ctx) { Base.find(3) }
|
338
|
-
end
|
306
|
+
field :empire, Faction, null: true, resolve: ->(obj, args, ctx) { StarWars::DATA["Faction"]["2"]}
|
339
307
|
|
340
|
-
|
341
|
-
resolve ->(obj, args, ctx) {
|
342
|
-
Base
|
343
|
-
.having('id in (select max(id) from bases group by faction_id)')
|
344
|
-
.group(:id)
|
345
|
-
.order('faction_id desc')
|
346
|
-
}
|
347
|
-
end
|
308
|
+
field :largestBase, BaseType, null: true, resolve: ->(obj, args, ctx) { Base.find(3) }
|
348
309
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
310
|
+
field :newestBasesGroupedByFaction, BaseType.connection_type, null: true, resolve: ->(obj, args, ctx) {
|
311
|
+
Base
|
312
|
+
.having('id in (select max(id) from bases group by faction_id)')
|
313
|
+
.group(:id)
|
314
|
+
.order('faction_id desc')
|
315
|
+
}
|
316
|
+
|
317
|
+
field :basesWithNullName, BaseType.connection_type, null: false, resolve: ->(obj, args, ctx) {
|
318
|
+
[OpenStruct.new(id: nil)]
|
319
|
+
}
|
354
320
|
|
355
|
-
field :node, GraphQL::Relay::Node.field
|
321
|
+
field :node, field: GraphQL::Relay::Node.field
|
356
322
|
|
357
323
|
custom_node_field = GraphQL::Relay::Node.field do
|
358
324
|
resolve ->(_, _, _) { StarWars::DATA["Faction"]["1"] }
|
359
325
|
end
|
360
|
-
field :nodeWithCustomResolver, custom_node_field
|
326
|
+
field :nodeWithCustomResolver, field: custom_node_field
|
361
327
|
|
362
|
-
field :nodes, GraphQL::Relay::Node.plural_field
|
363
|
-
field :nodesWithCustomResolver, GraphQL::Relay::Node.plural_field(
|
328
|
+
field :nodes, field: GraphQL::Relay::Node.plural_field
|
329
|
+
field :nodesWithCustomResolver, field: GraphQL::Relay::Node.plural_field(
|
364
330
|
resolve: ->(_, _, _) { [StarWars::DATA["Faction"]["1"], StarWars::DATA["Faction"]["2"]] }
|
365
331
|
)
|
366
332
|
|
367
|
-
field :batchedBase, BaseType do
|
368
|
-
argument :id,
|
369
|
-
|
370
|
-
|
371
|
-
|
333
|
+
field :batchedBase, BaseType, null: true do
|
334
|
+
argument :id, ID, required: true
|
335
|
+
end
|
336
|
+
|
337
|
+
def batched_base(id:)
|
338
|
+
LazyLoader.defer(@context, Base, id)
|
372
339
|
end
|
373
340
|
end
|
374
341
|
|
375
|
-
MutationType
|
376
|
-
|
342
|
+
class MutationType < GraphQL::Schema::Object
|
343
|
+
graphql_name "Mutation"
|
377
344
|
# The mutation object exposes a field:
|
378
345
|
field :introduceShip, field: IntroduceShipMutation.field
|
379
|
-
field :introduceShipFunction, IntroduceShipFunctionMutation.field
|
346
|
+
field :introduceShipFunction, field: IntroduceShipFunctionMutation.field
|
380
347
|
end
|
381
348
|
|
382
349
|
class ClassNameRecorder
|
@@ -399,12 +366,12 @@ module StarWars
|
|
399
366
|
end
|
400
367
|
end
|
401
368
|
|
402
|
-
Schema
|
369
|
+
class Schema < GraphQL::Schema
|
403
370
|
query(QueryType)
|
404
371
|
mutation(MutationType)
|
405
372
|
default_max_page_size 3
|
406
373
|
|
407
|
-
resolve_type
|
374
|
+
def self.resolve_type(type, object, ctx)
|
408
375
|
if object == :test_error
|
409
376
|
:not_a_type
|
410
377
|
elsif object.is_a?(Base)
|
@@ -416,14 +383,14 @@ module StarWars
|
|
416
383
|
else
|
417
384
|
nil
|
418
385
|
end
|
419
|
-
|
386
|
+
end
|
420
387
|
|
421
|
-
object_from_id
|
388
|
+
def self.object_from_id(node_id, ctx)
|
422
389
|
type_name, id = GraphQL::Schema::UniqueWithinType.decode(node_id)
|
423
390
|
StarWars::DATA[type_name][id]
|
424
391
|
end
|
425
392
|
|
426
|
-
id_from_object
|
393
|
+
def self.id_from_object(object, type, ctx)
|
427
394
|
GraphQL::Schema::UniqueWithinType.encode(type.name, object.id)
|
428
395
|
end
|
429
396
|
|