graphql 1.9.17 → 1.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/templates/schema.erb +7 -0
- data/lib/graphql/analysis/ast/field_usage.rb +1 -1
- data/lib/graphql/analysis/ast/visitor.rb +3 -3
- data/lib/graphql/analysis/ast.rb +12 -11
- data/lib/graphql/argument.rb +7 -35
- data/lib/graphql/backtrace/table.rb +10 -2
- data/lib/graphql/base_type.rb +4 -0
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
- data/lib/graphql/define/assign_enum_value.rb +1 -1
- data/lib/graphql/define/assign_object_field.rb +3 -3
- data/lib/graphql/define/defined_object_proxy.rb +8 -2
- data/lib/graphql/define/instance_definable.rb +10 -106
- data/lib/graphql/directive/deprecated_directive.rb +1 -12
- data/lib/graphql/directive.rb +4 -1
- data/lib/graphql/enum_type.rb +5 -71
- data/lib/graphql/execution/directive_checks.rb +2 -2
- data/lib/graphql/execution/errors.rb +2 -3
- data/lib/graphql/execution/execute.rb +1 -1
- data/lib/graphql/execution/interpreter/runtime.rb +106 -55
- data/lib/graphql/execution/interpreter.rb +5 -11
- data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
- data/lib/graphql/execution/lookahead.rb +5 -5
- data/lib/graphql/execution/multiplex.rb +13 -3
- data/lib/graphql/field.rb +9 -117
- data/lib/graphql/filter.rb +1 -1
- data/lib/graphql/function.rb +1 -30
- data/lib/graphql/input_object_type.rb +2 -24
- data/lib/graphql/interface_type.rb +2 -23
- data/lib/graphql/introspection/base_object.rb +2 -5
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +7 -7
- data/lib/graphql/introspection/input_value_type.rb +27 -9
- data/lib/graphql/introspection/schema_type.rb +1 -6
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/language/definition_slice.rb +21 -10
- data/lib/graphql/language/document_from_schema_definition.rb +50 -44
- data/lib/graphql/language/nodes.rb +3 -3
- data/lib/graphql/language/parser.rb +644 -646
- data/lib/graphql/language/parser.y +6 -4
- data/lib/graphql/language.rb +1 -1
- data/lib/graphql/non_null_type.rb +0 -10
- data/lib/graphql/object_type.rb +1 -21
- data/lib/graphql/pagination/active_record_relation_connection.rb +35 -0
- data/lib/graphql/pagination/array_connection.rb +77 -0
- data/lib/graphql/pagination/connection.rb +171 -0
- data/lib/graphql/pagination/connections.rb +108 -0
- data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
- data/lib/graphql/pagination/relation_connection.rb +151 -0
- data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
- data/lib/graphql/pagination.rb +6 -0
- data/lib/graphql/query/arguments.rb +2 -1
- data/lib/graphql/query/context.rb +2 -5
- data/lib/graphql/query/literal_input.rb +30 -10
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query/variables.rb +7 -3
- data/lib/graphql/query.rb +9 -5
- data/lib/graphql/relay/base_connection.rb +4 -0
- data/lib/graphql/relay/connection_type.rb +2 -1
- data/lib/graphql/relay/edge_type.rb +1 -0
- data/lib/graphql/relay/edges_instrumentation.rb +1 -1
- data/lib/graphql/relay/mutation.rb +1 -86
- data/lib/graphql/relay/node.rb +2 -2
- data/lib/graphql/scalar_type.rb +1 -58
- data/lib/graphql/schema/argument.rb +51 -6
- data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +10 -4
- data/lib/graphql/schema/build_from_definition.rb +167 -178
- data/lib/graphql/schema/built_in_types.rb +5 -5
- data/lib/graphql/schema/directive/deprecated.rb +18 -0
- data/lib/graphql/schema/directive.rb +28 -2
- data/lib/graphql/schema/enum.rb +40 -3
- data/lib/graphql/schema/enum_value.rb +5 -1
- data/lib/graphql/schema/field/connection_extension.rb +11 -1
- data/lib/graphql/schema/field.rb +59 -31
- data/lib/graphql/schema/find_inherited_value.rb +13 -0
- data/lib/graphql/schema/finder.rb +13 -11
- data/lib/graphql/schema/input_object.rb +107 -2
- data/lib/graphql/schema/interface.rb +10 -7
- data/lib/graphql/schema/introspection_system.rb +108 -37
- data/lib/graphql/schema/late_bound_type.rb +1 -0
- data/lib/graphql/schema/list.rb +41 -0
- data/lib/graphql/schema/loader.rb +16 -4
- data/lib/graphql/schema/member/base_dsl_methods.rb +21 -11
- data/lib/graphql/schema/member/build_type.rb +5 -1
- data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/member/has_ast_node.rb +17 -0
- data/lib/graphql/schema/member/has_fields.rb +4 -4
- data/lib/graphql/schema/member/validates_input.rb +33 -0
- data/lib/graphql/schema/member.rb +5 -0
- data/lib/graphql/schema/mutation.rb +1 -1
- data/lib/graphql/schema/non_null.rb +25 -0
- data/lib/graphql/schema/object.rb +15 -5
- data/lib/graphql/schema/printer.rb +1 -2
- data/lib/graphql/schema/relay_classic_mutation.rb +1 -1
- data/lib/graphql/schema/resolver.rb +3 -15
- data/lib/graphql/schema/scalar.rb +19 -3
- data/lib/graphql/schema/subscription.rb +5 -5
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +21 -13
- data/lib/graphql/schema/type_membership.rb +2 -2
- data/lib/graphql/schema/union.rb +2 -3
- data/lib/graphql/schema/validation.rb +2 -2
- data/lib/graphql/schema/warden.rb +45 -20
- data/lib/graphql/schema.rb +764 -151
- data/lib/graphql/static_validation/base_visitor.rb +10 -6
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +9 -4
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +10 -7
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +4 -4
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -3
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +1 -1
- data/lib/graphql/static_validation/type_stack.rb +2 -2
- data/lib/graphql/static_validation/validator.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -3
- data/lib/graphql/subscriptions/event.rb +7 -4
- data/lib/graphql/subscriptions/instrumentation.rb +10 -5
- data/lib/graphql/subscriptions/subscription_root.rb +0 -1
- data/lib/graphql/subscriptions.rb +34 -9
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +14 -10
- data/lib/graphql/tracing/appsignal_tracing.rb +8 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +8 -0
- data/lib/graphql/tracing/new_relic_tracing.rb +8 -0
- data/lib/graphql/tracing/platform_tracing.rb +26 -6
- data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
- data/lib/graphql/tracing/scout_tracing.rb +8 -0
- data/lib/graphql/tracing/skylight_tracing.rb +8 -0
- data/lib/graphql/tracing.rb +7 -3
- data/lib/graphql/types/int.rb +1 -1
- data/lib/graphql/types/relay/base_connection.rb +3 -1
- data/lib/graphql/union_type.rb +13 -28
- data/lib/graphql/unresolved_type_error.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +2 -1
- metadata +15 -4
@@ -28,6 +28,17 @@ module GraphQL
|
|
28
28
|
nil
|
29
29
|
elsif value.nil?
|
30
30
|
nil
|
31
|
+
elsif value.is_a?(GraphQL::Pagination::Connection)
|
32
|
+
# update the connection with some things that may not have been provided
|
33
|
+
value.context ||= context
|
34
|
+
value.first_value ||= arguments[:first]
|
35
|
+
value.after_value ||= arguments[:after]
|
36
|
+
value.last_value ||= arguments[:last]
|
37
|
+
value.before_value ||= arguments[:before]
|
38
|
+
value.max_page_size ||= field.max_page_size
|
39
|
+
value
|
40
|
+
elsif context.schema.new_connections?
|
41
|
+
context.schema.connections.wrap(field, value, arguments, context)
|
31
42
|
else
|
32
43
|
if object.is_a?(GraphQL::Schema::Object)
|
33
44
|
object = object.object
|
@@ -43,7 +54,6 @@ module GraphQL
|
|
43
54
|
)
|
44
55
|
end
|
45
56
|
end
|
46
|
-
|
47
57
|
end
|
48
58
|
end
|
49
59
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -13,8 +13,10 @@ module GraphQL
|
|
13
13
|
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
14
14
|
include GraphQL::Schema::Member::AcceptsDefinition
|
15
15
|
include GraphQL::Schema::Member::HasArguments
|
16
|
+
include GraphQL::Schema::Member::HasAstNode
|
16
17
|
include GraphQL::Schema::Member::HasPath
|
17
18
|
extend GraphQL::Schema::FindInheritedValue
|
19
|
+
include GraphQL::Schema::FindInheritedValue::EmptyObjects
|
18
20
|
|
19
21
|
# @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
|
20
22
|
attr_reader :name
|
@@ -35,9 +37,9 @@ module GraphQL
|
|
35
37
|
attr_reader :resolver_method
|
36
38
|
|
37
39
|
# @return [Class] The type that this field belongs to
|
38
|
-
|
40
|
+
attr_accessor :owner
|
39
41
|
|
40
|
-
# @return [
|
42
|
+
# @return [Symbol] the original name of the field, passed in by the user
|
41
43
|
attr_reader :original_name
|
42
44
|
|
43
45
|
# @return [Class, nil] The {Schema::Resolver} this field was derived from, if there is one
|
@@ -51,7 +53,7 @@ module GraphQL
|
|
51
53
|
attr_reader :trace
|
52
54
|
|
53
55
|
# @return [String, nil]
|
54
|
-
|
56
|
+
attr_accessor :subscription_scope
|
55
57
|
|
56
58
|
# Create a field instance from a list of arguments, keyword arguments, and a block.
|
57
59
|
#
|
@@ -152,6 +154,14 @@ module GraphQL
|
|
152
154
|
end
|
153
155
|
end
|
154
156
|
|
157
|
+
# @return Boolean
|
158
|
+
attr_reader :relay_node_field
|
159
|
+
|
160
|
+
# @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
|
161
|
+
def method_conflict_warning?
|
162
|
+
@method_conflict_warning
|
163
|
+
end
|
164
|
+
|
155
165
|
# @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
|
156
166
|
# @param type [Class, GraphQL::BaseType, Array] The return type of this field
|
157
167
|
# @param owner [Class] The type that this field belongs to
|
@@ -175,7 +185,9 @@ module GraphQL
|
|
175
185
|
# @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
|
176
186
|
# @param extensions [Array<Class, Hash<Class => Object>>] Named extensions to apply to this field (see also {#extension})
|
177
187
|
# @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
|
178
|
-
|
188
|
+
# @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
|
189
|
+
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
190
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: nil, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: [], extensions: EMPTY_ARRAY, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, arguments: EMPTY_HASH, &definition_block)
|
179
191
|
if name.nil?
|
180
192
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
181
193
|
end
|
@@ -191,8 +203,9 @@ module GraphQL
|
|
191
203
|
raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
|
192
204
|
end
|
193
205
|
@original_name = name
|
194
|
-
|
195
|
-
@
|
206
|
+
name_s = -name.to_s
|
207
|
+
@underscored_name = -Member::BuildType.underscore(name_s)
|
208
|
+
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
196
209
|
@description = description
|
197
210
|
if field.is_a?(GraphQL::Schema::Field)
|
198
211
|
raise ArgumentError, "Instead of passing a field as `field:`, use `add_field(field)` to add an already-defined field."
|
@@ -236,14 +249,14 @@ module GraphQL
|
|
236
249
|
@trace = trace
|
237
250
|
@relay_node_field = relay_node_field
|
238
251
|
@relay_nodes_field = relay_nodes_field
|
252
|
+
@ast_node = ast_node
|
253
|
+
@method_conflict_warning = method_conflict_warning
|
239
254
|
|
240
|
-
# Override the default from HasArguments
|
241
|
-
@own_arguments = {}
|
242
255
|
arguments.each do |name, arg|
|
243
256
|
if arg.is_a?(Hash)
|
244
257
|
argument(name: name, **arg)
|
245
258
|
else
|
246
|
-
|
259
|
+
own_arguments[name] = arg
|
247
260
|
end
|
248
261
|
end
|
249
262
|
|
@@ -347,7 +360,7 @@ module GraphQL
|
|
347
360
|
end
|
348
361
|
end
|
349
362
|
|
350
|
-
def complexity(new_complexity)
|
363
|
+
def complexity(new_complexity = nil)
|
351
364
|
case new_complexity
|
352
365
|
when Proc
|
353
366
|
if new_complexity.parameters.size != 3
|
@@ -360,6 +373,8 @@ module GraphQL
|
|
360
373
|
end
|
361
374
|
when Numeric
|
362
375
|
@complexity = new_complexity
|
376
|
+
when nil
|
377
|
+
@complexity
|
363
378
|
else
|
364
379
|
raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
|
365
380
|
end
|
@@ -416,6 +431,7 @@ module GraphQL
|
|
416
431
|
field_defn.introspection = @introspection
|
417
432
|
field_defn.complexity = @complexity
|
418
433
|
field_defn.subscription_scope = @subscription_scope
|
434
|
+
field_defn.ast_node = ast_node
|
419
435
|
|
420
436
|
arguments.each do |name, defn|
|
421
437
|
arg_graphql = defn.to_graphql
|
@@ -433,14 +449,25 @@ module GraphQL
|
|
433
449
|
|
434
450
|
# Ok, `self` isn't a class, but this is for consistency with the classes
|
435
451
|
field_defn.metadata[:type_class] = self
|
436
|
-
|
452
|
+
field_defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(field_defn)
|
437
453
|
field_defn
|
438
454
|
end
|
439
455
|
|
456
|
+
attr_writer :type
|
457
|
+
|
440
458
|
def type
|
441
|
-
@type ||=
|
442
|
-
|
443
|
-
|
459
|
+
@type ||= if @function
|
460
|
+
Member::BuildType.parse_type(@function.type, null: false)
|
461
|
+
elsif @field
|
462
|
+
Member::BuildType.parse_type(@field.type, null: false)
|
463
|
+
else
|
464
|
+
Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
465
|
+
end
|
466
|
+
rescue GraphQL::Schema::InvalidDocumentError => err
|
467
|
+
# Let this propagate up
|
468
|
+
raise err
|
469
|
+
rescue StandardError => err
|
470
|
+
raise ArgumentError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
|
444
471
|
end
|
445
472
|
|
446
473
|
def visible?(context)
|
@@ -459,14 +486,14 @@ module GraphQL
|
|
459
486
|
end
|
460
487
|
end
|
461
488
|
|
462
|
-
def authorized?(object, context)
|
489
|
+
def authorized?(object, args, context)
|
463
490
|
if @resolver_class
|
464
491
|
# The resolver will check itself during `resolve()`
|
465
492
|
@resolver_class.authorized?(object, context)
|
466
493
|
else
|
467
494
|
# Faster than `.any?`
|
468
495
|
arguments.each_value do |arg|
|
469
|
-
if !arg.authorized?(object, context)
|
496
|
+
if args.key?(arg.keyword) && !arg.authorized?(object, args[arg.keyword], context)
|
470
497
|
return false
|
471
498
|
end
|
472
499
|
end
|
@@ -485,21 +512,22 @@ module GraphQL
|
|
485
512
|
# Some legacy fields can have `nil` here, not exactly sure why.
|
486
513
|
# @see https://github.com/rmosolgo/graphql-ruby/issues/1990 before removing
|
487
514
|
inner_obj = after_obj && after_obj.object
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
515
|
+
ctx.schema.after_lazy(to_ruby_args(after_obj, args, ctx)) do |ruby_args|
|
516
|
+
if authorized?(inner_obj, ruby_args, query_ctx)
|
517
|
+
# Then if it passed, resolve the field
|
518
|
+
if @resolve_proc
|
519
|
+
# Might be nil, still want to call the func in that case
|
520
|
+
with_extensions(inner_obj, ruby_args, query_ctx) do |extended_obj, extended_args|
|
521
|
+
# Pass the GraphQL args here for compatibility:
|
522
|
+
@resolve_proc.call(extended_obj, args, ctx)
|
523
|
+
end
|
524
|
+
else
|
525
|
+
public_send_field(after_obj, ruby_args, ctx)
|
496
526
|
end
|
497
527
|
else
|
498
|
-
|
528
|
+
err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
|
529
|
+
query_ctx.schema.unauthorized_field(err)
|
499
530
|
end
|
500
|
-
else
|
501
|
-
err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
|
502
|
-
query_ctx.schema.unauthorized_field(err)
|
503
531
|
end
|
504
532
|
end
|
505
533
|
end
|
@@ -516,7 +544,7 @@ module GraphQL
|
|
516
544
|
begin
|
517
545
|
# Unwrap the GraphQL object to get the application object.
|
518
546
|
application_object = object.object
|
519
|
-
if self.authorized?(application_object, ctx)
|
547
|
+
if self.authorized?(application_object, args, ctx)
|
520
548
|
# Apply field extensions
|
521
549
|
with_extensions(object, args, ctx) do |extended_obj, extended_args|
|
522
550
|
field_receiver = if @resolver_class
|
@@ -594,7 +622,7 @@ module GraphQL
|
|
594
622
|
|
595
623
|
# @param ctx [GraphQL::Query::Context::FieldResolutionContext]
|
596
624
|
def fetch_extra(extra_name, ctx)
|
597
|
-
if extra_name != :path && respond_to?(extra_name)
|
625
|
+
if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
|
598
626
|
self.public_send(extra_name)
|
599
627
|
elsif ctx.respond_to?(extra_name)
|
600
628
|
ctx.public_send(extra_name)
|
@@ -661,7 +689,7 @@ module GraphQL
|
|
661
689
|
# Written iteratively to avoid big stack traces.
|
662
690
|
# @return [Object] Whatever the
|
663
691
|
def with_extensions(obj, args, ctx)
|
664
|
-
if @extensions.
|
692
|
+
if @extensions.nil?
|
665
693
|
yield(obj, args)
|
666
694
|
else
|
667
695
|
# Save these so that the originals can be re-given to `after_resolve` handlers.
|
@@ -1,6 +1,19 @@
|
|
1
1
|
module GraphQL
|
2
2
|
class Schema
|
3
3
|
module FindInheritedValue
|
4
|
+
module EmptyObjects
|
5
|
+
EMPTY_HASH = {}.freeze
|
6
|
+
EMPTY_ARRAY = [].freeze
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.extended(child_cls)
|
10
|
+
child_cls.singleton_class.include(EmptyObjects)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(child_cls)
|
14
|
+
child_cls.include(EmptyObjects)
|
15
|
+
end
|
16
|
+
|
4
17
|
private
|
5
18
|
|
6
19
|
def find_inherited_value(method_name, default_value = nil)
|
@@ -38,7 +38,7 @@ module GraphQL
|
|
38
38
|
|
39
39
|
find_in_directive(directive, path: path)
|
40
40
|
else
|
41
|
-
type = schema.
|
41
|
+
type = schema.get_type(type_or_directive)
|
42
42
|
|
43
43
|
if type.nil?
|
44
44
|
raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema."
|
@@ -66,22 +66,24 @@ module GraphQL
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def find_in_type(type, path:)
|
69
|
-
case type
|
70
|
-
when
|
69
|
+
case type.kind.name
|
70
|
+
when "OBJECT"
|
71
71
|
find_in_fields_type(type, kind: "object", path: path)
|
72
|
-
when
|
72
|
+
when "INTERFACE"
|
73
73
|
find_in_fields_type(type, kind: "interface", path: path)
|
74
|
-
when
|
74
|
+
when "INPUT_OBJECT"
|
75
75
|
find_in_input_object(type, path: path)
|
76
|
-
when
|
76
|
+
when "UNION"
|
77
77
|
# Error out if path that was provided is too long
|
78
78
|
# i.e UnionType.PossibleType.aField
|
79
79
|
# Use PossibleType.aField instead.
|
80
80
|
if invalid = path.first
|
81
81
|
raise MemberNotFoundError, "Cannot select union possible type `#{invalid}`. Select the type directly instead."
|
82
82
|
end
|
83
|
-
when
|
83
|
+
when "ENUM"
|
84
84
|
find_in_enum_type(type, path: path)
|
85
|
+
else
|
86
|
+
raise "Unexpected find_in_type: #{type.inspect} (#{path})"
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
@@ -90,7 +92,7 @@ module GraphQL
|
|
90
92
|
field = schema.get_field(type, field_name)
|
91
93
|
|
92
94
|
if field.nil?
|
93
|
-
raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type}`."
|
95
|
+
raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type.graphql_name}`."
|
94
96
|
end
|
95
97
|
|
96
98
|
return field if path.empty?
|
@@ -117,10 +119,10 @@ module GraphQL
|
|
117
119
|
|
118
120
|
def find_in_input_object(input_object, path:)
|
119
121
|
field_name = path.shift
|
120
|
-
input_field = input_object.
|
122
|
+
input_field = input_object.arguments[field_name]
|
121
123
|
|
122
124
|
if input_field.nil?
|
123
|
-
raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object}`."
|
125
|
+
raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`."
|
124
126
|
end
|
125
127
|
|
126
128
|
# Error out if path that was provided is too long
|
@@ -137,7 +139,7 @@ module GraphQL
|
|
137
139
|
enum_value = enum_type.values[value_name]
|
138
140
|
|
139
141
|
if enum_value.nil?
|
140
|
-
raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type}`."
|
142
|
+
raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`."
|
141
143
|
end
|
142
144
|
|
143
145
|
# Error out if path that was provided is too long
|
@@ -5,6 +5,8 @@ module GraphQL
|
|
5
5
|
extend GraphQL::Schema::Member::AcceptsDefinition
|
6
6
|
extend Forwardable
|
7
7
|
extend GraphQL::Schema::Member::HasArguments
|
8
|
+
extend GraphQL::Schema::Member::ValidatesInput
|
9
|
+
|
8
10
|
include GraphQL::Dig
|
9
11
|
|
10
12
|
def initialize(values = nil, ruby_kwargs: nil, context:, defaults_used:)
|
@@ -26,7 +28,7 @@ module GraphQL
|
|
26
28
|
if @ruby_style_hash.key?(ruby_kwargs_key) && loads && !arg_defn.from_resolver?
|
27
29
|
value = @ruby_style_hash[ruby_kwargs_key]
|
28
30
|
@ruby_style_hash[ruby_kwargs_key] = if arg_defn.type.list?
|
29
|
-
|
31
|
+
value.map { |val| load_application_object(arg_defn, loads, val) }
|
30
32
|
else
|
31
33
|
load_application_object(arg_defn, loads, value)
|
32
34
|
end
|
@@ -90,7 +92,7 @@ module GraphQL
|
|
90
92
|
end
|
91
93
|
|
92
94
|
def key?(key)
|
93
|
-
@ruby_style_hash.key?(key) || (@arguments && @arguments.key?(key))
|
95
|
+
@ruby_style_hash.key?(key) || (@arguments && @arguments.key?(key)) || false
|
94
96
|
end
|
95
97
|
|
96
98
|
# A copy of the Ruby-style hash
|
@@ -117,6 +119,7 @@ module GraphQL
|
|
117
119
|
type_defn.description = description
|
118
120
|
type_defn.metadata[:type_class] = self
|
119
121
|
type_defn.mutation = mutation
|
122
|
+
type_defn.ast_node = ast_node
|
120
123
|
arguments.each do |name, arg|
|
121
124
|
type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition
|
122
125
|
end
|
@@ -130,6 +133,108 @@ module GraphQL
|
|
130
133
|
def kind
|
131
134
|
GraphQL::TypeKinds::INPUT_OBJECT
|
132
135
|
end
|
136
|
+
|
137
|
+
# @api private
|
138
|
+
INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object responding to `to_h` or `to_unsafe_h`."
|
139
|
+
|
140
|
+
|
141
|
+
def validate_non_null_input(input, ctx)
|
142
|
+
result = GraphQL::Query::InputValidationResult.new
|
143
|
+
|
144
|
+
warden = ctx.warden
|
145
|
+
|
146
|
+
if input.is_a?(Array)
|
147
|
+
result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
|
148
|
+
return result
|
149
|
+
end
|
150
|
+
|
151
|
+
# We're not actually _using_ the coerced result, we're just
|
152
|
+
# using these methods to make sure that the object will
|
153
|
+
# behave like a hash below, when we call `each` on it.
|
154
|
+
begin
|
155
|
+
input.to_h
|
156
|
+
rescue
|
157
|
+
begin
|
158
|
+
# Handle ActionController::Parameters:
|
159
|
+
input.to_unsafe_h
|
160
|
+
rescue
|
161
|
+
# We're not sure it'll act like a hash, so reject it:
|
162
|
+
result.add_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
|
163
|
+
return result
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
visible_arguments_map = warden.arguments(self).reduce({}) { |m, f| m[f.name] = f; m}
|
168
|
+
|
169
|
+
# Items in the input that are unexpected
|
170
|
+
input.each do |name, value|
|
171
|
+
if visible_arguments_map[name].nil?
|
172
|
+
result.add_problem("Argument is not defined on #{self.graphql_name}", [name])
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Items in the input that are expected, but have invalid values
|
177
|
+
visible_arguments_map.map do |name, argument|
|
178
|
+
argument_result = argument.type.validate_input(input[name], ctx)
|
179
|
+
if !argument_result.valid?
|
180
|
+
result.merge_result!(name, argument_result)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
result
|
185
|
+
end
|
186
|
+
|
187
|
+
def coerce_input(value, ctx)
|
188
|
+
input_values = {}
|
189
|
+
|
190
|
+
arguments.each do |name, argument_defn|
|
191
|
+
arg_key = argument_defn.keyword
|
192
|
+
has_value = false
|
193
|
+
# Accept either string or symbol
|
194
|
+
field_value = if value.key?(name)
|
195
|
+
has_value = true
|
196
|
+
value[name]
|
197
|
+
elsif value.key?(arg_key)
|
198
|
+
has_value = true
|
199
|
+
value[arg_key]
|
200
|
+
elsif argument_defn.default_value?
|
201
|
+
has_value = true
|
202
|
+
argument_defn.default_value
|
203
|
+
else
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
# Only continue if some value was found for this argument
|
207
|
+
if has_value
|
208
|
+
coerced_value = argument_defn.type.coerce_input(field_value, ctx)
|
209
|
+
prepared_value = argument_defn.prepare_value(nil, coerced_value, context: ctx)
|
210
|
+
input_values[arg_key] = prepared_value
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
input_values
|
215
|
+
end
|
216
|
+
|
217
|
+
# It's funny to think of a _result_ of an input object.
|
218
|
+
# This is used for rendering the default value in introspection responses.
|
219
|
+
def coerce_result(value, ctx)
|
220
|
+
# Allow the application to provide values as :symbols, and convert them to the strings
|
221
|
+
value = value.reduce({}) { |memo, (k, v)| memo[k.to_s] = v; memo }
|
222
|
+
|
223
|
+
result = {}
|
224
|
+
|
225
|
+
arguments.each do |input_key, input_field_defn|
|
226
|
+
input_value = value[input_key]
|
227
|
+
if value.key?(input_key)
|
228
|
+
result[input_key] = if input_value.nil?
|
229
|
+
nil
|
230
|
+
else
|
231
|
+
input_field_defn.type.coerce_result(input_value, ctx)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
result
|
237
|
+
end
|
133
238
|
end
|
134
239
|
end
|
135
240
|
end
|
@@ -7,11 +7,13 @@ module GraphQL
|
|
7
7
|
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
8
8
|
include GraphQL::Relay::TypeExtensions
|
9
9
|
include GraphQL::Schema::Member::BaseDSLMethods
|
10
|
+
# ConfigurationExtension's responsibilities are in `def included` below
|
10
11
|
include GraphQL::Schema::Member::TypeSystemHelpers
|
11
12
|
include GraphQL::Schema::Member::HasFields
|
12
13
|
include GraphQL::Schema::Member::HasPath
|
13
14
|
include GraphQL::Schema::Member::RelayShortcuts
|
14
15
|
include GraphQL::Schema::Member::Scoped
|
16
|
+
include GraphQL::Schema::Member::HasAstNode
|
15
17
|
|
16
18
|
# Methods defined in this block will be:
|
17
19
|
# - Added as class methods to this interface
|
@@ -20,14 +22,9 @@ module GraphQL
|
|
20
22
|
self::DefinitionMethods.module_eval(&block)
|
21
23
|
end
|
22
24
|
|
23
|
-
#
|
25
|
+
# @see {Schema::Warden} hides interfaces without visible implementations
|
24
26
|
def visible?(context)
|
25
|
-
|
26
|
-
if context.schema.visible?(type, context)
|
27
|
-
return true
|
28
|
-
end
|
29
|
-
end
|
30
|
-
false
|
27
|
+
true
|
31
28
|
end
|
32
29
|
|
33
30
|
# The interface is accessible if any of its possible types are accessible
|
@@ -63,6 +60,11 @@ module GraphQL
|
|
63
60
|
child_class.const_set(:DefinitionMethods, defn_methods_module)
|
64
61
|
child_class.extend(child_class::DefinitionMethods)
|
65
62
|
end
|
63
|
+
child_class.introspection(introspection)
|
64
|
+
child_class.description(description)
|
65
|
+
if overridden_graphql_name
|
66
|
+
child_class.graphql_name(overridden_graphql_name)
|
67
|
+
end
|
66
68
|
elsif child_class < GraphQL::Schema::Object
|
67
69
|
# This is being included into an object type, make sure it's using `implements(...)`
|
68
70
|
backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
|
@@ -89,6 +91,7 @@ module GraphQL
|
|
89
91
|
type_defn.name = graphql_name
|
90
92
|
type_defn.description = description
|
91
93
|
type_defn.orphan_types = orphan_types
|
94
|
+
type_defn.ast_node = ast_node
|
92
95
|
fields.each do |field_name, field_inst|
|
93
96
|
field_defn = field_inst.graphql_definition
|
94
97
|
type_defn.fields[field_defn.name] = field_defn
|