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
@@ -8,6 +8,137 @@ module GraphQL
|
|
8
8
|
#
|
9
9
|
# @api private
|
10
10
|
class Runtime
|
11
|
+
|
12
|
+
module GraphQLResult
|
13
|
+
def initialize(result_name, parent_result)
|
14
|
+
@graphql_parent = parent_result
|
15
|
+
if parent_result && parent_result.graphql_dead
|
16
|
+
@graphql_dead = true
|
17
|
+
end
|
18
|
+
@graphql_result_name = result_name
|
19
|
+
# Jump through some hoops to avoid creating this duplicate storage if at all possible.
|
20
|
+
@graphql_metadata = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :graphql_dead
|
24
|
+
attr_reader :graphql_parent, :graphql_result_name
|
25
|
+
|
26
|
+
# Although these are used by only one of the Result classes,
|
27
|
+
# it's handy to have the methods implemented on both (even though they just return `nil`)
|
28
|
+
# because it makes it easy to check if anything is assigned.
|
29
|
+
# @return [nil, Array<String>]
|
30
|
+
attr_accessor :graphql_non_null_field_names
|
31
|
+
# @return [nil, true]
|
32
|
+
attr_accessor :graphql_non_null_list_items
|
33
|
+
|
34
|
+
# @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
|
35
|
+
attr_accessor :graphql_result_data
|
36
|
+
end
|
37
|
+
|
38
|
+
class GraphQLResultHash
|
39
|
+
def initialize(_result_name, _parent_result)
|
40
|
+
super
|
41
|
+
@graphql_result_data = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
include GraphQLResult
|
45
|
+
|
46
|
+
attr_accessor :graphql_merged_into
|
47
|
+
|
48
|
+
def []=(key, value)
|
49
|
+
# This is a hack.
|
50
|
+
# Basically, this object is merged into the root-level result at some point.
|
51
|
+
# But the problem is, some lazies are created whose closures retain reference to _this_
|
52
|
+
# object. When those lazies are resolved, they cause an update to this object.
|
53
|
+
#
|
54
|
+
# In order to return a proper top-level result, we have to update that top-level result object.
|
55
|
+
# In order to return a proper partial result (eg, for a directive), we have to update this object, too.
|
56
|
+
# Yowza.
|
57
|
+
if (t = @graphql_merged_into)
|
58
|
+
t[key] = value
|
59
|
+
end
|
60
|
+
|
61
|
+
if value.respond_to?(:graphql_result_data)
|
62
|
+
@graphql_result_data[key] = value.graphql_result_data
|
63
|
+
# If we encounter some part of this response that requires metadata tracking,
|
64
|
+
# then create the metadata hash if necessary. It will be kept up-to-date after this.
|
65
|
+
(@graphql_metadata ||= @graphql_result_data.dup)[key] = value
|
66
|
+
else
|
67
|
+
@graphql_result_data[key] = value
|
68
|
+
# keep this up-to-date if it's been initialized
|
69
|
+
@graphql_metadata && @graphql_metadata[key] = value
|
70
|
+
end
|
71
|
+
|
72
|
+
value
|
73
|
+
end
|
74
|
+
|
75
|
+
def delete(key)
|
76
|
+
@graphql_metadata && @graphql_metadata.delete(key)
|
77
|
+
@graphql_result_data.delete(key)
|
78
|
+
end
|
79
|
+
|
80
|
+
def each
|
81
|
+
(@graphql_metadata || @graphql_result_data).each { |k, v| yield(k, v) }
|
82
|
+
end
|
83
|
+
|
84
|
+
def values
|
85
|
+
(@graphql_metadata || @graphql_result_data).values
|
86
|
+
end
|
87
|
+
|
88
|
+
def key?(k)
|
89
|
+
@graphql_result_data.key?(k)
|
90
|
+
end
|
91
|
+
|
92
|
+
def [](k)
|
93
|
+
(@graphql_metadata || @graphql_result_data)[k]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class GraphQLResultArray
|
98
|
+
include GraphQLResult
|
99
|
+
|
100
|
+
def initialize(_result_name, _parent_result)
|
101
|
+
super
|
102
|
+
@graphql_result_data = []
|
103
|
+
end
|
104
|
+
|
105
|
+
def graphql_skip_at(index)
|
106
|
+
# Mark this index as dead. It's tricky because some indices may already be storing
|
107
|
+
# `Lazy`s. So the runtime is still holding indexes _before_ skipping,
|
108
|
+
# this object has to coordinate incoming writes to account for any already-skipped indices.
|
109
|
+
@skip_indices ||= []
|
110
|
+
@skip_indices << index
|
111
|
+
offset_by = @skip_indices.count { |skipped_idx| skipped_idx < index}
|
112
|
+
delete_at_index = index - offset_by
|
113
|
+
@graphql_metadata && @graphql_metadata.delete_at(delete_at_index)
|
114
|
+
@graphql_result_data.delete_at(delete_at_index)
|
115
|
+
end
|
116
|
+
|
117
|
+
def []=(idx, value)
|
118
|
+
if @skip_indices
|
119
|
+
offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
|
120
|
+
idx -= offset_by
|
121
|
+
end
|
122
|
+
if value.respond_to?(:graphql_result_data)
|
123
|
+
@graphql_result_data[idx] = value.graphql_result_data
|
124
|
+
(@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
|
125
|
+
else
|
126
|
+
@graphql_result_data[idx] = value
|
127
|
+
@graphql_metadata && @graphql_metadata[idx] = value
|
128
|
+
end
|
129
|
+
|
130
|
+
value
|
131
|
+
end
|
132
|
+
|
133
|
+
def values
|
134
|
+
(@graphql_metadata || @graphql_result_data)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class GraphQLSelectionSet < Hash
|
139
|
+
attr_accessor :graphql_directives
|
140
|
+
end
|
141
|
+
|
11
142
|
# @return [GraphQL::Query]
|
12
143
|
attr_reader :query
|
13
144
|
|
@@ -17,28 +148,49 @@ module GraphQL
|
|
17
148
|
# @return [GraphQL::Query::Context]
|
18
149
|
attr_reader :context
|
19
150
|
|
20
|
-
def initialize(query
|
151
|
+
def initialize(query:)
|
21
152
|
@query = query
|
153
|
+
@dataloader = query.multiplex.dataloader
|
22
154
|
@schema = query.schema
|
23
155
|
@context = query.context
|
156
|
+
@multiplex_context = query.multiplex.context
|
24
157
|
@interpreter_context = @context.namespace(:interpreter)
|
25
|
-
@response =
|
26
|
-
|
27
|
-
@
|
158
|
+
@response = GraphQLResultHash.new(nil, nil)
|
159
|
+
# Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
|
160
|
+
@runtime_directive_names = []
|
161
|
+
noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
|
162
|
+
@schema_directives = schema.directives
|
163
|
+
@schema_directives.each do |name, dir_defn|
|
164
|
+
if dir_defn.method(:resolve).owner != noop_resolve_owner
|
165
|
+
@runtime_directive_names << name
|
166
|
+
end
|
167
|
+
end
|
28
168
|
# A cache of { Class => { String => Schema::Field } }
|
29
169
|
# Which assumes that MyObject.get_field("myField") will return the same field
|
30
170
|
# during the lifetime of a query
|
31
171
|
@fields_cache = Hash.new { |h, k| h[k] = {} }
|
172
|
+
# { Class => Boolean }
|
173
|
+
@lazy_cache = {}
|
32
174
|
end
|
33
175
|
|
34
|
-
def
|
35
|
-
@response.
|
176
|
+
def final_result
|
177
|
+
@response && @response.graphql_result_data
|
36
178
|
end
|
37
179
|
|
38
180
|
def inspect
|
39
181
|
"#<#{self.class.name} response=#{@response.inspect}>"
|
40
182
|
end
|
41
183
|
|
184
|
+
def tap_or_each(obj_or_array)
|
185
|
+
if obj_or_array.is_a?(Array)
|
186
|
+
obj_or_array.each do |item|
|
187
|
+
yield(item, true)
|
188
|
+
end
|
189
|
+
else
|
190
|
+
yield(obj_or_array, false)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
42
194
|
# This _begins_ the execution. Some deferred work
|
43
195
|
# might be stored up in lazies.
|
44
196
|
# @return [void]
|
@@ -47,15 +199,50 @@ module GraphQL
|
|
47
199
|
root_op_type = root_operation.operation_type || "query"
|
48
200
|
root_type = schema.root_type_for_operation(root_op_type)
|
49
201
|
path = []
|
50
|
-
|
51
|
-
|
52
|
-
object_proxy = authorized_new(root_type, query.root_value, context, path)
|
202
|
+
set_all_interpreter_context(query.root_value, nil, nil, path)
|
203
|
+
object_proxy = authorized_new(root_type, query.root_value, context)
|
53
204
|
object_proxy = schema.sync_lazy(object_proxy)
|
205
|
+
|
54
206
|
if object_proxy.nil?
|
55
207
|
# Root .authorized? returned false.
|
56
|
-
|
208
|
+
@response = nil
|
57
209
|
else
|
58
|
-
|
210
|
+
call_method_on_directives(:resolve, object_proxy, root_operation.directives) do # execute query level directives
|
211
|
+
gathered_selections = gather_selections(object_proxy, root_type, root_operation.selections)
|
212
|
+
# This is kind of a hack -- `gathered_selections` is an Array if any of the selections
|
213
|
+
# require isolation during execution (because of runtime directives). In that case,
|
214
|
+
# make a new, isolated result hash for writing the result into. (That isolated response
|
215
|
+
# is eventually merged back into the main response)
|
216
|
+
#
|
217
|
+
# Otherwise, `gathered_selections` is a hash of selections which can be
|
218
|
+
# directly evaluated and the results can be written right into the main response hash.
|
219
|
+
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
220
|
+
if is_selection_array
|
221
|
+
selection_response = GraphQLResultHash.new(nil, nil)
|
222
|
+
final_response = @response
|
223
|
+
else
|
224
|
+
selection_response = @response
|
225
|
+
final_response = nil
|
226
|
+
end
|
227
|
+
|
228
|
+
@dataloader.append_job {
|
229
|
+
set_all_interpreter_context(query.root_value, nil, nil, path)
|
230
|
+
call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
|
231
|
+
evaluate_selections(
|
232
|
+
path,
|
233
|
+
context.scoped_context,
|
234
|
+
object_proxy,
|
235
|
+
root_type,
|
236
|
+
root_op_type == "mutation",
|
237
|
+
selections,
|
238
|
+
selection_response,
|
239
|
+
final_response,
|
240
|
+
nil,
|
241
|
+
)
|
242
|
+
end
|
243
|
+
}
|
244
|
+
end
|
245
|
+
end
|
59
246
|
end
|
60
247
|
delete_interpreter_context(:current_path)
|
61
248
|
delete_interpreter_context(:current_field)
|
@@ -64,15 +251,36 @@ module GraphQL
|
|
64
251
|
nil
|
65
252
|
end
|
66
253
|
|
67
|
-
|
254
|
+
# @return [void]
|
255
|
+
def deep_merge_selection_result(from_result, into_result)
|
256
|
+
from_result.each do |key, value|
|
257
|
+
if !into_result.key?(key)
|
258
|
+
into_result[key] = value
|
259
|
+
else
|
260
|
+
case value
|
261
|
+
when GraphQLResultHash
|
262
|
+
deep_merge_selection_result(value, into_result[key])
|
263
|
+
else
|
264
|
+
# We have to assume that, since this passed the `fields_will_merge` selection,
|
265
|
+
# that the old and new values are the same.
|
266
|
+
# There's no special handling of arrays because currently, there's no way to split the execution
|
267
|
+
# of a list over several concurrent flows.
|
268
|
+
into_result[key] = value
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
from_result.graphql_merged_into = into_result
|
273
|
+
nil
|
274
|
+
end
|
275
|
+
|
276
|
+
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = GraphQLSelectionSet.new)
|
68
277
|
selections.each do |node|
|
69
278
|
# Skip gathering this if the directive says so
|
70
279
|
if !directives_include?(node, owner_object, owner_type)
|
71
280
|
next
|
72
281
|
end
|
73
282
|
|
74
|
-
|
75
|
-
when GraphQL::Language::Nodes::Field
|
283
|
+
if node.is_a?(GraphQL::Language::Nodes::Field)
|
76
284
|
response_key = node.alias || node.name
|
77
285
|
selections = selections_by_name[response_key]
|
78
286
|
# if there was already a selection of this field,
|
@@ -88,203 +296,373 @@ module GraphQL
|
|
88
296
|
# No selection was found for this field yet
|
89
297
|
selections_by_name[response_key] = node
|
90
298
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
299
|
+
else
|
300
|
+
# This is an InlineFragment or a FragmentSpread
|
301
|
+
if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
|
302
|
+
next_selections = GraphQLSelectionSet.new
|
303
|
+
next_selections.graphql_directives = node.directives
|
304
|
+
if selections_to_run
|
305
|
+
selections_to_run << next_selections
|
306
|
+
else
|
307
|
+
selections_to_run = []
|
308
|
+
selections_to_run << selections_by_name
|
309
|
+
selections_to_run << next_selections
|
310
|
+
end
|
311
|
+
else
|
312
|
+
next_selections = selections_by_name
|
313
|
+
end
|
314
|
+
|
315
|
+
case node
|
316
|
+
when GraphQL::Language::Nodes::InlineFragment
|
317
|
+
if node.type
|
318
|
+
type_defn = schema.get_type(node.type.name, context)
|
319
|
+
|
320
|
+
# Faster than .map{}.include?()
|
321
|
+
query.warden.possible_types(type_defn).each do |t|
|
322
|
+
if t == owner_type
|
323
|
+
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
324
|
+
break
|
325
|
+
end
|
326
|
+
end
|
327
|
+
else
|
328
|
+
# it's an untyped fragment, definitely continue
|
329
|
+
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
330
|
+
end
|
331
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
332
|
+
fragment_def = query.fragments[node.name]
|
333
|
+
type_defn = query.get_type(fragment_def.type.name)
|
334
|
+
possible_types = query.warden.possible_types(type_defn)
|
335
|
+
possible_types.each do |t|
|
96
336
|
if t == owner_type
|
97
|
-
gather_selections(owner_object, owner_type,
|
337
|
+
gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
98
338
|
break
|
99
339
|
end
|
100
340
|
end
|
101
341
|
else
|
102
|
-
|
103
|
-
gather_selections(owner_object, owner_type, node.selections, selections_by_name)
|
342
|
+
raise "Invariant: unexpected selection class: #{node.class}"
|
104
343
|
end
|
105
|
-
when GraphQL::Language::Nodes::FragmentSpread
|
106
|
-
fragment_def = query.fragments[node.name]
|
107
|
-
type_defn = schema.get_type(fragment_def.type.name)
|
108
|
-
possible_types = query.warden.possible_types(type_defn)
|
109
|
-
possible_types.each do |t|
|
110
|
-
if t == owner_type
|
111
|
-
gather_selections(owner_object, owner_type, fragment_def.selections, selections_by_name)
|
112
|
-
break
|
113
|
-
end
|
114
|
-
end
|
115
|
-
else
|
116
|
-
raise "Invariant: unexpected selection class: #{node.class}"
|
117
344
|
end
|
118
345
|
end
|
346
|
+
selections_to_run || selections_by_name
|
119
347
|
end
|
120
348
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
field_name = ast_node.name
|
138
|
-
field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name)
|
139
|
-
is_introspection = false
|
140
|
-
if field_defn.nil?
|
141
|
-
field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
142
|
-
is_introspection = true
|
143
|
-
entry_point_field
|
144
|
-
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
145
|
-
is_introspection = true
|
146
|
-
dynamic_field
|
147
|
-
else
|
148
|
-
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
349
|
+
NO_ARGS = {}.freeze
|
350
|
+
|
351
|
+
# @return [void]
|
352
|
+
def evaluate_selections(path, scoped_context, owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
|
353
|
+
set_all_interpreter_context(owner_object, nil, nil, path)
|
354
|
+
|
355
|
+
finished_jobs = 0
|
356
|
+
enqueued_jobs = gathered_selections.size
|
357
|
+
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
358
|
+
@dataloader.append_job {
|
359
|
+
evaluate_selection(
|
360
|
+
path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_selection, selections_result, parent_object
|
361
|
+
)
|
362
|
+
finished_jobs += 1
|
363
|
+
if target_result && finished_jobs == enqueued_jobs
|
364
|
+
deep_merge_selection_result(selections_result, target_result)
|
149
365
|
end
|
366
|
+
}
|
367
|
+
end
|
368
|
+
|
369
|
+
selections_result
|
370
|
+
end
|
371
|
+
|
372
|
+
attr_reader :progress_path
|
373
|
+
|
374
|
+
# @return [void]
|
375
|
+
def evaluate_selection(path, result_name, field_ast_nodes_or_ast_node, scoped_context, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
|
376
|
+
return if dead_result?(selections_result)
|
377
|
+
# As a performance optimization, the hash key will be a `Node` if
|
378
|
+
# there's only one selection of the field. But if there are multiple
|
379
|
+
# selections of the field, it will be an Array of nodes
|
380
|
+
if field_ast_nodes_or_ast_node.is_a?(Array)
|
381
|
+
field_ast_nodes = field_ast_nodes_or_ast_node
|
382
|
+
ast_node = field_ast_nodes.first
|
383
|
+
else
|
384
|
+
field_ast_nodes = nil
|
385
|
+
ast_node = field_ast_nodes_or_ast_node
|
386
|
+
end
|
387
|
+
field_name = ast_node.name
|
388
|
+
# This can't use `query.get_field` because it gets confused on introspection below if `field_defn` isn't `nil`,
|
389
|
+
# because of how `is_introspection` is used to call `.authorized_new` later on.
|
390
|
+
field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name, @context)
|
391
|
+
is_introspection = false
|
392
|
+
if field_defn.nil?
|
393
|
+
field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
394
|
+
is_introspection = true
|
395
|
+
entry_point_field
|
396
|
+
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
397
|
+
is_introspection = true
|
398
|
+
dynamic_field
|
399
|
+
else
|
400
|
+
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
150
401
|
end
|
151
|
-
|
402
|
+
end
|
152
403
|
|
153
|
-
|
154
|
-
next_path << result_name
|
155
|
-
next_path.freeze
|
404
|
+
return_type = field_defn.type
|
156
405
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
set_type_at_path(next_path, return_type)
|
161
|
-
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
162
|
-
set_interpreter_context(:current_path, next_path)
|
163
|
-
set_interpreter_context(:current_field, field_defn)
|
406
|
+
next_path = path.dup
|
407
|
+
next_path << result_name
|
408
|
+
next_path.freeze
|
164
409
|
|
165
|
-
|
166
|
-
|
410
|
+
# This seems janky, but we need to know
|
411
|
+
# the field's return type at this path in order
|
412
|
+
# to propagate `null`
|
413
|
+
if return_type.non_null?
|
414
|
+
(selections_result.graphql_non_null_field_names ||= []).push(result_name)
|
415
|
+
end
|
416
|
+
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
417
|
+
set_all_interpreter_context(nil, field_defn, nil, next_path)
|
418
|
+
|
419
|
+
context.scoped_context = scoped_context
|
420
|
+
object = owner_object
|
167
421
|
|
168
|
-
|
169
|
-
|
422
|
+
if is_introspection
|
423
|
+
object = authorized_new(field_defn.owner, object, context)
|
424
|
+
end
|
425
|
+
|
426
|
+
total_args_count = field_defn.arguments(context).size
|
427
|
+
if total_args_count == 0
|
428
|
+
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
429
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
|
430
|
+
else
|
431
|
+
# TODO remove all arguments(...) usages?
|
432
|
+
@query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
|
433
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type)
|
170
434
|
end
|
435
|
+
end
|
436
|
+
end
|
171
437
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
438
|
+
def evaluate_selection_with_args(arguments, field_defn, next_path, ast_node, field_ast_nodes, scoped_context, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type) # rubocop:disable Metrics/ParameterLists
|
439
|
+
context.scoped_context = scoped_context
|
440
|
+
after_lazy(arguments, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
|
441
|
+
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
442
|
+
continue_value(next_path, resolved_arguments, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
|
176
443
|
next
|
177
444
|
end
|
178
445
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
446
|
+
kwarg_arguments = if resolved_arguments.empty? && field_defn.extras.empty?
|
447
|
+
# We can avoid allocating the `{ Symbol => Object }` hash in this case
|
448
|
+
NO_ARGS
|
449
|
+
else
|
450
|
+
# Bundle up the extras, then make a new arguments instance
|
451
|
+
# that includes the extras, too.
|
452
|
+
extra_args = {}
|
187
453
|
field_defn.extras.each do |extra|
|
188
454
|
case extra
|
189
455
|
when :ast_node
|
190
|
-
|
456
|
+
extra_args[:ast_node] = ast_node
|
191
457
|
when :execution_errors
|
192
|
-
|
458
|
+
extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
|
193
459
|
when :path
|
194
|
-
|
460
|
+
extra_args[:path] = next_path
|
195
461
|
when :lookahead
|
196
462
|
if !field_ast_nodes
|
197
463
|
field_ast_nodes = [ast_node]
|
198
464
|
end
|
199
|
-
|
465
|
+
|
466
|
+
extra_args[:lookahead] = Execution::Lookahead.new(
|
200
467
|
query: query,
|
201
468
|
ast_nodes: field_ast_nodes,
|
202
469
|
field: field_defn,
|
203
470
|
)
|
204
471
|
when :argument_details
|
205
|
-
|
472
|
+
# Use this flag to tell Interpreter::Arguments to add itself
|
473
|
+
# to the keyword args hash _before_ freezing everything.
|
474
|
+
extra_args[:argument_details] = :__arguments_add_self
|
475
|
+
when :irep_node
|
476
|
+
# This is used by `__typename` in order to support the legacy runtime,
|
477
|
+
# but it has no use here (and it's always `nil`).
|
478
|
+
# Stop adding it here to avoid the overhead of `.merge_extras` below.
|
479
|
+
when :parent
|
480
|
+
extra_args[:parent] = parent_object
|
206
481
|
else
|
207
|
-
|
482
|
+
extra_args[extra] = field_defn.fetch_extra(extra, context)
|
208
483
|
end
|
209
484
|
end
|
485
|
+
if extra_args.any?
|
486
|
+
resolved_arguments = resolved_arguments.merge_extras(extra_args)
|
487
|
+
end
|
488
|
+
resolved_arguments.keyword_arguments
|
489
|
+
end
|
210
490
|
|
211
|
-
|
491
|
+
set_all_interpreter_context(nil, nil, resolved_arguments, nil)
|
212
492
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
493
|
+
# Optimize for the case that field is selected only once
|
494
|
+
if field_ast_nodes.nil? || field_ast_nodes.size == 1
|
495
|
+
next_selections = ast_node.selections
|
496
|
+
directives = ast_node.directives
|
497
|
+
else
|
498
|
+
next_selections = []
|
499
|
+
directives = []
|
500
|
+
field_ast_nodes.each { |f|
|
501
|
+
next_selections.concat(f.selections)
|
502
|
+
directives.concat(f.directives)
|
503
|
+
}
|
504
|
+
end
|
220
505
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
end
|
506
|
+
field_result = call_method_on_directives(:resolve, object, directives) do
|
507
|
+
# Actually call the field resolver and capture the result
|
508
|
+
app_result = begin
|
509
|
+
query.with_error_handling do
|
510
|
+
query.trace("execute_field", {owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments}) do
|
511
|
+
field_defn.resolve(object, kwarg_arguments, context)
|
228
512
|
end
|
229
|
-
rescue GraphQL::ExecutionError => err
|
230
|
-
err
|
231
513
|
end
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
end
|
514
|
+
rescue GraphQL::ExecutionError => err
|
515
|
+
err
|
516
|
+
end
|
517
|
+
after_lazy(app_result, owner: owner_type, field: field_defn, path: next_path, ast_node: ast_node, scoped_context: context.scoped_context, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
|
518
|
+
continue_value = continue_value(next_path, inner_result, owner_type, field_defn, return_type.non_null?, ast_node, result_name, selection_result)
|
519
|
+
if HALT != continue_value
|
520
|
+
continue_field(next_path, continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
|
240
521
|
end
|
241
522
|
end
|
523
|
+
end
|
242
524
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
525
|
+
# If this field is a root mutation field, immediately resolve
|
526
|
+
# all of its child fields before moving on to the next root mutation field.
|
527
|
+
# (Subselections of this mutation will still be resolved level-by-level.)
|
528
|
+
if is_eager_field
|
529
|
+
Interpreter::Resolve.resolve_all([field_result], @dataloader)
|
530
|
+
else
|
531
|
+
# Return this from `after_lazy` because it might be another lazy that needs to be resolved
|
532
|
+
field_result
|
533
|
+
end
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
def dead_result?(selection_result)
|
538
|
+
selection_result.graphql_dead || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
|
539
|
+
end
|
540
|
+
|
541
|
+
def set_result(selection_result, result_name, value)
|
542
|
+
if !dead_result?(selection_result)
|
543
|
+
if value.nil? &&
|
544
|
+
( # there are two conditions under which `nil` is not allowed in the response:
|
545
|
+
(selection_result.graphql_non_null_list_items) || # this value would be written into a list that doesn't allow nils
|
546
|
+
((nn = selection_result.graphql_non_null_field_names) && nn.include?(result_name)) # this value would be written into a field that doesn't allow nils
|
547
|
+
)
|
548
|
+
# This is an invalid nil that should be propagated
|
549
|
+
# One caller of this method passes a block,
|
550
|
+
# namely when application code returns a `nil` to GraphQL and it doesn't belong there.
|
551
|
+
# The other possibility for reaching here is when a field returns an ExecutionError, so we write
|
552
|
+
# `nil` to the response, not knowing whether it's an invalid `nil` or not.
|
553
|
+
# (And in that case, we don't have to call the schema's handler, since it's not a bug in the application.)
|
554
|
+
# TODO the code is trying to tell me something.
|
555
|
+
yield if block_given?
|
556
|
+
parent = selection_result.graphql_parent
|
557
|
+
name_in_parent = selection_result.graphql_result_name
|
558
|
+
if parent.nil? # This is a top-level result hash
|
559
|
+
@response = nil
|
248
560
|
else
|
249
|
-
|
561
|
+
set_result(parent, name_in_parent, nil)
|
562
|
+
set_graphql_dead(selection_result)
|
250
563
|
end
|
564
|
+
else
|
565
|
+
selection_result[result_name] = value
|
251
566
|
end
|
252
567
|
end
|
253
568
|
end
|
254
569
|
|
570
|
+
# Mark this node and any already-registered children as dead,
|
571
|
+
# so that it accepts no more writes.
|
572
|
+
def set_graphql_dead(selection_result)
|
573
|
+
case selection_result
|
574
|
+
when GraphQLResultArray
|
575
|
+
selection_result.graphql_dead = true
|
576
|
+
selection_result.values.each { |v| set_graphql_dead(v) }
|
577
|
+
when GraphQLResultHash
|
578
|
+
selection_result.graphql_dead = true
|
579
|
+
selection_result.each { |k, v| set_graphql_dead(v) }
|
580
|
+
else
|
581
|
+
# It's a scalar, no way to mark it dead.
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
255
585
|
HALT = Object.new
|
256
|
-
def continue_value(path, value, parent_type, field, is_non_null, ast_node)
|
257
|
-
|
586
|
+
def continue_value(path, value, parent_type, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
587
|
+
case value
|
588
|
+
when nil
|
258
589
|
if is_non_null
|
259
|
-
|
260
|
-
|
590
|
+
set_result(selection_result, result_name, nil) do
|
591
|
+
# This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
|
592
|
+
err = parent_type::InvalidNullError.new(parent_type, field, value)
|
593
|
+
schema.type_error(err, context)
|
594
|
+
end
|
261
595
|
else
|
262
|
-
|
596
|
+
set_result(selection_result, result_name, nil)
|
263
597
|
end
|
264
598
|
HALT
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
599
|
+
when GraphQL::Error
|
600
|
+
# Handle these cases inside a single `when`
|
601
|
+
# to avoid the overhead of checking three different classes
|
602
|
+
# every time.
|
603
|
+
if value.is_a?(GraphQL::ExecutionError)
|
604
|
+
if selection_result.nil? || !dead_result?(selection_result)
|
605
|
+
value.path ||= path
|
606
|
+
value.ast_node ||= ast_node
|
607
|
+
context.errors << value
|
608
|
+
if selection_result
|
609
|
+
set_result(selection_result, result_name, nil)
|
610
|
+
end
|
611
|
+
end
|
612
|
+
HALT
|
613
|
+
elsif value.is_a?(GraphQL::UnauthorizedError)
|
614
|
+
# this hook might raise & crash, or it might return
|
615
|
+
# a replacement value
|
616
|
+
next_value = begin
|
617
|
+
schema.unauthorized_object(value)
|
618
|
+
rescue GraphQL::ExecutionError => err
|
619
|
+
err
|
620
|
+
end
|
621
|
+
continue_value(path, next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
|
622
|
+
elsif GraphQL::Execution::Execute::SKIP == value
|
623
|
+
# It's possible a lazy was already written here
|
624
|
+
case selection_result
|
625
|
+
when GraphQLResultHash
|
626
|
+
selection_result.delete(result_name)
|
627
|
+
when GraphQLResultArray
|
628
|
+
selection_result.graphql_skip_at(result_name)
|
629
|
+
when nil
|
630
|
+
# this can happen with directives
|
631
|
+
else
|
632
|
+
raise "Invariant: unexpected result class #{selection_result.class} (#{selection_result.inspect})"
|
633
|
+
end
|
634
|
+
HALT
|
635
|
+
else
|
636
|
+
# What could this actually _be_? Anyhow,
|
637
|
+
# preserve the default behavior of doing nothing with it.
|
638
|
+
value
|
274
639
|
end
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
640
|
+
when Array
|
641
|
+
# It's an array full of execution errors; add them all.
|
642
|
+
if value.any? && value.all? { |v| v.is_a?(GraphQL::ExecutionError) }
|
643
|
+
list_type_at_all = (field && (field.type.list?))
|
644
|
+
if selection_result.nil? || !dead_result?(selection_result)
|
645
|
+
value.each_with_index do |error, index|
|
646
|
+
error.ast_node ||= ast_node
|
647
|
+
error.path ||= path + (list_type_at_all ? [index] : [])
|
648
|
+
context.errors << error
|
649
|
+
end
|
650
|
+
if selection_result
|
651
|
+
if list_type_at_all
|
652
|
+
result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
|
653
|
+
set_result(selection_result, result_name, result_without_errors)
|
654
|
+
else
|
655
|
+
set_result(selection_result, result_name, nil)
|
656
|
+
end
|
657
|
+
end
|
658
|
+
end
|
659
|
+
HALT
|
660
|
+
else
|
661
|
+
value
|
284
662
|
end
|
285
|
-
|
286
|
-
|
287
|
-
|
663
|
+
when GraphQL::Execution::Interpreter::RawValue
|
664
|
+
# Write raw value directly to the response without resolving nested objects
|
665
|
+
set_result(selection_result, result_name, value.resolve)
|
288
666
|
HALT
|
289
667
|
else
|
290
668
|
value
|
@@ -299,17 +677,22 @@ module GraphQL
|
|
299
677
|
# Location information from `path` and `ast_node`.
|
300
678
|
#
|
301
679
|
# @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
|
302
|
-
def continue_field(path, value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments) # rubocop:disable Metrics/ParameterLists
|
680
|
+
def continue_field(path, value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
681
|
+
if current_type.non_null?
|
682
|
+
current_type = current_type.of_type
|
683
|
+
is_non_null = true
|
684
|
+
end
|
685
|
+
|
303
686
|
case current_type.kind.name
|
304
687
|
when "SCALAR", "ENUM"
|
305
688
|
r = current_type.coerce_result(value, context)
|
306
|
-
|
689
|
+
set_result(selection_result, result_name, r)
|
307
690
|
r
|
308
691
|
when "UNION", "INTERFACE"
|
309
692
|
resolved_type_or_lazy, resolved_value = resolve_type(current_type, value, path)
|
310
693
|
resolved_value ||= value
|
311
694
|
|
312
|
-
after_lazy(resolved_type_or_lazy, owner: current_type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
|
695
|
+
after_lazy(resolved_type_or_lazy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type|
|
313
696
|
possible_types = query.possible_types(current_type)
|
314
697
|
|
315
698
|
if !possible_types.include?(resolved_type)
|
@@ -317,46 +700,82 @@ module GraphQL
|
|
317
700
|
err_class = current_type::UnresolvedTypeError
|
318
701
|
type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
|
319
702
|
schema.type_error(type_error, context)
|
320
|
-
|
703
|
+
set_result(selection_result, result_name, nil)
|
321
704
|
nil
|
322
705
|
else
|
323
|
-
continue_field(path, resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
|
706
|
+
continue_field(path, resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
|
324
707
|
end
|
325
708
|
end
|
326
709
|
when "OBJECT"
|
327
710
|
object_proxy = begin
|
328
|
-
authorized_new(current_type, value, context
|
711
|
+
authorized_new(current_type, value, context)
|
329
712
|
rescue GraphQL::ExecutionError => err
|
330
713
|
err
|
331
714
|
end
|
332
|
-
after_lazy(object_proxy, owner: current_type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |inner_object|
|
333
|
-
continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node)
|
715
|
+
after_lazy(object_proxy, owner: current_type, path: path, ast_node: ast_node, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
|
716
|
+
continue_value = continue_value(path, inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
|
334
717
|
if HALT != continue_value
|
335
|
-
response_hash =
|
336
|
-
|
337
|
-
|
338
|
-
|
718
|
+
response_hash = GraphQLResultHash.new(result_name, selection_result)
|
719
|
+
set_result(selection_result, result_name, response_hash)
|
720
|
+
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
721
|
+
# There are two possibilities for `gathered_selections`:
|
722
|
+
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
723
|
+
# This case is handled below, and the result can be written right into the main `response_hash` above.
|
724
|
+
# In this case, `gathered_selections` is a hash of selections.
|
725
|
+
# 2. Some selections of this object have runtime directives that may or may not modify execution.
|
726
|
+
# That part of the selection is evaluated in an isolated way, writing into a sub-response object which is
|
727
|
+
# eventually merged into the final response. In this case, `gathered_selections` is an array of things to run in isolation.
|
728
|
+
# (Technically, it's possible that one of those entries _doesn't_ require isolation.)
|
729
|
+
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
730
|
+
if is_selection_array
|
731
|
+
this_result = GraphQLResultHash.new(result_name, selection_result)
|
732
|
+
final_result = response_hash
|
733
|
+
else
|
734
|
+
this_result = response_hash
|
735
|
+
final_result = nil
|
736
|
+
end
|
737
|
+
set_all_interpreter_context(continue_value, nil, nil, path) # reset this mutable state
|
738
|
+
call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
|
739
|
+
evaluate_selections(
|
740
|
+
path,
|
741
|
+
context.scoped_context,
|
742
|
+
continue_value,
|
743
|
+
current_type,
|
744
|
+
false,
|
745
|
+
selections,
|
746
|
+
this_result,
|
747
|
+
final_result,
|
748
|
+
owner_object.object,
|
749
|
+
)
|
750
|
+
this_result
|
751
|
+
end
|
752
|
+
end
|
339
753
|
end
|
340
754
|
end
|
341
755
|
when "LIST"
|
342
|
-
response_list = []
|
343
|
-
write_in_response(path, response_list)
|
344
756
|
inner_type = current_type.of_type
|
757
|
+
# This is true for objects, unions, and interfaces
|
758
|
+
use_dataloader_job = !inner_type.unwrap.kind.input?
|
759
|
+
response_list = GraphQLResultArray.new(result_name, selection_result)
|
760
|
+
response_list.graphql_non_null_list_items = inner_type.non_null?
|
761
|
+
set_result(selection_result, result_name, response_list)
|
762
|
+
|
345
763
|
idx = 0
|
346
764
|
scoped_context = context.scoped_context
|
347
765
|
begin
|
348
766
|
value.each do |inner_value|
|
767
|
+
break if dead_result?(response_list)
|
349
768
|
next_path = path.dup
|
350
769
|
next_path << idx
|
770
|
+
this_idx = idx
|
351
771
|
next_path.freeze
|
352
772
|
idx += 1
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node)
|
357
|
-
if HALT != continue_value
|
358
|
-
continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments)
|
773
|
+
if use_dataloader_job
|
774
|
+
@dataloader.append_job do
|
775
|
+
resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
359
776
|
end
|
777
|
+
else
|
778
|
+
resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
360
779
|
end
|
361
780
|
end
|
362
781
|
rescue NoMethodError => err
|
@@ -371,33 +790,56 @@ module GraphQL
|
|
371
790
|
end
|
372
791
|
|
373
792
|
response_list
|
374
|
-
when "NON_NULL"
|
375
|
-
inner_type = current_type.of_type
|
376
|
-
# Don't `set_type_at_path` because we want the static type,
|
377
|
-
# we're going to use that to determine whether a `nil` should be propagated or not.
|
378
|
-
continue_field(path, value, owner_type, field, inner_type, ast_node, next_selections, true, owner_object, arguments)
|
379
793
|
else
|
380
794
|
raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
|
381
795
|
end
|
382
796
|
end
|
383
797
|
|
384
|
-
def
|
385
|
-
|
386
|
-
|
798
|
+
def resolve_list_item(inner_value, inner_type, next_path, ast_node, scoped_context, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
|
799
|
+
set_all_interpreter_context(nil, nil, nil, next_path)
|
800
|
+
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
801
|
+
# This will update `response_list` with the lazy
|
802
|
+
after_lazy(inner_value, owner: inner_type, path: next_path, ast_node: ast_node, scoped_context: scoped_context, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
|
803
|
+
continue_value = continue_value(next_path, inner_inner_value, owner_type, field, inner_type.non_null?, ast_node, this_idx, response_list)
|
804
|
+
if HALT != continue_value
|
805
|
+
continue_field(next_path, continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
|
806
|
+
end
|
807
|
+
end
|
808
|
+
end
|
809
|
+
end
|
810
|
+
|
811
|
+
def call_method_on_directives(method_name, object, directives, &block)
|
812
|
+
return yield if directives.nil? || directives.empty?
|
813
|
+
run_directive(method_name, object, directives, 0, &block)
|
387
814
|
end
|
388
815
|
|
389
|
-
def run_directive(object,
|
390
|
-
dir_node =
|
816
|
+
def run_directive(method_name, object, directives, idx, &block)
|
817
|
+
dir_node = directives[idx]
|
391
818
|
if !dir_node
|
392
819
|
yield
|
393
820
|
else
|
394
|
-
dir_defn =
|
821
|
+
dir_defn = @schema_directives.fetch(dir_node.name)
|
395
822
|
if !dir_defn.is_a?(Class)
|
396
823
|
dir_defn = dir_defn.type_class || raise("Only class-based directives are supported (not `@#{dir_node.name}`)")
|
397
824
|
end
|
398
|
-
|
399
|
-
|
400
|
-
|
825
|
+
raw_dir_args = arguments(nil, dir_defn, dir_node)
|
826
|
+
dir_args = continue_value(
|
827
|
+
@context[:current_path], # path
|
828
|
+
raw_dir_args, # value
|
829
|
+
dir_defn, # parent_type
|
830
|
+
nil, # field
|
831
|
+
false, # is_non_null
|
832
|
+
dir_node, # ast_node
|
833
|
+
nil, # result_name
|
834
|
+
nil, # selection_result
|
835
|
+
)
|
836
|
+
|
837
|
+
if dir_args == HALT
|
838
|
+
nil
|
839
|
+
else
|
840
|
+
dir_defn.public_send(method_name, object, dir_args, context) do
|
841
|
+
run_directive(method_name, object, directives, idx + 1, &block)
|
842
|
+
end
|
401
843
|
end
|
402
844
|
end
|
403
845
|
end
|
@@ -405,8 +847,8 @@ module GraphQL
|
|
405
847
|
# Check {Schema::Directive.include?} for each directive that's present
|
406
848
|
def directives_include?(node, graphql_object, parent_type)
|
407
849
|
node.directives.each do |dir_node|
|
408
|
-
dir_defn =
|
409
|
-
args = arguments(graphql_object, dir_defn, dir_node)
|
850
|
+
dir_defn = @schema_directives.fetch(dir_node.name).type_class || raise("Only class-based directives are supported (not #{dir_node.name.inspect})")
|
851
|
+
args = arguments(graphql_object, dir_defn, dir_node)
|
410
852
|
if !dir_defn.include?(graphql_object, args, context)
|
411
853
|
return false
|
412
854
|
end
|
@@ -414,57 +856,68 @@ module GraphQL
|
|
414
856
|
true
|
415
857
|
end
|
416
858
|
|
859
|
+
def set_all_interpreter_context(object, field, arguments, path)
|
860
|
+
if object
|
861
|
+
@context[:current_object] = @interpreter_context[:current_object] = object
|
862
|
+
end
|
863
|
+
if field
|
864
|
+
@context[:current_field] = @interpreter_context[:current_field] = field
|
865
|
+
end
|
866
|
+
if arguments
|
867
|
+
@context[:current_arguments] = @interpreter_context[:current_arguments] = arguments
|
868
|
+
end
|
869
|
+
if path
|
870
|
+
@context[:current_path] = @interpreter_context[:current_path] = path
|
871
|
+
end
|
872
|
+
end
|
873
|
+
|
417
874
|
# @param obj [Object] Some user-returned value that may want to be batched
|
418
875
|
# @param path [Array<String>]
|
419
876
|
# @param field [GraphQL::Schema::Field]
|
420
877
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
421
878
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
422
879
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
423
|
-
def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, eager: false, trace: true, &block)
|
424
|
-
|
425
|
-
set_interpreter_context(:current_arguments, arguments)
|
426
|
-
set_interpreter_context(:current_path, path)
|
427
|
-
set_interpreter_context(:current_field, field)
|
428
|
-
if schema.lazy?(lazy_obj)
|
880
|
+
def after_lazy(lazy_obj, owner:, field:, path:, scoped_context:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
|
881
|
+
if lazy?(lazy_obj)
|
429
882
|
lazy = GraphQL::Execution::Lazy.new(path: path, field: field) do
|
430
|
-
|
431
|
-
set_interpreter_context(:current_field, field)
|
432
|
-
set_interpreter_context(:current_object, owner_object)
|
433
|
-
set_interpreter_context(:current_arguments, arguments)
|
883
|
+
set_all_interpreter_context(owner_object, field, arguments, path)
|
434
884
|
context.scoped_context = scoped_context
|
435
885
|
# Wrap the execution of _this_ method with tracing,
|
436
886
|
# but don't wrap the continuation below
|
437
887
|
inner_obj = begin
|
438
888
|
query.with_error_handling do
|
439
|
-
|
440
|
-
|
889
|
+
begin
|
890
|
+
if trace
|
891
|
+
query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments, ast_node: ast_node}) do
|
892
|
+
schema.sync_lazy(lazy_obj)
|
893
|
+
end
|
894
|
+
else
|
441
895
|
schema.sync_lazy(lazy_obj)
|
442
896
|
end
|
443
|
-
|
444
|
-
|
897
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
|
898
|
+
err
|
445
899
|
end
|
446
900
|
end
|
447
|
-
|
448
|
-
|
901
|
+
rescue GraphQL::ExecutionError => ex_err
|
902
|
+
ex_err
|
449
903
|
end
|
450
|
-
|
904
|
+
yield(inner_obj)
|
451
905
|
end
|
452
906
|
|
453
907
|
if eager
|
454
908
|
lazy.value
|
455
909
|
else
|
456
|
-
|
910
|
+
set_result(result, result_name, lazy)
|
457
911
|
lazy
|
458
912
|
end
|
459
913
|
else
|
914
|
+
set_all_interpreter_context(owner_object, field, arguments, path)
|
460
915
|
yield(lazy_obj)
|
461
916
|
end
|
462
917
|
end
|
463
918
|
|
464
919
|
def arguments(graphql_object, arg_owner, ast_node)
|
465
|
-
|
466
|
-
if arg_owner.arguments_statically_coercible? &&
|
467
|
-
(!arg_owner.is_a?(GraphQL::Schema::Field) || (arg_owner.extras.empty? && arg_owner.extensions.empty?))
|
920
|
+
if arg_owner.arguments_statically_coercible?
|
468
921
|
query.arguments_for(ast_node, arg_owner)
|
469
922
|
else
|
470
923
|
# The arguments must be prepared in the context of the given object
|
@@ -472,75 +925,6 @@ module GraphQL
|
|
472
925
|
end
|
473
926
|
end
|
474
927
|
|
475
|
-
def write_invalid_null_in_response(path, invalid_null_error)
|
476
|
-
if !dead_path?(path)
|
477
|
-
schema.type_error(invalid_null_error, context)
|
478
|
-
write_in_response(path, nil)
|
479
|
-
add_dead_path(path)
|
480
|
-
end
|
481
|
-
end
|
482
|
-
|
483
|
-
def write_execution_errors_in_response(path, errors)
|
484
|
-
if !dead_path?(path)
|
485
|
-
errors.each do |v|
|
486
|
-
context.errors << v
|
487
|
-
end
|
488
|
-
write_in_response(path, nil)
|
489
|
-
add_dead_path(path)
|
490
|
-
end
|
491
|
-
end
|
492
|
-
|
493
|
-
def write_in_response(path, value)
|
494
|
-
if dead_path?(path)
|
495
|
-
return
|
496
|
-
else
|
497
|
-
if value.nil? && path.any? && type_at(path).non_null?
|
498
|
-
# This nil is invalid, try writing it at the previous spot
|
499
|
-
propagate_path = path[0..-2]
|
500
|
-
write_in_response(propagate_path, value)
|
501
|
-
add_dead_path(propagate_path)
|
502
|
-
else
|
503
|
-
@response.write(path, value)
|
504
|
-
end
|
505
|
-
end
|
506
|
-
end
|
507
|
-
|
508
|
-
# To propagate nulls, we have to know what the field type was
|
509
|
-
# at previous parts of the response.
|
510
|
-
# This hash matches the response
|
511
|
-
def type_at(path)
|
512
|
-
@types_at_paths.fetch(path)
|
513
|
-
end
|
514
|
-
|
515
|
-
def set_type_at_path(path, type)
|
516
|
-
@types_at_paths[path] = type
|
517
|
-
nil
|
518
|
-
end
|
519
|
-
|
520
|
-
# Mark `path` as having been permanently nulled out.
|
521
|
-
# No values will be added beyond that path.
|
522
|
-
def add_dead_path(path)
|
523
|
-
dead = @dead_paths
|
524
|
-
path.each do |part|
|
525
|
-
dead = dead[part] ||= {}
|
526
|
-
end
|
527
|
-
dead[:__dead] = true
|
528
|
-
end
|
529
|
-
|
530
|
-
def dead_path?(path)
|
531
|
-
res = @dead_paths
|
532
|
-
path.each do |part|
|
533
|
-
if res
|
534
|
-
if res[:__dead]
|
535
|
-
break
|
536
|
-
else
|
537
|
-
res = res[part]
|
538
|
-
end
|
539
|
-
end
|
540
|
-
end
|
541
|
-
res && res[:__dead]
|
542
|
-
end
|
543
|
-
|
544
928
|
# Set this pair in the Query context, but also in the interpeter namespace,
|
545
929
|
# for compatibility.
|
546
930
|
def set_interpreter_context(key, value)
|
@@ -559,7 +943,7 @@ module GraphQL
|
|
559
943
|
query.resolve_type(type, value)
|
560
944
|
end
|
561
945
|
|
562
|
-
if
|
946
|
+
if lazy?(resolved_type)
|
563
947
|
GraphQL::Execution::Lazy.new do
|
564
948
|
query.trace("resolve_type_lazy", trace_payload) do
|
565
949
|
schema.sync_lazy(resolved_type)
|
@@ -570,22 +954,14 @@ module GraphQL
|
|
570
954
|
end
|
571
955
|
end
|
572
956
|
|
573
|
-
def authorized_new(type, value, context
|
574
|
-
|
575
|
-
|
576
|
-
auth_val = context.query.trace("authorized", trace_payload) do
|
577
|
-
type.authorized_new(value, context)
|
578
|
-
end
|
957
|
+
def authorized_new(type, value, context)
|
958
|
+
type.authorized_new(value, context)
|
959
|
+
end
|
579
960
|
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
end
|
585
|
-
end
|
586
|
-
else
|
587
|
-
auth_val
|
588
|
-
end
|
961
|
+
def lazy?(object)
|
962
|
+
@lazy_cache.fetch(object.class) {
|
963
|
+
@lazy_cache[object.class] = @schema.lazy?(object)
|
964
|
+
}
|
589
965
|
end
|
590
966
|
end
|
591
967
|
end
|