graphql 2.0.13 → 2.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/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 +3 -0
- data/lib/generators/graphql/mutation_delete_generator.rb +1 -1
- data/lib/generators/graphql/mutation_update_generator.rb +1 -1
- data/lib/generators/graphql/relay.rb +18 -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 +6 -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/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 +8 -0
- data/lib/graphql/analysis/analyzer.rb +89 -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 +183 -0
- data/lib/graphql/analysis/query_depth.rb +58 -0
- data/lib/graphql/analysis/visitor.rb +283 -0
- data/lib/graphql/analysis.rb +92 -1
- data/lib/graphql/backtrace/inspect_result.rb +0 -12
- data/lib/graphql/backtrace/table.rb +2 -2
- data/lib/graphql/backtrace/trace.rb +93 -0
- data/lib/graphql/backtrace/tracer.rb +1 -1
- data/lib/graphql/backtrace.rb +2 -1
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/dataloader/async_dataloader.rb +88 -0
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +89 -45
- data/lib/graphql/dataloader.rb +115 -142
- 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.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +33 -33
- data/lib/graphql/execution/interpreter/resolve.rb +19 -0
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +175 -0
- data/lib/graphql/execution/interpreter/runtime.rb +331 -455
- data/lib/graphql/execution/interpreter.rb +125 -61
- data/lib/graphql/execution/lazy.rb +6 -12
- data/lib/graphql/execution/lookahead.rb +124 -46
- data/lib/graphql/execution/multiplex.rb +3 -117
- data/lib/graphql/execution.rb +0 -1
- data/lib/graphql/introspection/directive_type.rb +3 -3
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/schema_type.rb +10 -13
- data/lib/graphql/introspection/type_type.rb +17 -10
- data/lib/graphql/introspection.rb +3 -2
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +75 -59
- data/lib/graphql/language/lexer.rb +358 -1506
- data/lib/graphql/language/nodes.rb +166 -93
- data/lib/graphql/language/parser.rb +795 -1953
- data/lib/graphql/language/printer.rb +340 -160
- data/lib/graphql/language/sanitized_printer.rb +21 -23
- data/lib/graphql/language/static_visitor.rb +167 -0
- data/lib/graphql/language/visitor.rb +188 -141
- data/lib/graphql/language.rb +61 -1
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/active_record_relation_connection.rb +0 -8
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +33 -6
- 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 +117 -112
- data/lib/graphql/query/null_context.rb +12 -25
- data/lib/graphql/query/validation_pipeline.rb +6 -5
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +86 -30
- data/lib/graphql/railtie.rb +9 -6
- data/lib/graphql/rake_task.rb +29 -11
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/schema/addition.rb +59 -23
- data/lib/graphql/schema/always_visible.rb +11 -0
- data/lib/graphql/schema/argument.rb +55 -26
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +56 -32
- data/lib/graphql/schema/directive/one_of.rb +24 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +15 -3
- data/lib/graphql/schema/enum.rb +35 -24
- data/lib/graphql/schema/enum_value.rb +2 -3
- data/lib/graphql/schema/field/connection_extension.rb +2 -16
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +147 -107
- data/lib/graphql/schema/field_extension.rb +1 -4
- data/lib/graphql/schema/find_inherited_value.rb +2 -7
- data/lib/graphql/schema/has_single_input_argument.rb +158 -0
- data/lib/graphql/schema/input_object.rb +47 -11
- data/lib/graphql/schema/interface.rb +15 -21
- data/lib/graphql/schema/introspection_system.rb +7 -17
- data/lib/graphql/schema/late_bound_type.rb +10 -0
- data/lib/graphql/schema/list.rb +2 -2
- data/lib/graphql/schema/loader.rb +2 -3
- data/lib/graphql/schema/member/base_dsl_methods.rb +18 -14
- data/lib/graphql/schema/member/build_type.rb +11 -3
- data/lib/graphql/schema/member/has_arguments.rb +170 -130
- data/lib/graphql/schema/member/has_ast_node.rb +12 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +3 -4
- data/lib/graphql/schema/member/has_directives.rb +81 -61
- data/lib/graphql/schema/member/has_fields.rb +100 -38
- data/lib/graphql/schema/member/has_interfaces.rb +65 -10
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +32 -6
- data/lib/graphql/schema/member/relay_shortcuts.rb +19 -0
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -0
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +16 -5
- data/lib/graphql/schema/printer.rb +11 -8
- data/lib/graphql/schema/relay_classic_mutation.rb +7 -129
- data/lib/graphql/schema/resolver/has_payload_type.rb +9 -9
- data/lib/graphql/schema/resolver.rb +47 -32
- data/lib/graphql/schema/scalar.rb +3 -3
- data/lib/graphql/schema/subscription.rb +11 -4
- data/lib/graphql/schema/subset.rb +397 -0
- data/lib/graphql/schema/timeout.rb +25 -29
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/type_membership.rb +3 -0
- data/lib/graphql/schema/union.rb +11 -2
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +60 -0
- data/lib/graphql/schema/validator.rb +4 -2
- data/lib/graphql/schema/warden.rb +238 -93
- data/lib/graphql/schema.rb +498 -103
- data/lib/graphql/static_validation/all_rules.rb +2 -1
- data/lib/graphql/static_validation/base_visitor.rb +7 -6
- data/lib/graphql/static_validation/definition_dependencies.rb +7 -1
- data/lib/graphql/static_validation/literal_validator.rb +24 -7
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -2
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +12 -4
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +10 -10
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
- 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/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 +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +5 -5
- data/lib/graphql/static_validation/validator.rb +4 -1
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +11 -4
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/event.rb +11 -10
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +20 -13
- data/lib/graphql/testing/helpers.rb +151 -0
- data/lib/graphql/testing.rb +2 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +16 -0
- data/lib/graphql/tracing/appoptics_trace.rb +251 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +2 -2
- data/lib/graphql/tracing/appsignal_trace.rb +77 -0
- data/lib/graphql/tracing/data_dog_trace.rb +183 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +9 -21
- data/lib/graphql/{execution/instrumentation.rb → tracing/legacy_hooks_trace.rb} +10 -28
- data/lib/graphql/tracing/legacy_trace.rb +69 -0
- data/lib/graphql/tracing/new_relic_trace.rb +75 -0
- data/lib/graphql/tracing/notifications_trace.rb +45 -0
- data/lib/graphql/tracing/platform_trace.rb +118 -0
- data/lib/graphql/tracing/platform_tracing.rb +17 -3
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +4 -2
- data/lib/graphql/tracing/prometheus_trace.rb +89 -0
- data/lib/graphql/tracing/prometheus_tracing.rb +3 -3
- data/lib/graphql/tracing/scout_trace.rb +72 -0
- data/lib/graphql/tracing/sentry_trace.rb +112 -0
- data/lib/graphql/tracing/statsd_trace.rb +56 -0
- data/lib/graphql/tracing/trace.rb +76 -0
- data/lib/graphql/tracing.rb +20 -40
- data/lib/graphql/type_kinds.rb +7 -4
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/base_connection.rb +1 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +68 -6
- data/lib/graphql/types/relay/edge_behaviors.rb +33 -5
- data/lib/graphql/types/relay/node_behaviors.rb +8 -2
- data/lib/graphql/types/relay/page_info_behaviors.rb +11 -2
- data/lib/graphql/types/relay.rb +0 -1
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/types.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +27 -20
- data/readme.md +13 -3
- metadata +96 -47
- 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 -269
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -53
- data/lib/graphql/language/lexer.rl +0 -280
- data/lib/graphql/language/parser.y +0 -554
- 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/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
- data/lib/graphql/types/relay/default_relay.rb +0 -21
@@ -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
|
@@ -91,7 +91,13 @@ module GraphQL
|
|
91
91
|
# A per-process map of subscriptions to deliver.
|
92
92
|
# This is provided by Rails, so let's use it
|
93
93
|
@subscriptions = Concurrent::Map.new
|
94
|
-
@events = 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
|
95
101
|
@action_cable = action_cable
|
96
102
|
@action_cable_coder = action_cable_coder
|
97
103
|
@serializer = serializer
|
@@ -101,7 +107,7 @@ module GraphQL
|
|
101
107
|
when 2
|
102
108
|
true
|
103
109
|
else
|
104
|
-
raise ArgumentError, "#{@serializer} must
|
110
|
+
raise ArgumentError, "#{@serializer} must respond to `.load` accepting one or two arguments"
|
105
111
|
end
|
106
112
|
@transmit_ns = namespace
|
107
113
|
super
|
@@ -118,7 +124,8 @@ module GraphQL
|
|
118
124
|
# This subscription was re-evaluated.
|
119
125
|
# Send it to the specific stream where this client was waiting.
|
120
126
|
def deliver(subscription_id, result)
|
121
|
-
|
127
|
+
has_more = !result.context.namespace(:subscriptions)[:final_update]
|
128
|
+
payload = { result: result.to_h, more: has_more }
|
122
129
|
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
123
130
|
end
|
124
131
|
|
@@ -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
|
@@ -37,7 +37,7 @@ 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
42
|
normalized_args = stringify_args(field, arguments.to_h, context)
|
43
43
|
subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
|
@@ -100,13 +100,8 @@ module GraphQL
|
|
100
100
|
arg_name = k.to_s
|
101
101
|
camelized_arg_name = GraphQL::Schema::Member::BuildType.camelize(arg_name)
|
102
102
|
arg_defn = get_arg_definition(arg_owner, camelized_arg_name, context)
|
103
|
-
|
104
|
-
|
105
|
-
normalized_arg_name = camelized_arg_name
|
106
|
-
else
|
107
|
-
normalized_arg_name = arg_name
|
108
|
-
arg_defn = get_arg_definition(arg_owner, normalized_arg_name, context)
|
109
|
-
end
|
103
|
+
arg_defn ||= get_arg_definition(arg_owner, arg_name, context)
|
104
|
+
normalized_arg_name = arg_defn.graphql_name
|
110
105
|
arg_base_type = arg_defn.type.unwrap
|
111
106
|
# In the case where the value being emitted is seen as a "JSON"
|
112
107
|
# type, treat the value as one atomic unit of serialization
|
@@ -131,12 +126,18 @@ module GraphQL
|
|
131
126
|
when GraphQL::Schema::InputObject
|
132
127
|
stringify_args(arg_owner, args.to_h, context)
|
133
128
|
else
|
134
|
-
|
129
|
+
if arg_owner.is_a?(Class) && arg_owner < GraphQL::Schema::Enum
|
130
|
+
# `prepare:` may have made the value something other than
|
131
|
+
# a defined value of this enum -- use _that_ in this case.
|
132
|
+
arg_owner.coerce_isolated_input(args) || args
|
133
|
+
else
|
134
|
+
args
|
135
|
+
end
|
135
136
|
end
|
136
137
|
end
|
137
138
|
|
138
139
|
def get_arg_definition(arg_owner, arg_name, context)
|
139
|
-
|
140
|
+
context.types.argument(arg_owner, arg_name) || context.types.arguments(arg_owner).find { |v| v.keyword.to_s == arg_name }
|
140
141
|
end
|
141
142
|
end
|
142
143
|
end
|
@@ -148,6 +148,8 @@ module GraphQL
|
|
148
148
|
{ TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
|
149
149
|
elsif 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,13 +80,14 @@ 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, GraphQL::Query::NullContext)
|
83
|
+
normalized_args = normalize_arguments(normalized_event_name, field, args, GraphQL::Query::NullContext.instance)
|
87
84
|
|
88
85
|
event = Subscriptions::Event.new(
|
89
86
|
name: normalized_event_name,
|
90
87
|
arguments: normalized_args,
|
91
88
|
field: field,
|
92
89
|
scope: scope,
|
90
|
+
context: context,
|
93
91
|
)
|
94
92
|
execute_all(event, object)
|
95
93
|
end
|
@@ -124,6 +122,10 @@ module GraphQL
|
|
124
122
|
variables: variables,
|
125
123
|
root_value: object,
|
126
124
|
}
|
125
|
+
|
126
|
+
# merge event's and query's context together
|
127
|
+
context.merge!(event.context) unless event.context.nil? || context.nil?
|
128
|
+
|
127
129
|
execute_options[:validate] = validate_update?(**execute_options)
|
128
130
|
result = @schema.execute(**execute_options)
|
129
131
|
subscriptions_context = result.context.namespace(:subscriptions)
|
@@ -131,11 +133,9 @@ module GraphQL
|
|
131
133
|
result = nil
|
132
134
|
end
|
133
135
|
|
134
|
-
unsubscribed
|
135
|
-
|
136
|
-
if unsubscribed
|
136
|
+
if subscriptions_context[:unsubscribed] && !subscriptions_context[:final_update]
|
137
137
|
# `unsubscribe` was called, clean up on our side
|
138
|
-
#
|
138
|
+
# The transport should also send `{more: false}` to client
|
139
139
|
delete_subscription(subscription_id)
|
140
140
|
result = nil
|
141
141
|
end
|
@@ -159,7 +159,14 @@ module GraphQL
|
|
159
159
|
res = execute_update(subscription_id, event, object)
|
160
160
|
if !res.nil?
|
161
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
|
162
168
|
end
|
169
|
+
|
163
170
|
end
|
164
171
|
|
165
172
|
# Event `event` occurred on `object`,
|
@@ -224,11 +231,11 @@ module GraphQL
|
|
224
231
|
|
225
232
|
# @return [Boolean] if true, then a query like this one would be broadcasted
|
226
233
|
def broadcastable?(query_str, **query_options)
|
227
|
-
query =
|
234
|
+
query = @schema.query_class.new(@schema, query_str, **query_options)
|
228
235
|
if !query.valid?
|
229
236
|
raise "Invalid query: #{query.validation_errors.map(&:to_h).inspect}"
|
230
237
|
end
|
231
|
-
GraphQL::Analysis
|
238
|
+
GraphQL::Analysis.analyze_query(query, @schema.query_analyzers)
|
232
239
|
query.context.namespace(:subscriptions)[:subscription_broadcastable]
|
233
240
|
end
|
234
241
|
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module Testing
|
4
|
+
module Helpers
|
5
|
+
# @param schema_class [Class<GraphQL::Schema>]
|
6
|
+
# @return [Module] A helpers module which always uses the given schema
|
7
|
+
def self.for(schema_class)
|
8
|
+
SchemaHelpers.for(schema_class)
|
9
|
+
end
|
10
|
+
|
11
|
+
class Error < GraphQL::Error
|
12
|
+
end
|
13
|
+
|
14
|
+
class TypeNotVisibleError < Error
|
15
|
+
def initialize(type_name:)
|
16
|
+
message = "`#{type_name}` should be `visible?` this field resolution and `context`, but it was not"
|
17
|
+
super(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class FieldNotVisibleError < Error
|
22
|
+
def initialize(type_name:, field_name:)
|
23
|
+
message = "`#{type_name}.#{field_name}` should be `visible?` for this resolution, but it was not"
|
24
|
+
super(message)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class TypeNotDefinedError < Error
|
29
|
+
def initialize(type_name:)
|
30
|
+
message = "No type named `#{type_name}` is defined; choose another type name or define this type."
|
31
|
+
super(message)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class FieldNotDefinedError < Error
|
36
|
+
def initialize(type_name:, field_name:)
|
37
|
+
message = "`#{type_name}` has no field named `#{field_name}`; pick another name or define this field."
|
38
|
+
super(message)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def run_graphql_field(schema, field_path, object, arguments: {}, context: {}, ast_node: nil, lookahead: nil)
|
43
|
+
type_name, *field_names = field_path.split(".")
|
44
|
+
dummy_query = GraphQL::Query.new(schema, "{ __typename }", context: context)
|
45
|
+
query_context = dummy_query.context
|
46
|
+
object_type = dummy_query.types.type(type_name) # rubocop:disable Development/ContextIsPassedCop
|
47
|
+
if object_type
|
48
|
+
graphql_result = object
|
49
|
+
field_names.each do |field_name|
|
50
|
+
inner_object = graphql_result
|
51
|
+
graphql_result = object_type.wrap(inner_object, query_context)
|
52
|
+
if graphql_result.nil?
|
53
|
+
return nil
|
54
|
+
end
|
55
|
+
visible_field = dummy_query.types.field(object_type, field_name) # rubocop:disable Development/ContextIsPassedCop
|
56
|
+
if visible_field
|
57
|
+
dummy_query.context.dataloader.run_isolated {
|
58
|
+
field_args = visible_field.coerce_arguments(graphql_result, arguments, query_context)
|
59
|
+
field_args = schema.sync_lazy(field_args)
|
60
|
+
if visible_field.extras.any?
|
61
|
+
extra_args = {}
|
62
|
+
visible_field.extras.each do |extra|
|
63
|
+
extra_args[extra] = case extra
|
64
|
+
when :ast_node
|
65
|
+
ast_node ||= GraphQL::Language::Nodes::Field.new(name: visible_field.graphql_name)
|
66
|
+
when :lookahead
|
67
|
+
lookahead ||= begin
|
68
|
+
ast_node ||= GraphQL::Language::Nodes::Field.new(name: visible_field.graphql_name)
|
69
|
+
Execution::Lookahead.new(
|
70
|
+
query: dummy_query,
|
71
|
+
ast_nodes: [ast_node],
|
72
|
+
field: visible_field,
|
73
|
+
)
|
74
|
+
end
|
75
|
+
else
|
76
|
+
raise ArgumentError, "This extra isn't supported in `run_graphql_field` yet: `#{extra.inspect}`. Open an issue on GitHub to request it: https://github.com/rmosolgo/graphql-ruby/issues/new"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
field_args = field_args.merge_extras(extra_args)
|
81
|
+
end
|
82
|
+
graphql_result = visible_field.resolve(graphql_result, field_args.keyword_arguments, query_context)
|
83
|
+
graphql_result = schema.sync_lazy(graphql_result)
|
84
|
+
}
|
85
|
+
object_type = visible_field.type.unwrap
|
86
|
+
elsif object_type.all_field_definitions.any? { |f| f.graphql_name == field_name }
|
87
|
+
raise FieldNotVisibleError.new(field_name: field_name, type_name: type_name)
|
88
|
+
else
|
89
|
+
raise FieldNotDefinedError.new(type_name: type_name, field_name: field_name)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
graphql_result
|
93
|
+
elsif schema.has_defined_type?(type_name)
|
94
|
+
raise TypeNotVisibleError.new(type_name: type_name)
|
95
|
+
else
|
96
|
+
raise TypeNotDefinedError.new(type_name: type_name)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def with_resolution_context(schema, type:, object:, context:{})
|
101
|
+
resolution_context = ResolutionAssertionContext.new(
|
102
|
+
self,
|
103
|
+
schema: schema,
|
104
|
+
type_name: type,
|
105
|
+
object: object,
|
106
|
+
context: context
|
107
|
+
)
|
108
|
+
yield(resolution_context)
|
109
|
+
end
|
110
|
+
|
111
|
+
class ResolutionAssertionContext
|
112
|
+
def initialize(test, type_name:, object:, schema:, context:)
|
113
|
+
@test = test
|
114
|
+
@type_name = type_name
|
115
|
+
@object = object
|
116
|
+
@schema = schema
|
117
|
+
@context = context
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
def run_graphql_field(field_name, arguments: {})
|
122
|
+
if @schema
|
123
|
+
@test.run_graphql_field(@schema, "#{@type_name}.#{field_name}", @object, arguments: arguments, context: @context)
|
124
|
+
else
|
125
|
+
@test.run_graphql_field("#{@type_name}.#{field_name}", @object, arguments: arguments, context: @context)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
module SchemaHelpers
|
131
|
+
include Helpers
|
132
|
+
|
133
|
+
def run_graphql_field(field_path, object, arguments: {}, context: {})
|
134
|
+
super(@@schema_class_for_helpers, field_path, object, arguments: arguments, context: context)
|
135
|
+
end
|
136
|
+
|
137
|
+
def with_resolution_context(*args, **kwargs, &block)
|
138
|
+
# schema will be added later
|
139
|
+
super(nil, *args, **kwargs, &block)
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.for(schema_class)
|
143
|
+
Module.new do
|
144
|
+
include SchemaHelpers
|
145
|
+
@@schema_class_for_helpers = schema_class
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'graphql/tracing/notifications_trace'
|
4
|
+
|
5
|
+
module GraphQL
|
6
|
+
module Tracing
|
7
|
+
# This implementation forwards events to ActiveSupport::Notifications
|
8
|
+
# with a `graphql` suffix.
|
9
|
+
module ActiveSupportNotificationsTrace
|
10
|
+
include NotificationsTrace
|
11
|
+
def initialize(engine: ActiveSupport::Notifications, **rest)
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|