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.
Files changed (149) hide show
  1. checksums.yaml +5 -5
  2. data/lib/generators/graphql/function_generator.rb +1 -1
  3. data/lib/generators/graphql/loader_generator.rb +1 -1
  4. data/lib/generators/graphql/mutation_generator.rb +1 -6
  5. data/lib/generators/graphql/templates/function.erb +2 -2
  6. data/lib/generators/graphql/templates/loader.erb +2 -2
  7. data/lib/graphql.rb +2 -0
  8. data/lib/graphql/argument.rb +0 -1
  9. data/lib/graphql/backwards_compatibility.rb +2 -3
  10. data/lib/graphql/base_type.rb +18 -16
  11. data/lib/graphql/compatibility/query_parser_specification.rb +0 -117
  12. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -14
  13. data/lib/graphql/define/assign_object_field.rb +5 -12
  14. data/lib/graphql/deprecated_dsl.rb +28 -0
  15. data/lib/graphql/directive.rb +0 -1
  16. data/lib/graphql/enum_type.rb +1 -3
  17. data/lib/graphql/execution.rb +0 -1
  18. data/lib/graphql/execution/multiplex.rb +29 -12
  19. data/lib/graphql/field.rb +5 -20
  20. data/lib/graphql/function.rb +12 -0
  21. data/lib/graphql/input_object_type.rb +1 -3
  22. data/lib/graphql/internal_representation/node.rb +14 -26
  23. data/lib/graphql/internal_representation/visit.rb +6 -3
  24. data/lib/graphql/introspection/arguments_field.rb +0 -1
  25. data/lib/graphql/introspection/enum_values_field.rb +0 -1
  26. data/lib/graphql/introspection/fields_field.rb +0 -1
  27. data/lib/graphql/introspection/input_fields_field.rb +0 -1
  28. data/lib/graphql/introspection/interfaces_field.rb +0 -1
  29. data/lib/graphql/introspection/of_type_field.rb +0 -1
  30. data/lib/graphql/introspection/possible_types_field.rb +0 -1
  31. data/lib/graphql/introspection/schema_field.rb +0 -1
  32. data/lib/graphql/introspection/type_by_name_field.rb +0 -1
  33. data/lib/graphql/introspection/typename_field.rb +0 -1
  34. data/lib/graphql/language.rb +0 -3
  35. data/lib/graphql/language/generation.rb +182 -3
  36. data/lib/graphql/language/lexer.rb +69 -144
  37. data/lib/graphql/language/lexer.rl +4 -15
  38. data/lib/graphql/language/nodes.rb +76 -136
  39. data/lib/graphql/language/parser.rb +621 -668
  40. data/lib/graphql/language/parser.y +11 -17
  41. data/lib/graphql/language/token.rb +3 -10
  42. data/lib/graphql/object_type.rb +6 -1
  43. data/lib/graphql/query.rb +13 -8
  44. data/lib/graphql/query/arguments.rb +33 -48
  45. data/lib/graphql/query/context.rb +1 -0
  46. data/lib/graphql/query/literal_input.rb +1 -4
  47. data/lib/graphql/relay/connection_resolve.rb +3 -0
  48. data/lib/graphql/relay/global_id_resolve.rb +5 -1
  49. data/lib/graphql/relay/relation_connection.rb +19 -14
  50. data/lib/graphql/schema.rb +219 -12
  51. data/lib/graphql/schema/argument.rb +33 -0
  52. data/lib/graphql/schema/build_from_definition.rb +18 -64
  53. data/lib/graphql/schema/enum.rb +76 -0
  54. data/lib/graphql/schema/field.rb +127 -0
  55. data/lib/graphql/schema/field/dynamic_resolve.rb +63 -0
  56. data/lib/graphql/schema/field/unwrapped_resolve.rb +20 -0
  57. data/lib/graphql/schema/input_object.rb +61 -0
  58. data/lib/graphql/schema/interface.rb +32 -0
  59. data/lib/graphql/schema/loader.rb +2 -2
  60. data/lib/graphql/schema/member.rb +97 -0
  61. data/lib/graphql/schema/member/build_type.rb +106 -0
  62. data/lib/graphql/schema/member/has_fields.rb +56 -0
  63. data/lib/graphql/schema/member/instrumentation.rb +113 -0
  64. data/lib/graphql/schema/member/list_type_proxy.rb +21 -0
  65. data/lib/graphql/schema/member/non_null_type_proxy.rb +21 -0
  66. data/lib/graphql/schema/object.rb +65 -0
  67. data/lib/graphql/schema/printer.rb +266 -33
  68. data/lib/graphql/schema/scalar.rb +25 -0
  69. data/lib/graphql/schema/traversal.rb +26 -17
  70. data/lib/graphql/schema/union.rb +48 -0
  71. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -5
  72. data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -15
  73. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +1 -11
  74. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +5 -7
  75. data/lib/graphql/tracing.rb +0 -1
  76. data/lib/graphql/tracing/platform_tracing.rb +7 -20
  77. data/lib/graphql/tracing/scout_tracing.rb +2 -2
  78. data/lib/graphql/unresolved_type_error.rb +2 -3
  79. data/lib/graphql/version.rb +1 -1
  80. data/readme.md +1 -1
  81. data/spec/dummy/app/channels/graphql_channel.rb +1 -22
  82. data/spec/dummy/log/development.log +0 -239
  83. data/spec/dummy/log/test.log +0 -204
  84. data/spec/dummy/test/system/action_cable_subscription_test.rb +0 -4
  85. data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
  86. data/spec/generators/graphql/function_generator_spec.rb +0 -26
  87. data/spec/generators/graphql/loader_generator_spec.rb +0 -24
  88. data/spec/graphql/analysis/max_query_complexity_spec.rb +3 -3
  89. data/spec/graphql/analysis/max_query_depth_spec.rb +3 -3
  90. data/spec/graphql/backtrace_spec.rb +0 -10
  91. data/spec/graphql/base_type_spec.rb +5 -19
  92. data/spec/graphql/boolean_type_spec.rb +3 -3
  93. data/spec/graphql/directive_spec.rb +1 -3
  94. data/spec/graphql/enum_type_spec.rb +5 -18
  95. data/spec/graphql/execution/execute_spec.rb +1 -1
  96. data/spec/graphql/execution/multiplex_spec.rb +2 -2
  97. data/spec/graphql/float_type_spec.rb +2 -2
  98. data/spec/graphql/id_type_spec.rb +1 -1
  99. data/spec/graphql/input_object_type_spec.rb +2 -15
  100. data/spec/graphql/int_type_spec.rb +2 -2
  101. data/spec/graphql/internal_representation/rewrite_spec.rb +2 -2
  102. data/spec/graphql/introspection/schema_type_spec.rb +0 -1
  103. data/spec/graphql/language/generation_spec.rb +186 -21
  104. data/spec/graphql/language/lexer_spec.rb +1 -21
  105. data/spec/graphql/language/nodes_spec.rb +12 -21
  106. data/spec/graphql/language/parser_spec.rb +1 -1
  107. data/spec/graphql/query/arguments_spec.rb +15 -37
  108. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +2 -2
  109. data/spec/graphql/query/variables_spec.rb +1 -1
  110. data/spec/graphql/query_spec.rb +5 -31
  111. data/spec/graphql/rake_task_spec.rb +1 -3
  112. data/spec/graphql/relay/base_connection_spec.rb +1 -1
  113. data/spec/graphql/relay/connection_instrumentation_spec.rb +2 -2
  114. data/spec/graphql/relay/connection_resolve_spec.rb +1 -1
  115. data/spec/graphql/relay/connection_type_spec.rb +1 -1
  116. data/spec/graphql/relay/mutation_spec.rb +3 -3
  117. data/spec/graphql/relay/relation_connection_spec.rb +1 -65
  118. data/spec/graphql/schema/build_from_definition_spec.rb +4 -86
  119. data/spec/graphql/schema/enum_spec.rb +60 -0
  120. data/spec/graphql/schema/field_spec.rb +14 -0
  121. data/spec/graphql/schema/input_object_spec.rb +43 -0
  122. data/spec/graphql/schema/interface_spec.rb +98 -0
  123. data/spec/graphql/schema/object_spec.rb +119 -0
  124. data/spec/graphql/schema/printer_spec.rb +15 -92
  125. data/spec/graphql/schema/scalar_spec.rb +40 -0
  126. data/spec/graphql/schema/union_spec.rb +35 -0
  127. data/spec/graphql/schema/validation_spec.rb +1 -1
  128. data/spec/graphql/schema/warden_spec.rb +11 -11
  129. data/spec/graphql/schema_spec.rb +25 -23
  130. data/spec/graphql/static_validation/rules/fields_have_appropriate_selections_spec.rb +2 -10
  131. data/spec/graphql/static_validation/rules/fields_will_merge_spec.rb +2 -2
  132. data/spec/graphql/string_type_spec.rb +3 -3
  133. data/spec/graphql/subscriptions_spec.rb +1 -1
  134. data/spec/graphql/tracing/platform_tracing_spec.rb +1 -60
  135. data/spec/support/dummy/schema.rb +25 -39
  136. data/spec/support/jazz.rb +334 -0
  137. data/spec/support/lazy_helpers.rb +21 -23
  138. data/spec/support/star_wars/data.rb +7 -6
  139. data/spec/support/star_wars/schema.rb +109 -142
  140. metadata +39 -33
  141. data/lib/graphql/execution/instrumentation.rb +0 -82
  142. data/lib/graphql/language/block_string.rb +0 -47
  143. data/lib/graphql/language/document_from_schema_definition.rb +0 -277
  144. data/lib/graphql/language/printer.rb +0 -351
  145. data/lib/graphql/tracing/data_dog_tracing.rb +0 -49
  146. data/spec/graphql/execution/instrumentation_spec.rb +0 -165
  147. data/spec/graphql/language/block_string_spec.rb +0 -70
  148. data/spec/graphql/language/document_from_schema_definition_spec.rb +0 -770
  149. 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 = GraphQL::ObjectType.define do
