graphql 1.12.23 → 1.13.3
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/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/type_generator.rb +0 -1
- data/lib/graphql/analysis/ast/field_usage.rb +2 -2
- 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.rb +55 -22
- 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 +31 -19
- 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/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 +4 -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 +10 -4
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/nodes.rb +3 -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/active_record_relation_connection.rb +43 -6
- data/lib/graphql/pagination/relation_connection.rb +55 -28
- 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/variables.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 +37 -28
- data/lib/graphql/schema/argument.rb +13 -15
- 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 +229 -77
- 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 +23 -5
- 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 +55 -13
- 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 +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 +37 -17
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/subscription.rb +11 -1
- 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/format_validator.rb +0 -4
- data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
- data/lib/graphql/schema/validator/required_validator.rb +29 -15
- data/lib/graphql/schema/validator.rb +4 -7
- data/lib/graphql/schema/warden.rb +116 -52
- data/lib/graphql/schema.rb +111 -23
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +5 -5
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -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 +1 -1
- 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 +2 -2
- 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/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -4
- data/lib/graphql/subscriptions/event.rb +20 -12
- data/lib/graphql/subscriptions/serialize.rb +22 -2
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
- data/lib/graphql/tracing/notifications_tracing.rb +59 -0
- 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/relay/node_field.rb +14 -3
- data/lib/graphql/types/relay/nodes_field.rb +13 -3
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -32
- metadata +13 -5
@@ -34,16 +34,16 @@ module GraphQL
|
|
34
34
|
parent_type = get_parent_type(context, parent)
|
35
35
|
return unless parent_type && parent_type.kind.input_object?
|
36
36
|
|
37
|
-
required_fields =
|
38
|
-
.select{|
|
39
|
-
.
|
37
|
+
required_fields = context.warden.arguments(parent_type)
|
38
|
+
.select{|arg| arg.type.kind.non_null?}
|
39
|
+
.map(&:graphql_name)
|
40
40
|
|
41
41
|
present_fields = ast_node.arguments.map(&:name)
|
42
42
|
missing_fields = required_fields - present_fields
|
43
43
|
|
44
44
|
missing_fields.each do |missing_field|
|
45
45
|
path = [*context.path, missing_field]
|
46
|
-
missing_field_type = parent_type
|
46
|
+
missing_field_type = context.warden.get_argument(parent_type, missing_field).type
|
47
47
|
add_error(RequiredInputObjectAttributesArePresentError.new(
|
48
48
|
"Argument '#{missing_field}' on InputObject '#{parent_type.to_type_signature}' is required. Expected type #{missing_field_type.to_type_signature}",
|
49
49
|
argument_name: missing_field,
|
@@ -22,15 +22,15 @@ module GraphQL
|
|
22
22
|
node_values = node_values.select { |value| value.is_a? GraphQL::Language::Nodes::VariableIdentifier }
|
23
23
|
|
24
24
|
if node_values.any?
|
25
|
-
|
25
|
+
argument_owner = case parent
|
26
26
|
when GraphQL::Language::Nodes::Field
|
27
|
-
context.field_definition
|
27
|
+
context.field_definition
|
28
28
|
when GraphQL::Language::Nodes::Directive
|
29
|
-
context.directive_definition
|
29
|
+
context.directive_definition
|
30
30
|
when GraphQL::Language::Nodes::InputObject
|
31
31
|
arg_type = context.argument_definition.type.unwrap
|
32
32
|
if arg_type.kind.input_object?
|
33
|
-
|
33
|
+
arg_type
|
34
34
|
else
|
35
35
|
# This is some kind of error
|
36
36
|
nil
|
@@ -43,7 +43,7 @@ module GraphQL
|
|
43
43
|
var_defn_ast = @declared_variables[node_value.name]
|
44
44
|
# Might be undefined :(
|
45
45
|
# VariablesAreUsedAndDefined can't finalize its search until the end of the document.
|
46
|
-
var_defn_ast &&
|
46
|
+
var_defn_ast && argument_owner && validate_usage(argument_owner, node, var_defn_ast)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
super
|
@@ -51,7 +51,7 @@ module GraphQL
|
|
51
51
|
|
52
52
|
private
|
53
53
|
|
54
|
-
def validate_usage(
|
54
|
+
def validate_usage(argument_owner, arg_node, ast_var)
|
55
55
|
var_type = context.schema.type_from_ast(ast_var.type, context: context)
|
56
56
|
if var_type.nil?
|
57
57
|
return
|
@@ -65,9 +65,15 @@ module GraphQL
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
arg_defn =
|
68
|
+
arg_defn = context.warden.get_argument(argument_owner, arg_node.name)
|
69
69
|
arg_defn_type = arg_defn.type
|
70
70
|
|
71
|
+
# If the argument is non-null, but it was given a default value,
|
72
|
+
# then treat it as nullable in practice, see https://github.com/rmosolgo/graphql-ruby/issues/3793
|
73
|
+
if arg_defn_type.non_null? && arg_defn.default_value?
|
74
|
+
arg_defn_type = arg_defn_type.of_type
|
75
|
+
end
|
76
|
+
|
71
77
|
var_inner_type = var_type.unwrap
|
72
78
|
arg_inner_type = arg_defn_type.unwrap
|
73
79
|
|
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)
|
@@ -170,10 +170,12 @@ module GraphQL
|
|
170
170
|
first_subscription_id = first_event.context.fetch(:subscription_id)
|
171
171
|
object ||= load_action_cable_message(message, first_event.context)
|
172
172
|
result = execute_update(first_subscription_id, first_event, object)
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
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
|
177
179
|
end
|
178
180
|
end
|
179
181
|
end
|
@@ -214,6 +216,8 @@ module GraphQL
|
|
214
216
|
# The channel was closed, forget about it.
|
215
217
|
def delete_subscription(subscription_id)
|
216
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 })
|
217
221
|
# This can be `nil` when `.trigger` happens inside an unsubscribed ActionCable channel,
|
218
222
|
# see https://github.com/rmosolgo/graphql-ruby/issues/2478
|
219
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
|
|
@@ -83,7 +91,7 @@ module GraphQL
|
|
83
91
|
end
|
84
92
|
end
|
85
93
|
|
86
|
-
def stringify_args(arg_owner, args)
|
94
|
+
def stringify_args(arg_owner, args, context)
|
87
95
|
arg_owner = arg_owner.respond_to?(:unwrap) ? arg_owner.unwrap : arg_owner # remove list and non-null wrappers
|
88
96
|
case args
|
89
97
|
when Hash
|
@@ -91,13 +99,13 @@ module GraphQL
|
|
91
99
|
args.each do |k, v|
|
92
100
|
arg_name = k.to_s
|
93
101
|
camelized_arg_name = GraphQL::Schema::Member::BuildType.camelize(arg_name)
|
94
|
-
arg_defn = get_arg_definition(arg_owner, camelized_arg_name)
|
102
|
+
arg_defn = get_arg_definition(arg_owner, camelized_arg_name, context)
|
95
103
|
|
96
104
|
if arg_defn
|
97
105
|
normalized_arg_name = camelized_arg_name
|
98
106
|
else
|
99
107
|
normalized_arg_name = arg_name
|
100
|
-
arg_defn = get_arg_definition(arg_owner, normalized_arg_name)
|
108
|
+
arg_defn = get_arg_definition(arg_owner, normalized_arg_name, context)
|
101
109
|
end
|
102
110
|
arg_base_type = arg_defn.type.unwrap
|
103
111
|
# In the case where the value being emitted is seen as a "JSON"
|
@@ -113,22 +121,22 @@ module GraphQL
|
|
113
121
|
end
|
114
122
|
next_args[normalized_arg_name] = sorted_value.respond_to?(:to_json) ? sorted_value.to_json : sorted_value
|
115
123
|
else
|
116
|
-
next_args[normalized_arg_name] = stringify_args(arg_base_type, v)
|
124
|
+
next_args[normalized_arg_name] = stringify_args(arg_base_type, v, context)
|
117
125
|
end
|
118
126
|
end
|
119
127
|
# Make sure they're deeply sorted
|
120
128
|
next_args.sort.to_h
|
121
129
|
when Array
|
122
|
-
args.map { |a| stringify_args(arg_owner, a) }
|
130
|
+
args.map { |a| stringify_args(arg_owner, a, context) }
|
123
131
|
when GraphQL::Schema::InputObject
|
124
|
-
stringify_args(arg_owner, args.to_h)
|
132
|
+
stringify_args(arg_owner, args.to_h, context)
|
125
133
|
else
|
126
134
|
args
|
127
135
|
end
|
128
136
|
end
|
129
137
|
|
130
|
-
def get_arg_definition(arg_owner, arg_name)
|
131
|
-
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 }
|
132
140
|
end
|
133
141
|
end
|
134
142
|
end
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -2,8 +2,7 @@
|
|
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
|
@@ -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,8 +2,7 @@
|
|
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
|
@@ -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/version.rb
CHANGED