graphql 2.1.0 → 2.1.1
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/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_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/visitor.rb +2 -2
- data/lib/graphql/analysis/ast.rb +15 -11
- data/lib/graphql/dataloader/source.rb +7 -0
- data/lib/graphql/dataloader.rb +9 -0
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +90 -251
- data/lib/graphql/execution/interpreter.rb +0 -6
- data/lib/graphql/execution/lookahead.rb +1 -1
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/language/block_string.rb +28 -16
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +30 -19
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/printer.rb +88 -27
- data/lib/graphql/language/sanitized_printer.rb +6 -1
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +2 -0
- data/lib/graphql/language.rb +1 -0
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +32 -98
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -15
- data/lib/graphql/schema/field.rb +6 -3
- data/lib/graphql/schema/has_single_input_argument.rb +156 -0
- data/lib/graphql/schema/introspection_system.rb +2 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +2 -1
- data/lib/graphql/schema/member/has_arguments.rb +14 -2
- data/lib/graphql/schema/member/has_fields.rb +4 -1
- data/lib/graphql/schema/member/has_interfaces.rb +21 -7
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -128
- data/lib/graphql/schema/resolver.rb +4 -0
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/warden.rb +20 -3
- data/lib/graphql/schema.rb +19 -2
- 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 +1 -1
- 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 +1 -1
- data/lib/graphql/static_validation/validation_context.rb +5 -2
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -1
- metadata +7 -2
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Query
|
4
|
+
class Context
|
5
|
+
class ScopedContext
|
6
|
+
def initialize(query_context)
|
7
|
+
@query_context = query_context
|
8
|
+
@scoped_contexts = nil
|
9
|
+
@all_keys = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def merged_context
|
13
|
+
if @scoped_contexts.nil?
|
14
|
+
GraphQL::EmptyObjects::EMPTY_HASH
|
15
|
+
else
|
16
|
+
merged_ctx = {}
|
17
|
+
each_present_path_ctx do |path_ctx|
|
18
|
+
merged_ctx = path_ctx.merge(merged_ctx)
|
19
|
+
end
|
20
|
+
merged_ctx
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def merge!(hash, at: current_path)
|
25
|
+
@all_keys ||= Set.new
|
26
|
+
@all_keys.merge(hash.keys)
|
27
|
+
ctx = @scoped_contexts ||= {}
|
28
|
+
at.each do |path_part|
|
29
|
+
ctx = ctx[path_part] ||= { parent: ctx }
|
30
|
+
end
|
31
|
+
this_scoped_ctx = ctx[:scoped_context] ||= {}
|
32
|
+
this_scoped_ctx.merge!(hash)
|
33
|
+
end
|
34
|
+
|
35
|
+
def key?(key)
|
36
|
+
if @all_keys && @all_keys.include?(key)
|
37
|
+
each_present_path_ctx do |path_ctx|
|
38
|
+
if path_ctx.key?(key)
|
39
|
+
return true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def [](key)
|
47
|
+
each_present_path_ctx do |path_ctx|
|
48
|
+
if path_ctx.key?(key)
|
49
|
+
return path_ctx[key]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def current_path
|
56
|
+
@query_context.current_path || GraphQL::EmptyObjects::EMPTY_ARRAY
|
57
|
+
end
|
58
|
+
|
59
|
+
def dig(key, *other_keys)
|
60
|
+
each_present_path_ctx do |path_ctx|
|
61
|
+
if path_ctx.key?(key)
|
62
|
+
found_value = path_ctx[key]
|
63
|
+
if other_keys.any?
|
64
|
+
return found_value.dig(*other_keys)
|
65
|
+
else
|
66
|
+
return found_value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
# Start at the current location,
|
76
|
+
# but look up the tree for previously-assigned scoped values
|
77
|
+
def each_present_path_ctx
|
78
|
+
ctx = @scoped_contexts
|
79
|
+
if ctx.nil?
|
80
|
+
# no-op
|
81
|
+
else
|
82
|
+
current_path.each do |path_part|
|
83
|
+
if ctx.key?(path_part)
|
84
|
+
ctx = ctx[path_part]
|
85
|
+
else
|
86
|
+
break
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
while ctx
|
91
|
+
if (scoped_ctx = ctx[:scoped_context])
|
92
|
+
yield(scoped_ctx)
|
93
|
+
end
|
94
|
+
ctx = ctx[:parent]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "graphql/query/context/scoped_context"
|
3
|
+
|
2
4
|
module GraphQL
|
3
5
|
class Query
|
4
6
|
# Expose some query-specific info to field resolve functions.
|
@@ -90,104 +92,6 @@ module GraphQL
|
|
90
92
|
@scoped_context = ScopedContext.new(self)
|
91
93
|
end
|
92
94
|
|
93
|
-
class ScopedContext
|
94
|
-
NO_PATH = GraphQL::EmptyObjects::EMPTY_ARRAY
|
95
|
-
NO_CONTEXT = GraphQL::EmptyObjects::EMPTY_HASH
|
96
|
-
|
97
|
-
def initialize(query_context)
|
98
|
-
@query_context = query_context
|
99
|
-
@scoped_contexts = nil
|
100
|
-
@all_keys = nil
|
101
|
-
end
|
102
|
-
|
103
|
-
def merged_context
|
104
|
-
if @scoped_contexts.nil?
|
105
|
-
NO_CONTEXT
|
106
|
-
else
|
107
|
-
merged_ctx = {}
|
108
|
-
each_present_path_ctx do |path_ctx|
|
109
|
-
merged_ctx = path_ctx.merge(merged_ctx)
|
110
|
-
end
|
111
|
-
merged_ctx
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def merge!(hash)
|
116
|
-
@all_keys ||= Set.new
|
117
|
-
@all_keys.merge(hash.keys)
|
118
|
-
ctx = @scoped_contexts ||= {}
|
119
|
-
current_path.each do |path_part|
|
120
|
-
ctx = ctx[path_part] ||= { parent: ctx }
|
121
|
-
end
|
122
|
-
this_scoped_ctx = ctx[:scoped_context] ||= {}
|
123
|
-
this_scoped_ctx.merge!(hash)
|
124
|
-
end
|
125
|
-
|
126
|
-
def key?(key)
|
127
|
-
if @all_keys && @all_keys.include?(key)
|
128
|
-
each_present_path_ctx do |path_ctx|
|
129
|
-
if path_ctx.key?(key)
|
130
|
-
return true
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
false
|
135
|
-
end
|
136
|
-
|
137
|
-
def [](key)
|
138
|
-
each_present_path_ctx do |path_ctx|
|
139
|
-
if path_ctx.key?(key)
|
140
|
-
return path_ctx[key]
|
141
|
-
end
|
142
|
-
end
|
143
|
-
nil
|
144
|
-
end
|
145
|
-
|
146
|
-
def current_path
|
147
|
-
@query_context.current_path || NO_PATH
|
148
|
-
end
|
149
|
-
|
150
|
-
def dig(key, *other_keys)
|
151
|
-
each_present_path_ctx do |path_ctx|
|
152
|
-
if path_ctx.key?(key)
|
153
|
-
found_value = path_ctx[key]
|
154
|
-
if other_keys.any?
|
155
|
-
return found_value.dig(*other_keys)
|
156
|
-
else
|
157
|
-
return found_value
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
nil
|
162
|
-
end
|
163
|
-
|
164
|
-
private
|
165
|
-
|
166
|
-
# Start at the current location,
|
167
|
-
# but look up the tree for previously-assigned scoped values
|
168
|
-
def each_present_path_ctx
|
169
|
-
ctx = @scoped_contexts
|
170
|
-
if ctx.nil?
|
171
|
-
# no-op
|
172
|
-
else
|
173
|
-
current_path.each do |path_part|
|
174
|
-
if ctx.key?(path_part)
|
175
|
-
ctx = ctx[path_part]
|
176
|
-
else
|
177
|
-
break
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
while ctx
|
182
|
-
if (scoped_ctx = ctx[:scoped_context])
|
183
|
-
yield(scoped_ctx)
|
184
|
-
end
|
185
|
-
ctx = ctx[:parent]
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
95
|
# @return [Hash] A hash that will be added verbatim to the result hash, as `"extensions" => { ... }`
|
192
96
|
def response_extensions
|
193
97
|
namespace(:__query_result_extensions__)
|
@@ -345,6 +249,36 @@ module GraphQL
|
|
345
249
|
scoped_merge!(key => value)
|
346
250
|
nil
|
347
251
|
end
|
252
|
+
|
253
|
+
# Use this when you need to do a scoped set _inside_ a lazy-loaded (or batch-loaded)
|
254
|
+
# block of code.
|
255
|
+
#
|
256
|
+
# @example using scoped context inside a promise
|
257
|
+
# scoped_ctx = context.scoped
|
258
|
+
# SomeBatchLoader.load(...).then do |thing|
|
259
|
+
# # use a scoped_ctx which was created _before_ dataloading:
|
260
|
+
# scoped_ctx.set!(:thing, thing)
|
261
|
+
# end
|
262
|
+
# @return [Context::Scoped]
|
263
|
+
def scoped
|
264
|
+
Scoped.new(@scoped_context, current_path)
|
265
|
+
end
|
266
|
+
|
267
|
+
class Scoped
|
268
|
+
def initialize(scoped_context, path)
|
269
|
+
@path = path
|
270
|
+
@scoped_context = scoped_context
|
271
|
+
end
|
272
|
+
|
273
|
+
def merge!(hash)
|
274
|
+
@scoped_context.merge!(hash, at: @path)
|
275
|
+
end
|
276
|
+
|
277
|
+
def set!(key, value)
|
278
|
+
@scoped_context.merge!({ key => value }, at: @path)
|
279
|
+
nil
|
280
|
+
end
|
281
|
+
end
|
348
282
|
end
|
349
283
|
end
|
350
284
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
class Directive < GraphQL::Schema::Member
|
5
|
+
class SpecifiedBy < GraphQL::Schema::Directive
|
6
|
+
description "Exposes a URL that specifies the behavior of this scalar."
|
7
|
+
locations(GraphQL::Schema::Directive::SCALAR)
|
8
|
+
default_directive true
|
9
|
+
|
10
|
+
argument :url, String, description: "The URL that specifies the behavior of this scalar."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -54,23 +54,9 @@ module GraphQL
|
|
54
54
|
value.edge_class = custom_t
|
55
55
|
end
|
56
56
|
value
|
57
|
-
|
57
|
+
else
|
58
58
|
context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
|
59
59
|
context.schema.connections.wrap(field, object.object, value, original_arguments, context)
|
60
|
-
else
|
61
|
-
if object.is_a?(GraphQL::Schema::Object)
|
62
|
-
object = object.object
|
63
|
-
end
|
64
|
-
connection_class = GraphQL::Relay::BaseConnection.connection_for_nodes(value)
|
65
|
-
connection_class.new(
|
66
|
-
value,
|
67
|
-
original_arguments,
|
68
|
-
field: field,
|
69
|
-
max_page_size: field.max_page_size,
|
70
|
-
default_page_size: field.default_page_size,
|
71
|
-
parent: object,
|
72
|
-
context: context,
|
73
|
-
)
|
74
60
|
end
|
75
61
|
end
|
76
62
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -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
|
@@ -659,7 +662,7 @@ module GraphQL
|
|
659
662
|
method_to_call = nil
|
660
663
|
method_args = nil
|
661
664
|
|
662
|
-
Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
665
|
+
@own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
663
666
|
|
664
667
|
query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
|
665
668
|
if is_authorized
|
@@ -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)
|
58
|
+
dummy.arguments(context)
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_field_argument(name, context = GraphQL::Query::NullContext)
|
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
|
@@ -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
|
@@ -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
|
|
@@ -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
|
@@ -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|
|
@@ -175,7 +183,7 @@ module GraphQL
|
|
175
183
|
module FieldConfigured
|
176
184
|
def arguments(context = GraphQL::Query::NullContext)
|
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
|
@@ -133,13 +133,16 @@ module GraphQL
|
|
133
133
|
def get_field(field_name, context = GraphQL::Query::NullContext)
|
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
|
@@ -72,9 +72,18 @@ module GraphQL
|
|
72
72
|
module InheritedInterfaces
|
73
73
|
def interfaces(context = GraphQL::Query::NullContext)
|
74
74
|
visible_interfaces = super
|
75
|
-
|
76
|
-
visible_interfaces.
|
77
|
-
|
75
|
+
inherited_interfaces = superclass.interfaces(context)
|
76
|
+
if visible_interfaces.any?
|
77
|
+
if inherited_interfaces.any?
|
78
|
+
visible_interfaces.concat(inherited_interfaces)
|
79
|
+
visible_interfaces.uniq!
|
80
|
+
end
|
81
|
+
visible_interfaces
|
82
|
+
elsif inherited_interfaces.any?
|
83
|
+
inherited_interfaces
|
84
|
+
else
|
85
|
+
EmptyObjects::EMPTY_ARRAY
|
86
|
+
end
|
78
87
|
end
|
79
88
|
|
80
89
|
def interface_type_memberships
|
@@ -92,23 +101,28 @@ module GraphQL
|
|
92
101
|
# param context [Query::Context] If omitted, skip filtering.
|
93
102
|
def interfaces(context = GraphQL::Query::NullContext)
|
94
103
|
warden = Warden.from_context(context)
|
95
|
-
visible_interfaces =
|
104
|
+
visible_interfaces = nil
|
96
105
|
own_interface_type_memberships.each do |type_membership|
|
97
106
|
case type_membership
|
98
107
|
when Schema::TypeMembership
|
99
108
|
if warden.visible_type_membership?(type_membership, context)
|
109
|
+
visible_interfaces ||= []
|
100
110
|
visible_interfaces << type_membership.abstract_type
|
101
111
|
end
|
102
112
|
when String, Schema::LateBoundType
|
103
113
|
# During initialization, `type_memberships` can hold late-bound types
|
114
|
+
visible_interfaces ||= []
|
104
115
|
visible_interfaces << type_membership
|
105
116
|
else
|
106
117
|
raise "Invariant: Unexpected type_membership #{type_membership.class}: #{type_membership.inspect}"
|
107
118
|
end
|
108
119
|
end
|
109
|
-
visible_interfaces
|
110
|
-
|
111
|
-
|
120
|
+
if visible_interfaces
|
121
|
+
visible_interfaces.uniq!
|
122
|
+
visible_interfaces
|
123
|
+
else
|
124
|
+
EmptyObjects::EMPTY_ARRAY
|
125
|
+
end
|
112
126
|
end
|
113
127
|
|
114
128
|
private
|