graphql 1.10.2 → 2.0.21
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +21 -10
- data/lib/generators/graphql/enum_generator.rb +4 -10
- data/lib/generators/graphql/field_extractor.rb +31 -0
- data/lib/generators/graphql/input_generator.rb +50 -0
- data/lib/generators/graphql/install/mutation_root_generator.rb +34 -0
- data/lib/generators/graphql/{templates → install/templates}/base_mutation.erb +2 -0
- data/lib/generators/graphql/{templates → install/templates}/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +45 -8
- data/lib/generators/graphql/interface_generator.rb +7 -7
- data/lib/generators/graphql/loader_generator.rb +1 -0
- data/lib/generators/graphql/mutation_create_generator.rb +22 -0
- data/lib/generators/graphql/mutation_delete_generator.rb +22 -0
- data/lib/generators/graphql/mutation_generator.rb +6 -30
- data/lib/generators/graphql/mutation_update_generator.rb +22 -0
- data/lib/generators/graphql/object_generator.rb +28 -12
- data/lib/generators/graphql/orm_mutations_base.rb +40 -0
- data/lib/generators/graphql/relay.rb +49 -0
- data/lib/generators/graphql/relay_generator.rb +21 -0
- data/lib/generators/graphql/scalar_generator.rb +4 -2
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +8 -0
- data/lib/generators/graphql/templates/base_edge.erb +8 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +7 -1
- data/lib/generators/graphql/templates/graphql_controller.erb +16 -12
- data/lib/generators/graphql/templates/input.erb +9 -0
- data/lib/generators/graphql/templates/interface.erb +6 -2
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +3 -1
- data/lib/generators/graphql/templates/mutation_create.erb +20 -0
- data/lib/generators/graphql/templates/mutation_delete.erb +20 -0
- data/lib/generators/graphql/templates/mutation_update.erb +21 -0
- data/lib/generators/graphql/templates/node_type.erb +9 -0
- data/lib/generators/graphql/templates/object.erb +7 -3
- data/lib/generators/graphql/templates/query_type.erb +3 -3
- data/lib/generators/graphql/templates/scalar.erb +5 -1
- data/lib/generators/graphql/templates/schema.erb +24 -33
- data/lib/generators/graphql/templates/union.erb +6 -2
- data/lib/generators/graphql/type_generator.rb +47 -10
- data/lib/generators/graphql/union_generator.rb +5 -5
- data/lib/graphql/analysis/ast/field_usage.rb +30 -1
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +125 -117
- data/lib/graphql/analysis/ast/query_depth.rb +0 -1
- data/lib/graphql/analysis/ast/visitor.rb +52 -36
- data/lib/graphql/analysis/ast.rb +7 -8
- data/lib/graphql/analysis.rb +0 -7
- data/lib/graphql/backtrace/inspect_result.rb +0 -1
- data/lib/graphql/backtrace/table.rb +31 -18
- data/lib/graphql/backtrace/trace.rb +96 -0
- data/lib/graphql/backtrace/traced_error.rb +0 -1
- data/lib/graphql/backtrace/tracer.rb +39 -9
- data/lib/graphql/backtrace.rb +26 -18
- data/lib/graphql/dataloader/null_dataloader.rb +24 -0
- data/lib/graphql/dataloader/request.rb +19 -0
- data/lib/graphql/dataloader/request_all.rb +19 -0
- data/lib/graphql/dataloader/source.rb +164 -0
- data/lib/graphql/dataloader.rb +311 -0
- data/lib/graphql/date_encoding_error.rb +16 -0
- data/lib/graphql/deprecation.rb +9 -0
- data/lib/graphql/dig.rb +1 -1
- data/lib/graphql/execution/errors.rb +77 -44
- data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
- data/lib/graphql/execution/interpreter/arguments.rb +88 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +104 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
- data/lib/graphql/execution/interpreter/resolve.rb +62 -24
- data/lib/graphql/execution/interpreter/runtime.rb +826 -464
- data/lib/graphql/execution/interpreter.rb +206 -68
- data/lib/graphql/execution/lazy.rb +11 -21
- data/lib/graphql/execution/lookahead.rb +55 -136
- data/lib/graphql/execution/multiplex.rb +6 -162
- data/lib/graphql/execution.rb +11 -4
- data/lib/graphql/filter.rb +7 -2
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/integer_encoding_error.rb +18 -2
- data/lib/graphql/introspection/directive_location_enum.rb +2 -2
- data/lib/graphql/introspection/directive_type.rb +11 -5
- data/lib/graphql/introspection/dynamic_fields.rb +3 -8
- data/lib/graphql/introspection/entry_points.rb +4 -17
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +9 -5
- data/lib/graphql/introspection/input_value_type.rb +15 -3
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/schema_type.rb +11 -6
- data/lib/graphql/introspection/type_type.rb +31 -14
- data/lib/graphql/introspection.rb +100 -0
- data/lib/graphql/invalid_null_error.rb +18 -0
- data/lib/graphql/language/block_string.rb +20 -5
- data/lib/graphql/language/cache.rb +37 -0
- data/lib/graphql/language/document_from_schema_definition.rb +96 -44
- data/lib/graphql/language/lexer.rb +216 -1462
- data/lib/graphql/language/nodes.rb +126 -129
- data/lib/graphql/language/parser.rb +997 -933
- data/lib/graphql/language/parser.y +148 -118
- data/lib/graphql/language/printer.rb +48 -23
- data/lib/graphql/language/sanitized_printer.rb +222 -0
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/language/visitor.rb +192 -84
- data/lib/graphql/language.rb +2 -0
- data/lib/graphql/name_validator.rb +2 -7
- data/lib/graphql/pagination/active_record_relation_connection.rb +45 -3
- data/lib/graphql/pagination/array_connection.rb +6 -4
- data/lib/graphql/pagination/connection.rb +105 -23
- data/lib/graphql/pagination/connections.rb +62 -35
- data/lib/graphql/pagination/relation_connection.rb +88 -36
- data/lib/graphql/parse_error.rb +0 -1
- data/lib/graphql/query/context.rb +203 -198
- data/lib/graphql/query/fingerprint.rb +26 -0
- data/lib/graphql/query/input_validation_result.rb +33 -7
- data/lib/graphql/query/null_context.rb +22 -9
- data/lib/graphql/query/validation_pipeline.rb +16 -38
- data/lib/graphql/query/variable_validation_error.rb +3 -3
- data/lib/graphql/query/variables.rb +36 -12
- data/lib/graphql/query.rb +92 -44
- data/lib/graphql/railtie.rb +6 -102
- data/lib/graphql/rake_task/validate.rb +1 -1
- data/lib/graphql/rake_task.rb +41 -10
- data/lib/graphql/relay/range_add.rb +17 -10
- data/lib/graphql/relay.rb +0 -15
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/schema/addition.rb +245 -0
- data/lib/graphql/schema/argument.rb +250 -46
- data/lib/graphql/schema/base_64_encoder.rb +2 -0
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
- data/lib/graphql/schema/build_from_definition.rb +243 -89
- data/lib/graphql/schema/directive/deprecated.rb +1 -1
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +57 -0
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +14 -2
- data/lib/graphql/schema/directive.rb +108 -20
- data/lib/graphql/schema/enum.rb +105 -44
- data/lib/graphql/schema/enum_value.rb +15 -25
- data/lib/graphql/schema/field/connection_extension.rb +50 -30
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +476 -331
- data/lib/graphql/schema/field_extension.rb +86 -2
- data/lib/graphql/schema/find_inherited_value.rb +6 -8
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +133 -121
- data/lib/graphql/schema/interface.rb +17 -45
- data/lib/graphql/schema/introspection_system.rb +3 -8
- data/lib/graphql/schema/late_bound_type.rb +8 -2
- data/lib/graphql/schema/list.rb +25 -8
- data/lib/graphql/schema/loader.rb +139 -103
- data/lib/graphql/schema/member/base_dsl_methods.rb +29 -35
- data/lib/graphql/schema/member/build_type.rb +19 -14
- data/lib/graphql/schema/member/has_arguments.rb +310 -26
- data/lib/graphql/schema/member/has_ast_node.rb +16 -1
- data/lib/graphql/schema/member/has_deprecation_reason.rb +24 -0
- data/lib/graphql/schema/member/has_directives.rb +118 -0
- data/lib/graphql/schema/member/has_fields.rb +164 -42
- data/lib/graphql/schema/member/has_interfaces.rb +129 -0
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
- data/lib/graphql/schema/member/has_validators.rb +57 -0
- data/lib/graphql/schema/member/relay_shortcuts.rb +47 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +20 -3
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/member.rb +6 -6
- data/lib/graphql/schema/mutation.rb +4 -9
- data/lib/graphql/schema/non_null.rb +12 -7
- data/lib/graphql/schema/object.rb +35 -69
- data/lib/graphql/schema/printer.rb +16 -34
- data/lib/graphql/schema/relay_classic_mutation.rb +90 -43
- data/lib/graphql/schema/resolver/has_payload_type.rb +51 -11
- data/lib/graphql/schema/resolver.rb +144 -79
- data/lib/graphql/schema/scalar.rb +27 -18
- data/lib/graphql/schema/subscription.rb +55 -26
- data/lib/graphql/schema/timeout.rb +45 -35
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +21 -4
- data/lib/graphql/schema/union.rb +48 -13
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
- data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
- data/lib/graphql/schema/validator/format_validator.rb +48 -0
- data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
- data/lib/graphql/schema/validator/length_validator.rb +59 -0
- data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
- data/lib/graphql/schema/validator/required_validator.rb +82 -0
- data/lib/graphql/schema/validator.rb +171 -0
- data/lib/graphql/schema/warden.rb +185 -32
- data/lib/graphql/schema/wrapper.rb +0 -5
- data/lib/graphql/schema.rb +471 -1116
- data/lib/graphql/static_validation/all_rules.rb +3 -0
- data/lib/graphql/static_validation/base_visitor.rb +13 -27
- data/lib/graphql/static_validation/definition_dependencies.rb +7 -2
- data/lib/graphql/static_validation/error.rb +3 -1
- data/lib/graphql/static_validation/literal_validator.rb +69 -26
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +44 -87
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +22 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +28 -22
- data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +12 -6
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +13 -13
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +92 -49
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
- data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
- data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -2
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +6 -7
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +13 -7
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +9 -10
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +14 -8
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +4 -2
- data/lib/graphql/static_validation/validation_context.rb +13 -3
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +32 -20
- data/lib/graphql/static_validation.rb +1 -2
- data/lib/graphql/string_encoding_error.rb +13 -3
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +126 -19
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +58 -0
- data/lib/graphql/subscriptions/event.rb +81 -35
- data/lib/graphql/subscriptions/instrumentation.rb +0 -52
- data/lib/graphql/subscriptions/serialize.rb +53 -6
- data/lib/graphql/subscriptions.rb +113 -58
- data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +8 -21
- data/lib/graphql/tracing/appoptics_trace.rb +231 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
- data/lib/graphql/tracing/appsignal_trace.rb +77 -0
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- data/lib/graphql/tracing/data_dog_trace.rb +148 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +26 -2
- data/lib/graphql/tracing/legacy_trace.rb +65 -0
- data/lib/graphql/tracing/new_relic_trace.rb +75 -0
- data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
- data/lib/graphql/tracing/notifications_trace.rb +42 -0
- data/lib/graphql/tracing/notifications_tracing.rb +59 -0
- data/lib/graphql/tracing/platform_trace.rb +109 -0
- data/lib/graphql/tracing/platform_tracing.rb +64 -43
- data/lib/graphql/tracing/prometheus_trace.rb +89 -0
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +5 -2
- data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
- data/lib/graphql/tracing/scout_trace.rb +72 -0
- data/lib/graphql/tracing/scout_tracing.rb +11 -0
- data/lib/graphql/tracing/statsd_trace.rb +56 -0
- data/lib/graphql/tracing/statsd_tracing.rb +42 -0
- data/lib/graphql/tracing/trace.rb +75 -0
- data/lib/graphql/tracing.rb +23 -71
- data/lib/graphql/type_kinds.rb +6 -3
- data/lib/graphql/types/big_int.rb +5 -1
- data/lib/graphql/types/int.rb +10 -3
- data/lib/graphql/types/iso_8601_date.rb +20 -9
- data/lib/graphql/types/iso_8601_date_time.rb +36 -10
- data/lib/graphql/types/relay/base_connection.rb +18 -92
- data/lib/graphql/types/relay/base_edge.rb +2 -34
- data/lib/graphql/types/relay/connection_behaviors.rb +176 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +75 -0
- data/lib/graphql/types/relay/has_node_field.rb +41 -0
- data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
- data/lib/graphql/types/relay/node.rb +2 -4
- data/lib/graphql/types/relay/node_behaviors.rb +25 -0
- data/lib/graphql/types/relay/page_info.rb +2 -14
- data/lib/graphql/types/relay/page_info_behaviors.rb +30 -0
- data/lib/graphql/types/relay.rb +10 -5
- data/lib/graphql/types/string.rb +8 -2
- data/lib/graphql/unauthorized_error.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +54 -65
- data/readme.md +3 -6
- metadata +116 -236
- data/lib/graphql/analysis/analyze_query.rb +0 -91
- data/lib/graphql/analysis/field_usage.rb +0 -45
- data/lib/graphql/analysis/max_query_complexity.rb +0 -26
- data/lib/graphql/analysis/max_query_depth.rb +0 -26
- data/lib/graphql/analysis/query_complexity.rb +0 -88
- data/lib/graphql/analysis/query_depth.rb +0 -43
- data/lib/graphql/analysis/reducer_state.rb +0 -48
- data/lib/graphql/argument.rb +0 -131
- data/lib/graphql/authorization.rb +0 -82
- data/lib/graphql/backwards_compatibility.rb +0 -60
- data/lib/graphql/base_type.rb +0 -230
- data/lib/graphql/boolean_type.rb +0 -2
- data/lib/graphql/compatibility/execution_specification/counter_schema.rb +0 -53
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +0 -200
- data/lib/graphql/compatibility/execution_specification.rb +0 -435
- data/lib/graphql/compatibility/lazy_execution_specification/lazy_schema.rb +0 -111
- data/lib/graphql/compatibility/lazy_execution_specification.rb +0 -213
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +0 -87
- data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +0 -79
- data/lib/graphql/compatibility/query_parser_specification.rb +0 -264
- data/lib/graphql/compatibility/schema_parser_specification.rb +0 -680
- data/lib/graphql/compatibility.rb +0 -5
- data/lib/graphql/define/assign_argument.rb +0 -12
- data/lib/graphql/define/assign_connection.rb +0 -13
- data/lib/graphql/define/assign_enum_value.rb +0 -18
- data/lib/graphql/define/assign_global_id_field.rb +0 -11
- data/lib/graphql/define/assign_mutation_function.rb +0 -34
- data/lib/graphql/define/assign_object_field.rb +0 -42
- data/lib/graphql/define/defined_object_proxy.rb +0 -53
- data/lib/graphql/define/instance_definable.rb +0 -210
- data/lib/graphql/define/no_definition_error.rb +0 -7
- data/lib/graphql/define/non_null_with_bang.rb +0 -16
- data/lib/graphql/define/type_definer.rb +0 -31
- data/lib/graphql/define.rb +0 -31
- data/lib/graphql/deprecated_dsl.rb +0 -42
- data/lib/graphql/directive/deprecated_directive.rb +0 -2
- data/lib/graphql/directive/include_directive.rb +0 -2
- data/lib/graphql/directive/skip_directive.rb +0 -2
- data/lib/graphql/directive.rb +0 -107
- data/lib/graphql/enum_type.rb +0 -127
- data/lib/graphql/execution/execute.rb +0 -326
- data/lib/graphql/execution/flatten.rb +0 -40
- data/lib/graphql/execution/instrumentation.rb +0 -92
- data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
- data/lib/graphql/execution/lazy/resolve.rb +0 -91
- data/lib/graphql/execution/typecast.rb +0 -50
- data/lib/graphql/field/resolve.rb +0 -59
- data/lib/graphql/field.rb +0 -222
- data/lib/graphql/float_type.rb +0 -2
- data/lib/graphql/function.rb +0 -124
- data/lib/graphql/id_type.rb +0 -2
- data/lib/graphql/input_object_type.rb +0 -132
- data/lib/graphql/int_type.rb +0 -2
- data/lib/graphql/interface_type.rb +0 -65
- data/lib/graphql/internal_representation/document.rb +0 -27
- data/lib/graphql/internal_representation/node.rb +0 -206
- data/lib/graphql/internal_representation/print.rb +0 -51
- data/lib/graphql/internal_representation/rewrite.rb +0 -184
- data/lib/graphql/internal_representation/scope.rb +0 -88
- data/lib/graphql/internal_representation/visit.rb +0 -36
- data/lib/graphql/internal_representation.rb +0 -7
- data/lib/graphql/language/lexer.rl +0 -258
- data/lib/graphql/list_type.rb +0 -80
- data/lib/graphql/literal_validation_error.rb +0 -6
- data/lib/graphql/non_null_type.rb +0 -71
- data/lib/graphql/object_type.rb +0 -121
- data/lib/graphql/query/arguments.rb +0 -188
- data/lib/graphql/query/arguments_cache.rb +0 -25
- data/lib/graphql/query/executor.rb +0 -53
- data/lib/graphql/query/literal_input.rb +0 -136
- data/lib/graphql/query/serial_execution/field_resolution.rb +0 -92
- data/lib/graphql/query/serial_execution/operation_resolution.rb +0 -19
- data/lib/graphql/query/serial_execution/selection_resolution.rb +0 -23
- data/lib/graphql/query/serial_execution/value_resolution.rb +0 -87
- data/lib/graphql/query/serial_execution.rb +0 -39
- data/lib/graphql/relay/array_connection.rb +0 -85
- data/lib/graphql/relay/base_connection.rb +0 -176
- data/lib/graphql/relay/connection_instrumentation.rb +0 -54
- data/lib/graphql/relay/connection_resolve.rb +0 -43
- data/lib/graphql/relay/connection_type.rb +0 -41
- data/lib/graphql/relay/edge.rb +0 -27
- data/lib/graphql/relay/edge_type.rb +0 -19
- data/lib/graphql/relay/edges_instrumentation.rb +0 -40
- data/lib/graphql/relay/global_id_resolve.rb +0 -18
- data/lib/graphql/relay/mongo_relation_connection.rb +0 -50
- data/lib/graphql/relay/mutation/instrumentation.rb +0 -23
- data/lib/graphql/relay/mutation/resolve.rb +0 -56
- data/lib/graphql/relay/mutation/result.rb +0 -38
- data/lib/graphql/relay/mutation.rb +0 -105
- data/lib/graphql/relay/node.rb +0 -36
- data/lib/graphql/relay/page_info.rb +0 -7
- data/lib/graphql/relay/relation_connection.rb +0 -190
- data/lib/graphql/relay/type_extensions.rb +0 -30
- data/lib/graphql/scalar_type.rb +0 -76
- data/lib/graphql/schema/catchall_middleware.rb +0 -35
- data/lib/graphql/schema/default_parse_error.rb +0 -10
- data/lib/graphql/schema/default_type_error.rb +0 -15
- data/lib/graphql/schema/member/accepts_definition.rb +0 -152
- data/lib/graphql/schema/member/cached_graphql_definition.rb +0 -31
- data/lib/graphql/schema/member/instrumentation.rb +0 -132
- data/lib/graphql/schema/middleware_chain.rb +0 -82
- data/lib/graphql/schema/possible_types.rb +0 -39
- data/lib/graphql/schema/rescue_middleware.rb +0 -60
- data/lib/graphql/schema/timeout_middleware.rb +0 -86
- data/lib/graphql/schema/traversal.rb +0 -228
- data/lib/graphql/schema/validation.rb +0 -303
- data/lib/graphql/static_validation/default_visitor.rb +0 -15
- data/lib/graphql/static_validation/no_validate_visitor.rb +0 -10
- data/lib/graphql/string_type.rb +0 -2
- data/lib/graphql/subscriptions/subscription_root.rb +0 -65
- data/lib/graphql/tracing/skylight_tracing.rb +0 -70
- data/lib/graphql/types/relay/base_field.rb +0 -22
- data/lib/graphql/types/relay/base_interface.rb +0 -29
- data/lib/graphql/types/relay/base_object.rb +0 -26
- data/lib/graphql/types/relay/node_field.rb +0 -43
- data/lib/graphql/types/relay/nodes_field.rb +0 -45
- data/lib/graphql/union_type.rb +0 -113
- data/lib/graphql/upgrader/member.rb +0 -936
- data/lib/graphql/upgrader/schema.rb +0 -37
@@ -17,23 +17,22 @@ module GraphQL
|
|
17
17
|
if type.nil?
|
18
18
|
# This is handled by another validator
|
19
19
|
else
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
20
|
+
validation_result = context.validate_literal(value, type)
|
21
|
+
|
22
|
+
if !validation_result.valid?
|
23
|
+
problems = validation_result.problems
|
24
|
+
first_problem = problems && problems.first
|
25
|
+
if first_problem
|
26
|
+
error_message = first_problem["explanation"]
|
27
|
+
end
|
27
28
|
|
28
|
-
if !valid
|
29
29
|
error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
|
30
|
-
VariableDefaultValuesAreCorrectlyTypedError
|
31
30
|
add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new(
|
32
31
|
error_message,
|
33
32
|
nodes: node,
|
34
33
|
name: node.name,
|
35
34
|
type: type.to_type_signature,
|
36
|
-
error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_TYPE]
|
35
|
+
error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_TYPE],
|
37
36
|
))
|
38
37
|
end
|
39
38
|
end
|
@@ -22,15 +22,15 @@ module GraphQL
|
|
22
22
|
node_values = node_values.select { |value| value.is_a? GraphQL::Language::Nodes::VariableIdentifier }
|
23
23
|
|
24
24
|
if node_values.any?
|
25
|
-
|
25
|
+
argument_owner = case parent
|
26
26
|
when GraphQL::Language::Nodes::Field
|
27
|
-
context.field_definition
|
27
|
+
context.field_definition
|
28
28
|
when GraphQL::Language::Nodes::Directive
|
29
|
-
context.directive_definition
|
29
|
+
context.directive_definition
|
30
30
|
when GraphQL::Language::Nodes::InputObject
|
31
31
|
arg_type = context.argument_definition.type.unwrap
|
32
|
-
if arg_type.
|
33
|
-
|
32
|
+
if arg_type.kind.input_object?
|
33
|
+
arg_type
|
34
34
|
else
|
35
35
|
# This is some kind of error
|
36
36
|
nil
|
@@ -43,7 +43,7 @@ module GraphQL
|
|
43
43
|
var_defn_ast = @declared_variables[node_value.name]
|
44
44
|
# Might be undefined :(
|
45
45
|
# VariablesAreUsedAndDefined can't finalize its search until the end of the document.
|
46
|
-
var_defn_ast &&
|
46
|
+
var_defn_ast && argument_owner && validate_usage(argument_owner, node, var_defn_ast)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
super
|
@@ -51,7 +51,7 @@ module GraphQL
|
|
51
51
|
|
52
52
|
private
|
53
53
|
|
54
|
-
def validate_usage(
|
54
|
+
def validate_usage(argument_owner, arg_node, ast_var)
|
55
55
|
var_type = context.schema.type_from_ast(ast_var.type, context: context)
|
56
56
|
if var_type.nil?
|
57
57
|
return
|
@@ -65,9 +65,15 @@ module GraphQL
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
arg_defn =
|
68
|
+
arg_defn = context.warden.get_argument(argument_owner, arg_node.name)
|
69
69
|
arg_defn_type = arg_defn.type
|
70
70
|
|
71
|
+
# If the argument is non-null, but it was given a default value,
|
72
|
+
# then treat it as nullable in practice, see https://github.com/rmosolgo/graphql-ruby/issues/3793
|
73
|
+
if arg_defn_type.non_null? && arg_defn.default_value?
|
74
|
+
arg_defn_type = arg_defn_type.of_type
|
75
|
+
end
|
76
|
+
|
71
77
|
var_inner_type = var_type.unwrap
|
72
78
|
arg_inner_type = arg_defn_type.unwrap
|
73
79
|
|
@@ -126,8 +126,9 @@ module GraphQL
|
|
126
126
|
node_variables
|
127
127
|
.select { |name, usage| usage.declared? && !usage.used? }
|
128
128
|
.each { |var_name, usage|
|
129
|
+
declared_by_error_name = usage.declared_by.name || "anonymous #{usage.declared_by.operation_type}"
|
129
130
|
add_error(GraphQL::StaticValidation::VariablesAreUsedAndDefinedError.new(
|
130
|
-
"Variable $#{var_name} is declared by #{
|
131
|
+
"Variable $#{var_name} is declared by #{declared_by_error_name} but not used",
|
131
132
|
nodes: usage.declared_by,
|
132
133
|
path: usage.path,
|
133
134
|
name: var_name,
|
@@ -139,8 +140,9 @@ module GraphQL
|
|
139
140
|
node_variables
|
140
141
|
.select { |name, usage| usage.used? && !usage.declared? }
|
141
142
|
.each { |var_name, usage|
|
143
|
+
used_by_error_name = usage.used_by.name || "anonymous #{usage.used_by.operation_type}"
|
142
144
|
add_error(GraphQL::StaticValidation::VariablesAreUsedAndDefinedError.new(
|
143
|
-
"Variable $#{var_name} is used by #{
|
145
|
+
"Variable $#{var_name} is used by #{used_by_error_name} but not declared",
|
144
146
|
nodes: usage.ast_node,
|
145
147
|
path: usage.path,
|
146
148
|
name: var_name,
|
@@ -15,14 +15,16 @@ module GraphQL
|
|
15
15
|
extend Forwardable
|
16
16
|
|
17
17
|
attr_reader :query, :errors, :visitor,
|
18
|
-
:on_dependency_resolve_handlers
|
18
|
+
:on_dependency_resolve_handlers,
|
19
|
+
:max_errors
|
19
20
|
|
20
21
|
def_delegators :@query, :schema, :document, :fragments, :operations, :warden
|
21
22
|
|
22
|
-
def initialize(query, visitor_class)
|
23
|
+
def initialize(query, visitor_class, max_errors)
|
23
24
|
@query = query
|
24
25
|
@literal_validator = LiteralValidator.new(context: query.context)
|
25
26
|
@errors = []
|
27
|
+
@max_errors = max_errors || Float::INFINITY
|
26
28
|
@on_dependency_resolve_handlers = []
|
27
29
|
@visitor = visitor_class.new(document, self)
|
28
30
|
end
|
@@ -35,9 +37,17 @@ module GraphQL
|
|
35
37
|
@on_dependency_resolve_handlers << handler
|
36
38
|
end
|
37
39
|
|
38
|
-
def
|
40
|
+
def validate_literal(ast_value, type)
|
39
41
|
@literal_validator.validate(ast_value, type)
|
40
42
|
end
|
43
|
+
|
44
|
+
def too_many_errors?
|
45
|
+
@errors.length >= @max_errors
|
46
|
+
end
|
47
|
+
|
48
|
+
def schema_directives
|
49
|
+
@schema_directives ||= schema.directives
|
50
|
+
end
|
41
51
|
end
|
42
52
|
end
|
43
53
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class ValidationTimeoutError < StaticValidation::Error
|
5
|
+
def initialize(message, path: nil, nodes: [])
|
6
|
+
super(message, path: path, nodes: nodes)
|
7
|
+
end
|
8
|
+
|
9
|
+
# A hash representation of this Message
|
10
|
+
def to_h
|
11
|
+
extensions = {
|
12
|
+
"code" => code
|
13
|
+
}
|
14
|
+
|
15
|
+
super.merge({
|
16
|
+
"extensions" => extensions
|
17
|
+
})
|
18
|
+
end
|
19
|
+
|
20
|
+
def code
|
21
|
+
"validationTimeout"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require "timeout"
|
3
|
+
|
2
4
|
module GraphQL
|
3
5
|
module StaticValidation
|
4
6
|
# Initialized with a {GraphQL::Schema}, then it can validate {GraphQL::Language::Nodes::Documents}s based on that schema.
|
@@ -20,42 +22,52 @@ module GraphQL
|
|
20
22
|
|
21
23
|
# Validate `query` against the schema. Returns an array of message hashes.
|
22
24
|
# @param query [GraphQL::Query]
|
25
|
+
# @param validate [Boolean]
|
26
|
+
# @param timeout [Float] Number of seconds to wait before aborting validation. Any positive number may be used, including Floats to specify fractional seconds.
|
27
|
+
# @param max_errors [Integer] Maximum number of errors before aborting validation. Any positive number will limit the number of errors. Defaults to nil for no limit.
|
23
28
|
# @return [Array<Hash>]
|
24
|
-
def validate(query, validate: true)
|
25
|
-
query.
|
26
|
-
|
27
|
-
errors = if validate == false && can_skip_rewrite
|
29
|
+
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
30
|
+
query.current_trace.validate(validate: validate, query: query) do
|
31
|
+
errors = if validate == false
|
28
32
|
[]
|
29
33
|
else
|
30
34
|
rules_to_use = validate ? @rules : []
|
31
|
-
visitor_class = BaseVisitor.including_rules(rules_to_use
|
35
|
+
visitor_class = BaseVisitor.including_rules(rules_to_use)
|
32
36
|
|
33
|
-
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class)
|
37
|
+
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class, max_errors)
|
34
38
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
begin
|
40
|
+
# CAUTION: Usage of the timeout module makes the assumption that validation rules are stateless Ruby code that requires no cleanup if process was interrupted. This means no blocking IO calls, native gems, locks, or `rescue` clauses that must be reached.
|
41
|
+
# A timeout value of 0 or nil will execute the block without any timeout.
|
42
|
+
Timeout::timeout(timeout) do
|
43
|
+
catch(:too_many_validation_errors) do
|
44
|
+
context.visitor.visit
|
45
|
+
end
|
39
46
|
end
|
47
|
+
rescue Timeout::Error
|
48
|
+
handle_timeout(query, context)
|
40
49
|
end
|
41
50
|
|
42
|
-
context.visitor.visit
|
43
51
|
context.errors
|
44
52
|
end
|
45
53
|
|
46
|
-
|
47
|
-
irep = if errors.empty? && context
|
48
|
-
# Only return this if there are no errors and validation was actually run
|
49
|
-
context.visitor.rewrite_document
|
50
|
-
else
|
51
|
-
nil
|
52
|
-
end
|
53
|
-
|
54
54
|
{
|
55
55
|
errors: errors,
|
56
|
-
irep: irep,
|
57
56
|
}
|
58
57
|
end
|
58
|
+
rescue GraphQL::ExecutionError => e
|
59
|
+
{
|
60
|
+
errors: [e],
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Invoked when static validation times out.
|
65
|
+
# @param query [GraphQL::Query]
|
66
|
+
# @param context [GraphQL::StaticValidation::ValidationContext]
|
67
|
+
def handle_timeout(query, context)
|
68
|
+
context.errors << GraphQL::StaticValidation::ValidationTimeoutError.new(
|
69
|
+
"Timeout on validation of query"
|
70
|
+
)
|
59
71
|
end
|
60
72
|
end
|
61
73
|
end
|
@@ -4,9 +4,9 @@ require "graphql/static_validation/definition_dependencies"
|
|
4
4
|
require "graphql/static_validation/type_stack"
|
5
5
|
require "graphql/static_validation/validator"
|
6
6
|
require "graphql/static_validation/validation_context"
|
7
|
+
require "graphql/static_validation/validation_timeout_error"
|
7
8
|
require "graphql/static_validation/literal_validator"
|
8
9
|
require "graphql/static_validation/base_visitor"
|
9
|
-
require "graphql/static_validation/no_validate_visitor"
|
10
10
|
|
11
11
|
rules_glob = File.expand_path("../static_validation/rules/*.rb", __FILE__)
|
12
12
|
Dir.glob(rules_glob).each do |file|
|
@@ -14,5 +14,4 @@ Dir.glob(rules_glob).each do |file|
|
|
14
14
|
end
|
15
15
|
|
16
16
|
require "graphql/static_validation/all_rules"
|
17
|
-
require "graphql/static_validation/default_visitor"
|
18
17
|
require "graphql/static_validation/interpreter_visitor"
|
@@ -1,10 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
class StringEncodingError < GraphQL::RuntimeTypeError
|
4
|
-
attr_reader :string
|
5
|
-
def initialize(str)
|
4
|
+
attr_reader :string, :field, :path
|
5
|
+
def initialize(str, context:)
|
6
6
|
@string = str
|
7
|
-
|
7
|
+
@field = context[:current_field]
|
8
|
+
@path = context[:current_path]
|
9
|
+
message = "String #{str.inspect} was encoded as #{str.encoding}".dup
|
10
|
+
if @path
|
11
|
+
message << " @ #{@path.join(".")}"
|
12
|
+
end
|
13
|
+
if @field
|
14
|
+
message << " (#{@field.path})"
|
15
|
+
end
|
16
|
+
message << ". GraphQL requires an encoding compatible with UTF-8."
|
17
|
+
super(message)
|
8
18
|
end
|
9
19
|
end
|
10
20
|
end
|
@@ -4,10 +4,11 @@ module GraphQL
|
|
4
4
|
# A subscriptions implementation that sends data
|
5
5
|
# as ActionCable broadcastings.
|
6
6
|
#
|
7
|
-
#
|
7
|
+
# Some things to keep in mind:
|
8
8
|
#
|
9
9
|
# - No queueing system; ActiveJob should be added
|
10
10
|
# - Take care to reload context when re-delivering the subscription. (see {Query#subscription_update?})
|
11
|
+
# - Avoid the async ActionCable adapter and use the redis or PostgreSQL adapters instead. Otherwise calling #trigger won't work from background jobs or the Rails console.
|
11
12
|
#
|
12
13
|
# @example Adding ActionCableSubscriptions to your schema
|
13
14
|
# class MySchema < GraphQL::Schema
|
@@ -33,12 +34,12 @@ module GraphQL
|
|
33
34
|
# channel: self,
|
34
35
|
# }
|
35
36
|
#
|
36
|
-
# result = MySchema.execute(
|
37
|
+
# result = MySchema.execute(
|
37
38
|
# query: query,
|
38
39
|
# context: context,
|
39
40
|
# variables: variables,
|
40
41
|
# operation_name: operation_name
|
41
|
-
#
|
42
|
+
# )
|
42
43
|
#
|
43
44
|
# payload = {
|
44
45
|
# result: result.to_h,
|
@@ -85,27 +86,46 @@ module GraphQL
|
|
85
86
|
EVENT_PREFIX = "graphql-event:"
|
86
87
|
|
87
88
|
# @param serializer [<#dump(obj), #load(string)] Used for serializing messages before handing them to `.broadcast(msg)`
|
88
|
-
|
89
|
+
# @param namespace [string] Used to namespace events and subscriptions (default: '')
|
90
|
+
def initialize(serializer: Serialize, namespace: '', action_cable: ActionCable, action_cable_coder: ActiveSupport::JSON, **rest)
|
89
91
|
# A per-process map of subscriptions to deliver.
|
90
92
|
# This is provided by Rails, so let's use it
|
91
93
|
@subscriptions = Concurrent::Map.new
|
94
|
+
@events = Concurrent::Map.new do |h, k|
|
95
|
+
h.compute_if_absent(k) do
|
96
|
+
Concurrent::Map.new do |h2, k2|
|
97
|
+
h2.compute_if_absent(k2) { Concurrent::Array.new }
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
@action_cable = action_cable
|
102
|
+
@action_cable_coder = action_cable_coder
|
92
103
|
@serializer = serializer
|
104
|
+
@serialize_with_context = case @serializer.method(:load).arity
|
105
|
+
when 1
|
106
|
+
false
|
107
|
+
when 2
|
108
|
+
true
|
109
|
+
else
|
110
|
+
raise ArgumentError, "#{@serializer} must repond to `.load` accepting one or two arguments"
|
111
|
+
end
|
112
|
+
@transmit_ns = namespace
|
93
113
|
super
|
94
114
|
end
|
95
115
|
|
96
116
|
# An event was triggered; Push the data over ActionCable.
|
97
117
|
# Subscribers will re-evaluate locally.
|
98
118
|
def execute_all(event, object)
|
99
|
-
stream =
|
119
|
+
stream = stream_event_name(event)
|
100
120
|
message = @serializer.dump(object)
|
101
|
-
|
121
|
+
@action_cable.server.broadcast(stream, message)
|
102
122
|
end
|
103
123
|
|
104
124
|
# This subscription was re-evaluated.
|
105
125
|
# Send it to the specific stream where this client was waiting.
|
106
126
|
def deliver(subscription_id, result)
|
107
127
|
payload = { result: result.to_h, more: true }
|
108
|
-
|
128
|
+
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
109
129
|
end
|
110
130
|
|
111
131
|
# A query was run where these events were subscribed to.
|
@@ -113,33 +133,120 @@ module GraphQL
|
|
113
133
|
# It will receive notifications when events come in
|
114
134
|
# and re-evaluate the query locally.
|
115
135
|
def write_subscription(query, events)
|
116
|
-
channel = query.context
|
136
|
+
unless (channel = query.context[:channel])
|
137
|
+
raise GraphQL::Error, "This GraphQL Subscription client does not support the transport protocol expected"\
|
138
|
+
"by the backend Subscription Server implementation (graphql-ruby ActionCableSubscriptions in this case)."\
|
139
|
+
"Some official client implementation including Apollo (https://graphql-ruby.org/javascript_client/apollo_subscriptions.html), "\
|
140
|
+
"Relay Modern (https://graphql-ruby.org/javascript_client/relay_subscriptions.html#actioncable)."\
|
141
|
+
"GraphiQL via `graphiql-rails` may not work out of box (#1051)."
|
142
|
+
end
|
117
143
|
subscription_id = query.context[:subscription_id] ||= build_id
|
118
|
-
stream =
|
144
|
+
stream = stream_subscription_name(subscription_id)
|
119
145
|
channel.stream_from(stream)
|
120
146
|
@subscriptions[subscription_id] = query
|
121
147
|
events.each do |event|
|
122
|
-
|
123
|
-
|
124
|
-
|
148
|
+
# Setup a new listener to run all events with this topic in this process
|
149
|
+
setup_stream(channel, event)
|
150
|
+
# Add this event to the list of events to be updated
|
151
|
+
@events[event.topic][event.fingerprint] << event
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Every subscribing channel is listening here, but only one of them takes any action.
|
156
|
+
# This is so we can reuse payloads when possible, and make one payload to send to
|
157
|
+
# all subscribers.
|
158
|
+
#
|
159
|
+
# But the problem is, any channel could close at any time, so each channel has to
|
160
|
+
# be ready to take over the primary position.
|
161
|
+
#
|
162
|
+
# To make sure there's always one-and-only-one channel building payloads,
|
163
|
+
# let the listener belonging to the first event on the list be
|
164
|
+
# the one to build and publish payloads.
|
165
|
+
#
|
166
|
+
def setup_stream(channel, initial_event)
|
167
|
+
topic = initial_event.topic
|
168
|
+
channel.stream_from(stream_event_name(initial_event), coder: @action_cable_coder) do |message|
|
169
|
+
events_by_fingerprint = @events[topic]
|
170
|
+
object = nil
|
171
|
+
events_by_fingerprint.each do |_fingerprint, events|
|
172
|
+
if events.any? && events.first == initial_event
|
173
|
+
# The fingerprint has told us that this response should be shared by all subscribers,
|
174
|
+
# so just run it once, then deliver the result to every subscriber
|
175
|
+
first_event = events.first
|
176
|
+
first_subscription_id = first_event.context.fetch(:subscription_id)
|
177
|
+
object ||= load_action_cable_message(message, first_event.context)
|
178
|
+
result = execute_update(first_subscription_id, first_event, object)
|
179
|
+
if !result.nil?
|
180
|
+
# Having calculated the result _once_, send the same payload to all subscribers
|
181
|
+
events.each do |event|
|
182
|
+
subscription_id = event.context.fetch(:subscription_id)
|
183
|
+
deliver(subscription_id, result)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
125
187
|
end
|
188
|
+
nil
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# This is called to turn an ActionCable-broadcasted string (JSON)
|
193
|
+
# into a query-ready application object.
|
194
|
+
# @param message [String] n ActionCable-broadcasted string (JSON)
|
195
|
+
# @param context [GraphQL::Query::Context] the context of the first event for a given subscription fingerprint
|
196
|
+
def load_action_cable_message(message, context)
|
197
|
+
if @serialize_with_context
|
198
|
+
@serializer.load(message, context)
|
199
|
+
else
|
200
|
+
@serializer.load(message)
|
126
201
|
end
|
127
202
|
end
|
128
203
|
|
129
204
|
# Return the query from "storage" (in memory)
|
130
205
|
def read_subscription(subscription_id)
|
131
206
|
query = @subscriptions[subscription_id]
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
207
|
+
if query.nil?
|
208
|
+
# This can happen when a subscription is triggered from an unsubscribed channel,
|
209
|
+
# see https://github.com/rmosolgo/graphql-ruby/issues/2478.
|
210
|
+
# (This `nil` is handled by `#execute_update`)
|
211
|
+
nil
|
212
|
+
else
|
213
|
+
{
|
214
|
+
query_string: query.query_string,
|
215
|
+
variables: query.provided_variables,
|
216
|
+
context: query.context.to_h,
|
217
|
+
operation_name: query.operation_name,
|
218
|
+
}
|
219
|
+
end
|
138
220
|
end
|
139
221
|
|
140
222
|
# The channel was closed, forget about it.
|
141
223
|
def delete_subscription(subscription_id)
|
142
|
-
@subscriptions.delete(subscription_id)
|
224
|
+
query = @subscriptions.delete(subscription_id)
|
225
|
+
# In case this came from the server, tell the client to unsubscribe:
|
226
|
+
@action_cable.server.broadcast(stream_subscription_name(subscription_id), { more: false })
|
227
|
+
# This can be `nil` when `.trigger` happens inside an unsubscribed ActionCable channel,
|
228
|
+
# see https://github.com/rmosolgo/graphql-ruby/issues/2478
|
229
|
+
if query
|
230
|
+
events = query.context.namespace(:subscriptions)[:events]
|
231
|
+
events.each do |event|
|
232
|
+
ev_by_fingerprint = @events[event.topic]
|
233
|
+
ev_for_fingerprint = ev_by_fingerprint[event.fingerprint]
|
234
|
+
ev_for_fingerprint.delete(event)
|
235
|
+
if ev_for_fingerprint.empty?
|
236
|
+
ev_by_fingerprint.delete(event.fingerprint)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
private
|
243
|
+
|
244
|
+
def stream_subscription_name(subscription_id)
|
245
|
+
[SUBSCRIPTION_PREFIX, @transmit_ns, subscription_id].join
|
246
|
+
end
|
247
|
+
|
248
|
+
def stream_event_name(event)
|
249
|
+
[EVENT_PREFIX, @transmit_ns, event.topic].join
|
143
250
|
end
|
144
251
|
end
|
145
252
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Subscriptions
|
5
|
+
# Detect whether the current operation:
|
6
|
+
# - Is a subscription operation
|
7
|
+
# - Is completely broadcastable
|
8
|
+
#
|
9
|
+
# Assign the result to `context.namespace(:subscriptions)[:subscription_broadcastable]`
|
10
|
+
# @api private
|
11
|
+
# @see Subscriptions#broadcastable? for a public API
|
12
|
+
class BroadcastAnalyzer < GraphQL::Analysis::AST::Analyzer
|
13
|
+
def initialize(subject)
|
14
|
+
super
|
15
|
+
@default_broadcastable = subject.schema.subscriptions.default_broadcastable
|
16
|
+
# Maybe this will get set to false while analyzing
|
17
|
+
@subscription_broadcastable = true
|
18
|
+
end
|
19
|
+
|
20
|
+
# Only analyze subscription operations
|
21
|
+
def analyze?
|
22
|
+
@query.subscription?
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_enter_field(node, parent, visitor)
|
26
|
+
if (@subscription_broadcastable == false) || visitor.skipping?
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
current_field = visitor.field_definition
|
31
|
+
apply_broadcastable(current_field)
|
32
|
+
|
33
|
+
current_type = visitor.parent_type_definition
|
34
|
+
if current_type.kind.interface?
|
35
|
+
pt = @query.possible_types(current_type)
|
36
|
+
pt.each do |object_type|
|
37
|
+
ot_field = @query.get_field(object_type, current_field.graphql_name)
|
38
|
+
# Inherited fields would be exactly the same object;
|
39
|
+
# only check fields that are overrides of the inherited one
|
40
|
+
if ot_field && ot_field != current_field
|
41
|
+
apply_broadcastable(ot_field)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Assign the result to context.
|
48
|
+
# (This method is allowed to return an error, but we don't need to)
|
49
|
+
# @return [void]
|
50
|
+
def result
|
51
|
+
query.context.namespace(:subscriptions)[:subscription_broadcastable] = @subscription_broadcastable
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Modify `@subscription_broadcastable` based on `field_defn`'s configuration (and/or the default value)
|
58
|
+
def apply_broadcastable(field_defn)
|
59
|
+
current_field_broadcastable = field_defn.introspection? || field_defn.broadcastable?
|
60
|
+
case current_field_broadcastable
|
61
|
+
when nil
|
62
|
+
# If the value wasn't set, mix in the default value:
|
63
|
+
# - If the default is false and the current value is true, make it false
|
64
|
+
# - If the default is true and the current value is true, it stays true
|
65
|
+
# - If the default is false and the current value is false, keep it false
|
66
|
+
# - If the default is true and the current value is false, keep it false
|
67
|
+
@subscription_broadcastable = @subscription_broadcastable && @default_broadcastable
|
68
|
+
when false
|
69
|
+
# One non-broadcastable field is enough to make the whole subscription non-broadcastable
|
70
|
+
@subscription_broadcastable = false
|
71
|
+
when true
|
72
|
+
# Leave `@broadcastable_query` true if it's already true,
|
73
|
+
# but don't _set_ it to true if it was set to false by something else.
|
74
|
+
# Actually, just leave it!
|
75
|
+
else
|
76
|
+
raise ArgumentError, "Unexpected `.broadcastable?` value for #{field_defn.path}: #{current_field_broadcastable}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Subscriptions
|
4
|
+
class DefaultSubscriptionResolveExtension < GraphQL::Schema::FieldExtension
|
5
|
+
def resolve(context:, object:, arguments:)
|
6
|
+
has_override_implementation = @field.resolver ||
|
7
|
+
object.respond_to?(@field.resolver_method)
|
8
|
+
|
9
|
+
if !has_override_implementation
|
10
|
+
if context.query.subscription_update?
|
11
|
+
object.object
|
12
|
+
else
|
13
|
+
context.skip
|
14
|
+
end
|
15
|
+
else
|
16
|
+
yield(object, arguments)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def after_resolve(value:, context:, object:, arguments:, **rest)
|
21
|
+
if value.is_a?(GraphQL::ExecutionError)
|
22
|
+
value
|
23
|
+
elsif (events = context.namespace(:subscriptions)[:events])
|
24
|
+
# This is the first execution, so gather an Event
|
25
|
+
# for the backend to register:
|
26
|
+
event = Subscriptions::Event.new(
|
27
|
+
name: field.name,
|
28
|
+
arguments: arguments_without_field_extras(arguments: arguments),
|
29
|
+
context: context,
|
30
|
+
field: field,
|
31
|
+
)
|
32
|
+
events << event
|
33
|
+
value
|
34
|
+
elsif context.query.subscription_topic == Subscriptions::Event.serialize(
|
35
|
+
field.name,
|
36
|
+
arguments_without_field_extras(arguments: arguments),
|
37
|
+
field,
|
38
|
+
scope: (field.subscription_scope ? context[field.subscription_scope] : nil),
|
39
|
+
)
|
40
|
+
# This is a subscription update. The resolver returned `skip` if it should be skipped,
|
41
|
+
# or else it returned an object to resolve the update.
|
42
|
+
value
|
43
|
+
else
|
44
|
+
# This is a subscription update, but this event wasn't triggered.
|
45
|
+
context.skip
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def arguments_without_field_extras(arguments:)
|
52
|
+
arguments.dup.tap do |event_args|
|
53
|
+
field.extras.each { |k| event_args.delete(k) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|