graphql 2.6.11 → 2.6.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rdoc_options +4 -0
- data/lib/generators/graphql/core.rb +1 -0
- data/lib/generators/graphql/detailed_trace_generator.rb +1 -0
- data/lib/generators/graphql/enum_generator.rb +1 -0
- data/lib/generators/graphql/field_extractor.rb +1 -0
- data/lib/generators/graphql/input_generator.rb +1 -0
- data/lib/generators/graphql/install/mutation_root_generator.rb +1 -0
- data/lib/generators/graphql/install_generator.rb +1 -0
- data/lib/generators/graphql/interface_generator.rb +1 -0
- data/lib/generators/graphql/loader_generator.rb +8 -2
- data/lib/generators/graphql/mutation_create_generator.rb +8 -2
- data/lib/generators/graphql/mutation_delete_generator.rb +8 -2
- data/lib/generators/graphql/mutation_generator.rb +8 -2
- data/lib/generators/graphql/mutation_update_generator.rb +8 -2
- data/lib/generators/graphql/object_generator.rb +1 -0
- data/lib/generators/graphql/orm_mutations_base.rb +8 -2
- data/lib/generators/graphql/relay.rb +1 -0
- data/lib/generators/graphql/relay_generator.rb +1 -0
- data/lib/generators/graphql/scalar_generator.rb +1 -0
- data/lib/generators/graphql/type_generator.rb +38 -8
- data/lib/generators/graphql/union_generator.rb +1 -0
- data/lib/graphql/analysis/analyzer.rb +35 -9
- data/lib/graphql/analysis/field_usage.rb +1 -0
- data/lib/graphql/analysis/max_query_complexity.rb +2 -1
- data/lib/graphql/analysis/max_query_depth.rb +1 -0
- data/lib/graphql/analysis/query_complexity.rb +57 -20
- data/lib/graphql/analysis/query_depth.rb +17 -12
- data/lib/graphql/analysis/visitor.rb +79 -16
- data/lib/graphql/analysis.rb +23 -6
- data/lib/graphql/analysis_error.rb +1 -0
- data/lib/graphql/autoload.rb +30 -7
- data/lib/graphql/backtrace/table.rb +21 -5
- data/lib/graphql/backtrace/traced_error.rb +8 -3
- data/lib/graphql/backtrace.rb +14 -9
- data/lib/graphql/coercion_error.rb +1 -0
- data/lib/graphql/current.rb +44 -19
- data/lib/graphql/dashboard/application_controller.rb +1 -0
- data/lib/graphql/dashboard/detailed_traces.rb +1 -0
- data/lib/graphql/dashboard/installable.rb +1 -0
- data/lib/graphql/dashboard/landings_controller.rb +1 -0
- data/lib/graphql/dashboard/limiters.rb +1 -0
- data/lib/graphql/dashboard/operation_store.rb +1 -0
- data/lib/graphql/dashboard/statics_controller.rb +1 -0
- data/lib/graphql/dashboard/subscriptions.rb +1 -0
- data/lib/graphql/dashboard.rb +36 -22
- data/lib/graphql/dataloader/active_record_association_source.rb +1 -0
- data/lib/graphql/dataloader/active_record_source.rb +1 -0
- data/lib/graphql/dataloader/async_dataloader.rb +10 -7
- data/lib/graphql/dataloader/null_dataloader.rb +4 -3
- data/lib/graphql/dataloader/request.rb +8 -2
- data/lib/graphql/dataloader/request_all.rb +8 -2
- data/lib/graphql/dataloader/source.rb +127 -35
- data/lib/graphql/dataloader.rb +93 -42
- data/lib/graphql/date_encoding_error.rb +2 -1
- data/lib/graphql/dig.rb +12 -3
- data/lib/graphql/duration_encoding_error.rb +2 -1
- data/lib/graphql/execution/directive_checks.rb +8 -3
- data/lib/graphql/execution/errors.rb +20 -6
- data/lib/graphql/execution/field_resolve_step.rb +40 -13
- data/lib/graphql/execution/finalize.rb +12 -6
- data/lib/graphql/execution/input_values.rb +2 -1
- data/lib/graphql/execution/interpreter/argument_value.rb +17 -5
- data/lib/graphql/execution/interpreter/arguments.rb +24 -9
- data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -1
- data/lib/graphql/execution/interpreter/execution_errors.rb +1 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +1 -0
- data/lib/graphql/execution/interpreter/resolve.rb +18 -8
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +4 -1
- data/lib/graphql/execution/interpreter/runtime.rb +72 -25
- data/lib/graphql/execution/interpreter.rb +15 -6
- data/lib/graphql/execution/lazy/lazy_method_map.rb +21 -8
- data/lib/graphql/execution/lazy.rb +34 -10
- data/lib/graphql/execution/load_argument_step.rb +1 -0
- data/lib/graphql/execution/lookahead.rb +137 -54
- data/lib/graphql/execution/multiplex.rb +4 -4
- data/lib/graphql/execution/next.rb +7 -0
- data/lib/graphql/execution/prepare_object_step.rb +1 -0
- data/lib/graphql/execution/resolve_type_step.rb +2 -1
- data/lib/graphql/execution/runner.rb +9 -3
- data/lib/graphql/execution/selections_step.rb +3 -2
- data/lib/graphql/execution.rb +2 -2
- data/lib/graphql/execution_error.rb +19 -10
- data/lib/graphql/float_decoding_error.rb +1 -0
- data/lib/graphql/float_encoding_error.rb +7 -2
- data/lib/graphql/integer_decoding_error.rb +2 -1
- data/lib/graphql/integer_encoding_error.rb +8 -3
- data/lib/graphql/introspection/base_object.rb +3 -1
- data/lib/graphql/introspection/directive_location_enum.rb +1 -0
- data/lib/graphql/introspection/directive_type.rb +1 -0
- data/lib/graphql/introspection/dynamic_fields.rb +1 -0
- data/lib/graphql/introspection/entry_points.rb +1 -0
- data/lib/graphql/introspection/enum_value_type.rb +1 -0
- data/lib/graphql/introspection/field_type.rb +1 -0
- data/lib/graphql/introspection/input_value_type.rb +1 -0
- data/lib/graphql/introspection/introspection_query.rb +1 -0
- data/lib/graphql/introspection/schema_type.rb +1 -0
- data/lib/graphql/introspection/type_kind_enum.rb +1 -0
- data/lib/graphql/introspection/type_type.rb +1 -0
- data/lib/graphql/introspection.rb +1 -0
- data/lib/graphql/invalid_name_error.rb +1 -0
- data/lib/graphql/invalid_null_error.rb +19 -6
- data/lib/graphql/language/block_string.rb +2 -1
- data/lib/graphql/language/cache.rb +10 -4
- data/lib/graphql/language/comment.rb +1 -0
- data/lib/graphql/language/definition_slice.rb +1 -0
- data/lib/graphql/language/document_from_schema_definition.rb +9 -10
- data/lib/graphql/language/generation.rb +23 -9
- data/lib/graphql/language/lexer.rb +5 -0
- data/lib/graphql/language/nodes.rb +2094 -419
- data/lib/graphql/language/parser.rb +24 -17
- data/lib/graphql/language/printer.rb +31 -16
- data/lib/graphql/language/sanitized_printer.rb +16 -5
- data/lib/graphql/language/static_visitor.rb +19 -4
- data/lib/graphql/language/visitor.rb +51 -29
- data/lib/graphql/language.rb +13 -7
- data/lib/graphql/load_application_object_failed_error.rb +14 -5
- data/lib/graphql/name_validator.rb +1 -0
- data/lib/graphql/pagination/active_record_relation_connection.rb +1 -0
- data/lib/graphql/pagination/array_connection.rb +20 -6
- data/lib/graphql/pagination/connection.rb +178 -48
- data/lib/graphql/pagination/connections.rb +21 -14
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -0
- data/lib/graphql/pagination/relation_connection.rb +123 -22
- data/lib/graphql/pagination/sequel_dataset_connection.rb +1 -0
- data/lib/graphql/pagination.rb +1 -0
- data/lib/graphql/parse_error.rb +1 -0
- data/lib/graphql/query/context/scoped_context.rb +2 -1
- data/lib/graphql/query/context.rb +106 -35
- data/lib/graphql/query/fingerprint.rb +15 -7
- data/lib/graphql/query/input_validation_result.rb +1 -0
- data/lib/graphql/query/null_context.rb +1 -0
- data/lib/graphql/query/partial.rb +28 -15
- data/lib/graphql/query/result.rb +15 -5
- data/lib/graphql/query/validation_pipeline.rb +16 -6
- data/lib/graphql/query/variable_validation_error.rb +1 -0
- data/lib/graphql/query/variables.rb +9 -2
- data/lib/graphql/query.rb +193 -49
- data/lib/graphql/railtie.rb +7 -3
- data/lib/graphql/rake_task/validate.rb +72 -17
- data/lib/graphql/rake_task.rb +55 -23
- data/lib/graphql/relay/range_add.rb +32 -21
- data/lib/graphql/relay.rb +1 -0
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +9 -6
- data/lib/graphql/rubocop/graphql/default_required_true.rb +9 -6
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +12 -8
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +9 -5
- data/lib/graphql/rubocop.rb +1 -0
- data/lib/graphql/runtime_error.rb +1 -0
- data/lib/graphql/runtime_type_error.rb +1 -0
- data/lib/graphql/schema/addition.rb +18 -6
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +265 -42
- data/lib/graphql/schema/base_64_encoder.rb +2 -2
- data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -0
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +2 -2
- data/lib/graphql/schema/build_from_definition.rb +94 -27
- data/lib/graphql/schema/built_in_types.rb +1 -0
- data/lib/graphql/schema/directive/deprecated.rb +1 -0
- data/lib/graphql/schema/directive/feature.rb +39 -22
- data/lib/graphql/schema/directive/flagged.rb +6 -1
- data/lib/graphql/schema/directive/include.rb +1 -0
- data/lib/graphql/schema/directive/one_of.rb +1 -0
- data/lib/graphql/schema/directive/skip.rb +1 -0
- data/lib/graphql/schema/directive/specified_by.rb +1 -0
- data/lib/graphql/schema/directive/transform.rb +17 -8
- data/lib/graphql/schema/directive.rb +12 -7
- data/lib/graphql/schema/enum.rb +99 -38
- data/lib/graphql/schema/enum_value.rb +21 -13
- data/lib/graphql/schema/field/connection_extension.rb +10 -2
- data/lib/graphql/schema/field/scope_extension.rb +1 -0
- data/lib/graphql/schema/field.rb +621 -109
- data/lib/graphql/schema/field_extension.rb +89 -34
- data/lib/graphql/schema/find_inherited_value.rb +5 -3
- data/lib/graphql/schema/finder.rb +22 -8
- data/lib/graphql/schema/has_single_input_argument.rb +39 -6
- data/lib/graphql/schema/input_object.rb +40 -13
- data/lib/graphql/schema/interface.rb +33 -17
- data/lib/graphql/schema/introspection_system.rb +7 -3
- data/lib/graphql/schema/late_bound_type.rb +2 -2
- data/lib/graphql/schema/list.rb +15 -4
- data/lib/graphql/schema/loader.rb +23 -12
- data/lib/graphql/schema/member/base_dsl_methods.rb +58 -12
- data/lib/graphql/schema/member/build_type.rb +16 -5
- data/lib/graphql/schema/member/graphql_type_names.rb +10 -6
- data/lib/graphql/schema/member/has_arguments.rb +115 -48
- data/lib/graphql/schema/member/has_ast_node.rb +1 -0
- data/lib/graphql/schema/member/has_authorization.rb +5 -0
- data/lib/graphql/schema/member/has_dataloader.rb +91 -34
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -2
- data/lib/graphql/schema/member/has_directives.rb +38 -10
- data/lib/graphql/schema/member/has_fields.rb +151 -70
- data/lib/graphql/schema/member/has_interfaces.rb +10 -2
- data/lib/graphql/schema/member/has_path.rb +14 -5
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +1 -0
- data/lib/graphql/schema/member/has_validators.rb +19 -4
- data/lib/graphql/schema/member/relay_shortcuts.rb +10 -2
- data/lib/graphql/schema/member/scoped.rb +14 -3
- data/lib/graphql/schema/member/type_system_helpers.rb +36 -6
- data/lib/graphql/schema/member/validates_input.rb +1 -0
- data/lib/graphql/schema/member.rb +9 -2
- data/lib/graphql/schema/mutation.rb +50 -46
- data/lib/graphql/schema/non_null.rb +21 -5
- data/lib/graphql/schema/object.rb +33 -12
- data/lib/graphql/schema/printer.rb +52 -31
- data/lib/graphql/schema/ractor_shareable.rb +6 -2
- data/lib/graphql/schema/relay_classic_mutation.rb +3 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +29 -7
- data/lib/graphql/schema/resolver.rb +156 -45
- data/lib/graphql/schema/scalar.rb +1 -0
- data/lib/graphql/schema/subscription.rb +67 -28
- data/lib/graphql/schema/timeout.rb +65 -25
- data/lib/graphql/schema/type_expression.rb +14 -5
- data/lib/graphql/schema/type_membership.rb +29 -10
- data/lib/graphql/schema/union.rb +4 -2
- data/lib/graphql/schema/unique_within_type.rb +24 -5
- data/lib/graphql/schema/validator/all_validator.rb +17 -9
- data/lib/graphql/schema/validator/allow_blank_validator.rb +8 -2
- data/lib/graphql/schema/validator/allow_null_validator.rb +8 -2
- data/lib/graphql/schema/validator/exclusion_validator.rb +14 -5
- data/lib/graphql/schema/validator/format_validator.rb +21 -10
- data/lib/graphql/schema/validator/inclusion_validator.rb +15 -6
- data/lib/graphql/schema/validator/length_validator.rb +23 -12
- data/lib/graphql/schema/validator/numericality_validator.rb +29 -16
- data/lib/graphql/schema/validator/required_validator.rb +45 -31
- data/lib/graphql/schema/validator.rb +89 -25
- data/lib/graphql/schema/visibility/migration.rb +14 -7
- data/lib/graphql/schema/visibility/profile.rb +65 -20
- data/lib/graphql/schema/visibility/visit.rb +12 -10
- data/lib/graphql/schema/visibility.rb +24 -20
- data/lib/graphql/schema/warden.rb +117 -31
- data/lib/graphql/schema/wrapper.rb +4 -1
- data/lib/graphql/schema.rb +1046 -300
- data/lib/graphql/static_validation/all_rules.rb +2 -1
- data/lib/graphql/static_validation/base_visitor.rb +59 -9
- data/lib/graphql/static_validation/definition_dependencies.rb +27 -7
- data/lib/graphql/static_validation/error.rb +3 -0
- data/lib/graphql/static_validation/interpreter_visitor.rb +1 -0
- data/lib/graphql/static_validation/literal_validator.rb +1 -0
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +4 -0
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +1 -0
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +3 -0
- data/lib/graphql/static_validation/rules/argument_names_are_unique_error.rb +1 -0
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -0
- data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +1 -0
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -0
- data/lib/graphql/static_validation/rules/directives_are_defined_error.rb +1 -0
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +3 -0
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations_error.rb +1 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +3 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type_error.rb +1 -0
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +9 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections_error.rb +1 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +295 -134
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +14 -2
- data/lib/graphql/static_validation/rules/fragment_names_are_unique.rb +3 -0
- data/lib/graphql/static_validation/rules/fragment_names_are_unique_error.rb +1 -0
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -0
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible_error.rb +1 -0
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +3 -0
- data/lib/graphql/static_validation/rules/fragment_types_exist_error.rb +1 -0
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +3 -0
- data/lib/graphql/static_validation/rules/fragments_are_finite_error.rb +1 -0
- data/lib/graphql/static_validation/rules/fragments_are_named.rb +3 -0
- data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +1 -0
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +3 -0
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types_error.rb +1 -0
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +3 -0
- data/lib/graphql/static_validation/rules/fragments_are_used_error.rb +1 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +3 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +1 -0
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +3 -0
- data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +1 -0
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +3 -0
- data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +1 -0
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +1 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +4 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +1 -0
- data/lib/graphql/static_validation/rules/operation_names_are_valid.rb +3 -0
- data/lib/graphql/static_validation/rules/operation_names_are_valid_error.rb +1 -0
- data/lib/graphql/static_validation/rules/query_root_exists.rb +3 -0
- data/lib/graphql/static_validation/rules/query_root_exists_error.rb +1 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present_error.rb +1 -0
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -0
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +1 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +43 -5
- data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +1 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +3 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location_error.rb +1 -0
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -0
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed_error.rb +1 -0
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +3 -0
- data/lib/graphql/static_validation/rules/variable_names_are_unique_error.rb +1 -0
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +207 -51
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed_error.rb +1 -0
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +3 -0
- data/lib/graphql/static_validation/rules/variables_are_input_types_error.rb +1 -0
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +6 -3
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined_error.rb +1 -0
- data/lib/graphql/static_validation/validation_context.rb +3 -2
- data/lib/graphql/static_validation/validation_timeout_error.rb +1 -0
- data/lib/graphql/static_validation/validator.rb +41 -16
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/string_encoding_error.rb +1 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +85 -65
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +10 -4
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +1 -0
- data/lib/graphql/subscriptions/event.rb +25 -6
- data/lib/graphql/subscriptions/serialize.rb +58 -19
- data/lib/graphql/subscriptions.rb +172 -46
- data/lib/graphql/testing/helpers.rb +17 -4
- data/lib/graphql/testing/mock_action_cable.rb +55 -33
- data/lib/graphql/testing.rb +1 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +17 -9
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +2 -1
- data/lib/graphql/tracing/appoptics_trace.rb +2 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -1
- data/lib/graphql/tracing/appsignal_trace.rb +16 -7
- data/lib/graphql/tracing/appsignal_tracing.rb +7 -3
- data/lib/graphql/tracing/call_legacy_tracers.rb +1 -0
- data/lib/graphql/tracing/data_dog_trace.rb +17 -6
- data/lib/graphql/tracing/data_dog_tracing.rb +10 -3
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +1 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +1 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +1 -0
- data/lib/graphql/tracing/detailed_trace.rb +105 -42
- data/lib/graphql/tracing/legacy_hooks_trace.rb +5 -0
- data/lib/graphql/tracing/legacy_trace.rb +1 -0
- data/lib/graphql/tracing/monitor_trace.rb +17 -11
- data/lib/graphql/tracing/new_relic_trace.rb +17 -8
- data/lib/graphql/tracing/new_relic_tracing.rb +7 -3
- data/lib/graphql/tracing/notifications_trace.rb +11 -8
- data/lib/graphql/tracing/notifications_tracing.rb +23 -7
- data/lib/graphql/tracing/null_trace.rb +1 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +1 -0
- data/lib/graphql/tracing/perfetto_trace.rb +41 -19
- data/lib/graphql/tracing/platform_trace.rb +3 -1
- data/lib/graphql/tracing/platform_tracing.rb +16 -7
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +1 -0
- data/lib/graphql/tracing/prometheus_trace.rb +22 -13
- data/lib/graphql/tracing/prometheus_tracing.rb +1 -0
- data/lib/graphql/tracing/scout_trace.rb +10 -4
- data/lib/graphql/tracing/scout_tracing.rb +7 -3
- data/lib/graphql/tracing/sentry_trace.rb +12 -5
- data/lib/graphql/tracing/statsd_trace.rb +12 -6
- data/lib/graphql/tracing/statsd_tracing.rb +7 -1
- data/lib/graphql/tracing/trace.rb +231 -58
- data/lib/graphql/tracing.rb +25 -9
- data/lib/graphql/type_kinds.rb +2 -1
- data/lib/graphql/types/big_int.rb +1 -0
- data/lib/graphql/types/boolean.rb +1 -0
- data/lib/graphql/types/float.rb +1 -0
- data/lib/graphql/types/id.rb +1 -0
- data/lib/graphql/types/int.rb +2 -1
- data/lib/graphql/types/iso_8601_date.rb +21 -4
- data/lib/graphql/types/iso_8601_date_time.rb +33 -6
- data/lib/graphql/types/iso_8601_duration.rb +42 -9
- data/lib/graphql/types/json.rb +1 -0
- data/lib/graphql/types/relay/base_connection.rb +38 -32
- data/lib/graphql/types/relay/base_edge.rb +15 -9
- data/lib/graphql/types/relay/connection_behaviors.rb +19 -3
- data/lib/graphql/types/relay/edge_behaviors.rb +17 -4
- data/lib/graphql/types/relay/has_node_field.rb +6 -1
- data/lib/graphql/types/relay/has_nodes_field.rb +6 -1
- data/lib/graphql/types/relay/node.rb +1 -0
- data/lib/graphql/types/relay/node_behaviors.rb +5 -1
- data/lib/graphql/types/relay/page_info.rb +1 -0
- data/lib/graphql/types/relay/page_info_behaviors.rb +1 -0
- data/lib/graphql/types/relay.rb +4 -3
- data/lib/graphql/types/string.rb +1 -0
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/unauthorized_enum_value_error.rb +4 -1
- data/lib/graphql/unauthorized_error.rb +11 -4
- data/lib/graphql/unauthorized_field_error.rb +4 -1
- data/lib/graphql/unresolved_type_error.rb +16 -5
- data/lib/graphql/version.rb +2 -1
- data/lib/graphql.rb +37 -9
- data/readme.md +1 -1
- metadata +89 -5
- data/.yardopts +0 -5
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
2
3
|
|
|
3
4
|
# frozen_string_literal: true
|
|
4
5
|
module GraphQL
|
|
5
6
|
module StaticValidation
|
|
7
|
+
#--
|
|
8
|
+
# @requires_ancestor: GraphQL::StaticValidation::BaseVisitor
|
|
9
|
+
# @requires_ancestor: GraphQL::StaticValidation::BaseVisitor::ContextMethods
|
|
6
10
|
module FieldsWillMerge
|
|
7
11
|
# Validates that a selection set is valid if all fields (including spreading any
|
|
8
12
|
# fragments) either correspond to distinct response names or can be merged
|
|
@@ -21,6 +25,7 @@ module GraphQL
|
|
|
21
25
|
class Field
|
|
22
26
|
attr_reader :node, :definition, :owner_type, :parents
|
|
23
27
|
|
|
28
|
+
# `parents` is only for the legacy check, see `legacy_mutually_exclusive?`
|
|
24
29
|
def initialize(node, definition, owner_type, parents)
|
|
25
30
|
@node = node
|
|
26
31
|
@definition = definition
|
|
@@ -28,6 +33,11 @@ module GraphQL
|
|
|
28
33
|
@parents = parents
|
|
29
34
|
end
|
|
30
35
|
|
|
36
|
+
# The types which decide if this field is mutually exclusive with other fields
|
|
37
|
+
def scope
|
|
38
|
+
@parents || @owner_type
|
|
39
|
+
end
|
|
40
|
+
|
|
31
41
|
def return_type
|
|
32
42
|
@return_type ||= @definition&.type
|
|
33
43
|
end
|
|
@@ -37,7 +47,7 @@ module GraphQL
|
|
|
37
47
|
end
|
|
38
48
|
|
|
39
49
|
def comparison_key
|
|
40
|
-
@comparison_key ||= [@node,
|
|
50
|
+
@comparison_key ||= [@node, scope]
|
|
41
51
|
end
|
|
42
52
|
end
|
|
43
53
|
|
|
@@ -52,30 +62,142 @@ module GraphQL
|
|
|
52
62
|
@compared_field_groups = {}
|
|
53
63
|
@field_group_signatures = {}.compare_by_identity
|
|
54
64
|
@field_selection_signatures = {}.compare_by_identity
|
|
55
|
-
# Cache mutually_exclusive? results for type pairs
|
|
56
|
-
@mutually_exclusive_cache = {}.compare_by_identity
|
|
57
65
|
# Cache collect_fields results for sub-selection comparison
|
|
58
66
|
@sub_fields_cache = {}.compare_by_identity
|
|
67
|
+
|
|
68
|
+
@allow_legacy_invalid_field_conflicts = @schema.allow_legacy_invalid_field_conflicts
|
|
69
|
+
# Unless legacy conflicts are rejected, use the legacy check, which is the same as previous versions.
|
|
70
|
+
# It uses `legacy_mutually_exclusive?` and the caches above. The spec check uses `@spec_caches`.
|
|
71
|
+
@legacy_exclusivity = @allow_legacy_invalid_field_conflicts != false
|
|
72
|
+
@spec_caches = nil
|
|
73
|
+
@mutually_exclusive_cache = nil
|
|
74
|
+
# True when the spec check can find conflicts which the legacy check doesn't find
|
|
75
|
+
@spec_check_needed = false
|
|
76
|
+
# During the legacy check, true when the spec rule treats the parent fields as mutually exclusive
|
|
77
|
+
@spec_inherited_exclusive = false
|
|
78
|
+
# The pairs of nodes which have a conflict in the legacy check
|
|
79
|
+
@legacy_conflict_nodes = nil
|
|
80
|
+
# When this is true, record conflicts in `@spec_conflicts` instead of adding errors
|
|
81
|
+
@find_spec_conflicts = false
|
|
82
|
+
@spec_conflicts = nil
|
|
59
83
|
end
|
|
60
84
|
|
|
61
85
|
def on_operation_definition(node, _parent)
|
|
62
|
-
|
|
63
|
-
conflicts_within_selection_set(node, type_definition)
|
|
64
|
-
@conflicts&.each_value { |error_type| error_type.each_value { |error| add_error(error) } }
|
|
86
|
+
check_selection_set(node, type_definition)
|
|
65
87
|
super
|
|
66
88
|
end
|
|
67
89
|
|
|
68
90
|
def on_field(node, _parent)
|
|
69
91
|
if !node.selections.empty? && selections_may_conflict?(node.selections)
|
|
70
|
-
|
|
71
|
-
conflicts_within_selection_set(node, type_definition)
|
|
72
|
-
@conflicts&.each_value { |error_type| error_type.each_value { |error| add_error(error) } }
|
|
92
|
+
check_selection_set(node, type_definition)
|
|
73
93
|
end
|
|
74
94
|
super
|
|
75
95
|
end
|
|
76
96
|
|
|
77
97
|
private
|
|
78
98
|
|
|
99
|
+
#: (untyped node, untyped parent_type) -> void
|
|
100
|
+
def check_selection_set(node, parent_type)
|
|
101
|
+
conflicts_within_selection_set(node, parent_type)
|
|
102
|
+
|
|
103
|
+
if @spec_check_needed
|
|
104
|
+
# Find the conflicts with the spec check, without errors or warnings. The new ones are legacy conflicts.
|
|
105
|
+
legacy_conflicts = @conflicts
|
|
106
|
+
conflict_count = @conflict_count
|
|
107
|
+
@conflicts = nil
|
|
108
|
+
with_spec_check { conflicts_within_selection_set(node, parent_type) }
|
|
109
|
+
@conflicts = legacy_conflicts
|
|
110
|
+
@conflict_count = conflict_count
|
|
111
|
+
legacy_conflict_nodes = (@legacy_conflict_nodes ||= {}.compare_by_identity)
|
|
112
|
+
@spec_conflicts&.each do |(response_key, node1, node2, different_names, different_arguments, type1, type2)|
|
|
113
|
+
next if legacy_conflict_nodes[node1]&.key?(node2)
|
|
114
|
+
add_legacy_invalid_conflict(response_key, node1, node2, different_names, different_arguments, type1, type2)
|
|
115
|
+
add_legacy_conflict_nodes(node1, node2)
|
|
116
|
+
end
|
|
117
|
+
@spec_conflicts = nil
|
|
118
|
+
@spec_check_needed = false
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
@conflicts&.each_value { |error_type| error_type.each_value { |error| add_error(error) } }
|
|
122
|
+
@conflicts = nil
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Use the spec rule for mutually exclusive fields, with its own caches, and record conflicts in `@spec_conflicts`
|
|
126
|
+
#: () { () -> void } -> void
|
|
127
|
+
def with_spec_check(&block)
|
|
128
|
+
legacy_caches = [@compared_sub_selections, @compared_field_groups, @field_group_signatures, @sub_fields_cache]
|
|
129
|
+
@compared_sub_selections, @compared_field_groups, @field_group_signatures, @sub_fields_cache =
|
|
130
|
+
(@spec_caches ||= [{}.compare_by_identity, {}, {}.compare_by_identity, {}.compare_by_identity])
|
|
131
|
+
@legacy_exclusivity = false
|
|
132
|
+
@find_spec_conflicts = true
|
|
133
|
+
yield
|
|
134
|
+
ensure
|
|
135
|
+
@legacy_exclusivity = true
|
|
136
|
+
@find_spec_conflicts = false
|
|
137
|
+
@compared_sub_selections, @compared_field_groups, @field_group_signatures, @sub_fields_cache = legacy_caches
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
#: (untyped response_key, GraphQL::Language::Nodes::Field node1, GraphQL::Language::Nodes::Field node2, bool different_names, bool different_arguments, untyped type1, untyped type2) -> void
|
|
141
|
+
def add_legacy_invalid_conflict(response_key, node1, node2, different_names, different_arguments, type1, type2)
|
|
142
|
+
message_override = nil
|
|
143
|
+
if @allow_legacy_invalid_field_conflicts == true
|
|
144
|
+
legacy_handling = @schema.legacy_invalid_field_conflicts(@context.query, node1, node2)
|
|
145
|
+
case legacy_handling
|
|
146
|
+
when nil
|
|
147
|
+
return
|
|
148
|
+
when :return_validation_error
|
|
149
|
+
# Add the error below
|
|
150
|
+
when String
|
|
151
|
+
message_override = legacy_handling
|
|
152
|
+
else
|
|
153
|
+
raise GraphQL::Error, "#{@schema}.legacy_invalid_field_conflicts returned unexpected value: #{legacy_handling.inspect}. Expected `nil`, String, or `:return_validation_error`."
|
|
154
|
+
end
|
|
155
|
+
else
|
|
156
|
+
@context.query.logger.warn <<~WARN
|
|
157
|
+
GraphQL-Ruby encountered fields which can't be merged in this query: `#{node1.name}` (at #{node1.line}:#{node1.col}) vs. `#{node2.name}` (at #{node2.line}:#{node2.col}).
|
|
158
|
+
This will return an error in future GraphQL-Ruby versions, as per the GraphQL specification
|
|
159
|
+
Learn about migrating here: https://graphql-ruby.org/api-doc/#{GraphQL::VERSION}/GraphQL/Schema.html#allow_legacy_invalid_field_conflicts-class_method
|
|
160
|
+
WARN
|
|
161
|
+
return
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
if type1
|
|
165
|
+
conflict = conflicts[:return_type][response_key]
|
|
166
|
+
conflict.message = message_override if message_override
|
|
167
|
+
conflict.add_conflict(node1, "`#{type1.to_type_signature}`")
|
|
168
|
+
conflict.add_conflict(node2, "`#{type2.to_type_signature}`")
|
|
169
|
+
@conflict_count += 1
|
|
170
|
+
else
|
|
171
|
+
add_field_conflict(response_key, node1, node2, different_names, different_arguments, message_override)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
#: (GraphQL::Language::Nodes::Field node1, GraphQL::Language::Nodes::Field node2) -> void
|
|
176
|
+
def add_legacy_conflict_nodes(node1, node2)
|
|
177
|
+
legacy_conflict_nodes = (@legacy_conflict_nodes ||= {}.compare_by_identity)
|
|
178
|
+
(legacy_conflict_nodes[node1] ||= {}.compare_by_identity)[node2] = true
|
|
179
|
+
(legacy_conflict_nodes[node2] ||= {}.compare_by_identity)[node1] = true
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
#: (untyped response_key, GraphQL::Language::Nodes::Field node1, GraphQL::Language::Nodes::Field node2, bool different_names, bool different_arguments, String? message_override) -> void
|
|
183
|
+
def add_field_conflict(response_key, node1, node2, different_names, different_arguments, message_override)
|
|
184
|
+
if different_names
|
|
185
|
+
conflict = conflicts[:field][response_key]
|
|
186
|
+
conflict.message = message_override if message_override
|
|
187
|
+
conflict.add_conflict(node1, node1.name)
|
|
188
|
+
conflict.add_conflict(node2, node2.name)
|
|
189
|
+
@conflict_count += 1
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
if different_arguments
|
|
193
|
+
conflict = conflicts[:argument][response_key]
|
|
194
|
+
conflict.message = message_override if message_override
|
|
195
|
+
conflict.add_conflict(node1, GraphQL::Language.serialize(serialize_field_args(node1)))
|
|
196
|
+
conflict.add_conflict(node2, GraphQL::Language.serialize(serialize_field_args(node2)))
|
|
197
|
+
@conflict_count += 1
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
79
201
|
# Quick check: can the direct children of this selection set possibly conflict?
|
|
80
202
|
# If all direct selections are Fields with unique names and no aliases,
|
|
81
203
|
# and there are no fragments, then no response key can have >1 field,
|
|
@@ -129,7 +251,7 @@ module GraphQL
|
|
|
129
251
|
return if node.selections.empty?
|
|
130
252
|
|
|
131
253
|
# Collect all fields from this selection set, expanding fragments transitively
|
|
132
|
-
response_keys = collect_fields(node.selections, owner_type: parent_type, parents: [])
|
|
254
|
+
response_keys = collect_fields(node.selections, owner_type: parent_type, parents: @legacy_exclusivity ? [] : nil)
|
|
133
255
|
|
|
134
256
|
# Find conflicts within each response key group
|
|
135
257
|
find_conflicts_within(response_keys)
|
|
@@ -144,7 +266,7 @@ module GraphQL
|
|
|
144
266
|
end
|
|
145
267
|
|
|
146
268
|
def collect_fields_inner(selections, owner_type:, parents:, response_keys:, visited_fragments:)
|
|
147
|
-
deferred_spreads = nil
|
|
269
|
+
deferred_spreads = nil #: Array[untyped]?
|
|
148
270
|
sel_idx = 0
|
|
149
271
|
sel_len = selections.size
|
|
150
272
|
|
|
@@ -166,11 +288,10 @@ module GraphQL
|
|
|
166
288
|
existing << field
|
|
167
289
|
end
|
|
168
290
|
when GraphQL::Language::Nodes::InlineFragment
|
|
169
|
-
frag_type = sel.type ? @types.type(
|
|
291
|
+
frag_type = (sel_type = sel.type) ? @types.type(sel_type.name) : owner_type
|
|
170
292
|
|
|
171
293
|
if frag_type
|
|
172
|
-
new_parents = parents.dup
|
|
173
|
-
new_parents << frag_type
|
|
294
|
+
new_parents = parents && (parents.dup << frag_type)
|
|
174
295
|
collect_fields_inner(sel.selections, owner_type: frag_type, parents: new_parents, response_keys: response_keys, visited_fragments: visited_fragments)
|
|
175
296
|
end
|
|
176
297
|
when GraphQL::Language::Nodes::FragmentSpread
|
|
@@ -197,8 +318,7 @@ module GraphQL
|
|
|
197
318
|
frag_type = @types.type(frag.type.name)
|
|
198
319
|
next unless frag_type
|
|
199
320
|
|
|
200
|
-
new_parents = parents.dup
|
|
201
|
-
new_parents << frag_type
|
|
321
|
+
new_parents = parents && (parents.dup << frag_type)
|
|
202
322
|
collect_fields_inner(frag.selections, owner_type: frag_type, parents: new_parents, response_keys: response_keys, visited_fragments: visited_fragments)
|
|
203
323
|
end
|
|
204
324
|
end
|
|
@@ -208,12 +328,13 @@ module GraphQL
|
|
|
208
328
|
response_keys.each do |key, fields|
|
|
209
329
|
next unless fields.is_a?(Array)
|
|
210
330
|
|
|
211
|
-
# Optimization:
|
|
212
|
-
#
|
|
213
|
-
|
|
214
|
-
|
|
331
|
+
# Optimization: compare only one of the fields which have the same signature
|
|
332
|
+
# (name + definition + arguments), parent type and sub-selections.
|
|
333
|
+
if fields.size > 4 && @legacy_exclusivity
|
|
334
|
+
legacy_find_conflicts_within(key, fields)
|
|
335
|
+
elsif fields.size > 4
|
|
215
336
|
f0 = fields[0]
|
|
216
|
-
all_same = true
|
|
337
|
+
all_same = true #: bool
|
|
217
338
|
i = 1
|
|
218
339
|
while i < fields.size
|
|
219
340
|
unless fields_same_signature?(f0, fields[i])
|
|
@@ -224,27 +345,23 @@ module GraphQL
|
|
|
224
345
|
end
|
|
225
346
|
|
|
226
347
|
if all_same
|
|
348
|
+
# These fields can only conflict on sub-selections.
|
|
227
349
|
find_conflicts_between_selection_groups(key, fields)
|
|
228
350
|
else
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
# Within the same group, fields can only conflict on sub-selections.
|
|
242
|
-
group = unique_groups[gi]
|
|
243
|
-
if group.size >= 2
|
|
244
|
-
find_conflicts_between_selection_groups(key, group)
|
|
351
|
+
representatives = {}
|
|
352
|
+
fields.each do |field|
|
|
353
|
+
representatives[[field_signature(field), field.scope, field_selection_signature(field)]] ||= field
|
|
354
|
+
end
|
|
355
|
+
representatives = representatives.values
|
|
356
|
+
|
|
357
|
+
i = 0
|
|
358
|
+
while i < representatives.size
|
|
359
|
+
j = i + 1
|
|
360
|
+
while j < representatives.size
|
|
361
|
+
find_conflict(key, representatives[i], representatives[j])
|
|
362
|
+
j += 1
|
|
245
363
|
end
|
|
246
|
-
|
|
247
|
-
gi += 1
|
|
364
|
+
i += 1
|
|
248
365
|
end
|
|
249
366
|
end
|
|
250
367
|
else
|
|
@@ -262,6 +379,42 @@ module GraphQL
|
|
|
262
379
|
end
|
|
263
380
|
end
|
|
264
381
|
|
|
382
|
+
# Previous versions compared only the first field of each group of fields with the same signature,
|
|
383
|
+
# and one field for each sub-selection in a group. For the spec check, that's the same as comparing each field,
|
|
384
|
+
# unless the fields in a group have different parent types or sub-selections. Then the spec check can find more conflicts.
|
|
385
|
+
#: (untyped response_key, Array[Field] fields) -> void
|
|
386
|
+
def legacy_find_conflicts_within(response_key, fields)
|
|
387
|
+
f0 = fields[0] #: as !nil
|
|
388
|
+
n0 = f0.node
|
|
389
|
+
if fields.all? { |f| f.definition.equal?(f0.definition) && f.node.name == n0.name && same_arguments?(f.node, n0) }
|
|
390
|
+
find_conflicts_between_selection_groups(response_key, fields)
|
|
391
|
+
if !@spec_check_needed && !fields.all? { |f| f.node.selections.empty? || f.owner_type.equal?(f0.owner_type) }
|
|
392
|
+
@spec_check_needed = true
|
|
393
|
+
end
|
|
394
|
+
else
|
|
395
|
+
groups = fields.group_by { |f| field_signature(f) }.values
|
|
396
|
+
gi = 0
|
|
397
|
+
while gi < groups.size
|
|
398
|
+
group = groups[gi] #: as !nil
|
|
399
|
+
gj = gi + 1
|
|
400
|
+
while gj < groups.size
|
|
401
|
+
other_group = groups[gj] #: as !nil
|
|
402
|
+
find_conflict(response_key, group[0], other_group[0])
|
|
403
|
+
gj += 1
|
|
404
|
+
end
|
|
405
|
+
if group.size >= 2
|
|
406
|
+
find_conflicts_between_selection_groups(response_key, group)
|
|
407
|
+
if !@spec_check_needed
|
|
408
|
+
g0 = group[0] #: as !nil
|
|
409
|
+
g0_selections = field_selection_signature(g0)
|
|
410
|
+
@spec_check_needed = !group.all? { |f| f.owner_type.equal?(g0.owner_type) && field_selection_signature(f) == g0_selections }
|
|
411
|
+
end
|
|
412
|
+
end
|
|
413
|
+
gi += 1
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
265
418
|
def find_conflicts_between_selection_groups(response_key, fields)
|
|
266
419
|
fields_by_selection = {}
|
|
267
420
|
fields.each do |field|
|
|
@@ -290,6 +443,7 @@ module GraphQL
|
|
|
290
443
|
n2 = f2.node
|
|
291
444
|
|
|
292
445
|
f1.definition.equal?(f2.definition) &&
|
|
446
|
+
f1.scope == f2.scope &&
|
|
293
447
|
n1.name == n2.name &&
|
|
294
448
|
same_arguments?(n1, n2)
|
|
295
449
|
end
|
|
@@ -313,25 +467,28 @@ module GraphQL
|
|
|
313
467
|
node1 = field1.node
|
|
314
468
|
node2 = field2.node
|
|
315
469
|
|
|
316
|
-
are_mutually_exclusive = mutually_exclusive || mutually_exclusive?(field1
|
|
470
|
+
are_mutually_exclusive = mutually_exclusive || mutually_exclusive?(field1, field2)
|
|
317
471
|
|
|
318
472
|
if !are_mutually_exclusive
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
473
|
+
different_names = node1.name != node2.name
|
|
474
|
+
different_arguments = !same_arguments?(node1, node2)
|
|
475
|
+
if different_names || different_arguments
|
|
476
|
+
if @find_spec_conflicts
|
|
477
|
+
(@spec_conflicts ||= []) << [response_key, node1, node2, different_names, different_arguments, nil, nil]
|
|
478
|
+
@conflict_count += 1
|
|
479
|
+
else
|
|
480
|
+
add_field_conflict(response_key, node1, node2, different_names, different_arguments, nil)
|
|
481
|
+
add_legacy_conflict_nodes(node1, node2) if @legacy_exclusivity
|
|
482
|
+
end
|
|
324
483
|
|
|
325
|
-
|
|
484
|
+
# Like graphql-js, stop here: the document is already invalid, and comparing sub-selections can be slow.
|
|
485
|
+
return
|
|
326
486
|
end
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
conflict.add_conflict(node2, GraphQL::Language.serialize(serialize_field_args(node2)))
|
|
333
|
-
|
|
334
|
-
@conflict_count += 1
|
|
487
|
+
elsif @legacy_exclusivity
|
|
488
|
+
# If the spec rule doesn't treat these fields as mutually exclusive, it can find a conflict
|
|
489
|
+
spec_exclusive = @spec_inherited_exclusive || spec_mutually_exclusive?(field1, field2)
|
|
490
|
+
if !spec_exclusive && !@spec_check_needed && (node1.name != node2.name || !same_arguments?(node1, node2))
|
|
491
|
+
@spec_check_needed = true
|
|
335
492
|
end
|
|
336
493
|
end
|
|
337
494
|
|
|
@@ -340,74 +497,77 @@ module GraphQL
|
|
|
340
497
|
(t1 = field1.return_type) &&
|
|
341
498
|
(t2 = field2.return_type) &&
|
|
342
499
|
return_types_conflict?(t1, t2)
|
|
500
|
+
if @find_spec_conflicts
|
|
501
|
+
(@spec_conflicts ||= []) << [response_key, node1, node2, false, false, t1, t2]
|
|
502
|
+
else
|
|
503
|
+
add_return_type_conflict(response_key, t1, t2, node1, node2)
|
|
504
|
+
add_legacy_conflict_nodes(node1, node2) if @legacy_exclusivity
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
if spec_exclusive && !@spec_inherited_exclusive
|
|
509
|
+
@spec_inherited_exclusive = true
|
|
510
|
+
find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive: are_mutually_exclusive)
|
|
511
|
+
@spec_inherited_exclusive = false
|
|
512
|
+
else
|
|
513
|
+
find_conflicts_between_sub_selection_sets(field1, field2, mutually_exclusive: are_mutually_exclusive)
|
|
514
|
+
end
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
#: (untyped response_key, untyped t1, untyped t2, GraphQL::Language::Nodes::Field node1, GraphQL::Language::Nodes::Field node2) -> void
|
|
518
|
+
def add_return_type_conflict(response_key, t1, t2, node1, node2)
|
|
519
|
+
return_error = nil
|
|
520
|
+
message_override = nil
|
|
343
521
|
|
|
344
|
-
|
|
345
|
-
|
|
522
|
+
case @schema.allow_legacy_invalid_return_type_conflicts
|
|
523
|
+
when false
|
|
524
|
+
return_error = true
|
|
525
|
+
when true
|
|
526
|
+
legacy_handling = @schema.legacy_invalid_return_type_conflicts(@context.query, t1, t2, node1, node2)
|
|
346
527
|
|
|
347
|
-
case
|
|
348
|
-
when
|
|
528
|
+
case legacy_handling
|
|
529
|
+
when nil
|
|
530
|
+
return_error = false
|
|
531
|
+
when :return_validation_error
|
|
349
532
|
return_error = true
|
|
350
|
-
when
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
case legacy_handling
|
|
354
|
-
when nil
|
|
355
|
-
return_error = false
|
|
356
|
-
when :return_validation_error
|
|
357
|
-
return_error = true
|
|
358
|
-
when String
|
|
359
|
-
return_error = true
|
|
360
|
-
message_override = legacy_handling
|
|
361
|
-
else
|
|
362
|
-
raise GraphQL::Error, "#{@schema}.legacy_invalid_scalar_conflicts returned unexpected value: #{legacy_handling.inspect}. Expected `nil`, String, or `:return_validation_error`."
|
|
363
|
-
end
|
|
533
|
+
when String
|
|
534
|
+
return_error = true
|
|
535
|
+
message_override = legacy_handling
|
|
364
536
|
else
|
|
365
|
-
|
|
366
|
-
@context.query.logger.warn <<~WARN
|
|
367
|
-
GraphQL-Ruby encountered mismatched types in this query: `#{t1.to_type_signature}` (at #{node1.line}:#{node1.col}) vs. `#{t2.to_type_signature}` (at #{node2.line}:#{node2.col}).
|
|
368
|
-
This will return an error in future GraphQL-Ruby versions, as per the GraphQL specification
|
|
369
|
-
Learn about migrating here: https://graphql-ruby.org/api-doc/#{GraphQL::VERSION}/GraphQL/Schema.html#allow_legacy_invalid_return_type_conflicts-class_method
|
|
370
|
-
WARN
|
|
537
|
+
raise GraphQL::Error, "#{@schema}.legacy_invalid_scalar_conflicts returned unexpected value: #{legacy_handling.inspect}. Expected `nil`, String, or `:return_validation_error`."
|
|
371
538
|
end
|
|
539
|
+
else
|
|
540
|
+
return_error = false
|
|
541
|
+
@context.query.logger.warn <<~WARN
|
|
542
|
+
GraphQL-Ruby encountered mismatched types in this query: `#{t1.to_type_signature}` (at #{node1.line}:#{node1.col}) vs. `#{t2.to_type_signature}` (at #{node2.line}:#{node2.col}).
|
|
543
|
+
This will return an error in future GraphQL-Ruby versions, as per the GraphQL specification
|
|
544
|
+
Learn about migrating here: https://graphql-ruby.org/api-doc/#{GraphQL::VERSION}/GraphQL/Schema.html#allow_legacy_invalid_return_type_conflicts-class_method
|
|
545
|
+
WARN
|
|
546
|
+
end
|
|
372
547
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
if message_override
|
|
377
|
-
conflict.message = message_override
|
|
378
|
-
end
|
|
548
|
+
if return_error
|
|
549
|
+
conflict = conflicts[:return_type][response_key]
|
|
379
550
|
|
|
380
|
-
|
|
381
|
-
conflict.
|
|
382
|
-
@conflict_count += 1
|
|
551
|
+
if message_override
|
|
552
|
+
conflict.message = message_override
|
|
383
553
|
end
|
|
384
|
-
end
|
|
385
554
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
)
|
|
555
|
+
conflict.add_conflict(node1, "`#{t1.to_type_signature}`")
|
|
556
|
+
conflict.add_conflict(node2, "`#{t2.to_type_signature}`")
|
|
557
|
+
@conflict_count += 1
|
|
558
|
+
end
|
|
391
559
|
end
|
|
392
560
|
|
|
561
|
+
# SameResponseShape in the spec. Compare one wrapper at a time: `list?` is also true for a non-null list.
|
|
562
|
+
#: (untyped type1, untyped type2) -> bool
|
|
393
563
|
def return_types_conflict?(type1, type2)
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
elsif
|
|
401
|
-
true
|
|
402
|
-
elsif type1.non_null?
|
|
403
|
-
if type2.non_null?
|
|
404
|
-
return_types_conflict?(type1.of_type, type2.of_type)
|
|
405
|
-
else
|
|
406
|
-
true
|
|
407
|
-
end
|
|
408
|
-
elsif type2.non_null?
|
|
409
|
-
true
|
|
410
|
-
elsif type1.kind.leaf? && type2.kind.leaf?
|
|
564
|
+
kind1 = type1.kind
|
|
565
|
+
kind2 = type2.kind
|
|
566
|
+
if kind1.non_null? || kind2.non_null?
|
|
567
|
+
!(kind1.non_null? && kind2.non_null?) || return_types_conflict?(type1.of_type, type2.of_type)
|
|
568
|
+
elsif kind1.list? || kind2.list?
|
|
569
|
+
!(kind1.list? && kind2.list?) || return_types_conflict?(type1.of_type, type2.of_type)
|
|
570
|
+
elsif kind1.leaf? || kind2.leaf?
|
|
411
571
|
type1 != type2
|
|
412
572
|
else
|
|
413
573
|
false
|
|
@@ -452,7 +612,7 @@ module GraphQL
|
|
|
452
612
|
if inner && inner.key?(return_type)
|
|
453
613
|
inner[return_type]
|
|
454
614
|
else
|
|
455
|
-
result = collect_fields(node.selections, owner_type: return_type, parents: [return_type])
|
|
615
|
+
result = collect_fields(node.selections, owner_type: return_type, parents: @legacy_exclusivity ? [return_type] : nil)
|
|
456
616
|
inner ||= {}.compare_by_identity
|
|
457
617
|
inner[return_type] = result
|
|
458
618
|
@sub_fields_cache[node] = inner
|
|
@@ -545,47 +705,48 @@ module GraphQL
|
|
|
545
705
|
serialized_args
|
|
546
706
|
end
|
|
547
707
|
|
|
548
|
-
|
|
549
|
-
def mutually_exclusive?(
|
|
708
|
+
#: (Field field1, Field field2) -> bool
|
|
709
|
+
def mutually_exclusive?(field1, field2)
|
|
710
|
+
@legacy_exclusivity ? legacy_mutually_exclusive?(field1.parents, field2.parents) : spec_mutually_exclusive?(field1, field2)
|
|
711
|
+
end
|
|
712
|
+
|
|
713
|
+
# As in the spec (FieldsInSetCanMerge), only fields on different object types are mutually exclusive,
|
|
714
|
+
# even if no runtime object can be selected by both of them.
|
|
715
|
+
#: (Field field1, Field field2) -> bool
|
|
716
|
+
def spec_mutually_exclusive?(field1, field2)
|
|
717
|
+
owner_type1 = field1.owner_type
|
|
718
|
+
owner_type2 = field2.owner_type
|
|
719
|
+
!owner_type1.equal?(owner_type2) && owner_type1.kind.object? && owner_type2.kind.object?
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
# Previous versions compared the lists of type conditions above each field, position by position,
|
|
723
|
+
# and treated lists with different lengths as mutually exclusive.
|
|
724
|
+
#: (Array[untyped] parents1, Array[untyped] parents2) -> bool
|
|
725
|
+
def legacy_mutually_exclusive?(parents1, parents2)
|
|
550
726
|
if parents1.empty? || parents2.empty?
|
|
551
727
|
false
|
|
552
728
|
elsif parents1.length == parents2.length
|
|
729
|
+
cache = (@mutually_exclusive_cache ||= {}.compare_by_identity)
|
|
553
730
|
i = 0
|
|
554
731
|
len = parents1.length
|
|
555
|
-
|
|
556
732
|
while i < len
|
|
557
733
|
type1 = parents1[i - 1]
|
|
558
734
|
type2 = parents2[i - 1]
|
|
559
735
|
unless type1.equal?(type2)
|
|
560
|
-
inner =
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
cached = types_mutually_exclusive?(type1, type2)
|
|
565
|
-
inner[type2] = cached
|
|
566
|
-
end
|
|
567
|
-
else
|
|
568
|
-
cached = types_mutually_exclusive?(type1, type2)
|
|
569
|
-
inner = {}.compare_by_identity
|
|
570
|
-
inner[type2] = cached
|
|
571
|
-
@mutually_exclusive_cache[type1] = inner
|
|
736
|
+
inner = (cache[type1] ||= {}.compare_by_identity)
|
|
737
|
+
exclusive = inner[type2]
|
|
738
|
+
if exclusive.nil?
|
|
739
|
+
exclusive = inner[type2] = (@types.possible_types(type1) & @types.possible_types(type2)).empty?
|
|
572
740
|
end
|
|
573
|
-
return true if
|
|
741
|
+
return true if exclusive
|
|
574
742
|
end
|
|
575
743
|
i += 1
|
|
576
744
|
end
|
|
577
|
-
|
|
578
745
|
false
|
|
579
746
|
else
|
|
580
747
|
true
|
|
581
748
|
end
|
|
582
749
|
end
|
|
583
|
-
|
|
584
|
-
def types_mutually_exclusive?(type1, type2)
|
|
585
|
-
possible_right_types = @types.possible_types(type1)
|
|
586
|
-
possible_left_types = @types.possible_types(type2)
|
|
587
|
-
(possible_right_types & possible_left_types).empty?
|
|
588
|
-
end
|
|
589
750
|
end
|
|
590
751
|
end
|
|
591
752
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
# typed: true
|
|
2
3
|
module GraphQL
|
|
3
4
|
module StaticValidation
|
|
4
5
|
class FieldsWillMergeError < StaticValidation::Error
|
|
@@ -11,6 +12,9 @@ module GraphQL
|
|
|
11
12
|
@field_name = field_name
|
|
12
13
|
@kind = kind
|
|
13
14
|
@conflicts = []
|
|
15
|
+
# Indexes of `@nodes`, so that `add_conflict` doesn't compare `node` with each one
|
|
16
|
+
@node_ids = {}.compare_by_identity #: Hash[GraphQL::Language::Nodes::AbstractNode, bool]
|
|
17
|
+
@nodes_by_location = nil #: Hash[Integer?, Hash[Integer?, Array[GraphQL::Language::Nodes::AbstractNode]]]?
|
|
14
18
|
end
|
|
15
19
|
|
|
16
20
|
def message
|
|
@@ -31,11 +35,19 @@ module GraphQL
|
|
|
31
35
|
# Check if we already have an error for this exact node.
|
|
32
36
|
# Use object identity first (fast path), then fall back to
|
|
33
37
|
# value + location comparison for duplicate AST nodes.
|
|
34
|
-
if
|
|
38
|
+
return if @node_ids.key?(node)
|
|
39
|
+
|
|
40
|
+
nodes_at_line = (@nodes_by_location ||= {})[node.line] ||= {}
|
|
41
|
+
nodes_at_location = nodes_at_line[node.col]
|
|
42
|
+
if nodes_at_location
|
|
35
43
|
# already have an error for this node
|
|
36
|
-
return
|
|
44
|
+
return if nodes_at_location.any? { |n| n == node }
|
|
45
|
+
nodes_at_location << node
|
|
46
|
+
else
|
|
47
|
+
nodes_at_line[node.col] = [node]
|
|
37
48
|
end
|
|
38
49
|
|
|
50
|
+
@node_ids[node] = true
|
|
39
51
|
@nodes << node
|
|
40
52
|
@conflicts << conflict_str
|
|
41
53
|
end
|