graphql 1.10.8 → 1.10.13
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/object_generator.rb +50 -8
- data/lib/graphql.rb +4 -4
- data/lib/graphql/execution/instrumentation.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments.rb +2 -5
- data/lib/graphql/execution/interpreter/runtime.rb +18 -23
- data/lib/graphql/object_type.rb +1 -1
- data/lib/graphql/schema.rb +11 -3
- data/lib/graphql/schema/argument.rb +6 -0
- data/lib/graphql/schema/enum.rb +9 -1
- data/lib/graphql/schema/field.rb +24 -20
- data/lib/graphql/schema/input_object.rb +17 -18
- data/lib/graphql/schema/interface.rb +5 -0
- data/lib/graphql/schema/list.rb +2 -1
- data/lib/graphql/schema/loader.rb +3 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/member/has_arguments.rb +20 -0
- data/lib/graphql/schema/member/has_fields.rb +1 -1
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
- data/lib/graphql/schema/object.rb +7 -0
- data/lib/graphql/schema/union.rb +6 -0
- data/lib/graphql/schema/warden.rb +13 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions/subscription_root.rb +10 -2
- data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
- data/lib/graphql/tracing/platform_tracing.rb +14 -0
- data/lib/graphql/tracing/scout_tracing.rb +11 -0
- data/lib/graphql/tracing/statsd_tracing.rb +42 -0
- data/lib/graphql/types/iso_8601_date.rb +2 -2
- data/lib/graphql/types/iso_8601_date_time.rb +19 -15
- data/lib/graphql/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 617464390cac424fae4c09fe3db0abbaa6e0d980cb349a9b8e51c4d84b2cb662
|
4
|
+
data.tar.gz: 4abf2fbac2606f6e8b493ca379a11569597500433bba51c8acbc7fdb891a3272
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: de0cca8810127cf5649e68f6968e970131d7f34a0b1cd33230b79e56ae7e311134f96f588820caf273ef529c89e12a8df8c9afe213dbb9477cb798923044beb7
|
7
|
+
data.tar.gz: 30014092e85f256ace16b3635ed9e4e2c8a081904bca4e1e6197d0fff07f13d7997968ffc8aaee9de576d5372e219d432a8a62ae3406d81a61f9435debeaa247
|
@@ -15,20 +15,62 @@ module Graphql
|
|
15
15
|
desc "Create a GraphQL::ObjectType with the given name and fields"
|
16
16
|
source_root File.expand_path('../templates', __FILE__)
|
17
17
|
|
18
|
-
argument :
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
argument :custom_fields,
|
19
|
+
type: :array,
|
20
|
+
default: [],
|
21
|
+
banner: "name:type name:type ...",
|
22
|
+
desc: "Fields for this object (type may be expressed as Ruby or GraphQL)"
|
23
23
|
|
24
24
|
class_option :node,
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
type: :boolean,
|
26
|
+
default: false,
|
27
|
+
desc: "Include the Relay Node interface"
|
28
28
|
|
29
29
|
def create_type_file
|
30
30
|
template "object.erb", "#{options[:directory]}/types/#{type_file_name}.rb"
|
31
31
|
end
|
32
|
+
|
33
|
+
def fields
|
34
|
+
columns = []
|
35
|
+
columns += klass.columns.map { |c| generate_column_string(c) } if class_exists?
|
36
|
+
columns + custom_fields
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.normalize_type_expression(type_expression, mode:, null: true)
|
40
|
+
case type_expression
|
41
|
+
when "Text"
|
42
|
+
["String", null]
|
43
|
+
when "DateTime", "Datetime"
|
44
|
+
["GraphQL::Types::ISO8601DateTime", null]
|
45
|
+
when "Date"
|
46
|
+
["GraphQL::Types::ISO8601Date", null]
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def generate_column_string(column)
|
55
|
+
name = column.name
|
56
|
+
required = column.null ? "" : "!"
|
57
|
+
type = column_type_string(column)
|
58
|
+
"#{name}:#{required}#{type}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def column_type_string(column)
|
62
|
+
column.name == "id" ? "ID" : column.type.to_s.camelize
|
63
|
+
end
|
64
|
+
|
65
|
+
def class_exists?
|
66
|
+
klass.is_a?(Class) && klass.ancestors.include?(ActiveRecord::Base)
|
67
|
+
rescue NameError
|
68
|
+
return false
|
69
|
+
end
|
70
|
+
|
71
|
+
def klass
|
72
|
+
@klass ||= Module.const_get(type_name.camelize)
|
73
|
+
end
|
32
74
|
end
|
33
75
|
end
|
34
76
|
end
|
data/lib/graphql.rb
CHANGED
@@ -89,12 +89,15 @@ require "graphql/name_validator"
|
|
89
89
|
require "graphql/language"
|
90
90
|
require "graphql/analysis"
|
91
91
|
require "graphql/tracing"
|
92
|
-
require "graphql/execution"
|
93
92
|
require "graphql/dig"
|
93
|
+
require "graphql/execution"
|
94
94
|
require "graphql/schema"
|
95
95
|
require "graphql/query"
|
96
96
|
require "graphql/directive"
|
97
97
|
require "graphql/execution"
|
98
|
+
require "graphql/runtime_type_error"
|
99
|
+
require "graphql/unresolved_type_error"
|
100
|
+
require "graphql/invalid_null_error"
|
98
101
|
require "graphql/types"
|
99
102
|
require "graphql/relay"
|
100
103
|
require "graphql/boolean_type"
|
@@ -109,10 +112,7 @@ require "graphql/introspection"
|
|
109
112
|
|
110
113
|
require "graphql/analysis_error"
|
111
114
|
require "graphql/coercion_error"
|
112
|
-
require "graphql/runtime_type_error"
|
113
|
-
require "graphql/invalid_null_error"
|
114
115
|
require "graphql/invalid_name_error"
|
115
|
-
require "graphql/unresolved_type_error"
|
116
116
|
require "graphql/integer_encoding_error"
|
117
117
|
require "graphql/string_encoding_error"
|
118
118
|
require "graphql/internal_representation"
|
@@ -77,7 +77,7 @@ module GraphQL
|
|
77
77
|
end
|
78
78
|
|
79
79
|
def call_after_hooks(instrumenters, object, after_hook_name, ex)
|
80
|
-
instrumenters.
|
80
|
+
instrumenters.reverse_each do |instrumenter|
|
81
81
|
begin
|
82
82
|
instrumenter.public_send(after_hook_name, object)
|
83
83
|
rescue => e
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
8
8
|
# @see GraphQL::Query#arguments_for to get access to these objects.
|
9
9
|
class Arguments
|
10
10
|
extend Forwardable
|
11
|
+
include GraphQL::Dig
|
11
12
|
|
12
13
|
# The Ruby-style arguments hash, ready for a resolver.
|
13
14
|
# This hash is the one used at runtime.
|
@@ -20,15 +21,11 @@ module GraphQL
|
|
20
21
|
@argument_values = argument_values
|
21
22
|
end
|
22
23
|
|
23
|
-
# Yields `ArgumentValue` instances which contain detailed metadata about each argument.
|
24
|
-
def each_value
|
25
|
-
argument_values.each { |arg_v| yield(arg_v) }
|
26
|
-
end
|
27
|
-
|
28
24
|
# @return [Hash{Symbol => ArgumentValue}]
|
29
25
|
attr_reader :argument_values
|
30
26
|
|
31
27
|
def_delegators :@keyword_arguments, :key?, :[], :keys, :each, :values
|
28
|
+
def_delegators :@argument_values, :each_value
|
32
29
|
|
33
30
|
def inspect
|
34
31
|
"#<#{self.class} @keyword_arguments=#{keyword_arguments.inspect}>"
|
@@ -182,8 +182,6 @@ module GraphQL
|
|
182
182
|
|
183
183
|
kwarg_arguments = resolved_arguments.keyword_arguments
|
184
184
|
|
185
|
-
# It might turn out that making arguments for every field is slow.
|
186
|
-
# If we have to cache them, we'll need a more subtle approach here.
|
187
185
|
field_defn.extras.each do |extra|
|
188
186
|
case extra
|
189
187
|
when :ast_node
|
@@ -256,7 +254,7 @@ module GraphQL
|
|
256
254
|
def continue_value(path, value, field, is_non_null, ast_node)
|
257
255
|
if value.nil?
|
258
256
|
if is_non_null
|
259
|
-
err =
|
257
|
+
err = field.owner::InvalidNullError.new(field.owner, field, value)
|
260
258
|
write_invalid_null_in_response(path, err)
|
261
259
|
else
|
262
260
|
write_in_response(path, nil)
|
@@ -306,18 +304,21 @@ module GraphQL
|
|
306
304
|
write_in_response(path, r)
|
307
305
|
r
|
308
306
|
when "UNION", "INTERFACE"
|
309
|
-
resolved_type_or_lazy = resolve_type(type, value, path)
|
307
|
+
resolved_type_or_lazy, resolved_value = resolve_type(type, value, path)
|
308
|
+
resolved_value ||= value
|
309
|
+
|
310
310
|
after_lazy(resolved_type_or_lazy, owner: type, path: path, scoped_context: context.scoped_context, field: field, owner_object: owner_object, arguments: arguments, trace: false) do |resolved_type|
|
311
311
|
possible_types = query.possible_types(type)
|
312
312
|
|
313
313
|
if !possible_types.include?(resolved_type)
|
314
314
|
parent_type = field.owner
|
315
|
-
|
315
|
+
err_class = type::UnresolvedTypeError
|
316
|
+
type_error = err_class.new(resolved_value, field, parent_type, resolved_type, possible_types)
|
316
317
|
schema.type_error(type_error, context)
|
317
318
|
write_in_response(path, nil)
|
318
319
|
nil
|
319
320
|
else
|
320
|
-
continue_field(path,
|
321
|
+
continue_field(path, resolved_value, field, resolved_type, ast_node, next_selections, is_non_null, owner_object, arguments)
|
321
322
|
end
|
322
323
|
end
|
323
324
|
when "OBJECT"
|
@@ -459,7 +460,13 @@ module GraphQL
|
|
459
460
|
end
|
460
461
|
|
461
462
|
def arguments(graphql_object, arg_owner, ast_node)
|
462
|
-
|
463
|
+
# Don't cache arguments if field extras are requested since extras mutate the argument data structure
|
464
|
+
if arg_owner.arguments_statically_coercible? && (!arg_owner.is_a?(GraphQL::Schema::Field) || arg_owner.extras.empty?)
|
465
|
+
query.arguments_for(ast_node, arg_owner)
|
466
|
+
else
|
467
|
+
# The arguments must be prepared in the context of the given object
|
468
|
+
query.arguments_for(ast_node, arg_owner, parent_object: graphql_object)
|
469
|
+
end
|
463
470
|
end
|
464
471
|
|
465
472
|
def write_invalid_null_in_response(path, invalid_null_error)
|
@@ -499,23 +506,11 @@ module GraphQL
|
|
499
506
|
# at previous parts of the response.
|
500
507
|
# This hash matches the response
|
501
508
|
def type_at(path)
|
502
|
-
|
503
|
-
path.each do |part|
|
504
|
-
t = t[part] || (raise("Invariant: #{part.inspect} not found in #{t}"))
|
505
|
-
end
|
506
|
-
t = t[:__type]
|
507
|
-
t
|
509
|
+
@types_at_paths.fetch(path)
|
508
510
|
end
|
509
511
|
|
510
512
|
def set_type_at_path(path, type)
|
511
|
-
|
512
|
-
path.each do |part|
|
513
|
-
types = types[part] ||= {}
|
514
|
-
end
|
515
|
-
# Use this magic key so that the hash contains:
|
516
|
-
# - string keys for nested fields
|
517
|
-
# - :__type for the object type of a selection
|
518
|
-
types[:__type] ||= type
|
513
|
+
@types_at_paths[path] = type
|
519
514
|
nil
|
520
515
|
end
|
521
516
|
|
@@ -545,7 +540,7 @@ module GraphQL
|
|
545
540
|
|
546
541
|
def resolve_type(type, value, path)
|
547
542
|
trace_payload = { context: context, type: type, object: value, path: path }
|
548
|
-
resolved_type = query.trace("resolve_type", trace_payload) do
|
543
|
+
resolved_type, resolved_value = query.trace("resolve_type", trace_payload) do
|
549
544
|
query.resolve_type(type, value)
|
550
545
|
end
|
551
546
|
|
@@ -556,7 +551,7 @@ module GraphQL
|
|
556
551
|
end
|
557
552
|
end
|
558
553
|
else
|
559
|
-
resolved_type
|
554
|
+
[resolved_type, resolved_value]
|
560
555
|
end
|
561
556
|
end
|
562
557
|
|
data/lib/graphql/object_type.rb
CHANGED
@@ -88,7 +88,7 @@ module GraphQL
|
|
88
88
|
interfaces.each do |iface|
|
89
89
|
iface = BaseType.resolve_related_type(iface)
|
90
90
|
if iface.is_a?(GraphQL::InterfaceType)
|
91
|
-
type_memberships << iface.type_membership_class.new(iface, self, options)
|
91
|
+
type_memberships << iface.type_membership_class.new(iface, self, **options)
|
92
92
|
end
|
93
93
|
end
|
94
94
|
end
|
data/lib/graphql/schema.rb
CHANGED
@@ -1389,7 +1389,7 @@ module GraphQL
|
|
1389
1389
|
# rubocop:disable Lint/DuplicateMethods
|
1390
1390
|
module ResolveTypeWithType
|
1391
1391
|
def resolve_type(type, obj, ctx)
|
1392
|
-
first_resolved_type = if type.is_a?(Module) && type.respond_to?(:resolve_type)
|
1392
|
+
first_resolved_type, resolved_value = if type.is_a?(Module) && type.respond_to?(:resolve_type)
|
1393
1393
|
type.resolve_type(obj, ctx)
|
1394
1394
|
else
|
1395
1395
|
super
|
@@ -1397,7 +1397,11 @@ module GraphQL
|
|
1397
1397
|
|
1398
1398
|
after_lazy(first_resolved_type) do |resolved_type|
|
1399
1399
|
if resolved_type.nil? || (resolved_type.is_a?(Module) && resolved_type.respond_to?(:kind)) || resolved_type.is_a?(GraphQL::BaseType)
|
1400
|
-
|
1400
|
+
if resolved_value
|
1401
|
+
[resolved_type, resolved_value]
|
1402
|
+
else
|
1403
|
+
resolved_type
|
1404
|
+
end
|
1401
1405
|
else
|
1402
1406
|
raise ".resolve_type should return a type definition, but got #{resolved_type.inspect} (#{resolved_type.class}) from `resolve_type(#{type}, #{obj}, #{ctx})`"
|
1403
1407
|
end
|
@@ -1506,7 +1510,11 @@ module GraphQL
|
|
1506
1510
|
|
1507
1511
|
# @return [GraphQL::Execution::Errors, Class<GraphQL::Execution::Errors::NullErrorHandler>]
|
1508
1512
|
def error_handler
|
1509
|
-
@error_handler
|
1513
|
+
if defined?(@error_handler)
|
1514
|
+
@error_handler
|
1515
|
+
else
|
1516
|
+
find_inherited_value(:error_handler, GraphQL::Execution::Errors::NullErrorHandler)
|
1517
|
+
end
|
1510
1518
|
end
|
1511
1519
|
|
1512
1520
|
def lazy_resolve(lazy_class, value_method)
|
@@ -154,6 +154,12 @@ module GraphQL
|
|
154
154
|
raise ArgumentError, "Couldn't build type for Argument #{@owner.name}.#{name}: #{err.class.name}: #{err.message}", err.backtrace
|
155
155
|
end
|
156
156
|
|
157
|
+
def statically_coercible?
|
158
|
+
return @statically_coercible if defined?(@statically_coercible)
|
159
|
+
|
160
|
+
@statically_coercible = !@prepare.is_a?(String) && !@prepare.is_a?(Symbol)
|
161
|
+
end
|
162
|
+
|
157
163
|
# Apply the {prepare} configuration to `value`, using methods from `obj`.
|
158
164
|
# Used by the runtime.
|
159
165
|
# @api private
|
data/lib/graphql/schema/enum.rb
CHANGED
@@ -23,6 +23,9 @@ module GraphQL
|
|
23
23
|
extend GraphQL::Schema::Member::AcceptsDefinition
|
24
24
|
extend GraphQL::Schema::Member::ValidatesInput
|
25
25
|
|
26
|
+
class UnresolvedValueError < GraphQL::EnumType::UnresolvedValueError
|
27
|
+
end
|
28
|
+
|
26
29
|
class << self
|
27
30
|
# Define a value for this enum
|
28
31
|
# @param graphql_name [String, Symbol] the GraphQL value for this, usually `SCREAMING_CASE`
|
@@ -94,7 +97,7 @@ module GraphQL
|
|
94
97
|
if enum_value
|
95
98
|
enum_value.graphql_name
|
96
99
|
else
|
97
|
-
raise(
|
100
|
+
raise(self::UnresolvedValueError, "Can't resolve enum #{graphql_name} for #{value.inspect}")
|
98
101
|
end
|
99
102
|
end
|
100
103
|
|
@@ -112,6 +115,11 @@ module GraphQL
|
|
112
115
|
end
|
113
116
|
end
|
114
117
|
|
118
|
+
def inherited(child_class)
|
119
|
+
child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError))
|
120
|
+
super
|
121
|
+
end
|
122
|
+
|
115
123
|
private
|
116
124
|
|
117
125
|
def own_values
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -556,34 +556,36 @@ module GraphQL
|
|
556
556
|
begin
|
557
557
|
# Unwrap the GraphQL object to get the application object.
|
558
558
|
application_object = object.object
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
extended_obj.
|
559
|
+
ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
|
560
|
+
if is_authorized
|
561
|
+
# Apply field extensions
|
562
|
+
with_extensions(object, args, ctx) do |extended_obj, extended_args|
|
563
|
+
field_receiver = if @resolver_class
|
564
|
+
resolver_obj = if extended_obj.is_a?(GraphQL::Schema::Object)
|
565
|
+
extended_obj.object
|
566
|
+
else
|
567
|
+
extended_obj
|
568
|
+
end
|
569
|
+
@resolver_class.new(object: resolver_obj, context: ctx, field: self)
|
565
570
|
else
|
566
571
|
extended_obj
|
567
572
|
end
|
568
|
-
@resolver_class.new(object: resolver_obj, context: ctx, field: self)
|
569
|
-
else
|
570
|
-
extended_obj
|
571
|
-
end
|
572
573
|
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
574
|
+
if field_receiver.respond_to?(@resolver_method)
|
575
|
+
# Call the method with kwargs, if there are any
|
576
|
+
if extended_args.any?
|
577
|
+
field_receiver.public_send(@resolver_method, **extended_args)
|
578
|
+
else
|
579
|
+
field_receiver.public_send(@resolver_method)
|
580
|
+
end
|
577
581
|
else
|
578
|
-
field_receiver
|
582
|
+
resolve_field_method(field_receiver, extended_args, ctx)
|
579
583
|
end
|
580
|
-
else
|
581
|
-
resolve_field_method(field_receiver, extended_args, ctx)
|
582
584
|
end
|
585
|
+
else
|
586
|
+
err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
|
587
|
+
ctx.schema.unauthorized_field(err)
|
583
588
|
end
|
584
|
-
else
|
585
|
-
err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
|
586
|
-
ctx.schema.unauthorized_field(err)
|
587
589
|
end
|
588
590
|
rescue GraphQL::UnauthorizedFieldError => err
|
589
591
|
err.field ||= self
|
@@ -672,6 +674,8 @@ module GraphQL
|
|
672
674
|
else
|
673
675
|
load_application_object(arg_defn, loads, value, field_ctx.query.context)
|
674
676
|
end
|
677
|
+
elsif arg_defn.type.list? && value.is_a?(Array)
|
678
|
+
field_ctx.schema.after_any_lazies(value, &:itself)
|
675
679
|
else
|
676
680
|
value
|
677
681
|
end
|
@@ -21,10 +21,8 @@ module GraphQL
|
|
21
21
|
@ruby_style_hash = @arguments.to_kwargs
|
22
22
|
end
|
23
23
|
# Apply prepares, not great to have it duplicated here.
|
24
|
-
@arguments_by_keyword = {}
|
25
24
|
maybe_lazies = []
|
26
|
-
self.class.arguments.
|
27
|
-
@arguments_by_keyword[arg_defn.keyword] = arg_defn
|
25
|
+
self.class.arguments.each_value do |arg_defn|
|
28
26
|
ruby_kwargs_key = arg_defn.keyword
|
29
27
|
|
30
28
|
if @ruby_style_hash.key?(ruby_kwargs_key)
|
@@ -168,10 +166,7 @@ module GraphQL
|
|
168
166
|
return result
|
169
167
|
end
|
170
168
|
|
171
|
-
|
172
|
-
# using these methods to make sure that the object will
|
173
|
-
# behave like a hash below, when we call `each` on it.
|
174
|
-
begin
|
169
|
+
input = begin
|
175
170
|
input.to_h
|
176
171
|
rescue
|
177
172
|
begin
|
@@ -184,21 +179,25 @@ module GraphQL
|
|
184
179
|
end
|
185
180
|
end
|
186
181
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
if visible_arguments_map[name].nil?
|
192
|
-
result.add_problem("Field is not defined on #{self.graphql_name}", [name])
|
182
|
+
# Inject missing required arguments
|
183
|
+
missing_required_inputs = self.arguments.reduce({}) do |m, (argument_name, argument)|
|
184
|
+
if !input.key?(argument_name) && argument.type.non_null? && warden.get_argument(self, argument_name)
|
185
|
+
m[argument_name] = nil
|
193
186
|
end
|
187
|
+
|
188
|
+
m
|
194
189
|
end
|
195
190
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
result.
|
191
|
+
input.merge(missing_required_inputs).each do |argument_name, value|
|
192
|
+
argument = warden.get_argument(self, argument_name)
|
193
|
+
# Items in the input that are unexpected
|
194
|
+
unless argument
|
195
|
+
result.add_problem("Field is not defined on #{self.graphql_name}", [argument_name])
|
196
|
+
next
|
201
197
|
end
|
198
|
+
# Items in the input that are expected, but have invalid values
|
199
|
+
argument_result = argument.type.validate_input(value, ctx)
|
200
|
+
result.merge_result!(argument_name, argument_result) unless argument_result.valid?
|
202
201
|
end
|
203
202
|
|
204
203
|
result
|
@@ -14,6 +14,7 @@ module GraphQL
|
|
14
14
|
include GraphQL::Schema::Member::RelayShortcuts
|
15
15
|
include GraphQL::Schema::Member::Scoped
|
16
16
|
include GraphQL::Schema::Member::HasAstNode
|
17
|
+
include GraphQL::Schema::Member::HasUnresolvedTypeError
|
17
18
|
|
18
19
|
# Methods defined in this block will be:
|
19
20
|
# - Added as class methods to this interface
|
@@ -74,6 +75,10 @@ module GraphQL
|
|
74
75
|
if overridden_graphql_name
|
75
76
|
child_class.graphql_name(overridden_graphql_name)
|
76
77
|
end
|
78
|
+
# If interfaces are mixed into each other, only define this class once
|
79
|
+
if !child_class.const_defined?(:UnresolvedTypeError, false)
|
80
|
+
add_unresolved_type_error(child_class)
|
81
|
+
end
|
77
82
|
elsif child_class < GraphQL::Schema::Object
|
78
83
|
# This is being included into an object type, make sure it's using `implements(...)`
|
79
84
|
backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
|
data/lib/graphql/schema/list.rb
CHANGED
@@ -44,7 +44,8 @@ module GraphQL
|
|
44
44
|
if value.nil?
|
45
45
|
nil
|
46
46
|
else
|
47
|
-
ensure_array(value).map { |item| item.nil? ? item : of_type.coerce_input(item, ctx) }
|
47
|
+
coerced = ensure_array(value).map { |item| item.nil? ? item : of_type.coerce_input(item, ctx) }
|
48
|
+
ctx.schema.after_any_lazies(coerced, &:itself)
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
@@ -157,6 +157,7 @@ module GraphQL
|
|
157
157
|
type: type_resolver.call(field_hash["type"]),
|
158
158
|
description: field_hash["description"],
|
159
159
|
null: true,
|
160
|
+
camelize: false,
|
160
161
|
) do
|
161
162
|
if field_hash["args"].any?
|
162
163
|
loader.build_arguments(self, field_hash["args"], type_resolver)
|
@@ -171,6 +172,8 @@ module GraphQL
|
|
171
172
|
type: type_resolver.call(arg["type"]),
|
172
173
|
description: arg["description"],
|
173
174
|
required: false,
|
175
|
+
method_access: false,
|
176
|
+
camelize: false,
|
174
177
|
}
|
175
178
|
|
176
179
|
if arg["defaultValue"]
|
@@ -5,6 +5,7 @@ require 'graphql/schema/member/cached_graphql_definition'
|
|
5
5
|
require 'graphql/schema/member/graphql_type_names'
|
6
6
|
require 'graphql/schema/member/has_ast_node'
|
7
7
|
require 'graphql/schema/member/has_path'
|
8
|
+
require 'graphql/schema/member/has_unresolved_type_error'
|
8
9
|
require 'graphql/schema/member/relay_shortcuts'
|
9
10
|
require 'graphql/schema/member/scoped'
|
10
11
|
require 'graphql/schema/member/type_system_helpers'
|
@@ -58,6 +58,20 @@ module GraphQL
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
# @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
|
62
|
+
def get_argument(argument_name)
|
63
|
+
if (a = own_arguments[argument_name])
|
64
|
+
a
|
65
|
+
else
|
66
|
+
for ancestor in ancestors
|
67
|
+
if ancestor.respond_to?(:own_arguments) && a = ancestor.own_arguments[argument_name]
|
68
|
+
return a
|
69
|
+
end
|
70
|
+
end
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
61
75
|
# @param new_arg_class [Class] A class to use for building argument definitions
|
62
76
|
def argument_class(new_arg_class = nil)
|
63
77
|
self.class.argument_class(new_arg_class)
|
@@ -135,6 +149,12 @@ module GraphQL
|
|
135
149
|
end
|
136
150
|
end
|
137
151
|
|
152
|
+
def arguments_statically_coercible?
|
153
|
+
return @arguments_statically_coercible if defined?(@arguments_statically_coercible)
|
154
|
+
|
155
|
+
@arguments_statically_coercible = arguments.each_value.all?(&:statically_coercible?)
|
156
|
+
end
|
157
|
+
|
138
158
|
module ArgumentClassAccessor
|
139
159
|
def argument_class(new_arg_class = nil)
|
140
160
|
if new_arg_class
|
@@ -47,7 +47,7 @@ module GraphQL
|
|
47
47
|
# A list of GraphQL-Ruby keywords.
|
48
48
|
#
|
49
49
|
# @api private
|
50
|
-
GRAPHQL_RUBY_KEYWORDS = [:context, :object, :method]
|
50
|
+
GRAPHQL_RUBY_KEYWORDS = [:context, :object, :method, :raw_value]
|
51
51
|
|
52
52
|
# A list of field names that we should advise users to pick a different
|
53
53
|
# resolve method name.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Member
|
6
|
+
# Set up a type-specific error to make debugging & bug tracker integration better
|
7
|
+
module HasUnresolvedTypeError
|
8
|
+
private
|
9
|
+
def add_unresolved_type_error(child_class)
|
10
|
+
child_class.const_set(:UnresolvedTypeError, Class.new(GraphQL::UnresolvedTypeError))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -71,6 +71,13 @@ module GraphQL
|
|
71
71
|
end
|
72
72
|
|
73
73
|
class << self
|
74
|
+
# Set up a type-specific invalid null error to use when this object's non-null fields wrongly return `nil`.
|
75
|
+
# It should help with debugging and bug tracker integrations.
|
76
|
+
def inherited(child_class)
|
77
|
+
child_class.const_set(:InvalidNullError, Class.new(GraphQL::InvalidNullError))
|
78
|
+
super
|
79
|
+
end
|
80
|
+
|
74
81
|
def implements(*new_interfaces, **options)
|
75
82
|
new_memberships = []
|
76
83
|
new_interfaces.each do |int|
|
data/lib/graphql/schema/union.rb
CHANGED
@@ -3,8 +3,14 @@ module GraphQL
|
|
3
3
|
class Schema
|
4
4
|
class Union < GraphQL::Schema::Member
|
5
5
|
extend GraphQL::Schema::Member::AcceptsDefinition
|
6
|
+
extend GraphQL::Schema::Member::HasUnresolvedTypeError
|
6
7
|
|
7
8
|
class << self
|
9
|
+
def inherited(child_class)
|
10
|
+
add_unresolved_type_error(child_class)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
8
14
|
def possible_types(*types, context: GraphQL::Query::NullContext, **options)
|
9
15
|
if types.any?
|
10
16
|
types.each do |t|
|
@@ -106,6 +106,12 @@ module GraphQL
|
|
106
106
|
@visible_parent_fields[parent_type][field_name]
|
107
107
|
end
|
108
108
|
|
109
|
+
# @return [GraphQL::Argument, nil] The argument named `argument_name` on `parent_type`, if it exists and is visible
|
110
|
+
def get_argument(parent_type, argument_name)
|
111
|
+
argument = parent_type.get_argument(argument_name)
|
112
|
+
return argument if argument && visible_argument?(argument)
|
113
|
+
end
|
114
|
+
|
109
115
|
# @return [Array<GraphQL::BaseType>] The types which may be member of `type_defn`
|
110
116
|
def possible_types(type_defn)
|
111
117
|
@visible_possible_types ||= read_through { |type_defn|
|
@@ -166,7 +172,13 @@ module GraphQL
|
|
166
172
|
end
|
167
173
|
|
168
174
|
def visible_field?(owner_type, field_defn)
|
169
|
-
visible
|
175
|
+
# This field is visible in its own right
|
176
|
+
visible?(field_defn) &&
|
177
|
+
# This field's return type is visible
|
178
|
+
visible_type?(field_defn.type.unwrap) &&
|
179
|
+
# This field is either defined on this object type,
|
180
|
+
# or the interface it's inherited from is also visible
|
181
|
+
((field_defn.respond_to?(:owner) && field_defn.owner == owner_type) || field_on_visible_interface?(field_defn, owner_type))
|
170
182
|
end
|
171
183
|
|
172
184
|
# We need this to tell whether a field was inherited by an interface
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
8
8
|
#
|
9
9
|
# - No queueing system; ActiveJob should be added
|
10
10
|
# - Take care to reload context when re-delivering the subscription. (see {Query#subscription_update?})
|
11
|
+
# - Avoid the async ActionCable adapter and use the redis or PostgreSQL adapters instead. Otherwise calling #trigger won't work from background jobs or the Rails console.
|
11
12
|
#
|
12
13
|
# @example Adding ActionCableSubscriptions to your schema
|
13
14
|
# class MySchema < GraphQL::Schema
|
@@ -40,7 +40,7 @@ module GraphQL
|
|
40
40
|
# for the backend to register:
|
41
41
|
event = Subscriptions::Event.new(
|
42
42
|
name: field.name,
|
43
|
-
arguments: arguments,
|
43
|
+
arguments: arguments_without_field_extras(arguments: arguments),
|
44
44
|
context: context,
|
45
45
|
field: field,
|
46
46
|
)
|
@@ -48,7 +48,7 @@ module GraphQL
|
|
48
48
|
value
|
49
49
|
elsif context.query.subscription_topic == Subscriptions::Event.serialize(
|
50
50
|
field.name,
|
51
|
-
arguments,
|
51
|
+
arguments_without_field_extras(arguments: arguments),
|
52
52
|
field,
|
53
53
|
scope: (field.subscription_scope ? context[field.subscription_scope] : nil),
|
54
54
|
)
|
@@ -60,6 +60,14 @@ module GraphQL
|
|
60
60
|
context.skip
|
61
61
|
end
|
62
62
|
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def arguments_without_field_extras(arguments:)
|
67
|
+
arguments.dup.tap do |event_args|
|
68
|
+
field.extras.each { |k| event_args.delete(k) }
|
69
|
+
end
|
70
|
+
end
|
63
71
|
end
|
64
72
|
end
|
65
73
|
end
|
@@ -26,18 +26,7 @@ module GraphQL
|
|
26
26
|
if key == "execute_query"
|
27
27
|
set_this_txn_name = data[:query].context[:set_new_relic_transaction_name]
|
28
28
|
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
|
29
|
-
|
30
|
-
# Set the transaction name based on the operation type and name
|
31
|
-
selected_op = query.selected_operation
|
32
|
-
if selected_op
|
33
|
-
op_type = selected_op.operation_type
|
34
|
-
op_name = selected_op.name || "anonymous"
|
35
|
-
else
|
36
|
-
op_type = "query"
|
37
|
-
op_name = "anonymous"
|
38
|
-
end
|
39
|
-
|
40
|
-
NewRelic::Agent.set_transaction_name("GraphQL/#{op_type}.#{op_name}")
|
29
|
+
NewRelic::Agent.set_transaction_name(transaction_name(data[:query]))
|
41
30
|
end
|
42
31
|
end
|
43
32
|
|
@@ -103,6 +103,20 @@ module GraphQL
|
|
103
103
|
end
|
104
104
|
|
105
105
|
private
|
106
|
+
|
107
|
+
# Get the transaction name based on the operation type and name
|
108
|
+
def transaction_name(query)
|
109
|
+
selected_op = query.selected_operation
|
110
|
+
if selected_op
|
111
|
+
op_type = selected_op.operation_type
|
112
|
+
op_name = selected_op.name || "anonymous"
|
113
|
+
else
|
114
|
+
op_type = "query"
|
115
|
+
op_name = "anonymous"
|
116
|
+
end
|
117
|
+
"GraphQL/#{op_type}.#{op_name}"
|
118
|
+
end
|
119
|
+
|
106
120
|
attr_reader :options
|
107
121
|
|
108
122
|
def platform_key_cache(ctx)
|
@@ -16,12 +16,23 @@ module GraphQL
|
|
16
16
|
"execute_query_lazy" => "execute.graphql",
|
17
17
|
}
|
18
18
|
|
19
|
+
# @param set_transaction_name [Boolean] If true, the GraphQL operation name will be used as the transaction name.
|
20
|
+
# This is not advised if you run more than one query per HTTP request, for example, with `graphql-client` or multiplexing.
|
21
|
+
# It can also be specified per-query with `context[:set_scout_transaction_name]`.
|
19
22
|
def initialize(options = {})
|
20
23
|
self.class.include ScoutApm::Tracer
|
24
|
+
@set_transaction_name = options.fetch(:set_transaction_name, false)
|
21
25
|
super(options)
|
22
26
|
end
|
23
27
|
|
24
28
|
def platform_trace(platform_key, key, data)
|
29
|
+
if key == "execute_query"
|
30
|
+
set_this_txn_name = data[:query].context[:set_scout_transaction_name]
|
31
|
+
if set_this_txn_name == true || (set_this_txn_name.nil? && @set_transaction_name)
|
32
|
+
ScoutApm::Transaction.rename(transaction_name(data[:query]))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
25
36
|
self.class.instrument("GraphQL", platform_key, INSTRUMENT_OPTS) do
|
26
37
|
yield
|
27
38
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
module Tracing
|
5
|
+
class StatsdTracing < PlatformTracing
|
6
|
+
self.platform_keys = {
|
7
|
+
'lex' => "graphql.lex",
|
8
|
+
'parse' => "graphql.parse",
|
9
|
+
'validate' => "graphql.validate",
|
10
|
+
'analyze_query' => "graphql.analyze_query",
|
11
|
+
'analyze_multiplex' => "graphql.analyze_multiplex",
|
12
|
+
'execute_multiplex' => "graphql.execute_multiplex",
|
13
|
+
'execute_query' => "graphql.execute_query",
|
14
|
+
'execute_query_lazy' => "graphql.execute_query",
|
15
|
+
}
|
16
|
+
|
17
|
+
# @param statsd [Object] A statsd client
|
18
|
+
def initialize(statsd:, **rest)
|
19
|
+
@statsd = statsd
|
20
|
+
super(**rest)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def platform_trace(platform_key, key, data)
|
25
|
+
@statsd.time(platform_key) do
|
26
|
+
yield
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def platform_field_key(type, field)
|
31
|
+
"graphql.#{type.graphql_name}.#{field.graphql_name}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def platform_authorized_key(type)
|
35
|
+
"graphql.authorized.#{type.graphql_name}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def platform_resolve_type_key(type)
|
39
|
+
"graphql.resolve_type.#{type.graphql_name}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -15,7 +15,7 @@ module GraphQL
|
|
15
15
|
class ISO8601Date < GraphQL::Schema::Scalar
|
16
16
|
description "An ISO 8601-encoded date"
|
17
17
|
|
18
|
-
# @param value [Date,DateTime,String]
|
18
|
+
# @param value [Date,Time,DateTime,String]
|
19
19
|
# @return [String]
|
20
20
|
def self.coerce_result(value, _ctx)
|
21
21
|
Date.parse(value.to_s).iso8601
|
@@ -25,7 +25,7 @@ module GraphQL
|
|
25
25
|
# @return [Date]
|
26
26
|
def self.coerce_input(str_value, _ctx)
|
27
27
|
Date.iso8601(str_value)
|
28
|
-
rescue ArgumentError
|
28
|
+
rescue ArgumentError, TypeError
|
29
29
|
# Invalid input
|
30
30
|
nil
|
31
31
|
end
|
@@ -1,7 +1,9 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
1
3
|
# frozen_string_literal: true
|
2
4
|
module GraphQL
|
3
5
|
module Types
|
4
|
-
# This scalar takes `
|
6
|
+
# This scalar takes `Time`s and transmits them as strings,
|
5
7
|
# using ISO 8601 format.
|
6
8
|
#
|
7
9
|
# Use it for fields or arguments as follows:
|
@@ -29,31 +31,33 @@ module GraphQL
|
|
29
31
|
@time_precision = value
|
30
32
|
end
|
31
33
|
|
32
|
-
# @param value [Date,DateTime,String]
|
34
|
+
# @param value [Time,Date,DateTime,String]
|
33
35
|
# @return [String]
|
34
|
-
def self.coerce_result(value, _ctx)
|
36
|
+
def self.coerce_result(value, _ctx)
|
35
37
|
case value
|
36
|
-
when DateTime
|
37
|
-
return value.iso8601(time_precision)
|
38
38
|
when Date
|
39
|
-
return
|
39
|
+
return value.to_time.iso8601(time_precision)
|
40
40
|
when ::String
|
41
|
-
return
|
41
|
+
return Time.parse(value).iso8601(time_precision)
|
42
42
|
else
|
43
|
-
#
|
43
|
+
# Time, DateTime or compatible is given:
|
44
44
|
return value.iso8601(time_precision)
|
45
|
-
end
|
45
|
+
end
|
46
46
|
rescue StandardError => error
|
47
|
-
raise GraphQL::Error, "An incompatible object (#{value.class}) was given to #{self}. Make sure that only Dates, DateTimes, and well-formatted Strings are used with this type. (#{error.message})"
|
47
|
+
raise GraphQL::Error, "An incompatible object (#{value.class}) was given to #{self}. Make sure that only Times, Dates, DateTimes, and well-formatted Strings are used with this type. (#{error.message})"
|
48
48
|
end
|
49
49
|
|
50
50
|
# @param str_value [String]
|
51
|
-
# @return [
|
51
|
+
# @return [Time]
|
52
52
|
def self.coerce_input(str_value, _ctx)
|
53
|
-
|
54
|
-
rescue ArgumentError
|
55
|
-
|
56
|
-
|
53
|
+
Time.iso8601(str_value)
|
54
|
+
rescue ArgumentError, TypeError
|
55
|
+
begin
|
56
|
+
Date.iso8601(str_value).to_time
|
57
|
+
rescue ArgumentError, TypeError
|
58
|
+
# Invalid input
|
59
|
+
nil
|
60
|
+
end
|
57
61
|
end
|
58
62
|
end
|
59
63
|
end
|
data/lib/graphql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.10.
|
4
|
+
version: 1.10.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Mosolgo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark-ips
|
@@ -582,6 +582,7 @@ files:
|
|
582
582
|
- lib/graphql/schema/member/has_ast_node.rb
|
583
583
|
- lib/graphql/schema/member/has_fields.rb
|
584
584
|
- lib/graphql/schema/member/has_path.rb
|
585
|
+
- lib/graphql/schema/member/has_unresolved_type_error.rb
|
585
586
|
- lib/graphql/schema/member/instrumentation.rb
|
586
587
|
- lib/graphql/schema/member/relay_shortcuts.rb
|
587
588
|
- lib/graphql/schema/member/scoped.rb
|
@@ -695,6 +696,7 @@ files:
|
|
695
696
|
- lib/graphql/tracing/prometheus_tracing/graphql_collector.rb
|
696
697
|
- lib/graphql/tracing/scout_tracing.rb
|
697
698
|
- lib/graphql/tracing/skylight_tracing.rb
|
699
|
+
- lib/graphql/tracing/statsd_tracing.rb
|
698
700
|
- lib/graphql/type_kinds.rb
|
699
701
|
- lib/graphql/types.rb
|
700
702
|
- lib/graphql/types/big_int.rb
|