graphql 2.0.28 → 2.2.11
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/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +3 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +6 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +2 -0
- data/lib/graphql/analysis/ast/analyzer.rb +7 -0
- data/lib/graphql/analysis/ast/field_usage.rb +32 -7
- data/lib/graphql/analysis/ast/query_complexity.rb +80 -128
- data/lib/graphql/analysis/ast/query_depth.rb +7 -2
- data/lib/graphql/analysis/ast/visitor.rb +2 -2
- data/lib/graphql/analysis/ast.rb +21 -11
- data/lib/graphql/backtrace/trace.rb +12 -15
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +85 -0
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +11 -3
- data/lib/graphql/dataloader.rb +109 -142
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +79 -248
- data/lib/graphql/execution/interpreter.rb +91 -157
- data/lib/graphql/execution/lookahead.rb +88 -21
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/schema_type.rb +3 -1
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +37 -37
- data/lib/graphql/language/lexer.rb +271 -177
- data/lib/graphql/language/nodes.rb +75 -57
- data/lib/graphql/language/parser.rb +707 -1986
- data/lib/graphql/language/printer.rb +303 -146
- data/lib/graphql/language/sanitized_printer.rb +20 -22
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +20 -81
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +3 -3
- data/lib/graphql/pagination/connection.rb +28 -1
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/pagination/relation_connection.rb +3 -3
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +36 -98
- data/lib/graphql/query/null_context.rb +4 -11
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +13 -22
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/schema/argument.rb +6 -1
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +0 -11
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive.rb +1 -1
- data/lib/graphql/schema/enum.rb +3 -3
- data/lib/graphql/schema/field/connection_extension.rb +1 -15
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +39 -35
- data/lib/graphql/schema/has_single_input_argument.rb +156 -0
- data/lib/graphql/schema/input_object.rb +2 -2
- data/lib/graphql/schema/interface.rb +15 -11
- data/lib/graphql/schema/introspection_system.rb +2 -0
- data/lib/graphql/schema/loader.rb +0 -2
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +61 -38
- data/lib/graphql/schema/member/has_fields.rb +8 -5
- data/lib/graphql/schema/member/has_interfaces.rb +23 -9
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +8 -7
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
- data/lib/graphql/schema/resolver.rb +16 -8
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema/warden.rb +96 -94
- data/lib/graphql/schema.rb +252 -78
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +1 -1
- data/lib/graphql/static_validation/literal_validator.rb +2 -3
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +2 -2
- data/lib/graphql/static_validation/validation_context.rb +5 -5
- data/lib/graphql/static_validation/validator.rb +3 -0
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
- data/lib/graphql/subscriptions/event.rb +8 -2
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +14 -12
- data/lib/graphql/testing/helpers.rb +129 -0
- data/lib/graphql/testing.rb +2 -0
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/tracing/legacy_hooks_trace.rb +74 -0
- data/lib/graphql/tracing/platform_tracing.rb +2 -0
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +3 -1
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing/trace.rb +1 -0
- data/lib/graphql/tracing.rb +3 -1
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +32 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +6 -5
- data/readme.md +12 -2
- metadata +46 -38
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/language/parser.y +0 -560
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
data/lib/graphql/schema/field.rb
CHANGED
@@ -138,7 +138,7 @@ module GraphQL
|
|
138
138
|
# As a last ditch, try to force loading the return type:
|
139
139
|
type.unwrap.name
|
140
140
|
end
|
141
|
-
@connection = return_type_name.end_with?("Connection")
|
141
|
+
@connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
|
142
142
|
else
|
143
143
|
@connection
|
144
144
|
end
|
@@ -218,8 +218,8 @@ module GraphQL
|
|
218
218
|
# @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
|
219
219
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
220
220
|
# @param validates [Array<Hash>] Configurations for validating this field
|
221
|
-
# @fallback_value [Object] A fallback value if the method is not defined
|
222
|
-
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, &definition_block)
|
221
|
+
# @param fallback_value [Object] A fallback value if the method is not defined
|
222
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
|
223
223
|
if name.nil?
|
224
224
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
225
225
|
end
|
@@ -267,6 +267,7 @@ module GraphQL
|
|
267
267
|
@method_sym = method_name.to_sym
|
268
268
|
@resolver_method = (resolver_method || name_s).to_sym
|
269
269
|
@complexity = complexity
|
270
|
+
@dynamic_introspection = dynamic_introspection
|
270
271
|
@return_type_expr = type
|
271
272
|
@return_type_null = if !null.nil?
|
272
273
|
null
|
@@ -351,6 +352,8 @@ module GraphQL
|
|
351
352
|
@call_after_define = true
|
352
353
|
end
|
353
354
|
|
355
|
+
attr_accessor :dynamic_introspection
|
356
|
+
|
354
357
|
# If true, subscription updates with this field can be shared between viewers
|
355
358
|
# @return [Boolean, nil]
|
356
359
|
# @see GraphQL::Subscriptions::BroadcastAnalyzer
|
@@ -480,41 +483,24 @@ module GraphQL
|
|
480
483
|
metadata_complexity = 0
|
481
484
|
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
482
485
|
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
486
|
+
lookahead.selections.each do |next_lookahead|
|
487
|
+
# this includes `pageInfo`, `nodes` and `edges` and any custom fields
|
488
|
+
# TODO this doesn't support procs yet -- unlikely to need it.
|
489
|
+
metadata_complexity += next_lookahead.field.complexity
|
490
|
+
if next_lookahead.name != :nodes && next_lookahead.name != :edges
|
491
|
+
# subfields, eg, for pageInfo -- assumes no subselections
|
492
|
+
metadata_complexity += next_lookahead.selections.size
|
493
|
+
end
|
490
494
|
end
|
491
495
|
|
492
|
-
nodes_edges_complexity = 0
|
493
|
-
nodes_edges_complexity += 1 if lookahead.selects?(:edges)
|
494
|
-
nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
|
495
|
-
|
496
496
|
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
497
|
-
items_complexity = child_complexity - metadata_complexity
|
498
|
-
|
499
|
-
|
497
|
+
items_complexity = child_complexity - metadata_complexity
|
498
|
+
subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity
|
499
|
+
# Apply this field's own complexity
|
500
|
+
apply_own_complexity_to(subfields_complexity, query, nodes)
|
500
501
|
end
|
501
502
|
else
|
502
|
-
|
503
|
-
case defined_complexity
|
504
|
-
when Proc
|
505
|
-
arguments = query.arguments_for(nodes.first, self)
|
506
|
-
if arguments.is_a?(GraphQL::ExecutionError)
|
507
|
-
return child_complexity
|
508
|
-
elsif arguments.respond_to?(:keyword_arguments)
|
509
|
-
arguments = arguments.keyword_arguments
|
510
|
-
end
|
511
|
-
|
512
|
-
defined_complexity.call(query.context, arguments, child_complexity)
|
513
|
-
when Numeric
|
514
|
-
defined_complexity + child_complexity
|
515
|
-
else
|
516
|
-
raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
|
517
|
-
end
|
503
|
+
apply_own_complexity_to(child_complexity, query, nodes)
|
518
504
|
end
|
519
505
|
end
|
520
506
|
|
@@ -659,7 +645,7 @@ module GraphQL
|
|
659
645
|
method_to_call = nil
|
660
646
|
method_args = nil
|
661
647
|
|
662
|
-
Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
648
|
+
@own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
663
649
|
|
664
650
|
query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
|
665
651
|
if is_authorized
|
@@ -701,7 +687,7 @@ module GraphQL
|
|
701
687
|
inner_object.dig(*@dig_keys)
|
702
688
|
elsif inner_object.key?(@method_sym)
|
703
689
|
inner_object[@method_sym]
|
704
|
-
elsif inner_object.key?(@method_str)
|
690
|
+
elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
|
705
691
|
inner_object[@method_str]
|
706
692
|
elsif @fallback_value != NOT_CONFIGURED
|
707
693
|
@fallback_value
|
@@ -879,6 +865,24 @@ ERR
|
|
879
865
|
yield(obj, args)
|
880
866
|
end
|
881
867
|
end
|
868
|
+
|
869
|
+
def apply_own_complexity_to(child_complexity, query, nodes)
|
870
|
+
case (own_complexity = complexity)
|
871
|
+
when Numeric
|
872
|
+
own_complexity + child_complexity
|
873
|
+
when Proc
|
874
|
+
arguments = query.arguments_for(nodes.first, self)
|
875
|
+
if arguments.is_a?(GraphQL::ExecutionError)
|
876
|
+
return child_complexity
|
877
|
+
elsif arguments.respond_to?(:keyword_arguments)
|
878
|
+
arguments = arguments.keyword_arguments
|
879
|
+
end
|
880
|
+
|
881
|
+
own_complexity.call(query.context, arguments, child_complexity)
|
882
|
+
else
|
883
|
+
raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
|
884
|
+
end
|
885
|
+
end
|
882
886
|
end
|
883
887
|
end
|
884
888
|
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
module HasSingleInputArgument
|
6
|
+
def resolve_with_support(**inputs)
|
7
|
+
if inputs[:input].is_a?(InputObject)
|
8
|
+
input = inputs[:input].to_kwargs
|
9
|
+
else
|
10
|
+
input = inputs[:input]
|
11
|
+
end
|
12
|
+
|
13
|
+
new_extras = field ? field.extras : []
|
14
|
+
all_extras = self.class.extras + new_extras
|
15
|
+
|
16
|
+
# Transfer these from the top-level hash to the
|
17
|
+
# shortcutted `input:` object
|
18
|
+
all_extras.each do |ext|
|
19
|
+
# It's possible that the `extra` was not passed along by this point,
|
20
|
+
# don't re-add it if it wasn't given here.
|
21
|
+
if inputs.key?(ext)
|
22
|
+
input[ext] = inputs[ext]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
if input
|
27
|
+
# This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
|
28
|
+
input_kwargs = input.to_h
|
29
|
+
else
|
30
|
+
# Relay Classic Mutations with no `argument`s
|
31
|
+
# don't require `input:`
|
32
|
+
input_kwargs = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
if input_kwargs.any?
|
36
|
+
super(**input_kwargs)
|
37
|
+
else
|
38
|
+
super()
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.included(base)
|
43
|
+
base.extend(ClassMethods)
|
44
|
+
end
|
45
|
+
|
46
|
+
module ClassMethods
|
47
|
+
def dummy
|
48
|
+
@dummy ||= begin
|
49
|
+
d = Class.new(GraphQL::Schema::Resolver)
|
50
|
+
d.argument_class(self.argument_class)
|
51
|
+
# TODO make this lazier?
|
52
|
+
d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
|
53
|
+
d
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def field_arguments(context = GraphQL::Query::NullContext.instance)
|
58
|
+
dummy.arguments(context)
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_field_argument(name, context = GraphQL::Query::NullContext.instance)
|
62
|
+
dummy.get_argument(name, context)
|
63
|
+
end
|
64
|
+
|
65
|
+
def own_field_arguments
|
66
|
+
dummy.own_arguments
|
67
|
+
end
|
68
|
+
|
69
|
+
def any_field_arguments?
|
70
|
+
dummy.any_arguments?
|
71
|
+
end
|
72
|
+
|
73
|
+
def all_field_argument_definitions
|
74
|
+
dummy.all_argument_definitions
|
75
|
+
end
|
76
|
+
|
77
|
+
# Also apply this argument to the input type:
|
78
|
+
def argument(*args, own_argument: false, **kwargs, &block)
|
79
|
+
it = input_type # make sure any inherited arguments are already added to it
|
80
|
+
arg = super(*args, **kwargs, &block)
|
81
|
+
|
82
|
+
# This definition might be overriding something inherited;
|
83
|
+
# if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
|
84
|
+
prev_args = it.own_arguments[arg.graphql_name]
|
85
|
+
case prev_args
|
86
|
+
when GraphQL::Schema::Argument
|
87
|
+
if prev_args.owner != self
|
88
|
+
it.own_arguments.delete(arg.graphql_name)
|
89
|
+
end
|
90
|
+
when Array
|
91
|
+
prev_args.reject! { |a| a.owner != self }
|
92
|
+
if prev_args.empty?
|
93
|
+
it.own_arguments.delete(arg.graphql_name)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it.add_argument(arg)
|
98
|
+
arg
|
99
|
+
end
|
100
|
+
|
101
|
+
# The base class for generated input object types
|
102
|
+
# @param new_class [Class] The base class to use for generating input object definitions
|
103
|
+
# @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
|
104
|
+
def input_object_class(new_class = nil)
|
105
|
+
if new_class
|
106
|
+
@input_object_class = new_class
|
107
|
+
end
|
108
|
+
@input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject)
|
109
|
+
end
|
110
|
+
|
111
|
+
# @param new_input_type [Class, nil] If provided, it configures this mutation to accept `new_input_type` instead of generating an input type
|
112
|
+
# @return [Class] The generated {Schema::InputObject} class for this mutation's `input`
|
113
|
+
def input_type(new_input_type = nil)
|
114
|
+
if new_input_type
|
115
|
+
@input_type = new_input_type
|
116
|
+
end
|
117
|
+
@input_type ||= generate_input_type
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
# Generate the input type for the `input:` argument
|
123
|
+
# To customize how input objects are generated, override this method
|
124
|
+
# @return [Class] a subclass of {.input_object_class}
|
125
|
+
def generate_input_type
|
126
|
+
mutation_args = all_argument_definitions
|
127
|
+
mutation_class = self
|
128
|
+
Class.new(input_object_class) do
|
129
|
+
class << self
|
130
|
+
def default_graphql_name
|
131
|
+
"#{self.mutation.graphql_name}Input"
|
132
|
+
end
|
133
|
+
|
134
|
+
def description(new_desc = nil)
|
135
|
+
super || "Autogenerated input type of #{self.mutation.graphql_name}"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
mutation(mutation_class)
|
139
|
+
# these might be inherited:
|
140
|
+
mutation_args.each do |arg|
|
141
|
+
add_argument(arg)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
|
149
|
+
def authorize_arguments(args, values)
|
150
|
+
# remove the `input` wrapper to match values
|
151
|
+
input_args = args["input"].type.unwrap.arguments(context)
|
152
|
+
super(input_args, values)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -79,7 +79,7 @@ module GraphQL
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def self.one_of?
|
82
|
-
|
82
|
+
false # Re-defined when `OneOf` is added
|
83
83
|
end
|
84
84
|
|
85
85
|
def unwrap_value(value)
|
@@ -145,7 +145,7 @@ module GraphQL
|
|
145
145
|
end
|
146
146
|
|
147
147
|
# @api private
|
148
|
-
INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object
|
148
|
+
INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object."
|
149
149
|
|
150
150
|
def validate_non_null_input(input, ctx, max_errors: nil)
|
151
151
|
warden = ctx.warden
|
@@ -20,6 +20,15 @@ module GraphQL
|
|
20
20
|
# - Added as class methods to this interface
|
21
21
|
# - Added as class methods to all child interfaces
|
22
22
|
def definition_methods(&block)
|
23
|
+
# Use an instance variable to tell whether it's been included previously or not;
|
24
|
+
# You can't use constant detection because constants are brought into scope
|
25
|
+
# by `include`, which has already happened at this point.
|
26
|
+
if !defined?(@_definition_methods)
|
27
|
+
defn_methods_module = Module.new
|
28
|
+
@_definition_methods = defn_methods_module
|
29
|
+
const_set(:DefinitionMethods, defn_methods_module)
|
30
|
+
extend(self::DefinitionMethods)
|
31
|
+
end
|
23
32
|
self::DefinitionMethods.module_eval(&block)
|
24
33
|
end
|
25
34
|
|
@@ -47,20 +56,11 @@ module GraphQL
|
|
47
56
|
|
48
57
|
child_class.type_membership_class(self.type_membership_class)
|
49
58
|
child_class.ancestors.reverse_each do |ancestor|
|
50
|
-
if ancestor.const_defined?(:DefinitionMethods)
|
59
|
+
if ancestor.const_defined?(:DefinitionMethods) && ancestor != child_class
|
51
60
|
child_class.extend(ancestor::DefinitionMethods)
|
52
61
|
end
|
53
62
|
end
|
54
63
|
|
55
|
-
# Use an instance variable to tell whether it's been included previously or not;
|
56
|
-
# You can't use constant detection because constants are brought into scope
|
57
|
-
# by `include`, which has already happened at this point.
|
58
|
-
if !child_class.instance_variable_defined?(:@_definition_methods)
|
59
|
-
defn_methods_module = Module.new
|
60
|
-
child_class.instance_variable_set(:@_definition_methods, defn_methods_module)
|
61
|
-
child_class.const_set(:DefinitionMethods, defn_methods_module)
|
62
|
-
child_class.extend(child_class::DefinitionMethods)
|
63
|
-
end
|
64
64
|
child_class.introspection(introspection)
|
65
65
|
child_class.description(description)
|
66
66
|
# If interfaces are mixed into each other, only define this class once
|
@@ -69,7 +69,11 @@ module GraphQL
|
|
69
69
|
end
|
70
70
|
elsif child_class < GraphQL::Schema::Object
|
71
71
|
# This is being included into an object type, make sure it's using `implements(...)`
|
72
|
-
backtrace_line =
|
72
|
+
backtrace_line = caller_locations(0, 10).find do |location|
|
73
|
+
location.base_label == "implements" &&
|
74
|
+
location.path.end_with?("schema/member/has_interfaces.rb")
|
75
|
+
end
|
76
|
+
|
73
77
|
if !backtrace_line
|
74
78
|
raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
|
75
79
|
end
|
@@ -39,7 +39,9 @@ module GraphQL
|
|
39
39
|
entry_point_fields.delete('__type') if schema.disable_type_introspection_entry_point?
|
40
40
|
entry_point_fields
|
41
41
|
end
|
42
|
+
@entry_point_fields.each { |k, v| v.dynamic_introspection = true }
|
42
43
|
@dynamic_fields = get_fields_from_class(class_sym: :DynamicFields)
|
44
|
+
@dynamic_fields.each { |k, v| v.dynamic_introspection = true }
|
43
45
|
end
|
44
46
|
|
45
47
|
def entry_points
|
@@ -176,7 +176,6 @@ module GraphQL
|
|
176
176
|
while (of_type = unwrapped_field_hash["ofType"])
|
177
177
|
unwrapped_field_hash = of_type
|
178
178
|
end
|
179
|
-
type_name = unwrapped_field_hash["name"]
|
180
179
|
|
181
180
|
type_defn.field(
|
182
181
|
field_hash["name"],
|
@@ -186,7 +185,6 @@ module GraphQL
|
|
186
185
|
null: true,
|
187
186
|
camelize: false,
|
188
187
|
connection_extension: nil,
|
189
|
-
connection: type_name.end_with?("Connection"),
|
190
188
|
) do
|
191
189
|
if field_hash["args"].any?
|
192
190
|
loader.build_arguments(self, field_hash["args"], type_resolver)
|
@@ -102,7 +102,8 @@ module GraphQL
|
|
102
102
|
def default_graphql_name
|
103
103
|
@default_graphql_name ||= begin
|
104
104
|
raise GraphQL::RequiredImplementationMissingError, 'Anonymous class should declare a `graphql_name`' if name.nil?
|
105
|
-
-name.split("::").last
|
105
|
+
g_name = -name.split("::").last
|
106
|
+
g_name.end_with?("Type") ? g_name.sub(/Type\Z/, "") : g_name
|
106
107
|
end
|
107
108
|
end
|
108
109
|
|
@@ -109,7 +109,7 @@ module GraphQL
|
|
109
109
|
end
|
110
110
|
|
111
111
|
# @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
|
112
|
-
def arguments(context = GraphQL::Query::NullContext)
|
112
|
+
def arguments(context = GraphQL::Query::NullContext.instance)
|
113
113
|
if own_arguments.any?
|
114
114
|
own_arguments_that_apply = {}
|
115
115
|
own_arguments.each do |name, args_entry|
|
@@ -122,6 +122,10 @@ module GraphQL
|
|
122
122
|
own_arguments_that_apply || own_arguments
|
123
123
|
end
|
124
124
|
|
125
|
+
def any_arguments?
|
126
|
+
own_arguments.any?
|
127
|
+
end
|
128
|
+
|
125
129
|
module ClassConfigured
|
126
130
|
def inherited(child_class)
|
127
131
|
super
|
@@ -129,7 +133,7 @@ module GraphQL
|
|
129
133
|
end
|
130
134
|
|
131
135
|
module InheritedArguments
|
132
|
-
def arguments(context = GraphQL::Query::NullContext)
|
136
|
+
def arguments(context = GraphQL::Query::NullContext.instance)
|
133
137
|
own_arguments = super
|
134
138
|
inherited_arguments = superclass.arguments(context)
|
135
139
|
|
@@ -145,6 +149,10 @@ module GraphQL
|
|
145
149
|
end
|
146
150
|
end
|
147
151
|
|
152
|
+
def any_arguments?
|
153
|
+
super || superclass.any_arguments?
|
154
|
+
end
|
155
|
+
|
148
156
|
def all_argument_definitions
|
149
157
|
all_defns = {}
|
150
158
|
ancestors.reverse_each do |ancestor|
|
@@ -158,7 +166,7 @@ module GraphQL
|
|
158
166
|
end
|
159
167
|
|
160
168
|
|
161
|
-
def get_argument(argument_name, context = GraphQL::Query::NullContext)
|
169
|
+
def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
|
162
170
|
warden = Warden.from_context(context)
|
163
171
|
for ancestor in ancestors
|
164
172
|
if ancestor.respond_to?(:own_arguments) &&
|
@@ -173,9 +181,9 @@ module GraphQL
|
|
173
181
|
end
|
174
182
|
|
175
183
|
module FieldConfigured
|
176
|
-
def arguments(context = GraphQL::Query::NullContext)
|
184
|
+
def arguments(context = GraphQL::Query::NullContext.instance)
|
177
185
|
own_arguments = super
|
178
|
-
if
|
186
|
+
if @resolver_class
|
179
187
|
inherited_arguments = @resolver_class.field_arguments(context)
|
180
188
|
if own_arguments.any?
|
181
189
|
if inherited_arguments.any?
|
@@ -191,8 +199,12 @@ module GraphQL
|
|
191
199
|
end
|
192
200
|
end
|
193
201
|
|
202
|
+
def any_arguments?
|
203
|
+
super || (@resolver_class && @resolver_class.any_field_arguments?)
|
204
|
+
end
|
205
|
+
|
194
206
|
def all_argument_definitions
|
195
|
-
if
|
207
|
+
if @resolver_class
|
196
208
|
all_defns = {}
|
197
209
|
@resolver_class.all_field_argument_definitions.each do |arg_defn|
|
198
210
|
key = arg_defn.graphql_name
|
@@ -224,7 +236,7 @@ module GraphQL
|
|
224
236
|
end
|
225
237
|
|
226
238
|
# @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
|
227
|
-
def get_argument(argument_name, context = GraphQL::Query::NullContext)
|
239
|
+
def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
|
228
240
|
warden = Warden.from_context(context)
|
229
241
|
if (arg_config = own_arguments[argument_name]) && (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden))
|
230
242
|
visible_arg
|
@@ -367,41 +379,52 @@ module GraphQL
|
|
367
379
|
def authorize_application_object(argument, id, context, loaded_application_object)
|
368
380
|
context.query.after_lazy(loaded_application_object) do |application_object|
|
369
381
|
if application_object.nil?
|
370
|
-
err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
|
371
|
-
load_application_object_failed(err)
|
382
|
+
err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
|
383
|
+
application_object = load_application_object_failed(err)
|
372
384
|
end
|
373
385
|
# Double-check that the located object is actually of this type
|
374
386
|
# (Don't want to allow arbitrary access to objects this way)
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
387
|
+
if application_object.nil?
|
388
|
+
nil
|
389
|
+
else
|
390
|
+
maybe_lazy_resolve_type = context.schema.resolve_type(argument.loads, application_object, context)
|
391
|
+
context.query.after_lazy(maybe_lazy_resolve_type) do |resolve_type_result|
|
392
|
+
if resolve_type_result.is_a?(Array) && resolve_type_result.size == 2
|
393
|
+
application_object_type, application_object = resolve_type_result
|
394
|
+
else
|
395
|
+
application_object_type = resolve_type_result
|
396
|
+
# application_object is already assigned
|
397
|
+
end
|
398
|
+
|
399
|
+
if !(
|
400
|
+
context.warden.possible_types(argument.loads).include?(application_object_type) ||
|
401
|
+
context.warden.loadable?(argument.loads, context)
|
402
|
+
)
|
403
|
+
err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
|
404
|
+
application_object = load_application_object_failed(err)
|
405
|
+
end
|
406
|
+
|
407
|
+
if application_object.nil?
|
408
|
+
nil
|
409
|
+
else
|
410
|
+
# This object was loaded successfully
|
411
|
+
# and resolved to the right type,
|
412
|
+
# now apply the `.authorized?` class method if there is one
|
413
|
+
context.query.after_lazy(application_object_type.authorized?(application_object, context)) do |authed|
|
414
|
+
if authed
|
415
|
+
application_object
|
403
416
|
else
|
404
|
-
|
417
|
+
err = GraphQL::UnauthorizedError.new(
|
418
|
+
object: application_object,
|
419
|
+
type: application_object_type,
|
420
|
+
context: context,
|
421
|
+
)
|
422
|
+
if self.respond_to?(:unauthorized_object)
|
423
|
+
err.set_backtrace(caller)
|
424
|
+
unauthorized_object(err)
|
425
|
+
else
|
426
|
+
raise err
|
427
|
+
end
|
405
428
|
end
|
406
429
|
end
|
407
430
|
end
|
@@ -97,7 +97,7 @@ module GraphQL
|
|
97
97
|
end
|
98
98
|
|
99
99
|
module InterfaceMethods
|
100
|
-
def get_field(field_name, context = GraphQL::Query::NullContext)
|
100
|
+
def get_field(field_name, context = GraphQL::Query::NullContext.instance)
|
101
101
|
warden = Warden.from_context(context)
|
102
102
|
for ancestor in ancestors
|
103
103
|
if ancestor.respond_to?(:own_fields) &&
|
@@ -110,7 +110,7 @@ module GraphQL
|
|
110
110
|
end
|
111
111
|
|
112
112
|
# @return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields
|
113
|
-
def fields(context = GraphQL::Query::NullContext)
|
113
|
+
def fields(context = GraphQL::Query::NullContext.instance)
|
114
114
|
warden = Warden.from_context(context)
|
115
115
|
# Local overrides take precedence over inherited fields
|
116
116
|
visible_fields = {}
|
@@ -130,22 +130,25 @@ module GraphQL
|
|
130
130
|
end
|
131
131
|
|
132
132
|
module ObjectMethods
|
133
|
-
def get_field(field_name, context = GraphQL::Query::NullContext)
|
133
|
+
def get_field(field_name, context = GraphQL::Query::NullContext.instance)
|
134
134
|
# Objects need to check that the interface implementation is visible, too
|
135
135
|
warden = Warden.from_context(context)
|
136
|
-
|
136
|
+
ancs = ancestors
|
137
|
+
i = 0
|
138
|
+
while (ancestor = ancs[i])
|
137
139
|
if ancestor.respond_to?(:own_fields) &&
|
138
140
|
visible_interface_implementation?(ancestor, context, warden) &&
|
139
141
|
(f_entry = ancestor.own_fields[field_name]) &&
|
140
142
|
(f = Warden.visible_entry?(:visible_field?, f_entry, context, warden))
|
141
143
|
return f
|
142
144
|
end
|
145
|
+
i += 1
|
143
146
|
end
|
144
147
|
nil
|
145
148
|
end
|
146
149
|
|
147
150
|
# @return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields
|
148
|
-
def fields(context = GraphQL::Query::NullContext)
|
151
|
+
def fields(context = GraphQL::Query::NullContext.instance)
|
149
152
|
# Objects need to check that the interface implementation is visible, too
|
150
153
|
warden = Warden.from_context(context)
|
151
154
|
# Local overrides take precedence over inherited fields
|