graphql 1.12.21 → 1.13.2
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +3 -1
- data/lib/generators/graphql/install_generator.rb +9 -2
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/type_generator.rb +0 -1
- data/lib/graphql/analysis/ast/field_usage.rb +2 -2
- data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
- data/lib/graphql/analysis/ast/visitor.rb +4 -4
- data/lib/graphql/backtrace/table.rb +1 -1
- data/lib/graphql/base_type.rb +4 -2
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/dataloader.rb +55 -22
- data/lib/graphql/directive/deprecated_directive.rb +1 -1
- data/lib/graphql/directive/include_directive.rb +1 -1
- data/lib/graphql/directive/skip_directive.rb +1 -1
- data/lib/graphql/directive.rb +0 -4
- data/lib/graphql/enum_type.rb +5 -1
- data/lib/graphql/execution/errors.rb +1 -0
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
- data/lib/graphql/execution/interpreter/runtime.rb +31 -19
- data/lib/graphql/execution/lookahead.rb +2 -2
- data/lib/graphql/execution/multiplex.rb +4 -1
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/int_type.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/input_value_type.rb +4 -4
- data/lib/graphql/introspection/schema_type.rb +2 -2
- data/lib/graphql/introspection/type_type.rb +10 -10
- data/lib/graphql/language/block_string.rb +2 -6
- data/lib/graphql/language/document_from_schema_definition.rb +4 -2
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/nodes.rb +12 -2
- data/lib/graphql/language/parser.rb +442 -434
- data/lib/graphql/language/parser.y +5 -4
- data/lib/graphql/language/printer.rb +6 -1
- data/lib/graphql/language/sanitized_printer.rb +5 -5
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/name_validator.rb +0 -4
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/arguments_cache.rb +1 -1
- data/lib/graphql/query/context.rb +15 -2
- data/lib/graphql/query/literal_input.rb +1 -1
- data/lib/graphql/query/null_context.rb +12 -7
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/relay/edges_instrumentation.rb +0 -1
- data/lib/graphql/relay/global_id_resolve.rb +1 -1
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/schema/addition.rb +37 -28
- data/lib/graphql/schema/argument.rb +8 -6
- data/lib/graphql/schema/build_from_definition.rb +5 -5
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +7 -3
- data/lib/graphql/schema/enum.rb +60 -10
- data/lib/graphql/schema/enum_value.rb +6 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +126 -38
- data/lib/graphql/schema/field_extension.rb +52 -2
- data/lib/graphql/schema/find_inherited_value.rb +1 -0
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +8 -5
- data/lib/graphql/schema/interface.rb +11 -20
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/list.rb +3 -1
- data/lib/graphql/schema/member/accepts_definition.rb +15 -3
- data/lib/graphql/schema/member/build_type.rb +0 -4
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/member/has_arguments.rb +55 -13
- data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +76 -18
- data/lib/graphql/schema/member/has_interfaces.rb +90 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/non_null.rb +3 -1
- data/lib/graphql/schema/object.rb +10 -75
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
- data/lib/graphql/schema/resolver.rb +37 -17
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/subscription.rb +11 -1
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +18 -4
- data/lib/graphql/schema/union.rb +8 -1
- data/lib/graphql/schema/validator/format_validator.rb +0 -4
- data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
- data/lib/graphql/schema/validator.rb +4 -7
- data/lib/graphql/schema/warden.rb +116 -52
- data/lib/graphql/schema.rb +106 -22
- data/lib/graphql/static_validation/base_visitor.rb +5 -5
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +15 -8
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
- data/lib/graphql/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -4
- data/lib/graphql/subscriptions/event.rb +20 -12
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
- data/lib/graphql/types/relay/default_relay.rb +5 -1
- data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
- data/lib/graphql/types/relay/has_node_field.rb +1 -1
- data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -32
- metadata +10 -5
|
@@ -35,7 +35,7 @@ module GraphQL
|
|
|
35
35
|
GraphQL::Schema::Directive::INPUT_FIELD_DEFINITION,
|
|
36
36
|
)
|
|
37
37
|
|
|
38
|
-
argument :by, [String], "Flags to check for this schema member"
|
|
38
|
+
argument :by, [String], "Flags to check for this schema member"
|
|
39
39
|
|
|
40
40
|
module VisibleByFlag
|
|
41
41
|
def self.included(schema_class)
|
|
@@ -44,7 +44,7 @@ module GraphQL
|
|
|
44
44
|
|
|
45
45
|
def visible?(context)
|
|
46
46
|
if dir = self.directives.find { |d| d.is_a?(Flagged) }
|
|
47
|
-
relevant_flags = (f = context[:flags]) && dir.arguments[:by] & f
|
|
47
|
+
relevant_flags = (f = context[:flags]) && dir.arguments[:by] & f # rubocop:disable Development/ContextIsPassedCop -- definition-related
|
|
48
48
|
relevant_flags && relevant_flags.any? && super
|
|
49
49
|
else
|
|
50
50
|
super
|
|
@@ -8,6 +8,8 @@ module GraphQL
|
|
|
8
8
|
# - {.resolve}: Wraps field resolution (so it should call `yield` to continue)
|
|
9
9
|
class Directive < GraphQL::Schema::Member
|
|
10
10
|
extend GraphQL::Schema::Member::HasArguments
|
|
11
|
+
extend GraphQL::Schema::Member::AcceptsDefinition
|
|
12
|
+
|
|
11
13
|
class << self
|
|
12
14
|
# Directives aren't types, they don't have kinds.
|
|
13
15
|
undef_method :kind
|
|
@@ -53,6 +55,8 @@ module GraphQL
|
|
|
53
55
|
default_directive
|
|
54
56
|
end
|
|
55
57
|
|
|
58
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
|
59
|
+
|
|
56
60
|
def to_graphql
|
|
57
61
|
defn = GraphQL::Directive.new
|
|
58
62
|
defn.name = self.graphql_name
|
|
@@ -61,9 +65,9 @@ module GraphQL
|
|
|
61
65
|
defn.default_directive = self.default_directive
|
|
62
66
|
defn.ast_node = ast_node
|
|
63
67
|
defn.metadata[:type_class] = self
|
|
64
|
-
|
|
65
|
-
arg_graphql = arg_defn.to_graphql
|
|
66
|
-
defn.arguments[arg_graphql.name] = arg_graphql
|
|
68
|
+
all_argument_definitions.each do |arg_defn|
|
|
69
|
+
arg_graphql = arg_defn.to_graphql(silence_deprecation_warning: true)
|
|
70
|
+
defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
|
67
71
|
end
|
|
68
72
|
# Make a reference to a classic-style Arguments class
|
|
69
73
|
defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(defn)
|
data/lib/graphql/schema/enum.rb
CHANGED
|
@@ -46,20 +46,70 @@ module GraphQL
|
|
|
46
46
|
def value(*args, **kwargs, &block)
|
|
47
47
|
kwargs[:owner] = self
|
|
48
48
|
value = enum_value_class.new(*args, **kwargs, &block)
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
key = value.graphql_name
|
|
50
|
+
prev_value = own_values[key]
|
|
51
|
+
case prev_value
|
|
52
|
+
when nil
|
|
53
|
+
own_values[key] = value
|
|
54
|
+
when GraphQL::Schema::EnumValue
|
|
55
|
+
own_values[key] = [prev_value, value]
|
|
56
|
+
when Array
|
|
57
|
+
prev_value << value
|
|
58
|
+
else
|
|
59
|
+
raise "Invariant: Unexpected enum value for #{key.inspect}: #{prev_value.inspect}"
|
|
51
60
|
end
|
|
52
|
-
|
|
53
|
-
nil
|
|
61
|
+
value
|
|
54
62
|
end
|
|
55
63
|
|
|
56
|
-
# @return [
|
|
57
|
-
def
|
|
58
|
-
inherited_values = superclass
|
|
59
|
-
|
|
60
|
-
|
|
64
|
+
# @return [Array<GraphQL::Schema::EnumValue>] Possible values of this enum
|
|
65
|
+
def enum_values(context = GraphQL::Query::NullContext)
|
|
66
|
+
inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
|
|
67
|
+
visible_values = []
|
|
68
|
+
warden = Warden.from_context(context)
|
|
69
|
+
own_values.each do |key, values_entry|
|
|
70
|
+
if (v = Warden.visible_entry?(:visible_enum_value?, values_entry, context, warden))
|
|
71
|
+
visible_values << v
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
if inherited_values
|
|
76
|
+
# Local values take precedence over inherited ones
|
|
77
|
+
inherited_values.each do |i_val|
|
|
78
|
+
if !visible_values.any? { |v| v.graphql_name == i_val.graphql_name }
|
|
79
|
+
visible_values << i_val
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
visible_values
|
|
61
85
|
end
|
|
62
86
|
|
|
87
|
+
# @return [Array<Schema::EnumValue>] An unfiltered list of all definitions
|
|
88
|
+
def all_enum_value_definitions
|
|
89
|
+
all_defns = if superclass.respond_to?(:all_enum_value_definitions)
|
|
90
|
+
superclass.all_enum_value_definitions
|
|
91
|
+
else
|
|
92
|
+
[]
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
@own_values && @own_values.each do |_key, value|
|
|
96
|
+
if value.is_a?(Array)
|
|
97
|
+
all_defns.concat(value)
|
|
98
|
+
else
|
|
99
|
+
all_defns << value
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
all_defns
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @return [Hash<String => GraphQL::Schema::EnumValue>] Possible values of this enum, keyed by name.
|
|
107
|
+
def values(context = GraphQL::Query::NullContext)
|
|
108
|
+
enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
|
112
|
+
|
|
63
113
|
# @return [GraphQL::EnumType]
|
|
64
114
|
def to_graphql
|
|
65
115
|
enum_type = GraphQL::EnumType.new
|
|
@@ -68,7 +118,7 @@ module GraphQL
|
|
|
68
118
|
enum_type.introspection = introspection
|
|
69
119
|
enum_type.ast_node = ast_node
|
|
70
120
|
values.each do |name, val|
|
|
71
|
-
enum_type.add_value(val.
|
|
121
|
+
enum_type.add_value(val.deprecated_to_graphql)
|
|
72
122
|
end
|
|
73
123
|
enum_type.metadata[:type_class] = self
|
|
74
124
|
enum_type
|
|
@@ -73,6 +73,8 @@ module GraphQL
|
|
|
73
73
|
@value
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
|
77
|
+
|
|
76
78
|
# @return [GraphQL::EnumType::EnumValue] A runtime-ready object derived from this object
|
|
77
79
|
def to_graphql
|
|
78
80
|
enum_value = GraphQL::EnumType::EnumValue.new
|
|
@@ -85,6 +87,10 @@ module GraphQL
|
|
|
85
87
|
enum_value
|
|
86
88
|
end
|
|
87
89
|
|
|
90
|
+
def inspect
|
|
91
|
+
"#<#{self.class} #{path} @value=#{@value.inspect}#{description ? " @description=#{description.inspect}" : ""}>"
|
|
92
|
+
end
|
|
93
|
+
|
|
88
94
|
def visible?(_ctx); true; end
|
|
89
95
|
def accessible?(_ctx); true; end
|
|
90
96
|
def authorized?(_ctx); true; end
|
|
@@ -42,7 +42,7 @@ module GraphQL
|
|
|
42
42
|
value.after_value ||= original_arguments[:after]
|
|
43
43
|
value.last_value ||= original_arguments[:last]
|
|
44
44
|
value.before_value ||= original_arguments[:before]
|
|
45
|
-
value.arguments ||= original_arguments
|
|
45
|
+
value.arguments ||= original_arguments # rubocop:disable Development/ContextIsPassedCop -- unrelated .arguments method
|
|
46
46
|
value.field ||= field
|
|
47
47
|
if field.has_max_page_size? && !value.has_max_page_size_override?
|
|
48
48
|
value.max_page_size = field.max_page_size
|
data/lib/graphql/schema/field.rb
CHANGED
|
@@ -5,10 +5,6 @@ require "graphql/schema/field/scope_extension"
|
|
|
5
5
|
module GraphQL
|
|
6
6
|
class Schema
|
|
7
7
|
class Field
|
|
8
|
-
if !String.method_defined?(:-@)
|
|
9
|
-
using GraphQL::StringDedupBackport
|
|
10
|
-
end
|
|
11
|
-
|
|
12
8
|
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
|
13
9
|
include GraphQL::Schema::Member::AcceptsDefinition
|
|
14
10
|
include GraphQL::Schema::Member::HasArguments
|
|
@@ -61,7 +57,7 @@ module GraphQL
|
|
|
61
57
|
end
|
|
62
58
|
|
|
63
59
|
def inspect
|
|
64
|
-
"#<#{self.class} #{path}#{
|
|
60
|
+
"#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
|
|
65
61
|
end
|
|
66
62
|
|
|
67
63
|
alias :mutation :resolver
|
|
@@ -212,7 +208,7 @@ module GraphQL
|
|
|
212
208
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
|
213
209
|
# @param validates [Array<Hash>] Configurations for validating this field
|
|
214
210
|
# @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
|
|
215
|
-
def initialize(type: nil, name: nil, owner: nil, null:
|
|
211
|
+
def initialize(type: nil, name: nil, owner: nil, null: true, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, 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: nil, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, legacy_edge_class: nil, &definition_block)
|
|
216
212
|
if name.nil?
|
|
217
213
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
|
218
214
|
end
|
|
@@ -220,9 +216,6 @@ module GraphQL
|
|
|
220
216
|
if type.nil?
|
|
221
217
|
raise ArgumentError, "missing second `type` argument or keyword `type:`"
|
|
222
218
|
end
|
|
223
|
-
if null.nil?
|
|
224
|
-
raise ArgumentError, "missing keyword argument null:"
|
|
225
|
-
end
|
|
226
219
|
end
|
|
227
220
|
if (field || function || resolve) && extras.any?
|
|
228
221
|
raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
|
|
@@ -281,10 +274,15 @@ module GraphQL
|
|
|
281
274
|
@legacy_edge_class = legacy_edge_class
|
|
282
275
|
|
|
283
276
|
arguments.each do |name, arg|
|
|
284
|
-
|
|
277
|
+
case arg
|
|
278
|
+
when Hash
|
|
285
279
|
argument(name: name, **arg)
|
|
286
|
-
|
|
280
|
+
when GraphQL::Schema::Argument
|
|
287
281
|
add_argument(arg)
|
|
282
|
+
when Array
|
|
283
|
+
arg.each { |a| add_argument(a) }
|
|
284
|
+
else
|
|
285
|
+
raise ArgumentError, "Unexpected argument config (#{arg.class}): #{arg.inspect}"
|
|
288
286
|
end
|
|
289
287
|
end
|
|
290
288
|
|
|
@@ -292,6 +290,7 @@ module GraphQL
|
|
|
292
290
|
@subscription_scope = subscription_scope
|
|
293
291
|
|
|
294
292
|
@extensions = EMPTY_ARRAY
|
|
293
|
+
@call_after_define = false
|
|
295
294
|
# This should run before connection extension,
|
|
296
295
|
# but should it run after the definition block?
|
|
297
296
|
if scoped?
|
|
@@ -324,6 +323,9 @@ module GraphQL
|
|
|
324
323
|
instance_eval(&definition_block)
|
|
325
324
|
end
|
|
326
325
|
end
|
|
326
|
+
|
|
327
|
+
self.extensions.each(&:after_define_apply)
|
|
328
|
+
@call_after_define = true
|
|
327
329
|
end
|
|
328
330
|
|
|
329
331
|
# If true, subscription updates with this field can be shared between viewers
|
|
@@ -356,27 +358,20 @@ module GraphQL
|
|
|
356
358
|
# @example adding an extension with options
|
|
357
359
|
# extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
|
|
358
360
|
#
|
|
359
|
-
# @param extensions [Array<Class, Hash<Class =>
|
|
361
|
+
# @param extensions [Array<Class, Hash<Class => Hash>>] Add extensions to this field. For hash elements, only the first key/value is used.
|
|
360
362
|
# @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
|
|
361
363
|
def extensions(new_extensions = nil)
|
|
362
|
-
if new_extensions
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
@extensions = @extensions.dup
|
|
368
|
-
end
|
|
369
|
-
new_extensions.each do |extension|
|
|
370
|
-
if extension.is_a?(Hash)
|
|
371
|
-
extension = extension.to_a[0]
|
|
372
|
-
extension_class, options = *extension
|
|
373
|
-
@extensions << extension_class.new(field: self, options: options)
|
|
364
|
+
if new_extensions
|
|
365
|
+
new_extensions.each do |extension_config|
|
|
366
|
+
if extension_config.is_a?(Hash)
|
|
367
|
+
extension_class, options = *extension_config.to_a[0]
|
|
368
|
+
self.extension(extension_class, options)
|
|
374
369
|
else
|
|
375
|
-
|
|
376
|
-
@extensions << extension_class.new(field: self, options: nil)
|
|
370
|
+
self.extension(extension_config)
|
|
377
371
|
end
|
|
378
372
|
end
|
|
379
373
|
end
|
|
374
|
+
@extensions
|
|
380
375
|
end
|
|
381
376
|
|
|
382
377
|
# Add `extension` to this field, initialized with `options` if provided.
|
|
@@ -387,10 +382,19 @@ module GraphQL
|
|
|
387
382
|
# @example adding an extension with options
|
|
388
383
|
# extension(MyExtensionClass, filter: true)
|
|
389
384
|
#
|
|
390
|
-
# @param
|
|
391
|
-
# @param options [
|
|
392
|
-
|
|
393
|
-
|
|
385
|
+
# @param extension_class [Class] subclass of {Schema::FieldExtension}
|
|
386
|
+
# @param options [Hash] if provided, given as `options:` when initializing `extension`.
|
|
387
|
+
# @return [void]
|
|
388
|
+
def extension(extension_class, options = nil)
|
|
389
|
+
extension_inst = extension_class.new(field: self, options: options)
|
|
390
|
+
if @extensions.frozen?
|
|
391
|
+
@extensions = @extensions.dup
|
|
392
|
+
end
|
|
393
|
+
if @call_after_define
|
|
394
|
+
extension_inst.after_define_apply
|
|
395
|
+
end
|
|
396
|
+
@extensions << extension_inst
|
|
397
|
+
nil
|
|
394
398
|
end
|
|
395
399
|
|
|
396
400
|
# Read extras (as symbols) from this field,
|
|
@@ -411,6 +415,62 @@ module GraphQL
|
|
|
411
415
|
end
|
|
412
416
|
end
|
|
413
417
|
|
|
418
|
+
def calculate_complexity(query:, nodes:, child_complexity:)
|
|
419
|
+
if respond_to?(:complexity_for)
|
|
420
|
+
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
|
421
|
+
complexity_for(child_complexity: child_complexity, query: query, lookahead: lookahead)
|
|
422
|
+
elsif connection?
|
|
423
|
+
arguments = query.arguments_for(nodes.first, self)
|
|
424
|
+
max_possible_page_size = nil
|
|
425
|
+
if arguments[:first]
|
|
426
|
+
max_possible_page_size = arguments[:first]
|
|
427
|
+
end
|
|
428
|
+
if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
|
|
429
|
+
max_possible_page_size = arguments[:last]
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
if max_possible_page_size.nil?
|
|
433
|
+
max_possible_page_size = max_page_size || query.schema.default_max_page_size
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
if max_possible_page_size.nil?
|
|
437
|
+
raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `max_page_size` or `default_max_page_size`"
|
|
438
|
+
else
|
|
439
|
+
metadata_complexity = 0
|
|
440
|
+
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
|
441
|
+
|
|
442
|
+
if (page_info_lookahead = lookahead.selection(:page_info)).selected?
|
|
443
|
+
metadata_complexity += 1 # pageInfo
|
|
444
|
+
metadata_complexity += page_info_lookahead.selections.size # subfields
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
|
|
448
|
+
metadata_complexity += 1
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
nodes_edges_complexity = 0
|
|
452
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:edges)
|
|
453
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
|
|
454
|
+
|
|
455
|
+
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
|
456
|
+
items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
|
|
457
|
+
# Add 1 for _this_ field
|
|
458
|
+
1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
|
|
459
|
+
end
|
|
460
|
+
else
|
|
461
|
+
defined_complexity = complexity
|
|
462
|
+
case defined_complexity
|
|
463
|
+
when Proc
|
|
464
|
+
arguments = query.arguments_for(nodes.first, self)
|
|
465
|
+
defined_complexity.call(query.context, arguments.keyword_arguments, child_complexity)
|
|
466
|
+
when Numeric
|
|
467
|
+
defined_complexity + child_complexity
|
|
468
|
+
else
|
|
469
|
+
raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
end
|
|
473
|
+
|
|
414
474
|
def complexity(new_complexity = nil)
|
|
415
475
|
case new_complexity
|
|
416
476
|
when Proc
|
|
@@ -439,6 +499,8 @@ module GraphQL
|
|
|
439
499
|
# @return [Integer, nil] Applied to connections if {#has_max_page_size?}
|
|
440
500
|
attr_reader :max_page_size
|
|
441
501
|
|
|
502
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
|
503
|
+
|
|
442
504
|
# @return [GraphQL::Field]
|
|
443
505
|
def to_graphql
|
|
444
506
|
field_defn = if @field
|
|
@@ -493,9 +555,9 @@ module GraphQL
|
|
|
493
555
|
field_defn.subscription_scope = @subscription_scope
|
|
494
556
|
field_defn.ast_node = ast_node
|
|
495
557
|
|
|
496
|
-
|
|
497
|
-
arg_graphql = defn.
|
|
498
|
-
field_defn.arguments[arg_graphql.name] = arg_graphql
|
|
558
|
+
all_argument_definitions.each do |defn|
|
|
559
|
+
arg_graphql = defn.deprecated_to_graphql
|
|
560
|
+
field_defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
|
499
561
|
end
|
|
500
562
|
|
|
501
563
|
# Support a passed-in proc, one way or another
|
|
@@ -556,13 +618,39 @@ module GraphQL
|
|
|
556
618
|
|
|
557
619
|
def authorized?(object, args, context)
|
|
558
620
|
if @resolver_class
|
|
559
|
-
# The resolver will check itself during `resolve()`
|
|
621
|
+
# The resolver _instance_ will check itself during `resolve()`
|
|
560
622
|
@resolver_class.authorized?(object, context)
|
|
561
623
|
else
|
|
624
|
+
if (arg_values = context[:current_arguments])
|
|
625
|
+
# ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
|
|
626
|
+
using_arg_values = true
|
|
627
|
+
arg_values = arg_values.argument_values
|
|
628
|
+
else
|
|
629
|
+
arg_values = args
|
|
630
|
+
using_arg_values = false
|
|
631
|
+
end
|
|
562
632
|
# Faster than `.any?`
|
|
563
|
-
arguments.each_value do |arg|
|
|
564
|
-
|
|
565
|
-
|
|
633
|
+
arguments(context).each_value do |arg|
|
|
634
|
+
arg_key = arg.keyword
|
|
635
|
+
if arg_values.key?(arg_key)
|
|
636
|
+
arg_value = arg_values[arg_key]
|
|
637
|
+
if using_arg_values
|
|
638
|
+
if arg_value.default_used?
|
|
639
|
+
# pass -- no auth required for default used
|
|
640
|
+
next
|
|
641
|
+
else
|
|
642
|
+
application_arg_value = arg_value.value
|
|
643
|
+
if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
|
|
644
|
+
application_arg_value.keyword_arguments
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
else
|
|
648
|
+
application_arg_value = arg_value
|
|
649
|
+
end
|
|
650
|
+
|
|
651
|
+
if !arg.authorized?(object, application_arg_value, context)
|
|
652
|
+
return false
|
|
653
|
+
end
|
|
566
654
|
end
|
|
567
655
|
end
|
|
568
656
|
true
|
|
@@ -659,7 +747,7 @@ module GraphQL
|
|
|
659
747
|
ruby_kwargs = graphql_args.to_kwargs
|
|
660
748
|
maybe_lazies = []
|
|
661
749
|
# Apply any `prepare` methods. Not great code organization, can this go somewhere better?
|
|
662
|
-
arguments.each do |name, arg_defn|
|
|
750
|
+
arguments(field_ctx).each do |name, arg_defn|
|
|
663
751
|
ruby_kwargs_key = arg_defn.keyword
|
|
664
752
|
|
|
665
753
|
if ruby_kwargs.key?(ruby_kwargs_key)
|
|
@@ -15,15 +15,40 @@ module GraphQL
|
|
|
15
15
|
# @return [Object]
|
|
16
16
|
attr_reader :options
|
|
17
17
|
|
|
18
|
+
# @return [Array<Symbol>, nil] `default_argument`s added, if any were added (otherwise, `nil`)
|
|
19
|
+
attr_reader :added_default_arguments
|
|
20
|
+
|
|
18
21
|
# Called when the extension is mounted with `extension(name, options)`.
|
|
19
|
-
# The instance
|
|
22
|
+
# The instance will be frozen to avoid improper use of state during execution.
|
|
20
23
|
# @param field [GraphQL::Schema::Field] The field where this extension was mounted
|
|
21
24
|
# @param options [Object] The second argument to `extension`, or `{}` if nothing was passed.
|
|
22
25
|
def initialize(field:, options:)
|
|
23
26
|
@field = field
|
|
24
27
|
@options = options || {}
|
|
28
|
+
@added_default_arguments = nil
|
|
25
29
|
apply
|
|
26
|
-
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
class << self
|
|
33
|
+
# @return [Array(Array, Hash), nil] A list of default argument configs, or `nil` if there aren't any
|
|
34
|
+
def default_argument_configurations
|
|
35
|
+
args = superclass.respond_to?(:default_argument_configurations) ? superclass.default_argument_configurations : nil
|
|
36
|
+
if @own_default_argument_configurations
|
|
37
|
+
if args
|
|
38
|
+
args.concat(@own_default_argument_configurations)
|
|
39
|
+
else
|
|
40
|
+
args = @own_default_argument_configurations.dup
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
args
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# @see Argument#initialize
|
|
47
|
+
# @see HasArguments#argument
|
|
48
|
+
def default_argument(*argument_args, **argument_kwargs)
|
|
49
|
+
configs = @own_default_argument_configurations ||= []
|
|
50
|
+
configs << [argument_args, argument_kwargs]
|
|
51
|
+
end
|
|
27
52
|
end
|
|
28
53
|
|
|
29
54
|
# Called when this extension is attached to a field.
|
|
@@ -32,6 +57,31 @@ module GraphQL
|
|
|
32
57
|
def apply
|
|
33
58
|
end
|
|
34
59
|
|
|
60
|
+
# Called after the field's definition block has been executed.
|
|
61
|
+
# (Any arguments from the block are present on `field`)
|
|
62
|
+
# @return [void]
|
|
63
|
+
def after_define
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# @api private
|
|
67
|
+
def after_define_apply
|
|
68
|
+
after_define
|
|
69
|
+
if (configs = self.class.default_argument_configurations)
|
|
70
|
+
existing_keywords = field.all_argument_definitions.map(&:keyword)
|
|
71
|
+
existing_keywords.uniq!
|
|
72
|
+
@added_default_arguments = []
|
|
73
|
+
configs.each do |config|
|
|
74
|
+
argument_args, argument_kwargs = config
|
|
75
|
+
arg_name = argument_args[0]
|
|
76
|
+
if !existing_keywords.include?(arg_name)
|
|
77
|
+
@added_default_arguments << arg_name
|
|
78
|
+
field.argument(*argument_args, **argument_kwargs)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
freeze
|
|
83
|
+
end
|
|
84
|
+
|
|
35
85
|
# Called before resolving {#field}. It should either:
|
|
36
86
|
#
|
|
37
87
|
# - `yield` values to continue execution; OR
|
|
@@ -38,7 +38,7 @@ module GraphQL
|
|
|
38
38
|
|
|
39
39
|
find_in_directive(directive, path: path)
|
|
40
40
|
else
|
|
41
|
-
type = schema.get_type(type_or_directive)
|
|
41
|
+
type = schema.get_type(type_or_directive) # rubocop:disable Development/ContextIsPassedCop -- build-time
|
|
42
42
|
|
|
43
43
|
if type.nil?
|
|
44
44
|
raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema."
|
|
@@ -56,7 +56,7 @@ module GraphQL
|
|
|
56
56
|
|
|
57
57
|
def find_in_directive(directive, path:)
|
|
58
58
|
argument_name = path.shift
|
|
59
|
-
argument = directive.
|
|
59
|
+
argument = directive.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
|
|
60
60
|
|
|
61
61
|
if argument.nil?
|
|
62
62
|
raise MemberNotFoundError, "Could not find argument `#{argument_name}` on directive #{directive}."
|
|
@@ -102,7 +102,7 @@ module GraphQL
|
|
|
102
102
|
|
|
103
103
|
def find_in_field(field, path:)
|
|
104
104
|
argument_name = path.shift
|
|
105
|
-
argument = field.
|
|
105
|
+
argument = field.get_argument(argument_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
|
|
106
106
|
|
|
107
107
|
if argument.nil?
|
|
108
108
|
raise MemberNotFoundError, "Could not find argument `#{argument_name}` on field `#{field.name}`."
|
|
@@ -119,7 +119,7 @@ module GraphQL
|
|
|
119
119
|
|
|
120
120
|
def find_in_input_object(input_object, path:)
|
|
121
121
|
field_name = path.shift
|
|
122
|
-
input_field = input_object.
|
|
122
|
+
input_field = input_object.get_argument(field_name) # rubocop:disable Development/ContextIsPassedCop -- build-time
|
|
123
123
|
|
|
124
124
|
if input_field.nil?
|
|
125
125
|
raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`."
|
|
@@ -136,7 +136,7 @@ module GraphQL
|
|
|
136
136
|
|
|
137
137
|
def find_in_enum_type(enum_type, path:)
|
|
138
138
|
value_name = path.shift
|
|
139
|
-
enum_value = enum_type.
|
|
139
|
+
enum_value = enum_type.enum_values.find { |v| v.graphql_name == value_name } # rubocop:disable Development/ContextIsPassedCop -- build-time, not runtime
|
|
140
140
|
|
|
141
141
|
if enum_value.nil?
|
|
142
142
|
raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`."
|
|
@@ -31,7 +31,7 @@ module GraphQL
|
|
|
31
31
|
end
|
|
32
32
|
# Apply prepares, not great to have it duplicated here.
|
|
33
33
|
maybe_lazies = []
|
|
34
|
-
self.class.arguments.each_value do |arg_defn|
|
|
34
|
+
self.class.arguments(context).each_value do |arg_defn|
|
|
35
35
|
ruby_kwargs_key = arg_defn.keyword
|
|
36
36
|
|
|
37
37
|
if @ruby_style_hash.key?(ruby_kwargs_key)
|
|
@@ -129,8 +129,11 @@ module GraphQL
|
|
|
129
129
|
self[#{method_name.inspect}]
|
|
130
130
|
end
|
|
131
131
|
RUBY
|
|
132
|
+
argument_defn
|
|
132
133
|
end
|
|
133
134
|
|
|
135
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
|
136
|
+
|
|
134
137
|
def to_graphql
|
|
135
138
|
type_defn = GraphQL::InputObjectType.new
|
|
136
139
|
type_defn.name = graphql_name
|
|
@@ -138,8 +141,8 @@ module GraphQL
|
|
|
138
141
|
type_defn.metadata[:type_class] = self
|
|
139
142
|
type_defn.mutation = mutation
|
|
140
143
|
type_defn.ast_node = ast_node
|
|
141
|
-
|
|
142
|
-
type_defn.arguments[arg.graphql_definition.name] = arg.graphql_definition
|
|
144
|
+
all_argument_definitions.each do |arg|
|
|
145
|
+
type_defn.arguments[arg.graphql_definition(silence_deprecation_warning: true).name] = arg.graphql_definition(silence_deprecation_warning: true) # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
|
143
146
|
end
|
|
144
147
|
# Make a reference to a classic-style Arguments class
|
|
145
148
|
self.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(type_defn)
|
|
@@ -172,7 +175,7 @@ module GraphQL
|
|
|
172
175
|
end
|
|
173
176
|
|
|
174
177
|
# Inject missing required arguments
|
|
175
|
-
missing_required_inputs = self.arguments.reduce({}) do |m, (argument_name, argument)|
|
|
178
|
+
missing_required_inputs = self.arguments(ctx).reduce({}) do |m, (argument_name, argument)|
|
|
176
179
|
if !input.key?(argument_name) && argument.type.non_null? && warden.get_argument(self, argument_name)
|
|
177
180
|
m[argument_name] = nil
|
|
178
181
|
end
|
|
@@ -223,7 +226,7 @@ module GraphQL
|
|
|
223
226
|
|
|
224
227
|
result = {}
|
|
225
228
|
|
|
226
|
-
arguments.each do |input_key, input_field_defn|
|
|
229
|
+
arguments(ctx).each do |input_key, input_field_defn|
|
|
227
230
|
input_value = value[input_key]
|
|
228
231
|
if value.key?(input_key)
|
|
229
232
|
result[input_key] = if input_value.nil?
|