graphql 1.10.2 → 2.0.21
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 +21 -10
- 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/{templates → install/templates}/base_mutation.erb +2 -0
- data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +45 -8
- 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 +28 -12
- data/lib/generators/graphql/orm_mutations_base.rb +40 -0
- data/lib/generators/graphql/relay.rb +49 -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_argument.erb +2 -0
- 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/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +7 -1
- data/lib/generators/graphql/templates/graphql_controller.erb +16 -12
- data/lib/generators/graphql/templates/input.erb +9 -0
- data/lib/generators/graphql/templates/interface.erb +6 -2
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +3 -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 +7 -3
- data/lib/generators/graphql/templates/query_type.erb +3 -3
- data/lib/generators/graphql/templates/scalar.erb +5 -1
- data/lib/generators/graphql/templates/schema.erb +24 -33
- data/lib/generators/graphql/templates/union.erb +6 -2
- data/lib/generators/graphql/type_generator.rb +47 -10
- data/lib/generators/graphql/union_generator.rb +5 -5
- data/lib/graphql/analysis/ast/field_usage.rb +30 -1
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +125 -117
- data/lib/graphql/analysis/ast/query_depth.rb +0 -1
- data/lib/graphql/analysis/ast/visitor.rb +52 -36
- data/lib/graphql/analysis/ast.rb +7 -8
- data/lib/graphql/analysis.rb +0 -7
- data/lib/graphql/backtrace/inspect_result.rb +0 -1
- data/lib/graphql/backtrace/table.rb +31 -18
- data/lib/graphql/backtrace/trace.rb +96 -0
- data/lib/graphql/backtrace/traced_error.rb +0 -1
- data/lib/graphql/backtrace/tracer.rb +39 -9
- data/lib/graphql/backtrace.rb +26 -18
- data/lib/graphql/dataloader/null_dataloader.rb +24 -0
- data/lib/graphql/dataloader/request.rb +19 -0
- data/lib/graphql/dataloader/request_all.rb +19 -0
- data/lib/graphql/dataloader/source.rb +164 -0
- data/lib/graphql/dataloader.rb +311 -0
- data/lib/graphql/date_encoding_error.rb +16 -0
- data/lib/graphql/deprecation.rb +9 -0
- data/lib/graphql/dig.rb +1 -1
- data/lib/graphql/execution/errors.rb +77 -44
- data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
- data/lib/graphql/execution/interpreter/arguments.rb +88 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +104 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
- data/lib/graphql/execution/interpreter/resolve.rb +62 -24
- data/lib/graphql/execution/interpreter/runtime.rb +826 -464
- data/lib/graphql/execution/interpreter.rb +206 -68
- data/lib/graphql/execution/lazy.rb +11 -21
- data/lib/graphql/execution/lookahead.rb +55 -136
- data/lib/graphql/execution/multiplex.rb +6 -162
- data/lib/graphql/execution.rb +11 -4
- data/lib/graphql/filter.rb +7 -2
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/integer_encoding_error.rb +18 -2
- data/lib/graphql/introspection/directive_location_enum.rb +2 -2
- data/lib/graphql/introspection/directive_type.rb +11 -5
- data/lib/graphql/introspection/dynamic_fields.rb +3 -8
- data/lib/graphql/introspection/entry_points.rb +4 -17
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +9 -5
- data/lib/graphql/introspection/input_value_type.rb +15 -3
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/schema_type.rb +11 -6
- data/lib/graphql/introspection/type_type.rb +31 -14
- data/lib/graphql/introspection.rb +100 -0
- data/lib/graphql/invalid_null_error.rb +18 -0
- data/lib/graphql/language/block_string.rb +20 -5
- data/lib/graphql/language/cache.rb +37 -0
- data/lib/graphql/language/document_from_schema_definition.rb +96 -44
- data/lib/graphql/language/lexer.rb +216 -1462
- data/lib/graphql/language/nodes.rb +126 -129
- data/lib/graphql/language/parser.rb +997 -933
- data/lib/graphql/language/parser.y +148 -118
- data/lib/graphql/language/printer.rb +48 -23
- data/lib/graphql/language/sanitized_printer.rb +222 -0
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/language/visitor.rb +192 -84
- data/lib/graphql/language.rb +2 -0
- data/lib/graphql/name_validator.rb +2 -7
- data/lib/graphql/pagination/active_record_relation_connection.rb +45 -3
- data/lib/graphql/pagination/array_connection.rb +6 -4
- data/lib/graphql/pagination/connection.rb +105 -23
- data/lib/graphql/pagination/connections.rb +62 -35
- data/lib/graphql/pagination/relation_connection.rb +88 -36
- data/lib/graphql/parse_error.rb +0 -1
- data/lib/graphql/query/context.rb +203 -198
- data/lib/graphql/query/fingerprint.rb +26 -0
- data/lib/graphql/query/input_validation_result.rb +33 -7
- data/lib/graphql/query/null_context.rb +22 -9
- data/lib/graphql/query/validation_pipeline.rb +16 -38
- data/lib/graphql/query/variable_validation_error.rb +3 -3
- data/lib/graphql/query/variables.rb +36 -12
- data/lib/graphql/query.rb +92 -44
- data/lib/graphql/railtie.rb +6 -102
- data/lib/graphql/rake_task/validate.rb +1 -1
- data/lib/graphql/rake_task.rb +41 -10
- data/lib/graphql/relay/range_add.rb +17 -10
- data/lib/graphql/relay.rb +0 -15
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/schema/addition.rb +245 -0
- data/lib/graphql/schema/argument.rb +250 -46
- data/lib/graphql/schema/base_64_encoder.rb +2 -0
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
- data/lib/graphql/schema/build_from_definition.rb +243 -89
- data/lib/graphql/schema/directive/deprecated.rb +1 -1
- 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/one_of.rb +12 -0
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +14 -2
- data/lib/graphql/schema/directive.rb +108 -20
- data/lib/graphql/schema/enum.rb +105 -44
- data/lib/graphql/schema/enum_value.rb +15 -25
- data/lib/graphql/schema/field/connection_extension.rb +50 -30
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +476 -331
- data/lib/graphql/schema/field_extension.rb +86 -2
- data/lib/graphql/schema/find_inherited_value.rb +6 -8
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +133 -121
- data/lib/graphql/schema/interface.rb +17 -45
- data/lib/graphql/schema/introspection_system.rb +3 -8
- data/lib/graphql/schema/late_bound_type.rb +8 -2
- data/lib/graphql/schema/list.rb +25 -8
- data/lib/graphql/schema/loader.rb +139 -103
- data/lib/graphql/schema/member/base_dsl_methods.rb +29 -35
- data/lib/graphql/schema/member/build_type.rb +19 -14
- data/lib/graphql/schema/member/has_arguments.rb +310 -26
- data/lib/graphql/schema/member/has_ast_node.rb +16 -1
- data/lib/graphql/schema/member/has_deprecation_reason.rb +24 -0
- data/lib/graphql/schema/member/has_directives.rb +118 -0
- data/lib/graphql/schema/member/has_fields.rb +164 -42
- data/lib/graphql/schema/member/has_interfaces.rb +129 -0
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
- data/lib/graphql/schema/member/has_validators.rb +57 -0
- data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +20 -3
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/member.rb +6 -6
- data/lib/graphql/schema/mutation.rb +4 -9
- data/lib/graphql/schema/non_null.rb +12 -7
- data/lib/graphql/schema/object.rb +35 -69
- data/lib/graphql/schema/printer.rb +16 -34
- data/lib/graphql/schema/relay_classic_mutation.rb +90 -43
- data/lib/graphql/schema/resolver/has_payload_type.rb +51 -11
- data/lib/graphql/schema/resolver.rb +144 -79
- data/lib/graphql/schema/scalar.rb +27 -18
- data/lib/graphql/schema/subscription.rb +55 -26
- data/lib/graphql/schema/timeout.rb +45 -35
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +21 -4
- data/lib/graphql/schema/union.rb +48 -13
- data/lib/graphql/schema/unique_within_type.rb +1 -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 +185 -32
- data/lib/graphql/schema/wrapper.rb +0 -5
- data/lib/graphql/schema.rb +471 -1116
- data/lib/graphql/static_validation/all_rules.rb +3 -0
- data/lib/graphql/static_validation/base_visitor.rb +13 -27
- data/lib/graphql/static_validation/definition_dependencies.rb +7 -2
- data/lib/graphql/static_validation/error.rb +3 -1
- data/lib/graphql/static_validation/literal_validator.rb +69 -26
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +44 -87
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +22 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +28 -22
- data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +12 -6
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +13 -13
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +92 -49
- 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/one_of_input_objects_are_valid.rb +66 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -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 +6 -7
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +13 -7
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +9 -10
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +14 -8
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +4 -2
- data/lib/graphql/static_validation/validation_context.rb +13 -3
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +32 -20
- data/lib/graphql/static_validation.rb +1 -2
- data/lib/graphql/string_encoding_error.rb +13 -3
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +126 -19
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +58 -0
- data/lib/graphql/subscriptions/event.rb +81 -35
- data/lib/graphql/subscriptions/instrumentation.rb +0 -52
- data/lib/graphql/subscriptions/serialize.rb +53 -6
- data/lib/graphql/subscriptions.rb +113 -58
- data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +8 -21
- data/lib/graphql/tracing/appoptics_trace.rb +231 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
- data/lib/graphql/tracing/appsignal_trace.rb +77 -0
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- data/lib/graphql/tracing/data_dog_trace.rb +148 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +26 -2
- data/lib/graphql/tracing/legacy_trace.rb +65 -0
- data/lib/graphql/tracing/new_relic_trace.rb +75 -0
- data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
- data/lib/graphql/tracing/notifications_trace.rb +42 -0
- data/lib/graphql/tracing/notifications_tracing.rb +59 -0
- data/lib/graphql/tracing/platform_trace.rb +109 -0
- data/lib/graphql/tracing/platform_tracing.rb +64 -43
- data/lib/graphql/tracing/prometheus_trace.rb +89 -0
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +5 -2
- data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
- data/lib/graphql/tracing/scout_trace.rb +72 -0
- data/lib/graphql/tracing/scout_tracing.rb +11 -0
- data/lib/graphql/tracing/statsd_trace.rb +56 -0
- data/lib/graphql/tracing/statsd_tracing.rb +42 -0
- data/lib/graphql/tracing/trace.rb +75 -0
- data/lib/graphql/tracing.rb +23 -71
- data/lib/graphql/type_kinds.rb +6 -3
- 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 +20 -9
- data/lib/graphql/types/iso_8601_date_time.rb +36 -10
- data/lib/graphql/types/relay/base_connection.rb +18 -92
- data/lib/graphql/types/relay/base_edge.rb +2 -34
- data/lib/graphql/types/relay/connection_behaviors.rb +176 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +75 -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 +25 -0
- data/lib/graphql/types/relay/page_info.rb +2 -14
- data/lib/graphql/types/relay/page_info_behaviors.rb +30 -0
- data/lib/graphql/types/relay.rb +10 -5
- data/lib/graphql/types/string.rb +8 -2
- data/lib/graphql/unauthorized_error.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +54 -65
- data/readme.md +3 -6
- metadata +116 -236
- data/lib/graphql/analysis/analyze_query.rb +0 -91
- data/lib/graphql/analysis/field_usage.rb +0 -45
- data/lib/graphql/analysis/max_query_complexity.rb +0 -26
- data/lib/graphql/analysis/max_query_depth.rb +0 -26
- data/lib/graphql/analysis/query_complexity.rb +0 -88
- data/lib/graphql/analysis/query_depth.rb +0 -43
- data/lib/graphql/analysis/reducer_state.rb +0 -48
- data/lib/graphql/argument.rb +0 -131
- data/lib/graphql/authorization.rb +0 -82
- data/lib/graphql/backwards_compatibility.rb +0 -60
- data/lib/graphql/base_type.rb +0 -230
- data/lib/graphql/boolean_type.rb +0 -2
- data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
- data/lib/graphql/compatibility/execution_specification.rb +0 -435
- data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
- data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -213
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
- data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
- data/lib/graphql/compatibility/query_parser_specification.rb +0 -264
- data/lib/graphql/compatibility/schema_parser_specification.rb +0 -680
- data/lib/graphql/compatibility.rb +0 -5
- data/lib/graphql/define/assign_argument.rb +0 -12
- data/lib/graphql/define/assign_connection.rb +0 -13
- data/lib/graphql/define/assign_enum_value.rb +0 -18
- data/lib/graphql/define/assign_global_id_field.rb +0 -11
- data/lib/graphql/define/assign_mutation_function.rb +0 -34
- data/lib/graphql/define/assign_object_field.rb +0 -42
- data/lib/graphql/define/defined_object_proxy.rb +0 -53
- data/lib/graphql/define/instance_definable.rb +0 -210
- data/lib/graphql/define/no_definition_error.rb +0 -7
- data/lib/graphql/define/non_null_with_bang.rb +0 -16
- data/lib/graphql/define/type_definer.rb +0 -31
- data/lib/graphql/define.rb +0 -31
- data/lib/graphql/deprecated_dsl.rb +0 -42
- data/lib/graphql/directive/deprecated_directive.rb +0 -2
- data/lib/graphql/directive/include_directive.rb +0 -2
- data/lib/graphql/directive/skip_directive.rb +0 -2
- data/lib/graphql/directive.rb +0 -107
- data/lib/graphql/enum_type.rb +0 -127
- data/lib/graphql/execution/execute.rb +0 -326
- data/lib/graphql/execution/flatten.rb +0 -40
- data/lib/graphql/execution/instrumentation.rb +0 -92
- data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
- data/lib/graphql/execution/lazy/resolve.rb +0 -91
- data/lib/graphql/execution/typecast.rb +0 -50
- data/lib/graphql/field/resolve.rb +0 -59
- data/lib/graphql/field.rb +0 -222
- data/lib/graphql/float_type.rb +0 -2
- data/lib/graphql/function.rb +0 -124
- data/lib/graphql/id_type.rb +0 -2
- data/lib/graphql/input_object_type.rb +0 -132
- data/lib/graphql/int_type.rb +0 -2
- data/lib/graphql/interface_type.rb +0 -65
- data/lib/graphql/internal_representation/document.rb +0 -27
- data/lib/graphql/internal_representation/node.rb +0 -206
- data/lib/graphql/internal_representation/print.rb +0 -51
- data/lib/graphql/internal_representation/rewrite.rb +0 -184
- data/lib/graphql/internal_representation/scope.rb +0 -88
- data/lib/graphql/internal_representation/visit.rb +0 -36
- data/lib/graphql/internal_representation.rb +0 -7
- data/lib/graphql/language/lexer.rl +0 -258
- data/lib/graphql/list_type.rb +0 -80
- data/lib/graphql/literal_validation_error.rb +0 -6
- data/lib/graphql/non_null_type.rb +0 -71
- data/lib/graphql/object_type.rb +0 -121
- data/lib/graphql/query/arguments.rb +0 -188
- data/lib/graphql/query/arguments_cache.rb +0 -25
- data/lib/graphql/query/executor.rb +0 -53
- data/lib/graphql/query/literal_input.rb +0 -136
- data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
- data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
- data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
- data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
- data/lib/graphql/query/serial_execution.rb +0 -39
- data/lib/graphql/relay/array_connection.rb +0 -85
- data/lib/graphql/relay/base_connection.rb +0 -176
- data/lib/graphql/relay/connection_instrumentation.rb +0 -54
- data/lib/graphql/relay/connection_resolve.rb +0 -43
- data/lib/graphql/relay/connection_type.rb +0 -41
- data/lib/graphql/relay/edge.rb +0 -27
- data/lib/graphql/relay/edge_type.rb +0 -19
- data/lib/graphql/relay/edges_instrumentation.rb +0 -40
- data/lib/graphql/relay/global_id_resolve.rb +0 -18
- data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
- data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
- data/lib/graphql/relay/mutation/resolve.rb +0 -56
- data/lib/graphql/relay/mutation/result.rb +0 -38
- data/lib/graphql/relay/mutation.rb +0 -105
- data/lib/graphql/relay/node.rb +0 -36
- data/lib/graphql/relay/page_info.rb +0 -7
- data/lib/graphql/relay/relation_connection.rb +0 -190
- data/lib/graphql/relay/type_extensions.rb +0 -30
- data/lib/graphql/scalar_type.rb +0 -76
- data/lib/graphql/schema/catchall_middleware.rb +0 -35
- data/lib/graphql/schema/default_parse_error.rb +0 -10
- data/lib/graphql/schema/default_type_error.rb +0 -15
- data/lib/graphql/schema/member/accepts_definition.rb +0 -152
- data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -31
- data/lib/graphql/schema/member/instrumentation.rb +0 -132
- data/lib/graphql/schema/middleware_chain.rb +0 -82
- data/lib/graphql/schema/possible_types.rb +0 -39
- data/lib/graphql/schema/rescue_middleware.rb +0 -60
- data/lib/graphql/schema/timeout_middleware.rb +0 -86
- data/lib/graphql/schema/traversal.rb +0 -228
- data/lib/graphql/schema/validation.rb +0 -303
- data/lib/graphql/static_validation/default_visitor.rb +0 -15
- data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
- data/lib/graphql/string_type.rb +0 -2
- data/lib/graphql/subscriptions/subscription_root.rb +0 -65
- data/lib/graphql/tracing/skylight_tracing.rb +0 -70
- 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/graphql/types/relay/node_field.rb +0 -43
- data/lib/graphql/types/relay/nodes_field.rb +0 -45
- data/lib/graphql/union_type.rb +0 -113
- data/lib/graphql/upgrader/member.rb +0 -936
- data/lib/graphql/upgrader/schema.rb +0 -37
@@ -8,6 +8,191 @@ module GraphQL
|
|
8
8
|
#
|
9
9
|
# @api private
|
10
10
|
class Runtime
|
11
|
+
class CurrentState
|
12
|
+
def initialize
|
13
|
+
@current_object = nil
|
14
|
+
@current_field = nil
|
15
|
+
@current_arguments = nil
|
16
|
+
@current_result_name = nil
|
17
|
+
@current_result = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_accessor :current_result, :current_result_name,
|
21
|
+
:current_arguments, :current_field, :current_object
|
22
|
+
end
|
23
|
+
|
24
|
+
module GraphQLResult
|
25
|
+
def initialize(result_name, parent_result)
|
26
|
+
@graphql_parent = parent_result
|
27
|
+
if parent_result && parent_result.graphql_dead
|
28
|
+
@graphql_dead = true
|
29
|
+
end
|
30
|
+
@graphql_result_name = result_name
|
31
|
+
# Jump through some hoops to avoid creating this duplicate storage if at all possible.
|
32
|
+
@graphql_metadata = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def path
|
36
|
+
@path ||= build_path([])
|
37
|
+
end
|
38
|
+
|
39
|
+
def build_path(path_array)
|
40
|
+
graphql_result_name && path_array.unshift(graphql_result_name)
|
41
|
+
@graphql_parent ? @graphql_parent.build_path(path_array) : path_array
|
42
|
+
end
|
43
|
+
|
44
|
+
attr_accessor :graphql_dead
|
45
|
+
attr_reader :graphql_parent, :graphql_result_name
|
46
|
+
|
47
|
+
# Although these are used by only one of the Result classes,
|
48
|
+
# it's handy to have the methods implemented on both (even though they just return `nil`)
|
49
|
+
# because it makes it easy to check if anything is assigned.
|
50
|
+
# @return [nil, Array<String>]
|
51
|
+
attr_accessor :graphql_non_null_field_names
|
52
|
+
# @return [nil, true]
|
53
|
+
attr_accessor :graphql_non_null_list_items
|
54
|
+
|
55
|
+
# @return [Hash] Plain-Ruby result data (`@graphql_metadata` contains Result wrapper objects)
|
56
|
+
attr_accessor :graphql_result_data
|
57
|
+
end
|
58
|
+
|
59
|
+
class GraphQLResultHash
|
60
|
+
def initialize(_result_name, _parent_result)
|
61
|
+
super
|
62
|
+
@graphql_result_data = {}
|
63
|
+
end
|
64
|
+
|
65
|
+
include GraphQLResult
|
66
|
+
|
67
|
+
attr_accessor :graphql_merged_into
|
68
|
+
|
69
|
+
def set_leaf(key, value)
|
70
|
+
# This is a hack.
|
71
|
+
# Basically, this object is merged into the root-level result at some point.
|
72
|
+
# But the problem is, some lazies are created whose closures retain reference to _this_
|
73
|
+
# object. When those lazies are resolved, they cause an update to this object.
|
74
|
+
#
|
75
|
+
# In order to return a proper top-level result, we have to update that top-level result object.
|
76
|
+
# In order to return a proper partial result (eg, for a directive), we have to update this object, too.
|
77
|
+
# Yowza.
|
78
|
+
if (t = @graphql_merged_into)
|
79
|
+
t.set_leaf(key, value)
|
80
|
+
end
|
81
|
+
|
82
|
+
@graphql_result_data[key] = value
|
83
|
+
# keep this up-to-date if it's been initialized
|
84
|
+
@graphql_metadata && @graphql_metadata[key] = value
|
85
|
+
|
86
|
+
value
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_child_result(key, value)
|
90
|
+
if (t = @graphql_merged_into)
|
91
|
+
t.set_child_result(key, value)
|
92
|
+
end
|
93
|
+
@graphql_result_data[key] = value.graphql_result_data
|
94
|
+
# If we encounter some part of this response that requires metadata tracking,
|
95
|
+
# then create the metadata hash if necessary. It will be kept up-to-date after this.
|
96
|
+
(@graphql_metadata ||= @graphql_result_data.dup)[key] = value
|
97
|
+
value
|
98
|
+
end
|
99
|
+
|
100
|
+
def delete(key)
|
101
|
+
@graphql_metadata && @graphql_metadata.delete(key)
|
102
|
+
@graphql_result_data.delete(key)
|
103
|
+
end
|
104
|
+
|
105
|
+
def each
|
106
|
+
(@graphql_metadata || @graphql_result_data).each { |k, v| yield(k, v) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def values
|
110
|
+
(@graphql_metadata || @graphql_result_data).values
|
111
|
+
end
|
112
|
+
|
113
|
+
def key?(k)
|
114
|
+
@graphql_result_data.key?(k)
|
115
|
+
end
|
116
|
+
|
117
|
+
def [](k)
|
118
|
+
(@graphql_metadata || @graphql_result_data)[k]
|
119
|
+
end
|
120
|
+
|
121
|
+
def merge_into(into_result)
|
122
|
+
self.each do |key, value|
|
123
|
+
case value
|
124
|
+
when GraphQLResultHash
|
125
|
+
next_into = into_result[key]
|
126
|
+
if next_into
|
127
|
+
value.merge_into(next_into)
|
128
|
+
else
|
129
|
+
into_result.set_child_result(key, value)
|
130
|
+
end
|
131
|
+
when GraphQLResultArray
|
132
|
+
# There's no special handling of arrays because currently, there's no way to split the execution
|
133
|
+
# of a list over several concurrent flows.
|
134
|
+
next_result.set_child_result(key, value)
|
135
|
+
else
|
136
|
+
# We have to assume that, since this passed the `fields_will_merge` selection,
|
137
|
+
# that the old and new values are the same.
|
138
|
+
into_result.set_leaf(key, value)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
@graphql_merged_into = into_result
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class GraphQLResultArray
|
146
|
+
include GraphQLResult
|
147
|
+
|
148
|
+
def initialize(_result_name, _parent_result)
|
149
|
+
super
|
150
|
+
@graphql_result_data = []
|
151
|
+
end
|
152
|
+
|
153
|
+
def graphql_skip_at(index)
|
154
|
+
# Mark this index as dead. It's tricky because some indices may already be storing
|
155
|
+
# `Lazy`s. So the runtime is still holding indexes _before_ skipping,
|
156
|
+
# this object has to coordinate incoming writes to account for any already-skipped indices.
|
157
|
+
@skip_indices ||= []
|
158
|
+
@skip_indices << index
|
159
|
+
offset_by = @skip_indices.count { |skipped_idx| skipped_idx < index}
|
160
|
+
delete_at_index = index - offset_by
|
161
|
+
@graphql_metadata && @graphql_metadata.delete_at(delete_at_index)
|
162
|
+
@graphql_result_data.delete_at(delete_at_index)
|
163
|
+
end
|
164
|
+
|
165
|
+
def set_leaf(idx, value)
|
166
|
+
if @skip_indices
|
167
|
+
offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
|
168
|
+
idx -= offset_by
|
169
|
+
end
|
170
|
+
@graphql_result_data[idx] = value
|
171
|
+
@graphql_metadata && @graphql_metadata[idx] = value
|
172
|
+
value
|
173
|
+
end
|
174
|
+
|
175
|
+
def set_child_result(idx, value)
|
176
|
+
if @skip_indices
|
177
|
+
offset_by = @skip_indices.count { |skipped_idx| skipped_idx < idx }
|
178
|
+
idx -= offset_by
|
179
|
+
end
|
180
|
+
@graphql_result_data[idx] = value.graphql_result_data
|
181
|
+
# If we encounter some part of this response that requires metadata tracking,
|
182
|
+
# then create the metadata hash if necessary. It will be kept up-to-date after this.
|
183
|
+
(@graphql_metadata ||= @graphql_result_data.dup)[idx] = value
|
184
|
+
value
|
185
|
+
end
|
186
|
+
|
187
|
+
def values
|
188
|
+
(@graphql_metadata || @graphql_result_data)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class GraphQLSelectionSet < Hash
|
193
|
+
attr_accessor :graphql_directives
|
194
|
+
end
|
195
|
+
|
11
196
|
# @return [GraphQL::Query]
|
12
197
|
attr_reader :query
|
13
198
|
|
@@ -17,60 +202,118 @@ module GraphQL
|
|
17
202
|
# @return [GraphQL::Query::Context]
|
18
203
|
attr_reader :context
|
19
204
|
|
20
|
-
def initialize(query:,
|
205
|
+
def initialize(query:, lazies_at_depth:)
|
21
206
|
@query = query
|
207
|
+
@dataloader = query.multiplex.dataloader
|
208
|
+
@lazies_at_depth = lazies_at_depth
|
22
209
|
@schema = query.schema
|
23
210
|
@context = query.context
|
24
|
-
@
|
25
|
-
|
26
|
-
|
27
|
-
@
|
211
|
+
@multiplex_context = query.multiplex.context
|
212
|
+
# Start this off empty:
|
213
|
+
Thread.current[:__graphql_runtime_info] = nil
|
214
|
+
@response = GraphQLResultHash.new(nil, nil)
|
215
|
+
# Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
|
216
|
+
@runtime_directive_names = []
|
217
|
+
noop_resolve_owner = GraphQL::Schema::Directive.singleton_class
|
218
|
+
@schema_directives = schema.directives
|
219
|
+
@schema_directives.each do |name, dir_defn|
|
220
|
+
if dir_defn.method(:resolve).owner != noop_resolve_owner
|
221
|
+
@runtime_directive_names << name
|
222
|
+
end
|
223
|
+
end
|
28
224
|
# A cache of { Class => { String => Schema::Field } }
|
29
225
|
# Which assumes that MyObject.get_field("myField") will return the same field
|
30
226
|
# during the lifetime of a query
|
31
227
|
@fields_cache = Hash.new { |h, k| h[k] = {} }
|
228
|
+
# { Class => Boolean }
|
229
|
+
@lazy_cache = {}
|
32
230
|
end
|
33
231
|
|
34
|
-
def
|
35
|
-
@response.
|
232
|
+
def final_result
|
233
|
+
@response && @response.graphql_result_data
|
36
234
|
end
|
37
235
|
|
38
236
|
def inspect
|
39
237
|
"#<#{self.class.name} response=#{@response.inspect}>"
|
40
238
|
end
|
41
239
|
|
240
|
+
def tap_or_each(obj_or_array)
|
241
|
+
if obj_or_array.is_a?(Array)
|
242
|
+
obj_or_array.each do |item|
|
243
|
+
yield(item, true)
|
244
|
+
end
|
245
|
+
else
|
246
|
+
yield(obj_or_array, false)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
42
250
|
# This _begins_ the execution. Some deferred work
|
43
251
|
# might be stored up in lazies.
|
44
252
|
# @return [void]
|
45
253
|
def run_eager
|
46
|
-
|
47
254
|
root_operation = query.selected_operation
|
48
255
|
root_op_type = root_operation.operation_type || "query"
|
49
256
|
root_type = schema.root_type_for_operation(root_op_type)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
object_proxy = authorized_new(root_type, query.root_value, context
|
257
|
+
st = get_current_runtime_state
|
258
|
+
st.current_object = query.root_value
|
259
|
+
st.current_result = @response
|
260
|
+
object_proxy = authorized_new(root_type, query.root_value, context)
|
54
261
|
object_proxy = schema.sync_lazy(object_proxy)
|
262
|
+
|
55
263
|
if object_proxy.nil?
|
56
264
|
# Root .authorized? returned false.
|
57
|
-
|
58
|
-
nil
|
265
|
+
@response = nil
|
59
266
|
else
|
60
|
-
|
61
|
-
|
267
|
+
call_method_on_directives(:resolve, object_proxy, root_operation.directives) do # execute query level directives
|
268
|
+
gathered_selections = gather_selections(object_proxy, root_type, root_operation.selections)
|
269
|
+
# This is kind of a hack -- `gathered_selections` is an Array if any of the selections
|
270
|
+
# require isolation during execution (because of runtime directives). In that case,
|
271
|
+
# make a new, isolated result hash for writing the result into. (That isolated response
|
272
|
+
# is eventually merged back into the main response)
|
273
|
+
#
|
274
|
+
# Otherwise, `gathered_selections` is a hash of selections which can be
|
275
|
+
# directly evaluated and the results can be written right into the main response hash.
|
276
|
+
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
277
|
+
if is_selection_array
|
278
|
+
selection_response = GraphQLResultHash.new(nil, nil)
|
279
|
+
final_response = @response
|
280
|
+
else
|
281
|
+
selection_response = @response
|
282
|
+
final_response = nil
|
283
|
+
end
|
284
|
+
|
285
|
+
@dataloader.append_job {
|
286
|
+
st = get_current_runtime_state
|
287
|
+
st.current_object = query.root_value
|
288
|
+
st.current_result = selection_response
|
289
|
+
|
290
|
+
call_method_on_directives(:resolve, object_proxy, selections.graphql_directives) do
|
291
|
+
evaluate_selections(
|
292
|
+
object_proxy,
|
293
|
+
root_type,
|
294
|
+
root_op_type == "mutation",
|
295
|
+
selections,
|
296
|
+
selection_response,
|
297
|
+
final_response,
|
298
|
+
nil,
|
299
|
+
)
|
300
|
+
end
|
301
|
+
}
|
302
|
+
end
|
303
|
+
end
|
62
304
|
end
|
305
|
+
delete_all_interpreter_context
|
306
|
+
nil
|
63
307
|
end
|
64
308
|
|
65
|
-
def gather_selections(owner_object, owner_type, selections, selections_by_name)
|
309
|
+
def gather_selections(owner_object, owner_type, selections, selections_to_run = nil, selections_by_name = GraphQLSelectionSet.new)
|
66
310
|
selections.each do |node|
|
67
311
|
# Skip gathering this if the directive says so
|
68
312
|
if !directives_include?(node, owner_object, owner_type)
|
69
313
|
next
|
70
314
|
end
|
71
315
|
|
72
|
-
|
73
|
-
when GraphQL::Language::Nodes::Field
|
316
|
+
if node.is_a?(GraphQL::Language::Nodes::Field)
|
74
317
|
response_key = node.alias || node.name
|
75
318
|
selections = selections_by_name[response_key]
|
76
319
|
# if there was already a selection of this field,
|
@@ -86,192 +329,413 @@ module GraphQL
|
|
86
329
|
# No selection was found for this field yet
|
87
330
|
selections_by_name[response_key] = node
|
88
331
|
end
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
332
|
+
else
|
333
|
+
# This is an InlineFragment or a FragmentSpread
|
334
|
+
if @runtime_directive_names.any? && node.directives.any? { |d| @runtime_directive_names.include?(d.name) }
|
335
|
+
next_selections = GraphQLSelectionSet.new
|
336
|
+
next_selections.graphql_directives = node.directives
|
337
|
+
if selections_to_run
|
338
|
+
selections_to_run << next_selections
|
339
|
+
else
|
340
|
+
selections_to_run = []
|
341
|
+
selections_to_run << selections_by_name
|
342
|
+
selections_to_run << next_selections
|
343
|
+
end
|
344
|
+
else
|
345
|
+
next_selections = selections_by_name
|
346
|
+
end
|
347
|
+
|
348
|
+
case node
|
349
|
+
when GraphQL::Language::Nodes::InlineFragment
|
350
|
+
if node.type
|
351
|
+
type_defn = schema.get_type(node.type.name, context)
|
352
|
+
|
353
|
+
# Faster than .map{}.include?()
|
354
|
+
query.warden.possible_types(type_defn).each do |t|
|
355
|
+
if t == owner_type
|
356
|
+
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
357
|
+
break
|
358
|
+
end
|
359
|
+
end
|
360
|
+
else
|
361
|
+
# it's an untyped fragment, definitely continue
|
362
|
+
gather_selections(owner_object, owner_type, node.selections, selections_to_run, next_selections)
|
363
|
+
end
|
364
|
+
when GraphQL::Language::Nodes::FragmentSpread
|
365
|
+
fragment_def = query.fragments[node.name]
|
366
|
+
type_defn = query.get_type(fragment_def.type.name)
|
367
|
+
possible_types = query.warden.possible_types(type_defn)
|
368
|
+
possible_types.each do |t|
|
94
369
|
if t == owner_type
|
95
|
-
gather_selections(owner_object, owner_type,
|
370
|
+
gather_selections(owner_object, owner_type, fragment_def.selections, selections_to_run, next_selections)
|
96
371
|
break
|
97
372
|
end
|
98
373
|
end
|
99
374
|
else
|
100
|
-
|
101
|
-
gather_selections(owner_object, owner_type, node.selections, selections_by_name)
|
375
|
+
raise "Invariant: unexpected selection class: #{node.class}"
|
102
376
|
end
|
103
|
-
when GraphQL::Language::Nodes::FragmentSpread
|
104
|
-
fragment_def = query.fragments[node.name]
|
105
|
-
type_defn = schema.get_type(fragment_def.type.name)
|
106
|
-
possible_types = query.warden.possible_types(type_defn)
|
107
|
-
possible_types.each do |t|
|
108
|
-
if t == owner_type
|
109
|
-
gather_selections(owner_object, owner_type, fragment_def.selections, selections_by_name)
|
110
|
-
break
|
111
|
-
end
|
112
|
-
end
|
113
|
-
else
|
114
|
-
raise "Invariant: unexpected selection class: #{node.class}"
|
115
377
|
end
|
116
378
|
end
|
379
|
+
selections_to_run || selections_by_name
|
117
380
|
end
|
118
381
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
if field_defn.nil?
|
139
|
-
field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
140
|
-
is_introspection = true
|
141
|
-
entry_point_field
|
142
|
-
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
143
|
-
is_introspection = true
|
144
|
-
dynamic_field
|
145
|
-
else
|
146
|
-
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
382
|
+
NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
|
383
|
+
|
384
|
+
# @return [void]
|
385
|
+
def evaluate_selections(owner_object, owner_type, is_eager_selection, gathered_selections, selections_result, target_result, parent_object) # rubocop:disable Metrics/ParameterLists
|
386
|
+
st = get_current_runtime_state
|
387
|
+
st.current_object = owner_object
|
388
|
+
st.current_result_name = nil
|
389
|
+
st.current_result = selections_result
|
390
|
+
|
391
|
+
finished_jobs = 0
|
392
|
+
enqueued_jobs = gathered_selections.size
|
393
|
+
gathered_selections.each do |result_name, field_ast_nodes_or_ast_node|
|
394
|
+
@dataloader.append_job {
|
395
|
+
evaluate_selection(
|
396
|
+
result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_selection, selections_result, parent_object
|
397
|
+
)
|
398
|
+
finished_jobs += 1
|
399
|
+
if target_result && finished_jobs == enqueued_jobs
|
400
|
+
selections_result.merge_into(target_result)
|
147
401
|
end
|
402
|
+
}
|
403
|
+
end
|
404
|
+
|
405
|
+
selections_result
|
406
|
+
end
|
407
|
+
|
408
|
+
# @return [void]
|
409
|
+
def evaluate_selection(result_name, field_ast_nodes_or_ast_node, owner_object, owner_type, is_eager_field, selections_result, parent_object) # rubocop:disable Metrics/ParameterLists
|
410
|
+
return if dead_result?(selections_result)
|
411
|
+
# As a performance optimization, the hash key will be a `Node` if
|
412
|
+
# there's only one selection of the field. But if there are multiple
|
413
|
+
# selections of the field, it will be an Array of nodes
|
414
|
+
if field_ast_nodes_or_ast_node.is_a?(Array)
|
415
|
+
field_ast_nodes = field_ast_nodes_or_ast_node
|
416
|
+
ast_node = field_ast_nodes.first
|
417
|
+
else
|
418
|
+
field_ast_nodes = nil
|
419
|
+
ast_node = field_ast_nodes_or_ast_node
|
420
|
+
end
|
421
|
+
field_name = ast_node.name
|
422
|
+
# This can't use `query.get_field` because it gets confused on introspection below if `field_defn` isn't `nil`,
|
423
|
+
# because of how `is_introspection` is used to call `.authorized_new` later on.
|
424
|
+
field_defn = @fields_cache[owner_type][field_name] ||= owner_type.get_field(field_name, @context)
|
425
|
+
is_introspection = false
|
426
|
+
if field_defn.nil?
|
427
|
+
field_defn = if owner_type == schema.query && (entry_point_field = schema.introspection_system.entry_point(name: field_name))
|
428
|
+
is_introspection = true
|
429
|
+
entry_point_field
|
430
|
+
elsif (dynamic_field = schema.introspection_system.dynamic_field(name: field_name))
|
431
|
+
is_introspection = true
|
432
|
+
dynamic_field
|
433
|
+
else
|
434
|
+
raise "Invariant: no field for #{owner_type}.#{field_name}"
|
148
435
|
end
|
436
|
+
end
|
149
437
|
|
150
|
-
|
438
|
+
return_type = field_defn.type
|
151
439
|
|
152
|
-
|
153
|
-
|
154
|
-
|
440
|
+
# This seems janky, but we need to know
|
441
|
+
# the field's return type at this path in order
|
442
|
+
# to propagate `null`
|
443
|
+
return_type_non_null = return_type.non_null?
|
444
|
+
if return_type_non_null
|
445
|
+
(selections_result.graphql_non_null_field_names ||= []).push(result_name)
|
446
|
+
end
|
447
|
+
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
448
|
+
st = get_current_runtime_state
|
449
|
+
st.current_field = field_defn
|
450
|
+
st.current_result = selections_result
|
451
|
+
st.current_result_name = result_name
|
155
452
|
|
156
|
-
|
157
|
-
# the field's return type at this path in order
|
158
|
-
# to propagate `null`
|
159
|
-
set_type_at_path(next_path, return_type)
|
160
|
-
# Set this before calling `run_with_directives`, so that the directive can have the latest path
|
161
|
-
@interpreter_context[:current_path] = next_path
|
162
|
-
@interpreter_context[:current_field] = field_defn
|
453
|
+
object = owner_object
|
163
454
|
|
164
|
-
|
165
|
-
object =
|
455
|
+
if is_introspection
|
456
|
+
object = authorized_new(field_defn.owner, object, context)
|
457
|
+
end
|
166
458
|
|
167
|
-
|
168
|
-
|
459
|
+
total_args_count = field_defn.arguments(context).size
|
460
|
+
if total_args_count == 0
|
461
|
+
resolved_arguments = GraphQL::Execution::Interpreter::Arguments::EMPTY
|
462
|
+
if field_defn.extras.size == 0
|
463
|
+
evaluate_selection_with_resolved_keyword_args(
|
464
|
+
NO_ARGS, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null
|
465
|
+
)
|
466
|
+
else
|
467
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
|
169
468
|
end
|
469
|
+
else
|
470
|
+
@query.arguments_cache.dataload_for(ast_node, field_defn, object) do |resolved_arguments|
|
471
|
+
evaluate_selection_with_args(resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selections_result, parent_object, return_type, return_type_non_null)
|
472
|
+
end
|
473
|
+
end
|
474
|
+
end
|
170
475
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
continue_value(
|
476
|
+
def evaluate_selection_with_args(arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null) # rubocop:disable Metrics/ParameterLists
|
477
|
+
after_lazy(arguments, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: arguments, result_name: result_name, result: selection_result) do |resolved_arguments|
|
478
|
+
if resolved_arguments.is_a?(GraphQL::ExecutionError) || resolved_arguments.is_a?(GraphQL::UnauthorizedError)
|
479
|
+
continue_value(resolved_arguments, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
175
480
|
next
|
176
481
|
end
|
177
482
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
when :ast_node
|
183
|
-
kwarg_arguments[:ast_node] = ast_node
|
184
|
-
when :execution_errors
|
185
|
-
kwarg_arguments[:execution_errors] = ExecutionErrors.new(context, ast_node, next_path)
|
186
|
-
when :path
|
187
|
-
kwarg_arguments[:path] = next_path
|
188
|
-
when :lookahead
|
189
|
-
if !field_ast_nodes
|
190
|
-
field_ast_nodes = [ast_node]
|
191
|
-
end
|
192
|
-
kwarg_arguments[:lookahead] = Execution::Lookahead.new(
|
193
|
-
query: query,
|
194
|
-
ast_nodes: field_ast_nodes,
|
195
|
-
field: field_defn,
|
196
|
-
)
|
483
|
+
kwarg_arguments = if field_defn.extras.empty?
|
484
|
+
if resolved_arguments.empty?
|
485
|
+
# We can avoid allocating the `{ Symbol => Object }` hash in this case
|
486
|
+
NO_ARGS
|
197
487
|
else
|
198
|
-
|
488
|
+
resolved_arguments.keyword_arguments
|
489
|
+
end
|
490
|
+
else
|
491
|
+
# Bundle up the extras, then make a new arguments instance
|
492
|
+
# that includes the extras, too.
|
493
|
+
extra_args = {}
|
494
|
+
field_defn.extras.each do |extra|
|
495
|
+
case extra
|
496
|
+
when :ast_node
|
497
|
+
extra_args[:ast_node] = ast_node
|
498
|
+
when :execution_errors
|
499
|
+
extra_args[:execution_errors] = ExecutionErrors.new(context, ast_node, current_path)
|
500
|
+
when :path
|
501
|
+
extra_args[:path] = current_path
|
502
|
+
when :lookahead
|
503
|
+
if !field_ast_nodes
|
504
|
+
field_ast_nodes = [ast_node]
|
505
|
+
end
|
506
|
+
|
507
|
+
extra_args[:lookahead] = Execution::Lookahead.new(
|
508
|
+
query: query,
|
509
|
+
ast_nodes: field_ast_nodes,
|
510
|
+
field: field_defn,
|
511
|
+
)
|
512
|
+
when :argument_details
|
513
|
+
# Use this flag to tell Interpreter::Arguments to add itself
|
514
|
+
# to the keyword args hash _before_ freezing everything.
|
515
|
+
extra_args[:argument_details] = :__arguments_add_self
|
516
|
+
when :parent
|
517
|
+
extra_args[:parent] = parent_object
|
518
|
+
else
|
519
|
+
extra_args[extra] = field_defn.fetch_extra(extra, context)
|
520
|
+
end
|
521
|
+
end
|
522
|
+
if extra_args.any?
|
523
|
+
resolved_arguments = resolved_arguments.merge_extras(extra_args)
|
199
524
|
end
|
525
|
+
resolved_arguments.keyword_arguments
|
200
526
|
end
|
201
527
|
|
202
|
-
|
528
|
+
evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null)
|
529
|
+
end
|
530
|
+
end
|
203
531
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
532
|
+
def evaluate_selection_with_resolved_keyword_args(kwarg_arguments, resolved_arguments, field_defn, ast_node, field_ast_nodes, owner_type, object, is_eager_field, result_name, selection_result, parent_object, return_type, return_type_non_null) # rubocop:disable Metrics/ParameterLists
|
533
|
+
st = get_current_runtime_state
|
534
|
+
st.current_field = field_defn
|
535
|
+
st.current_object = object
|
536
|
+
st.current_arguments = resolved_arguments
|
537
|
+
st.current_result_name = result_name
|
538
|
+
st.current_result = selection_result
|
539
|
+
# Optimize for the case that field is selected only once
|
540
|
+
if field_ast_nodes.nil? || field_ast_nodes.size == 1
|
541
|
+
next_selections = ast_node.selections
|
542
|
+
directives = ast_node.directives
|
543
|
+
else
|
544
|
+
next_selections = []
|
545
|
+
directives = []
|
546
|
+
field_ast_nodes.each { |f|
|
547
|
+
next_selections.concat(f.selections)
|
548
|
+
directives.concat(f.directives)
|
549
|
+
}
|
550
|
+
end
|
211
551
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
field_defn.resolve(object, kwarg_arguments, context)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
rescue GraphQL::ExecutionError => err
|
221
|
-
err
|
552
|
+
field_result = call_method_on_directives(:resolve, object, directives) do
|
553
|
+
# Actually call the field resolver and capture the result
|
554
|
+
app_result = begin
|
555
|
+
query.current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
556
|
+
field_defn.resolve(object, kwarg_arguments, context)
|
222
557
|
end
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
558
|
+
rescue GraphQL::ExecutionError => err
|
559
|
+
err
|
560
|
+
rescue StandardError => err
|
561
|
+
begin
|
562
|
+
query.handle_or_reraise(err)
|
563
|
+
rescue GraphQL::ExecutionError => ex_err
|
564
|
+
ex_err
|
228
565
|
end
|
229
566
|
end
|
567
|
+
after_lazy(app_result, owner: owner_type, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result) do |inner_result|
|
568
|
+
continue_value = continue_value(inner_result, owner_type, field_defn, return_type_non_null, ast_node, result_name, selection_result)
|
569
|
+
if HALT != continue_value
|
570
|
+
continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
230
574
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
575
|
+
# If this field is a root mutation field, immediately resolve
|
576
|
+
# all of its child fields before moving on to the next root mutation field.
|
577
|
+
# (Subselections of this mutation will still be resolved level-by-level.)
|
578
|
+
if is_eager_field
|
579
|
+
Interpreter::Resolve.resolve_all([field_result], @dataloader)
|
580
|
+
else
|
581
|
+
# Return this from `after_lazy` because it might be another lazy that needs to be resolved
|
582
|
+
field_result
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
|
587
|
+
def dead_result?(selection_result)
|
588
|
+
selection_result.graphql_dead # || ((parent = selection_result.graphql_parent) && parent.graphql_dead)
|
589
|
+
end
|
590
|
+
|
591
|
+
def set_result(selection_result, result_name, value, is_child_result)
|
592
|
+
if !dead_result?(selection_result)
|
593
|
+
if value.nil? &&
|
594
|
+
( # there are two conditions under which `nil` is not allowed in the response:
|
595
|
+
(selection_result.graphql_non_null_list_items) || # this value would be written into a list that doesn't allow nils
|
596
|
+
((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
|
597
|
+
)
|
598
|
+
# This is an invalid nil that should be propagated
|
599
|
+
# One caller of this method passes a block,
|
600
|
+
# namely when application code returns a `nil` to GraphQL and it doesn't belong there.
|
601
|
+
# The other possibility for reaching here is when a field returns an ExecutionError, so we write
|
602
|
+
# `nil` to the response, not knowing whether it's an invalid `nil` or not.
|
603
|
+
# (And in that case, we don't have to call the schema's handler, since it's not a bug in the application.)
|
604
|
+
# TODO the code is trying to tell me something.
|
605
|
+
yield if block_given?
|
606
|
+
parent = selection_result.graphql_parent
|
607
|
+
name_in_parent = selection_result.graphql_result_name
|
608
|
+
if parent.nil? # This is a top-level result hash
|
609
|
+
@response = nil
|
610
|
+
else
|
611
|
+
set_result(parent, name_in_parent, nil, false)
|
612
|
+
set_graphql_dead(selection_result)
|
613
|
+
end
|
614
|
+
elsif is_child_result
|
615
|
+
selection_result.set_child_result(result_name, value)
|
236
616
|
else
|
237
|
-
|
617
|
+
selection_result.set_leaf(result_name, value)
|
238
618
|
end
|
239
619
|
end
|
240
620
|
end
|
241
621
|
|
622
|
+
# Mark this node and any already-registered children as dead,
|
623
|
+
# so that it accepts no more writes.
|
624
|
+
def set_graphql_dead(selection_result)
|
625
|
+
case selection_result
|
626
|
+
when GraphQLResultArray
|
627
|
+
selection_result.graphql_dead = true
|
628
|
+
selection_result.values.each { |v| set_graphql_dead(v) }
|
629
|
+
when GraphQLResultHash
|
630
|
+
selection_result.graphql_dead = true
|
631
|
+
selection_result.each { |k, v| set_graphql_dead(v) }
|
632
|
+
else
|
633
|
+
# It's a scalar, no way to mark it dead.
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
637
|
+
def current_path
|
638
|
+
st = get_current_runtime_state
|
639
|
+
result = st.current_result
|
640
|
+
path = result && result.path
|
641
|
+
if path && (rn = st.current_result_name)
|
642
|
+
path = path.dup
|
643
|
+
path.push(rn)
|
644
|
+
end
|
645
|
+
path
|
646
|
+
end
|
647
|
+
|
242
648
|
HALT = Object.new
|
243
|
-
def continue_value(
|
244
|
-
|
649
|
+
def continue_value(value, parent_type, field, is_non_null, ast_node, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
650
|
+
case value
|
651
|
+
when nil
|
245
652
|
if is_non_null
|
246
|
-
|
247
|
-
|
653
|
+
set_result(selection_result, result_name, nil, false) do
|
654
|
+
# This block is called if `result_name` is not dead. (Maybe a previous invalid nil caused it be marked dead.)
|
655
|
+
err = parent_type::InvalidNullError.new(parent_type, field, value)
|
656
|
+
schema.type_error(err, context)
|
657
|
+
end
|
248
658
|
else
|
249
|
-
|
659
|
+
set_result(selection_result, result_name, nil, false)
|
250
660
|
end
|
251
661
|
HALT
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
662
|
+
when GraphQL::Error
|
663
|
+
# Handle these cases inside a single `when`
|
664
|
+
# to avoid the overhead of checking three different classes
|
665
|
+
# every time.
|
666
|
+
if value.is_a?(GraphQL::ExecutionError)
|
667
|
+
if selection_result.nil? || !dead_result?(selection_result)
|
668
|
+
value.path ||= current_path
|
669
|
+
value.ast_node ||= ast_node
|
670
|
+
context.errors << value
|
671
|
+
if selection_result
|
672
|
+
set_result(selection_result, result_name, nil, false)
|
673
|
+
end
|
674
|
+
end
|
675
|
+
HALT
|
676
|
+
elsif value.is_a?(GraphQL::UnauthorizedFieldError)
|
677
|
+
value.field ||= field
|
678
|
+
# this hook might raise & crash, or it might return
|
679
|
+
# a replacement value
|
680
|
+
next_value = begin
|
681
|
+
schema.unauthorized_field(value)
|
682
|
+
rescue GraphQL::ExecutionError => err
|
683
|
+
err
|
684
|
+
end
|
685
|
+
continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
|
686
|
+
elsif value.is_a?(GraphQL::UnauthorizedError)
|
687
|
+
# this hook might raise & crash, or it might return
|
688
|
+
# a replacement value
|
689
|
+
next_value = begin
|
690
|
+
schema.unauthorized_object(value)
|
691
|
+
rescue GraphQL::ExecutionError => err
|
692
|
+
err
|
693
|
+
end
|
694
|
+
continue_value(next_value, parent_type, field, is_non_null, ast_node, result_name, selection_result)
|
695
|
+
elsif GraphQL::Execution::SKIP == value
|
696
|
+
# It's possible a lazy was already written here
|
697
|
+
case selection_result
|
698
|
+
when GraphQLResultHash
|
699
|
+
selection_result.delete(result_name)
|
700
|
+
when GraphQLResultArray
|
701
|
+
selection_result.graphql_skip_at(result_name)
|
702
|
+
when nil
|
703
|
+
# this can happen with directives
|
704
|
+
else
|
705
|
+
raise "Invariant: unexpected result class #{selection_result.class} (#{selection_result.inspect})"
|
706
|
+
end
|
707
|
+
HALT
|
708
|
+
else
|
709
|
+
# What could this actually _be_? Anyhow,
|
710
|
+
# preserve the default behavior of doing nothing with it.
|
711
|
+
value
|
261
712
|
end
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
713
|
+
when Array
|
714
|
+
# It's an array full of execution errors; add them all.
|
715
|
+
if value.any? && value.all? { |v| v.is_a?(GraphQL::ExecutionError) }
|
716
|
+
list_type_at_all = (field && (field.type.list?))
|
717
|
+
if selection_result.nil? || !dead_result?(selection_result)
|
718
|
+
value.each_with_index do |error, index|
|
719
|
+
error.ast_node ||= ast_node
|
720
|
+
error.path ||= current_path + (list_type_at_all ? [index] : [])
|
721
|
+
context.errors << error
|
722
|
+
end
|
723
|
+
if selection_result
|
724
|
+
if list_type_at_all
|
725
|
+
result_without_errors = value.map { |v| v.is_a?(GraphQL::ExecutionError) ? nil : v }
|
726
|
+
set_result(selection_result, result_name, result_without_errors, false)
|
727
|
+
else
|
728
|
+
set_result(selection_result, result_name, nil, false)
|
729
|
+
end
|
730
|
+
end
|
731
|
+
end
|
732
|
+
HALT
|
733
|
+
else
|
734
|
+
value
|
271
735
|
end
|
272
|
-
|
273
|
-
|
274
|
-
|
736
|
+
when GraphQL::Execution::Interpreter::RawValue
|
737
|
+
# Write raw value directly to the response without resolving nested objects
|
738
|
+
set_result(selection_result, result_name, value.resolve, false)
|
275
739
|
HALT
|
276
740
|
else
|
277
741
|
value
|
@@ -286,90 +750,184 @@ module GraphQL
|
|
286
750
|
# Location information from `path` and `ast_node`.
|
287
751
|
#
|
288
752
|
# @return [Lazy, Array, Hash, Object] Lazy, Array, and Hash are all traversed to resolve lazy values later
|
289
|
-
def continue_field(
|
290
|
-
|
753
|
+
def continue_field(value, owner_type, field, current_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result) # rubocop:disable Metrics/ParameterLists
|
754
|
+
if current_type.non_null?
|
755
|
+
current_type = current_type.of_type
|
756
|
+
is_non_null = true
|
757
|
+
end
|
758
|
+
|
759
|
+
case current_type.kind.name
|
291
760
|
when "SCALAR", "ENUM"
|
292
|
-
r =
|
293
|
-
|
761
|
+
r = begin
|
762
|
+
current_type.coerce_result(value, context)
|
763
|
+
rescue StandardError => err
|
764
|
+
schema.handle_or_reraise(context, err)
|
765
|
+
end
|
766
|
+
set_result(selection_result, result_name, r, false)
|
294
767
|
r
|
295
768
|
when "UNION", "INTERFACE"
|
296
|
-
resolved_type_or_lazy = resolve_type(
|
297
|
-
after_lazy(resolved_type_or_lazy, owner:
|
298
|
-
|
769
|
+
resolved_type_or_lazy = resolve_type(current_type, value)
|
770
|
+
after_lazy(resolved_type_or_lazy, owner: current_type, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |resolved_type_result|
|
771
|
+
if resolved_type_result.is_a?(Array) && resolved_type_result.length == 2
|
772
|
+
resolved_type, resolved_value = resolved_type_result
|
773
|
+
else
|
774
|
+
resolved_type = resolved_type_result
|
775
|
+
resolved_value = value
|
776
|
+
end
|
299
777
|
|
778
|
+
possible_types = query.possible_types(current_type)
|
300
779
|
if !possible_types.include?(resolved_type)
|
301
|
-
parent_type = field.
|
302
|
-
|
780
|
+
parent_type = field.owner_type
|
781
|
+
err_class = current_type::UnresolvedTypeError
|
782
|
+
type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
|
303
783
|
schema.type_error(type_error, context)
|
304
|
-
|
784
|
+
set_result(selection_result, result_name, nil, false)
|
305
785
|
nil
|
306
786
|
else
|
307
|
-
continue_field(
|
787
|
+
continue_field(resolved_value, owner_type, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments, result_name, selection_result)
|
308
788
|
end
|
309
789
|
end
|
310
790
|
when "OBJECT"
|
311
791
|
object_proxy = begin
|
312
|
-
authorized_new(
|
792
|
+
authorized_new(current_type, value, context)
|
313
793
|
rescue GraphQL::ExecutionError => err
|
314
794
|
err
|
315
795
|
end
|
316
|
-
after_lazy(object_proxy, owner:
|
317
|
-
continue_value = continue_value(
|
796
|
+
after_lazy(object_proxy, owner: current_type, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result) do |inner_object|
|
797
|
+
continue_value = continue_value(inner_object, owner_type, field, is_non_null, ast_node, result_name, selection_result)
|
318
798
|
if HALT != continue_value
|
319
|
-
response_hash =
|
320
|
-
|
321
|
-
|
322
|
-
|
799
|
+
response_hash = GraphQLResultHash.new(result_name, selection_result)
|
800
|
+
set_result(selection_result, result_name, response_hash, true)
|
801
|
+
gathered_selections = gather_selections(continue_value, current_type, next_selections)
|
802
|
+
# There are two possibilities for `gathered_selections`:
|
803
|
+
# 1. All selections of this object should be evaluated together (there are no runtime directives modifying execution).
|
804
|
+
# This case is handled below, and the result can be written right into the main `response_hash` above.
|
805
|
+
# In this case, `gathered_selections` is a hash of selections.
|
806
|
+
# 2. Some selections of this object have runtime directives that may or may not modify execution.
|
807
|
+
# That part of the selection is evaluated in an isolated way, writing into a sub-response object which is
|
808
|
+
# eventually merged into the final response. In this case, `gathered_selections` is an array of things to run in isolation.
|
809
|
+
# (Technically, it's possible that one of those entries _doesn't_ require isolation.)
|
810
|
+
tap_or_each(gathered_selections) do |selections, is_selection_array|
|
811
|
+
if is_selection_array
|
812
|
+
this_result = GraphQLResultHash.new(result_name, selection_result)
|
813
|
+
final_result = response_hash
|
814
|
+
else
|
815
|
+
this_result = response_hash
|
816
|
+
final_result = nil
|
817
|
+
end
|
818
|
+
# reset this mutable state
|
819
|
+
# Unset `result_name` here because it's already included in the new response hash
|
820
|
+
st = get_current_runtime_state
|
821
|
+
st.current_object = continue_value
|
822
|
+
st.current_result_name = nil
|
823
|
+
st.current_result = this_result
|
824
|
+
|
825
|
+
call_method_on_directives(:resolve, continue_value, selections.graphql_directives) do
|
826
|
+
evaluate_selections(
|
827
|
+
continue_value,
|
828
|
+
current_type,
|
829
|
+
false,
|
830
|
+
selections,
|
831
|
+
this_result,
|
832
|
+
final_result,
|
833
|
+
owner_object.object,
|
834
|
+
)
|
835
|
+
this_result
|
836
|
+
end
|
837
|
+
end
|
323
838
|
end
|
324
839
|
end
|
325
840
|
when "LIST"
|
326
|
-
|
327
|
-
|
328
|
-
|
841
|
+
inner_type = current_type.of_type
|
842
|
+
# This is true for objects, unions, and interfaces
|
843
|
+
use_dataloader_job = !inner_type.unwrap.kind.input?
|
844
|
+
inner_type_non_null = inner_type.non_null?
|
845
|
+
response_list = GraphQLResultArray.new(result_name, selection_result)
|
846
|
+
response_list.graphql_non_null_list_items = inner_type_non_null
|
847
|
+
set_result(selection_result, result_name, response_list, true)
|
329
848
|
idx = 0
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
continue_value = continue_value(next_path, inner_inner_value, field, false, ast_node)
|
341
|
-
if HALT != continue_value
|
342
|
-
continue_field(next_path, continue_value, field, inner_type, ast_node, next_selections, false, owner_object, arguments)
|
849
|
+
list_value = begin
|
850
|
+
value.each do |inner_value|
|
851
|
+
this_idx = idx
|
852
|
+
idx += 1
|
853
|
+
if use_dataloader_job
|
854
|
+
@dataloader.append_job do
|
855
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
856
|
+
end
|
857
|
+
else
|
858
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type)
|
343
859
|
end
|
344
860
|
end
|
861
|
+
|
862
|
+
response_list
|
863
|
+
rescue NoMethodError => err
|
864
|
+
# Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
|
865
|
+
if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
|
866
|
+
# This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
|
867
|
+
raise ListResultFailedError.new(value: value, field: field, path: current_path)
|
868
|
+
else
|
869
|
+
# This was some other NoMethodError -- let it bubble to reveal the real error.
|
870
|
+
raise
|
871
|
+
end
|
872
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
873
|
+
ex_err
|
874
|
+
rescue StandardError => err
|
875
|
+
begin
|
876
|
+
query.handle_or_reraise(err)
|
877
|
+
rescue GraphQL::ExecutionError => ex_err
|
878
|
+
ex_err
|
879
|
+
end
|
345
880
|
end
|
346
|
-
|
347
|
-
|
348
|
-
inner_type = type.of_type
|
349
|
-
# Don't `set_type_at_path` because we want the static type,
|
350
|
-
# we're going to use that to determine whether a `nil` should be propagated or not.
|
351
|
-
continue_field(path, value, field, inner_type, ast_node, next_selections, true, owner_object, arguments)
|
881
|
+
|
882
|
+
continue_value(list_value, owner_type, field, inner_type.non_null?, ast_node, result_name, selection_result)
|
352
883
|
else
|
353
|
-
raise "Invariant: Unhandled type kind #{
|
884
|
+
raise "Invariant: Unhandled type kind #{current_type.kind} (#{current_type})"
|
354
885
|
end
|
355
886
|
end
|
356
887
|
|
357
|
-
def
|
358
|
-
|
888
|
+
def resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, next_selections, owner_type) # rubocop:disable Metrics/ParameterLists
|
889
|
+
st = get_current_runtime_state
|
890
|
+
st.current_result_name = this_idx
|
891
|
+
st.current_result = response_list
|
892
|
+
call_method_on_directives(:resolve_each, owner_object, ast_node.directives) do
|
893
|
+
# This will update `response_list` with the lazy
|
894
|
+
after_lazy(inner_value, owner: inner_type, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, result_name: this_idx, result: response_list) do |inner_inner_value|
|
895
|
+
continue_value = continue_value(inner_inner_value, owner_type, field, inner_type_non_null, ast_node, this_idx, response_list)
|
896
|
+
if HALT != continue_value
|
897
|
+
continue_field(continue_value, owner_type, field, inner_type, ast_node, next_selections, false, owner_object, arguments, this_idx, response_list)
|
898
|
+
end
|
899
|
+
end
|
900
|
+
end
|
359
901
|
end
|
360
902
|
|
361
|
-
def
|
362
|
-
|
903
|
+
def call_method_on_directives(method_name, object, directives, &block)
|
904
|
+
return yield if directives.nil? || directives.empty?
|
905
|
+
run_directive(method_name, object, directives, 0, &block)
|
906
|
+
end
|
907
|
+
|
908
|
+
def run_directive(method_name, object, directives, idx, &block)
|
909
|
+
dir_node = directives[idx]
|
363
910
|
if !dir_node
|
364
911
|
yield
|
365
912
|
else
|
366
|
-
dir_defn =
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
913
|
+
dir_defn = @schema_directives.fetch(dir_node.name)
|
914
|
+
raw_dir_args = arguments(nil, dir_defn, dir_node)
|
915
|
+
dir_args = continue_value(
|
916
|
+
raw_dir_args, # value
|
917
|
+
dir_defn, # parent_type
|
918
|
+
nil, # field
|
919
|
+
false, # is_non_null
|
920
|
+
dir_node, # ast_node
|
921
|
+
nil, # result_name
|
922
|
+
nil, # selection_result
|
923
|
+
)
|
924
|
+
|
925
|
+
if dir_args == HALT
|
926
|
+
nil
|
927
|
+
else
|
928
|
+
dir_defn.public_send(method_name, object, dir_args, context) do
|
929
|
+
run_directive(method_name, object, directives, idx + 1, &block)
|
930
|
+
end
|
373
931
|
end
|
374
932
|
end
|
375
933
|
end
|
@@ -377,7 +935,7 @@ module GraphQL
|
|
377
935
|
# Check {Schema::Directive.include?} for each directive that's present
|
378
936
|
def directives_include?(node, graphql_object, parent_type)
|
379
937
|
node.directives.each do |dir_node|
|
380
|
-
dir_defn =
|
938
|
+
dir_defn = @schema_directives.fetch(dir_node.name)
|
381
939
|
args = arguments(graphql_object, dir_defn, dir_node)
|
382
940
|
if !dir_defn.include?(graphql_object, args, context)
|
383
941
|
return false
|
@@ -386,298 +944,102 @@ module GraphQL
|
|
386
944
|
true
|
387
945
|
end
|
388
946
|
|
947
|
+
def get_current_runtime_state
|
948
|
+
Thread.current[:__graphql_runtime_info] ||= CurrentState.new
|
949
|
+
end
|
950
|
+
|
389
951
|
# @param obj [Object] Some user-returned value that may want to be batched
|
390
|
-
# @param path [Array<String>]
|
391
952
|
# @param field [GraphQL::Schema::Field]
|
392
953
|
# @param eager [Boolean] Set to `true` for mutation root fields only
|
393
954
|
# @param trace [Boolean] If `false`, don't wrap this with field tracing
|
394
955
|
# @return [GraphQL::Execution::Lazy, Object] If loading `object` will be deferred, it's a wrapper over it.
|
395
|
-
def after_lazy(lazy_obj, owner:, field:,
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
@interpreter_context[:current_arguments] = arguments
|
406
|
-
context.scoped_context = scoped_context
|
956
|
+
def after_lazy(lazy_obj, owner:, field:, owner_object:, arguments:, ast_node:, result:, result_name:, eager: false, trace: true, &block)
|
957
|
+
if lazy?(lazy_obj)
|
958
|
+
orig_result = result
|
959
|
+
lazy = GraphQL::Execution::Lazy.new(field: field) do
|
960
|
+
st = get_current_runtime_state
|
961
|
+
st.current_object = owner_object
|
962
|
+
st.current_field = field
|
963
|
+
st.current_arguments = arguments
|
964
|
+
st.current_result_name = result_name
|
965
|
+
st.current_result = orig_result
|
407
966
|
# Wrap the execution of _this_ method with tracing,
|
408
967
|
# but don't wrap the continuation below
|
409
968
|
inner_obj = begin
|
410
|
-
|
411
|
-
|
412
|
-
query.trace("execute_field_lazy", {owner: owner, field: field, path: path, query: query, object: owner_object, arguments: arguments}) do
|
413
|
-
schema.sync_lazy(lazy_obj)
|
414
|
-
end
|
415
|
-
else
|
969
|
+
if trace
|
970
|
+
query.current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
416
971
|
schema.sync_lazy(lazy_obj)
|
417
972
|
end
|
973
|
+
else
|
974
|
+
schema.sync_lazy(lazy_obj)
|
975
|
+
end
|
976
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
977
|
+
ex_err
|
978
|
+
rescue StandardError => err
|
979
|
+
begin
|
980
|
+
query.handle_or_reraise(err)
|
981
|
+
rescue GraphQL::ExecutionError => ex_err
|
982
|
+
ex_err
|
418
983
|
end
|
419
|
-
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => err
|
420
|
-
yield(err)
|
421
|
-
end
|
422
|
-
after_lazy(inner_obj, owner: owner, field: field, path: path, scoped_context: context.scoped_context, owner_object: owner_object, arguments: arguments, eager: eager) do |really_inner_obj|
|
423
|
-
yield(really_inner_obj)
|
424
984
|
end
|
985
|
+
yield(inner_obj)
|
425
986
|
end
|
426
987
|
|
427
988
|
if eager
|
428
989
|
lazy.value
|
429
990
|
else
|
430
|
-
|
991
|
+
set_result(result, result_name, lazy, false)
|
992
|
+
current_depth = 0
|
993
|
+
while result
|
994
|
+
current_depth += 1
|
995
|
+
result = result.graphql_parent
|
996
|
+
end
|
997
|
+
@lazies_at_depth[current_depth] << lazy
|
431
998
|
lazy
|
432
999
|
end
|
433
1000
|
else
|
1001
|
+
# Don't need to reset state here because it _wasn't_ lazy.
|
434
1002
|
yield(lazy_obj)
|
435
1003
|
end
|
436
1004
|
end
|
437
1005
|
|
438
|
-
def
|
439
|
-
|
440
|
-
|
441
|
-
ast_args_or_hash.arguments.each do |arg|
|
442
|
-
yield(arg.name, arg.value)
|
443
|
-
end
|
444
|
-
when Hash
|
445
|
-
ast_args_or_hash.each do |key, value|
|
446
|
-
normalized_name = GraphQL::Schema::Member::BuildType.camelize(key.to_s)
|
447
|
-
yield(normalized_name, value)
|
448
|
-
end
|
449
|
-
else
|
450
|
-
raise "Invariant, unexpected #{ast_args_or_hash.inspect}"
|
451
|
-
end
|
452
|
-
end
|
453
|
-
|
454
|
-
def arguments(graphql_object, arg_owner, ast_node_or_hash)
|
455
|
-
kwarg_arguments = {}
|
456
|
-
arg_defns = arg_owner.arguments
|
457
|
-
each_argument_pair(ast_node_or_hash) do |arg_name, arg_value|
|
458
|
-
arg_defn = arg_defns[arg_name]
|
459
|
-
# Need to distinguish between client-provided `nil`
|
460
|
-
# and nothing-at-all
|
461
|
-
is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_value, already_arguments: false)
|
462
|
-
if is_present
|
463
|
-
# This doesn't apply to directives, which are legacy
|
464
|
-
# Can remove this when Skip and Include use classes or something.
|
465
|
-
if graphql_object
|
466
|
-
value = arg_defn.prepare_value(graphql_object, value)
|
467
|
-
end
|
468
|
-
kwarg_arguments[arg_defn.keyword] = value
|
469
|
-
end
|
470
|
-
end
|
471
|
-
arg_defns.each do |name, arg_defn|
|
472
|
-
if arg_defn.default_value? && !kwarg_arguments.key?(arg_defn.keyword)
|
473
|
-
_is_present, value = arg_to_value(graphql_object, arg_defn.type, arg_defn.default_value, already_arguments: false)
|
474
|
-
kwarg_arguments[arg_defn.keyword] = value
|
475
|
-
end
|
476
|
-
end
|
477
|
-
kwarg_arguments
|
478
|
-
end
|
479
|
-
|
480
|
-
# TODO CAN THIS USE `.coerce_input` ???
|
481
|
-
|
482
|
-
# Get a Ruby-ready value from a client query.
|
483
|
-
# @param graphql_object [Object] The owner of the field whose argument this is
|
484
|
-
# @param arg_type [Class, GraphQL::Schema::NonNull, GraphQL::Schema::List]
|
485
|
-
# @param ast_value [GraphQL::Language::Nodes::VariableIdentifier, String, Integer, Float, Boolean]
|
486
|
-
# @param already_arguments [Boolean] if true, don't re-coerce these with `arguments(...)`
|
487
|
-
# @return [Array(is_present, value)]
|
488
|
-
def arg_to_value(graphql_object, arg_type, ast_value, already_arguments:)
|
489
|
-
if ast_value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
490
|
-
# If it's not here, it will get added later
|
491
|
-
if query.variables.key?(ast_value.name)
|
492
|
-
variable_value = query.variables[ast_value.name]
|
493
|
-
arg_to_value(graphql_object, arg_type, variable_value, already_arguments: true)
|
494
|
-
else
|
495
|
-
return false, nil
|
496
|
-
end
|
497
|
-
elsif ast_value.is_a?(GraphQL::Language::Nodes::NullValue)
|
498
|
-
return true, nil
|
499
|
-
elsif arg_type.is_a?(GraphQL::Schema::NonNull)
|
500
|
-
arg_to_value(graphql_object, arg_type.of_type, ast_value, already_arguments: already_arguments)
|
501
|
-
elsif arg_type.is_a?(GraphQL::Schema::List)
|
502
|
-
if ast_value.nil?
|
503
|
-
return true, nil
|
504
|
-
else
|
505
|
-
# Treat a single value like a list
|
506
|
-
arg_value = Array(ast_value)
|
507
|
-
list = []
|
508
|
-
arg_value.map do |inner_v|
|
509
|
-
_present, value = arg_to_value(graphql_object, arg_type.of_type, inner_v, already_arguments: already_arguments)
|
510
|
-
list << value
|
511
|
-
end
|
512
|
-
return true, list
|
513
|
-
end
|
514
|
-
elsif arg_type.is_a?(Class) && arg_type < GraphQL::Schema::InputObject
|
515
|
-
if already_arguments
|
516
|
-
# This came from a variable, already prepared.
|
517
|
-
# But replace `nil` with `{}` like we would for `arg_type`
|
518
|
-
if ast_value.nil?
|
519
|
-
return false, nil
|
520
|
-
else
|
521
|
-
return true, ast_value
|
522
|
-
end
|
523
|
-
else
|
524
|
-
# For these, `prepare` is applied during `#initialize`.
|
525
|
-
# Pass `nil` so it will be skipped in `#arguments`.
|
526
|
-
# What a mess.
|
527
|
-
args = arguments(nil, arg_type, ast_value)
|
528
|
-
end
|
529
|
-
|
530
|
-
input_obj = query.with_error_handling do
|
531
|
-
# We're not tracking defaults_used, but for our purposes
|
532
|
-
# we compare the value to the default value.
|
533
|
-
arg_type.new(ruby_kwargs: args, context: context, defaults_used: nil)
|
534
|
-
end
|
535
|
-
return true, input_obj
|
536
|
-
else
|
537
|
-
flat_value = if already_arguments
|
538
|
-
# It was coerced by variable handling
|
539
|
-
ast_value
|
540
|
-
else
|
541
|
-
v = flatten_ast_value(ast_value)
|
542
|
-
arg_type.coerce_input(v, context)
|
543
|
-
end
|
544
|
-
return true, flat_value
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
|
-
def flatten_ast_value(v)
|
549
|
-
case v
|
550
|
-
when GraphQL::Language::Nodes::Enum
|
551
|
-
v.name
|
552
|
-
when GraphQL::Language::Nodes::InputObject
|
553
|
-
h = {}
|
554
|
-
v.arguments.each do |arg|
|
555
|
-
h[arg.name] = flatten_ast_value(arg.value)
|
556
|
-
end
|
557
|
-
h
|
558
|
-
when Array
|
559
|
-
v.map { |v2| flatten_ast_value(v2) }
|
560
|
-
when GraphQL::Language::Nodes::VariableIdentifier
|
561
|
-
flatten_ast_value(query.variables[v.name])
|
562
|
-
else
|
563
|
-
v
|
564
|
-
end
|
565
|
-
end
|
566
|
-
|
567
|
-
def write_invalid_null_in_response(path, invalid_null_error)
|
568
|
-
if !dead_path?(path)
|
569
|
-
schema.type_error(invalid_null_error, context)
|
570
|
-
write_in_response(path, nil)
|
571
|
-
add_dead_path(path)
|
572
|
-
end
|
573
|
-
end
|
574
|
-
|
575
|
-
def write_execution_errors_in_response(path, errors)
|
576
|
-
if !dead_path?(path)
|
577
|
-
errors.each do |v|
|
578
|
-
context.errors << v
|
579
|
-
end
|
580
|
-
write_in_response(path, nil)
|
581
|
-
add_dead_path(path)
|
582
|
-
end
|
583
|
-
end
|
584
|
-
|
585
|
-
def write_in_response(path, value)
|
586
|
-
if dead_path?(path)
|
587
|
-
return
|
1006
|
+
def arguments(graphql_object, arg_owner, ast_node)
|
1007
|
+
if arg_owner.arguments_statically_coercible?
|
1008
|
+
query.arguments_for(ast_node, arg_owner)
|
588
1009
|
else
|
589
|
-
|
590
|
-
|
591
|
-
propagate_path = path[0..-2]
|
592
|
-
write_in_response(propagate_path, value)
|
593
|
-
add_dead_path(propagate_path)
|
594
|
-
else
|
595
|
-
@response.write(path, value)
|
596
|
-
end
|
597
|
-
end
|
598
|
-
end
|
599
|
-
|
600
|
-
# To propagate nulls, we have to know what the field type was
|
601
|
-
# at previous parts of the response.
|
602
|
-
# This hash matches the response
|
603
|
-
def type_at(path)
|
604
|
-
t = @types_at_paths
|
605
|
-
path.each do |part|
|
606
|
-
t = t[part] || (raise("Invariant: #{part.inspect} not found in #{t}"))
|
607
|
-
end
|
608
|
-
t = t[:__type]
|
609
|
-
t
|
610
|
-
end
|
611
|
-
|
612
|
-
def set_type_at_path(path, type)
|
613
|
-
types = @types_at_paths
|
614
|
-
path.each do |part|
|
615
|
-
types = types[part] ||= {}
|
616
|
-
end
|
617
|
-
# Use this magic key so that the hash contains:
|
618
|
-
# - string keys for nested fields
|
619
|
-
# - :__type for the object type of a selection
|
620
|
-
types[:__type] ||= type
|
621
|
-
nil
|
622
|
-
end
|
623
|
-
|
624
|
-
# Mark `path` as having been permanently nulled out.
|
625
|
-
# No values will be added beyond that path.
|
626
|
-
def add_dead_path(path)
|
627
|
-
dead = @dead_paths
|
628
|
-
path.each do |part|
|
629
|
-
dead = dead[part] ||= {}
|
1010
|
+
# The arguments must be prepared in the context of the given object
|
1011
|
+
query.arguments_for(ast_node, arg_owner, parent_object: graphql_object)
|
630
1012
|
end
|
631
|
-
dead[:__dead] = true
|
632
1013
|
end
|
633
1014
|
|
634
|
-
def
|
635
|
-
|
636
|
-
path.each do |part|
|
637
|
-
if res
|
638
|
-
if res[:__dead]
|
639
|
-
break
|
640
|
-
else
|
641
|
-
res = res[part]
|
642
|
-
end
|
643
|
-
end
|
644
|
-
end
|
645
|
-
res && res[:__dead]
|
1015
|
+
def delete_all_interpreter_context
|
1016
|
+
Thread.current[:__graphql_runtime_info] = nil
|
646
1017
|
end
|
647
1018
|
|
648
|
-
def resolve_type(type, value
|
649
|
-
|
650
|
-
resolved_type = query.trace("resolve_type", trace_payload) do
|
1019
|
+
def resolve_type(type, value)
|
1020
|
+
resolved_type, resolved_value = query.current_trace.resolve_type(query: query, type: type, object: value) do
|
651
1021
|
query.resolve_type(type, value)
|
652
1022
|
end
|
653
1023
|
|
654
|
-
if
|
1024
|
+
if lazy?(resolved_type)
|
655
1025
|
GraphQL::Execution::Lazy.new do
|
656
|
-
query.
|
1026
|
+
query.current_trace.resolve_type_lazy(query: query, type: type, object: value) do
|
657
1027
|
schema.sync_lazy(resolved_type)
|
658
1028
|
end
|
659
1029
|
end
|
660
1030
|
else
|
661
|
-
resolved_type
|
1031
|
+
[resolved_type, resolved_value]
|
662
1032
|
end
|
663
1033
|
end
|
664
1034
|
|
665
|
-
def authorized_new(type, value, context
|
666
|
-
|
667
|
-
|
668
|
-
auth_val = context.query.trace("authorized", trace_payload) do
|
669
|
-
type.authorized_new(value, context)
|
670
|
-
end
|
1035
|
+
def authorized_new(type, value, context)
|
1036
|
+
type.authorized_new(value, context)
|
1037
|
+
end
|
671
1038
|
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
end
|
677
|
-
end
|
678
|
-
else
|
679
|
-
auth_val
|
680
|
-
end
|
1039
|
+
def lazy?(object)
|
1040
|
+
@lazy_cache.fetch(object.class) {
|
1041
|
+
@lazy_cache[object.class] = @schema.lazy?(object)
|
1042
|
+
}
|
681
1043
|
end
|
682
1044
|
end
|
683
1045
|
end
|