50
- name "LazySum"
51
- field :value, types.Int do
52
- resolve ->(o, a, c) { o == 13 ? nil : o }
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
- field :nestedSum, !LazySum do
55
- argument :value, !types.Int
56
- resolve ->(o, args, c) {
57
- if args[:value] == 13
58
- Wrapper.new(nil)
59
- else
60
- SumAll.new(c, o + args[:value])
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, types.Int
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 = GraphQL::Schema.define do
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
- DB = Sequel.connect('jdbc:sqlite:./_test_.db')
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, :basesClone
63
- def initialize(id:, name:, ships:, bases:, basesClone:)
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
- @basesClone = basesClone
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
- basesClone: Base.where(faction_id: 1),
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
- basesClone: Base.where(faction_id: 2),
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 = GraphQL::ObjectType.define do
7
- name "Ship"
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, types.String
9
+ field :name, String, null: true
11
10
  # Test cyclical connection types:
12
- connection :ships, Ship.connection_type
11
+ field :ships, Ship.connection_type, null: false
13
12
  end
14
13
 
15
- BaseType = GraphQL::ObjectType.define do
16
- name "Base"
17
- interfaces [GraphQL::Relay::Node.interface]
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, !types.String do
20
- resolve ->(obj, args, ctx) {
21
- LazyWrapper.new {
22
- if obj.id.nil?
23
- raise GraphQL::ExecutionError, "Boom!"
24
- else
25
- obj.name
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
- end
30
- field :planet, types.String
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 = GraphQL::ObjectType.define do
94
- name "Faction"
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
- connection :bases, BaseConnectionWithTotalCountType do
130
- # Resolve field should return an Array, the Connection
131
- # will do the rest!
132
- resolve ->(obj, args, ctx) {
133
- all_bases = Base.where(id: obj.bases)
134
- if args[:nameIncludes]
135
- all_bases = all_bases.where("name LIKE ?", "%#{args[:nameIncludes]}%")
136
- end
137
- all_bases
138
- }
139
- argument :nameIncludes, types.String
140
- end
141
-
142
- connection :basesClone, BaseType.connection_type
143
- connection :basesByName, BaseType.connection_type, property: :bases do
144
- argument :order, types.String, default_value: "name"
145
- resolve ->(obj, args, ctx) {
146
- if args[:order].present?
147
- obj.bases.order(args[:order])
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
- obj.bases
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
- connection :basesWithMaxLimitRelation, BaseType.connection_type, max_page_size: 2 do
155
- resolve ->(object, args, context) { Base.all }
156
- end
122
+ field :shipsWithMaxPageSize, max_page_size: 2, function: ShipsWithMaxPageSize.new
157
123
 
158
- connection :basesWithMaxLimitArray, BaseType.connection_type, max_page_size: 2 do
159
- resolve ->(object, args, context) { Base.all.to_a }
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
- connection :basesWithDefaultMaxLimitRelation, BaseType.connection_type do
163
- resolve ->(object, args, context) { Base.all }
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
- connection :basesWithDefaultMaxLimitArray, BaseType.connection_type do
167
- resolve ->(object, args, context) { Base.all.to_a }
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
- connection :basesWithLargeMaxLimitRelation, BaseType.connection_type, max_page_size: 1000 do
171
- resolve ->(object, args, context) { Base.all }
172
- end
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
- connection :basesAsSequelDataset, BaseConnectionWithTotalCountType, max_page_size: 1000 do
175
- argument :nameIncludes, types.String
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
- connection :basesWithCustomEdge, CustomEdgeBaseConnectionType do
186
- resolve ->(o, a, c) {
187
- LazyNodesWrapper.new(o.bases)
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 = GraphQL::ObjectType.define do
327
- name "Query"
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 :empire, Faction do
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 :largestBase, BaseType do
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
- connection :newestBasesGroupedByFaction, BaseType.connection_type do
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
- connection :basesWithNullName, BaseType.connection_type do
350
- resolve ->(obj, args, ctx) {
351
- [OpenStruct.new(id: nil)]
352
- }
353
- end
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, !types.ID
369
- resolve ->(o, a, c) {
370
- LazyLoader.defer(c, Base, a["id"])
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 = GraphQL::ObjectType.define do
376
- name "Mutation"
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 = GraphQL::Schema.define do
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 ->(type, object, ctx) {
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 ->(node_id, ctx) do
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 ->(object, type, ctx) do
393
+ def self.id_from_object(object, type, ctx)
427
394
  GraphQL::Schema::UniqueWithinType.encode(type.name, object.id)
428
395
  end
429
396