rails-graphql 1.0.0.beta → 1.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/gql_parser.c +1 -16
- data/ext/gql_parser.h +21 -0
- data/ext/shared.c +0 -5
- data/ext/shared.h +6 -6
- data/lib/generators/graphql/channel_generator.rb +27 -0
- data/lib/generators/graphql/controller_generator.rb +9 -4
- data/lib/generators/graphql/install_generator.rb +49 -0
- data/lib/generators/graphql/schema_generator.rb +9 -4
- data/lib/generators/graphql/templates/channel.erb +7 -0
- data/lib/generators/graphql/templates/config.rb +97 -0
- data/lib/generators/graphql/templates/controller.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +5 -3
- data/lib/gql_parser.so +0 -0
- data/lib/rails/graphql/alternative/field_set.rb +12 -0
- data/lib/rails/graphql/alternative/query.rb +13 -8
- data/lib/rails/graphql/alternative/subscription.rb +2 -1
- data/lib/rails/graphql/alternative.rb +4 -0
- data/lib/rails/graphql/argument.rb +5 -3
- data/lib/rails/graphql/callback.rb +10 -8
- data/lib/rails/graphql/collectors/hash_collector.rb +12 -1
- data/lib/rails/graphql/collectors/json_collector.rb +21 -0
- data/lib/rails/graphql/config.rb +86 -59
- data/lib/rails/graphql/directive/include_directive.rb +0 -1
- data/lib/rails/graphql/directive/skip_directive.rb +0 -1
- data/lib/rails/graphql/directive/specified_by_directive.rb +24 -0
- data/lib/rails/graphql/directive.rb +31 -25
- data/lib/rails/graphql/event.rb +7 -6
- data/lib/rails/graphql/field/authorized_field.rb +0 -5
- data/lib/rails/graphql/field/input_field.rb +0 -5
- data/lib/rails/graphql/field/mutation_field.rb +5 -6
- data/lib/rails/graphql/field/output_field.rb +13 -2
- data/lib/rails/graphql/field/proxied_field.rb +6 -6
- data/lib/rails/graphql/field/resolved_field.rb +1 -1
- data/lib/rails/graphql/field/subscription_field.rb +35 -52
- data/lib/rails/graphql/field/typed_field.rb +26 -2
- data/lib/rails/graphql/field.rb +20 -19
- data/lib/rails/graphql/global_id.rb +5 -1
- data/lib/rails/graphql/helpers/inherited_collection/array.rb +1 -0
- data/lib/rails/graphql/helpers/inherited_collection/base.rb +3 -1
- data/lib/rails/graphql/helpers/inherited_collection/hash.rb +2 -1
- data/lib/rails/graphql/helpers/registerable.rb +1 -1
- data/lib/rails/graphql/helpers/with_arguments.rb +3 -2
- data/lib/rails/graphql/helpers/with_assignment.rb +5 -5
- data/lib/rails/graphql/helpers/with_callbacks.rb +3 -3
- data/lib/rails/graphql/helpers/with_description.rb +10 -8
- data/lib/rails/graphql/helpers/with_directives.rb +5 -1
- data/lib/rails/graphql/helpers/with_events.rb +1 -0
- data/lib/rails/graphql/helpers/with_fields.rb +30 -24
- data/lib/rails/graphql/helpers/with_name.rb +3 -2
- data/lib/rails/graphql/helpers/with_schema_fields.rb +75 -51
- data/lib/rails/graphql/introspection.rb +1 -1
- data/lib/rails/graphql/railtie.rb +3 -2
- data/lib/rails/graphql/railties/app/base_channel.rb +10 -0
- data/lib/rails/graphql/railties/app/base_controller.rb +12 -0
- data/lib/rails/graphql/railties/app/views/_cable.js.erb +56 -0
- data/lib/rails/graphql/railties/app/views/_fetch.js.erb +20 -0
- data/lib/rails/graphql/railties/app/views/graphiql.html.erb +101 -0
- data/lib/rails/graphql/railties/base_generator.rb +3 -9
- data/lib/rails/graphql/railties/channel.rb +8 -8
- data/lib/rails/graphql/railties/controller.rb +51 -26
- data/lib/rails/graphql/request/arguments.rb +2 -1
- data/lib/rails/graphql/request/backtrace.rb +31 -10
- data/lib/rails/graphql/request/component/field.rb +15 -8
- data/lib/rails/graphql/request/component/fragment.rb +13 -7
- data/lib/rails/graphql/request/component/operation/subscription.rb +4 -6
- data/lib/rails/graphql/request/component/operation.rb +12 -5
- data/lib/rails/graphql/request/component/spread.rb +13 -4
- data/lib/rails/graphql/request/component/typename.rb +1 -1
- data/lib/rails/graphql/request/component.rb +2 -0
- data/lib/rails/graphql/request/context.rb +1 -1
- data/lib/rails/graphql/request/event.rb +6 -2
- data/lib/rails/graphql/request/helpers/directives.rb +1 -0
- data/lib/rails/graphql/request/helpers/selection_set.rb +10 -4
- data/lib/rails/graphql/request/helpers/value_writers.rb +8 -5
- data/lib/rails/graphql/request/prepared_data.rb +3 -1
- data/lib/rails/graphql/request/steps/organizable.rb +1 -1
- data/lib/rails/graphql/request/steps/preparable.rb +1 -1
- data/lib/rails/graphql/request/steps/resolvable.rb +1 -1
- data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +3 -3
- data/lib/rails/graphql/request/strategy.rb +18 -4
- data/lib/rails/graphql/request/subscription.rb +18 -16
- data/lib/rails/graphql/request.rb +71 -41
- data/lib/rails/graphql/schema.rb +39 -86
- data/lib/rails/graphql/shortcuts.rb +11 -5
- data/lib/rails/graphql/source/active_record/builders.rb +22 -24
- data/lib/rails/graphql/source/active_record_source.rb +96 -34
- data/lib/rails/graphql/source/base.rb +13 -40
- data/lib/rails/graphql/source/builder.rb +14 -22
- data/lib/rails/graphql/source/scoped_arguments.rb +10 -4
- data/lib/rails/graphql/source.rb +31 -38
- data/lib/rails/graphql/subscription/provider/action_cable.rb +10 -9
- data/lib/rails/graphql/subscription/provider/base.rb +6 -5
- data/lib/rails/graphql/subscription/store/base.rb +5 -9
- data/lib/rails/graphql/subscription/store/memory.rb +18 -9
- data/lib/rails/graphql/type/creator.rb +198 -0
- data/lib/rails/graphql/type/enum.rb +17 -9
- data/lib/rails/graphql/type/input.rb +30 -7
- data/lib/rails/graphql/type/interface.rb +15 -4
- data/lib/rails/graphql/type/object/directive_object.rb +6 -5
- data/lib/rails/graphql/type/object/input_value_object.rb +3 -4
- data/lib/rails/graphql/type/object/type_object.rb +40 -13
- data/lib/rails/graphql/type/object.rb +11 -6
- data/lib/rails/graphql/type/scalar/binary_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/date_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/date_time_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/decimal_scalar.rb +2 -0
- data/lib/rails/graphql/type/scalar/json_scalar.rb +3 -1
- data/lib/rails/graphql/type/scalar/time_scalar.rb +3 -1
- data/lib/rails/graphql/type/scalar.rb +2 -2
- data/lib/rails/graphql/type/union.rb +7 -2
- data/lib/rails/graphql/type.rb +10 -2
- data/lib/rails/graphql/type_map.rb +20 -7
- data/lib/rails/graphql/uri.rb +5 -4
- data/lib/rails/graphql/version.rb +6 -2
- data/lib/rails/graphql.rb +11 -8
- data/test/assets/introspection-mem.txt +1 -1
- data/test/assets/introspection.gql +2 -0
- data/test/assets/mem.gql +74 -60
- data/test/assets/mysql.gql +69 -55
- data/test/assets/sqlite.gql +78 -64
- data/test/assets/translate.gql +50 -39
- data/test/config.rb +2 -1
- data/test/graphql/schema_test.rb +2 -31
- data/test/graphql/source_test.rb +1 -11
- data/test/graphql/type/interface_test.rb +8 -5
- data/test/graphql/type/object_test.rb +8 -2
- data/test/graphql/type_map_test.rb +13 -16
- data/test/integration/global_id_test.rb +4 -4
- data/test/integration/memory/star_wars_validation_test.rb +2 -2
- data/test/integration/mysql/star_wars_introspection_test.rb +1 -1
- data/test/integration/resolver_precedence_test.rb +1 -1
- data/test/integration/schemas/memory.rb +3 -4
- data/test/integration/sqlite/star_wars_global_id_test.rb +27 -21
- data/test/integration/sqlite/star_wars_introspection_test.rb +1 -1
- data/test/integration/translate_test.rb +26 -14
- metadata +22 -9
@@ -70,8 +70,11 @@ module Rails
|
|
70
70
|
alias channel origin
|
71
71
|
|
72
72
|
delegate :action_name, to: :controller, allow_nil: true
|
73
|
+
delegate :find_type!, to: :strategy, allow_nil: true
|
73
74
|
delegate :all_listeners, :all_events, to: :schema
|
74
75
|
|
76
|
+
alias find_type find_type!
|
77
|
+
|
75
78
|
class << self
|
76
79
|
# Shortcut for initialize, set context, and execute
|
77
80
|
def execute(*args, schema: nil, namespace: :base, context: {}, **xargs)
|
@@ -92,12 +95,12 @@ module Rails
|
|
92
95
|
|
93
96
|
# Allow accessing component-based objects through the request
|
94
97
|
def const_defined?(name, *)
|
95
|
-
Component.const_defined?(name) || super
|
98
|
+
Component.const_defined?(name, false) || super
|
96
99
|
end
|
97
100
|
|
98
101
|
# Allow accessing component-based objects through the request
|
99
102
|
def const_missing(name)
|
100
|
-
Component.const_defined?(name) ? Component.const_get(name) : super
|
103
|
+
Component.const_defined?(name, false) ? Component.const_get(name, false) : super
|
101
104
|
end
|
102
105
|
end
|
103
106
|
|
@@ -105,8 +108,6 @@ module Rails
|
|
105
108
|
def initialize(schema = nil, namespace: :base)
|
106
109
|
@namespace = schema&.namespace || namespace
|
107
110
|
@schema = GraphQL::Schema.find!(@namespace)
|
108
|
-
@prepared_data = {}
|
109
|
-
@extensions = {}
|
110
111
|
|
111
112
|
ensure_schema!
|
112
113
|
end
|
@@ -126,6 +127,11 @@ module Rails
|
|
126
127
|
@context = build_ostruct(data).freeze
|
127
128
|
end
|
128
129
|
|
130
|
+
# Allow adding extra information to the response, in a extensions key
|
131
|
+
def extensions
|
132
|
+
@extensions ||= {}
|
133
|
+
end
|
134
|
+
|
129
135
|
# Execute a given document with the given arguments
|
130
136
|
def execute(document, **xargs)
|
131
137
|
output = xargs.delete(:as) || schema.config.default_response_format
|
@@ -134,14 +140,15 @@ module Rails
|
|
134
140
|
|
135
141
|
document, cache = nil, document if xargs.delete(:compiled)
|
136
142
|
prepared_data = xargs.delete(:data_for)
|
137
|
-
|
138
143
|
reset!(**xargs)
|
139
|
-
|
144
|
+
|
140
145
|
@response = initialize_response(output, formatter)
|
146
|
+
import_prepared_data(prepared_data)
|
141
147
|
execute!(document, cache)
|
142
148
|
|
143
149
|
response.public_send(formatter)
|
144
150
|
rescue StaticResponse
|
151
|
+
# TODO: Maybe change this to a throw/catch instead
|
145
152
|
response.public_send(formatter)
|
146
153
|
end
|
147
154
|
|
@@ -178,31 +185,43 @@ module Rails
|
|
178
185
|
# This is used by cache and static responses to jump from executing to
|
179
186
|
# delivery a response right away
|
180
187
|
def force_response(response, error = StaticResponse)
|
188
|
+
return unless defined?(@response)
|
181
189
|
@response = response
|
182
190
|
raise error
|
183
191
|
end
|
184
192
|
|
193
|
+
# Import prepared data that is formatted as a hash
|
194
|
+
def import_prepared_data(prepared_data)
|
195
|
+
prepared_data&.each do |key, value|
|
196
|
+
prepare_data_for(key, value)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
185
200
|
# Add a new prepared data from +value+ to the given +field+
|
186
201
|
def prepare_data_for(field, value, **options)
|
187
202
|
field = PreparedData.lookup(self, field)
|
188
203
|
|
189
|
-
if
|
190
|
-
|
204
|
+
if prepared_data.key?(field)
|
205
|
+
prepared_data[field].push(value)
|
191
206
|
else
|
192
|
-
|
207
|
+
prepared_data[field] = PreparedData.new(field, value, **options)
|
193
208
|
end
|
194
209
|
end
|
195
210
|
|
196
211
|
# Recover the next prepared data for the given field
|
197
212
|
def prepared_data_for(field)
|
213
|
+
return unless defined?(@prepared_data)
|
214
|
+
|
198
215
|
field = field.field if field.is_a?(Component::Field)
|
199
|
-
|
216
|
+
prepared_data[field]
|
200
217
|
end
|
201
218
|
|
202
219
|
# Check if the given field has prepared data
|
203
220
|
def prepared_data_for?(field)
|
221
|
+
return false unless defined?(@prepared_data)
|
222
|
+
|
204
223
|
field = field.field if field.is_a?(Component::Field)
|
205
|
-
|
224
|
+
prepared_data.key?(field)
|
206
225
|
end
|
207
226
|
|
208
227
|
# Build a easy-to-access object representing the current information of
|
@@ -231,7 +250,7 @@ module Rails
|
|
231
250
|
|
232
251
|
# A little helper to report an error on a given node
|
233
252
|
def report_node_error(message, node, **xargs)
|
234
|
-
xargs[:locations] ||= location_of(node)
|
253
|
+
xargs[:locations] ||= location_of(node)
|
235
254
|
report_error(message, **xargs)
|
236
255
|
end
|
237
256
|
|
@@ -251,8 +270,7 @@ module Rails
|
|
251
270
|
xargs[:path] ||= stack_to_path
|
252
271
|
errors.add(message, **xargs)
|
253
272
|
|
254
|
-
# Return nil for easier usage
|
255
|
-
nil
|
273
|
+
nil # Return nil for easier usage
|
256
274
|
end
|
257
275
|
|
258
276
|
# Add the given +object+ into the execution +stack+ and execute the given
|
@@ -271,25 +289,25 @@ module Rails
|
|
271
289
|
end.compact.reverse
|
272
290
|
end
|
273
291
|
|
274
|
-
# Add extensions to the request, which ensures a bunch of
|
275
|
-
# behaviors for all the objects created through the request
|
292
|
+
# Add class extensions to the request, which ensures a bunch of
|
293
|
+
# extended behaviors for all the objects created through the request
|
276
294
|
def extend(*modules)
|
277
|
-
|
278
|
-
request_ext =
|
295
|
+
import_class_extensions(*modules)
|
296
|
+
request_ext = class_extensions[self.class]
|
279
297
|
super(request_ext) if request_ext && !is_a?(request_ext)
|
280
298
|
end
|
281
299
|
|
282
|
-
# This initiates a new object which is aware of extensions
|
300
|
+
# This initiates a new object which is aware of class extensions
|
283
301
|
def build(klass, *args, &block)
|
284
|
-
ext_module =
|
302
|
+
ext_module = class_extensions[klass]
|
285
303
|
obj = klass.new(*args, &block)
|
286
304
|
obj.extend(ext_module) if ext_module
|
287
305
|
obj
|
288
306
|
end
|
289
307
|
|
290
|
-
# This allocates a new object which is aware of extensions
|
308
|
+
# This allocates a new object which is aware of class extensions
|
291
309
|
def build_from_cache(klass)
|
292
|
-
ext_module =
|
310
|
+
ext_module = class_extensions[klass]
|
293
311
|
obj = klass.allocate
|
294
312
|
obj.extend(ext_module) if ext_module
|
295
313
|
obj
|
@@ -349,6 +367,7 @@ module Rails
|
|
349
367
|
resolve_from_cache = (version == schema.version)
|
350
368
|
|
351
369
|
# Run the document from scratch if TypeMap has changed
|
370
|
+
# TODO: We need to save the new organized document
|
352
371
|
return run_document unless resolve_from_cache
|
353
372
|
@valid_cache = true unless defined?(@valid_cache)
|
354
373
|
|
@@ -360,9 +379,19 @@ module Rails
|
|
360
379
|
@strategy.resolve!
|
361
380
|
end
|
362
381
|
|
363
|
-
|
382
|
+
protected
|
364
383
|
|
365
|
-
|
384
|
+
# Stores all the class extensions
|
385
|
+
def class_extensions
|
386
|
+
@class_extensions ||= {}
|
387
|
+
end
|
388
|
+
|
389
|
+
# Stores all the prepared data, but only when it is needed
|
390
|
+
def prepared_data
|
391
|
+
@prepared_data ||= {}
|
392
|
+
end
|
393
|
+
|
394
|
+
private
|
366
395
|
|
367
396
|
# Reset principal variables and set the given +args+
|
368
397
|
def reset!(args: nil, variables: {}, operation_name: nil, origin: nil)
|
@@ -382,8 +411,6 @@ module Rails
|
|
382
411
|
@stack = [schema]
|
383
412
|
@cache = {}
|
384
413
|
@log_extra = {}
|
385
|
-
@fragments = {}
|
386
|
-
@operations = {}
|
387
414
|
@subscriptions = {}
|
388
415
|
@used_variables = Set.new
|
389
416
|
|
@@ -401,14 +428,18 @@ module Rails
|
|
401
428
|
ensure
|
402
429
|
report_unused_variables
|
403
430
|
write_cache_request(cache) if cache.present? && !valid_cache?
|
431
|
+
@response.try(:append_errors, errors)
|
432
|
+
|
433
|
+
if defined?(@extensions)
|
434
|
+
@response.try(:append_extensions, @extensions)
|
435
|
+
@extensions.clear
|
436
|
+
end
|
404
437
|
|
405
438
|
@cache.clear
|
406
439
|
@strategy&.clear
|
407
440
|
@fragments&.clear
|
408
|
-
@operations
|
409
|
-
@prepared_data
|
410
|
-
|
411
|
-
@response.try(:append_errors, errors)
|
441
|
+
@operations&.clear
|
442
|
+
@prepared_data&.clear
|
412
443
|
end
|
413
444
|
|
414
445
|
# Prepare the definitions, find the strategy and resolve
|
@@ -416,9 +447,8 @@ module Rails
|
|
416
447
|
return if @document.nil?
|
417
448
|
|
418
449
|
collect_definitions!
|
419
|
-
|
420
450
|
@strategy ||= find_strategy!
|
421
|
-
@strategy.trigger_event(:request)
|
451
|
+
@strategy.trigger_event(:request) if with == :resolve!
|
422
452
|
@strategy.public_send(with)
|
423
453
|
end
|
424
454
|
|
@@ -440,26 +470,26 @@ module Rails
|
|
440
470
|
build(klass, self)
|
441
471
|
end
|
442
472
|
|
443
|
-
# Find all necessary extensions inside the given +modules+
|
444
|
-
# the extension base module
|
445
|
-
def
|
473
|
+
# Find all necessary class extensions inside the given +modules+
|
474
|
+
# and prepare the extension base module
|
475
|
+
def import_class_extensions(*modules)
|
446
476
|
modules.each do |mod|
|
447
477
|
mod.constants.each do |const_name|
|
448
478
|
const_name = const_name.to_s
|
449
|
-
const = mod.const_get(const_name)
|
479
|
+
const = mod.const_get(const_name, false)
|
450
480
|
next unless const.is_a?(Module)
|
451
481
|
|
452
482
|
# Find the related request class to extend
|
453
483
|
klass = const_name === 'Request' ? self.class : begin
|
454
484
|
const_name.split('_').inject(self.class) do |k, next_const|
|
455
|
-
k.const_defined?(next_const) ? k.const_get(next_const) : break
|
485
|
+
k.const_defined?(next_const) ? k.const_get(next_const, false) : break
|
456
486
|
end
|
457
487
|
end
|
458
488
|
|
459
|
-
# Create the shared module and include the extension
|
489
|
+
# Create the shared module and include the class extension
|
460
490
|
next unless klass&.is_a?(Class)
|
461
|
-
|
462
|
-
|
491
|
+
class_extensions[klass] ||= Module.new
|
492
|
+
class_extensions[klass].include(const)
|
463
493
|
end
|
464
494
|
end
|
465
495
|
end
|
@@ -477,7 +507,7 @@ module Rails
|
|
477
507
|
# Build the payload to be sent to the log
|
478
508
|
def log_payload(data)
|
479
509
|
name = @operation_name.presence
|
480
|
-
name ||= operations.keys.first if operations
|
510
|
+
name ||= operations.keys.first if operations&.size&.eql?(1)
|
481
511
|
map_variables = args.to_h.transform_keys do |key|
|
482
512
|
@arg_names[key.to_s]
|
483
513
|
end
|
data/lib/rails/graphql/schema.rb
CHANGED
@@ -57,7 +57,7 @@ module Rails
|
|
57
57
|
# :singleton-method:
|
58
58
|
# Since there are only one schema per namespace, the name is constant
|
59
59
|
def gql_name
|
60
|
-
'
|
60
|
+
'__Schema'
|
61
61
|
end
|
62
62
|
|
63
63
|
alias graphql_name gql_name
|
@@ -136,21 +136,6 @@ module Rails
|
|
136
136
|
namespace
|
137
137
|
end
|
138
138
|
|
139
|
-
# Return the subscription provider for the current schema
|
140
|
-
def subscription_provider
|
141
|
-
if !defined?(@subscription_provider)
|
142
|
-
@subscription_provider = config.default_subscription_provider
|
143
|
-
subscription_provider
|
144
|
-
elsif @subscription_provider.is_a?(String)
|
145
|
-
provider = (name = @subscription_provider).safe_constantize
|
146
|
-
return @subscription_provider = provider.new(logger: logger) unless provider.nil?
|
147
|
-
|
148
|
-
raise ::NameError, +"uninitialized constant #{name}"
|
149
|
-
else
|
150
|
-
@subscription_provider
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
139
|
# Check if the schema is valid
|
155
140
|
def valid?
|
156
141
|
defined?(@validated) && @validated
|
@@ -221,6 +206,35 @@ module Rails
|
|
221
206
|
type_map.fetch!(directive, **xargs)
|
222
207
|
end
|
223
208
|
|
209
|
+
# See {Request}[rdoc-ref:Rails::GraphQL::Request]
|
210
|
+
def request
|
211
|
+
return if self == ::Rails::GraphQL::Schema
|
212
|
+
Rails::GraphQL::Request.new(self)
|
213
|
+
end
|
214
|
+
|
215
|
+
# See {Request}[rdoc-ref:Rails::GraphQL::Request]
|
216
|
+
def execute(*args, **xargs)
|
217
|
+
return if self == ::Rails::GraphQL::Schema
|
218
|
+
Rails::GraphQL::Request.execute(*args, **xargs, schema: self)
|
219
|
+
end
|
220
|
+
|
221
|
+
alias perform execute
|
222
|
+
|
223
|
+
# Return the subscription provider for the current schema
|
224
|
+
def subscription_provider
|
225
|
+
if !defined?(@subscription_provider)
|
226
|
+
@subscription_provider = config.subscription_provider || config.default_subscription_provider
|
227
|
+
subscription_provider
|
228
|
+
elsif @subscription_provider.is_a?(String)
|
229
|
+
provider = (name = @subscription_provider).safe_constantize
|
230
|
+
return @subscription_provider = provider.new(logger: logger) unless provider.nil?
|
231
|
+
|
232
|
+
raise ::NameError, +"uninitialized constant #{name}"
|
233
|
+
else
|
234
|
+
@subscription_provider
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
224
238
|
# Remove subscriptions by their provided +sids+
|
225
239
|
def remove_subscriptions(*sids)
|
226
240
|
subscription_provider&.remove(*sids)
|
@@ -286,12 +300,10 @@ module Rails
|
|
286
300
|
end
|
287
301
|
|
288
302
|
protected
|
289
|
-
|
290
303
|
attr_writer :subscription_provider
|
291
304
|
|
292
305
|
# Mark the given class to be pending of registration
|
293
306
|
def inherited(subclass)
|
294
|
-
subclass.spec_object = false
|
295
307
|
subclass.abstract = false
|
296
308
|
super if defined? super
|
297
309
|
|
@@ -301,6 +313,11 @@ module Rails
|
|
301
313
|
end
|
302
314
|
end
|
303
315
|
|
316
|
+
# Syntax sugar for beginners
|
317
|
+
def field(*args, **xargs, &block)
|
318
|
+
add_field(:query, *args, **xargs, &block)
|
319
|
+
end
|
320
|
+
|
304
321
|
# Indicate to type map that the current schema depends on all the
|
305
322
|
# files in the provided +path+ directory
|
306
323
|
def load_directory(dir = '.', recursive: true)
|
@@ -376,20 +393,9 @@ module Rails
|
|
376
393
|
superclass ||= GraphQL::Source.find_for!(object)
|
377
394
|
|
378
395
|
xargs[:suffix] = 'Source'
|
379
|
-
|
380
|
-
schema_namespace = namespace
|
381
|
-
|
382
|
-
create_klass(object, superclass, GraphQL::Source, **xargs) do
|
383
|
-
set_namespace schema_namespace
|
384
|
-
|
385
|
-
xargs.each do |key, value|
|
386
|
-
_, segment = key.to_s.split('skip_on_')
|
387
|
-
skip_on segment, value if segment.present?
|
388
|
-
end
|
396
|
+
xargs[:build] = build
|
389
397
|
|
390
|
-
|
391
|
-
build_all if create_and_build
|
392
|
-
end
|
398
|
+
create_type(object, superclass, **xargs, &block)
|
393
399
|
end
|
394
400
|
|
395
401
|
# Helper method to create multiple sources with the same type
|
@@ -402,61 +408,8 @@ module Rails
|
|
402
408
|
|
403
409
|
# A simpler way to create a new type object without having to create
|
404
410
|
# a class in a different file
|
405
|
-
def create_type(
|
406
|
-
|
407
|
-
xargs[:suffix] ||= superclass.base_type.name.demodulize
|
408
|
-
|
409
|
-
create_klass(name, superclass, GraphQL::Type, **xargs, &block)
|
410
|
-
end
|
411
|
-
|
412
|
-
private
|
413
|
-
|
414
|
-
# Helper to create objects that are actually classes of a given
|
415
|
-
# +superclass+ ensuring that it inherits from +base_class+.
|
416
|
-
#
|
417
|
-
# The +suffix+ option can ensures that the name of the created
|
418
|
-
# class ends with a specific suffix.
|
419
|
-
def create_klass(name_or_object, superclass, base_class = nil, **xargs, &block)
|
420
|
-
name = name_or_object.is_a?(Module) ? name_or_object.name : name_or_object.to_s
|
421
|
-
|
422
|
-
base_module = name.classify.deconstantize
|
423
|
-
base_module.prepend('GraphQL::') unless base_module =~ /^GraphQL(::|$)/
|
424
|
-
base_module = base_module.delete_suffix('::').constantize
|
425
|
-
|
426
|
-
klass_name = name.classify.demodulize
|
427
|
-
klass_name += xargs[:suffix] if xargs.key?(:suffix) &&
|
428
|
-
!klass_name.end_with?(xargs[:suffix])
|
429
|
-
|
430
|
-
if base_module.const_defined?(klass_name)
|
431
|
-
klass = base_module.const_get(klass_name)
|
432
|
-
|
433
|
-
raise DuplicatedError, (+<<~MSG).squish unless !xargs[:once] && klass < superclass
|
434
|
-
A constant named "#{klass_name}" already exists for the
|
435
|
-
"#{base_module.name}" module.
|
436
|
-
MSG
|
437
|
-
|
438
|
-
# This likely happened because the classes are being reloaded, so
|
439
|
-
# call inherited again as if the class has just been created
|
440
|
-
superclass.inherited(klass)
|
441
|
-
else
|
442
|
-
base_class ||= superclass.ancestors.find { |k| k.superclass === Class }
|
443
|
-
|
444
|
-
valid = superclass.is_a?(Module) && superclass < base_class
|
445
|
-
raise DefinitionError, (+<<~MSG).squish unless valid
|
446
|
-
The given "#{superclass}" superclass does not inherites from
|
447
|
-
#{base_class.name} class.
|
448
|
-
MSG
|
449
|
-
|
450
|
-
klass = base_module.const_set(klass_name, Class.new(superclass))
|
451
|
-
end
|
452
|
-
|
453
|
-
klass.abstract = xargs[:abstract] if xargs.key?(:abstract)
|
454
|
-
klass.assigned_to = name_or_object if name_or_object.is_a?(Module) &&
|
455
|
-
klass.is_a?(Helpers::WithAssignment)
|
456
|
-
|
457
|
-
klass.set_namespace(namespace)
|
458
|
-
klass.module_exec(&block) if block.present?
|
459
|
-
klass
|
411
|
+
def create_type(*args, **xargs, &block)
|
412
|
+
GraphQL::Type.create!(self, *args, **xargs, &block)
|
460
413
|
end
|
461
414
|
end
|
462
415
|
end
|
@@ -2,23 +2,31 @@
|
|
2
2
|
|
3
3
|
# This exposed module allows some shortcuts while working outside of the gem
|
4
4
|
module GraphQL
|
5
|
+
autoload :BaseController, "#{__dir__}/railties/app/base_controller.rb"
|
6
|
+
autoload :BaseChannel, "#{__dir__}/railties/app/base_channel.rb"
|
7
|
+
|
5
8
|
# List of constant shortcuts, as string to not trigger autoload
|
6
9
|
CONST_SHORTCUTS = {
|
7
10
|
CacheKey: '::Rails::GraphQL::CacheKey',
|
8
11
|
Channel: '::Rails::GraphQL::Channel',
|
9
12
|
Controller: '::Rails::GraphQL::Controller',
|
10
13
|
Directive: '::Rails::GraphQL::Directive',
|
11
|
-
Field: '::Rails::GraphQL::Field',
|
12
14
|
GlobalID: '::Rails::GraphQL::GlobalID',
|
13
15
|
Request: '::Rails::GraphQL::Request',
|
14
16
|
Schema: '::Rails::GraphQL::Schema',
|
15
17
|
Source: '::Rails::GraphQL::Source',
|
16
18
|
Type: '::Rails::GraphQL::Type',
|
17
19
|
|
20
|
+
Field: '::Rails::GraphQL::Alternative::Field',
|
18
21
|
Query: '::Rails::GraphQL::Alternative::Query',
|
19
22
|
Mutation: '::Rails::GraphQL::Alternative::Mutation',
|
20
23
|
Subscription: '::Rails::GraphQL::Alternative::Subscription',
|
21
24
|
|
25
|
+
FieldSet: '::Rails::GraphQL::Alternative::FieldSet',
|
26
|
+
QuerySet: '::Rails::GraphQL::Alternative::QuerySet',
|
27
|
+
MutationSet: '::Rails::GraphQL::Alternative::MutationSet',
|
28
|
+
SubscriptionSet: '::Rails::GraphQL::Alternative::SubscriptionSet',
|
29
|
+
|
22
30
|
Enum: '::Rails::GraphQL::Type::Enum',
|
23
31
|
Input: '::Rails::GraphQL::Type::Input',
|
24
32
|
Interface: '::Rails::GraphQL::Type::Interface',
|
@@ -26,9 +34,6 @@ module GraphQL
|
|
26
34
|
Scalar: '::Rails::GraphQL::Type::Scalar',
|
27
35
|
Union: '::Rails::GraphQL::Type::Union',
|
28
36
|
|
29
|
-
ProxyField: '::Rails::GraphQL::Field::ProxyField',
|
30
|
-
AssociationField: '::Rails::GraphQL::Field::AssociationField',
|
31
|
-
|
32
37
|
BaseSource: '::Rails::GraphQL::Source::BaseSource',
|
33
38
|
ActiveRecordSource: '::Rails::GraphQL::Source::ActiveRecordSource',
|
34
39
|
}.freeze
|
@@ -43,7 +48,8 @@ module GraphQL
|
|
43
48
|
#
|
44
49
|
# Rails::GraphQL::Directive::DeprecatedDirective(...)
|
45
50
|
# # => Rails::GraphQL::Directive::DeprecatedDirective.new(...)
|
46
|
-
DIRECTIVE_SHORTCUTS = %i[DeprecatedDirective IncludeDirective SkipDirective
|
51
|
+
DIRECTIVE_SHORTCUTS = %i[DeprecatedDirective IncludeDirective SkipDirective
|
52
|
+
SpecifiedByDirective].freeze
|
47
53
|
|
48
54
|
class << self
|
49
55
|
delegate *DIRECTIVE_SHORTCUTS, to: 'Rails::GraphQL::Directive'
|
@@ -4,11 +4,6 @@ module Rails
|
|
4
4
|
module GraphQL
|
5
5
|
# All the helper methods for building the source
|
6
6
|
module Source::ActiveRecordSource::Builders
|
7
|
-
# Override the object class to identify interfaces due to STI
|
8
|
-
def object_class
|
9
|
-
sti_interface? ? interface_class : super
|
10
|
-
end
|
11
|
-
|
12
7
|
# List of all columns that should be threated as IDs
|
13
8
|
# TODO: Add a exclusive cache for the build process
|
14
9
|
def id_columns
|
@@ -62,9 +57,7 @@ module Rails
|
|
62
57
|
# Check if the given model is consider an interface due to single table
|
63
58
|
# inheritance and the given model is the base class
|
64
59
|
def sti_interface?
|
65
|
-
|
66
|
-
model.has_attribute?(model.inheritance_column) && model.base_class == model
|
67
|
-
end
|
60
|
+
model.has_attribute?(model.inheritance_column) && model.base_class == model
|
68
61
|
end
|
69
62
|
|
70
63
|
# Build all enums associated to the class, collecting them from the
|
@@ -73,8 +66,8 @@ module Rails
|
|
73
66
|
return remove_instance_variable(:@enums) if enums.blank?
|
74
67
|
|
75
68
|
@enums = enums.each_with_object({}) do |(attribute, setting), hash|
|
76
|
-
class_name = base_name + attribute.to_s.classify
|
77
|
-
hash[attribute.to_s] = create_enum(class_name, setting
|
69
|
+
class_name = base_name.tr('_', '') + attribute.to_s.classify
|
70
|
+
hash[attribute.to_s] = create_enum(class_name, setting)
|
78
71
|
rescue DuplicatedError
|
79
72
|
next
|
80
73
|
end.freeze
|
@@ -82,15 +75,14 @@ module Rails
|
|
82
75
|
|
83
76
|
# Build all necessary attribute fields into the given +holder+
|
84
77
|
def build_attribute_fields(holder, **field_options)
|
85
|
-
each_attribute(holder) do |key, type, **options|
|
86
|
-
next if holder.field?(key) || skip_field?(key, on: holder.kind)
|
87
78
|
|
79
|
+
each_attribute(holder) do |key, type, **options|
|
88
80
|
str_key = key.to_s
|
89
81
|
type = (defined?(@enums) && @enums.key?(str_key) && @enums[str_key]) ||
|
90
82
|
(id_columns.include?(str_key) && :id) || type
|
91
83
|
|
92
84
|
options[:null] = !attr_required?(key) unless options.key?(:null)
|
93
|
-
holder.
|
85
|
+
holder.safe_field(key, type, **options.merge(field_options[key] || {}))
|
94
86
|
end
|
95
87
|
end
|
96
88
|
|
@@ -99,20 +91,22 @@ module Rails
|
|
99
91
|
return unless with_associations?
|
100
92
|
|
101
93
|
each_reflection do |item|
|
102
|
-
next if holder.
|
94
|
+
next if holder.has_field?(item.name) || item.polymorphic? ||
|
103
95
|
skip_field?(item.name, on: holder.kind)
|
104
96
|
|
105
97
|
type_map_after_register(item.klass) do |type|
|
106
|
-
next unless
|
107
|
-
type.
|
98
|
+
next unless type.try(:assigned_to) != item.klass ||
|
99
|
+
type.input_type? || type.leaf_type?
|
108
100
|
|
109
101
|
options = reflection_to_options(item)
|
110
102
|
|
111
|
-
if
|
112
|
-
|
113
|
-
|
103
|
+
if item.collection?
|
104
|
+
owner = type.try(:owner)
|
105
|
+
source = owner.is_a?(Helpers::WithSchemaFields) &&
|
106
|
+
(owner.try(:collection_field) || owner.query_fields.try(:[], item.name))
|
114
107
|
|
115
|
-
if
|
108
|
+
if source.present?
|
109
|
+
proxy_options = options.merge(alias: item.name, of_type: :proxy)
|
116
110
|
field = holder.safe_field(source, **proxy_options)
|
117
111
|
end
|
118
112
|
end
|
@@ -130,16 +124,20 @@ module Rails
|
|
130
124
|
def build_reflection_inputs(holder)
|
131
125
|
return unless with_associations?
|
132
126
|
|
127
|
+
suffix = GraphQL.config.auto_suffix_input_objects
|
133
128
|
model.nested_attributes_options.each_key do |reflection_name|
|
134
|
-
|
129
|
+
reflection = model._reflect_on_association(reflection_name)
|
130
|
+
next if reflection.nil? || reflection.polymorphic?
|
135
131
|
|
136
132
|
expected_name = reflection.klass.name.tr(':', '')
|
137
|
-
expected_name +=
|
133
|
+
expected_name += suffix unless expected_name.end_with?(suffix)
|
134
|
+
|
135
|
+
type_map_after_register(expected_name) do |type|
|
136
|
+
next unless type.input?
|
138
137
|
|
139
|
-
type_map_after_register(expected_name) do |input|
|
140
138
|
options = reflection_to_options(reflection).merge(null: true)
|
141
139
|
field_name = "#{reflection.name}_attributes"
|
142
|
-
holder.safe_field(field_name,
|
140
|
+
holder.safe_field(field_name, type, **options)
|
143
141
|
end
|
144
142
|
end
|
145
143
|
end
|