graphql 2.4.8 → 2.4.10
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/graphql/backtrace/table.rb +95 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +5 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +64 -0
- data/lib/graphql/dataloader/active_record_source.rb +26 -0
- data/lib/graphql/dataloader/async_dataloader.rb +17 -5
- data/lib/graphql/dataloader/null_dataloader.rb +1 -1
- data/lib/graphql/dataloader/source.rb +2 -2
- data/lib/graphql/dataloader.rb +37 -5
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +11 -4
- data/lib/graphql/execution/interpreter/runtime.rb +59 -32
- data/lib/graphql/execution/interpreter.rb +9 -1
- data/lib/graphql/execution/multiplex.rb +0 -4
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/language/parser.rb +1 -1
- data/lib/graphql/query.rb +8 -12
- data/lib/graphql/schema/build_from_definition.rb +1 -0
- data/lib/graphql/schema/enum.rb +21 -1
- data/lib/graphql/schema/interface.rb +1 -0
- data/lib/graphql/schema/loader.rb +1 -0
- data/lib/graphql/schema/member/has_dataloader.rb +56 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/object.rb +17 -8
- data/lib/graphql/schema/resolver.rb +2 -5
- data/lib/graphql/schema/validator/required_validator.rb +23 -6
- data/lib/graphql/schema/visibility/profile.rb +5 -5
- data/lib/graphql/schema/visibility.rb +14 -9
- data/lib/graphql/schema.rb +9 -25
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -3
- data/lib/graphql/tracing/appoptics_trace.rb +1 -1
- data/lib/graphql/tracing/new_relic_trace.rb +138 -41
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +726 -0
- data/lib/graphql/tracing/trace.rb +125 -1
- data/lib/graphql/tracing.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- metadata +135 -10
- data/lib/graphql/backtrace/inspect_result.rb +0 -38
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/schema/null_mask.rb +0 -11
@@ -74,7 +74,7 @@ module GraphQL
|
|
74
74
|
runtime_object = root_type.wrap(query.root_value, context)
|
75
75
|
runtime_object = schema.sync_lazy(runtime_object)
|
76
76
|
is_eager = root_op_type == "mutation"
|
77
|
-
@response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager)
|
77
|
+
@response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, root_operation.selections, is_eager, root_operation, nil, nil)
|
78
78
|
st = get_current_runtime_state
|
79
79
|
st.current_result = @response
|
80
80
|
|
@@ -85,7 +85,7 @@ module GraphQL
|
|
85
85
|
call_method_on_directives(:resolve, runtime_object, root_operation.directives) do # execute query level directives
|
86
86
|
each_gathered_selections(@response) do |selections, is_selection_array|
|
87
87
|
if is_selection_array
|
88
|
-
selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager)
|
88
|
+
selection_response = GraphQLResultHash.new(nil, root_type, runtime_object, nil, false, selections, is_eager, root_operation, nil, nil)
|
89
89
|
final_response = @response
|
90
90
|
else
|
91
91
|
selection_response = @response
|
@@ -218,8 +218,10 @@ module GraphQL
|
|
218
218
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
219
219
|
)
|
220
220
|
finished_jobs += 1
|
221
|
-
if
|
222
|
-
|
221
|
+
if finished_jobs == enqueued_jobs
|
222
|
+
if target_result
|
223
|
+
selections_result.merge_into(target_result)
|
224
|
+
end
|
223
225
|
end
|
224
226
|
@dataloader.clear_cache
|
225
227
|
}
|
@@ -229,8 +231,10 @@ module GraphQL
|
|
229
231
|
result_name, field_ast_nodes_or_ast_node, selections_result
|
230
232
|
)
|
231
233
|
finished_jobs += 1
|
232
|
-
if
|
233
|
-
|
234
|
+
if finished_jobs == enqueued_jobs
|
235
|
+
if target_result
|
236
|
+
selections_result.merge_into(target_result)
|
237
|
+
end
|
234
238
|
end
|
235
239
|
}
|
236
240
|
end
|
@@ -371,6 +375,7 @@ module GraphQL
|
|
371
375
|
end
|
372
376
|
# Actually call the field resolver and capture the result
|
373
377
|
app_result = begin
|
378
|
+
@current_trace.begin_execute_field(field_defn, object, kwarg_arguments, query)
|
374
379
|
@current_trace.execute_field(field: field_defn, ast_node: ast_node, query: query, object: object, arguments: kwarg_arguments) do
|
375
380
|
field_defn.resolve(object, kwarg_arguments, context)
|
376
381
|
end
|
@@ -383,6 +388,7 @@ module GraphQL
|
|
383
388
|
ex_err
|
384
389
|
end
|
385
390
|
end
|
391
|
+
@current_trace.end_execute_field(field_defn, object, kwarg_arguments, query, app_result)
|
386
392
|
after_lazy(app_result, field: field_defn, ast_node: ast_node, owner_object: object, arguments: resolved_arguments, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_result, runtime_state|
|
387
393
|
owner_type = selection_result.graphql_result_type
|
388
394
|
return_type = field_defn.type
|
@@ -391,6 +397,8 @@ module GraphQL
|
|
391
397
|
was_scoped = runtime_state.was_authorized_by_scope_items
|
392
398
|
runtime_state.was_authorized_by_scope_items = nil
|
393
399
|
continue_field(continue_value, owner_type, field_defn, return_type, ast_node, next_selections, false, object, resolved_arguments, result_name, selection_result, was_scoped, runtime_state)
|
400
|
+
else
|
401
|
+
nil
|
394
402
|
end
|
395
403
|
end
|
396
404
|
end
|
@@ -574,7 +582,7 @@ module GraphQL
|
|
574
582
|
r = begin
|
575
583
|
current_type.coerce_result(value, context)
|
576
584
|
rescue StandardError => err
|
577
|
-
|
585
|
+
query.handle_or_reraise(err)
|
578
586
|
end
|
579
587
|
set_result(selection_result, result_name, r, false, is_non_null)
|
580
588
|
r
|
@@ -609,11 +617,11 @@ module GraphQL
|
|
609
617
|
after_lazy(object_proxy, ast_node: ast_node, field: field, owner_object: owner_object, arguments: arguments, trace: false, result_name: result_name, result: selection_result, runtime_state: runtime_state) do |inner_object, runtime_state|
|
610
618
|
continue_value = continue_value(inner_object, field, is_non_null, ast_node, result_name, selection_result)
|
611
619
|
if HALT != continue_value
|
612
|
-
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false)
|
620
|
+
response_hash = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
|
613
621
|
set_result(selection_result, result_name, response_hash, true, is_non_null)
|
614
622
|
each_gathered_selections(response_hash) do |selections, is_selection_array|
|
615
623
|
if is_selection_array
|
616
|
-
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false)
|
624
|
+
this_result = GraphQLResultHash.new(result_name, current_type, continue_value, selection_result, is_non_null, selections, false, ast_node, arguments, field)
|
617
625
|
final_result = response_hash
|
618
626
|
else
|
619
627
|
this_result = response_hash
|
@@ -634,35 +642,43 @@ module GraphQL
|
|
634
642
|
# This is true for objects, unions, and interfaces
|
635
643
|
use_dataloader_job = !inner_type.unwrap.kind.input?
|
636
644
|
inner_type_non_null = inner_type.non_null?
|
637
|
-
response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false)
|
645
|
+
response_list = GraphQLResultArray.new(result_name, current_type, owner_object, selection_result, is_non_null, next_selections, false, ast_node, arguments, field)
|
638
646
|
set_result(selection_result, result_name, response_list, true, is_non_null)
|
639
647
|
idx = nil
|
640
648
|
list_value = begin
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
649
|
+
begin
|
650
|
+
value.each do |inner_value|
|
651
|
+
idx ||= 0
|
652
|
+
this_idx = idx
|
653
|
+
idx += 1
|
654
|
+
if use_dataloader_job
|
655
|
+
@dataloader.append_job do
|
656
|
+
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
|
657
|
+
end
|
658
|
+
else
|
647
659
|
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
|
648
660
|
end
|
649
|
-
else
|
650
|
-
resolve_list_item(inner_value, inner_type, inner_type_non_null, ast_node, field, owner_object, arguments, this_idx, response_list, owner_type, was_scoped, runtime_state)
|
651
661
|
end
|
652
|
-
end
|
653
662
|
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
+
response_list
|
664
|
+
rescue NoMethodError => err
|
665
|
+
# Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
|
666
|
+
if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
|
667
|
+
# This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
|
668
|
+
raise ListResultFailedError.new(value: value, field: field, path: current_path)
|
669
|
+
else
|
670
|
+
# This was some other NoMethodError -- let it bubble to reveal the real error.
|
671
|
+
raise
|
672
|
+
end
|
673
|
+
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
674
|
+
ex_err
|
675
|
+
rescue StandardError => err
|
676
|
+
begin
|
677
|
+
query.handle_or_reraise(err)
|
678
|
+
rescue GraphQL::ExecutionError => ex_err
|
679
|
+
ex_err
|
680
|
+
end
|
663
681
|
end
|
664
|
-
rescue GraphQL::ExecutionError, GraphQL::UnauthorizedError => ex_err
|
665
|
-
ex_err
|
666
682
|
rescue StandardError => err
|
667
683
|
begin
|
668
684
|
query.handle_or_reraise(err)
|
@@ -773,8 +789,10 @@ module GraphQL
|
|
773
789
|
runtime_state.was_authorized_by_scope_items = was_authorized_by_scope_items
|
774
790
|
# Wrap the execution of _this_ method with tracing,
|
775
791
|
# but don't wrap the continuation below
|
792
|
+
result = nil
|
776
793
|
inner_obj = begin
|
777
|
-
if trace
|
794
|
+
result = if trace
|
795
|
+
@current_trace.begin_execute_field(field, owner_object, arguments, query)
|
778
796
|
@current_trace.execute_field_lazy(field: field, query: query, object: owner_object, arguments: arguments, ast_node: ast_node) do
|
779
797
|
schema.sync_lazy(lazy_obj)
|
780
798
|
end
|
@@ -789,6 +807,10 @@ module GraphQL
|
|
789
807
|
rescue GraphQL::ExecutionError => ex_err
|
790
808
|
ex_err
|
791
809
|
end
|
810
|
+
ensure
|
811
|
+
if trace
|
812
|
+
@current_trace.end_execute_field(field, owner_object, arguments, query, result)
|
813
|
+
end
|
792
814
|
end
|
793
815
|
yield(inner_obj, runtime_state)
|
794
816
|
end
|
@@ -832,14 +854,19 @@ module GraphQL
|
|
832
854
|
end
|
833
855
|
|
834
856
|
def resolve_type(type, value)
|
857
|
+
@current_trace.begin_resolve_type(type, value, context)
|
835
858
|
resolved_type, resolved_value = @current_trace.resolve_type(query: query, type: type, object: value) do
|
836
859
|
query.resolve_type(type, value)
|
837
860
|
end
|
861
|
+
@current_trace.end_resolve_type(type, value, context, resolved_type)
|
838
862
|
|
839
863
|
if lazy?(resolved_type)
|
840
864
|
GraphQL::Execution::Lazy.new do
|
865
|
+
@current_trace.begin_resolve_type(type, value, context)
|
841
866
|
@current_trace.resolve_type_lazy(query: query, type: type, object: value) do
|
842
|
-
schema.sync_lazy(resolved_type)
|
867
|
+
rt = schema.sync_lazy(resolved_type)
|
868
|
+
@current_trace.end_resolve_type(type, value, context, rt)
|
869
|
+
rt
|
843
870
|
end
|
844
871
|
end
|
845
872
|
else
|
@@ -33,9 +33,12 @@ module GraphQL
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
|
36
37
|
multiplex = Execution::Multiplex.new(schema: schema, queries: queries, context: context, max_complexity: max_complexity)
|
37
38
|
Fiber[:__graphql_current_multiplex] = multiplex
|
38
|
-
multiplex.current_trace
|
39
|
+
trace = multiplex.current_trace
|
40
|
+
trace.begin_execute_multiplex(multiplex)
|
41
|
+
trace.execute_multiplex(multiplex: multiplex) do
|
39
42
|
schema = multiplex.schema
|
40
43
|
queries = multiplex.queries
|
41
44
|
lazies_at_depth = Hash.new { |h, k| h[k] = [] }
|
@@ -44,7 +47,10 @@ module GraphQL
|
|
44
47
|
multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity]
|
45
48
|
end
|
46
49
|
|
50
|
+
trace.begin_analyze_multiplex(multiplex, multiplex_analyzers)
|
47
51
|
schema.analysis_engine.analyze_multiplex(multiplex, multiplex_analyzers)
|
52
|
+
trace.end_analyze_multiplex(multiplex, multiplex_analyzers)
|
53
|
+
|
48
54
|
begin
|
49
55
|
# Since this is basically the batching context,
|
50
56
|
# share it for a whole multiplex
|
@@ -148,6 +154,8 @@ module GraphQL
|
|
148
154
|
}
|
149
155
|
end
|
150
156
|
end
|
157
|
+
ensure
|
158
|
+
trace&.end_execute_multiplex(multiplex)
|
151
159
|
end
|
152
160
|
end
|
153
161
|
|
@@ -35,10 +35,6 @@ module GraphQL
|
|
35
35
|
@current_trace = @context[:trace] || schema.new_trace(multiplex: self)
|
36
36
|
@dataloader = @context[:dataloader] ||= @schema.dataloader_class.new
|
37
37
|
@tracers = schema.tracers + (context[:tracers] || [])
|
38
|
-
# Support `context: {backtrace: true}`
|
39
|
-
if context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
|
40
|
-
@tracers << GraphQL::Backtrace::Tracer
|
41
|
-
end
|
42
38
|
@max_complexity = max_complexity
|
43
39
|
end
|
44
40
|
end
|
@@ -7,7 +7,7 @@ module GraphQL
|
|
7
7
|
"a __DirectiveLocation describes one such possible adjacencies."
|
8
8
|
|
9
9
|
GraphQL::Schema::Directive::LOCATIONS.each do |location|
|
10
|
-
value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location)
|
10
|
+
value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location, value_method: false)
|
11
11
|
end
|
12
12
|
introspection true
|
13
13
|
end
|
@@ -161,7 +161,7 @@ module GraphQL
|
|
161
161
|
expect_token(:VAR_SIGN)
|
162
162
|
var_name = parse_name
|
163
163
|
expect_token(:COLON)
|
164
|
-
var_type = self.type
|
164
|
+
var_type = self.type || raise_parse_error("Missing type definition for variable: $#{var_name}")
|
165
165
|
default_value = if at?(:EQUALS)
|
166
166
|
advance_token
|
167
167
|
value
|
data/lib/graphql/query.rb
CHANGED
@@ -97,21 +97,22 @@ module GraphQL
|
|
97
97
|
# @param root_value [Object] the object used to resolve fields on the root type
|
98
98
|
# @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
|
99
99
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
100
|
-
# @param visibility_profile [Symbol]
|
100
|
+
# @param visibility_profile [Symbol] Another way to assign `context[:visibility_profile]`
|
101
101
|
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil)
|
102
102
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
103
103
|
variables ||= {}
|
104
104
|
@schema = schema
|
105
105
|
@context = schema.context_class.new(query: self, values: context)
|
106
|
+
if visibility_profile
|
107
|
+
@context[:visibility_profile] ||= visibility_profile
|
108
|
+
end
|
106
109
|
|
107
110
|
if use_visibility_profile.nil?
|
108
111
|
use_visibility_profile = warden ? false : schema.use_visibility_profile?
|
109
112
|
end
|
110
113
|
|
111
|
-
@visibility_profile = visibility_profile
|
112
|
-
|
113
114
|
if use_visibility_profile
|
114
|
-
@visibility_profile = @schema.visibility.profile_for(@context
|
115
|
+
@visibility_profile = @schema.visibility.profile_for(@context)
|
115
116
|
@warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema)
|
116
117
|
else
|
117
118
|
@visibility_profile = nil
|
@@ -127,14 +128,6 @@ module GraphQL
|
|
127
128
|
context_tracers = (context ? context.fetch(:tracers, []) : [])
|
128
129
|
@tracers = schema.tracers + context_tracers
|
129
130
|
|
130
|
-
# Support `ctx[:backtrace] = true` for wrapping backtraces
|
131
|
-
if context && context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
|
132
|
-
if schema.trace_class <= GraphQL::Tracing::CallLegacyTracers
|
133
|
-
context_tracers += [GraphQL::Backtrace::Tracer]
|
134
|
-
@tracers << GraphQL::Backtrace::Tracer
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
131
|
if !context_tracers.empty? && !(schema.trace_class <= GraphQL::Tracing::CallLegacyTracers)
|
139
132
|
raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
|
140
133
|
end
|
@@ -448,6 +441,7 @@ module GraphQL
|
|
448
441
|
@warden ||= @schema.warden_class.new(schema: @schema, context: @context)
|
449
442
|
parse_error = nil
|
450
443
|
@document ||= begin
|
444
|
+
current_trace.begin_parse(query_string)
|
451
445
|
if query_string
|
452
446
|
GraphQL.parse(query_string, trace: self.current_trace, max_tokens: @schema.max_query_string_tokens)
|
453
447
|
end
|
@@ -455,6 +449,8 @@ module GraphQL
|
|
455
449
|
parse_error = err
|
456
450
|
@schema.parse_error(err, @context)
|
457
451
|
nil
|
452
|
+
ensure
|
453
|
+
current_trace.end_parse(query_string)
|
458
454
|
end
|
459
455
|
|
460
456
|
@fragments = {}
|
@@ -298,6 +298,7 @@ module GraphQL
|
|
298
298
|
description: enum_value_definition.description,
|
299
299
|
directives: builder.prepare_directives(enum_value_definition, type_resolver),
|
300
300
|
ast_node: enum_value_definition,
|
301
|
+
value_method: GraphQL::Schema::Enum.respond_to?(enum_value_definition.name.downcase) ? false : nil,
|
301
302
|
)
|
302
303
|
end
|
303
304
|
end
|
data/lib/graphql/schema/enum.rb
CHANGED
@@ -61,12 +61,17 @@ module GraphQL
|
|
61
61
|
# @option kwargs [String] :description, the GraphQL description for this value, present in documentation
|
62
62
|
# @option kwargs [String] :comment, the GraphQL comment for this value, present in documentation
|
63
63
|
# @option kwargs [::Object] :value the translated Ruby value for this object (defaults to `graphql_name`)
|
64
|
+
# @option kwargs [::Object] :value_method, the method name to fetch `graphql_name` (defaults to `graphql_name.downcase`)
|
64
65
|
# @option kwargs [String] :deprecation_reason if this object is deprecated, include a message here
|
66
|
+
# @param value_method [Symbol, false] A method to generate for this value, or `false` to skip generation
|
65
67
|
# @return [void]
|
66
68
|
# @see {Schema::EnumValue} which handles these inputs by default
|
67
|
-
def value(*args, **kwargs, &block)
|
69
|
+
def value(*args, value_method: nil, **kwargs, &block)
|
68
70
|
kwargs[:owner] = self
|
69
71
|
value = enum_value_class.new(*args, **kwargs, &block)
|
72
|
+
|
73
|
+
generate_value_method(value, value_method)
|
74
|
+
|
70
75
|
key = value.graphql_name
|
71
76
|
prev_value = own_values[key]
|
72
77
|
case prev_value
|
@@ -223,6 +228,21 @@ module GraphQL
|
|
223
228
|
def own_values
|
224
229
|
@own_values ||= {}
|
225
230
|
end
|
231
|
+
|
232
|
+
def generate_value_method(value, configured_value_method)
|
233
|
+
return if configured_value_method == false
|
234
|
+
|
235
|
+
value_method_name = configured_value_method || value.graphql_name.downcase
|
236
|
+
|
237
|
+
if respond_to?(value_method_name.to_sym)
|
238
|
+
warn "Failed to define value method for :#{value_method_name}, because " \
|
239
|
+
"#{value.owner.name || value.owner.graphql_name} already responds to that method. Use `value_method:` to override the method name " \
|
240
|
+
"or `value_method: false` to disable Enum value method generation."
|
241
|
+
return
|
242
|
+
end
|
243
|
+
|
244
|
+
instance_eval("def #{value_method_name}; #{value.graphql_name.inspect}; end;", __FILE__, __LINE__)
|
245
|
+
end
|
226
246
|
end
|
227
247
|
|
228
248
|
enum_value_class(GraphQL::Schema::EnumValue)
|
@@ -13,6 +13,7 @@ module GraphQL
|
|
13
13
|
include GraphQL::Schema::Member::Scoped
|
14
14
|
include GraphQL::Schema::Member::HasAstNode
|
15
15
|
include GraphQL::Schema::Member::HasUnresolvedTypeError
|
16
|
+
include GraphQL::Schema::Member::HasDataloader
|
16
17
|
include GraphQL::Schema::Member::HasDirectives
|
17
18
|
include GraphQL::Schema::Member::HasInterfaces
|
18
19
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GraphQL
|
4
|
+
class Schema
|
5
|
+
class Member
|
6
|
+
module HasDataloader
|
7
|
+
# @return [GraphQL::Dataloader] The dataloader for the currently-running query
|
8
|
+
def dataloader
|
9
|
+
context.dataloader
|
10
|
+
end
|
11
|
+
|
12
|
+
# A shortcut method for loading a key from a source.
|
13
|
+
# Identical to `dataloader.with(source_class, *source_args).load(load_key)`
|
14
|
+
# @param source_class [Class<GraphQL::Dataloader::Source>]
|
15
|
+
# @param source_args [Array<Object>] Any extra parameters defined in `source_class`'s `initialize` method
|
16
|
+
# @param load_key [Object] The key to look up using `def fetch`
|
17
|
+
def dataload(source_class, *source_args, load_key)
|
18
|
+
dataloader.with(source_class, *source_args).load(load_key)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Find an object with ActiveRecord via {Dataloader::ActiveRecordSource}.
|
22
|
+
# @param model [Class<ActiveRecord::Base>]
|
23
|
+
# @param find_by_value [Object] Usually an `id`, might be another value if `find_by:` is also provided
|
24
|
+
# @param find_by [Symbol, String] A column name to look the record up by. (Defaults to the model's primary key.)
|
25
|
+
# @return [ActiveRecord::Base, nil]
|
26
|
+
def dataload_record(model, find_by_value, find_by: nil)
|
27
|
+
source = if find_by
|
28
|
+
dataloader.with(Dataloader::ActiveRecordSource, model, find_by: find_by)
|
29
|
+
else
|
30
|
+
dataloader.with(Dataloader::ActiveRecordSource, model)
|
31
|
+
end
|
32
|
+
|
33
|
+
source.load(find_by_value)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Look up an associated record using a Rails association.
|
37
|
+
# @param association_name [Symbol] A `belongs_to` or `has_one` association. (If a `has_many` association is named here, it will be selected without pagination.)
|
38
|
+
# @param record [ActiveRecord::Base] The object that the association belongs to.
|
39
|
+
# @param scope [ActiveRecord::Relation] A scope to look up the associated record in
|
40
|
+
# @return [ActiveRecord::Base, nil] The associated record, if there is one
|
41
|
+
# @example Looking up a belongs_to on the current object
|
42
|
+
# dataload_association(:parent) # Equivalent to `object.parent`, but dataloaded
|
43
|
+
# @example Looking up an associated record on some other object
|
44
|
+
# dataload_association(:post, comment) # Equivalent to `comment.post`, but dataloaded
|
45
|
+
def dataload_association(record = object, association_name, scope: nil)
|
46
|
+
source = if scope
|
47
|
+
dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name, scope)
|
48
|
+
else
|
49
|
+
dataloader.with(Dataloader::ActiveRecordAssociationSource, association_name)
|
50
|
+
end
|
51
|
+
source.load(record)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'graphql/schema/member/base_dsl_methods'
|
3
3
|
require 'graphql/schema/member/graphql_type_names'
|
4
4
|
require 'graphql/schema/member/has_ast_node'
|
5
|
+
require 'graphql/schema/member/has_dataloader'
|
5
6
|
require 'graphql/schema/member/has_directives'
|
6
7
|
require 'graphql/schema/member/has_deprecation_reason'
|
7
8
|
require 'graphql/schema/member/has_interfaces'
|
@@ -7,6 +7,7 @@ module GraphQL
|
|
7
7
|
class Object < GraphQL::Schema::Member
|
8
8
|
extend GraphQL::Schema::Member::HasFields
|
9
9
|
extend GraphQL::Schema::Member::HasInterfaces
|
10
|
+
include Member::HasDataloader
|
10
11
|
|
11
12
|
# Raised when an Object doesn't have any field defined and hasn't explicitly opted out of this requirement
|
12
13
|
class FieldsAreRequiredError < GraphQL::Error
|
@@ -65,20 +66,28 @@ module GraphQL
|
|
65
66
|
# @return [GraphQL::Schema::Object, GraphQL::Execution::Lazy]
|
66
67
|
# @raise [GraphQL::UnauthorizedError] if the user-provided hook returns `false`
|
67
68
|
def authorized_new(object, context)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
69
|
+
context.query.current_trace.begin_authorized(self, object, context)
|
70
|
+
begin
|
71
|
+
maybe_lazy_auth_val = context.query.current_trace.authorized(query: context.query, type: self, object: object) do
|
72
|
+
begin
|
73
|
+
authorized?(object, context)
|
74
|
+
rescue GraphQL::UnauthorizedError => err
|
75
|
+
context.schema.unauthorized_object(err)
|
76
|
+
rescue StandardError => err
|
77
|
+
context.query.handle_or_reraise(err)
|
78
|
+
end
|
75
79
|
end
|
80
|
+
ensure
|
81
|
+
context.query.current_trace.end_authorized(self, object, context, maybe_lazy_auth_val)
|
76
82
|
end
|
77
83
|
|
78
84
|
auth_val = if context.schema.lazy?(maybe_lazy_auth_val)
|
79
85
|
GraphQL::Execution::Lazy.new do
|
86
|
+
context.query.current_trace.begin_authorized(self, object, context)
|
80
87
|
context.query.current_trace.authorized_lazy(query: context.query, type: self, object: object) do
|
81
|
-
context.schema.sync_lazy(maybe_lazy_auth_val)
|
88
|
+
res = context.schema.sync_lazy(maybe_lazy_auth_val)
|
89
|
+
context.query.current_trace.end_authorized(self, object, context, res)
|
90
|
+
res
|
82
91
|
end
|
83
92
|
end
|
84
93
|
else
|
@@ -22,11 +22,13 @@ module GraphQL
|
|
22
22
|
include Schema::Member::GraphQLTypeNames
|
23
23
|
# Really we only need description & comment from here, but:
|
24
24
|
extend Schema::Member::BaseDSLMethods
|
25
|
+
extend Member::BaseDSLMethods::ConfigurationExtension
|
25
26
|
extend GraphQL::Schema::Member::HasArguments
|
26
27
|
extend GraphQL::Schema::Member::HasValidators
|
27
28
|
include Schema::Member::HasPath
|
28
29
|
extend Schema::Member::HasPath
|
29
30
|
extend Schema::Member::HasDirectives
|
31
|
+
include Schema::Member::HasDataloader
|
30
32
|
|
31
33
|
# @param object [Object] The application object that this field is being resolved on
|
32
34
|
# @param context [GraphQL::Query::Context]
|
@@ -49,11 +51,6 @@ module GraphQL
|
|
49
51
|
# @return [GraphQL::Query::Context]
|
50
52
|
attr_reader :context
|
51
53
|
|
52
|
-
# @return [GraphQL::Dataloader]
|
53
|
-
def dataloader
|
54
|
-
context.dataloader
|
55
|
-
end
|
56
|
-
|
57
54
|
# @return [GraphQL::Schema::Field]
|
58
55
|
attr_reader :field
|
59
56
|
|
@@ -51,19 +51,36 @@ module GraphQL
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def validate(_object, context, value)
|
54
|
-
|
54
|
+
fully_matched_conditions = 0
|
55
|
+
partially_matched_conditions = 0
|
55
56
|
|
56
57
|
if !value.nil?
|
57
58
|
@one_of.each do |one_of_condition|
|
58
59
|
case one_of_condition
|
59
60
|
when Symbol
|
60
61
|
if value.key?(one_of_condition)
|
61
|
-
|
62
|
+
fully_matched_conditions += 1
|
62
63
|
end
|
63
64
|
when Array
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
any_match = false
|
66
|
+
full_match = true
|
67
|
+
|
68
|
+
one_of_condition.each do |k|
|
69
|
+
if value.key?(k)
|
70
|
+
any_match = true
|
71
|
+
else
|
72
|
+
full_match = false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
partial_match = !full_match && any_match
|
77
|
+
|
78
|
+
if full_match
|
79
|
+
fully_matched_conditions += 1
|
80
|
+
end
|
81
|
+
|
82
|
+
if partial_match
|
83
|
+
partially_matched_conditions += 1
|
67
84
|
end
|
68
85
|
else
|
69
86
|
raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}"
|
@@ -71,7 +88,7 @@ module GraphQL
|
|
71
88
|
end
|
72
89
|
end
|
73
90
|
|
74
|
-
if
|
91
|
+
if fully_matched_conditions == 1 && partially_matched_conditions == 0
|
75
92
|
nil # OK
|
76
93
|
else
|
77
94
|
@message || build_message(context)
|
@@ -18,7 +18,7 @@ module GraphQL
|
|
18
18
|
if ctx.respond_to?(:types) && (types = ctx.types).is_a?(self)
|
19
19
|
types
|
20
20
|
else
|
21
|
-
schema.visibility.profile_for(ctx
|
21
|
+
schema.visibility.profile_for(ctx)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -159,7 +159,7 @@ module GraphQL
|
|
159
159
|
end
|
160
160
|
end
|
161
161
|
end
|
162
|
-
visible_f
|
162
|
+
visible_f&.ensure_loaded
|
163
163
|
elsif f && @cached_visible_fields[owner][f.ensure_loaded]
|
164
164
|
f
|
165
165
|
else
|
@@ -319,9 +319,9 @@ module GraphQL
|
|
319
319
|
case type.kind.name
|
320
320
|
when "INTERFACE"
|
321
321
|
pts = []
|
322
|
-
@schema.visibility.all_interface_type_memberships[type].each do |itm|
|
323
|
-
if @cached_visible[itm] &&
|
324
|
-
pts <<
|
322
|
+
@schema.visibility.all_interface_type_memberships[type].each do |(itm, impl_type)|
|
323
|
+
if @cached_visible[itm] && @cached_visible[impl_type] && referenced?(impl_type)
|
324
|
+
pts << impl_type
|
325
325
|
end
|
326
326
|
end
|
327
327
|
pts
|