rails-graphql 1.0.0.beta → 1.0.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|