graphql 2.6.9 → 2.6.14
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 +3 -2
- 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 +45 -16
- 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 +98 -44
- 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 +288 -168
- data/lib/graphql/execution/finalize.rb +12 -6
- data/lib/graphql/execution/input_values.rb +5 -2
- 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 +18 -7
- 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 +38 -13
- data/lib/graphql/execution/resolve_type_step.rb +2 -1
- data/lib/graphql/execution/runner.rb +32 -15
- 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 +14 -0
- data/lib/graphql/float_encoding_error.rb +33 -0
- 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 +16 -8
- data/lib/graphql/language/nodes.rb +2094 -419
- data/lib/graphql/language/parser.rb +29 -18
- 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 +15 -9
- 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 +24 -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 +164 -27
- 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 -16
- 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 +17 -1
- data/lib/graphql/query/variables.rb +9 -2
- data/lib/graphql/query.rb +195 -50
- 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 +69 -50
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +279 -45
- 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 +507 -178
- 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 +33 -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 +622 -110
- 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 +21 -7
- data/lib/graphql/schema/member/graphql_type_names.rb +10 -6
- data/lib/graphql/schema/member/has_arguments.rb +112 -49
- 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 +45 -11
- data/lib/graphql/schema/member/has_fields.rb +151 -70
- data/lib/graphql/schema/member/has_interfaces.rb +8 -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 +44 -15
- 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 +73 -19
- 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 +1052 -304
- 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 +355 -157
- 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 +89 -23
- 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 +25 -6
- 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 +19 -4
- 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 +39 -9
- data/readme.md +1 -1
- metadata +91 -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
|
|
@@ -15,10 +19,13 @@ module GraphQL
|
|
|
15
19
|
# separately (which leads to exponential recursion through nested fragments),
|
|
16
20
|
# we flatten all fragment spreads into a single field map and compare within it.
|
|
17
21
|
NO_ARGS = GraphQL::EmptyObjects::EMPTY_HASH
|
|
22
|
+
EXCLUSIVE_COMPARISON = 1
|
|
23
|
+
NONEXCLUSIVE_COMPARISON = 2
|
|
18
24
|
|
|
19
25
|
class Field
|
|
20
26
|
attr_reader :node, :definition, :owner_type, :parents
|
|
21
27
|
|
|
28
|
+
# `parents` is only for the legacy check, see `legacy_mutually_exclusive?`
|
|
22
29
|
def initialize(node, definition, owner_type, parents)
|
|
23
30
|
@node = node
|
|
24
31
|
@definition = definition
|
|
@@ -26,6 +33,11 @@ module GraphQL
|
|
|
26
33
|
@parents = parents
|
|
27
34
|
end
|
|
28
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
|
+
|
|
29
41
|
def return_type
|
|
30
42
|
@return_type ||= @definition&.type
|
|
31
43
|
end
|
|
@@ -33,6 +45,10 @@ module GraphQL
|
|
|
33
45
|
def unwrapped_return_type
|
|
34
46
|
@unwrapped_return_type ||= return_type&.unwrap
|
|
35
47
|
end
|
|
48
|
+
|
|
49
|
+
def comparison_key
|
|
50
|
+
@comparison_key ||= [@node, scope]
|
|
51
|
+
end
|
|
36
52
|
end
|
|
37
53
|
|
|
38
54
|
def initialize(*)
|
|
@@ -43,30 +59,145 @@ module GraphQL
|
|
|
43
59
|
# Track which sub-selection node pairs have been compared to prevent
|
|
44
60
|
# infinite recursion with cyclic fragments
|
|
45
61
|
@compared_sub_selections = {}.compare_by_identity
|
|
46
|
-
|
|
47
|
-
@
|
|
62
|
+
@compared_field_groups = {}
|
|
63
|
+
@field_group_signatures = {}.compare_by_identity
|
|
64
|
+
@field_selection_signatures = {}.compare_by_identity
|
|
48
65
|
# Cache collect_fields results for sub-selection comparison
|
|
49
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
|
|
50
83
|
end
|
|
51
84
|
|
|
52
85
|
def on_operation_definition(node, _parent)
|
|
53
|
-
|
|
54
|
-
conflicts_within_selection_set(node, type_definition)
|
|
55
|
-
@conflicts&.each_value { |error_type| error_type.each_value { |error| add_error(error) } }
|
|
86
|
+
check_selection_set(node, type_definition)
|
|
56
87
|
super
|
|
57
88
|
end
|
|
58
89
|
|
|
59
90
|
def on_field(node, _parent)
|
|
60
91
|
if !node.selections.empty? && selections_may_conflict?(node.selections)
|
|
61
|
-
|
|
62
|
-
conflicts_within_selection_set(node, type_definition)
|
|
63
|
-
@conflicts&.each_value { |error_type| error_type.each_value { |error| add_error(error) } }
|
|
92
|
+
check_selection_set(node, type_definition)
|
|
64
93
|
end
|
|
65
94
|
super
|
|
66
95
|
end
|
|
67
96
|
|
|
68
97
|
private
|
|
69
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
|
+
|
|
70
201
|
# Quick check: can the direct children of this selection set possibly conflict?
|
|
71
202
|
# If all direct selections are Fields with unique names and no aliases,
|
|
72
203
|
# and there are no fragments, then no response key can have >1 field,
|
|
@@ -120,7 +251,7 @@ module GraphQL
|
|
|
120
251
|
return if node.selections.empty?
|
|
121
252
|
|
|
122
253
|
# Collect all fields from this selection set, expanding fragments transitively
|
|
123
|
-
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)
|
|
124
255
|
|
|
125
256
|
# Find conflicts within each response key group
|
|
126
257
|
find_conflicts_within(response_keys)
|
|
@@ -135,7 +266,7 @@ module GraphQL
|
|
|
135
266
|
end
|
|
136
267
|
|
|
137
268
|
def collect_fields_inner(selections, owner_type:, parents:, response_keys:, visited_fragments:)
|
|
138
|
-
deferred_spreads = nil
|
|
269
|
+
deferred_spreads = nil #: Array[untyped]?
|
|
139
270
|
sel_idx = 0
|
|
140
271
|
sel_len = selections.size
|
|
141
272
|
|
|
@@ -157,11 +288,10 @@ module GraphQL
|
|
|
157
288
|
existing << field
|
|
158
289
|
end
|
|
159
290
|
when GraphQL::Language::Nodes::InlineFragment
|
|
160
|
-
frag_type = sel.type ? @types.type(
|
|
291
|
+
frag_type = (sel_type = sel.type) ? @types.type(sel_type.name) : owner_type
|
|
161
292
|
|
|
162
293
|
if frag_type
|
|
163
|
-
new_parents = parents.dup
|
|
164
|
-
new_parents << frag_type
|
|
294
|
+
new_parents = parents && (parents.dup << frag_type)
|
|
165
295
|
collect_fields_inner(sel.selections, owner_type: frag_type, parents: new_parents, response_keys: response_keys, visited_fragments: visited_fragments)
|
|
166
296
|
end
|
|
167
297
|
when GraphQL::Language::Nodes::FragmentSpread
|
|
@@ -188,8 +318,7 @@ module GraphQL
|
|
|
188
318
|
frag_type = @types.type(frag.type.name)
|
|
189
319
|
next unless frag_type
|
|
190
320
|
|
|
191
|
-
new_parents = parents.dup
|
|
192
|
-
new_parents << frag_type
|
|
321
|
+
new_parents = parents && (parents.dup << frag_type)
|
|
193
322
|
collect_fields_inner(frag.selections, owner_type: frag_type, parents: new_parents, response_keys: response_keys, visited_fragments: visited_fragments)
|
|
194
323
|
end
|
|
195
324
|
end
|
|
@@ -199,12 +328,13 @@ module GraphQL
|
|
|
199
328
|
response_keys.each do |key, fields|
|
|
200
329
|
next unless fields.is_a?(Array)
|
|
201
330
|
|
|
202
|
-
# Optimization:
|
|
203
|
-
#
|
|
204
|
-
|
|
205
|
-
|
|
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
|
|
206
336
|
f0 = fields[0]
|
|
207
|
-
all_same = true
|
|
337
|
+
all_same = true #: bool
|
|
208
338
|
i = 1
|
|
209
339
|
while i < fields.size
|
|
210
340
|
unless fields_same_signature?(f0, fields[i])
|
|
@@ -215,54 +345,24 @@ module GraphQL
|
|
|
215
345
|
end
|
|
216
346
|
|
|
217
347
|
if all_same
|
|
218
|
-
#
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
348
|
+
# These fields can only conflict on sub-selections.
|
|
349
|
+
find_conflicts_between_selection_groups(key, fields)
|
|
350
|
+
else
|
|
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
|
+
|
|
222
357
|
i = 0
|
|
223
|
-
while i <
|
|
358
|
+
while i < representatives.size
|
|
224
359
|
j = i + 1
|
|
225
|
-
while j <
|
|
226
|
-
|
|
227
|
-
find_conflict(key, unique_nodes[i], unique_nodes[j])
|
|
228
|
-
end
|
|
360
|
+
while j < representatives.size
|
|
361
|
+
find_conflict(key, representatives[i], representatives[j])
|
|
229
362
|
j += 1
|
|
230
363
|
end
|
|
231
364
|
i += 1
|
|
232
365
|
end
|
|
233
|
-
else
|
|
234
|
-
groups = fields.group_by { |f| field_signature(f) }
|
|
235
|
-
unique_groups = groups.values
|
|
236
|
-
|
|
237
|
-
# Compare representatives across different groups
|
|
238
|
-
gi = 0
|
|
239
|
-
while gi < unique_groups.size
|
|
240
|
-
gj = gi + 1
|
|
241
|
-
while gj < unique_groups.size
|
|
242
|
-
find_conflict(key, unique_groups[gi][0], unique_groups[gj][0])
|
|
243
|
-
gj += 1
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
# Within same group, deduplicate by AST node and compare all
|
|
247
|
-
# pairs for sub-selection conflicts
|
|
248
|
-
group = unique_groups[gi]
|
|
249
|
-
if group.size >= 2
|
|
250
|
-
unique_in_group = group.uniq { |f| f.node.object_id }
|
|
251
|
-
ui = 0
|
|
252
|
-
while ui < unique_in_group.size
|
|
253
|
-
uj = ui + 1
|
|
254
|
-
while uj < unique_in_group.size
|
|
255
|
-
if unique_in_group[ui].node.selections.size > 0 || unique_in_group[uj].node.selections.size > 0
|
|
256
|
-
find_conflict(key, unique_in_group[ui], unique_in_group[uj])
|
|
257
|
-
end
|
|
258
|
-
uj += 1
|
|
259
|
-
end
|
|
260
|
-
ui += 1
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
gi += 1
|
|
265
|
-
end
|
|
266
366
|
end
|
|
267
367
|
else
|
|
268
368
|
# Small number of fields — original O(n²) is fine
|
|
@@ -279,11 +379,71 @@ module GraphQL
|
|
|
279
379
|
end
|
|
280
380
|
end
|
|
281
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
|
+
|
|
418
|
+
def find_conflicts_between_selection_groups(response_key, fields)
|
|
419
|
+
fields_by_selection = {}
|
|
420
|
+
fields.each do |field|
|
|
421
|
+
fields_by_selection[field_selection_signature(field)] ||= field
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
representatives = fields_by_selection.values
|
|
425
|
+
i = 0
|
|
426
|
+
while i < representatives.size
|
|
427
|
+
j = i + 1
|
|
428
|
+
while j < representatives.size
|
|
429
|
+
find_conflict(response_key, representatives[i], representatives[j])
|
|
430
|
+
j += 1
|
|
431
|
+
end
|
|
432
|
+
i += 1
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
def field_selection_signature(field)
|
|
437
|
+
node = field.node
|
|
438
|
+
@field_selection_signatures[node] ||= node.selections.map(&:to_query_string)
|
|
439
|
+
end
|
|
440
|
+
|
|
282
441
|
def fields_same_signature?(f1, f2)
|
|
283
442
|
n1 = f1.node
|
|
284
443
|
n2 = f2.node
|
|
285
444
|
|
|
286
445
|
f1.definition.equal?(f2.definition) &&
|
|
446
|
+
f1.scope == f2.scope &&
|
|
287
447
|
n1.name == n2.name &&
|
|
288
448
|
same_arguments?(n1, n2)
|
|
289
449
|
end
|
|
@@ -307,25 +467,28 @@ module GraphQL
|
|
|
307
467
|
node1 = field1.node
|
|
308
468
|
node2 = field2.node
|
|
309
469
|
|
|
310
|
-
are_mutually_exclusive = mutually_exclusive || mutually_exclusive?(field1
|
|
470
|
+
are_mutually_exclusive = mutually_exclusive || mutually_exclusive?(field1, field2)
|
|
311
471
|
|
|
312
472
|
if !are_mutually_exclusive
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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
|
|
318
483
|
|
|
319
|
-
|
|
484
|
+
# Like graphql-js, stop here: the document is already invalid, and comparing sub-selections can be slow.
|
|
485
|
+
return
|
|
320
486
|
end
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
conflict.add_conflict(node2, GraphQL::Language.serialize(serialize_field_args(node2)))
|
|
327
|
-
|
|
328
|
-
@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
|
|
329
492
|
end
|
|
330
493
|
end
|
|
331
494
|
|
|
@@ -334,74 +497,77 @@ module GraphQL
|
|
|
334
497
|
(t1 = field1.return_type) &&
|
|
335
498
|
(t2 = field2.return_type) &&
|
|
336
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
|
|
337
507
|
|
|
338
|
-
|
|
339
|
-
|
|
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
|
|
340
521
|
|
|
341
|
-
|
|
342
|
-
|
|
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)
|
|
527
|
+
|
|
528
|
+
case legacy_handling
|
|
529
|
+
when nil
|
|
530
|
+
return_error = false
|
|
531
|
+
when :return_validation_error
|
|
343
532
|
return_error = true
|
|
344
|
-
when
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
case legacy_handling
|
|
348
|
-
when nil
|
|
349
|
-
return_error = false
|
|
350
|
-
when :return_validation_error
|
|
351
|
-
return_error = true
|
|
352
|
-
when String
|
|
353
|
-
return_error = true
|
|
354
|
-
message_override = legacy_handling
|
|
355
|
-
else
|
|
356
|
-
raise GraphQL::Error, "#{@schema}.legacy_invalid_scalar_conflicts returned unexpected value: #{legacy_handling.inspect}. Expected `nil`, String, or `:return_validation_error`."
|
|
357
|
-
end
|
|
533
|
+
when String
|
|
534
|
+
return_error = true
|
|
535
|
+
message_override = legacy_handling
|
|
358
536
|
else
|
|
359
|
-
|
|
360
|
-
@context.query.logger.warn <<~WARN
|
|
361
|
-
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}).
|
|
362
|
-
This will return an error in future GraphQL-Ruby versions, as per the GraphQL specification
|
|
363
|
-
Learn about migrating here: https://graphql-ruby.org/api-doc/#{GraphQL::VERSION}/GraphQL/Schema.html#allow_legacy_invalid_return_type_conflicts-class_method
|
|
364
|
-
WARN
|
|
537
|
+
raise GraphQL::Error, "#{@schema}.legacy_invalid_scalar_conflicts returned unexpected value: #{legacy_handling.inspect}. Expected `nil`, String, or `:return_validation_error`."
|
|
365
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
|
|
366
547
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
if message_override
|
|
371
|
-
conflict.message = message_override
|
|
372
|
-
end
|
|
548
|
+
if return_error
|
|
549
|
+
conflict = conflicts[:return_type][response_key]
|
|
373
550
|
|
|
374
|
-
|
|
375
|
-
conflict.
|
|
376
|
-
@conflict_count += 1
|
|
551
|
+
if message_override
|
|
552
|
+
conflict.message = message_override
|
|
377
553
|
end
|
|
378
|
-
end
|
|
379
554
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
)
|
|
555
|
+
conflict.add_conflict(node1, "`#{t1.to_type_signature}`")
|
|
556
|
+
conflict.add_conflict(node2, "`#{t2.to_type_signature}`")
|
|
557
|
+
@conflict_count += 1
|
|
558
|
+
end
|
|
385
559
|
end
|
|
386
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
|
|
387
563
|
def return_types_conflict?(type1, type2)
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
elsif
|
|
395
|
-
true
|
|
396
|
-
elsif type1.non_null?
|
|
397
|
-
if type2.non_null?
|
|
398
|
-
return_types_conflict?(type1.of_type, type2.of_type)
|
|
399
|
-
else
|
|
400
|
-
true
|
|
401
|
-
end
|
|
402
|
-
elsif type2.non_null?
|
|
403
|
-
true
|
|
404
|
-
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?
|
|
405
571
|
type1 != type2
|
|
406
572
|
else
|
|
407
573
|
false
|
|
@@ -446,7 +612,7 @@ module GraphQL
|
|
|
446
612
|
if inner && inner.key?(return_type)
|
|
447
613
|
inner[return_type]
|
|
448
614
|
else
|
|
449
|
-
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)
|
|
450
616
|
inner ||= {}.compare_by_identity
|
|
451
617
|
inner[return_type] = result
|
|
452
618
|
@sub_fields_cache[node] = inner
|
|
@@ -458,6 +624,7 @@ module GraphQL
|
|
|
458
624
|
response_keys.each do |key, fields|
|
|
459
625
|
fields2 = response_keys2[key]
|
|
460
626
|
next unless fields2
|
|
627
|
+
next if field_groups_already_compared?(fields, fields2, mutually_exclusive)
|
|
461
628
|
|
|
462
629
|
fields_arr = fields.is_a?(Field) ? [fields] : fields
|
|
463
630
|
fields2_arr = fields2.is_a?(Field) ? [fields2] : fields2
|
|
@@ -475,6 +642,36 @@ module GraphQL
|
|
|
475
642
|
end
|
|
476
643
|
end
|
|
477
644
|
|
|
645
|
+
def field_groups_already_compared?(fields, fields2, mutually_exclusive)
|
|
646
|
+
signature1 = field_group_signature(fields)
|
|
647
|
+
signature2 = field_group_signature(fields2)
|
|
648
|
+
previous_comparisons = @compared_field_groups[signature1]
|
|
649
|
+
comparison_state = previous_comparisons && previous_comparisons[signature2]
|
|
650
|
+
|
|
651
|
+
if mutually_exclusive
|
|
652
|
+
return true if comparison_state
|
|
653
|
+
new_state = EXCLUSIVE_COMPARISON
|
|
654
|
+
else
|
|
655
|
+
return true if comparison_state == NONEXCLUSIVE_COMPARISON
|
|
656
|
+
new_state = NONEXCLUSIVE_COMPARISON
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
previous_comparisons ||= (@compared_field_groups[signature1] = {})
|
|
660
|
+
previous_comparisons[signature2] = new_state
|
|
661
|
+
|
|
662
|
+
reverse_comparisons = @compared_field_groups[signature2] ||= {}
|
|
663
|
+
reverse_comparisons[signature1] = new_state
|
|
664
|
+
false
|
|
665
|
+
end
|
|
666
|
+
|
|
667
|
+
def field_group_signature(fields)
|
|
668
|
+
if fields.is_a?(Field)
|
|
669
|
+
fields.comparison_key
|
|
670
|
+
else
|
|
671
|
+
@field_group_signatures[fields] ||= fields.map(&:comparison_key)
|
|
672
|
+
end
|
|
673
|
+
end
|
|
674
|
+
|
|
478
675
|
def same_arguments?(field1, field2)
|
|
479
676
|
arguments1 = field1.arguments
|
|
480
677
|
arguments2 = field2.arguments
|
|
@@ -508,47 +705,48 @@ module GraphQL
|
|
|
508
705
|
serialized_args
|
|
509
706
|
end
|
|
510
707
|
|
|
511
|
-
|
|
512
|
-
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)
|
|
513
726
|
if parents1.empty? || parents2.empty?
|
|
514
727
|
false
|
|
515
728
|
elsif parents1.length == parents2.length
|
|
729
|
+
cache = (@mutually_exclusive_cache ||= {}.compare_by_identity)
|
|
516
730
|
i = 0
|
|
517
731
|
len = parents1.length
|
|
518
|
-
|
|
519
732
|
while i < len
|
|
520
733
|
type1 = parents1[i - 1]
|
|
521
734
|
type2 = parents2[i - 1]
|
|
522
735
|
unless type1.equal?(type2)
|
|
523
|
-
inner =
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
cached = types_mutually_exclusive?(type1, type2)
|
|
528
|
-
inner[type2] = cached
|
|
529
|
-
end
|
|
530
|
-
else
|
|
531
|
-
cached = types_mutually_exclusive?(type1, type2)
|
|
532
|
-
inner = {}.compare_by_identity
|
|
533
|
-
inner[type2] = cached
|
|
534
|
-
@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?
|
|
535
740
|
end
|
|
536
|
-
return true if
|
|
741
|
+
return true if exclusive
|
|
537
742
|
end
|
|
538
743
|
i += 1
|
|
539
744
|
end
|
|
540
|
-
|
|
541
745
|
false
|
|
542
746
|
else
|
|
543
747
|
true
|
|
544
748
|
end
|
|
545
749
|
end
|
|
546
|
-
|
|
547
|
-
def types_mutually_exclusive?(type1, type2)
|
|
548
|
-
possible_right_types = @types.possible_types(type1)
|
|
549
|
-
possible_left_types = @types.possible_types(type2)
|
|
550
|
-
(possible_right_types & possible_left_types).empty?
|
|
551
|
-
end
|
|
552
750
|
end
|
|
553
751
|
end
|
|
554
752
|
end
|