graphql 1.11.6 → 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 +17 -7
- data/lib/generators/graphql/interface_generator.rb +7 -7
- data/lib/generators/graphql/loader_generator.rb +1 -0
- 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 +6 -30
- data/lib/generators/graphql/mutation_update_generator.rb +22 -0
- data/lib/generators/graphql/object_generator.rb +12 -38
- data/lib/generators/graphql/orm_mutations_base.rb +40 -0
- data/lib/generators/graphql/relay.rb +63 -0
- data/lib/generators/graphql/relay_generator.rb +21 -0
- data/lib/generators/graphql/scalar_generator.rb +4 -2
- data/lib/generators/graphql/templates/base_connection.erb +8 -0
- data/lib/generators/graphql/templates/base_edge.erb +8 -0
- data/lib/generators/graphql/templates/enum.erb +5 -1
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -2
- 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/node_type.erb +9 -0
- data/lib/generators/graphql/templates/object.erb +5 -3
- data/lib/generators/graphql/templates/query_type.erb +1 -3
- data/lib/generators/graphql/templates/scalar.erb +3 -1
- data/lib/generators/graphql/templates/schema.erb +19 -34
- data/lib/generators/graphql/templates/union.erb +4 -2
- data/lib/generators/graphql/type_generator.rb +47 -10
- data/lib/generators/graphql/union_generator.rb +5 -5
- data/lib/graphql/analysis/analyze_query.rb +7 -0
- data/lib/graphql/analysis/ast/field_usage.rb +28 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
- data/lib/graphql/analysis/ast/visitor.rb +14 -5
- data/lib/graphql/analysis/ast.rb +11 -2
- data/lib/graphql/argument.rb +1 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -1
- data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
- data/lib/graphql/backtrace/table.rb +34 -3
- data/lib/graphql/backtrace/traced_error.rb +0 -1
- data/lib/graphql/backtrace/tracer.rb +40 -10
- data/lib/graphql/backtrace.rb +28 -19
- data/lib/graphql/backwards_compatibility.rb +2 -1
- data/lib/graphql/base_type.rb +6 -4
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/compatibility/execution_specification.rb +1 -0
- data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
- data/lib/graphql/dataloader/null_dataloader.rb +22 -0
- data/lib/graphql/dataloader/request.rb +19 -0
- data/lib/graphql/dataloader/request_all.rb +19 -0
- data/lib/graphql/dataloader/source.rb +155 -0
- data/lib/graphql/dataloader.rb +308 -0
- data/lib/graphql/date_encoding_error.rb +16 -0
- data/lib/graphql/define/assign_global_id_field.rb +1 -1
- data/lib/graphql/define/instance_definable.rb +48 -3
- data/lib/graphql/define/type_definer.rb +5 -5
- data/lib/graphql/deprecated_dsl.rb +18 -5
- data/lib/graphql/deprecation.rb +9 -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 +9 -3
- data/lib/graphql/execution/errors.rb +110 -7
- data/lib/graphql/execution/execute.rb +8 -1
- data/lib/graphql/execution/interpreter/arguments.rb +57 -5
- data/lib/graphql/execution/interpreter/arguments_cache.rb +49 -15
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +0 -7
- data/lib/graphql/execution/interpreter/resolve.rb +37 -25
- data/lib/graphql/execution/interpreter/runtime.rb +670 -294
- data/lib/graphql/execution/interpreter.rb +16 -16
- data/lib/graphql/execution/lazy.rb +5 -1
- data/lib/graphql/execution/lookahead.rb +2 -2
- data/lib/graphql/execution/multiplex.rb +39 -23
- data/lib/graphql/field.rb +1 -1
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/function.rb +4 -0
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/input_object_type.rb +3 -1
- data/lib/graphql/int_type.rb +1 -1
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/integer_encoding_error.rb +18 -2
- data/lib/graphql/interface_type.rb +4 -2
- data/lib/graphql/internal_representation/document.rb +2 -2
- data/lib/graphql/internal_representation/rewrite.rb +1 -1
- data/lib/graphql/introspection/directive_location_enum.rb +2 -2
- data/lib/graphql/introspection/directive_type.rb +11 -5
- 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 +10 -4
- data/lib/graphql/introspection/schema_type.rb +10 -5
- data/lib/graphql/introspection/type_type.rb +18 -12
- data/lib/graphql/introspection.rb +5 -2
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/block_string.rb +2 -6
- data/lib/graphql/language/cache.rb +37 -0
- data/lib/graphql/language/document_from_schema_definition.rb +60 -26
- data/lib/graphql/language/lexer.rb +50 -28
- data/lib/graphql/language/lexer.rl +2 -4
- data/lib/graphql/language/nodes.rb +14 -4
- data/lib/graphql/language/parser.rb +856 -825
- data/lib/graphql/language/parser.y +28 -11
- 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/language.rb +1 -0
- data/lib/graphql/name_validator.rb +0 -4
- data/lib/graphql/object_type.rb +4 -4
- data/lib/graphql/pagination/active_record_relation_connection.rb +47 -3
- data/lib/graphql/pagination/connection.rb +19 -1
- data/lib/graphql/pagination/connections.rb +45 -30
- data/lib/graphql/pagination/relation_connection.rb +69 -28
- data/lib/graphql/parse_error.rb +0 -1
- data/lib/graphql/query/arguments.rb +2 -2
- data/lib/graphql/query/arguments_cache.rb +1 -2
- data/lib/graphql/query/context.rb +22 -4
- data/lib/graphql/query/executor.rb +0 -1
- 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 +21 -9
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/serial_execution.rb +1 -0
- data/lib/graphql/query/validation_pipeline.rb +3 -4
- data/lib/graphql/query/variable_validation_error.rb +3 -3
- data/lib/graphql/query/variables.rb +35 -4
- data/lib/graphql/query.rb +20 -8
- data/lib/graphql/railtie.rb +9 -1
- data/lib/graphql/rake_task.rb +3 -0
- data/lib/graphql/relay/array_connection.rb +2 -2
- data/lib/graphql/relay/base_connection.rb +7 -0
- data/lib/graphql/relay/connection_instrumentation.rb +4 -4
- data/lib/graphql/relay/connection_type.rb +16 -3
- 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 +2 -1
- data/lib/graphql/relay/node.rb +3 -0
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/relay/range_add.rb +14 -5
- data/lib/graphql/relay/type_extensions.rb +2 -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 +3 -1
- data/lib/graphql/schema/addition.rb +247 -0
- data/lib/graphql/schema/argument.rb +177 -21
- data/lib/graphql/schema/build_from_definition.rb +150 -55
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +57 -0
- 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 +14 -2
- data/lib/graphql/schema/directive.rb +103 -4
- data/lib/graphql/schema/enum.rb +72 -11
- data/lib/graphql/schema/enum_value.rb +18 -6
- data/lib/graphql/schema/field/connection_extension.rb +4 -2
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +332 -111
- data/lib/graphql/schema/field_extension.rb +89 -2
- data/lib/graphql/schema/find_inherited_value.rb +4 -1
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +79 -55
- data/lib/graphql/schema/interface.rb +12 -20
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/list.rb +21 -4
- data/lib/graphql/schema/loader.rb +11 -0
- data/lib/graphql/schema/member/accepts_definition.rb +15 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +5 -16
- data/lib/graphql/schema/member/build_type.rb +4 -7
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/member/has_arguments.rb +166 -74
- data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
- data/lib/graphql/schema/member/has_directives.rb +98 -0
- data/lib/graphql/schema/member/has_fields.rb +77 -22
- data/lib/graphql/schema/member/has_interfaces.rb +100 -0
- data/lib/graphql/schema/member/has_validators.rb +31 -0
- data/lib/graphql/schema/member/instrumentation.rb +0 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member/validates_input.rb +2 -2
- data/lib/graphql/schema/member.rb +5 -0
- data/lib/graphql/schema/middleware_chain.rb +1 -1
- data/lib/graphql/schema/non_null.rb +9 -3
- data/lib/graphql/schema/object.rb +40 -80
- data/lib/graphql/schema/printer.rb +16 -20
- data/lib/graphql/schema/relay_classic_mutation.rb +38 -4
- data/lib/graphql/schema/resolver/has_payload_type.rb +29 -2
- data/lib/graphql/schema/resolver.rb +110 -64
- data/lib/graphql/schema/scalar.rb +18 -2
- data/lib/graphql/schema/subscription.rb +55 -9
- data/lib/graphql/schema/timeout_middleware.rb +3 -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/validation.rb +4 -2
- data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
- data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
- data/lib/graphql/schema/validator/format_validator.rb +48 -0
- data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
- data/lib/graphql/schema/validator/length_validator.rb +59 -0
- data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
- data/lib/graphql/schema/validator/required_validator.rb +82 -0
- data/lib/graphql/schema/validator.rb +171 -0
- data/lib/graphql/schema/warden.rb +126 -53
- data/lib/graphql/schema.rb +262 -281
- data/lib/graphql/static_validation/all_rules.rb +2 -0
- data/lib/graphql/static_validation/base_visitor.rb +9 -6
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/error.rb +3 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +4 -2
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +6 -2
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
- data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
- data/lib/graphql/static_validation/rules/directives_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 +90 -47
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
- 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 +4 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +14 -8
- data/lib/graphql/static_validation/validation_context.rb +12 -2
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +41 -10
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/string_encoding_error.rb +13 -3
- data/lib/graphql/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +39 -8
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +0 -3
- data/lib/graphql/subscriptions/event.rb +68 -32
- data/lib/graphql/subscriptions/instrumentation.rb +0 -1
- data/lib/graphql/subscriptions/serialize.rb +34 -5
- data/lib/graphql/subscriptions/subscription_root.rb +1 -1
- data/lib/graphql/subscriptions.rb +34 -39
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +8 -21
- data/lib/graphql/tracing/appoptics_tracing.rb +3 -1
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- 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 +24 -12
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/tracing/skylight_tracing.rb +1 -1
- data/lib/graphql/tracing.rb +2 -2
- data/lib/graphql/types/big_int.rb +5 -1
- data/lib/graphql/types/int.rb +10 -3
- 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/base_connection.rb +6 -91
- data/lib/graphql/types/relay/base_edge.rb +2 -34
- data/lib/graphql/types/relay/connection_behaviors.rb +174 -0
- data/lib/graphql/types/relay/default_relay.rb +31 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +64 -0
- data/lib/graphql/types/relay/has_node_field.rb +41 -0
- data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
- data/lib/graphql/types/relay/node.rb +2 -4
- data/lib/graphql/types/relay/node_behaviors.rb +15 -0
- data/lib/graphql/types/relay/node_field.rb +3 -22
- data/lib/graphql/types/relay/nodes_field.rb +16 -18
- data/lib/graphql/types/relay/page_info.rb +2 -14
- data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
- data/lib/graphql/types/relay.rb +11 -3
- data/lib/graphql/types/string.rb +8 -2
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/union_type.rb +3 -1
- data/lib/graphql/upgrader/member.rb +1 -0
- data/lib/graphql/upgrader/schema.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +68 -37
- data/readme.md +3 -6
- metadata +83 -113
- data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
- data/lib/graphql/types/relay/base_field.rb +0 -22
- data/lib/graphql/types/relay/base_interface.rb +0 -29
- data/lib/graphql/types/relay/base_object.rb +0 -26
- /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/field.rb
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
# test_via: ../object.rb
|
3
2
|
require "graphql/schema/field/connection_extension"
|
4
3
|
require "graphql/schema/field/scope_extension"
|
5
4
|
|
6
5
|
module GraphQL
|
7
6
|
class Schema
|
8
7
|
class Field
|
9
|
-
if !String.method_defined?(:-@)
|
10
|
-
using GraphQL::StringDedupBackport
|
11
|
-
end
|
12
|
-
|
13
8
|
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
14
9
|
include GraphQL::Schema::Member::AcceptsDefinition
|
15
10
|
include GraphQL::Schema::Member::HasArguments
|
16
11
|
include GraphQL::Schema::Member::HasAstNode
|
17
12
|
include GraphQL::Schema::Member::HasPath
|
13
|
+
include GraphQL::Schema::Member::HasValidators
|
18
14
|
extend GraphQL::Schema::FindInheritedValue
|
19
15
|
include GraphQL::Schema::FindInheritedValue::EmptyObjects
|
16
|
+
include GraphQL::Schema::Member::HasDirectives
|
17
|
+
include GraphQL::Schema::Member::HasDeprecationReason
|
18
|
+
|
19
|
+
class FieldImplementationFailed < GraphQL::Error; end
|
20
20
|
|
21
21
|
# @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
|
22
22
|
attr_reader :name
|
@@ -24,9 +24,6 @@ module GraphQL
|
|
24
24
|
|
25
25
|
attr_writer :description
|
26
26
|
|
27
|
-
# @return [String, nil] If present, the field is marked as deprecated with this documentation
|
28
|
-
attr_accessor :deprecation_reason
|
29
|
-
|
30
27
|
# @return [Symbol] Method or hash key on the underlying object to look up
|
31
28
|
attr_reader :method_sym
|
32
29
|
|
@@ -41,7 +38,9 @@ module GraphQL
|
|
41
38
|
|
42
39
|
# @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type)
|
43
40
|
def owner_type
|
44
|
-
@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
|
45
44
|
owner.payload_type
|
46
45
|
else
|
47
46
|
owner
|
@@ -61,6 +60,10 @@ module GraphQL
|
|
61
60
|
@introspection
|
62
61
|
end
|
63
62
|
|
63
|
+
def inspect
|
64
|
+
"#<#{self.class} #{path}#{all_argument_definitions.any? ? "(...)" : ""}: #{type.to_type_signature}>"
|
65
|
+
end
|
66
|
+
|
64
67
|
alias :mutation :resolver
|
65
68
|
|
66
69
|
# @return [Boolean] Apply tracing to this field? (Default: skip scalars, this is the override value)
|
@@ -82,11 +85,11 @@ module GraphQL
|
|
82
85
|
# @see {.initialize} for other options
|
83
86
|
def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
|
84
87
|
if kwargs[:field]
|
85
|
-
if kwargs[:field] == GraphQL::Relay::
|
86
|
-
warn("Legacy-style `GraphQL::Relay::Node.field` is being added to a class-based type. See `GraphQL::Types::Relay::NodeField` for a replacement.")
|
88
|
+
if kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodeField.graphql_definition
|
89
|
+
GraphQL::Deprecation.warn("Legacy-style `GraphQL::Relay::Node.field` is being added to a class-based type. See `GraphQL::Types::Relay::NodeField` for a replacement.")
|
87
90
|
return GraphQL::Types::Relay::NodeField
|
88
|
-
elsif kwargs[:field] == GraphQL::Relay::
|
89
|
-
warn("Legacy-style `GraphQL::Relay::Node.plural_field` is being added to a class-based type. See `GraphQL::Types::Relay::NodesField` for a replacement.")
|
91
|
+
elsif kwargs[:field].is_a?(GraphQL::Field) && kwargs[:field] == GraphQL::Types::Relay::NodesField.graphql_definition
|
92
|
+
GraphQL::Deprecation.warn("Legacy-style `GraphQL::Relay::Node.plural_field` is being added to a class-based type. See `GraphQL::Types::Relay::NodesField` for a replacement.")
|
90
93
|
return GraphQL::Types::Relay::NodesField
|
91
94
|
end
|
92
95
|
end
|
@@ -119,6 +122,9 @@ module GraphQL
|
|
119
122
|
else
|
120
123
|
kwargs[:type] = type
|
121
124
|
end
|
125
|
+
if type.is_a?(Class) && type < GraphQL::Schema::Mutation
|
126
|
+
raise ArgumentError, "Use `field #{name.inspect}, mutation: Mutation, ...` to provide a mutation to this field instead"
|
127
|
+
end
|
122
128
|
end
|
123
129
|
new(**kwargs, &block)
|
124
130
|
end
|
@@ -170,6 +176,8 @@ module GraphQL
|
|
170
176
|
|
171
177
|
# @return Boolean
|
172
178
|
attr_reader :relay_node_field
|
179
|
+
# @return Boolean
|
180
|
+
attr_reader :relay_nodes_field
|
173
181
|
|
174
182
|
# @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
|
175
183
|
def method_conflict_warning?
|
@@ -184,6 +192,7 @@ module GraphQL
|
|
184
192
|
# @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
|
185
193
|
# @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
|
186
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
|
187
196
|
# @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
|
188
197
|
# @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
|
189
198
|
# @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
|
@@ -199,11 +208,14 @@ module GraphQL
|
|
199
208
|
# @param scope [Boolean] If true, the return type's `.scope_items` method will be called on the return value
|
200
209
|
# @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
|
201
210
|
# @param extensions [Array<Class, Hash<Class => Object>>] Named extensions to apply to this field (see also {#extension})
|
211
|
+
# @param directives [Hash{Class => Hash}] Directives to apply to this field
|
202
212
|
# @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
|
203
213
|
# @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
|
204
214
|
# @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
|
205
215
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
206
|
-
|
216
|
+
# @param validates [Array<Hash>] Configurations for validating this field
|
217
|
+
# @param legacy_edge_class [Class, nil] (DEPRECATED) If present, pass this along to the legacy field definition
|
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)
|
207
219
|
if name.nil?
|
208
220
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
209
221
|
end
|
@@ -211,15 +223,13 @@ module GraphQL
|
|
211
223
|
if type.nil?
|
212
224
|
raise ArgumentError, "missing second `type` argument or keyword `type:`"
|
213
225
|
end
|
214
|
-
if null.nil?
|
215
|
-
raise ArgumentError, "missing keyword argument null:"
|
216
|
-
end
|
217
226
|
end
|
218
227
|
if (field || function || resolve) && extras.any?
|
219
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:`"
|
220
229
|
end
|
221
230
|
@original_name = name
|
222
231
|
name_s = -name.to_s
|
232
|
+
|
223
233
|
@underscored_name = -Member::BuildType.underscore(name_s)
|
224
234
|
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
225
235
|
@description = description
|
@@ -230,10 +240,10 @@ module GraphQL
|
|
230
240
|
end
|
231
241
|
@function = function
|
232
242
|
@resolve = resolve
|
233
|
-
|
243
|
+
self.deprecation_reason = deprecation_reason
|
234
244
|
|
235
|
-
if method && hash_key
|
236
|
-
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}`)"
|
237
247
|
end
|
238
248
|
|
239
249
|
if resolver_method
|
@@ -241,13 +251,18 @@ module GraphQL
|
|
241
251
|
raise ArgumentError, "Provide `method:` _or_ `resolver_method:`, not both. (called with: `method: #{method.inspect}, resolver_method: #{resolver_method.inspect}`)"
|
242
252
|
end
|
243
253
|
|
244
|
-
if hash_key
|
245
|
-
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}`)"
|
246
256
|
end
|
247
257
|
end
|
248
258
|
|
249
259
|
# TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
|
250
260
|
method_name = method || hash_key || name_s
|
261
|
+
@dig_keys = dig
|
262
|
+
if hash_key
|
263
|
+
@hash_key = hash_key
|
264
|
+
end
|
265
|
+
|
251
266
|
resolver_method ||= name_s.to_sym
|
252
267
|
|
253
268
|
@method_str = -method_name.to_s
|
@@ -269,34 +284,51 @@ module GraphQL
|
|
269
284
|
@relay_nodes_field = relay_nodes_field
|
270
285
|
@ast_node = ast_node
|
271
286
|
@method_conflict_warning = method_conflict_warning
|
287
|
+
@legacy_edge_class = legacy_edge_class
|
272
288
|
|
273
289
|
arguments.each do |name, arg|
|
274
|
-
|
290
|
+
case arg
|
291
|
+
when Hash
|
275
292
|
argument(name: name, **arg)
|
276
|
-
|
293
|
+
when GraphQL::Schema::Argument
|
277
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}"
|
278
299
|
end
|
279
300
|
end
|
280
301
|
|
281
302
|
@owner = owner
|
282
303
|
@subscription_scope = subscription_scope
|
283
304
|
|
284
|
-
# Do this last so we have as much context as possible when initializing them:
|
285
305
|
@extensions = EMPTY_ARRAY
|
286
|
-
|
287
|
-
self.extensions(extensions)
|
288
|
-
end
|
306
|
+
@call_after_define = false
|
289
307
|
# This should run before connection extension,
|
290
308
|
# but should it run after the definition block?
|
291
309
|
if scoped?
|
292
310
|
self.extension(ScopeExtension)
|
293
311
|
end
|
312
|
+
|
294
313
|
# The problem with putting this after the definition_block
|
295
314
|
# is that it would override arguments
|
296
315
|
if connection? && connection_extension
|
297
316
|
self.extension(connection_extension)
|
298
317
|
end
|
299
318
|
|
319
|
+
# Do this last so we have as much context as possible when initializing them:
|
320
|
+
if extensions.any?
|
321
|
+
self.extensions(extensions)
|
322
|
+
end
|
323
|
+
|
324
|
+
if directives.any?
|
325
|
+
directives.each do |(dir_class, options)|
|
326
|
+
self.directive(dir_class, **options)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
self.validates(validates)
|
331
|
+
|
300
332
|
if definition_block
|
301
333
|
if definition_block.arity == 1
|
302
334
|
yield self
|
@@ -304,6 +336,9 @@ module GraphQL
|
|
304
336
|
instance_eval(&definition_block)
|
305
337
|
end
|
306
338
|
end
|
339
|
+
|
340
|
+
self.extensions.each(&:after_define_apply)
|
341
|
+
@call_after_define = true
|
307
342
|
end
|
308
343
|
|
309
344
|
# If true, subscription updates with this field can be shared between viewers
|
@@ -336,27 +371,20 @@ module GraphQL
|
|
336
371
|
# @example adding an extension with options
|
337
372
|
# extensions([MyExtensionClass, { AnotherExtensionClass => { filter: true } }])
|
338
373
|
#
|
339
|
-
# @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.
|
340
375
|
# @return [Array<GraphQL::Schema::FieldExtension>] extensions to apply to this field
|
341
376
|
def extensions(new_extensions = nil)
|
342
|
-
if new_extensions
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
@extensions = @extensions.dup
|
348
|
-
end
|
349
|
-
new_extensions.each do |extension|
|
350
|
-
if extension.is_a?(Hash)
|
351
|
-
extension = extension.to_a[0]
|
352
|
-
extension_class, options = *extension
|
353
|
-
@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)
|
354
382
|
else
|
355
|
-
|
356
|
-
@extensions << extension_class.new(field: self, options: nil)
|
383
|
+
self.extension(extension_config)
|
357
384
|
end
|
358
385
|
end
|
359
386
|
end
|
387
|
+
@extensions
|
360
388
|
end
|
361
389
|
|
362
390
|
# Add `extension` to this field, initialized with `options` if provided.
|
@@ -367,10 +395,19 @@ module GraphQL
|
|
367
395
|
# @example adding an extension with options
|
368
396
|
# extension(MyExtensionClass, filter: true)
|
369
397
|
#
|
370
|
-
# @param
|
371
|
-
# @param options [
|
372
|
-
|
373
|
-
|
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
|
374
411
|
end
|
375
412
|
|
376
413
|
# Read extras (as symbols) from this field,
|
@@ -391,6 +428,71 @@ module GraphQL
|
|
391
428
|
end
|
392
429
|
end
|
393
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
|
+
|
394
496
|
def complexity(new_complexity = nil)
|
395
497
|
case new_complexity
|
396
498
|
when Proc
|
@@ -419,6 +521,8 @@ module GraphQL
|
|
419
521
|
# @return [Integer, nil] Applied to connections if {#has_max_page_size?}
|
420
522
|
attr_reader :max_page_size
|
421
523
|
|
524
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
525
|
+
|
422
526
|
# @return [GraphQL::Field]
|
423
527
|
def to_graphql
|
424
528
|
field_defn = if @field
|
@@ -438,8 +542,8 @@ module GraphQL
|
|
438
542
|
field_defn.description = @description
|
439
543
|
end
|
440
544
|
|
441
|
-
if
|
442
|
-
field_defn.deprecation_reason =
|
545
|
+
if self.deprecation_reason
|
546
|
+
field_defn.deprecation_reason = self.deprecation_reason
|
443
547
|
end
|
444
548
|
|
445
549
|
if @resolver_class
|
@@ -461,6 +565,10 @@ module GraphQL
|
|
461
565
|
field_defn.relay_nodes_field = @relay_nodes_field
|
462
566
|
end
|
463
567
|
|
568
|
+
if @legacy_edge_class
|
569
|
+
field_defn.edge_class = @legacy_edge_class
|
570
|
+
end
|
571
|
+
|
464
572
|
field_defn.resolve = self.method(:resolve_field)
|
465
573
|
field_defn.connection = connection?
|
466
574
|
field_defn.connection_max_page_size = max_page_size
|
@@ -469,9 +577,9 @@ module GraphQL
|
|
469
577
|
field_defn.subscription_scope = @subscription_scope
|
470
578
|
field_defn.ast_node = ast_node
|
471
579
|
|
472
|
-
|
473
|
-
arg_graphql = defn.
|
474
|
-
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
|
475
583
|
end
|
476
584
|
|
477
585
|
# Support a passed-in proc, one way or another
|
@@ -489,6 +597,7 @@ module GraphQL
|
|
489
597
|
field_defn
|
490
598
|
end
|
491
599
|
|
600
|
+
class MissingReturnTypeError < GraphQL::Error; end
|
492
601
|
attr_writer :type
|
493
602
|
|
494
603
|
def type
|
@@ -496,14 +605,21 @@ module GraphQL
|
|
496
605
|
Member::BuildType.parse_type(@function.type, null: false)
|
497
606
|
elsif @field
|
498
607
|
Member::BuildType.parse_type(@field.type, null: false)
|
608
|
+
elsif @return_type_expr.nil?
|
609
|
+
# Not enough info to determine type
|
610
|
+
message = "Can't determine the return type for #{self.path}"
|
611
|
+
if @resolver_class
|
612
|
+
message += " (it has `resolver: #{@resolver_class}`, consider configuration a `type ...` for that class)"
|
613
|
+
end
|
614
|
+
raise MissingReturnTypeError, message
|
499
615
|
else
|
500
616
|
Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
501
617
|
end
|
502
|
-
rescue GraphQL::Schema::InvalidDocumentError => err
|
618
|
+
rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
|
503
619
|
# Let this propagate up
|
504
620
|
raise err
|
505
621
|
rescue StandardError => err
|
506
|
-
raise
|
622
|
+
raise MissingReturnTypeError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
|
507
623
|
end
|
508
624
|
|
509
625
|
def visible?(context)
|
@@ -524,13 +640,41 @@ module GraphQL
|
|
524
640
|
|
525
641
|
def authorized?(object, args, context)
|
526
642
|
if @resolver_class
|
527
|
-
# The resolver will check itself during `resolve()`
|
643
|
+
# The resolver _instance_ will check itself during `resolve()`
|
528
644
|
@resolver_class.authorized?(object, context)
|
529
645
|
else
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
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
|
534
678
|
end
|
535
679
|
end
|
536
680
|
true
|
@@ -580,12 +724,14 @@ module GraphQL
|
|
580
724
|
begin
|
581
725
|
# Unwrap the GraphQL object to get the application object.
|
582
726
|
application_object = object.object
|
727
|
+
|
728
|
+
Schema::Validator.validate!(validators, application_object, ctx, args)
|
729
|
+
|
583
730
|
ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
|
584
731
|
if is_authorized
|
585
732
|
public_send_field(object, args, ctx)
|
586
733
|
else
|
587
|
-
|
588
|
-
ctx.schema.unauthorized_field(err)
|
734
|
+
raise GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
|
589
735
|
end
|
590
736
|
end
|
591
737
|
rescue GraphQL::UnauthorizedFieldError => err
|
@@ -625,7 +771,7 @@ module GraphQL
|
|
625
771
|
ruby_kwargs = graphql_args.to_kwargs
|
626
772
|
maybe_lazies = []
|
627
773
|
# Apply any `prepare` methods. Not great code organization, can this go somewhere better?
|
628
|
-
arguments.each do |name, arg_defn|
|
774
|
+
arguments(field_ctx).each do |name, arg_defn|
|
629
775
|
ruby_kwargs_key = arg_defn.keyword
|
630
776
|
|
631
777
|
if ruby_kwargs.key?(ruby_kwargs_key)
|
@@ -670,51 +816,107 @@ module GraphQL
|
|
670
816
|
|
671
817
|
def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
|
672
818
|
with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
|
680
|
-
|
681
|
-
#
|
682
|
-
# - A method on the type instance;
|
683
|
-
# - Hash keys, if the wrapped object is a hash;
|
684
|
-
# - A method on the wrapped object;
|
685
|
-
# - Or, raise not implemented.
|
686
|
-
#
|
687
|
-
if obj.respond_to?(@resolver_method)
|
688
|
-
# Call the method with kwargs, if there are any
|
689
|
-
if ruby_kwargs.any?
|
690
|
-
obj.public_send(@resolver_method, **ruby_kwargs)
|
691
|
-
else
|
692
|
-
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)
|
693
827
|
end
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
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
|
698
864
|
else
|
699
|
-
|
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
|
700
874
|
end
|
701
|
-
|
702
|
-
|
703
|
-
|
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)
|
704
895
|
else
|
705
|
-
|
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."
|
706
897
|
end
|
707
|
-
|
708
|
-
|
709
|
-
|
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
|
710
910
|
|
711
|
-
|
712
|
-
|
713
|
-
|
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:
|
714
914
|
|
715
|
-
|
716
|
-
|
717
|
-
|
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
|
718
920
|
end
|
719
921
|
end
|
720
922
|
|
@@ -725,33 +927,52 @@ module GraphQL
|
|
725
927
|
if @extensions.empty?
|
726
928
|
yield(obj, args)
|
727
929
|
else
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
value = run_extensions_before_resolve(
|
733
|
-
|
734
|
-
|
930
|
+
# This is a hack to get the _last_ value for extended obj and args,
|
931
|
+
# in case one of the extensions doesn't `yield`.
|
932
|
+
# (There's another implementation that uses multiple-return, but I'm wary of the perf cost of the extra arrays)
|
933
|
+
extended = { args: args, obj: obj, memos: nil, added_extras: nil }
|
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
|
735
939
|
yield(obj, args)
|
736
940
|
end
|
737
941
|
|
942
|
+
extended_obj = extended[:obj]
|
943
|
+
extended_args = extended[:args]
|
944
|
+
memos = extended[:memos] || EMPTY_HASH
|
945
|
+
|
738
946
|
ctx.schema.after_lazy(value) do |resolved_value|
|
739
|
-
|
947
|
+
idx = 0
|
948
|
+
@extensions.each do |ext|
|
740
949
|
memo = memos[idx]
|
741
950
|
# TODO after_lazy?
|
742
951
|
resolved_value = ext.after_resolve(object: extended_obj, arguments: extended_args, context: ctx, value: resolved_value, memo: memo)
|
952
|
+
idx += 1
|
743
953
|
end
|
744
954
|
resolved_value
|
745
955
|
end
|
746
956
|
end
|
747
957
|
end
|
748
958
|
|
749
|
-
def run_extensions_before_resolve(
|
959
|
+
def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0)
|
750
960
|
extension = @extensions[idx]
|
751
961
|
if extension
|
752
962
|
extension.resolve(object: obj, arguments: args, context: ctx) do |extended_obj, extended_args, memo|
|
753
|
-
|
754
|
-
|
963
|
+
if memo
|
964
|
+
memos = extended[:memos] ||= {}
|
965
|
+
memos[idx] = memo
|
966
|
+
end
|
967
|
+
|
968
|
+
if (extras = extension.added_extras)
|
969
|
+
ae = extended[:added_extras] ||= []
|
970
|
+
ae.concat(extras)
|
971
|
+
end
|
972
|
+
|
973
|
+
extended[:obj] = extended_obj
|
974
|
+
extended[:args] = extended_args
|
975
|
+
run_extensions_before_resolve(extended_obj, extended_args, ctx, extended, idx: idx + 1) { |o, a| yield(o, a) }
|
755
976
|
end
|
756
977
|
else
|
757
978
|
yield(obj, args)
|