graphql 2.1.0 → 2.1.1
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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- 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
|