graphql 2.0.27 → 2.1.3
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/query_depth.rb +7 -2
- 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 +38 -10
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +170 -0
- data/lib/graphql/execution/interpreter/runtime.rb +95 -254
- 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 +36 -35
- data/lib/graphql/language/nodes.rb +2 -2
- data/lib/graphql/language/printer.rb +294 -145
- 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/pagination/connection.rb +23 -1
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +32 -98
- data/lib/graphql/query.rb +2 -19
- data/lib/graphql/rake_task.rb +3 -12
- 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/scope_extension.rb +7 -1
- data/lib/graphql/schema/field.rb +7 -4
- 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 +19 -4
- data/lib/graphql/schema/member/has_fields.rb +4 -1
- data/lib/graphql/schema/member/has_interfaces.rb +21 -7
- data/lib/graphql/schema/member/scoped.rb +19 -0
- 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 +4 -0
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/warden.rb +87 -89
- data/lib/graphql/schema.rb +125 -55
- 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 -5
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -1
- data/lib/graphql/subscriptions.rb +11 -6
- data/lib/graphql/tracing/appoptics_trace.rb +2 -2
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/types/relay/connection_behaviors.rb +19 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +7 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -2
- metadata +23 -20
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/static_validation/type_stack.rb +0 -216
@@ -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
|
data/lib/graphql/query.rb
CHANGED
@@ -95,15 +95,10 @@ module GraphQL
|
|
95
95
|
# @param root_value [Object] the object used to resolve fields on the root type
|
96
96
|
# @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
|
97
97
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
98
|
-
|
99
|
-
# @param only [<#call(schema_member, context)>] If provided, objects will be hidden from the schema when `.call(schema_member, context)` returns false
|
100
|
-
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil, warden: nil)
|
98
|
+
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil)
|
101
99
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
102
100
|
variables ||= {}
|
103
101
|
@schema = schema
|
104
|
-
if only || except
|
105
|
-
merge_filters(except: except, only: only)
|
106
|
-
end
|
107
102
|
@context = schema.context_class.new(query: self, object: root_value, values: context)
|
108
103
|
@warden = warden
|
109
104
|
@subscription_topic = subscription_topic
|
@@ -129,7 +124,6 @@ module GraphQL
|
|
129
124
|
raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
|
130
125
|
end
|
131
126
|
|
132
|
-
|
133
127
|
@analysis_errors = []
|
134
128
|
if variables.is_a?(String)
|
135
129
|
raise ArgumentError, "Query variables should be a Hash, not a String. Try JSON.parse to prepare variables."
|
@@ -354,17 +348,6 @@ module GraphQL
|
|
354
348
|
with_prepared_ast { @query }
|
355
349
|
end
|
356
350
|
|
357
|
-
# @return [void]
|
358
|
-
def merge_filters(only: nil, except: nil)
|
359
|
-
if @prepared_ast
|
360
|
-
raise "Can't add filters after preparing the query"
|
361
|
-
else
|
362
|
-
@filter ||= @schema.default_filter
|
363
|
-
@filter = @filter.merge(only: only, except: except)
|
364
|
-
end
|
365
|
-
nil
|
366
|
-
end
|
367
|
-
|
368
351
|
def subscription?
|
369
352
|
with_prepared_ast { @subscription }
|
370
353
|
end
|
@@ -400,7 +383,7 @@ module GraphQL
|
|
400
383
|
|
401
384
|
def prepare_ast
|
402
385
|
@prepared_ast = true
|
403
|
-
@warden ||= @schema.warden_class.new(
|
386
|
+
@warden ||= @schema.warden_class.new(schema: @schema, context: @context)
|
404
387
|
parse_error = nil
|
405
388
|
@document ||= begin
|
406
389
|
if query_string
|
data/lib/graphql/rake_task.rb
CHANGED
@@ -9,8 +9,7 @@ module GraphQL
|
|
9
9
|
# By default, schemas are looked up by name as constants using `schema_name:`.
|
10
10
|
# You can provide a `load_schema` function to return your schema another way.
|
11
11
|
#
|
12
|
-
# `load_context
|
13
|
-
# you can keep an eye on how filters affect your schema.
|
12
|
+
# Use `load_context:` and `visible?` to dump schemas under certain visibility constraints.
|
14
13
|
#
|
15
14
|
# @example Dump a Schema to .graphql + .json files
|
16
15
|
# require "graphql/rake_task"
|
@@ -36,8 +35,6 @@ module GraphQL
|
|
36
35
|
schema_name: nil,
|
37
36
|
load_schema: ->(task) { Object.const_get(task.schema_name) },
|
38
37
|
load_context: ->(task) { {} },
|
39
|
-
only: nil,
|
40
|
-
except: nil,
|
41
38
|
directory: ".",
|
42
39
|
idl_outfile: "schema.graphql",
|
43
40
|
json_outfile: "schema.json",
|
@@ -68,12 +65,6 @@ module GraphQL
|
|
68
65
|
# @return [<#call(task)>] A callable for loading the query context
|
69
66
|
attr_accessor :load_context
|
70
67
|
|
71
|
-
# @return [<#call(member, ctx)>, nil] A filter for this task
|
72
|
-
attr_accessor :only
|
73
|
-
|
74
|
-
# @return [<#call(member, ctx)>, nil] A filter for this task
|
75
|
-
attr_accessor :except
|
76
|
-
|
77
68
|
# @return [String] target for IDL task
|
78
69
|
attr_accessor :idl_outfile
|
79
70
|
|
@@ -117,10 +108,10 @@ module GraphQL
|
|
117
108
|
include_is_repeatable: include_is_repeatable,
|
118
109
|
include_specified_by_url: include_specified_by_url,
|
119
110
|
include_schema_description: include_schema_description,
|
120
|
-
|
111
|
+
context: context
|
121
112
|
)
|
122
113
|
when :to_definition
|
123
|
-
schema.to_definition(
|
114
|
+
schema.to_definition(context: context)
|
124
115
|
else
|
125
116
|
raise ArgumentError, "Unexpected schema dump method: #{method_name.inspect}"
|
126
117
|
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
|
@@ -10,7 +10,13 @@ module GraphQL
|
|
10
10
|
else
|
11
11
|
ret_type = @field.type.unwrap
|
12
12
|
if ret_type.respond_to?(:scope_items)
|
13
|
-
ret_type.scope_items(value, context)
|
13
|
+
scoped_items = ret_type.scope_items(value, context)
|
14
|
+
if !scoped_items.equal?(value) && !ret_type.reauthorize_scoped_objects
|
15
|
+
current_runtime_state = Thread.current[:__graphql_runtime_info]
|
16
|
+
query_runtime_state = current_runtime_state[context.query]
|
17
|
+
query_runtime_state.was_authorized_by_scope_items = true
|
18
|
+
end
|
19
|
+
scoped_items
|
14
20
|
else
|
15
21
|
value
|
16
22
|
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
|
@@ -701,7 +704,7 @@ module GraphQL
|
|
701
704
|
inner_object.dig(*@dig_keys)
|
702
705
|
elsif inner_object.key?(@method_sym)
|
703
706
|
inner_object[@method_sym]
|
704
|
-
elsif inner_object.key?(@method_str)
|
707
|
+
elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
|
705
708
|
inner_object[@method_str]
|
706
709
|
elsif @fallback_value != NOT_CONFIGURED
|
707
710
|
@fallback_value
|
@@ -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
|
@@ -380,8 +392,11 @@ module GraphQL
|
|
380
392
|
application_object_type = resolve_type_result
|
381
393
|
# application_object is already assigned
|
382
394
|
end
|
383
|
-
|
384
|
-
if !
|
395
|
+
|
396
|
+
if !(
|
397
|
+
context.warden.possible_types(argument.loads).include?(application_object_type) ||
|
398
|
+
context.warden.loadable?(argument.loads, context)
|
399
|
+
)
|
385
400
|
err = GraphQL::LoadApplicationObjectFailedError.new(argument: argument, id: id, object: application_object)
|
386
401
|
load_application_object_failed(err)
|
387
402
|
else
|
@@ -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
|
@@ -15,6 +15,25 @@ module GraphQL
|
|
15
15
|
def scope_items(items, context)
|
16
16
|
items
|
17
17
|
end
|
18
|
+
|
19
|
+
def reauthorize_scoped_objects(new_value = nil)
|
20
|
+
if new_value.nil?
|
21
|
+
if @reauthorize_scoped_objects != nil
|
22
|
+
@reauthorize_scoped_objects
|
23
|
+
else
|
24
|
+
find_inherited_value(:reauthorize_scoped_objects, nil)
|
25
|
+
end
|
26
|
+
else
|
27
|
+
@reauthorize_scoped_objects = new_value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def inherited(subclass)
|
32
|
+
super
|
33
|
+
subclass.class_eval do
|
34
|
+
@reauthorize_scoped_objects = nil
|
35
|
+
end
|
36
|
+
end
|
18
37
|
end
|
19
38
|
end
|
20
39
|
end
|