graphql 1.13.0 → 1.13.24
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 -8
- data/lib/generators/graphql/enum_generator.rb +4 -10
- data/lib/generators/graphql/field_extractor.rb +31 -0
- data/lib/generators/graphql/input_generator.rb +50 -0
- data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
- data/lib/generators/graphql/install_generator.rb +10 -3
- data/lib/generators/graphql/interface_generator.rb +7 -7
- data/lib/generators/graphql/mutation_create_generator.rb +22 -0
- data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
- data/lib/generators/graphql/mutation_generator.rb +5 -30
- data/lib/generators/graphql/mutation_update_generator.rb +22 -0
- data/lib/generators/graphql/object_generator.rb +8 -37
- data/lib/generators/graphql/orm_mutations_base.rb +40 -0
- data/lib/generators/graphql/scalar_generator.rb +4 -2
- data/lib/generators/graphql/templates/enum.erb +5 -1
- data/lib/generators/graphql/templates/input.erb +9 -0
- data/lib/generators/graphql/templates/interface.erb +4 -2
- data/lib/generators/graphql/templates/mutation.erb +1 -1
- data/lib/generators/graphql/templates/mutation_create.erb +20 -0
- data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
- data/lib/generators/graphql/templates/mutation_update.erb +21 -0
- data/lib/generators/graphql/templates/object.erb +4 -2
- data/lib/generators/graphql/templates/scalar.erb +3 -1
- data/lib/generators/graphql/templates/union.erb +4 -2
- data/lib/generators/graphql/type_generator.rb +46 -9
- data/lib/generators/graphql/union_generator.rb +5 -5
- data/lib/graphql/analysis/ast/field_usage.rb +6 -2
- data/lib/graphql/analysis/ast/visitor.rb +2 -1
- data/lib/graphql/argument.rb +1 -1
- data/lib/graphql/base_type.rb +5 -3
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/dataloader/source.rb +2 -2
- data/lib/graphql/date_encoding_error.rb +16 -0
- data/lib/graphql/define/instance_definable.rb +15 -0
- 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 +1 -1
- data/lib/graphql/enum_type.rb +2 -2
- data/lib/graphql/execution/interpreter/arguments_cache.rb +4 -2
- data/lib/graphql/execution/interpreter/runtime.rb +48 -28
- data/lib/graphql/execution/multiplex.rb +3 -0
- data/lib/graphql/field.rb +1 -1
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/input_object_type.rb +1 -1
- data/lib/graphql/int_type.rb +1 -1
- data/lib/graphql/interface_type.rb +1 -1
- data/lib/graphql/introspection/directive_location_enum.rb +2 -2
- data/lib/graphql/introspection/directive_type.rb +4 -2
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +7 -2
- data/lib/graphql/introspection/type_type.rb +14 -8
- data/lib/graphql/introspection.rb +4 -1
- data/lib/graphql/language/block_string.rb +2 -2
- data/lib/graphql/language/document_from_schema_definition.rb +8 -3
- data/lib/graphql/language/lexer.rb +50 -25
- data/lib/graphql/language/lexer.rl +2 -0
- data/lib/graphql/language/nodes.rb +15 -3
- data/lib/graphql/language/parser.rb +829 -816
- data/lib/graphql/language/parser.y +8 -2
- data/lib/graphql/language/printer.rb +4 -0
- data/lib/graphql/object_type.rb +2 -2
- data/lib/graphql/pagination/active_record_relation_connection.rb +43 -6
- data/lib/graphql/pagination/relation_connection.rb +59 -29
- data/lib/graphql/query/context.rb +10 -0
- data/lib/graphql/query/input_validation_result.rb +9 -0
- data/lib/graphql/query/validation_pipeline.rb +2 -3
- data/lib/graphql/query/variable_validation_error.rb +2 -2
- data/lib/graphql/query/variables.rb +30 -3
- data/lib/graphql/query.rb +0 -1
- data/lib/graphql/relay/connection_type.rb +15 -2
- data/lib/graphql/relay/global_id_resolve.rb +1 -2
- data/lib/graphql/relay/mutation.rb +1 -1
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/relay/range_add.rb +4 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +4 -4
- data/lib/graphql/scalar_type.rb +1 -1
- data/lib/graphql/schema/argument.rb +29 -16
- data/lib/graphql/schema/build_from_definition.rb +9 -7
- data/lib/graphql/schema/directive.rb +25 -2
- data/lib/graphql/schema/enum.rb +4 -3
- data/lib/graphql/schema/enum_value.rb +3 -1
- data/lib/graphql/schema/field.rb +196 -92
- data/lib/graphql/schema/field_extension.rb +89 -2
- data/lib/graphql/schema/input_object.rb +27 -9
- data/lib/graphql/schema/interface.rb +8 -2
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/list.rb +21 -4
- data/lib/graphql/schema/loader.rb +3 -0
- data/lib/graphql/schema/member/accepts_definition.rb +7 -2
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/member/has_fields.rb +1 -1
- data/lib/graphql/schema/member/has_interfaces.rb +11 -1
- data/lib/graphql/schema/member/validates_input.rb +2 -2
- data/lib/graphql/schema/non_null.rb +9 -3
- data/lib/graphql/schema/object.rb +3 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +8 -0
- data/lib/graphql/schema/resolver.rb +19 -13
- data/lib/graphql/schema/scalar.rb +15 -1
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/union.rb +2 -0
- data/lib/graphql/schema/validator/required_validator.rb +29 -15
- data/lib/graphql/schema/validator.rb +4 -7
- data/lib/graphql/schema/warden.rb +11 -2
- data/lib/graphql/schema.rb +34 -10
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +14 -7
- data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
- data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +6 -0
- data/lib/graphql/static_validation/validation_context.rb +4 -0
- data/lib/graphql/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +2 -0
- data/lib/graphql/subscriptions/serialize.rb +22 -2
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
- data/lib/graphql/tracing/data_dog_tracing.rb +24 -15
- data/lib/graphql/tracing/notifications_tracing.rb +59 -0
- data/lib/graphql/tracing/platform_tracing.rb +20 -10
- data/lib/graphql/types/iso_8601_date.rb +13 -5
- data/lib/graphql/types/iso_8601_date_time.rb +8 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +28 -10
- 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/node_field.rb +2 -3
- data/lib/graphql/types/relay/nodes_field.rb +19 -3
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/union_type.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +14 -1
- metadata +56 -30
- /data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +0 -0
- /data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +0 -0
@@ -377,6 +377,7 @@ module GraphQL
|
|
377
377
|
Class.new(GraphQL::Schema::Directive) do
|
378
378
|
graphql_name(directive_definition.name)
|
379
379
|
description(directive_definition.description)
|
380
|
+
repeatable(directive_definition.repeatable)
|
380
381
|
locations(*directive_definition.locations.map { |location| location.name.to_sym })
|
381
382
|
ast_node(directive_definition)
|
382
383
|
builder.build_arguments(self, directive_definition.arguments, type_resolver)
|
@@ -426,17 +427,18 @@ module GraphQL
|
|
426
427
|
|
427
428
|
# Don't do this for interfaces
|
428
429
|
if default_resolve
|
429
|
-
owner
|
430
|
-
# frozen_string_literal: true
|
431
|
-
def #{resolve_method_name}(**args)
|
432
|
-
field_instance = self.class.get_field("#{field_definition.name}")
|
433
|
-
context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
|
434
|
-
end
|
435
|
-
RUBY
|
430
|
+
define_field_resolve_method(owner, resolve_method_name, field_definition.name)
|
436
431
|
end
|
437
432
|
end
|
438
433
|
end
|
439
434
|
|
435
|
+
def define_field_resolve_method(owner, method_name, field_name)
|
436
|
+
owner.define_method(method_name) { |**args|
|
437
|
+
field_instance = self.class.get_field(field_name)
|
438
|
+
context.schema.definition_default_resolve.call(self.class, field_instance, object, args, context)
|
439
|
+
}
|
440
|
+
end
|
441
|
+
|
440
442
|
def build_resolve_type(lookup_hash, directives, missing_type_handler)
|
441
443
|
resolve_type_proc = nil
|
442
444
|
resolve_type_proc = ->(ast_node) {
|
@@ -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
|
@@ -20,7 +22,7 @@ module GraphQL
|
|
20
22
|
# but downcase the first letter.
|
21
23
|
def default_graphql_name
|
22
24
|
@default_graphql_name ||= begin
|
23
|
-
camelized_name = super
|
25
|
+
camelized_name = super.dup
|
24
26
|
camelized_name[0] = camelized_name[0].downcase
|
25
27
|
camelized_name
|
26
28
|
end
|
@@ -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
|
@@ -62,7 +66,7 @@ module GraphQL
|
|
62
66
|
defn.ast_node = ast_node
|
63
67
|
defn.metadata[:type_class] = self
|
64
68
|
all_argument_definitions.each do |arg_defn|
|
65
|
-
arg_graphql = arg_defn.to_graphql
|
69
|
+
arg_graphql = arg_defn.to_graphql(silence_deprecation_warning: true)
|
66
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
|
@@ -86,6 +90,11 @@ module GraphQL
|
|
86
90
|
yield
|
87
91
|
end
|
88
92
|
|
93
|
+
# Continuing is passed as a block, yield to continue.
|
94
|
+
def resolve_each(object, arguments, context)
|
95
|
+
yield
|
96
|
+
end
|
97
|
+
|
89
98
|
def on_field?
|
90
99
|
locations.include?(FIELD)
|
91
100
|
end
|
@@ -97,6 +106,14 @@ module GraphQL
|
|
97
106
|
def on_operation?
|
98
107
|
locations.include?(QUERY) && locations.include?(MUTATION) && locations.include?(SUBSCRIPTION)
|
99
108
|
end
|
109
|
+
|
110
|
+
def repeatable?
|
111
|
+
!!@repeatable
|
112
|
+
end
|
113
|
+
|
114
|
+
def repeatable(new_value)
|
115
|
+
@repeatable = new_value
|
116
|
+
end
|
100
117
|
end
|
101
118
|
|
102
119
|
# @return [GraphQL::Schema::Field, GraphQL::Schema::Argument, Class, Module]
|
@@ -116,6 +133,10 @@ module GraphQL
|
|
116
133
|
@arguments = self.class.coerce_arguments(nil, arguments, Query::NullContext)
|
117
134
|
end
|
118
135
|
|
136
|
+
def graphql_name
|
137
|
+
self.class.graphql_name
|
138
|
+
end
|
139
|
+
|
119
140
|
LOCATIONS = [
|
120
141
|
QUERY = :QUERY,
|
121
142
|
MUTATION = :MUTATION,
|
@@ -135,6 +156,7 @@ module GraphQL
|
|
135
156
|
ENUM_VALUE = :ENUM_VALUE,
|
136
157
|
INPUT_OBJECT = :INPUT_OBJECT,
|
137
158
|
INPUT_FIELD_DEFINITION = :INPUT_FIELD_DEFINITION,
|
159
|
+
VARIABLE_DEFINITION = :VARIABLE_DEFINITION,
|
138
160
|
]
|
139
161
|
|
140
162
|
DEFAULT_DEPRECATION_REASON = 'No longer supported'
|
@@ -157,6 +179,7 @@ module GraphQL
|
|
157
179
|
ENUM_VALUE: 'Location adjacent to an enum value definition.',
|
158
180
|
INPUT_OBJECT: 'Location adjacent to an input object type definition.',
|
159
181
|
INPUT_FIELD_DEFINITION: 'Location adjacent to an input object field definition.',
|
182
|
+
VARIABLE_DEFINITION: 'Location adjacent to a variable definition.',
|
160
183
|
}
|
161
184
|
|
162
185
|
private
|
data/lib/graphql/schema/enum.rb
CHANGED
@@ -108,6 +108,8 @@ module GraphQL
|
|
108
108
|
enum_values(context).each_with_object({}) { |val, obj| obj[val.graphql_name] = val }
|
109
109
|
end
|
110
110
|
|
111
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
112
|
+
|
111
113
|
# @return [GraphQL::EnumType]
|
112
114
|
def to_graphql
|
113
115
|
enum_type = GraphQL::EnumType.new
|
@@ -116,7 +118,7 @@ module GraphQL
|
|
116
118
|
enum_type.introspection = introspection
|
117
119
|
enum_type.ast_node = ast_node
|
118
120
|
values.each do |name, val|
|
119
|
-
enum_type.add_value(val.
|
121
|
+
enum_type.add_value(val.deprecated_to_graphql)
|
120
122
|
end
|
121
123
|
enum_type.metadata[:type_class] = self
|
122
124
|
enum_type
|
@@ -137,9 +139,8 @@ module GraphQL
|
|
137
139
|
GraphQL::TypeKinds::ENUM
|
138
140
|
end
|
139
141
|
|
140
|
-
def validate_non_null_input(value_name, ctx)
|
142
|
+
def validate_non_null_input(value_name, ctx, max_errors: nil)
|
141
143
|
result = GraphQL::Query::InputValidationResult.new
|
142
|
-
|
143
144
|
allowed_values = ctx.warden.enum_values(self)
|
144
145
|
matching_value = allowed_values.find { |v| v.graphql_name == value_name }
|
145
146
|
|
@@ -55,7 +55,7 @@ module GraphQL
|
|
55
55
|
end
|
56
56
|
|
57
57
|
if block_given?
|
58
|
-
|
58
|
+
instance_exec(self, &block)
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
@@ -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
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -16,6 +16,8 @@ module GraphQL
|
|
16
16
|
include GraphQL::Schema::Member::HasDirectives
|
17
17
|
include GraphQL::Schema::Member::HasDeprecationReason
|
18
18
|
|
19
|
+
class FieldImplementationFailed < GraphQL::Error; end
|
20
|
+
|
19
21
|
# @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
|
20
22
|
attr_reader :name
|
21
23
|
alias :graphql_name :name
|
@@ -36,7 +38,9 @@ module GraphQL
|
|
36
38
|
|
37
39
|
# @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type)
|
38
40
|
def owner_type
|
39
|
-
@owner_type ||= if owner
|
41
|
+
@owner_type ||= if owner.nil?
|
42
|
+
raise GraphQL::InvariantError, "Field #{original_name.inspect} (graphql name: #{graphql_name.inspect}) has no owner, but all fields should have an owner. How did this happen?!"
|
43
|
+
elsif owner < GraphQL::Schema::Mutation
|
40
44
|
owner.payload_type
|
41
45
|
else
|
42
46
|
owner
|
@@ -172,6 +176,8 @@ module GraphQL
|
|
172
176
|
|
173
177
|
# @return Boolean
|
174
178
|
attr_reader :relay_node_field
|
179
|
+
# @return Boolean
|
180
|
+
attr_reader :relay_nodes_field
|
175
181
|
|
176
182
|
# @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
|
177
183
|
def method_conflict_warning?
|
@@ -186,6 +192,7 @@ module GraphQL
|
|
186
192
|
# @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
|
187
193
|
# @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
|
188
194
|
# @param hash_key [String, Symbol] The hash key to lookup on the underlying object (if its a Hash) to resolve this field (defaults to `name` or `name.to_s`)
|
195
|
+
# @param dig [Array<String, Symbol>] The nested hash keys to lookup on the underlying hash to resolve this field using dig
|
189
196
|
# @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
|
190
197
|
# @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
|
191
198
|
# @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
|
@@ -208,7 +215,7 @@ module GraphQL
|
|
208
215
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
209
216
|
# @param validates [Array<Hash>] Configurations for validating this field
|
210
217
|
# @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
|
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)
|
218
|
+
def initialize(type: nil, name: nil, owner: nil, null: true, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, dig: 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)
|
212
219
|
if name.nil?
|
213
220
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
214
221
|
end
|
@@ -222,8 +229,10 @@ module GraphQL
|
|
222
229
|
end
|
223
230
|
@original_name = name
|
224
231
|
name_s = -name.to_s
|
232
|
+
|
225
233
|
@underscored_name = -Member::BuildType.underscore(name_s)
|
226
234
|
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
235
|
+
NameValidator.validate!(@name)
|
227
236
|
@description = description
|
228
237
|
if field.is_a?(GraphQL::Schema::Field)
|
229
238
|
raise ArgumentError, "Instead of passing a field as `field:`, use `add_field(field)` to add an already-defined field."
|
@@ -234,8 +243,8 @@ module GraphQL
|
|
234
243
|
@resolve = resolve
|
235
244
|
self.deprecation_reason = deprecation_reason
|
236
245
|
|
237
|
-
if method && hash_key
|
238
|
-
raise ArgumentError, "Provide `method:` _or_ `
|
246
|
+
if method && hash_key && dig
|
247
|
+
raise ArgumentError, "Provide `method:`, `hash_key:` _or_ `dig:`, not multiple. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}, dig: #{dig.inspect}`)"
|
239
248
|
end
|
240
249
|
|
241
250
|
if resolver_method
|
@@ -243,13 +252,18 @@ module GraphQL
|
|
243
252
|
raise ArgumentError, "Provide `method:` _or_ `resolver_method:`, not both. (called with: `method: #{method.inspect}, resolver_method: #{resolver_method.inspect}`)"
|
244
253
|
end
|
245
254
|
|
246
|
-
if hash_key
|
247
|
-
raise ArgumentError, "Provide `hash_key
|
255
|
+
if hash_key || dig
|
256
|
+
raise ArgumentError, "Provide `hash_key:`, `dig:`, _or_ `resolver_method:`, not multiple. (called with: `hash_key: #{hash_key.inspect}, dig: #{dig.inspect}, resolver_method: #{resolver_method.inspect}`)"
|
248
257
|
end
|
249
258
|
end
|
250
259
|
|
251
260
|
# TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
|
252
261
|
method_name = method || hash_key || name_s
|
262
|
+
@dig_keys = dig
|
263
|
+
if hash_key
|
264
|
+
@hash_key = hash_key
|
265
|
+
end
|
266
|
+
|
253
267
|
resolver_method ||= name_s.to_sym
|
254
268
|
|
255
269
|
@method_str = -method_name.to_s
|
@@ -290,6 +304,7 @@ module GraphQL
|
|
290
304
|
@subscription_scope = subscription_scope
|
291
305
|
|
292
306
|
@extensions = EMPTY_ARRAY
|
307
|
+
@call_after_define = false
|
293
308
|
# This should run before connection extension,
|
294
309
|
# but should it run after the definition block?
|
295
310
|
if scoped?
|
@@ -322,6 +337,9 @@ module GraphQL
|
|
322
337
|
instance_eval(&definition_block)
|
323
338
|
end
|
324
339
|
end
|
340
|
+
|
341
|
+
self.extensions.each(&:after_define_apply)
|
342
|
+
@call_after_define = true
|
325
343
|
end
|
326
344
|
|
327
345
|
# If true, subscription updates with this field can be shared between viewers
|
@@ -354,27 +372,20 @@ module GraphQL
|
|
354
372
|
# @example adding an extension with options
|
355
373
|
# extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
|
356
374
|
#
|
357
|
-
# @param extensions [Array<Class, Hash<Class =>
|
375
|
+
# @param extensions [Array<Class, Hash<Class => Hash>>] Add extensions to this field. For hash elements, only the first key/value is used.
|
358
376
|
# @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
|
359
377
|
def extensions(new_extensions = nil)
|
360
|
-
if new_extensions
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
@extensions = @extensions.dup
|
366
|
-
end
|
367
|
-
new_extensions.each do |extension|
|
368
|
-
if extension.is_a?(Hash)
|
369
|
-
extension = extension.to_a[0]
|
370
|
-
extension_class, options = *extension
|
371
|
-
@extensions << extension_class.new(field: self, options: options)
|
378
|
+
if new_extensions
|
379
|
+
new_extensions.each do |extension_config|
|
380
|
+
if extension_config.is_a?(Hash)
|
381
|
+
extension_class, options = *extension_config.to_a[0]
|
382
|
+
self.extension(extension_class, options)
|
372
383
|
else
|
373
|
-
|
374
|
-
@extensions << extension_class.new(field: self, options: nil)
|
384
|
+
self.extension(extension_config)
|
375
385
|
end
|
376
386
|
end
|
377
387
|
end
|
388
|
+
@extensions
|
378
389
|
end
|
379
390
|
|
380
391
|
# Add `extension` to this field, initialized with `options` if provided.
|
@@ -385,10 +396,19 @@ module GraphQL
|
|
385
396
|
# @example adding an extension with options
|
386
397
|
# extension(MyExtensionClass, filter: true)
|
387
398
|
#
|
388
|
-
# @param
|
389
|
-
# @param options [
|
390
|
-
|
391
|
-
|
399
|
+
# @param extension_class [Class] subclass of {Schema::FieldExtension}
|
400
|
+
# @param options [Hash] if provided, given as `options:` when initializing `extension`.
|
401
|
+
# @return [void]
|
402
|
+
def extension(extension_class, options = nil)
|
403
|
+
extension_inst = extension_class.new(field: self, options: options)
|
404
|
+
if @extensions.frozen?
|
405
|
+
@extensions = @extensions.dup
|
406
|
+
end
|
407
|
+
if @call_after_define
|
408
|
+
extension_inst.after_define_apply
|
409
|
+
end
|
410
|
+
@extensions << extension_inst
|
411
|
+
nil
|
392
412
|
end
|
393
413
|
|
394
414
|
# Read extras (as symbols) from this field,
|
@@ -416,11 +436,14 @@ module GraphQL
|
|
416
436
|
elsif connection?
|
417
437
|
arguments = query.arguments_for(nodes.first, self)
|
418
438
|
max_possible_page_size = nil
|
419
|
-
if arguments[
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
439
|
+
if arguments.respond_to?(:[]) # It might have been an error
|
440
|
+
if arguments[:first]
|
441
|
+
max_possible_page_size = arguments[:first]
|
442
|
+
end
|
443
|
+
|
444
|
+
if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
|
445
|
+
max_possible_page_size = arguments[:last]
|
446
|
+
end
|
424
447
|
end
|
425
448
|
|
426
449
|
if max_possible_page_size.nil?
|
@@ -441,17 +464,28 @@ module GraphQL
|
|
441
464
|
if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
|
442
465
|
metadata_complexity += 1
|
443
466
|
end
|
467
|
+
|
468
|
+
nodes_edges_complexity = 0
|
469
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:edges)
|
470
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
|
471
|
+
|
444
472
|
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
445
|
-
items_complexity = child_complexity - metadata_complexity
|
473
|
+
items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
|
446
474
|
# Add 1 for _this_ field
|
447
|
-
1 + (max_possible_page_size * items_complexity) + metadata_complexity
|
475
|
+
1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
|
448
476
|
end
|
449
477
|
else
|
450
478
|
defined_complexity = complexity
|
451
479
|
case defined_complexity
|
452
480
|
when Proc
|
453
481
|
arguments = query.arguments_for(nodes.first, self)
|
454
|
-
|
482
|
+
if arguments.is_a?(GraphQL::ExecutionError)
|
483
|
+
return child_complexity
|
484
|
+
elsif arguments.respond_to?(:keyword_arguments)
|
485
|
+
arguments = arguments.keyword_arguments
|
486
|
+
end
|
487
|
+
|
488
|
+
defined_complexity.call(query.context, arguments, child_complexity)
|
455
489
|
when Numeric
|
456
490
|
defined_complexity + child_complexity
|
457
491
|
else
|
@@ -488,6 +522,8 @@ module GraphQL
|
|
488
522
|
# @return [Integer, nil] Applied to connections if {#has_max_page_size?}
|
489
523
|
attr_reader :max_page_size
|
490
524
|
|
525
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
526
|
+
|
491
527
|
# @return [GraphQL::Field]
|
492
528
|
def to_graphql
|
493
529
|
field_defn = if @field
|
@@ -543,7 +579,7 @@ module GraphQL
|
|
543
579
|
field_defn.ast_node = ast_node
|
544
580
|
|
545
581
|
all_argument_definitions.each do |defn|
|
546
|
-
arg_graphql = defn.
|
582
|
+
arg_graphql = defn.deprecated_to_graphql
|
547
583
|
field_defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
548
584
|
end
|
549
585
|
|
@@ -605,7 +641,7 @@ module GraphQL
|
|
605
641
|
|
606
642
|
def authorized?(object, args, context)
|
607
643
|
if @resolver_class
|
608
|
-
# The resolver will check itself during `resolve()`
|
644
|
+
# The resolver _instance_ will check itself during `resolve()`
|
609
645
|
@resolver_class.authorized?(object, context)
|
610
646
|
else
|
611
647
|
if (arg_values = context[:current_arguments])
|
@@ -616,27 +652,29 @@ module GraphQL
|
|
616
652
|
arg_values = args
|
617
653
|
using_arg_values = false
|
618
654
|
end
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
if
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
application_arg_value.
|
655
|
+
if args.size > 0
|
656
|
+
args = context.warden.arguments(self)
|
657
|
+
args.each do |arg|
|
658
|
+
arg_key = arg.keyword
|
659
|
+
if arg_values.key?(arg_key)
|
660
|
+
arg_value = arg_values[arg_key]
|
661
|
+
if using_arg_values
|
662
|
+
if arg_value.default_used?
|
663
|
+
# pass -- no auth required for default used
|
664
|
+
next
|
665
|
+
else
|
666
|
+
application_arg_value = arg_value.value
|
667
|
+
if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
|
668
|
+
application_arg_value.keyword_arguments
|
669
|
+
end
|
632
670
|
end
|
671
|
+
else
|
672
|
+
application_arg_value = arg_value
|
633
673
|
end
|
634
|
-
else
|
635
|
-
application_arg_value = arg_value
|
636
|
-
end
|
637
674
|
|
638
|
-
|
639
|
-
|
675
|
+
if !arg.authorized?(object, application_arg_value, context)
|
676
|
+
return false
|
677
|
+
end
|
640
678
|
end
|
641
679
|
end
|
642
680
|
end
|
@@ -779,51 +817,107 @@ module GraphQL
|
|
779
817
|
|
780
818
|
def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
|
781
819
|
with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
#
|
791
|
-
# - A method on the type instance;
|
792
|
-
# - Hash keys, if the wrapped object is a hash;
|
793
|
-
# - A method on the wrapped object;
|
794
|
-
# - Or, raise not implemented.
|
795
|
-
#
|
796
|
-
if obj.respond_to?(@resolver_method)
|
797
|
-
# Call the method with kwargs, if there are any
|
798
|
-
if ruby_kwargs.any?
|
799
|
-
obj.public_send(@resolver_method, **ruby_kwargs)
|
800
|
-
else
|
801
|
-
obj.public_send(@resolver_method)
|
820
|
+
begin
|
821
|
+
method_receiver = nil
|
822
|
+
method_to_call = nil
|
823
|
+
if @resolver_class
|
824
|
+
if obj.is_a?(GraphQL::Schema::Object)
|
825
|
+
obj = obj.object
|
826
|
+
end
|
827
|
+
obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
|
802
828
|
end
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
829
|
+
|
830
|
+
# Find a way to resolve this field, checking:
|
831
|
+
#
|
832
|
+
# - A method on the type instance;
|
833
|
+
# - Hash keys, if the wrapped object is a hash or responds to `#[]`
|
834
|
+
# - A method on the wrapped object;
|
835
|
+
# - Or, raise not implemented.
|
836
|
+
#
|
837
|
+
if obj.respond_to?(@resolver_method)
|
838
|
+
method_to_call = @resolver_method
|
839
|
+
method_receiver = obj
|
840
|
+
# Call the method with kwargs, if there are any
|
841
|
+
if ruby_kwargs.any?
|
842
|
+
obj.public_send(@resolver_method, **ruby_kwargs)
|
843
|
+
else
|
844
|
+
obj.public_send(@resolver_method)
|
845
|
+
end
|
846
|
+
elsif obj.object.is_a?(Hash)
|
847
|
+
inner_object = obj.object
|
848
|
+
if @dig_keys
|
849
|
+
inner_object.dig(*@dig_keys)
|
850
|
+
elsif inner_object.key?(@method_sym)
|
851
|
+
inner_object[@method_sym]
|
852
|
+
else
|
853
|
+
inner_object[@method_str]
|
854
|
+
end
|
855
|
+
elsif defined?(@hash_key) && obj.object.respond_to?(:[])
|
856
|
+
obj.object[@hash_key]
|
857
|
+
elsif obj.object.respond_to?(@method_sym)
|
858
|
+
method_to_call = @method_sym
|
859
|
+
method_receiver = obj.object
|
860
|
+
if ruby_kwargs.any?
|
861
|
+
obj.object.public_send(@method_sym, **ruby_kwargs)
|
862
|
+
else
|
863
|
+
obj.object.public_send(@method_sym)
|
864
|
+
end
|
807
865
|
else
|
808
|
-
|
866
|
+
raise <<-ERR
|
867
|
+
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
868
|
+
|
869
|
+
- `#{obj.class}##{@resolver_method}`, which did not exist
|
870
|
+
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
871
|
+
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
872
|
+
|
873
|
+
To implement this field, define one of the methods above (and check for typos)
|
874
|
+
ERR
|
809
875
|
end
|
810
|
-
|
811
|
-
|
812
|
-
|
876
|
+
rescue ArgumentError
|
877
|
+
assert_satisfactory_implementation(method_receiver, method_to_call, ruby_kwargs)
|
878
|
+
# if the line above doesn't raise, re-raise
|
879
|
+
raise
|
880
|
+
end
|
881
|
+
end
|
882
|
+
end
|
883
|
+
|
884
|
+
def assert_satisfactory_implementation(receiver, method_name, ruby_kwargs)
|
885
|
+
method_defn = receiver.method(method_name)
|
886
|
+
unsatisfied_ruby_kwargs = ruby_kwargs.dup
|
887
|
+
unsatisfied_method_params = []
|
888
|
+
encountered_keyrest = false
|
889
|
+
method_defn.parameters.each do |(param_type, param_name)|
|
890
|
+
case param_type
|
891
|
+
when :key
|
892
|
+
unsatisfied_ruby_kwargs.delete(param_name)
|
893
|
+
when :keyreq
|
894
|
+
if unsatisfied_ruby_kwargs.key?(param_name)
|
895
|
+
unsatisfied_ruby_kwargs.delete(param_name)
|
813
896
|
else
|
814
|
-
|
897
|
+
unsatisfied_method_params << "- `#{param_name}:` is required by Ruby, but not by GraphQL. Consider `#{param_name}: nil` instead, or making this argument required in GraphQL."
|
815
898
|
end
|
816
|
-
|
817
|
-
|
818
|
-
|
899
|
+
when :keyrest
|
900
|
+
encountered_keyrest = true
|
901
|
+
when :req
|
902
|
+
unsatisfied_method_params << "- `#{param_name}` is required by Ruby, but GraphQL doesn't pass positional arguments. If it's meant to be a GraphQL argument, use `#{param_name}:` instead. Otherwise, remove it."
|
903
|
+
when :opt, :rest
|
904
|
+
# This is fine, although it will never be present
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
if encountered_keyrest
|
909
|
+
unsatisfied_ruby_kwargs.clear
|
910
|
+
end
|
819
911
|
|
820
|
-
|
821
|
-
|
822
|
-
|
912
|
+
if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
|
913
|
+
raise FieldImplementationFailed.new, <<-ERR
|
914
|
+
Failed to call #{method_name} on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
|
823
915
|
|
824
|
-
|
825
|
-
|
826
|
-
|
916
|
+
#{ unsatisfied_ruby_kwargs
|
917
|
+
.map { |key, value| "- `#{key}: #{value}` was given by GraphQL but not defined in the Ruby method. Add `#{key}:` to the method parameters." }
|
918
|
+
.concat(unsatisfied_method_params)
|
919
|
+
.join("\n") }
|
920
|
+
ERR
|
827
921
|
end
|
828
922
|
end
|
829
923
|
|
@@ -837,8 +931,12 @@ module GraphQL
|
|
837
931
|
# This is a hack to get the _last_ value for extended obj and args,
|
838
932
|
# in case one of the extensions doesn't `yield`.
|
839
933
|
# (There's another implementation that uses multiple-return, but I'm wary of the perf cost of the extra arrays)
|
840
|
-
extended = { args: args, obj: obj, memos: nil }
|
934
|
+
extended = { args: args, obj: obj, memos: nil, added_extras: nil }
|
841
935
|
value = run_extensions_before_resolve(obj, args, ctx, extended) do |obj, args|
|
936
|
+
if (added_extras = extended[:added_extras])
|
937
|
+
args = args.dup
|
938
|
+
added_extras.each { |e| args.delete(e) }
|
939
|
+
end
|
842
940
|
yield(obj, args)
|
843
941
|
end
|
844
942
|
|
@@ -867,6 +965,12 @@ module GraphQL
|
|
867
965
|
memos = extended[:memos] ||= {}
|
868
966
|
memos[idx] = memo
|
869
967
|
end
|
968
|
+
|
969
|
+
if (extras = extension.added_extras)
|
970
|
+
ae = extended[:added_extras] ||= []
|
971
|
+
ae.concat(extras)
|
972
|
+
end
|
973
|
+
|
870
974
|
extended[:obj] = extended_obj
|
871
975
|
extended[:args] = extended_args
|
872
976
|
run_extensions_before_resolve(extended_obj, extended_args, ctx, extended, idx: idx + 1) { |o, a| yield(o, a) }
|