graphql 1.12.16 → 1.13.2
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 +3 -1
- data/lib/generators/graphql/install_generator.rb +9 -2
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/object_generator.rb +2 -1
- data/lib/generators/graphql/relay.rb +19 -11
- data/lib/generators/graphql/templates/schema.erb +14 -2
- data/lib/generators/graphql/type_generator.rb +0 -1
- data/lib/graphql/analysis/ast/field_usage.rb +3 -3
- data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
- data/lib/graphql/analysis/ast/visitor.rb +4 -4
- data/lib/graphql/backtrace/table.rb +1 -1
- data/lib/graphql/base_type.rb +4 -2
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/dataloader/source.rb +50 -2
- data/lib/graphql/dataloader.rb +93 -37
- data/lib/graphql/define/instance_definable.rb +1 -1
- data/lib/graphql/deprecated_dsl.rb +11 -3
- data/lib/graphql/deprecation.rb +1 -5
- data/lib/graphql/directive/deprecated_directive.rb +1 -1
- data/lib/graphql/directive/include_directive.rb +1 -1
- data/lib/graphql/directive/skip_directive.rb +1 -1
- data/lib/graphql/directive.rb +0 -4
- data/lib/graphql/enum_type.rb +5 -1
- data/lib/graphql/execution/errors.rb +1 -0
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
- data/lib/graphql/execution/interpreter/runtime.rb +39 -23
- data/lib/graphql/execution/lookahead.rb +2 -2
- data/lib/graphql/execution/multiplex.rb +4 -1
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/int_type.rb +1 -1
- data/lib/graphql/integer_encoding_error.rb +18 -2
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/input_value_type.rb +10 -4
- data/lib/graphql/introspection/schema_type.rb +2 -2
- data/lib/graphql/introspection/type_type.rb +10 -10
- data/lib/graphql/language/block_string.rb +2 -6
- data/lib/graphql/language/document_from_schema_definition.rb +4 -2
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/nodes.rb +12 -2
- data/lib/graphql/language/parser.rb +442 -434
- data/lib/graphql/language/parser.y +5 -4
- data/lib/graphql/language/printer.rb +6 -1
- data/lib/graphql/language/sanitized_printer.rb +5 -5
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/name_validator.rb +0 -4
- data/lib/graphql/pagination/connections.rb +35 -16
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/arguments_cache.rb +1 -1
- data/lib/graphql/query/context.rb +15 -2
- data/lib/graphql/query/literal_input.rb +1 -1
- data/lib/graphql/query/null_context.rb +12 -7
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/query.rb +4 -0
- data/lib/graphql/relay/edges_instrumentation.rb +0 -1
- data/lib/graphql/relay/global_id_resolve.rb +1 -1
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/schema/addition.rb +37 -28
- data/lib/graphql/schema/argument.rb +79 -34
- data/lib/graphql/schema/build_from_definition.rb +5 -5
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +1 -1
- data/lib/graphql/schema/directive.rb +7 -3
- data/lib/graphql/schema/enum.rb +60 -10
- data/lib/graphql/schema/enum_value.rb +6 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +140 -42
- data/lib/graphql/schema/field_extension.rb +52 -2
- data/lib/graphql/schema/find_inherited_value.rb +1 -0
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +13 -14
- data/lib/graphql/schema/interface.rb +11 -20
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/list.rb +3 -1
- data/lib/graphql/schema/member/accepts_definition.rb +15 -3
- data/lib/graphql/schema/member/build_type.rb +0 -4
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/member/has_arguments.rb +145 -57
- data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +76 -18
- data/lib/graphql/schema/member/has_interfaces.rb +90 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/non_null.rb +3 -1
- data/lib/graphql/schema/object.rb +10 -75
- data/lib/graphql/schema/printer.rb +1 -1
- data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
- data/lib/graphql/schema/resolver.rb +49 -64
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/subscription.rb +17 -9
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +18 -4
- data/lib/graphql/schema/union.rb +8 -1
- data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
- data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/format_validator.rb +4 -5
- data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/length_validator.rb +5 -3
- data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
- data/lib/graphql/schema/validator.rb +33 -25
- data/lib/graphql/schema/warden.rb +116 -52
- data/lib/graphql/schema.rb +124 -27
- data/lib/graphql/static_validation/base_visitor.rb +8 -5
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/error.rb +3 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +52 -26
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +7 -7
- data/lib/graphql/static_validation/validation_context.rb +8 -2
- data/lib/graphql/static_validation/validator.rb +15 -12
- data/lib/graphql/string_encoding_error.rb +13 -3
- data/lib/graphql/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +15 -5
- data/lib/graphql/subscriptions/event.rb +66 -13
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- data/lib/graphql/types/int.rb +1 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
- data/lib/graphql/types/relay/default_relay.rb +5 -1
- data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
- data/lib/graphql/types/relay/has_node_field.rb +1 -1
- data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -32
- data/readme.md +1 -1
- metadata +13 -6
@@ -15,14 +15,16 @@ module GraphQL
|
|
15
15
|
extend Forwardable
|
16
16
|
|
17
17
|
attr_reader :query, :errors, :visitor,
|
18
|
-
:on_dependency_resolve_handlers
|
18
|
+
:on_dependency_resolve_handlers,
|
19
|
+
:max_errors
|
19
20
|
|
20
21
|
def_delegators :@query, :schema, :document, :fragments, :operations, :warden
|
21
22
|
|
22
|
-
def initialize(query, visitor_class)
|
23
|
+
def initialize(query, visitor_class, max_errors)
|
23
24
|
@query = query
|
24
25
|
@literal_validator = LiteralValidator.new(context: query.context)
|
25
26
|
@errors = []
|
27
|
+
@max_errors = max_errors || Float::INFINITY
|
26
28
|
@on_dependency_resolve_handlers = []
|
27
29
|
@visitor = visitor_class.new(document, self)
|
28
30
|
end
|
@@ -38,6 +40,10 @@ module GraphQL
|
|
38
40
|
def validate_literal(ast_value, type)
|
39
41
|
@literal_validator.validate(ast_value, type)
|
40
42
|
end
|
43
|
+
|
44
|
+
def too_many_errors?
|
45
|
+
@errors.length >= @max_errors
|
46
|
+
end
|
41
47
|
end
|
42
48
|
end
|
43
49
|
end
|
@@ -24,8 +24,9 @@ module GraphQL
|
|
24
24
|
# @param query [GraphQL::Query]
|
25
25
|
# @param validate [Boolean]
|
26
26
|
# @param timeout [Float] Number of seconds to wait before aborting validation. Any positive number may be used, including Floats to specify fractional seconds.
|
27
|
+
# @param max_errors [Integer] Maximum number of errors before aborting validation. Any positive number will limit the number of errors. Defaults to nil for no limit.
|
27
28
|
# @return [Array<Hash>]
|
28
|
-
def validate(query, validate: true, timeout: nil)
|
29
|
+
def validate(query, validate: true, timeout: nil, max_errors: nil)
|
29
30
|
query.trace("validate", { validate: validate, query: query }) do
|
30
31
|
can_skip_rewrite = query.context.interpreter? && query.schema.using_ast_analysis? && query.schema.is_a?(Class)
|
31
32
|
errors = if validate == false && can_skip_rewrite
|
@@ -34,25 +35,27 @@ module GraphQL
|
|
34
35
|
rules_to_use = validate ? @rules : []
|
35
36
|
visitor_class = BaseVisitor.including_rules(rules_to_use, rewrite: !can_skip_rewrite)
|
36
37
|
|
37
|
-
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class)
|
38
|
+
context = GraphQL::StaticValidation::ValidationContext.new(query, visitor_class, max_errors)
|
38
39
|
|
39
40
|
begin
|
40
41
|
# CAUTION: Usage of the timeout module makes the assumption that validation rules are stateless Ruby code that requires no cleanup if process was interrupted. This means no blocking IO calls, native gems, locks, or `rescue` clauses that must be reached.
|
41
42
|
# A timeout value of 0 or nil will execute the block without any timeout.
|
42
43
|
Timeout::timeout(timeout) do
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
legacy_rules
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
44
|
+
catch(:too_many_validation_errors) do
|
45
|
+
# Attach legacy-style rules.
|
46
|
+
# Only loop through rules if it has legacy-style rules
|
47
|
+
unless (legacy_rules = rules_to_use - GraphQL::StaticValidation::ALL_RULES).empty?
|
48
|
+
legacy_rules.each do |rule_class_or_module|
|
49
|
+
if rule_class_or_module.method_defined?(:validate)
|
50
|
+
GraphQL::Deprecation.warn "Legacy validator rules will be removed from GraphQL-Ruby 2.0, use a module instead (see the built-in rules: https://github.com/rmosolgo/graphql-ruby/tree/master/lib/graphql/static_validation/rules)"
|
51
|
+
GraphQL::Deprecation.warn " -> Legacy validator: #{rule_class_or_module}"
|
52
|
+
rule_class_or_module.new.validate(context)
|
53
|
+
end
|
51
54
|
end
|
52
55
|
end
|
53
|
-
end
|
54
56
|
|
55
|
-
|
57
|
+
context.visitor.visit
|
58
|
+
end
|
56
59
|
end
|
57
60
|
rescue Timeout::Error
|
58
61
|
handle_timeout(query, context)
|
@@ -1,10 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module GraphQL
|
3
3
|
class StringEncodingError < GraphQL::RuntimeTypeError
|
4
|
-
attr_reader :string
|
5
|
-
def initialize(str)
|
4
|
+
attr_reader :string, :field, :path
|
5
|
+
def initialize(str, context:)
|
6
6
|
@string = str
|
7
|
-
|
7
|
+
@field = context[:current_field]
|
8
|
+
@path = context[:current_path]
|
9
|
+
message = "String #{str.inspect} was encoded as #{str.encoding}".dup
|
10
|
+
if @path
|
11
|
+
message << " @ #{@path.join(".")}"
|
12
|
+
end
|
13
|
+
if @field
|
14
|
+
message << " (#{@field.path})"
|
15
|
+
end
|
16
|
+
message << ". GraphQL requires an encoding compatible with UTF-8."
|
17
|
+
super(message)
|
8
18
|
end
|
9
19
|
end
|
10
20
|
end
|
data/lib/graphql/string_type.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
GraphQL::STRING_TYPE = GraphQL::Types::String.graphql_definition
|
2
|
+
GraphQL::STRING_TYPE = GraphQL::Types::String.graphql_definition(silence_deprecation_warning: true)
|
@@ -127,7 +127,13 @@ module GraphQL
|
|
127
127
|
# It will receive notifications when events come in
|
128
128
|
# and re-evaluate the query locally.
|
129
129
|
def write_subscription(query, events)
|
130
|
-
channel = query.context
|
130
|
+
unless (channel = query.context[:channel])
|
131
|
+
raise GraphQL::Error, "This GraphQL Subscription client does not support the transport protocol expected"\
|
132
|
+
"by the backend Subscription Server implementation (graphql-ruby ActionCableSubscriptions in this case)."\
|
133
|
+
"Some official client implementation including Apollo (https://graphql-ruby.org/javascript_client/apollo_subscriptions.html), "\
|
134
|
+
"Relay Modern (https://graphql-ruby.org/javascript_client/relay_subscriptions.html#actioncable)."\
|
135
|
+
"GraphiQL via `graphiql-rails` may not work out of box (#1051)."
|
136
|
+
end
|
131
137
|
subscription_id = query.context[:subscription_id] ||= build_id
|
132
138
|
stream = stream_subscription_name(subscription_id)
|
133
139
|
channel.stream_from(stream)
|
@@ -164,10 +170,12 @@ module GraphQL
|
|
164
170
|
first_subscription_id = first_event.context.fetch(:subscription_id)
|
165
171
|
object ||= load_action_cable_message(message, first_event.context)
|
166
172
|
result = execute_update(first_subscription_id, first_event, object)
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
173
|
+
if !result.nil?
|
174
|
+
# Having calculated the result _once_, send the same payload to all subscribers
|
175
|
+
events.each do |event|
|
176
|
+
subscription_id = event.context.fetch(:subscription_id)
|
177
|
+
deliver(subscription_id, result)
|
178
|
+
end
|
171
179
|
end
|
172
180
|
end
|
173
181
|
end
|
@@ -208,6 +216,8 @@ module GraphQL
|
|
208
216
|
# The channel was closed, forget about it.
|
209
217
|
def delete_subscription(subscription_id)
|
210
218
|
query = @subscriptions.delete(subscription_id)
|
219
|
+
# In case this came from the server, tell the client to unsubscribe:
|
220
|
+
@action_cable.server.broadcast(stream_subscription_name(subscription_id), { more: false })
|
211
221
|
# This can be `nil` when `.trigger` happens inside an unsubscribed ActionCable channel,
|
212
222
|
# see https://github.com/rmosolgo/graphql-ruby/issues/2478
|
213
223
|
if query
|
@@ -23,15 +23,23 @@ module GraphQL
|
|
23
23
|
@arguments = arguments
|
24
24
|
@context = context
|
25
25
|
field ||= context.field
|
26
|
-
|
26
|
+
scope_key = field.subscription_scope
|
27
|
+
scope_val = scope || (context && scope_key && context[scope_key])
|
28
|
+
if scope_key &&
|
29
|
+
(subscription = field.resolver) &&
|
30
|
+
(subscription.respond_to?(:subscription_scope_optional?)) &&
|
31
|
+
!subscription.subscription_scope_optional? &&
|
32
|
+
scope_val.nil?
|
33
|
+
raise Subscriptions::SubscriptionScopeMissingError, "#{field.path} (#{subscription}) requires a `scope:` value to trigger updates (Set `subscription_scope ..., optional: true` to disable this requirement)"
|
34
|
+
end
|
27
35
|
|
28
|
-
@topic = self.class.serialize(name, arguments, field, scope: scope_val)
|
36
|
+
@topic = self.class.serialize(name, arguments, field, scope: scope_val, context: context)
|
29
37
|
end
|
30
38
|
|
31
39
|
# @return [String] an identifier for this unit of subscription
|
32
|
-
def self.serialize(_name, arguments, field, scope:)
|
40
|
+
def self.serialize(_name, arguments, field, scope:, context: GraphQL::Query::NullContext)
|
33
41
|
subscription = field.resolver || GraphQL::Schema::Subscription
|
34
|
-
normalized_args = stringify_args(field, arguments.to_h)
|
42
|
+
normalized_args = stringify_args(field, arguments.to_h, context)
|
35
43
|
subscription.topic_for(arguments: normalized_args, field: field, scope: scope)
|
36
44
|
end
|
37
45
|
|
@@ -53,37 +61,82 @@ module GraphQL
|
|
53
61
|
|
54
62
|
class << self
|
55
63
|
private
|
56
|
-
|
64
|
+
|
65
|
+
# This method does not support cyclic references in the Hash,
|
66
|
+
# nor does it support Hashes whose keys are not sortable
|
67
|
+
# with respect to their peers ( cases where a <=> b might throw an error )
|
68
|
+
def deep_sort_hash_keys(hash_to_sort)
|
69
|
+
raise ArgumentError.new("Argument must be a Hash") unless hash_to_sort.is_a?(Hash)
|
70
|
+
hash_to_sort.keys.sort.map do |k|
|
71
|
+
if hash_to_sort[k].is_a?(Hash)
|
72
|
+
[k, deep_sort_hash_keys(hash_to_sort[k])]
|
73
|
+
elsif hash_to_sort[k].is_a?(Array)
|
74
|
+
[k, deep_sort_array_hashes(hash_to_sort[k])]
|
75
|
+
else
|
76
|
+
[k, hash_to_sort[k]]
|
77
|
+
end
|
78
|
+
end.to_h
|
79
|
+
end
|
80
|
+
|
81
|
+
def deep_sort_array_hashes(array_to_inspect)
|
82
|
+
raise ArgumentError.new("Argument must be an Array") unless array_to_inspect.is_a?(Array)
|
83
|
+
array_to_inspect.map do |v|
|
84
|
+
if v.is_a?(Hash)
|
85
|
+
deep_sort_hash_keys(v)
|
86
|
+
elsif v.is_a?(Array)
|
87
|
+
deep_sort_array_hashes(v)
|
88
|
+
else
|
89
|
+
v
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def stringify_args(arg_owner, args, context)
|
95
|
+
arg_owner = arg_owner.respond_to?(:unwrap) ? arg_owner.unwrap : arg_owner # remove list and non-null wrappers
|
57
96
|
case args
|
58
97
|
when Hash
|
59
98
|
next_args = {}
|
60
99
|
args.each do |k, v|
|
61
100
|
arg_name = k.to_s
|
62
101
|
camelized_arg_name = GraphQL::Schema::Member::BuildType.camelize(arg_name)
|
63
|
-
arg_defn = get_arg_definition(arg_owner, camelized_arg_name)
|
102
|
+
arg_defn = get_arg_definition(arg_owner, camelized_arg_name, context)
|
64
103
|
|
65
104
|
if arg_defn
|
66
105
|
normalized_arg_name = camelized_arg_name
|
67
106
|
else
|
68
107
|
normalized_arg_name = arg_name
|
69
|
-
arg_defn = get_arg_definition(arg_owner, normalized_arg_name)
|
108
|
+
arg_defn = get_arg_definition(arg_owner, normalized_arg_name, context)
|
109
|
+
end
|
110
|
+
arg_base_type = arg_defn.type.unwrap
|
111
|
+
# In the case where the value being emitted is seen as a "JSON"
|
112
|
+
# type, treat the value as one atomic unit of serialization
|
113
|
+
is_json_definition = arg_base_type && arg_base_type <= GraphQL::Types::JSON
|
114
|
+
if is_json_definition
|
115
|
+
sorted_value = if v.is_a?(Hash)
|
116
|
+
deep_sort_hash_keys(v)
|
117
|
+
elsif v.is_a?(Array)
|
118
|
+
deep_sort_array_hashes(v)
|
119
|
+
else
|
120
|
+
v
|
121
|
+
end
|
122
|
+
next_args[normalized_arg_name] = sorted_value.respond_to?(:to_json) ? sorted_value.to_json : sorted_value
|
123
|
+
else
|
124
|
+
next_args[normalized_arg_name] = stringify_args(arg_base_type, v, context)
|
70
125
|
end
|
71
|
-
|
72
|
-
next_args[normalized_arg_name] = stringify_args(arg_defn.type, v)
|
73
126
|
end
|
74
127
|
# Make sure they're deeply sorted
|
75
128
|
next_args.sort.to_h
|
76
129
|
when Array
|
77
|
-
args.map { |a| stringify_args(arg_owner, a) }
|
130
|
+
args.map { |a| stringify_args(arg_owner, a, context) }
|
78
131
|
when GraphQL::Schema::InputObject
|
79
|
-
stringify_args(arg_owner, args.to_h)
|
132
|
+
stringify_args(arg_owner, args.to_h, context)
|
80
133
|
else
|
81
134
|
args
|
82
135
|
end
|
83
136
|
end
|
84
137
|
|
85
|
-
def get_arg_definition(arg_owner, arg_name)
|
86
|
-
arg_owner.
|
138
|
+
def get_arg_definition(arg_owner, arg_name, context)
|
139
|
+
arg_owner.get_argument(arg_name, context) || arg_owner.arguments(context).each_value.find { |v| v.keyword.to_s == arg_name }
|
87
140
|
end
|
88
141
|
end
|
89
142
|
end
|
@@ -9,7 +9,7 @@ module GraphQL
|
|
9
9
|
SYMBOL_KEY = "__sym__"
|
10
10
|
SYMBOL_KEYS_KEY = "__sym_keys__"
|
11
11
|
TIMESTAMP_KEY = "__timestamp__"
|
12
|
-
TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%N%
|
12
|
+
TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S.%N%z" # eg '2020-01-01 23:59:59.123456789+05:00'
|
13
13
|
OPEN_STRUCT_KEY = "__ostruct__"
|
14
14
|
|
15
15
|
module_function
|
@@ -16,6 +16,13 @@ module GraphQL
|
|
16
16
|
class InvalidTriggerError < GraphQL::Error
|
17
17
|
end
|
18
18
|
|
19
|
+
# Raised when either:
|
20
|
+
# - An initial subscription didn't have a value for `context[subscription_scope]`
|
21
|
+
# - Or, an update didn't pass `.trigger(..., scope:)`
|
22
|
+
# When raised, the initial subscription or update fails completely.
|
23
|
+
class SubscriptionScopeMissingError < GraphQL::Error
|
24
|
+
end
|
25
|
+
|
19
26
|
# @see {Subscriptions#initialize} for options, concrete implementations may add options.
|
20
27
|
def self.use(defn, options = {})
|
21
28
|
schema = defn.is_a?(Class) ? defn : defn.target
|
@@ -76,7 +83,8 @@ module GraphQL
|
|
76
83
|
end
|
77
84
|
|
78
85
|
# Normalize symbol-keyed args to strings, try camelizing them
|
79
|
-
|
86
|
+
# Should this accept a real context somehow?
|
87
|
+
normalized_args = normalize_arguments(normalized_event_name, field, args, GraphQL::Query::NullContext)
|
80
88
|
|
81
89
|
event = Subscriptions::Event.new(
|
82
90
|
name: normalized_event_name,
|
@@ -151,16 +159,6 @@ module GraphQL
|
|
151
159
|
# @param object [Object]
|
152
160
|
# @return [void]
|
153
161
|
def execute_all(event, object)
|
154
|
-
each_subscription_id(event) do |subscription_id|
|
155
|
-
execute(subscription_id, event, object)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# Get each `subscription_id` subscribed to `event.topic` and yield them
|
160
|
-
# @param event [GraphQL::Subscriptions::Event]
|
161
|
-
# @yieldparam subscription_id [String]
|
162
|
-
# @return [void]
|
163
|
-
def each_subscription_id(event)
|
164
162
|
raise GraphQL::RequiredImplementationMissingError
|
165
163
|
end
|
166
164
|
|
@@ -233,7 +231,7 @@ module GraphQL
|
|
233
231
|
# @param arg_owner [GraphQL::Field, GraphQL::BaseType]
|
234
232
|
# @param args [Hash, Array, Any] some GraphQL input value to coerce as `arg_owner`
|
235
233
|
# @return [Any] normalized arguments value
|
236
|
-
def normalize_arguments(event_name, arg_owner, args)
|
234
|
+
def normalize_arguments(event_name, arg_owner, args, context)
|
237
235
|
case arg_owner
|
238
236
|
when GraphQL::Field, GraphQL::InputObjectType, GraphQL::Schema::Field, Class
|
239
237
|
if arg_owner.is_a?(Class) && !arg_owner.kind.input_object?
|
@@ -244,19 +242,19 @@ module GraphQL
|
|
244
242
|
missing_arg_names = []
|
245
243
|
args.each do |k, v|
|
246
244
|
arg_name = k.to_s
|
247
|
-
arg_defn = arg_owner.
|
245
|
+
arg_defn = arg_owner.get_argument(arg_name, context)
|
248
246
|
if arg_defn
|
249
247
|
normalized_arg_name = arg_name
|
250
248
|
else
|
251
249
|
normalized_arg_name = normalize_name(arg_name)
|
252
|
-
arg_defn = arg_owner.
|
250
|
+
arg_defn = arg_owner.get_argument(normalized_arg_name, context)
|
253
251
|
end
|
254
252
|
|
255
253
|
if arg_defn
|
256
254
|
if arg_defn.loads
|
257
255
|
normalized_arg_name = arg_defn.keyword.to_s
|
258
256
|
end
|
259
|
-
normalized = normalize_arguments(event_name, arg_defn.type, v)
|
257
|
+
normalized = normalize_arguments(event_name, arg_defn.type, v, context)
|
260
258
|
normalized_args[normalized_arg_name] = normalized
|
261
259
|
else
|
262
260
|
# Couldn't find a matching argument definition
|
@@ -266,12 +264,12 @@ module GraphQL
|
|
266
264
|
|
267
265
|
# Backfill default values so that trigger arguments
|
268
266
|
# match query arguments.
|
269
|
-
arg_owner.arguments.each do |
|
267
|
+
arg_owner.arguments(context).each do |_name, arg_defn|
|
270
268
|
if arg_defn.default_value? && !normalized_args.key?(arg_defn.name)
|
271
269
|
default_value = arg_defn.default_value
|
272
270
|
# We don't have an underlying "object" here, so it can't call methods.
|
273
271
|
# This is broken.
|
274
|
-
normalized_args[arg_defn.name] = arg_defn.prepare_value(nil, default_value, context:
|
272
|
+
normalized_args[arg_defn.name] = arg_defn.prepare_value(nil, default_value, context: context)
|
275
273
|
end
|
276
274
|
end
|
277
275
|
|
@@ -290,9 +288,9 @@ module GraphQL
|
|
290
288
|
|
291
289
|
normalized_args
|
292
290
|
when GraphQL::ListType, GraphQL::Schema::List
|
293
|
-
args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a) }
|
291
|
+
args.map { |a| normalize_arguments(event_name, arg_owner.of_type, a, context) }
|
294
292
|
when GraphQL::NonNullType, GraphQL::Schema::NonNull
|
295
|
-
normalize_arguments(event_name, arg_owner.of_type, args)
|
293
|
+
normalize_arguments(event_name, arg_owner.of_type, args, context)
|
296
294
|
else
|
297
295
|
args
|
298
296
|
end
|
@@ -14,7 +14,22 @@ module GraphQL
|
|
14
14
|
"execute_query_lazy" => "execute.graphql",
|
15
15
|
}
|
16
16
|
|
17
|
+
# @param set_action_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
|
18
|
+
# This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
|
19
|
+
# It can also be specified per-query with `context[:set_appsignal_action_name]`.
|
20
|
+
def initialize(options = {})
|
21
|
+
@set_action_name = options.fetch(:set_action_name, false)
|
22
|
+
super
|
23
|
+
end
|
24
|
+
|
17
25
|
def platform_trace(platform_key, key, data)
|
26
|
+
if key == "execute_query"
|
27
|
+
set_this_txn_name = data[:query].context[:set_appsignal_action_name]
|
28
|
+
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_action_name)
|
29
|
+
Appsignal::Transaction.current.set_action(transaction_name(data[:query]))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
18
33
|
Appsignal.instrument(platform_key) do
|
19
34
|
yield
|
20
35
|
end
|
data/lib/graphql/types/int.rb
CHANGED
@@ -35,7 +35,8 @@ module GraphQL
|
|
35
35
|
# It's called when you subclass this base connection, trying to use the
|
36
36
|
# class name to set defaults. You can call it again in the class definition
|
37
37
|
# to override the default (or provide a value, if the default lookup failed).
|
38
|
-
|
38
|
+
# @param field_options [Hash] Any extra keyword arguments to pass to the `field :edges, ...` and `field :nodes, ...` configurations
|
39
|
+
def edge_type(edge_type_class, edge_class: GraphQL::Relay::Edge, node_type: edge_type_class.node_type, nodes_field: self.has_nodes_field, node_nullable: self.node_nullable, edges_nullable: self.edges_nullable, edge_nullable: self.edge_nullable, field_options: nil)
|
39
40
|
# Set this connection's graphql name
|
40
41
|
node_type_name = node_type.graphql_name
|
41
42
|
|
@@ -43,13 +44,22 @@ module GraphQL
|
|
43
44
|
@edge_type = edge_type_class
|
44
45
|
@edge_class = edge_class
|
45
46
|
|
46
|
-
|
47
|
+
base_field_options = {
|
48
|
+
name: :edges,
|
49
|
+
type: [edge_type_class, null: edge_nullable],
|
47
50
|
null: edges_nullable,
|
48
51
|
description: "A list of edges.",
|
49
52
|
legacy_edge_class: edge_class, # This is used by the old runtime only, for EdgesInstrumentation
|
50
|
-
connection: false
|
53
|
+
connection: false,
|
54
|
+
}
|
51
55
|
|
52
|
-
|
56
|
+
if field_options
|
57
|
+
base_field_options.merge!(field_options)
|
58
|
+
end
|
59
|
+
|
60
|
+
field(**base_field_options)
|
61
|
+
|
62
|
+
define_nodes_field(node_nullable, field_options: field_options) if nodes_field
|
53
63
|
|
54
64
|
description("The connection type for #{node_type_name}.")
|
55
65
|
end
|
@@ -60,8 +70,8 @@ module GraphQL
|
|
60
70
|
end
|
61
71
|
|
62
72
|
# Add the shortcut `nodes` field to this connection and its subclasses
|
63
|
-
def nodes_field(node_nullable: self.node_nullable)
|
64
|
-
define_nodes_field(node_nullable)
|
73
|
+
def nodes_field(node_nullable: self.node_nullable, field_options: nil)
|
74
|
+
define_nodes_field(node_nullable, field_options: field_options)
|
65
75
|
end
|
66
76
|
|
67
77
|
def authorized?(obj, ctx)
|
@@ -118,11 +128,18 @@ module GraphQL
|
|
118
128
|
|
119
129
|
private
|
120
130
|
|
121
|
-
def define_nodes_field(nullable)
|
122
|
-
|
131
|
+
def define_nodes_field(nullable, field_options: nil)
|
132
|
+
base_field_options = {
|
133
|
+
name: :nodes,
|
134
|
+
type: [@node_type, null: nullable],
|
123
135
|
null: nullable,
|
124
136
|
description: "A list of nodes.",
|
125
|
-
connection: false
|
137
|
+
connection: false,
|
138
|
+
}
|
139
|
+
if field_options
|
140
|
+
base_field_options.merge!(field_options)
|
141
|
+
end
|
142
|
+
field(**base_field_options)
|
126
143
|
end
|
127
144
|
end
|
128
145
|
|
@@ -17,7 +17,11 @@ module GraphQL
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def to_graphql
|
20
|
-
type_defn =
|
20
|
+
type_defn = if method(:to_graphql).super_method.arity
|
21
|
+
super(silence_deprecation_warning: true)
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
21
25
|
type_defn.default_relay = default_relay?
|
22
26
|
type_defn
|
23
27
|
end
|
@@ -16,11 +16,22 @@ module GraphQL
|
|
16
16
|
#
|
17
17
|
# @param node_type [Class] A `Schema::Object` subclass
|
18
18
|
# @param null [Boolean]
|
19
|
-
|
19
|
+
# @param field_options [Hash] Any extra arguments to pass to the `field :node` configuration
|
20
|
+
def node_type(node_type = nil, null: self.node_nullable, field_options: nil)
|
20
21
|
if node_type
|
21
22
|
@node_type = node_type
|
22
23
|
# Add a default `node` field
|
23
|
-
|
24
|
+
base_field_options = {
|
25
|
+
name: :node,
|
26
|
+
type: node_type,
|
27
|
+
null: null,
|
28
|
+
description: "The item at the end of the edge.",
|
29
|
+
connection: false,
|
30
|
+
}
|
31
|
+
if field_options
|
32
|
+
base_field_options.merge!(field_options)
|
33
|
+
end
|
34
|
+
field(**base_field_options)
|
24
35
|
end
|
25
36
|
@node_type
|
26
37
|
end
|
data/lib/graphql/types/string.rb
CHANGED
@@ -12,7 +12,7 @@ module GraphQL
|
|
12
12
|
attr_reader :type
|
13
13
|
|
14
14
|
# @return [GraphQL::Query::Context] the context for the current query
|
15
|
-
|
15
|
+
attr_accessor :context
|
16
16
|
|
17
17
|
def initialize(message = nil, object: nil, type: nil, context: nil)
|
18
18
|
if message.nil? && object.nil? && type.nil?
|
data/lib/graphql/version.rb
CHANGED
data/lib/graphql.rb
CHANGED
@@ -55,31 +55,6 @@ module GraphQL
|
|
55
55
|
def self.scan_with_ragel(graphql_string)
|
56
56
|
GraphQL::Language::Lexer.tokenize(graphql_string)
|
57
57
|
end
|
58
|
-
|
59
|
-
# Support Ruby 2.2 by implementing `-"str"`. If we drop 2.2 support, we can remove this backport.
|
60
|
-
if !String.method_defined?(:-@)
|
61
|
-
module StringDedupBackport
|
62
|
-
refine String do
|
63
|
-
def -@
|
64
|
-
if frozen?
|
65
|
-
self
|
66
|
-
else
|
67
|
-
self.dup.freeze
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
if !String.method_defined?(:match?)
|
75
|
-
module StringMatchBackport
|
76
|
-
refine String do
|
77
|
-
def match?(pattern)
|
78
|
-
self =~ pattern
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
58
|
end
|
84
59
|
|
85
60
|
# Order matters for these:
|
@@ -125,10 +100,14 @@ require "graphql/execution"
|
|
125
100
|
require "graphql/pagination"
|
126
101
|
require "graphql/schema"
|
127
102
|
require "graphql/query"
|
103
|
+
require "graphql/types"
|
104
|
+
require "graphql/dataloader"
|
105
|
+
require "graphql/filter"
|
106
|
+
require "graphql/internal_representation"
|
128
107
|
require "graphql/directive"
|
108
|
+
require "graphql/static_validation"
|
129
109
|
require "graphql/execution"
|
130
|
-
require "graphql/
|
131
|
-
require "graphql/relay"
|
110
|
+
require "graphql/deprecation"
|
132
111
|
require "graphql/boolean_type"
|
133
112
|
require "graphql/float_type"
|
134
113
|
require "graphql/id_type"
|
@@ -137,11 +116,8 @@ require "graphql/string_type"
|
|
137
116
|
require "graphql/schema/built_in_types"
|
138
117
|
require "graphql/schema/loader"
|
139
118
|
require "graphql/schema/printer"
|
140
|
-
require "graphql/filter"
|
141
|
-
require "graphql/internal_representation"
|
142
|
-
require "graphql/static_validation"
|
143
|
-
require "graphql/dataloader"
|
144
119
|
require "graphql/introspection"
|
120
|
+
require "graphql/relay"
|
145
121
|
|
146
122
|
require "graphql/version"
|
147
123
|
require "graphql/compatibility"
|
@@ -155,7 +131,9 @@ require "graphql/authorization"
|
|
155
131
|
require "graphql/unauthorized_error"
|
156
132
|
require "graphql/unauthorized_field_error"
|
157
133
|
require "graphql/load_application_object_failed_error"
|
158
|
-
require "graphql/
|
134
|
+
require "graphql/directive/include_directive"
|
135
|
+
require "graphql/directive/skip_directive"
|
136
|
+
require "graphql/directive/deprecated_directive"
|
159
137
|
|
160
138
|
module GraphQL
|
161
139
|
# Ruby has `deprecate_constant`,
|
data/readme.md
CHANGED
@@ -44,6 +44,6 @@ I also sell [GraphQL::Pro](https://graphql.pro) which provides several features
|
|
44
44
|
|
45
45
|
## Getting Involved
|
46
46
|
|
47
|
-
- __Say hi & ask questions__ in the
|
47
|
+
- __Say hi & ask questions__ in the #graphql-ruby channel on [Discord](https://discord.com/invite/xud7bH9) or [on Twitter](https://twitter.com/rmosolgo)!
|
48
48
|
- __Report bugs__ by posting a description, full stack trace, and all relevant code in a [GitHub issue](https://github.com/rmosolgo/graphql-ruby/issues).
|
49
49
|
- __Start hacking__ with the [Development guide](https://graphql-ruby.org/development).
|