graphql 1.12.10 → 1.13.4
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/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 +28 -1
- 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 +15 -3
- data/lib/graphql/backtrace/tracer.rb +7 -4
- data/lib/graphql/base_type.rb +4 -2
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/dataloader/null_dataloader.rb +1 -0
- data/lib/graphql/dataloader/source.rb +50 -2
- data/lib/graphql/dataloader.rb +110 -41
- 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/execute.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -4
- data/lib/graphql/execution/interpreter/resolve.rb +6 -2
- data/lib/graphql/execution/interpreter/runtime.rb +513 -213
- data/lib/graphql/execution/interpreter.rb +4 -8
- data/lib/graphql/execution/lazy.rb +5 -1
- 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 +3 -3
- 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 +10 -4
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/nodes.rb +13 -3
- 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/active_record_relation_connection.rb +43 -6
- data/lib/graphql/pagination/connections.rb +40 -16
- data/lib/graphql/pagination/relation_connection.rb +57 -27
- 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 +5 -1
- 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 +247 -0
- data/lib/graphql/schema/argument.rb +103 -45
- data/lib/graphql/schema/build_from_definition.rb +13 -7
- 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 +14 -2
- data/lib/graphql/schema/directive.rb +7 -3
- data/lib/graphql/schema/enum.rb +70 -11
- 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 +243 -81
- data/lib/graphql/schema/field_extension.rb +89 -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 +39 -29
- 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 +1 -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 +7 -1
- data/lib/graphql/schema/object.rb +10 -75
- data/lib/graphql/schema/printer.rb +12 -17
- 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 +75 -65
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/subscription.rb +36 -8
- 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/required_validator.rb +29 -15
- data/lib/graphql/schema/validator.rb +33 -25
- data/lib/graphql/schema/warden.rb +116 -52
- data/lib/graphql/schema.rb +162 -227
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- 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/query_root_exists.rb +17 -0
- data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +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 +13 -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 +36 -6
- data/lib/graphql/subscriptions/event.rb +68 -31
- data/lib/graphql/subscriptions/serialize.rb +23 -3
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- data/lib/graphql/tracing/notifications_tracing.rb +59 -0
- data/lib/graphql/types/big_int.rb +5 -1
- 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 +2 -2
- data/lib/graphql/types/relay/has_nodes_field.rb +2 -2
- data/lib/graphql/types/relay/node_field.rb +15 -4
- data/lib/graphql/types/relay/nodes_field.rb +14 -4
- 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 -28
- data/readme.md +1 -4
- metadata +17 -21
- data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
|
@@ -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
|
|
@@ -71,9 +71,17 @@ module GraphQL
|
|
|
71
71
|
when SYMBOL_KEY
|
|
72
72
|
value[SYMBOL_KEY].to_sym
|
|
73
73
|
when TIMESTAMP_KEY
|
|
74
|
-
timestamp_class_name,
|
|
74
|
+
timestamp_class_name, *timestamp_args = value[TIMESTAMP_KEY]
|
|
75
75
|
timestamp_class = Object.const_get(timestamp_class_name)
|
|
76
|
-
|
|
76
|
+
if defined?(ActiveSupport::TimeWithZone) && timestamp_class <= ActiveSupport::TimeWithZone
|
|
77
|
+
zone_name, timestamp_s = timestamp_args
|
|
78
|
+
zone = ActiveSupport::TimeZone[zone_name]
|
|
79
|
+
raise "Zone #{zone_name} not found, unable to deserialize" unless zone
|
|
80
|
+
zone.strptime(timestamp_s, TIMESTAMP_FORMAT)
|
|
81
|
+
else
|
|
82
|
+
timestamp_s = timestamp_args.first
|
|
83
|
+
timestamp_class.strptime(timestamp_s, TIMESTAMP_FORMAT)
|
|
84
|
+
end
|
|
77
85
|
when OPEN_STRUCT_KEY
|
|
78
86
|
ostruct_values = load_value(value[OPEN_STRUCT_KEY])
|
|
79
87
|
OpenStruct.new(ostruct_values)
|
|
@@ -123,6 +131,18 @@ module GraphQL
|
|
|
123
131
|
{ SYMBOL_KEY => obj.to_s }
|
|
124
132
|
elsif obj.respond_to?(:to_gid_param)
|
|
125
133
|
{GLOBALID_KEY => obj.to_gid_param}
|
|
134
|
+
elsif defined?(ActiveSupport::TimeWithZone) && obj.is_a?(ActiveSupport::TimeWithZone) && obj.class.name != Time.name
|
|
135
|
+
# This handles a case where Rails prior to 7 would
|
|
136
|
+
# make the class ActiveSupport::TimeWithZone return "Time" for
|
|
137
|
+
# its name. In Rails 7, it will now return "ActiveSupport::TimeWithZone",
|
|
138
|
+
# which happens to be incompatible with expectations we have
|
|
139
|
+
# with what a Time class supports ( notably, strptime in `load_value` ).
|
|
140
|
+
#
|
|
141
|
+
# This now passes along the name of the zone, such that a future deserialization
|
|
142
|
+
# of this string will use the correct time zone from the ActiveSupport TimeZone
|
|
143
|
+
# list to produce the time.
|
|
144
|
+
#
|
|
145
|
+
{ TIMESTAMP_KEY => [obj.class.name, obj.time_zone.name, obj.strftime(TIMESTAMP_FORMAT)] }
|
|
126
146
|
elsif obj.is_a?(Date) || obj.is_a?(Time)
|
|
127
147
|
# DateTime extends Date; for TimeWithZone, call `.utc` first.
|
|
128
148
|
{ TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
|
|
@@ -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
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'graphql/tracing/notifications_tracing'
|
|
4
|
+
|
|
3
5
|
module GraphQL
|
|
4
6
|
module Tracing
|
|
5
7
|
# This implementation forwards events to ActiveSupport::Notifications
|
|
@@ -8,27 +10,11 @@ module GraphQL
|
|
|
8
10
|
# @see KEYS for event names
|
|
9
11
|
module ActiveSupportNotificationsTracing
|
|
10
12
|
# A cache of frequently-used keys to avoid needless string allocations
|
|
11
|
-
KEYS =
|
|
12
|
-
|
|
13
|
-
"parse" => "parse.graphql",
|
|
14
|
-
"validate" => "validate.graphql",
|
|
15
|
-
"analyze_multiplex" => "analyze_multiplex.graphql",
|
|
16
|
-
"analyze_query" => "analyze_query.graphql",
|
|
17
|
-
"execute_query" => "execute_query.graphql",
|
|
18
|
-
"execute_query_lazy" => "execute_query_lazy.graphql",
|
|
19
|
-
"execute_field" => "execute_field.graphql",
|
|
20
|
-
"execute_field_lazy" => "execute_field_lazy.graphql",
|
|
21
|
-
"authorized" => "authorized.graphql",
|
|
22
|
-
"authorized_lazy" => "authorized_lazy.graphql",
|
|
23
|
-
"resolve_type" => "resolve_type.graphql",
|
|
24
|
-
"resolve_type_lazy" => "resolve_type.graphql",
|
|
25
|
-
}
|
|
13
|
+
KEYS = NotificationsTracing::KEYS
|
|
14
|
+
NOTIFICATIONS_ENGINE = NotificationsTracing.new(ActiveSupport::Notifications) if defined?(ActiveSupport)
|
|
26
15
|
|
|
27
|
-
def self.trace(key, metadata)
|
|
28
|
-
|
|
29
|
-
ActiveSupport::Notifications.instrument(prefixed_key, metadata) do
|
|
30
|
-
yield
|
|
31
|
-
end
|
|
16
|
+
def self.trace(key, metadata, &blk)
|
|
17
|
+
NOTIFICATIONS_ENGINE.trace(key, metadata, &blk)
|
|
32
18
|
end
|
|
33
19
|
end
|
|
34
20
|
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
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
module Tracing
|
|
5
|
+
# This implementation forwards events to a notification handler (i.e.
|
|
6
|
+
# ActiveSupport::Notifications or Dry::Monitor::Notifications)
|
|
7
|
+
# with a `graphql` suffix.
|
|
8
|
+
#
|
|
9
|
+
# @see KEYS for event names
|
|
10
|
+
class NotificationsTracing
|
|
11
|
+
# A cache of frequently-used keys to avoid needless string allocations
|
|
12
|
+
KEYS = {
|
|
13
|
+
"lex" => "lex.graphql",
|
|
14
|
+
"parse" => "parse.graphql",
|
|
15
|
+
"validate" => "validate.graphql",
|
|
16
|
+
"analyze_multiplex" => "analyze_multiplex.graphql",
|
|
17
|
+
"analyze_query" => "analyze_query.graphql",
|
|
18
|
+
"execute_query" => "execute_query.graphql",
|
|
19
|
+
"execute_query_lazy" => "execute_query_lazy.graphql",
|
|
20
|
+
"execute_field" => "execute_field.graphql",
|
|
21
|
+
"execute_field_lazy" => "execute_field_lazy.graphql",
|
|
22
|
+
"authorized" => "authorized.graphql",
|
|
23
|
+
"authorized_lazy" => "authorized_lazy.graphql",
|
|
24
|
+
"resolve_type" => "resolve_type.graphql",
|
|
25
|
+
"resolve_type_lazy" => "resolve_type.graphql",
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
MAX_KEYS_SIZE = 100
|
|
29
|
+
|
|
30
|
+
# Initialize a new NotificationsTracing instance
|
|
31
|
+
#
|
|
32
|
+
# @param [Object] notifications_engine The notifications engine to use
|
|
33
|
+
def initialize(notifications_engine)
|
|
34
|
+
@notifications_engine = notifications_engine
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Sends a GraphQL tracing event to the notification handler
|
|
38
|
+
#
|
|
39
|
+
# @example
|
|
40
|
+
# . notifications_engine = Dry::Monitor::Notifications.new(:graphql)
|
|
41
|
+
# . tracer = GraphQL::Tracing::NotificationsTracing.new(notifications_engine)
|
|
42
|
+
# . tracer.trace("lex") { ... }
|
|
43
|
+
#
|
|
44
|
+
# @param [string] key The key for the event
|
|
45
|
+
# @param [Hash] metadata The metadata for the event
|
|
46
|
+
# @yield The block to execute for the event
|
|
47
|
+
def trace(key, metadata, &blk)
|
|
48
|
+
prefixed_key = KEYS[key] || "#{key}.graphql"
|
|
49
|
+
|
|
50
|
+
# Cache the new keys while making sure not to induce a memory leak
|
|
51
|
+
if KEYS.size < MAX_KEYS_SIZE
|
|
52
|
+
KEYS[key] ||= prefixed_key
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
@notifications_engine.instrument(prefixed_key, metadata, &blk)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -6,7 +6,7 @@ module GraphQL
|
|
|
6
6
|
description "Represents non-fractional signed whole numeric values. Since the value may exceed the size of a 32-bit integer, it's encoded as a string."
|
|
7
7
|
|
|
8
8
|
def self.coerce_input(value, _ctx)
|
|
9
|
-
value &&
|
|
9
|
+
value && parse_int(value)
|
|
10
10
|
rescue ArgumentError
|
|
11
11
|
nil
|
|
12
12
|
end
|
|
@@ -14,6 +14,10 @@ module GraphQL
|
|
|
14
14
|
def self.coerce_result(value, _ctx)
|
|
15
15
|
value.to_i.to_s
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
def self.parse_int(value)
|
|
19
|
+
value.is_a?(Numeric) ? value : Integer(value, 10)
|
|
20
|
+
end
|
|
17
21
|
end
|
|
18
22
|
end
|
|
19
23
|
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
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Types
|
|
5
5
|
module Relay
|
|
6
|
+
# Include this module to your root Query type to get a Relay-compliant `node(id: ID!): Node` field that uses the schema's `object_from_id` hook.
|
|
6
7
|
module HasNodeField
|
|
7
8
|
def self.included(child_class)
|
|
8
9
|
child_class.field(**field_options, &field_block)
|
|
@@ -12,7 +13,6 @@ module GraphQL
|
|
|
12
13
|
def field_options
|
|
13
14
|
{
|
|
14
15
|
name: "node",
|
|
15
|
-
owner: nil,
|
|
16
16
|
type: GraphQL::Types::Relay::Node,
|
|
17
17
|
null: true,
|
|
18
18
|
description: "Fetches an object given its ID.",
|
|
@@ -22,7 +22,7 @@ module GraphQL
|
|
|
22
22
|
|
|
23
23
|
def field_block
|
|
24
24
|
Proc.new {
|
|
25
|
-
argument :id, "ID!",
|
|
25
|
+
argument :id, "ID!",
|
|
26
26
|
description: "ID of the object."
|
|
27
27
|
|
|
28
28
|
def resolve(obj, args, ctx)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Types
|
|
5
5
|
module Relay
|
|
6
|
+
# Include this module to your root Query type to get a Relay-style `nodes(id: ID!): [Node]` field that uses the schema's `object_from_id` hook.
|
|
6
7
|
module HasNodesField
|
|
7
8
|
def self.included(child_class)
|
|
8
9
|
child_class.field(**field_options, &field_block)
|
|
@@ -12,7 +13,6 @@ module GraphQL
|
|
|
12
13
|
def field_options
|
|
13
14
|
{
|
|
14
15
|
name: "nodes",
|
|
15
|
-
owner: nil,
|
|
16
16
|
type: [GraphQL::Types::Relay::Node, null: true],
|
|
17
17
|
null: false,
|
|
18
18
|
description: "Fetches a list of objects given a list of IDs.",
|
|
@@ -22,7 +22,7 @@ module GraphQL
|
|
|
22
22
|
|
|
23
23
|
def field_block
|
|
24
24
|
Proc.new {
|
|
25
|
-
argument :ids, "[ID!]!",
|
|
25
|
+
argument :ids, "[ID!]!",
|
|
26
26
|
description: "IDs of the objects."
|
|
27
27
|
|
|
28
28
|
def resolve(obj, args, ctx)
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
module GraphQL
|
|
3
3
|
module Types
|
|
4
4
|
module Relay
|
|
5
|
-
#
|
|
6
|
-
# or use it for inspiration for your own field definition.
|
|
5
|
+
# Don't use this field directly, instead, use one of these approaches:
|
|
7
6
|
#
|
|
8
7
|
# @example Adding this field directly
|
|
9
|
-
#
|
|
8
|
+
# include GraphQL::Types::Relay::HasNodeField
|
|
10
9
|
#
|
|
11
10
|
# @example Implementing a similar field in your own Query root
|
|
12
11
|
#
|
|
@@ -19,7 +18,19 @@ module GraphQL
|
|
|
19
18
|
# context.schema.object_from_id(id, context)
|
|
20
19
|
# end
|
|
21
20
|
#
|
|
22
|
-
|
|
21
|
+
def self.const_missing(const_name)
|
|
22
|
+
if const_name == :NodeField
|
|
23
|
+
message = "NodeField is deprecated, use `include GraphQL::Types::Relay::HasNodeField` instead."
|
|
24
|
+
message += "\n(referenced from #{caller(1, 1).first})"
|
|
25
|
+
GraphQL::Deprecation.warn(message)
|
|
26
|
+
|
|
27
|
+
DeprecatedNodeField
|
|
28
|
+
else
|
|
29
|
+
super
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
DeprecatedNodeField = GraphQL::Schema::Field.new(owner: nil, **HasNodeField.field_options, &HasNodeField.field_block)
|
|
23
34
|
end
|
|
24
35
|
end
|
|
25
36
|
end
|
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
module GraphQL
|
|
3
3
|
module Types
|
|
4
4
|
module Relay
|
|
5
|
-
#
|
|
6
|
-
# or use it for inspiration for your own field definition.
|
|
5
|
+
# Don't use this directly, instead, use one of these:
|
|
7
6
|
#
|
|
8
7
|
# @example Adding this field directly
|
|
9
|
-
#
|
|
8
|
+
# include GraphQL::Types::Relay::HasNodesField
|
|
10
9
|
#
|
|
11
10
|
# @example Implementing a similar field in your own Query root
|
|
12
11
|
#
|
|
@@ -21,7 +20,18 @@ module GraphQL
|
|
|
21
20
|
# end
|
|
22
21
|
# end
|
|
23
22
|
#
|
|
24
|
-
|
|
23
|
+
def self.const_missing(const_name)
|
|
24
|
+
if const_name == :NodesField
|
|
25
|
+
message = "NodesField is deprecated, use `include GraphQL::Types::Relay::HasNodesField` instead."
|
|
26
|
+
message += "\n(referenced from #{caller(1, 1).first})"
|
|
27
|
+
GraphQL::Deprecation.warn(message)
|
|
28
|
+
|
|
29
|
+
DeprecatedNodesField
|
|
30
|
+
else
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
DeprecatedNodesField = GraphQL::Schema::Field.new(owner: nil, **HasNodesField.field_options, &HasNodesField.field_block)
|
|
25
35
|
end
|
|
26
36
|
end
|
|
27
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,27 +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
|
-
module StringDedupBackport
|
|
61
|
-
refine String do
|
|
62
|
-
def -@
|
|
63
|
-
if frozen?
|
|
64
|
-
self
|
|
65
|
-
else
|
|
66
|
-
self.dup.freeze
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
module StringMatchBackport
|
|
73
|
-
refine String do
|
|
74
|
-
def match?(pattern)
|
|
75
|
-
self =~ pattern
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
58
|
end
|
|
80
59
|
|
|
81
60
|
# Order matters for these:
|
|
@@ -121,10 +100,14 @@ require "graphql/execution"
|
|
|
121
100
|
require "graphql/pagination"
|
|
122
101
|
require "graphql/schema"
|
|
123
102
|
require "graphql/query"
|
|
103
|
+
require "graphql/types"
|
|
104
|
+
require "graphql/dataloader"
|
|
105
|
+
require "graphql/filter"
|
|
106
|
+
require "graphql/internal_representation"
|
|
124
107
|
require "graphql/directive"
|
|
108
|
+
require "graphql/static_validation"
|
|
125
109
|
require "graphql/execution"
|
|
126
|
-
require "graphql/
|
|
127
|
-
require "graphql/relay"
|
|
110
|
+
require "graphql/deprecation"
|
|
128
111
|
require "graphql/boolean_type"
|
|
129
112
|
require "graphql/float_type"
|
|
130
113
|
require "graphql/id_type"
|
|
@@ -133,11 +116,8 @@ require "graphql/string_type"
|
|
|
133
116
|
require "graphql/schema/built_in_types"
|
|
134
117
|
require "graphql/schema/loader"
|
|
135
118
|
require "graphql/schema/printer"
|
|
136
|
-
require "graphql/filter"
|
|
137
|
-
require "graphql/internal_representation"
|
|
138
|
-
require "graphql/static_validation"
|
|
139
|
-
require "graphql/dataloader"
|
|
140
119
|
require "graphql/introspection"
|
|
120
|
+
require "graphql/relay"
|
|
141
121
|
|
|
142
122
|
require "graphql/version"
|
|
143
123
|
require "graphql/compatibility"
|
|
@@ -151,7 +131,9 @@ require "graphql/authorization"
|
|
|
151
131
|
require "graphql/unauthorized_error"
|
|
152
132
|
require "graphql/unauthorized_field_error"
|
|
153
133
|
require "graphql/load_application_object_failed_error"
|
|
154
|
-
require "graphql/
|
|
134
|
+
require "graphql/directive/include_directive"
|
|
135
|
+
require "graphql/directive/skip_directive"
|
|
136
|
+
require "graphql/directive/deprecated_directive"
|
|
155
137
|
|
|
156
138
|
module GraphQL
|
|
157
139
|
# Ruby has `deprecate_constant`,
|
data/readme.md
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/rmosolgo/graphql-ruby/actions/workflows/ci.yaml)
|
|
4
4
|
[](https://rubygems.org/gems/graphql)
|
|
5
|
-
[](https://codeclimate.com/github/rmosolgo/graphql-ruby)
|
|
6
|
-
[](https://codeclimate.com/github/rmosolgo/graphql-ruby)
|
|
7
|
-
[](https://rmosolgo.github.io/react-badges/)
|
|
8
5
|
|
|
9
6
|
A Ruby implementation of [GraphQL](https://graphql.org/).
|
|
10
7
|
|
|
@@ -47,6 +44,6 @@ I also sell [GraphQL::Pro](https://graphql.pro) which provides several features
|
|
|
47
44
|
|
|
48
45
|
## Getting Involved
|
|
49
46
|
|
|
50
|
-
- __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)!
|
|
51
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).
|
|
52
49
|
- __Start hacking__ with the [Development guide](https://graphql-ruby.org/development).
|