graphql 1.10.8 → 1.10.13
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/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
|