graphql 2.0.32 → 2.5.22
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/lib/generators/graphql/detailed_trace_generator.rb +77 -0
- data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +49 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -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_resolver.erb +8 -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/create_graphql_detailed_traces.erb +10 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +5 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis/analyzer.rb +90 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +263 -0
- data/lib/graphql/analysis/query_depth.rb +58 -0
- data/lib/graphql/analysis/visitor.rb +280 -0
- data/lib/graphql/analysis.rb +95 -1
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/current.rb +57 -0
- data/lib/graphql/dashboard/application_controller.rb +41 -0
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- data/lib/graphql/dashboard/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +97 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
- data/lib/graphql/dashboard.rb +96 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
- data/lib/graphql/dataloader/active_record_source.rb +47 -0
- data/lib/graphql/dataloader/async_dataloader.rb +112 -0
- data/lib/graphql/dataloader/null_dataloader.rb +55 -10
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +35 -12
- data/lib/graphql/dataloader.rb +224 -149
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +23 -25
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
- data/lib/graphql/execution/interpreter/runtime.rb +363 -434
- data/lib/graphql/execution/interpreter.rb +91 -164
- data/lib/graphql/execution/lookahead.rb +105 -31
- data/lib/graphql/execution/multiplex.rb +7 -6
- data/lib/graphql/execution/next/field_resolve_step.rb +711 -0
- data/lib/graphql/execution/next/load_argument_step.rb +60 -0
- data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
- data/lib/graphql/execution/next/runner.rb +389 -0
- data/lib/graphql/execution/next/selections_step.rb +37 -0
- data/lib/graphql/execution/next.rb +70 -0
- data/lib/graphql/execution.rb +1 -0
- data/lib/graphql/execution_error.rb +13 -10
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +20 -6
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/schema_type.rb +8 -11
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +26 -17
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +90 -61
- data/lib/graphql/language/lexer.rb +319 -193
- data/lib/graphql/language/nodes.rb +136 -77
- data/lib/graphql/language/parser.rb +807 -1985
- data/lib/graphql/language/printer.rb +324 -151
- data/lib/graphql/language/sanitized_printer.rb +21 -23
- data/lib/graphql/language/static_visitor.rb +171 -0
- data/lib/graphql/language/visitor.rb +23 -83
- data/lib/graphql/language.rb +71 -1
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +30 -1
- data/lib/graphql/pagination/connections.rb +32 -0
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +82 -144
- data/lib/graphql/query/null_context.rb +15 -18
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query/validation_pipeline.rb +4 -4
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +126 -81
- data/lib/graphql/railtie.rb +16 -6
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
- data/lib/graphql/rubocop.rb +2 -0
- data/lib/graphql/schema/addition.rb +26 -13
- data/lib/graphql/schema/always_visible.rb +7 -2
- data/lib/graphql/schema/argument.rb +75 -9
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +123 -60
- data/lib/graphql/schema/directive/flagged.rb +4 -2
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive.rb +54 -2
- data/lib/graphql/schema/enum.rb +110 -27
- data/lib/graphql/schema/enum_value.rb +10 -2
- data/lib/graphql/schema/field/connection_extension.rb +15 -49
- data/lib/graphql/schema/field/scope_extension.rb +23 -7
- data/lib/graphql/schema/field.rb +245 -118
- data/lib/graphql/schema/field_extension.rb +34 -1
- data/lib/graphql/schema/has_single_input_argument.rb +160 -0
- data/lib/graphql/schema/input_object.rb +116 -60
- data/lib/graphql/schema/interface.rb +34 -16
- data/lib/graphql/schema/introspection_system.rb +8 -17
- data/lib/graphql/schema/late_bound_type.rb +4 -0
- data/lib/graphql/schema/list.rb +3 -3
- data/lib/graphql/schema/loader.rb +3 -4
- data/lib/graphql/schema/member/base_dsl_methods.rb +18 -2
- data/lib/graphql/schema/member/has_arguments.rb +132 -100
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +99 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +4 -4
- data/lib/graphql/schema/member/has_fields.rb +115 -15
- data/lib/graphql/schema/member/has_interfaces.rb +26 -12
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +17 -4
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +34 -8
- data/lib/graphql/schema/printer.rb +9 -7
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
- data/lib/graphql/schema/resolver.rb +90 -32
- data/lib/graphql/schema/scalar.rb +4 -9
- data/lib/graphql/schema/subscription.rb +63 -10
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/union.rb +2 -2
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator/required_validator.rb +92 -11
- data/lib/graphql/schema/validator.rb +3 -1
- data/lib/graphql/schema/visibility/migration.rb +188 -0
- data/lib/graphql/schema/visibility/profile.rb +445 -0
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +311 -0
- data/lib/graphql/schema/warden.rb +275 -103
- data/lib/graphql/schema.rb +950 -210
- data/lib/graphql/static_validation/all_rules.rb +3 -3
- data/lib/graphql/static_validation/base_visitor.rb +7 -6
- data/lib/graphql/static_validation/literal_validator.rb +6 -7
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -5
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
- data/lib/graphql/static_validation/validation_context.rb +21 -5
- data/lib/graphql/static_validation/validator.rb +9 -1
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +21 -4
- data/lib/graphql/subscriptions/serialize.rb +3 -1
- data/lib/graphql/subscriptions.rb +21 -17
- data/lib/graphql/testing/helpers.rb +161 -0
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +3 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +7 -3
- data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
- data/lib/graphql/tracing/appsignal_trace.rb +32 -59
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -162
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +156 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +183 -37
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +864 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
- data/lib/graphql/tracing/prometheus_trace.rb +73 -73
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -58
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +82 -0
- data/lib/graphql/tracing/statsd_trace.rb +33 -45
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +112 -1
- data/lib/graphql/tracing.rb +31 -28
- data/lib/graphql/type_kinds.rb +2 -1
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +44 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +18 -0
- data/lib/graphql/types/relay/has_node_field.rb +13 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
- data/lib/graphql/types/relay/node_behaviors.rb +13 -2
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/types.rb +18 -10
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/unauthorized_error.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +71 -54
- data/readme.md +12 -2
- metadata +233 -37
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -276
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/backtrace/inspect_result.rb +0 -50
- data/lib/graphql/backtrace/trace.rb +0 -96
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/language/parser.y +0 -560
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
|
@@ -26,7 +26,7 @@ module GraphQL
|
|
|
26
26
|
context.directive_definition || context.field_definition
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
parent_type = context.
|
|
29
|
+
parent_type = context.types.argument(defn, parent_name(parent, defn))
|
|
30
30
|
parent_type ? parent_type.type.unwrap : nil
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -34,16 +34,16 @@ module GraphQL
|
|
|
34
34
|
parent_type = get_parent_type(context, parent)
|
|
35
35
|
return unless parent_type && parent_type.kind.input_object?
|
|
36
36
|
|
|
37
|
-
required_fields = context.
|
|
38
|
-
.select{|arg| arg.type.kind.non_null?}
|
|
39
|
-
.map(&:graphql_name)
|
|
37
|
+
required_fields = context.types.arguments(parent_type)
|
|
38
|
+
.select{ |arg| arg.type.kind.non_null? && !arg.default_value? }
|
|
39
|
+
.map!(&:graphql_name)
|
|
40
40
|
|
|
41
41
|
present_fields = ast_node.arguments.map(&:name)
|
|
42
42
|
missing_fields = required_fields - present_fields
|
|
43
43
|
|
|
44
44
|
missing_fields.each do |missing_field|
|
|
45
45
|
path = [*context.path, missing_field]
|
|
46
|
-
missing_field_type = context.
|
|
46
|
+
missing_field_type = context.types.argument(parent_type, missing_field).type
|
|
47
47
|
add_error(RequiredInputObjectAttributesArePresentError.new(
|
|
48
48
|
"Argument '#{missing_field}' on InputObject '#{parent_type.to_type_signature}' is required. Expected type #{missing_field_type.to_type_signature}",
|
|
49
49
|
argument_name: missing_field,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
module StaticValidation
|
|
4
|
+
module SubscriptionRootExistsAndSingleSubscriptionSelection
|
|
5
|
+
def on_operation_definition(node, parent)
|
|
6
|
+
if node.operation_type == "subscription"
|
|
7
|
+
if context.types.subscription_root.nil?
|
|
8
|
+
add_error(GraphQL::StaticValidation::SubscriptionRootExistsError.new(
|
|
9
|
+
'Schema is not configured for subscriptions',
|
|
10
|
+
nodes: node
|
|
11
|
+
))
|
|
12
|
+
elsif node.selections.size != 1
|
|
13
|
+
add_error(GraphQL::StaticValidation::NotSingleSubscriptionError.new(
|
|
14
|
+
'A subscription operation may only have one selection',
|
|
15
|
+
nodes: node,
|
|
16
|
+
))
|
|
17
|
+
else
|
|
18
|
+
super
|
|
19
|
+
end
|
|
20
|
+
else
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -19,13 +19,17 @@ module GraphQL
|
|
|
19
19
|
:on_field,
|
|
20
20
|
]
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if node.directives.
|
|
22
|
+
VALIDATE_DIRECTIVE_LOCATION_ON_NODE = <<~RUBY
|
|
23
|
+
def %{method_name}(node, parent)
|
|
24
|
+
if !node.directives.empty?
|
|
25
25
|
validate_directive_location(node)
|
|
26
26
|
end
|
|
27
27
|
super(node, parent)
|
|
28
28
|
end
|
|
29
|
+
RUBY
|
|
30
|
+
DIRECTIVE_NODE_HOOKS.each do |method_name|
|
|
31
|
+
# Can't use `define_method {...}` here because the proc can't be isolated for use in non-main Ractors
|
|
32
|
+
module_eval(VALIDATE_DIRECTIVE_LOCATION_ON_NODE % { method_name: method_name }) # rubocop:disable Development/NoEvalCop
|
|
29
33
|
end
|
|
30
34
|
|
|
31
35
|
private
|
|
@@ -5,36 +5,27 @@ module GraphQL
|
|
|
5
5
|
def on_variable_definition(node, parent)
|
|
6
6
|
if !node.default_value.nil?
|
|
7
7
|
value = node.default_value
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
nodes: node,
|
|
12
|
-
name: node.name,
|
|
13
|
-
error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_ON_NON_NULL]
|
|
14
|
-
))
|
|
8
|
+
type = context.schema.type_from_ast(node.type, context: context)
|
|
9
|
+
if type.nil?
|
|
10
|
+
# This is handled by another validator
|
|
15
11
|
else
|
|
16
|
-
|
|
17
|
-
if type.nil?
|
|
18
|
-
# This is handled by another validator
|
|
19
|
-
else
|
|
20
|
-
validation_result = context.validate_literal(value, type)
|
|
12
|
+
validation_result = context.validate_literal(value, type)
|
|
21
13
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
|
|
30
|
-
add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new(
|
|
31
|
-
error_message,
|
|
32
|
-
nodes: node,
|
|
33
|
-
name: node.name,
|
|
34
|
-
type: type.to_type_signature,
|
|
35
|
-
error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_TYPE],
|
|
36
|
-
))
|
|
14
|
+
if !validation_result.valid?
|
|
15
|
+
problems = validation_result.problems
|
|
16
|
+
first_problem = problems && problems.first
|
|
17
|
+
if first_problem
|
|
18
|
+
error_message = first_problem["explanation"]
|
|
37
19
|
end
|
|
20
|
+
|
|
21
|
+
error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
|
|
22
|
+
add_error(GraphQL::StaticValidation::VariableDefaultValuesAreCorrectlyTypedError.new(
|
|
23
|
+
error_message,
|
|
24
|
+
nodes: node,
|
|
25
|
+
name: node.name,
|
|
26
|
+
type: type.to_type_signature,
|
|
27
|
+
error_type: VariableDefaultValuesAreCorrectlyTypedError::VIOLATIONS[:INVALID_TYPE],
|
|
28
|
+
))
|
|
38
29
|
end
|
|
39
30
|
end
|
|
40
31
|
end
|
|
@@ -4,7 +4,7 @@ module GraphQL
|
|
|
4
4
|
module VariableNamesAreUnique
|
|
5
5
|
def on_operation_definition(node, parent)
|
|
6
6
|
var_defns = node.variables
|
|
7
|
-
if var_defns.
|
|
7
|
+
if !var_defns.empty?
|
|
8
8
|
vars_by_name = Hash.new { |h, k| h[k] = [] }
|
|
9
9
|
var_defns.each { |v| vars_by_name[v.name] << v }
|
|
10
10
|
vars_by_name.each do |name, defns|
|
|
@@ -21,7 +21,7 @@ module GraphQL
|
|
|
21
21
|
end
|
|
22
22
|
node_values = node_values.select { |value| value.is_a? GraphQL::Language::Nodes::VariableIdentifier }
|
|
23
23
|
|
|
24
|
-
if node_values.
|
|
24
|
+
if !node_values.empty?
|
|
25
25
|
argument_owner = case parent
|
|
26
26
|
when GraphQL::Language::Nodes::Field
|
|
27
27
|
context.field_definition
|
|
@@ -65,7 +65,7 @@ module GraphQL
|
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
67
|
|
|
68
|
-
arg_defn =
|
|
68
|
+
arg_defn = @types.argument(argument_owner, arg_node.name)
|
|
69
69
|
arg_defn_type = arg_defn.type
|
|
70
70
|
|
|
71
71
|
# If the argument is non-null, but it was given a default value,
|
|
@@ -4,11 +4,20 @@ module GraphQL
|
|
|
4
4
|
module VariablesAreInputTypes
|
|
5
5
|
def on_variable_definition(node, parent)
|
|
6
6
|
type_name = get_type_name(node.type)
|
|
7
|
-
type = context.
|
|
7
|
+
type = context.query.types.type(type_name)
|
|
8
8
|
|
|
9
9
|
if type.nil?
|
|
10
|
+
@all_possible_input_type_names ||= begin
|
|
11
|
+
names = []
|
|
12
|
+
context.types.all_types.each { |(t)|
|
|
13
|
+
if t.kind.input?
|
|
14
|
+
names << t.graphql_name
|
|
15
|
+
end
|
|
16
|
+
}
|
|
17
|
+
names
|
|
18
|
+
end
|
|
10
19
|
add_error(GraphQL::StaticValidation::VariablesAreInputTypesError.new(
|
|
11
|
-
"#{type_name} isn't a defined input type (on $#{node.name})",
|
|
20
|
+
"#{type_name} isn't a defined input type (on $#{node.name})#{context.did_you_mean_suggestion(type_name, @all_possible_input_type_names)}",
|
|
12
21
|
nodes: node,
|
|
13
22
|
name: node.name,
|
|
14
23
|
type: type_name
|
|
@@ -8,20 +8,20 @@ module GraphQL
|
|
|
8
8
|
# It provides access to the schema & fragments which validators may read from.
|
|
9
9
|
#
|
|
10
10
|
# It holds a list of errors which each validator may add to.
|
|
11
|
-
#
|
|
12
|
-
# It also provides limited access to the {TypeStack} instance,
|
|
13
|
-
# which tracks state as you climb in and out of different fields.
|
|
14
11
|
class ValidationContext
|
|
15
12
|
extend Forwardable
|
|
16
13
|
|
|
17
14
|
attr_reader :query, :errors, :visitor,
|
|
18
15
|
:on_dependency_resolve_handlers,
|
|
19
|
-
:max_errors
|
|
16
|
+
:max_errors, :types, :schema
|
|
17
|
+
|
|
20
18
|
|
|
21
|
-
def_delegators :@query, :
|
|
19
|
+
def_delegators :@query, :document, :fragments, :operations
|
|
22
20
|
|
|
23
21
|
def initialize(query, visitor_class, max_errors)
|
|
24
22
|
@query = query
|
|
23
|
+
@types = query.types # TODO update migrated callers to use this accessor
|
|
24
|
+
@schema = query.schema
|
|
25
25
|
@literal_validator = LiteralValidator.new(context: query.context)
|
|
26
26
|
@errors = []
|
|
27
27
|
@max_errors = max_errors || Float::INFINITY
|
|
@@ -29,6 +29,7 @@ module GraphQL
|
|
|
29
29
|
@visitor = visitor_class.new(document, self)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
# TODO stop using def_delegators because of Array allocations
|
|
32
33
|
def_delegators :@visitor,
|
|
33
34
|
:path, :type_definition, :field_definition, :argument_definition,
|
|
34
35
|
:parent_type_definition, :directive_definition, :object_types, :dependencies
|
|
@@ -48,6 +49,21 @@ module GraphQL
|
|
|
48
49
|
def schema_directives
|
|
49
50
|
@schema_directives ||= schema.directives
|
|
50
51
|
end
|
|
52
|
+
|
|
53
|
+
def did_you_mean_suggestion(name, options)
|
|
54
|
+
if did_you_mean = schema.did_you_mean
|
|
55
|
+
suggestions = did_you_mean::SpellChecker.new(dictionary: options).correct(name)
|
|
56
|
+
case suggestions.size
|
|
57
|
+
when 0
|
|
58
|
+
""
|
|
59
|
+
when 1
|
|
60
|
+
" (Did you mean `#{suggestions.first}`?)"
|
|
61
|
+
else
|
|
62
|
+
last_sugg = suggestions.pop
|
|
63
|
+
" (Did you mean #{suggestions.map {|s| "`#{s}`"}.join(", ")} or `#{last_sugg}`?)"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
51
67
|
end
|
|
52
68
|
end
|
|
53
69
|
end
|
|
@@ -27,7 +27,10 @@ module GraphQL
|
|
|
27
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.
|
|
28
28
|
# @return [Array<Hash>]
|
|
29
29
|
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
|
30
|
+
errors = nil
|
|
31
|
+
query.current_trace.begin_validate(query, validate)
|
|
30
32
|
query.current_trace.validate(validate: validate, query: query) do
|
|
33
|
+
begin_t = Time.now
|
|
31
34
|
errors = if validate == false
|
|
32
35
|
[]
|
|
33
36
|
else
|
|
@@ -52,13 +55,18 @@ module GraphQL
|
|
|
52
55
|
end
|
|
53
56
|
|
|
54
57
|
{
|
|
58
|
+
remaining_timeout: timeout ? (timeout - (Time.now - begin_t)) : nil,
|
|
55
59
|
errors: errors,
|
|
56
60
|
}
|
|
57
61
|
end
|
|
58
62
|
rescue GraphQL::ExecutionError => e
|
|
63
|
+
errors = [e]
|
|
59
64
|
{
|
|
60
|
-
|
|
65
|
+
remaining_timeout: nil,
|
|
66
|
+
errors: errors,
|
|
61
67
|
}
|
|
68
|
+
ensure
|
|
69
|
+
query.current_trace.end_validate(query, validate, errors)
|
|
62
70
|
end
|
|
63
71
|
|
|
64
72
|
# Invoked when static validation times out.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
require "graphql/static_validation/error"
|
|
3
3
|
require "graphql/static_validation/definition_dependencies"
|
|
4
|
-
require "graphql/static_validation/type_stack"
|
|
5
4
|
require "graphql/static_validation/validator"
|
|
6
5
|
require "graphql/static_validation/validation_context"
|
|
7
6
|
require "graphql/static_validation/validation_timeout_error"
|
|
@@ -35,7 +35,7 @@ module GraphQL
|
|
|
35
35
|
# }
|
|
36
36
|
#
|
|
37
37
|
# result = MySchema.execute(
|
|
38
|
-
# query
|
|
38
|
+
# query,
|
|
39
39
|
# context: context,
|
|
40
40
|
# variables: variables,
|
|
41
41
|
# operation_name: operation_name
|
|
@@ -81,6 +81,7 @@ module GraphQL
|
|
|
81
81
|
# end
|
|
82
82
|
# end
|
|
83
83
|
#
|
|
84
|
+
# @see GraphQL::Testing::MockActionCable for test helpers
|
|
84
85
|
class ActionCableSubscriptions < GraphQL::Subscriptions
|
|
85
86
|
SUBSCRIPTION_PREFIX = "graphql-subscription:"
|
|
86
87
|
EVENT_PREFIX = "graphql-event:"
|
|
@@ -107,7 +108,7 @@ module GraphQL
|
|
|
107
108
|
when 2
|
|
108
109
|
true
|
|
109
110
|
else
|
|
110
|
-
raise ArgumentError, "#{@serializer} must
|
|
111
|
+
raise ArgumentError, "#{@serializer} must respond to `.load` accepting one or two arguments"
|
|
111
112
|
end
|
|
112
113
|
@transmit_ns = namespace
|
|
113
114
|
super
|
|
@@ -124,7 +125,8 @@ module GraphQL
|
|
|
124
125
|
# This subscription was re-evaluated.
|
|
125
126
|
# Send it to the specific stream where this client was waiting.
|
|
126
127
|
def deliver(subscription_id, result)
|
|
127
|
-
|
|
128
|
+
has_more = !result.context.namespace(:subscriptions)[:final_update]
|
|
129
|
+
payload = { result: result.to_h, more: has_more }
|
|
128
130
|
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
|
129
131
|
end
|
|
130
132
|
|
|
@@ -165,11 +167,12 @@ module GraphQL
|
|
|
165
167
|
#
|
|
166
168
|
def setup_stream(channel, initial_event)
|
|
167
169
|
topic = initial_event.topic
|
|
168
|
-
|
|
170
|
+
event_stream = stream_event_name(initial_event)
|
|
171
|
+
channel.stream_from(event_stream, coder: @action_cable_coder) do |message|
|
|
169
172
|
events_by_fingerprint = @events[topic]
|
|
170
173
|
object = nil
|
|
171
174
|
events_by_fingerprint.each do |_fingerprint, events|
|
|
172
|
-
if events.
|
|
175
|
+
if !events.empty? && events.first == initial_event
|
|
173
176
|
# The fingerprint has told us that this response should be shared by all subscribers,
|
|
174
177
|
# so just run it once, then deliver the result to every subscriber
|
|
175
178
|
first_event = events.first
|
|
@@ -9,7 +9,7 @@ module GraphQL
|
|
|
9
9
|
# Assign the result to `context.namespace(:subscriptions)[:subscription_broadcastable]`
|
|
10
10
|
# @api private
|
|
11
11
|
# @see Subscriptions#broadcastable? for a public API
|
|
12
|
-
class BroadcastAnalyzer < GraphQL::Analysis::
|
|
12
|
+
class BroadcastAnalyzer < GraphQL::Analysis::Analyzer
|
|
13
13
|
def initialize(subject)
|
|
14
14
|
super
|
|
15
15
|
@default_broadcastable = subject.schema.subscriptions.default_broadcastable
|
|
@@ -28,9 +28,8 @@ module GraphQL
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
current_field = visitor.field_definition
|
|
31
|
-
apply_broadcastable(current_field)
|
|
32
|
-
|
|
33
31
|
current_type = visitor.parent_type_definition
|
|
32
|
+
apply_broadcastable(current_type, current_field)
|
|
34
33
|
if current_type.kind.interface?
|
|
35
34
|
pt = @query.possible_types(current_type)
|
|
36
35
|
pt.each do |object_type|
|
|
@@ -38,7 +37,7 @@ module GraphQL
|
|
|
38
37
|
# Inherited fields would be exactly the same object;
|
|
39
38
|
# only check fields that are overrides of the inherited one
|
|
40
39
|
if ot_field && ot_field != current_field
|
|
41
|
-
apply_broadcastable(ot_field)
|
|
40
|
+
apply_broadcastable(object_type, ot_field)
|
|
42
41
|
end
|
|
43
42
|
end
|
|
44
43
|
end
|
|
@@ -55,10 +54,16 @@ module GraphQL
|
|
|
55
54
|
private
|
|
56
55
|
|
|
57
56
|
# Modify `@subscription_broadcastable` based on `field_defn`'s configuration (and/or the default value)
|
|
58
|
-
def apply_broadcastable(field_defn)
|
|
57
|
+
def apply_broadcastable(owner_type, field_defn)
|
|
59
58
|
current_field_broadcastable = field_defn.introspection? || field_defn.broadcastable?
|
|
59
|
+
|
|
60
|
+
if current_field_broadcastable.nil? && owner_type.respond_to?(:default_broadcastable?)
|
|
61
|
+
current_field_broadcastable = owner_type.default_broadcastable?
|
|
62
|
+
end
|
|
63
|
+
|
|
60
64
|
case current_field_broadcastable
|
|
61
65
|
when nil
|
|
66
|
+
query.logger.debug { "`broadcastable: nil` for field: #{field_defn.path}" }
|
|
62
67
|
# If the value wasn't set, mix in the default value:
|
|
63
68
|
# - If the default is false and the current value is true, make it false
|
|
64
69
|
# - If the default is true and the current value is true, it stays true
|
|
@@ -66,6 +71,7 @@ module GraphQL
|
|
|
66
71
|
# - If the default is true and the current value is false, keep it false
|
|
67
72
|
@subscription_broadcastable = @subscription_broadcastable && @default_broadcastable
|
|
68
73
|
when false
|
|
74
|
+
query.logger.debug { "`broadcastable: false` for field: #{field_defn.path}" }
|
|
69
75
|
# One non-broadcastable field is enough to make the whole subscription non-broadcastable
|
|
70
76
|
@subscription_broadcastable = false
|
|
71
77
|
when true
|
|
@@ -20,12 +20,22 @@ module GraphQL
|
|
|
20
20
|
def after_resolve(value:, context:, object:, arguments:, **rest)
|
|
21
21
|
if value.is_a?(GraphQL::ExecutionError)
|
|
22
22
|
value
|
|
23
|
+
elsif @field.resolver&.method_defined?(:subscription_written?) &&
|
|
24
|
+
(subscription_namespace = context.namespace(:subscriptions)) &&
|
|
25
|
+
(subscriptions_by_path = subscription_namespace[:subscriptions])
|
|
26
|
+
(subscription_instance = subscriptions_by_path[context.current_path])
|
|
27
|
+
# If it was already written, don't append this event to be written later
|
|
28
|
+
if !subscription_instance.subscription_written?
|
|
29
|
+
events = context.namespace(:subscriptions)[:events]
|
|
30
|
+
events << subscription_instance.event
|
|
31
|
+
end
|
|
32
|
+
value
|
|
23
33
|
elsif (events = context.namespace(:subscriptions)[:events])
|
|
24
34
|
# This is the first execution, so gather an Event
|
|
25
35
|
# for the backend to register:
|
|
26
36
|
event = Subscriptions::Event.new(
|
|
27
37
|
name: field.name,
|
|
28
|
-
arguments:
|
|
38
|
+
arguments: arguments,
|
|
29
39
|
context: context,
|
|
30
40
|
field: field,
|
|
31
41
|
)
|
|
@@ -33,7 +43,7 @@ module GraphQL
|
|
|
33
43
|
value
|
|
34
44
|
elsif context.query.subscription_topic == Subscriptions::Event.serialize(
|
|
35
45
|
field.name,
|
|
36
|
-
|
|
46
|
+
arguments,
|
|
37
47
|
field,
|
|
38
48
|
scope: (field.subscription_scope ? context[field.subscription_scope] : nil),
|
|
39
49
|
)
|
|
@@ -45,14 +55,6 @@ module GraphQL
|
|
|
45
55
|
context.skip
|
|
46
56
|
end
|
|
47
57
|
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
58
|
end
|
|
57
59
|
end
|
|
58
60
|
end
|
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
|
20
20
|
|
|
21
21
|
def initialize(name:, arguments:, field: nil, context: nil, scope: nil)
|
|
22
22
|
@name = name
|
|
23
|
-
@arguments = arguments
|
|
23
|
+
@arguments = self.class.arguments_without_field_extras(arguments: arguments, field: field)
|
|
24
24
|
@context = context
|
|
25
25
|
field ||= context.field
|
|
26
26
|
scope_key = field.subscription_scope
|
|
@@ -37,8 +37,9 @@ module GraphQL
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
# @return [String] an identifier for this unit of subscription
|
|
40
|
-
def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext)
|
|
40
|
+
def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext.instance)
|
|
41
41
|
subscription = field.resolver || GraphQL::Schema::Subscription
|
|
42
|
+
arguments = arguments_without_field_extras(field: field, arguments: arguments)
|
|
42
43
|
normalized_args = stringify_args(field, arguments.to_h, context)
|
|
43
44
|
subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
|
|
44
45
|
end
|
|
@@ -60,6 +61,16 @@ module GraphQL
|
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
class << self
|
|
64
|
+
def arguments_without_field_extras(arguments:, field:)
|
|
65
|
+
if !field.extras.empty?
|
|
66
|
+
arguments = arguments.dup
|
|
67
|
+
field.extras.each do |extra_key|
|
|
68
|
+
arguments.delete(extra_key)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
arguments
|
|
72
|
+
end
|
|
73
|
+
|
|
63
74
|
private
|
|
64
75
|
|
|
65
76
|
# This method does not support cyclic references in the Hash,
|
|
@@ -126,12 +137,18 @@ module GraphQL
|
|
|
126
137
|
when GraphQL::Schema::InputObject
|
|
127
138
|
stringify_args(arg_owner, args.to_h, context)
|
|
128
139
|
else
|
|
129
|
-
|
|
140
|
+
if arg_owner.is_a?(Class) && arg_owner < GraphQL::Schema::Enum
|
|
141
|
+
# `prepare:` may have made the value something other than
|
|
142
|
+
# a defined value of this enum -- use _that_ in this case.
|
|
143
|
+
arg_owner.coerce_isolated_input(args) || args
|
|
144
|
+
else
|
|
145
|
+
args
|
|
146
|
+
end
|
|
130
147
|
end
|
|
131
148
|
end
|
|
132
149
|
|
|
133
150
|
def get_arg_definition(arg_owner, arg_name, context)
|
|
134
|
-
|
|
151
|
+
context.types.argument(arg_owner, arg_name) || context.types.arguments(arg_owner).find { |v| v.keyword.to_s == arg_name }
|
|
135
152
|
end
|
|
136
153
|
end
|
|
137
154
|
end
|
|
@@ -146,8 +146,10 @@ module GraphQL
|
|
|
146
146
|
elsif obj.is_a?(Date) || obj.is_a?(Time)
|
|
147
147
|
# DateTime extends Date; for TimeWithZone, call `.utc` first.
|
|
148
148
|
{ TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
|
|
149
|
-
elsif obj.is_a?(OpenStruct)
|
|
149
|
+
elsif defined?(OpenStruct) && obj.is_a?(OpenStruct)
|
|
150
150
|
{ OPEN_STRUCT_KEY => dump_value(obj.to_h) }
|
|
151
|
+
elsif defined?(ActiveRecord::Relation) && obj.is_a?(ActiveRecord::Relation)
|
|
152
|
+
dump_value(obj.to_a)
|
|
151
153
|
else
|
|
152
154
|
obj
|
|
153
155
|
end
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
require "securerandom"
|
|
3
3
|
require "graphql/subscriptions/broadcast_analyzer"
|
|
4
4
|
require "graphql/subscriptions/event"
|
|
5
|
-
require "graphql/subscriptions/instrumentation"
|
|
6
5
|
require "graphql/subscriptions/serialize"
|
|
7
6
|
require "graphql/subscriptions/action_cable_subscriptions"
|
|
8
7
|
require "graphql/subscriptions/default_subscription_resolve_extension"
|
|
@@ -30,8 +29,6 @@ module GraphQL
|
|
|
30
29
|
raise ArgumentError, "Can't reinstall subscriptions. #{schema} is using #{schema.subscriptions}, can't also add #{self}"
|
|
31
30
|
end
|
|
32
31
|
|
|
33
|
-
instrumentation = Subscriptions::Instrumentation.new(schema: schema)
|
|
34
|
-
defn.instrument(:query, instrumentation)
|
|
35
32
|
options[:schema] = schema
|
|
36
33
|
schema.subscriptions = self.new(**options)
|
|
37
34
|
schema.add_subscription_extension_if_necessary
|
|
@@ -62,17 +59,17 @@ module GraphQL
|
|
|
62
59
|
# @return [void]
|
|
63
60
|
def trigger(event_name, args, object, scope: nil, context: {})
|
|
64
61
|
# Make something as context-like as possible, even though there isn't a current query:
|
|
65
|
-
dummy_query =
|
|
62
|
+
dummy_query = @schema.query_class.new(@schema, "{ __typename }", validate: false, context: context)
|
|
66
63
|
context = dummy_query.context
|
|
67
64
|
event_name = event_name.to_s
|
|
68
65
|
|
|
69
66
|
# Try with the verbatim input first:
|
|
70
|
-
field =
|
|
67
|
+
field = dummy_query.types.field(@schema.subscription, event_name) # rubocop:disable Development/ContextIsPassedCop
|
|
71
68
|
|
|
72
69
|
if field.nil?
|
|
73
70
|
# And if it wasn't found, normalize it:
|
|
74
71
|
normalized_event_name = normalize_name(event_name)
|
|
75
|
-
field =
|
|
72
|
+
field = dummy_query.types.field(@schema.subscription, normalized_event_name) # rubocop:disable Development/ContextIsPassedCop
|
|
76
73
|
if field.nil?
|
|
77
74
|
raise InvalidTriggerError, "No subscription matching trigger: #{event_name} (looked for #{@schema.subscription.graphql_name}.#{normalized_event_name})"
|
|
78
75
|
end
|
|
@@ -83,7 +80,7 @@ module GraphQL
|
|
|
83
80
|
|
|
84
81
|
# Normalize symbol-keyed args to strings, try camelizing them
|
|
85
82
|
# Should this accept a real context somehow?
|
|
86
|
-
normalized_args = normalize_arguments(normalized_event_name, field, args,
|
|
83
|
+
normalized_args = normalize_arguments(normalized_event_name, field, args, @schema.null_context)
|
|
87
84
|
|
|
88
85
|
event = Subscriptions::Event.new(
|
|
89
86
|
name: normalized_event_name,
|
|
@@ -125,10 +122,10 @@ module GraphQL
|
|
|
125
122
|
variables: variables,
|
|
126
123
|
root_value: object,
|
|
127
124
|
}
|
|
128
|
-
|
|
125
|
+
|
|
129
126
|
# merge event's and query's context together
|
|
130
127
|
context.merge!(event.context) unless event.context.nil? || context.nil?
|
|
131
|
-
|
|
128
|
+
|
|
132
129
|
execute_options[:validate] = validate_update?(**execute_options)
|
|
133
130
|
result = @schema.execute(**execute_options)
|
|
134
131
|
subscriptions_context = result.context.namespace(:subscriptions)
|
|
@@ -136,11 +133,9 @@ module GraphQL
|
|
|
136
133
|
result = nil
|
|
137
134
|
end
|
|
138
135
|
|
|
139
|
-
unsubscribed
|
|
140
|
-
|
|
141
|
-
if unsubscribed
|
|
136
|
+
if subscriptions_context[:unsubscribed] && !subscriptions_context[:final_update]
|
|
142
137
|
# `unsubscribe` was called, clean up on our side
|
|
143
|
-
#
|
|
138
|
+
# The transport should also send `{more: false}` to client
|
|
144
139
|
delete_subscription(subscription_id)
|
|
145
140
|
result = nil
|
|
146
141
|
end
|
|
@@ -164,7 +159,14 @@ module GraphQL
|
|
|
164
159
|
res = execute_update(subscription_id, event, object)
|
|
165
160
|
if !res.nil?
|
|
166
161
|
deliver(subscription_id, res)
|
|
162
|
+
|
|
163
|
+
if res.context.namespace(:subscriptions)[:unsubscribed]
|
|
164
|
+
# `unsubscribe` was called, clean up on our side
|
|
165
|
+
# The transport should also send `{more: false}` to client
|
|
166
|
+
delete_subscription(subscription_id)
|
|
167
|
+
end
|
|
167
168
|
end
|
|
169
|
+
|
|
168
170
|
end
|
|
169
171
|
|
|
170
172
|
# Event `event` occurred on `object`,
|
|
@@ -229,11 +231,11 @@ module GraphQL
|
|
|
229
231
|
|
|
230
232
|
# @return [Boolean] if true, then a query like this one would be broadcasted
|
|
231
233
|
def broadcastable?(query_str, **query_options)
|
|
232
|
-
query =
|
|
234
|
+
query = @schema.query_class.new(@schema, query_str, **query_options)
|
|
233
235
|
if !query.valid?
|
|
234
236
|
raise "Invalid query: #{query.validation_errors.map(&:to_h).inspect}"
|
|
235
237
|
end
|
|
236
|
-
GraphQL::Analysis
|
|
238
|
+
GraphQL::Analysis.analyze_query(query, @schema.query_analyzers)
|
|
237
239
|
query.context.namespace(:subscriptions)[:subscription_broadcastable]
|
|
238
240
|
end
|
|
239
241
|
|
|
@@ -248,6 +250,8 @@ module GraphQL
|
|
|
248
250
|
def normalize_arguments(event_name, arg_owner, args, context)
|
|
249
251
|
case arg_owner
|
|
250
252
|
when GraphQL::Schema::Field, Class
|
|
253
|
+
return args if args.nil?
|
|
254
|
+
|
|
251
255
|
if arg_owner.is_a?(Class) && !arg_owner.kind.input_object?
|
|
252
256
|
# it's a type, but not an input object
|
|
253
257
|
return args
|
|
@@ -287,7 +291,7 @@ module GraphQL
|
|
|
287
291
|
end
|
|
288
292
|
end
|
|
289
293
|
|
|
290
|
-
if missing_arg_names.
|
|
294
|
+
if !missing_arg_names.empty?
|
|
291
295
|
arg_owner_name = if arg_owner.is_a?(GraphQL::Schema::Field)
|
|
292
296
|
arg_owner.path
|
|
293
297
|
elsif arg_owner.is_a?(Class)
|
|
@@ -300,7 +304,7 @@ module GraphQL
|
|
|
300
304
|
|
|
301
305
|
normalized_args
|
|
302
306
|
when GraphQL::Schema::List
|
|
303
|
-
args
|
|
307
|
+
args&.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) }
|
|
304
308
|
when GraphQL::Schema::NonNull
|
|
305
309
|
normalize_arguments(event_name, arg_owner.of_type, args, context)
|
|
306
310
|
else
|