graphql 2.4.13 → 2.5.11
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/analysis/query_complexity.rb +87 -7
- data/lib/graphql/backtrace/table.rb +37 -14
- data/lib/graphql/current.rb +1 -1
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +27 -0
- data/lib/graphql/dashboard/statics/dashboard.js +74 -9
- data/lib/graphql/dashboard/subscriptions.rb +96 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +23 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +49 -1
- data/lib/graphql/dashboard.rb +45 -29
- data/lib/graphql/dataloader/active_record_association_source.rb +28 -8
- data/lib/graphql/dataloader/active_record_source.rb +26 -5
- data/lib/graphql/dataloader/null_dataloader.rb +7 -0
- data/lib/graphql/dataloader/source.rb +16 -4
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/execution/interpreter/resolve.rb +3 -3
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +34 -1
- data/lib/graphql/execution/interpreter/runtime.rb +163 -59
- data/lib/graphql/execution/interpreter.rb +5 -13
- data/lib/graphql/execution/multiplex.rb +6 -1
- data/lib/graphql/invalid_null_error.rb +15 -2
- data/lib/graphql/language/lexer.rb +9 -2
- data/lib/graphql/language/nodes.rb +5 -1
- data/lib/graphql/language/parser.rb +14 -6
- data/lib/graphql/query/context.rb +3 -8
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query.rb +59 -55
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +9 -3
- data/lib/graphql/schema/build_from_definition.rb +96 -47
- data/lib/graphql/schema/directive/flagged.rb +2 -0
- data/lib/graphql/schema/directive.rb +33 -1
- data/lib/graphql/schema/field.rb +23 -1
- data/lib/graphql/schema/input_object.rb +38 -30
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/member/has_dataloader.rb +4 -2
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_interfaces.rb +2 -2
- data/lib/graphql/schema/member/type_system_helpers.rb +16 -2
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/resolver.rb +1 -0
- data/lib/graphql/schema/scalar.rb +1 -6
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/validator/required_validator.rb +15 -6
- data/lib/graphql/schema/visibility/migration.rb +2 -2
- data/lib/graphql/schema/visibility/profile.rb +107 -21
- data/lib/graphql/schema/visibility.rb +41 -29
- data/lib/graphql/schema/warden.rb +13 -5
- data/lib/graphql/schema.rb +228 -32
- data/lib/graphql/static_validation/all_rules.rb +2 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +78 -16
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
- data/lib/graphql/testing/helpers.rb +5 -2
- data/lib/graphql/tracing/active_support_notifications_trace.rb +7 -0
- data/lib/graphql/tracing/appoptics_tracing.rb +5 -0
- data/lib/graphql/tracing/appsignal_trace.rb +26 -61
- data/lib/graphql/tracing/data_dog_trace.rb +41 -164
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +34 -164
- data/lib/graphql/tracing/notifications_trace.rb +183 -37
- data/lib/graphql/tracing/null_trace.rb +1 -1
- data/lib/graphql/tracing/perfetto_trace.rb +16 -19
- data/lib/graphql/tracing/prometheus_trace.rb +47 -74
- data/lib/graphql/tracing/scout_trace.rb +25 -59
- data/lib/graphql/tracing/sentry_trace.rb +56 -99
- data/lib/graphql/tracing/statsd_trace.rb +24 -47
- data/lib/graphql/tracing/trace.rb +0 -17
- data/lib/graphql/tracing.rb +1 -0
- data/lib/graphql/type_kinds.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +1 -1
- metadata +35 -26
- data/lib/graphql/dashboard/views/graphql/dashboard/traces/index.html.erb +0 -63
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
module RactorShareable
|
5
|
+
def self.extended(schema_class)
|
6
|
+
schema_class.extend(SchemaExtension)
|
7
|
+
schema_class.freeze_schema
|
8
|
+
end
|
9
|
+
|
10
|
+
module SchemaExtension
|
11
|
+
|
12
|
+
def freeze_error_handlers(handlers)
|
13
|
+
handlers[:subclass_handlers].default_proc = nil
|
14
|
+
handlers[:subclass_handlers].each do |_class, subclass_handlers|
|
15
|
+
freeze_error_handlers(subclass_handlers)
|
16
|
+
end
|
17
|
+
Ractor.make_shareable(handlers)
|
18
|
+
end
|
19
|
+
|
20
|
+
def freeze_schema
|
21
|
+
# warm some ivars:
|
22
|
+
default_analysis_engine
|
23
|
+
default_execution_strategy
|
24
|
+
GraphQL.default_parser
|
25
|
+
default_logger
|
26
|
+
freeze_error_handlers(error_handlers)
|
27
|
+
# TODO: this freezes errors of parent classes which could cause trouble
|
28
|
+
parent_class = superclass
|
29
|
+
while parent_class.respond_to?(:error_handlers)
|
30
|
+
freeze_error_handlers(parent_class.error_handlers)
|
31
|
+
parent_class = parent_class.superclass
|
32
|
+
end
|
33
|
+
|
34
|
+
own_tracers.freeze
|
35
|
+
@frozen_tracers = tracers.freeze
|
36
|
+
own_trace_modes.each do |m|
|
37
|
+
trace_options_for(m)
|
38
|
+
build_trace_mode(m)
|
39
|
+
end
|
40
|
+
build_trace_mode(:default)
|
41
|
+
Ractor.make_shareable(@trace_options_for_mode)
|
42
|
+
Ractor.make_shareable(own_trace_modes)
|
43
|
+
Ractor.make_shareable(own_multiplex_analyzers)
|
44
|
+
@frozen_multiplex_analyzers = Ractor.make_shareable(multiplex_analyzers)
|
45
|
+
Ractor.make_shareable(own_query_analyzers)
|
46
|
+
@frozen_query_analyzers = Ractor.make_shareable(query_analyzers)
|
47
|
+
Ractor.make_shareable(own_plugins)
|
48
|
+
own_plugins.each do |(plugin, options)|
|
49
|
+
Ractor.make_shareable(plugin)
|
50
|
+
Ractor.make_shareable(options)
|
51
|
+
end
|
52
|
+
@frozen_plugins = Ractor.make_shareable(plugins)
|
53
|
+
Ractor.make_shareable(own_references_to)
|
54
|
+
@frozen_directives = Ractor.make_shareable(directives)
|
55
|
+
|
56
|
+
Ractor.make_shareable(visibility)
|
57
|
+
Ractor.make_shareable(introspection_system)
|
58
|
+
extend(FrozenMethods)
|
59
|
+
|
60
|
+
Ractor.make_shareable(self)
|
61
|
+
superclass.respond_to?(:freeze_schema) && superclass.freeze_schema
|
62
|
+
end
|
63
|
+
|
64
|
+
module FrozenMethods
|
65
|
+
def tracers; @frozen_tracers; end
|
66
|
+
def multiplex_analyzers; @frozen_multiplex_analyzers; end
|
67
|
+
def query_analyzers; @frozen_query_analyzers; end
|
68
|
+
def plugins; @frozen_plugins; end
|
69
|
+
def directives; @frozen_directives; end
|
70
|
+
|
71
|
+
# This actually accumulates info during execution...
|
72
|
+
# How to support it?
|
73
|
+
def lazy?(_obj); false; end
|
74
|
+
def sync_lazy(obj); obj; end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -28,6 +28,7 @@ module GraphQL
|
|
28
28
|
extend Schema::Member::HasPath
|
29
29
|
extend Schema::Member::HasDirectives
|
30
30
|
include Schema::Member::HasDataloader
|
31
|
+
extend Schema::Member::HasDeprecationReason
|
31
32
|
|
32
33
|
# @param object [Object] The application object that this field is being resolved on
|
33
34
|
# @param context [GraphQL::Query::Context]
|
@@ -50,12 +50,7 @@ module GraphQL
|
|
50
50
|
end
|
51
51
|
|
52
52
|
if coerced_result.nil?
|
53
|
-
|
54
|
-
""
|
55
|
-
else
|
56
|
-
" #{GraphQL::Language.serialize(value)}"
|
57
|
-
end
|
58
|
-
Query::InputValidationResult.from_problem("Could not coerce value#{str_value} to #{graphql_name}")
|
53
|
+
Query::InputValidationResult.from_problem("Could not coerce value #{GraphQL::Language.serialize(value)} to #{graphql_name}")
|
59
54
|
elsif coerced_result.is_a?(GraphQL::CoercionError)
|
60
55
|
Query::InputValidationResult.from_problem(coerced_result.message, message: coerced_result.message, extensions: coerced_result.extensions)
|
61
56
|
else
|
@@ -71,15 +71,23 @@ module GraphQL
|
|
71
71
|
def execute_field(query:, field:, **_rest)
|
72
72
|
timeout_state = query.context.namespace(@timeout).fetch(:state)
|
73
73
|
# If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query.
|
74
|
-
if timeout_state
|
74
|
+
if timeout_state == false
|
75
|
+
super
|
76
|
+
elsif Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
|
75
77
|
error = GraphQL::Schema::Timeout::TimeoutError.new(field)
|
76
78
|
# Only invoke the timeout callback for the first timeout
|
77
79
|
if !timeout_state[:timed_out]
|
78
80
|
timeout_state[:timed_out] = true
|
79
81
|
@timeout.handle_timeout(error, query)
|
82
|
+
timeout_state = query.context.namespace(@timeout).fetch(:state)
|
80
83
|
end
|
81
84
|
|
82
|
-
|
85
|
+
# `handle_timeout` may have set this to be `false`
|
86
|
+
if timeout_state != false
|
87
|
+
error
|
88
|
+
else
|
89
|
+
super
|
90
|
+
end
|
83
91
|
else
|
84
92
|
super
|
85
93
|
end
|
@@ -102,6 +110,15 @@ module GraphQL
|
|
102
110
|
# override to do something interesting
|
103
111
|
end
|
104
112
|
|
113
|
+
# Call this method (eg, from {#handle_timeout}) to disable timeout tracking
|
114
|
+
# for the given query.
|
115
|
+
# @param query [GraphQL::Query]
|
116
|
+
# @return [void]
|
117
|
+
def disable_timeout(query)
|
118
|
+
query.context.namespace(self)[:state] = false
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
|
105
122
|
# This error is raised when a query exceeds `max_seconds`.
|
106
123
|
# Since it's a child of {GraphQL::ExecutionError},
|
107
124
|
# its message will be added to the response's `errors` key.
|
@@ -96,26 +96,35 @@ module GraphQL
|
|
96
96
|
end
|
97
97
|
|
98
98
|
def build_message(context)
|
99
|
-
argument_definitions =
|
99
|
+
argument_definitions = context.types.arguments(@validated)
|
100
|
+
|
100
101
|
required_names = @one_of.map do |arg_keyword|
|
101
102
|
if arg_keyword.is_a?(Array)
|
102
|
-
names = arg_keyword.map { |arg|
|
103
|
+
names = arg_keyword.map { |arg| arg_keyword_to_graphql_name(argument_definitions, arg) }
|
104
|
+
names.compact! # hidden arguments are `nil`
|
103
105
|
"(" + names.join(" and ") + ")"
|
104
106
|
else
|
105
|
-
|
107
|
+
arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
|
106
108
|
end
|
107
109
|
end
|
110
|
+
required_names.compact! # remove entries for hidden arguments
|
111
|
+
|
108
112
|
|
109
|
-
|
113
|
+
case required_names.size
|
114
|
+
when 0
|
115
|
+
# The required definitions were hidden from the client.
|
116
|
+
# Another option here would be to raise an error in the application....
|
117
|
+
"%{validated} is missing a required argument."
|
118
|
+
when 1
|
110
119
|
"%{validated} must include the following argument: #{required_names.first}."
|
111
120
|
else
|
112
121
|
"%{validated} must include exactly one of the following arguments: #{required_names.join(", ")}."
|
113
122
|
end
|
114
123
|
end
|
115
124
|
|
116
|
-
def
|
125
|
+
def arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
|
117
126
|
argument_definition = argument_definitions.find { |defn| defn.keyword == arg_keyword }
|
118
|
-
argument_definition
|
127
|
+
argument_definition&.graphql_name
|
119
128
|
end
|
120
129
|
end
|
121
130
|
end
|
@@ -76,10 +76,10 @@ module GraphQL
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
|
79
|
-
def initialize(context:, schema:, name: nil)
|
79
|
+
def initialize(context:, schema:, name: nil, visibility:)
|
80
80
|
@name = name
|
81
81
|
@skip_error = context[:skip_visibility_migration_error] || context.is_a?(Query::NullContext) || context.is_a?(Hash)
|
82
|
-
@profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema)
|
82
|
+
@profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema, visibility: visibility)
|
83
83
|
if !@skip_error
|
84
84
|
context[:visibility_migration_running] = true
|
85
85
|
warden_ctx_vals = context.to_h.dup
|
@@ -31,10 +31,37 @@ module GraphQL
|
|
31
31
|
# @return [Symbol, nil]
|
32
32
|
attr_reader :name
|
33
33
|
|
34
|
-
def
|
34
|
+
def freeze
|
35
|
+
@cached_visible.default_proc = nil
|
36
|
+
@cached_visible_fields.default_proc = nil
|
37
|
+
@cached_visible_fields.each do |type, fields|
|
38
|
+
fields.default_proc = nil
|
39
|
+
end
|
40
|
+
@cached_visible_arguments.default_proc = nil
|
41
|
+
@cached_visible_arguments.each do |type, fields|
|
42
|
+
fields.default_proc = nil
|
43
|
+
end
|
44
|
+
@cached_parent_fields.default_proc = nil
|
45
|
+
@cached_parent_fields.each do |type, fields|
|
46
|
+
fields.default_proc = nil
|
47
|
+
end
|
48
|
+
@cached_parent_arguments.default_proc = nil
|
49
|
+
@cached_parent_arguments.each do |type, args|
|
50
|
+
args.default_proc = nil
|
51
|
+
end
|
52
|
+
@cached_possible_types.default_proc = nil
|
53
|
+
@cached_enum_values.default_proc = nil
|
54
|
+
@cached_fields.default_proc = nil
|
55
|
+
@cached_arguments.default_proc = nil
|
56
|
+
@loadable_possible_types.default_proc = nil
|
57
|
+
super
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize(name: nil, context:, schema:, visibility:)
|
35
61
|
@name = name
|
36
62
|
@context = context
|
37
63
|
@schema = schema
|
64
|
+
@visibility = visibility
|
38
65
|
@all_types = {}
|
39
66
|
@all_types_loaded = false
|
40
67
|
@unvisited_types = []
|
@@ -47,12 +74,21 @@ module GraphQL
|
|
47
74
|
end.compare_by_identity
|
48
75
|
}.compare_by_identity
|
49
76
|
|
50
|
-
@cached_visible_arguments = Hash.new do |h,
|
51
|
-
h[
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
77
|
+
@cached_visible_arguments = Hash.new do |h, owner|
|
78
|
+
h[owner] = Hash.new do |h2, arg|
|
79
|
+
h2[arg] = if @cached_visible[arg] && (arg_type = arg.type.unwrap) && @cached_visible[arg_type]
|
80
|
+
case owner
|
81
|
+
when GraphQL::Schema::Field
|
82
|
+
@cached_visible_fields[owner.owner][owner]
|
83
|
+
when Class
|
84
|
+
@cached_visible[owner]
|
85
|
+
else
|
86
|
+
raise "Unexpected argument owner for `#{arg.path}`: #{owner.inspect}"
|
87
|
+
end
|
88
|
+
else
|
89
|
+
false
|
90
|
+
end
|
91
|
+
end.compare_by_identity
|
56
92
|
end.compare_by_identity
|
57
93
|
|
58
94
|
@cached_parent_fields = Hash.new do |h, type|
|
@@ -82,7 +118,7 @@ module GraphQL
|
|
82
118
|
end.compare_by_identity
|
83
119
|
|
84
120
|
@cached_arguments = Hash.new do |h, owner|
|
85
|
-
h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments)
|
121
|
+
h[owner] = non_duplicate_items(owner.all_argument_definitions, @cached_visible_arguments[owner])
|
86
122
|
end.compare_by_identity
|
87
123
|
|
88
124
|
@loadable_possible_types = Hash.new { |h, union_type| h[union_type] = union_type.possible_types }.compare_by_identity
|
@@ -113,7 +149,7 @@ module GraphQL
|
|
113
149
|
end
|
114
150
|
|
115
151
|
def type(type_name)
|
116
|
-
t = @
|
152
|
+
t = @visibility.get_type(type_name) # rubocop:disable Development/ContextIsPassedCop
|
117
153
|
if t
|
118
154
|
if t.is_a?(Array)
|
119
155
|
vis_t = nil
|
@@ -180,7 +216,7 @@ module GraphQL
|
|
180
216
|
if arg.is_a?(Array)
|
181
217
|
visible_arg = nil
|
182
218
|
arg.each do |arg_defn|
|
183
|
-
if @cached_visible_arguments[arg_defn]
|
219
|
+
if @cached_visible_arguments[owner][arg_defn]
|
184
220
|
if visible_arg.nil?
|
185
221
|
visible_arg = arg_defn
|
186
222
|
else
|
@@ -190,7 +226,7 @@ module GraphQL
|
|
190
226
|
end
|
191
227
|
visible_arg
|
192
228
|
else
|
193
|
-
if arg && @cached_visible_arguments[arg]
|
229
|
+
if arg && @cached_visible_arguments[owner][arg]
|
194
230
|
arg
|
195
231
|
else
|
196
232
|
nil
|
@@ -241,14 +277,13 @@ module GraphQL
|
|
241
277
|
end
|
242
278
|
|
243
279
|
def directives
|
244
|
-
@all_directives ||= @
|
245
|
-
@cached_visible[dir] && @
|
280
|
+
@all_directives ||= @visibility.all_directives.select { |dir|
|
281
|
+
@cached_visible[dir] && @visibility.all_references[dir].any? { |ref| ref == true || (@cached_visible[ref] && referenced?(ref)) }
|
246
282
|
}
|
247
283
|
end
|
248
284
|
|
249
285
|
def loadable?(t, _ctx)
|
250
|
-
|
251
|
-
!@all_types[t.graphql_name] && @cached_visible[t]
|
286
|
+
@cached_visible[t] && !referenced?(t)
|
252
287
|
end
|
253
288
|
|
254
289
|
def loadable_possible_types(t, _ctx)
|
@@ -268,13 +303,51 @@ module GraphQL
|
|
268
303
|
@cached_visible[enum_value]
|
269
304
|
end
|
270
305
|
|
306
|
+
def preload
|
307
|
+
load_all_types
|
308
|
+
@all_types.each do |type_name, type_defn|
|
309
|
+
if type_defn.kind.fields?
|
310
|
+
fields(type_defn).each do |f|
|
311
|
+
field(type_defn, f.graphql_name)
|
312
|
+
arguments(f).each do |arg|
|
313
|
+
argument(f, arg.graphql_name)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
@schema.introspection_system.dynamic_fields.each do |f|
|
317
|
+
field(type_defn, f.graphql_name)
|
318
|
+
end
|
319
|
+
elsif type_defn.kind.input_object?
|
320
|
+
arguments(type_defn).each do |arg|
|
321
|
+
argument(type_defn, arg.graphql_name)
|
322
|
+
end
|
323
|
+
elsif type_defn.kind.enum?
|
324
|
+
enum_values(type_defn)
|
325
|
+
end
|
326
|
+
# Lots more to do here
|
327
|
+
end
|
328
|
+
@schema.introspection_system.entry_points.each do |f|
|
329
|
+
arguments(f).each do |arg|
|
330
|
+
argument(f, arg.graphql_name)
|
331
|
+
end
|
332
|
+
field(@schema.query, f.graphql_name)
|
333
|
+
end
|
334
|
+
@schema.introspection_system.dynamic_fields.each do |f|
|
335
|
+
arguments(f).each do |arg|
|
336
|
+
argument(f, arg.graphql_name)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
end
|
341
|
+
|
271
342
|
private
|
272
343
|
|
273
344
|
def non_duplicate_items(definitions, visibility_cache)
|
274
345
|
non_dups = []
|
346
|
+
names = Set.new
|
275
347
|
definitions.each do |defn|
|
276
348
|
if visibility_cache[defn]
|
277
|
-
if (
|
349
|
+
if !names.add?(defn.graphql_name)
|
350
|
+
dup_defn = non_dups.find { |d| d.graphql_name == defn.graphql_name }
|
278
351
|
raise_duplicate_definition(dup_defn, defn)
|
279
352
|
end
|
280
353
|
non_dups << defn
|
@@ -292,7 +365,7 @@ module GraphQL
|
|
292
365
|
@all_types_loaded = true
|
293
366
|
visit = Visibility::Visit.new(@schema) do |member|
|
294
367
|
if member.is_a?(Module) && member.respond_to?(:kind)
|
295
|
-
if @cached_visible[member]
|
368
|
+
if @cached_visible[member] && referenced?(member)
|
296
369
|
type_name = member.graphql_name
|
297
370
|
if (prev_t = @all_types[type_name]) && !prev_t.equal?(member)
|
298
371
|
raise_duplicate_definition(member, prev_t)
|
@@ -312,16 +385,29 @@ module GraphQL
|
|
312
385
|
end
|
313
386
|
|
314
387
|
def referenced?(type_defn)
|
315
|
-
@
|
388
|
+
@visibility.all_references[type_defn].any? do |ref|
|
389
|
+
case ref
|
390
|
+
when GraphQL::Schema::Argument
|
391
|
+
@cached_visible_arguments[ref.owner][ref]
|
392
|
+
when GraphQL::Schema::Field
|
393
|
+
@cached_visible_fields[ref.owner][ref]
|
394
|
+
when Module
|
395
|
+
@cached_visible[ref]
|
396
|
+
when true
|
397
|
+
true
|
398
|
+
end
|
399
|
+
end
|
316
400
|
end
|
317
401
|
|
318
402
|
def possible_types_for(type)
|
319
403
|
case type.kind.name
|
320
404
|
when "INTERFACE"
|
321
405
|
pts = []
|
322
|
-
@
|
323
|
-
if
|
324
|
-
|
406
|
+
@visibility.all_interface_type_memberships[type].each do |impl_type, type_memberships|
|
407
|
+
if impl_type.kind.object? && referenced?(impl_type) && @cached_visible[impl_type]
|
408
|
+
if type_memberships.any? { |itm| @cached_visible[itm] }
|
409
|
+
pts << impl_type
|
410
|
+
end
|
325
411
|
end
|
326
412
|
end
|
327
413
|
pts
|
@@ -12,15 +12,12 @@ module GraphQL
|
|
12
12
|
# @param profiles [Hash<Symbol => Hash>] A hash of `name => context` pairs for preloading visibility profiles
|
13
13
|
# @param preload [Boolean] if `true`, load the default schema profile and all named profiles immediately (defaults to `true` for `Rails.env.production?`)
|
14
14
|
# @param migration_errors [Boolean] if `true`, raise an error when `Visibility` and `Warden` return different results
|
15
|
-
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails) ? Rails.env.production? : nil), migration_errors: false)
|
15
|
+
def self.use(schema, dynamic: false, profiles: EmptyObjects::EMPTY_HASH, preload: (defined?(Rails.env) ? Rails.env.production? : nil), migration_errors: false)
|
16
16
|
profiles&.each { |name, ctx|
|
17
17
|
ctx[:visibility_profile] = name
|
18
18
|
ctx.freeze
|
19
19
|
}
|
20
20
|
schema.visibility = self.new(schema, dynamic: dynamic, preload: preload, profiles: profiles, migration_errors: migration_errors)
|
21
|
-
if preload
|
22
|
-
schema.visibility.preload
|
23
|
-
end
|
24
21
|
end
|
25
22
|
|
26
23
|
def initialize(schema, dynamic:, preload:, profiles:, migration_errors:)
|
@@ -43,6 +40,17 @@ module GraphQL
|
|
43
40
|
@types = nil
|
44
41
|
@all_references = nil
|
45
42
|
@loaded_all = false
|
43
|
+
if preload
|
44
|
+
self.preload
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def freeze
|
49
|
+
load_all
|
50
|
+
@visit = true
|
51
|
+
@interface_type_memberships.default_proc = nil
|
52
|
+
@all_references.default_proc = nil
|
53
|
+
super
|
46
54
|
end
|
47
55
|
|
48
56
|
def all_directives
|
@@ -65,6 +73,8 @@ module GraphQL
|
|
65
73
|
@types[type_name]
|
66
74
|
end
|
67
75
|
|
76
|
+
attr_accessor :types
|
77
|
+
|
68
78
|
def preload?
|
69
79
|
@preload
|
70
80
|
end
|
@@ -86,7 +96,7 @@ module GraphQL
|
|
86
96
|
ensure_all_loaded(types_to_visit)
|
87
97
|
@profiles.each do |profile_name, example_ctx|
|
88
98
|
prof = profile_for(example_ctx)
|
89
|
-
prof.
|
99
|
+
prof.preload
|
90
100
|
end
|
91
101
|
end
|
92
102
|
|
@@ -148,28 +158,27 @@ module GraphQL
|
|
148
158
|
|
149
159
|
attr_reader :cached_profiles
|
150
160
|
|
151
|
-
def profile_for(context
|
161
|
+
def profile_for(context)
|
152
162
|
if !@profiles.empty?
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
163
|
+
visibility_profile = context[:visibility_profile]
|
164
|
+
if @profiles.include?(visibility_profile)
|
165
|
+
profile_ctx = @profiles[visibility_profile]
|
166
|
+
@cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: profile_ctx, schema: @schema, visibility: self)
|
167
|
+
elsif @dynamic
|
168
|
+
if context.is_a?(Query::NullContext)
|
169
|
+
top_level_profile
|
170
|
+
else
|
171
|
+
@schema.visibility_profile_class.new(context: context, schema: @schema, visibility: self)
|
162
172
|
end
|
163
|
-
elsif
|
164
|
-
raise ArgumentError, "
|
173
|
+
elsif !context.key?(:visibility_profile)
|
174
|
+
raise ArgumentError, "#{@schema} expects a visibility profile, but `visibility_profile:` wasn't passed. Provide a `visibility_profile:` value or add `dynamic: true` to your visibility configuration."
|
165
175
|
else
|
166
|
-
|
167
|
-
@cached_profiles[visibility_profile] ||= @schema.visibility_profile_class.new(name: visibility_profile, context: profile_ctx, schema: @schema)
|
176
|
+
raise ArgumentError, "`#{visibility_profile.inspect}` isn't allowed for `visibility_profile:` (must be one of #{@profiles.keys.map(&:inspect).join(", ")}). Or, add `#{visibility_profile.inspect}` to the list of profiles in the schema definition."
|
168
177
|
end
|
169
178
|
elsif context.is_a?(Query::NullContext)
|
170
179
|
top_level_profile
|
171
180
|
else
|
172
|
-
@schema.visibility_profile_class.new(context: context, schema: @schema)
|
181
|
+
@schema.visibility_profile_class.new(context: context, schema: @schema, visibility: self)
|
173
182
|
end
|
174
183
|
end
|
175
184
|
|
@@ -182,7 +191,7 @@ module GraphQL
|
|
182
191
|
if refresh
|
183
192
|
@top_level_profile = nil
|
184
193
|
end
|
185
|
-
@top_level_profile ||= @schema.visibility_profile_class.new(context: Query::NullContext.instance, schema: @schema)
|
194
|
+
@top_level_profile ||= @schema.visibility_profile_class.new(context: Query::NullContext.instance, schema: @schema, visibility: self)
|
186
195
|
end
|
187
196
|
|
188
197
|
private
|
@@ -203,7 +212,11 @@ module GraphQL
|
|
203
212
|
def load_all(types: nil)
|
204
213
|
if @visit.nil?
|
205
214
|
# Set up the visit system
|
206
|
-
@interface_type_memberships = Hash.new { |h, interface_type|
|
215
|
+
@interface_type_memberships = Hash.new { |h, interface_type|
|
216
|
+
h[interface_type] = Hash.new { |h2, obj_type|
|
217
|
+
h2[obj_type] = []
|
218
|
+
}.compare_by_identity
|
219
|
+
}.compare_by_identity
|
207
220
|
@directives = []
|
208
221
|
@types = {} # String => Module
|
209
222
|
@all_references = Hash.new { |h, member| h[member] = Set.new.compare_by_identity }.compare_by_identity
|
@@ -228,7 +241,7 @@ module GraphQL
|
|
228
241
|
@all_references[itm.abstract_type] << member
|
229
242
|
# `itm.object_type` may not actually be `member` if this implementation
|
230
243
|
# is inherited from a superclass
|
231
|
-
@interface_type_memberships[itm.abstract_type] <<
|
244
|
+
@interface_type_memberships[itm.abstract_type][member] << itm
|
232
245
|
end
|
233
246
|
elsif member < GraphQL::Schema::Union
|
234
247
|
@unions_for_references << member
|
@@ -277,12 +290,11 @@ module GraphQL
|
|
277
290
|
|
278
291
|
# TODO: somehow don't iterate over all these,
|
279
292
|
# only the ones that may have been modified
|
280
|
-
@interface_type_memberships.each do |int_type,
|
281
|
-
|
282
|
-
if !
|
283
|
-
|
284
|
-
|
285
|
-
@all_references[impl_type] |= referers
|
293
|
+
@interface_type_memberships.each do |int_type, obj_type_memberships|
|
294
|
+
referrers = @all_references[int_type].select { |r| r.is_a?(GraphQL::Schema::Field) }
|
295
|
+
if !referrers.empty?
|
296
|
+
obj_type_memberships.each_key do |impl_type|
|
297
|
+
@all_references[impl_type] |= referrers
|
286
298
|
end
|
287
299
|
end
|
288
300
|
end
|
@@ -107,7 +107,7 @@ module GraphQL
|
|
107
107
|
def get_field(parent_type, field_name); @schema.get_field(parent_type, field_name); end
|
108
108
|
def reachable_type?(type_name); true; end
|
109
109
|
def loadable?(type, _ctx); true; end
|
110
|
-
def loadable_possible_types(
|
110
|
+
def loadable_possible_types(abstract_type, _ctx); union_type.possible_types; end
|
111
111
|
def reachable_types; @schema.types.values; end # rubocop:disable Development/ContextIsPassedCop
|
112
112
|
def possible_types(type_defn); @schema.possible_types(type_defn, Query::NullContext.instance, false); end
|
113
113
|
def interfaces(obj_type); obj_type.interfaces; end
|
@@ -232,14 +232,22 @@ module GraphQL
|
|
232
232
|
|
233
233
|
# @return [Boolean] True if this type is used for `loads:` but not in the schema otherwise and not _explicitly_ hidden.
|
234
234
|
def loadable?(type, _ctx)
|
235
|
-
|
235
|
+
visible_type?(type) &&
|
236
|
+
!referenced?(type) &&
|
237
|
+
(type.respond_to?(:interfaces) ? interfaces(type).all? { |i| loadable?(i, _ctx) } : true)
|
236
238
|
end
|
237
239
|
|
238
|
-
|
240
|
+
# This abstract type was determined to be used for `loads` only.
|
241
|
+
# All its possible types are valid possibilities here -- no filtering.
|
242
|
+
def loadable_possible_types(abstract_type, _ctx)
|
239
243
|
@loadable_possible_types ||= read_through do |t|
|
240
|
-
t.
|
244
|
+
if t.is_a?(Class) # union
|
245
|
+
t.possible_types
|
246
|
+
else
|
247
|
+
@schema.possible_types(abstract_type)
|
248
|
+
end
|
241
249
|
end
|
242
|
-
@loadable_possible_types[
|
250
|
+
@loadable_possible_types[abstract_type]
|
243
251
|
end
|
244
252
|
|
245
253
|
# @return [GraphQL::BaseType, nil] The type named `type_name`, if it exists (else `nil`)
|