graphql 1.12.24 → 1.13.19
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/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 -10
- data/lib/generators/graphql/union_generator.rb +5 -5
- 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 +5 -4
- data/lib/graphql/argument.rb +1 -1
- data/lib/graphql/backtrace/table.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/dataloader.rb +55 -22
- 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 -5
- data/lib/graphql/enum_type.rb +7 -3
- 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 +6 -4
- data/lib/graphql/execution/interpreter/runtime.rb +66 -38
- data/lib/graphql/execution/lookahead.rb +2 -2
- data/lib/graphql/execution/multiplex.rb +4 -1
- 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 +5 -3
- 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 +3 -3
- data/lib/graphql/introspection/input_value_type.rb +4 -4
- data/lib/graphql/introspection/schema_type.rb +9 -4
- data/lib/graphql/introspection/type_type.rb +18 -12
- data/lib/graphql/introspection.rb +4 -1
- data/lib/graphql/language/block_string.rb +2 -6
- data/lib/graphql/language/document_from_schema_definition.rb +11 -4
- data/lib/graphql/language/lexer.rb +50 -28
- data/lib/graphql/language/lexer.rl +2 -4
- data/lib/graphql/language/nodes.rb +4 -3
- data/lib/graphql/language/parser.rb +841 -820
- data/lib/graphql/language/parser.y +13 -6
- data/lib/graphql/language/printer.rb +10 -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/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/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/input_validation_result.rb +9 -0
- 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/validation_pipeline.rb +2 -3
- data/lib/graphql/query/variable_validation_error.rb +2 -2
- data/lib/graphql/query/variables.rb +35 -4
- data/lib/graphql/query.rb +0 -1
- data/lib/graphql/relay/connection_type.rb +15 -2
- data/lib/graphql/relay/edges_instrumentation.rb +0 -1
- 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/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/scalar_type.rb +1 -1
- data/lib/graphql/schema/addition.rb +37 -28
- data/lib/graphql/schema/argument.rb +30 -15
- data/lib/graphql/schema/build_from_definition.rb +6 -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 +23 -4
- data/lib/graphql/schema/enum.rb +61 -12
- 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 +261 -83
- data/lib/graphql/schema/field_extension.rb +89 -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 +24 -7
- data/lib/graphql/schema/interface.rb +11 -20
- 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 +15 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
- 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 +56 -14
- 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 +100 -0
- data/lib/graphql/schema/member/validates_input.rb +2 -2
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/non_null.rb +9 -3
- 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 +15 -1
- 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/required_validator.rb +29 -15
- data/lib/graphql/schema/validator.rb +4 -7
- data/lib/graphql/schema/warden.rb +126 -53
- data/lib/graphql/schema.rb +120 -24
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +6 -6
- 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/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 +1 -1
- 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 -3
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +13 -7
- 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 +8 -4
- data/lib/graphql/subscriptions/event.rb +20 -12
- data/lib/graphql/subscriptions/serialize.rb +22 -2
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
- data/lib/graphql/tracing/data_dog_tracing.rb +24 -2
- 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/has_node_field.rb +1 -1
- data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
- 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 +22 -32
- metadata +31 -11
- /data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +0 -0
- /data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +0 -0
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}"
|
60
|
+
end
|
61
|
+
value
|
62
|
+
end
|
63
|
+
|
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
|
85
|
+
end
|
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
|
51
101
|
end
|
52
|
-
|
53
|
-
|
102
|
+
|
103
|
+
all_defns
|
54
104
|
end
|
55
105
|
|
56
|
-
# @return [Hash<String => GraphQL::Schema::
|
57
|
-
def values
|
58
|
-
|
59
|
-
# Local values take precedence over inherited ones
|
60
|
-
inherited_values.merge(own_values)
|
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 }
|
61
109
|
end
|
62
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
|
@@ -89,9 +139,8 @@ module GraphQL
|
|
89
139
|
GraphQL::TypeKinds::ENUM
|
90
140
|
end
|
91
141
|
|
92
|
-
def validate_non_null_input(value_name, ctx)
|
142
|
+
def validate_non_null_input(value_name, ctx, max_errors: nil)
|
93
143
|
result = GraphQL::Query::InputValidationResult.new
|
94
|
-
|
95
144
|
allowed_values = ctx.warden.enum_values(self)
|
96
145
|
matching_value = allowed_values.find { |v| v.graphql_name == value_name }
|
97
146
|
|
@@ -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
|
@@ -20,6 +16,8 @@ module GraphQL
|
|
20
16
|
include GraphQL::Schema::Member::HasDirectives
|
21
17
|
include GraphQL::Schema::Member::HasDeprecationReason
|
22
18
|
|
19
|
+
class FieldImplementationFailed < GraphQL::Error; end
|
20
|
+
|
23
21
|
# @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
|
24
22
|
attr_reader :name
|
25
23
|
alias :graphql_name :name
|
@@ -40,7 +38,9 @@ module GraphQL
|
|
40
38
|
|
41
39
|
# @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type)
|
42
40
|
def owner_type
|
43
|
-
@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
|
44
44
|
owner.payload_type
|
45
45
|
else
|
46
46
|
owner
|
@@ -61,7 +61,7 @@ module GraphQL
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def inspect
|
64
|
-
"#<#{self.class} #{path}#{
|
64
|
+
"#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
|
65
65
|
end
|
66
66
|
|
67
67
|
alias :mutation :resolver
|
@@ -176,6 +176,8 @@ module GraphQL
|
|
176
176
|
|
177
177
|
# @return Boolean
|
178
178
|
attr_reader :relay_node_field
|
179
|
+
# @return Boolean
|
180
|
+
attr_reader :relay_nodes_field
|
179
181
|
|
180
182
|
# @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
|
181
183
|
def method_conflict_warning?
|
@@ -190,6 +192,7 @@ module GraphQL
|
|
190
192
|
# @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
|
191
193
|
# @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
|
192
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
|
193
196
|
# @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
|
194
197
|
# @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
|
195
198
|
# @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
|
@@ -212,7 +215,7 @@ module GraphQL
|
|
212
215
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
213
216
|
# @param validates [Array<Hash>] Configurations for validating this field
|
214
217
|
# @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:
|
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)
|
216
219
|
if name.nil?
|
217
220
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
218
221
|
end
|
@@ -220,15 +223,13 @@ module GraphQL
|
|
220
223
|
if type.nil?
|
221
224
|
raise ArgumentError, "missing second `type` argument or keyword `type:`"
|
222
225
|
end
|
223
|
-
if null.nil?
|
224
|
-
raise ArgumentError, "missing keyword argument null:"
|
225
|
-
end
|
226
226
|
end
|
227
227
|
if (field || function || resolve) && extras.any?
|
228
228
|
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:`"
|
229
229
|
end
|
230
230
|
@original_name = name
|
231
231
|
name_s = -name.to_s
|
232
|
+
|
232
233
|
@underscored_name = -Member::BuildType.underscore(name_s)
|
233
234
|
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
234
235
|
@description = description
|
@@ -241,8 +242,8 @@ module GraphQL
|
|
241
242
|
@resolve = resolve
|
242
243
|
self.deprecation_reason = deprecation_reason
|
243
244
|
|
244
|
-
if method && hash_key
|
245
|
-
raise ArgumentError, "Provide `method:` _or_ `
|
245
|
+
if method && hash_key && dig
|
246
|
+
raise ArgumentError, "Provide `method:`, `hash_key:` _or_ `dig:`, not multiple. (called with: `method: #{method.inspect}, hash_key: #{hash_key.inspect}, dig: #{dig.inspect}`)"
|
246
247
|
end
|
247
248
|
|
248
249
|
if resolver_method
|
@@ -250,13 +251,18 @@ module GraphQL
|
|
250
251
|
raise ArgumentError, "Provide `method:` _or_ `resolver_method:`, not both. (called with: `method: #{method.inspect}, resolver_method: #{resolver_method.inspect}`)"
|
251
252
|
end
|
252
253
|
|
253
|
-
if hash_key
|
254
|
-
raise ArgumentError, "Provide `hash_key
|
254
|
+
if hash_key || dig
|
255
|
+
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}`)"
|
255
256
|
end
|
256
257
|
end
|
257
258
|
|
258
259
|
# TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
|
259
260
|
method_name = method || hash_key || name_s
|
261
|
+
@dig_keys = dig
|
262
|
+
if hash_key
|
263
|
+
@hash_key = hash_key
|
264
|
+
end
|
265
|
+
|
260
266
|
resolver_method ||= name_s.to_sym
|
261
267
|
|
262
268
|
@method_str = -method_name.to_s
|
@@ -281,10 +287,15 @@ module GraphQL
|
|
281
287
|
@legacy_edge_class = legacy_edge_class
|
282
288
|
|
283
289
|
arguments.each do |name, arg|
|
284
|
-
|
290
|
+
case arg
|
291
|
+
when Hash
|
285
292
|
argument(name: name, **arg)
|
286
|
-
|
293
|
+
when GraphQL::Schema::Argument
|
287
294
|
add_argument(arg)
|
295
|
+
when Array
|
296
|
+
arg.each { |a| add_argument(a) }
|
297
|
+
else
|
298
|
+
raise ArgumentError, "Unexpected argument config (#{arg.class}): #{arg.inspect}"
|
288
299
|
end
|
289
300
|
end
|
290
301
|
|
@@ -292,6 +303,7 @@ module GraphQL
|
|
292
303
|
@subscription_scope = subscription_scope
|
293
304
|
|
294
305
|
@extensions = EMPTY_ARRAY
|
306
|
+
@call_after_define = false
|
295
307
|
# This should run before connection extension,
|
296
308
|
# but should it run after the definition block?
|
297
309
|
if scoped?
|
@@ -324,6 +336,9 @@ module GraphQL
|
|
324
336
|
instance_eval(&definition_block)
|
325
337
|
end
|
326
338
|
end
|
339
|
+
|
340
|
+
self.extensions.each(&:after_define_apply)
|
341
|
+
@call_after_define = true
|
327
342
|
end
|
328
343
|
|
329
344
|
# If true, subscription updates with this field can be shared between viewers
|
@@ -356,27 +371,20 @@ module GraphQL
|
|
356
371
|
# @example adding an extension with options
|
357
372
|
# extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
|
358
373
|
#
|
359
|
-
# @param extensions [Array<Class, Hash<Class =>
|
374
|
+
# @param extensions [Array<Class, Hash<Class => Hash>>] Add extensions to this field. For hash elements, only the first key/value is used.
|
360
375
|
# @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
|
361
376
|
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)
|
377
|
+
if new_extensions
|
378
|
+
new_extensions.each do |extension_config|
|
379
|
+
if extension_config.is_a?(Hash)
|
380
|
+
extension_class, options = *extension_config.to_a[0]
|
381
|
+
self.extension(extension_class, options)
|
374
382
|
else
|
375
|
-
|
376
|
-
@extensions << extension_class.new(field: self, options: nil)
|
383
|
+
self.extension(extension_config)
|
377
384
|
end
|
378
385
|
end
|
379
386
|
end
|
387
|
+
@extensions
|
380
388
|
end
|
381
389
|
|
382
390
|
# Add `extension` to this field, initialized with `options` if provided.
|
@@ -387,10 +395,19 @@ module GraphQL
|
|
387
395
|
# @example adding an extension with options
|
388
396
|
# extension(MyExtensionClass, filter: true)
|
389
397
|
#
|
390
|
-
# @param
|
391
|
-
# @param options [
|
392
|
-
|
393
|
-
|
398
|
+
# @param extension_class [Class] subclass of {Schema::FieldExtension}
|
399
|
+
# @param options [Hash] if provided, given as `options:` when initializing `extension`.
|
400
|
+
# @return [void]
|
401
|
+
def extension(extension_class, options = nil)
|
402
|
+
extension_inst = extension_class.new(field: self, options: options)
|
403
|
+
if @extensions.frozen?
|
404
|
+
@extensions = @extensions.dup
|
405
|
+
end
|
406
|
+
if @call_after_define
|
407
|
+
extension_inst.after_define_apply
|
408
|
+
end
|
409
|
+
@extensions << extension_inst
|
410
|
+
nil
|
394
411
|
end
|
395
412
|
|
396
413
|
# Read extras (as symbols) from this field,
|
@@ -411,6 +428,71 @@ module GraphQL
|
|
411
428
|
end
|
412
429
|
end
|
413
430
|
|
431
|
+
def calculate_complexity(query:, nodes:, child_complexity:)
|
432
|
+
if respond_to?(:complexity_for)
|
433
|
+
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
434
|
+
complexity_for(child_complexity: child_complexity, query: query, lookahead: lookahead)
|
435
|
+
elsif connection?
|
436
|
+
arguments = query.arguments_for(nodes.first, self)
|
437
|
+
max_possible_page_size = nil
|
438
|
+
if arguments.respond_to?(:[]) # It might have been an error
|
439
|
+
if arguments[:first]
|
440
|
+
max_possible_page_size = arguments[:first]
|
441
|
+
end
|
442
|
+
|
443
|
+
if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
|
444
|
+
max_possible_page_size = arguments[:last]
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
if max_possible_page_size.nil?
|
449
|
+
max_possible_page_size = max_page_size || query.schema.default_max_page_size
|
450
|
+
end
|
451
|
+
|
452
|
+
if max_possible_page_size.nil?
|
453
|
+
raise GraphQL::Error, "Can't calculate complexity for #{path}, no `first:`, `last:`, `max_page_size` or `default_max_page_size`"
|
454
|
+
else
|
455
|
+
metadata_complexity = 0
|
456
|
+
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
457
|
+
|
458
|
+
if (page_info_lookahead = lookahead.selection(:page_info)).selected?
|
459
|
+
metadata_complexity += 1 # pageInfo
|
460
|
+
metadata_complexity += page_info_lookahead.selections.size # subfields
|
461
|
+
end
|
462
|
+
|
463
|
+
if lookahead.selects?(:total) || lookahead.selects?(:total_count) || lookahead.selects?(:count)
|
464
|
+
metadata_complexity += 1
|
465
|
+
end
|
466
|
+
|
467
|
+
nodes_edges_complexity = 0
|
468
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:edges)
|
469
|
+
nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
|
470
|
+
|
471
|
+
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
472
|
+
items_complexity = child_complexity - metadata_complexity - nodes_edges_complexity
|
473
|
+
# Add 1 for _this_ field
|
474
|
+
1 + (max_possible_page_size * items_complexity) + metadata_complexity + nodes_edges_complexity
|
475
|
+
end
|
476
|
+
else
|
477
|
+
defined_complexity = complexity
|
478
|
+
case defined_complexity
|
479
|
+
when Proc
|
480
|
+
arguments = query.arguments_for(nodes.first, self)
|
481
|
+
if arguments.is_a?(GraphQL::ExecutionError)
|
482
|
+
return child_complexity
|
483
|
+
elsif arguments.respond_to?(:keyword_arguments)
|
484
|
+
arguments = arguments.keyword_arguments
|
485
|
+
end
|
486
|
+
|
487
|
+
defined_complexity.call(query.context, arguments, child_complexity)
|
488
|
+
when Numeric
|
489
|
+
defined_complexity + child_complexity
|
490
|
+
else
|
491
|
+
raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
414
496
|
def complexity(new_complexity = nil)
|
415
497
|
case new_complexity
|
416
498
|
when Proc
|
@@ -439,6 +521,8 @@ module GraphQL
|
|
439
521
|
# @return [Integer, nil] Applied to connections if {#has_max_page_size?}
|
440
522
|
attr_reader :max_page_size
|
441
523
|
|
524
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
525
|
+
|
442
526
|
# @return [GraphQL::Field]
|
443
527
|
def to_graphql
|
444
528
|
field_defn = if @field
|
@@ -493,9 +577,9 @@ module GraphQL
|
|
493
577
|
field_defn.subscription_scope = @subscription_scope
|
494
578
|
field_defn.ast_node = ast_node
|
495
579
|
|
496
|
-
|
497
|
-
arg_graphql = defn.
|
498
|
-
field_defn.arguments[arg_graphql.name] = arg_graphql
|
580
|
+
all_argument_definitions.each do |defn|
|
581
|
+
arg_graphql = defn.deprecated_to_graphql
|
582
|
+
field_defn.arguments[arg_graphql.name] = arg_graphql # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
499
583
|
end
|
500
584
|
|
501
585
|
# Support a passed-in proc, one way or another
|
@@ -556,13 +640,41 @@ module GraphQL
|
|
556
640
|
|
557
641
|
def authorized?(object, args, context)
|
558
642
|
if @resolver_class
|
559
|
-
# The resolver will check itself during `resolve()`
|
643
|
+
# The resolver _instance_ will check itself during `resolve()`
|
560
644
|
@resolver_class.authorized?(object, context)
|
561
645
|
else
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
646
|
+
if (arg_values = context[:current_arguments])
|
647
|
+
# ^^ that's provided by the interpreter at runtime, and includes info about whether the default value was used or not.
|
648
|
+
using_arg_values = true
|
649
|
+
arg_values = arg_values.argument_values
|
650
|
+
else
|
651
|
+
arg_values = args
|
652
|
+
using_arg_values = false
|
653
|
+
end
|
654
|
+
if args.size > 0
|
655
|
+
args = context.warden.arguments(self)
|
656
|
+
args.each do |arg|
|
657
|
+
arg_key = arg.keyword
|
658
|
+
if arg_values.key?(arg_key)
|
659
|
+
arg_value = arg_values[arg_key]
|
660
|
+
if using_arg_values
|
661
|
+
if arg_value.default_used?
|
662
|
+
# pass -- no auth required for default used
|
663
|
+
next
|
664
|
+
else
|
665
|
+
application_arg_value = arg_value.value
|
666
|
+
if application_arg_value.is_a?(GraphQL::Execution::Interpreter::Arguments)
|
667
|
+
application_arg_value.keyword_arguments
|
668
|
+
end
|
669
|
+
end
|
670
|
+
else
|
671
|
+
application_arg_value = arg_value
|
672
|
+
end
|
673
|
+
|
674
|
+
if !arg.authorized?(object, application_arg_value, context)
|
675
|
+
return false
|
676
|
+
end
|
677
|
+
end
|
566
678
|
end
|
567
679
|
end
|
568
680
|
true
|
@@ -659,7 +771,7 @@ module GraphQL
|
|
659
771
|
ruby_kwargs = graphql_args.to_kwargs
|
660
772
|
maybe_lazies = []
|
661
773
|
# Apply any `prepare` methods. Not great code organization, can this go somewhere better?
|
662
|
-
arguments.each do |name, arg_defn|
|
774
|
+
arguments(field_ctx).each do |name, arg_defn|
|
663
775
|
ruby_kwargs_key = arg_defn.keyword
|
664
776
|
|
665
777
|
if ruby_kwargs.key?(ruby_kwargs_key)
|
@@ -704,51 +816,107 @@ module GraphQL
|
|
704
816
|
|
705
817
|
def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
|
706
818
|
with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
#
|
716
|
-
# - A method on the type instance;
|
717
|
-
# - Hash keys, if the wrapped object is a hash;
|
718
|
-
# - A method on the wrapped object;
|
719
|
-
# - Or, raise not implemented.
|
720
|
-
#
|
721
|
-
if obj.respond_to?(@resolver_method)
|
722
|
-
# Call the method with kwargs, if there are any
|
723
|
-
if ruby_kwargs.any?
|
724
|
-
obj.public_send(@resolver_method, **ruby_kwargs)
|
725
|
-
else
|
726
|
-
obj.public_send(@resolver_method)
|
819
|
+
begin
|
820
|
+
method_receiver = nil
|
821
|
+
method_to_call = nil
|
822
|
+
if @resolver_class
|
823
|
+
if obj.is_a?(GraphQL::Schema::Object)
|
824
|
+
obj = obj.object
|
825
|
+
end
|
826
|
+
obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
|
727
827
|
end
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
828
|
+
|
829
|
+
# Find a way to resolve this field, checking:
|
830
|
+
#
|
831
|
+
# - A method on the type instance;
|
832
|
+
# - Hash keys, if the wrapped object is a hash or responds to `#[]`
|
833
|
+
# - A method on the wrapped object;
|
834
|
+
# - Or, raise not implemented.
|
835
|
+
#
|
836
|
+
if obj.respond_to?(@resolver_method)
|
837
|
+
method_to_call = @resolver_method
|
838
|
+
method_receiver = obj
|
839
|
+
# Call the method with kwargs, if there are any
|
840
|
+
if ruby_kwargs.any?
|
841
|
+
obj.public_send(@resolver_method, **ruby_kwargs)
|
842
|
+
else
|
843
|
+
obj.public_send(@resolver_method)
|
844
|
+
end
|
845
|
+
elsif obj.object.is_a?(Hash)
|
846
|
+
inner_object = obj.object
|
847
|
+
if @dig_keys
|
848
|
+
inner_object.dig(*@dig_keys)
|
849
|
+
elsif inner_object.key?(@method_sym)
|
850
|
+
inner_object[@method_sym]
|
851
|
+
else
|
852
|
+
inner_object[@method_str]
|
853
|
+
end
|
854
|
+
elsif defined?(@hash_key) && obj.object.respond_to?(:[])
|
855
|
+
obj.object[@hash_key]
|
856
|
+
elsif obj.object.respond_to?(@method_sym)
|
857
|
+
method_to_call = @method_sym
|
858
|
+
method_receiver = obj.object
|
859
|
+
if ruby_kwargs.any?
|
860
|
+
obj.object.public_send(@method_sym, **ruby_kwargs)
|
861
|
+
else
|
862
|
+
obj.object.public_send(@method_sym)
|
863
|
+
end
|
732
864
|
else
|
733
|
-
|
865
|
+
raise <<-ERR
|
866
|
+
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
867
|
+
|
868
|
+
- `#{obj.class}##{@resolver_method}`, which did not exist
|
869
|
+
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
870
|
+
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
871
|
+
|
872
|
+
To implement this field, define one of the methods above (and check for typos)
|
873
|
+
ERR
|
734
874
|
end
|
735
|
-
|
736
|
-
|
737
|
-
|
875
|
+
rescue ArgumentError
|
876
|
+
assert_satisfactory_implementation(method_receiver, method_to_call, ruby_kwargs)
|
877
|
+
# if the line above doesn't raise, re-raise
|
878
|
+
raise
|
879
|
+
end
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
def assert_satisfactory_implementation(receiver, method_name, ruby_kwargs)
|
884
|
+
method_defn = receiver.method(method_name)
|
885
|
+
unsatisfied_ruby_kwargs = ruby_kwargs.dup
|
886
|
+
unsatisfied_method_params = []
|
887
|
+
encountered_keyrest = false
|
888
|
+
method_defn.parameters.each do |(param_type, param_name)|
|
889
|
+
case param_type
|
890
|
+
when :key
|
891
|
+
unsatisfied_ruby_kwargs.delete(param_name)
|
892
|
+
when :keyreq
|
893
|
+
if unsatisfied_ruby_kwargs.key?(param_name)
|
894
|
+
unsatisfied_ruby_kwargs.delete(param_name)
|
738
895
|
else
|
739
|
-
|
896
|
+
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."
|
740
897
|
end
|
741
|
-
|
742
|
-
|
743
|
-
|
898
|
+
when :keyrest
|
899
|
+
encountered_keyrest = true
|
900
|
+
when :req
|
901
|
+
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."
|
902
|
+
when :opt, :rest
|
903
|
+
# This is fine, although it will never be present
|
904
|
+
end
|
905
|
+
end
|
906
|
+
|
907
|
+
if encountered_keyrest
|
908
|
+
unsatisfied_ruby_kwargs.clear
|
909
|
+
end
|
744
910
|
|
745
|
-
|
746
|
-
|
747
|
-
|
911
|
+
if unsatisfied_ruby_kwargs.any? || unsatisfied_method_params.any?
|
912
|
+
raise FieldImplementationFailed.new, <<-ERR
|
913
|
+
Failed to call #{method_name} on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
|
748
914
|
|
749
|
-
|
750
|
-
|
751
|
-
|
915
|
+
#{ unsatisfied_ruby_kwargs
|
916
|
+
.map { |key, value| "- `#{key}: #{value}` was given by GraphQL but not defined in the Ruby method. Add `#{key}:` to the method parameters." }
|
917
|
+
.concat(unsatisfied_method_params)
|
918
|
+
.join("\n") }
|
919
|
+
ERR
|
752
920
|
end
|
753
921
|
end
|
754
922
|
|
@@ -762,8 +930,12 @@ module GraphQL
|
|
762
930
|
# This is a hack to get the _last_ value for extended obj and args,
|
763
931
|
# in case one of the extensions doesn't `yield`.
|
764
932
|
# (There's another implementation that uses multiple-return, but I'm wary of the perf cost of the extra arrays)
|
765
|
-
extended = { args: args, obj: obj, memos: nil }
|
933
|
+
extended = { args: args, obj: obj, memos: nil, added_extras: nil }
|
766
934
|
value = run_extensions_before_resolve(obj, args, ctx, extended) do |obj, args|
|
935
|
+
if (added_extras = extended[:added_extras])
|
936
|
+
args = args.dup
|
937
|
+
added_extras.each { |e| args.delete(e) }
|
938
|
+
end
|
767
939
|
yield(obj, args)
|
768
940
|
end
|
769
941
|
|
@@ -792,6 +964,12 @@ module GraphQL
|
|
792
964
|
memos = extended[:memos] ||= {}
|
793
965
|
memos[idx] = memo
|
794
966
|
end
|
967
|
+
|
968
|
+
if (extras = extension.added_extras)
|
969
|
+
ae = extended[:added_extras] ||= []
|
970
|
+
ae.concat(extras)
|
971
|
+
end
|
972
|
+
|
795
973
|
extended[:obj] = extended_obj
|
796
974
|
extended[:args] = extended_args
|
797
975
|
run_extensions_before_resolve(extended_obj, extended_args, ctx, extended, idx: idx + 1) { |o, a| yield(o, a) }
|