graphql 1.11.2 → 1.11.7
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 +8 -0
- data/lib/generators/graphql/object_generator.rb +2 -0
- data/lib/generators/graphql/templates/base_argument.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_mutation.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 +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/interface.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/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/object.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/scalar.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +2 -0
- data/lib/generators/graphql/templates/union.erb +3 -1
- data/lib/graphql.rb +17 -0
- data/lib/graphql/argument.rb +3 -3
- data/lib/graphql/backtrace/tracer.rb +2 -1
- data/lib/graphql/define/assign_global_id_field.rb +2 -2
- data/lib/graphql/execution/interpreter.rb +10 -0
- data/lib/graphql/execution/interpreter/arguments.rb +21 -6
- data/lib/graphql/execution/interpreter/arguments_cache.rb +8 -0
- data/lib/graphql/execution/interpreter/runtime.rb +94 -67
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/introspection.rb +96 -0
- data/lib/graphql/introspection/field_type.rb +7 -3
- data/lib/graphql/introspection/input_value_type.rb +6 -0
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/type_type.rb +7 -3
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/block_string.rb +24 -5
- data/lib/graphql/language/lexer.rb +7 -3
- data/lib/graphql/language/lexer.rl +7 -3
- data/lib/graphql/language/nodes.rb +1 -1
- data/lib/graphql/language/parser.rb +107 -103
- data/lib/graphql/language/parser.y +4 -0
- data/lib/graphql/language/sanitized_printer.rb +59 -26
- data/lib/graphql/name_validator.rb +6 -7
- data/lib/graphql/pagination/connections.rb +11 -3
- data/lib/graphql/query.rb +6 -3
- data/lib/graphql/query/context.rb +34 -4
- data/lib/graphql/query/fingerprint.rb +2 -0
- data/lib/graphql/query/validation_pipeline.rb +4 -1
- data/lib/graphql/relay/array_connection.rb +2 -2
- data/lib/graphql/relay/range_add.rb +14 -5
- data/lib/graphql/schema.rb +49 -18
- data/lib/graphql/schema/argument.rb +56 -10
- data/lib/graphql/schema/build_from_definition.rb +67 -38
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive/deprecated.rb +1 -1
- data/lib/graphql/schema/field.rb +32 -16
- data/lib/graphql/schema/field/connection_extension.rb +44 -37
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/input_object.rb +5 -3
- data/lib/graphql/schema/interface.rb +1 -1
- data/lib/graphql/schema/late_bound_type.rb +2 -2
- data/lib/graphql/schema/loader.rb +1 -0
- data/lib/graphql/schema/member/build_type.rb +14 -4
- data/lib/graphql/schema/member/has_arguments.rb +54 -53
- data/lib/graphql/schema/member/has_fields.rb +17 -7
- data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
- data/lib/graphql/schema/mutation.rb +4 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +4 -2
- data/lib/graphql/schema/resolver.rb +6 -0
- data/lib/graphql/schema/resolver/has_payload_type.rb +2 -1
- data/lib/graphql/schema/subscription.rb +2 -12
- data/lib/graphql/schema/timeout.rb +29 -15
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema/validation.rb +8 -0
- data/lib/graphql/schema/warden.rb +2 -3
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +25 -17
- 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/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +29 -7
- data/lib/graphql/subscriptions.rb +32 -22
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +21 -7
- data/lib/graphql/tracing/appoptics_tracing.rb +10 -2
- data/lib/graphql/tracing/platform_tracing.rb +1 -1
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/types/int.rb +9 -2
- data/lib/graphql/types/iso_8601_date_time.rb +2 -1
- data/lib/graphql/types/relay/base_connection.rb +8 -6
- data/lib/graphql/types/relay/base_edge.rb +2 -1
- data/lib/graphql/types/string.rb +7 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/readme.md +1 -1
- metadata +10 -6
@@ -27,8 +27,7 @@ module GraphQL
|
|
27
27
|
# @param node_id [String] A unique ID generated by {.encode}
|
28
28
|
# @return [Array<(String, String)>] The type name & value passed to {.encode}
|
29
29
|
def decode(node_id, separator: self.default_id_separator)
|
30
|
-
|
31
|
-
Base64Bp.urlsafe_decode64(node_id).split(separator, 2)
|
30
|
+
GraphQL::Schema::Base64Encoder.decode(node_id).split(separator, 2)
|
32
31
|
end
|
33
32
|
end
|
34
33
|
end
|
@@ -133,6 +133,12 @@ module GraphQL
|
|
133
133
|
end
|
134
134
|
}
|
135
135
|
|
136
|
+
DEPRECATED_ARGUMENTS_ARE_OPTIONAL = ->(argument) {
|
137
|
+
if argument.deprecation_reason && argument.type.non_null?
|
138
|
+
"must be optional because it's deprecated"
|
139
|
+
end
|
140
|
+
}
|
141
|
+
|
136
142
|
TYPE_IS_VALID_INPUT_TYPE = ->(type) {
|
137
143
|
outer_type = type.type
|
138
144
|
inner_type = outer_type.respond_to?(:unwrap) ? outer_type.unwrap : nil
|
@@ -265,8 +271,10 @@ module GraphQL
|
|
265
271
|
Rules::NAME_IS_STRING,
|
266
272
|
Rules::RESERVED_NAME,
|
267
273
|
Rules::DESCRIPTION_IS_STRING_OR_NIL,
|
274
|
+
Rules.assert_property(:deprecation_reason, String, NilClass),
|
268
275
|
Rules::TYPE_IS_VALID_INPUT_TYPE,
|
269
276
|
Rules::DEFAULT_VALUE_IS_VALID_FOR_TYPE,
|
277
|
+
Rules::DEPRECATED_ARGUMENTS_ARE_OPTIONAL,
|
270
278
|
],
|
271
279
|
GraphQL::BaseType => [
|
272
280
|
Rules::NAME_IS_STRING,
|
@@ -40,7 +40,6 @@ module GraphQL
|
|
40
40
|
# @param filter [<#call(member)>] Objects are hidden when `.call(member, ctx)` returns true
|
41
41
|
# @param context [GraphQL::Query::Context]
|
42
42
|
# @param schema [GraphQL::Schema]
|
43
|
-
# @param deep_check [Boolean]
|
44
43
|
def initialize(filter, context:, schema:)
|
45
44
|
@schema = schema.interpreter? ? schema : schema.graphql_definition
|
46
45
|
# Cache these to avoid repeated hits to the inheritance chain when one isn't present
|
@@ -51,7 +50,7 @@ module GraphQL
|
|
51
50
|
@visibility_cache = read_through { |m| filter.call(m, context) }
|
52
51
|
end
|
53
52
|
|
54
|
-
# @return [
|
53
|
+
# @return [Hash<String, GraphQL::BaseType>] Visible types in the schema
|
55
54
|
def types
|
56
55
|
@types ||= begin
|
57
56
|
vis_types = {}
|
@@ -199,7 +198,7 @@ module GraphQL
|
|
199
198
|
if (iface_field_defn = interface_type.get_field(field_defn.graphql_name))
|
200
199
|
any_interface_has_field = true
|
201
200
|
|
202
|
-
if
|
201
|
+
if interfaces(type_defn).include?(interface_type) && visible_field?(interface_type, iface_field_defn)
|
203
202
|
any_interface_has_visible_field = true
|
204
203
|
end
|
205
204
|
end
|
@@ -4,6 +4,7 @@ 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
10
|
require "graphql/static_validation/no_validate_visitor"
|
@@ -202,15 +202,16 @@ module GraphQL
|
|
202
202
|
)
|
203
203
|
end
|
204
204
|
|
205
|
-
|
206
|
-
|
207
|
-
|
205
|
+
if !same_arguments?(node1, node2)
|
206
|
+
args = [serialize_field_args(node1), serialize_field_args(node2)]
|
207
|
+
conflicts = args.map { |arg| GraphQL::Language.serialize(arg) }.join(" or ")
|
208
|
+
msg = "Field '#{response_key}' has an argument conflict: #{conflicts}?"
|
208
209
|
context.errors << GraphQL::StaticValidation::FieldsWillMergeError.new(
|
209
210
|
msg,
|
210
211
|
nodes: [node1, node2],
|
211
212
|
path: [],
|
212
213
|
field_name: response_key,
|
213
|
-
conflicts:
|
214
|
+
conflicts: conflicts
|
214
215
|
)
|
215
216
|
end
|
216
217
|
end
|
@@ -326,20 +327,19 @@ module GraphQL
|
|
326
327
|
[fields, fragment_spreads]
|
327
328
|
end
|
328
329
|
|
329
|
-
def
|
330
|
+
def same_arguments?(field1, field2)
|
330
331
|
# Check for incompatible / non-identical arguments on this node:
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
end.uniq
|
332
|
+
arguments1 = field1.arguments
|
333
|
+
arguments2 = field2.arguments
|
334
|
+
|
335
|
+
return false if arguments1.length != arguments2.length
|
336
|
+
|
337
|
+
arguments1.all? do |argument1|
|
338
|
+
argument2 = arguments2.find { |argument| argument.name == argument1.name }
|
339
|
+
return false if argument2.nil?
|
340
|
+
|
341
|
+
serialize_arg(argument1.value) == serialize_arg(argument2.value)
|
342
|
+
end
|
343
343
|
end
|
344
344
|
|
345
345
|
def serialize_arg(arg_value)
|
@@ -353,6 +353,14 @@ module GraphQL
|
|
353
353
|
end
|
354
354
|
end
|
355
355
|
|
356
|
+
def serialize_field_args(field)
|
357
|
+
serialized_args = {}
|
358
|
+
field.arguments.each do |argument|
|
359
|
+
serialized_args[argument.name] = serialize_arg(argument.value)
|
360
|
+
end
|
361
|
+
serialized_args
|
362
|
+
end
|
363
|
+
|
356
364
|
def compared_fragments_key(frag1, frag2, exclusive)
|
357
365
|
# Cache key to not compare two fragments more than once.
|
358
366
|
# The key includes both fragment names sorted (this way we
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
module InputObjectNamesAreUnique
|
5
|
+
def on_input_object(node, parent)
|
6
|
+
validate_input_fields(node)
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def validate_input_fields(node)
|
13
|
+
input_field_defns = node.arguments
|
14
|
+
input_fields_by_name = Hash.new { |h, k| h[k] = [] }
|
15
|
+
input_field_defns.each { |a| input_fields_by_name[a.name] << a }
|
16
|
+
|
17
|
+
input_fields_by_name.each do |name, defns|
|
18
|
+
if defns.size > 1
|
19
|
+
error = GraphQL::StaticValidation::InputObjectNamesAreUniqueError.new(
|
20
|
+
"There can be only one input field named \"#{name}\"",
|
21
|
+
nodes: defns,
|
22
|
+
name: name
|
23
|
+
)
|
24
|
+
add_error(error)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
module StaticValidation
|
4
|
+
class InputObjectNamesAreUniqueError < StaticValidation::Error
|
5
|
+
attr_reader :name
|
6
|
+
|
7
|
+
def initialize(message, path: nil, nodes: [], name:)
|
8
|
+
super(message, path: path, nodes: nodes)
|
9
|
+
@name = name
|
10
|
+
end
|
11
|
+
|
12
|
+
# A hash representation of this Message
|
13
|
+
def to_h
|
14
|
+
extensions = {
|
15
|
+
"code" => code,
|
16
|
+
"name" => name
|
17
|
+
}
|
18
|
+
|
19
|
+
super.merge({
|
20
|
+
"extensions" => extensions
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
def code
|
25
|
+
"inputFieldNotUnique"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -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
|
@@ -20,8 +20,10 @@ module GraphQL
|
|
20
20
|
|
21
21
|
# Validate `query` against the schema. Returns an array of message hashes.
|
22
22
|
# @param query [GraphQL::Query]
|
23
|
+
# @param validate [Boolean]
|
24
|
+
# @param timeout [Float] Number of seconds to wait before aborting validation. Any positive number may be used, including Floats to specify fractional seconds.
|
23
25
|
# @return [Array<Hash>]
|
24
|
-
def validate(query, validate: true)
|
26
|
+
def validate(query, validate: true, timeout: nil)
|
25
27
|
query.trace("validate", { validate: validate, query: query }) do
|
26
28
|
can_skip_rewrite = query.context.interpreter? && query.schema.using_ast_analysis? && query.schema.is_a?(Class)
|
27
29
|
errors = if validate == false && can_skip_rewrite
|
@@ -32,18 +34,29 @@ module GraphQL
|
|
32
34
|
|
33
35
|
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class)
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
begin
|
38
|
+
# 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.
|
39
|
+
# A timeout value of 0 or nil will execute the block without any timeout.
|
40
|
+
Timeout::timeout(timeout) do
|
41
|
+
# Attach legacy-style rules.
|
42
|
+
# Only loop through rules if it has legacy-style rules
|
43
|
+
unless (legacy_rules = rules_to_use - GraphQL::StaticValidation::ALL_RULES).empty?
|
44
|
+
legacy_rules.each do |rule_class_or_module|
|
45
|
+
if rule_class_or_module.method_defined?(:validate)
|
46
|
+
rule_class_or_module.new.validate(context)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context.visitor.visit
|
39
52
|
end
|
53
|
+
rescue Timeout::Error
|
54
|
+
handle_timeout(query, context)
|
40
55
|
end
|
41
56
|
|
42
|
-
context.visitor.visit
|
43
57
|
context.errors
|
44
58
|
end
|
45
59
|
|
46
|
-
|
47
60
|
irep = if errors.empty? && context
|
48
61
|
# Only return this if there are no errors and validation was actually run
|
49
62
|
context.visitor.rewrite_document
|
@@ -57,6 +70,15 @@ module GraphQL
|
|
57
70
|
}
|
58
71
|
end
|
59
72
|
end
|
73
|
+
|
74
|
+
# Invoked when static validation times out.
|
75
|
+
# @param query [GraphQL::Query]
|
76
|
+
# @param context [GraphQL::StaticValidation::ValidationContext]
|
77
|
+
def handle_timeout(query, context)
|
78
|
+
context.errors << GraphQL::StaticValidation::ValidationTimeoutError.new(
|
79
|
+
"Timeout on validation of query"
|
80
|
+
)
|
81
|
+
end
|
60
82
|
end
|
61
83
|
end
|
62
84
|
end
|
@@ -4,9 +4,7 @@ require "graphql/subscriptions/broadcast_analyzer"
|
|
4
4
|
require "graphql/subscriptions/event"
|
5
5
|
require "graphql/subscriptions/instrumentation"
|
6
6
|
require "graphql/subscriptions/serialize"
|
7
|
-
|
8
|
-
require "graphql/subscriptions/action_cable_subscriptions"
|
9
|
-
end
|
7
|
+
require "graphql/subscriptions/action_cable_subscriptions"
|
10
8
|
require "graphql/subscriptions/subscription_root"
|
11
9
|
require "graphql/subscriptions/default_subscription_resolve_extension"
|
12
10
|
|
@@ -100,31 +98,43 @@ module GraphQL
|
|
100
98
|
# Lookup the saved data for this subscription
|
101
99
|
query_data = read_subscription(subscription_id)
|
102
100
|
if query_data.nil?
|
103
|
-
|
104
|
-
|
101
|
+
delete_subscription(subscription_id)
|
102
|
+
return nil
|
105
103
|
end
|
104
|
+
|
106
105
|
# Fetch the required keys from the saved data
|
107
106
|
query_string = query_data.fetch(:query_string)
|
108
107
|
variables = query_data.fetch(:variables)
|
109
108
|
context = query_data.fetch(:context)
|
110
109
|
operation_name = query_data.fetch(:operation_name)
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
110
|
+
result = nil
|
111
|
+
# this will be set to `false` unless `.execute` is terminated
|
112
|
+
# with a `throw :graphql_subscription_unsubscribed`
|
113
|
+
unsubscribed = true
|
114
|
+
catch(:graphql_subscription_unsubscribed) do
|
115
|
+
catch(:graphql_no_subscription_update) do
|
116
|
+
# Re-evaluate the saved query,
|
117
|
+
# but if it terminates early with a `throw`,
|
118
|
+
# it will stay `nil`
|
119
|
+
result = @schema.execute(
|
120
|
+
query: query_string,
|
121
|
+
context: context,
|
122
|
+
subscription_topic: event.topic,
|
123
|
+
operation_name: operation_name,
|
124
|
+
variables: variables,
|
125
|
+
root_value: object,
|
126
|
+
)
|
127
|
+
end
|
128
|
+
unsubscribed = false
|
129
|
+
end
|
130
|
+
|
131
|
+
if unsubscribed
|
132
|
+
# `unsubscribe` was called, clean up on our side
|
133
|
+
# TODO also send `{more: false}` to client?
|
134
|
+
delete_subscription(subscription_id)
|
135
|
+
end
|
136
|
+
|
137
|
+
result
|
128
138
|
end
|
129
139
|
|
130
140
|
# Run the update query for this subscription and deliver it
|
@@ -4,7 +4,7 @@ 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?})
|
@@ -86,28 +86,32 @@ module GraphQL
|
|
86
86
|
EVENT_PREFIX = "graphql-event:"
|
87
87
|
|
88
88
|
# @param serializer [<#dump(obj), #load(string)] Used for serializing messages before handing them to `.broadcast(msg)`
|
89
|
-
|
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)
|
90
91
|
# A per-process map of subscriptions to deliver.
|
91
92
|
# This is provided by Rails, so let's use it
|
92
93
|
@subscriptions = Concurrent::Map.new
|
93
94
|
@events = Concurrent::Map.new { |h, k| h[k] = Concurrent::Map.new { |h2, k2| h2[k2] = Concurrent::Array.new } }
|
95
|
+
@action_cable = action_cable
|
96
|
+
@action_cable_coder = action_cable_coder
|
94
97
|
@serializer = serializer
|
98
|
+
@transmit_ns = namespace
|
95
99
|
super
|
96
100
|
end
|
97
101
|
|
98
102
|
# An event was triggered; Push the data over ActionCable.
|
99
103
|
# Subscribers will re-evaluate locally.
|
100
104
|
def execute_all(event, object)
|
101
|
-
stream =
|
105
|
+
stream = stream_event_name(event)
|
102
106
|
message = @serializer.dump(object)
|
103
|
-
|
107
|
+
@action_cable.server.broadcast(stream, message)
|
104
108
|
end
|
105
109
|
|
106
110
|
# This subscription was re-evaluated.
|
107
111
|
# Send it to the specific stream where this client was waiting.
|
108
112
|
def deliver(subscription_id, result)
|
109
113
|
payload = { result: result.to_h, more: true }
|
110
|
-
|
114
|
+
@action_cable.server.broadcast(stream_subscription_name(subscription_id), payload)
|
111
115
|
end
|
112
116
|
|
113
117
|
# A query was run where these events were subscribed to.
|
@@ -117,7 +121,7 @@ module GraphQL
|
|
117
121
|
def write_subscription(query, events)
|
118
122
|
channel = query.context.fetch(:channel)
|
119
123
|
subscription_id = query.context[:subscription_id] ||= build_id
|
120
|
-
stream =
|
124
|
+
stream = stream_subscription_name(subscription_id)
|
121
125
|
channel.stream_from(stream)
|
122
126
|
@subscriptions[subscription_id] = query
|
123
127
|
events.each do |event|
|
@@ -141,7 +145,7 @@ module GraphQL
|
|
141
145
|
#
|
142
146
|
def setup_stream(channel, initial_event)
|
143
147
|
topic = initial_event.topic
|
144
|
-
channel.stream_from(
|
148
|
+
channel.stream_from(stream_event_name(initial_event), coder: @action_cable_coder) do |message|
|
145
149
|
object = @serializer.load(message)
|
146
150
|
events_by_fingerprint = @events[topic]
|
147
151
|
events_by_fingerprint.each do |_fingerprint, events|
|
@@ -197,6 +201,16 @@ module GraphQL
|
|
197
201
|
end
|
198
202
|
end
|
199
203
|
end
|
204
|
+
|
205
|
+
private
|
206
|
+
|
207
|
+
def stream_subscription_name(subscription_id)
|
208
|
+
[SUBSCRIPTION_PREFIX, @transmit_ns, subscription_id].join
|
209
|
+
end
|
210
|
+
|
211
|
+
def stream_event_name(event)
|
212
|
+
[EVENT_PREFIX, @transmit_ns, event.topic].join
|
213
|
+
end
|
200
214
|
end
|
201
215
|
end
|
202
216
|
end
|