graphql 2.2.17 → 2.5.16
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/install/mutation_root_generator.rb +2 -2
- data/lib/generators/graphql/install_generator.rb +46 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_resolver.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +3 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis/analyzer.rb +90 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +263 -0
- data/lib/graphql/analysis/{ast/query_depth.rb → query_depth.rb} +23 -25
- data/lib/graphql/analysis/visitor.rb +280 -0
- data/lib/graphql/analysis.rb +95 -1
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +57 -0
- 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/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- 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/landings/show.html.erb +18 -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 +108 -0
- data/lib/graphql/dashboard.rb +158 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
- data/lib/graphql/dataloader/active_record_source.rb +47 -0
- data/lib/graphql/dataloader/async_dataloader.rb +46 -19
- data/lib/graphql/dataloader/null_dataloader.rb +51 -10
- data/lib/graphql/dataloader/source.rb +20 -9
- data/lib/graphql/dataloader.rb +153 -45
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +23 -25
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +63 -5
- data/lib/graphql/execution/interpreter/runtime.rb +321 -222
- data/lib/graphql/execution/interpreter.rb +23 -30
- data/lib/graphql/execution/lookahead.rb +18 -11
- data/lib/graphql/execution/multiplex.rb +6 -5
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +6 -11
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +20 -17
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/document_from_schema_definition.rb +64 -35
- data/lib/graphql/language/lexer.rb +72 -42
- data/lib/graphql/language/nodes.rb +93 -52
- data/lib/graphql/language/parser.rb +168 -61
- data/lib/graphql/language/printer.rb +31 -15
- data/lib/graphql/language/sanitized_printer.rb +1 -1
- data/lib/graphql/language.rb +61 -1
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +46 -47
- data/lib/graphql/query/null_context.rb +3 -5
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query/validation_pipeline.rb +2 -2
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +123 -69
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
- data/lib/graphql/rubocop.rb +2 -0
- data/lib/graphql/schema/addition.rb +26 -13
- data/lib/graphql/schema/always_visible.rb +7 -2
- data/lib/graphql/schema/argument.rb +57 -8
- data/lib/graphql/schema/build_from_definition.rb +116 -49
- data/lib/graphql/schema/directive/flagged.rb +4 -2
- data/lib/graphql/schema/directive.rb +54 -2
- data/lib/graphql/schema/enum.rb +107 -24
- data/lib/graphql/schema/enum_value.rb +10 -2
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +134 -45
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +6 -2
- data/lib/graphql/schema/input_object.rb +122 -64
- data/lib/graphql/schema/interface.rb +23 -5
- data/lib/graphql/schema/introspection_system.rb +6 -17
- data/lib/graphql/schema/late_bound_type.rb +4 -0
- data/lib/graphql/schema/list.rb +3 -3
- data/lib/graphql/schema/loader.rb +3 -2
- data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
- data/lib/graphql/schema/member/has_arguments.rb +44 -58
- data/lib/graphql/schema/member/has_dataloader.rb +62 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +4 -4
- data/lib/graphql/schema/member/has_fields.rb +26 -6
- data/lib/graphql/schema/member/has_interfaces.rb +6 -6
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +17 -4
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +25 -8
- data/lib/graphql/schema/printer.rb +1 -0
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +29 -23
- data/lib/graphql/schema/scalar.rb +1 -6
- data/lib/graphql/schema/subscription.rb +52 -6
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator/required_validator.rb +92 -11
- data/lib/graphql/schema/validator.rb +3 -1
- data/lib/graphql/schema/visibility/migration.rb +188 -0
- data/lib/graphql/schema/visibility/profile.rb +445 -0
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +311 -0
- data/lib/graphql/schema/warden.rb +190 -20
- data/lib/graphql/schema.rb +695 -167
- data/lib/graphql/static_validation/all_rules.rb +2 -2
- data/lib/graphql/static_validation/base_visitor.rb +6 -5
- data/lib/graphql/static_validation/literal_validator.rb +4 -4
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
- 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 +7 -3
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
- data/lib/graphql/static_validation/validation_context.rb +18 -2
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +5 -3
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +13 -2
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +7 -5
- data/lib/graphql/testing/helpers.rb +48 -16
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +1 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +5 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
- data/lib/graphql/tracing/appsignal_trace.rb +32 -59
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -162
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +141 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +183 -37
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- 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 +818 -0
- data/lib/graphql/tracing/platform_tracing.rb +1 -1
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- data/lib/graphql/tracing/prometheus_trace.rb +73 -73
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -58
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +64 -98
- data/lib/graphql/tracing/statsd_trace.rb +33 -45
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +111 -1
- data/lib/graphql/tracing.rb +31 -30
- data/lib/graphql/type_kinds.rb +2 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +64 -54
- metadata +197 -22
- data/lib/graphql/analysis/ast/analyzer.rb +0 -91
- data/lib/graphql/analysis/ast/field_usage.rb +0 -82
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -182
- data/lib/graphql/analysis/ast/visitor.rb +0 -276
- data/lib/graphql/analysis/ast.rb +0 -94
- data/lib/graphql/backtrace/inspect_result.rb +0 -50
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
data/lib/graphql/schema.rb
CHANGED
|
@@ -5,10 +5,9 @@ require "graphql/schema/always_visible"
|
|
|
5
5
|
require "graphql/schema/base_64_encoder"
|
|
6
6
|
require "graphql/schema/find_inherited_value"
|
|
7
7
|
require "graphql/schema/finder"
|
|
8
|
-
require "graphql/schema/invalid_type_error"
|
|
9
8
|
require "graphql/schema/introspection_system"
|
|
10
9
|
require "graphql/schema/late_bound_type"
|
|
11
|
-
require "graphql/schema/
|
|
10
|
+
require "graphql/schema/ractor_shareable"
|
|
12
11
|
require "graphql/schema/timeout"
|
|
13
12
|
require "graphql/schema/type_expression"
|
|
14
13
|
require "graphql/schema/unique_within_type"
|
|
@@ -46,6 +45,7 @@ require "graphql/schema/mutation"
|
|
|
46
45
|
require "graphql/schema/has_single_input_argument"
|
|
47
46
|
require "graphql/schema/relay_classic_mutation"
|
|
48
47
|
require "graphql/schema/subscription"
|
|
48
|
+
require "graphql/schema/visibility"
|
|
49
49
|
|
|
50
50
|
module GraphQL
|
|
51
51
|
# A GraphQL schema which may be queried with {GraphQL::Query}.
|
|
@@ -61,7 +61,7 @@ module GraphQL
|
|
|
61
61
|
# Any undiscoverable types may be provided with the `types` configuration.
|
|
62
62
|
#
|
|
63
63
|
# Schemas can restrict large incoming queries with `max_depth` and `max_complexity` configurations.
|
|
64
|
-
# (These configurations can be overridden by specific calls to {Schema
|
|
64
|
+
# (These configurations can be overridden by specific calls to {Schema.execute})
|
|
65
65
|
#
|
|
66
66
|
# @example defining a schema
|
|
67
67
|
# class MySchema < GraphQL::Schema
|
|
@@ -73,6 +73,9 @@ module GraphQL
|
|
|
73
73
|
class Schema
|
|
74
74
|
extend GraphQL::Schema::Member::HasAstNode
|
|
75
75
|
extend GraphQL::Schema::FindInheritedValue
|
|
76
|
+
extend Autoload
|
|
77
|
+
|
|
78
|
+
autoload :BUILT_IN_TYPES, "graphql/schema/built_in_types"
|
|
76
79
|
|
|
77
80
|
class DuplicateNamesError < GraphQL::Error
|
|
78
81
|
attr_reader :duplicated_name
|
|
@@ -109,7 +112,7 @@ module GraphQL
|
|
|
109
112
|
# @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
|
|
110
113
|
# @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
|
|
111
114
|
# @return [Class] the schema described by `document`
|
|
112
|
-
def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
|
|
115
|
+
def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {}, base_types: {})
|
|
113
116
|
# If the file ends in `.graphql` or `.graphqls`, treat it like a filepath
|
|
114
117
|
if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls")
|
|
115
118
|
GraphQL::Schema::BuildFromDefinition.from_definition_path(
|
|
@@ -118,6 +121,7 @@ module GraphQL
|
|
|
118
121
|
default_resolve: default_resolve,
|
|
119
122
|
parser: parser,
|
|
120
123
|
using: using,
|
|
124
|
+
base_types: base_types,
|
|
121
125
|
)
|
|
122
126
|
else
|
|
123
127
|
GraphQL::Schema::BuildFromDefinition.from_definition(
|
|
@@ -126,6 +130,7 @@ module GraphQL
|
|
|
126
130
|
default_resolve: default_resolve,
|
|
127
131
|
parser: parser,
|
|
128
132
|
using: using,
|
|
133
|
+
base_types: base_types,
|
|
129
134
|
)
|
|
130
135
|
end
|
|
131
136
|
end
|
|
@@ -144,10 +149,12 @@ module GraphQL
|
|
|
144
149
|
end
|
|
145
150
|
|
|
146
151
|
# @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
|
|
147
|
-
def default_trace_mode(new_mode =
|
|
148
|
-
if new_mode
|
|
152
|
+
def default_trace_mode(new_mode = NOT_CONFIGURED)
|
|
153
|
+
if !NOT_CONFIGURED.equal?(new_mode)
|
|
149
154
|
@default_trace_mode = new_mode
|
|
150
|
-
elsif defined?(@default_trace_mode)
|
|
155
|
+
elsif defined?(@default_trace_mode) &&
|
|
156
|
+
!@default_trace_mode.nil? # This `nil?` check seems necessary because of
|
|
157
|
+
# Ractors silently initializing @default_trace_mode somehow
|
|
151
158
|
@default_trace_mode
|
|
152
159
|
elsif superclass.respond_to?(:default_trace_mode)
|
|
153
160
|
superclass.default_trace_mode
|
|
@@ -162,10 +169,8 @@ module GraphQL
|
|
|
162
169
|
# re-apply them here
|
|
163
170
|
mods = trace_modules_for(:default)
|
|
164
171
|
mods.each { |mod| new_class.include(mod) }
|
|
172
|
+
new_class.include(DefaultTraceClass)
|
|
165
173
|
trace_mode(:default, new_class)
|
|
166
|
-
backtrace_class = Class.new(new_class)
|
|
167
|
-
backtrace_class.include(GraphQL::Backtrace::Trace)
|
|
168
|
-
trace_mode(:default_backtrace, backtrace_class)
|
|
169
174
|
end
|
|
170
175
|
trace_class_for(:default, build: true)
|
|
171
176
|
end
|
|
@@ -187,7 +192,7 @@ module GraphQL
|
|
|
187
192
|
# {default_trace_mode} is used when no `trace_mode: ...` is requested.
|
|
188
193
|
#
|
|
189
194
|
# When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
|
|
190
|
-
# unless `trace_mode` is explicitly given. (This class will not
|
|
195
|
+
# unless `trace_mode` is explicitly given. (This class will not receive any default trace modules.)
|
|
191
196
|
#
|
|
192
197
|
# Subclasses of the schema will use `trace_class` as a base class for this mode and those
|
|
193
198
|
# subclass also will _not_ receive default tracing modules.
|
|
@@ -204,24 +209,14 @@ module GraphQL
|
|
|
204
209
|
@own_trace_modes ||= {}
|
|
205
210
|
end
|
|
206
211
|
|
|
207
|
-
module DefaultTraceClass
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
private_constant :DefaultTraceClass
|
|
211
|
-
|
|
212
212
|
def build_trace_mode(mode)
|
|
213
213
|
case mode
|
|
214
214
|
when :default
|
|
215
215
|
# Use the superclass's default mode if it has one, or else start an inheritance chain at the built-in base class.
|
|
216
|
-
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || GraphQL::Tracing::Trace
|
|
217
|
-
Class.new(base_class) do
|
|
216
|
+
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode, build: true)) || GraphQL::Tracing::Trace
|
|
217
|
+
const_set(:DefaultTrace, Class.new(base_class) do
|
|
218
218
|
include DefaultTraceClass
|
|
219
|
-
end
|
|
220
|
-
when :default_backtrace
|
|
221
|
-
schema_base_class = trace_class_for(:default, build: true)
|
|
222
|
-
Class.new(schema_base_class) do
|
|
223
|
-
include(GraphQL::Backtrace::Trace)
|
|
224
|
-
end
|
|
219
|
+
end)
|
|
225
220
|
else
|
|
226
221
|
# First, see if the superclass has a custom-defined class for this.
|
|
227
222
|
# Then, if it doesn't, use this class's default trace
|
|
@@ -237,7 +232,7 @@ module GraphQL
|
|
|
237
232
|
add_trace_options_for(mode, default_options)
|
|
238
233
|
|
|
239
234
|
Class.new(base_class) do
|
|
240
|
-
mods.
|
|
235
|
+
!mods.empty? && include(*mods)
|
|
241
236
|
end
|
|
242
237
|
end
|
|
243
238
|
end
|
|
@@ -257,7 +252,7 @@ module GraphQL
|
|
|
257
252
|
|
|
258
253
|
|
|
259
254
|
# Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
|
|
260
|
-
# @see
|
|
255
|
+
# @see #as_json Return a Hash representation of the schema
|
|
261
256
|
# @return [String]
|
|
262
257
|
def to_json(**args)
|
|
263
258
|
JSON.pretty_generate(as_json(**args))
|
|
@@ -265,8 +260,6 @@ module GraphQL
|
|
|
265
260
|
|
|
266
261
|
# Return the Hash response of {Introspection::INTROSPECTION_QUERY}.
|
|
267
262
|
# @param context [Hash]
|
|
268
|
-
# @param only [<#call(member, ctx)>]
|
|
269
|
-
# @param except [<#call(member, ctx)>]
|
|
270
263
|
# @param include_deprecated_args [Boolean] If true, deprecated arguments will be included in the JSON response
|
|
271
264
|
# @param include_schema_description [Boolean] If true, the schema's description will be queried and included in the response
|
|
272
265
|
# @param include_is_repeatable [Boolean] If true, `isRepeatable: true|false` will be included with the schema's directives
|
|
@@ -321,8 +314,11 @@ module GraphQL
|
|
|
321
314
|
GraphQL::StaticValidation::Validator.new(schema: self)
|
|
322
315
|
end
|
|
323
316
|
|
|
317
|
+
# Add `plugin` to this schema
|
|
318
|
+
# @param plugin [#use] A Schema plugin
|
|
319
|
+
# @return void
|
|
324
320
|
def use(plugin, **kwargs)
|
|
325
|
-
if kwargs.
|
|
321
|
+
if !kwargs.empty?
|
|
326
322
|
plugin.use(self, **kwargs)
|
|
327
323
|
else
|
|
328
324
|
plugin.use(self)
|
|
@@ -338,6 +334,10 @@ module GraphQL
|
|
|
338
334
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
|
339
335
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
|
340
336
|
def types(context = GraphQL::Query::NullContext.instance)
|
|
337
|
+
if use_visibility_profile?
|
|
338
|
+
types = Visibility::Profile.from_context(context, self)
|
|
339
|
+
return types.all_types_h
|
|
340
|
+
end
|
|
341
341
|
all_types = non_introspection_types.merge(introspection_system.types)
|
|
342
342
|
visible_types = {}
|
|
343
343
|
all_types.each do |k, v|
|
|
@@ -363,27 +363,37 @@ module GraphQL
|
|
|
363
363
|
end
|
|
364
364
|
|
|
365
365
|
# @param type_name [String]
|
|
366
|
+
# @param context [GraphQL::Query::Context] Used for filtering definitions at query-time
|
|
367
|
+
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
366
368
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
|
367
|
-
def get_type(type_name, context = GraphQL::Query::NullContext.instance)
|
|
369
|
+
def get_type(type_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
|
|
370
|
+
if use_visibility_profile
|
|
371
|
+
profile = Visibility::Profile.from_context(context, self)
|
|
372
|
+
return profile.type(type_name)
|
|
373
|
+
end
|
|
368
374
|
local_entry = own_types[type_name]
|
|
369
375
|
type_defn = case local_entry
|
|
370
376
|
when nil
|
|
371
377
|
nil
|
|
372
378
|
when Array
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
379
|
+
if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
|
|
380
|
+
local_entry
|
|
381
|
+
else
|
|
382
|
+
visible_t = nil
|
|
383
|
+
warden = Warden.from_context(context)
|
|
384
|
+
local_entry.each do |t|
|
|
385
|
+
if warden.visible_type?(t, context)
|
|
386
|
+
if visible_t.nil?
|
|
387
|
+
visible_t = t
|
|
388
|
+
else
|
|
389
|
+
raise DuplicateNamesError.new(
|
|
390
|
+
duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
|
|
391
|
+
)
|
|
392
|
+
end
|
|
383
393
|
end
|
|
384
394
|
end
|
|
395
|
+
visible_t
|
|
385
396
|
end
|
|
386
|
-
visible_t
|
|
387
397
|
when Module
|
|
388
398
|
local_entry
|
|
389
399
|
else
|
|
@@ -392,7 +402,7 @@ module GraphQL
|
|
|
392
402
|
|
|
393
403
|
type_defn ||
|
|
394
404
|
introspection_system.types[type_name] || # todo context-specific introspection?
|
|
395
|
-
(superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
|
|
405
|
+
(superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context, use_visibility_profile) : nil)
|
|
396
406
|
end
|
|
397
407
|
|
|
398
408
|
# @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
|
|
@@ -419,55 +429,127 @@ module GraphQL
|
|
|
419
429
|
end
|
|
420
430
|
end
|
|
421
431
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
432
|
+
# Get or set the root `query { ... }` object for this schema.
|
|
433
|
+
#
|
|
434
|
+
# @example Using `Types::Query` as the entry-point
|
|
435
|
+
# query { Types::Query }
|
|
436
|
+
#
|
|
437
|
+
# @param new_query_object [Class<GraphQL::Schema::Object>] The root type to use for queries
|
|
438
|
+
# @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root query type.
|
|
439
|
+
# @return [Class<GraphQL::Schema::Object>, nil] The configured query root type, if there is one.
|
|
440
|
+
def query(new_query_object = nil, &lazy_load_block)
|
|
441
|
+
if new_query_object || block_given?
|
|
428
442
|
if @query_object
|
|
429
|
-
|
|
443
|
+
dup_defn = new_query_object || yield
|
|
444
|
+
raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
|
|
445
|
+
elsif use_visibility_profile?
|
|
446
|
+
if block_given?
|
|
447
|
+
if visibility.preload?
|
|
448
|
+
@query_object = lazy_load_block.call
|
|
449
|
+
self.visibility.query_configured(@query_object)
|
|
450
|
+
else
|
|
451
|
+
@query_object = lazy_load_block
|
|
452
|
+
end
|
|
453
|
+
else
|
|
454
|
+
@query_object = new_query_object
|
|
455
|
+
self.visibility.query_configured(@query_object)
|
|
456
|
+
end
|
|
430
457
|
else
|
|
431
|
-
@query_object = new_query_object
|
|
432
|
-
add_type_and_traverse(
|
|
433
|
-
nil
|
|
458
|
+
@query_object = new_query_object || lazy_load_block.call
|
|
459
|
+
add_type_and_traverse(@query_object, root: true)
|
|
434
460
|
end
|
|
461
|
+
nil
|
|
462
|
+
elsif @query_object.is_a?(Proc)
|
|
463
|
+
@query_object = @query_object.call
|
|
464
|
+
self.visibility&.query_configured(@query_object)
|
|
465
|
+
@query_object
|
|
435
466
|
else
|
|
436
467
|
@query_object || find_inherited_value(:query)
|
|
437
468
|
end
|
|
438
469
|
end
|
|
439
470
|
|
|
440
|
-
|
|
441
|
-
|
|
471
|
+
# Get or set the root `mutation { ... }` object for this schema.
|
|
472
|
+
#
|
|
473
|
+
# @example Using `Types::Mutation` as the entry-point
|
|
474
|
+
# mutation { Types::Mutation }
|
|
475
|
+
#
|
|
476
|
+
# @param new_mutation_object [Class<GraphQL::Schema::Object>] The root type to use for mutations
|
|
477
|
+
# @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root mutation type.
|
|
478
|
+
# @return [Class<GraphQL::Schema::Object>, nil] The configured mutation root type, if there is one.
|
|
479
|
+
def mutation(new_mutation_object = nil, &lazy_load_block)
|
|
480
|
+
if new_mutation_object || block_given?
|
|
442
481
|
if @mutation_object
|
|
443
|
-
|
|
482
|
+
dup_defn = new_mutation_object || yield
|
|
483
|
+
raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
|
|
484
|
+
elsif use_visibility_profile?
|
|
485
|
+
if block_given?
|
|
486
|
+
if visibility.preload?
|
|
487
|
+
@mutation_object = lazy_load_block.call
|
|
488
|
+
self.visibility.mutation_configured(@mutation_object)
|
|
489
|
+
else
|
|
490
|
+
@mutation_object = lazy_load_block
|
|
491
|
+
end
|
|
492
|
+
else
|
|
493
|
+
@mutation_object = new_mutation_object
|
|
494
|
+
self.visibility.mutation_configured(@mutation_object)
|
|
495
|
+
end
|
|
444
496
|
else
|
|
445
|
-
@mutation_object = new_mutation_object
|
|
446
|
-
add_type_and_traverse(
|
|
447
|
-
nil
|
|
497
|
+
@mutation_object = new_mutation_object || lazy_load_block.call
|
|
498
|
+
add_type_and_traverse(@mutation_object, root: true)
|
|
448
499
|
end
|
|
500
|
+
nil
|
|
501
|
+
elsif @mutation_object.is_a?(Proc)
|
|
502
|
+
@mutation_object = @mutation_object.call
|
|
503
|
+
self.visibility&.mutation_configured(@mutation_object)
|
|
504
|
+
@mutation_object
|
|
449
505
|
else
|
|
450
506
|
@mutation_object || find_inherited_value(:mutation)
|
|
451
507
|
end
|
|
452
508
|
end
|
|
453
509
|
|
|
454
|
-
|
|
455
|
-
|
|
510
|
+
# Get or set the root `subscription { ... }` object for this schema.
|
|
511
|
+
#
|
|
512
|
+
# @example Using `Types::Subscription` as the entry-point
|
|
513
|
+
# subscription { Types::Subscription }
|
|
514
|
+
#
|
|
515
|
+
# @param new_subscription_object [Class<GraphQL::Schema::Object>] The root type to use for subscriptions
|
|
516
|
+
# @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root subscription type.
|
|
517
|
+
# @return [Class<GraphQL::Schema::Object>, nil] The configured subscription root type, if there is one.
|
|
518
|
+
def subscription(new_subscription_object = nil, &lazy_load_block)
|
|
519
|
+
if new_subscription_object || block_given?
|
|
456
520
|
if @subscription_object
|
|
457
|
-
|
|
521
|
+
dup_defn = new_subscription_object || yield
|
|
522
|
+
raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
|
|
523
|
+
elsif use_visibility_profile?
|
|
524
|
+
if block_given?
|
|
525
|
+
if visibility.preload?
|
|
526
|
+
@subscription_object = lazy_load_block.call
|
|
527
|
+
visibility.subscription_configured(@subscription_object)
|
|
528
|
+
else
|
|
529
|
+
@subscription_object = lazy_load_block
|
|
530
|
+
end
|
|
531
|
+
else
|
|
532
|
+
@subscription_object = new_subscription_object
|
|
533
|
+
self.visibility.subscription_configured(@subscription_object)
|
|
534
|
+
end
|
|
535
|
+
add_subscription_extension_if_necessary
|
|
458
536
|
else
|
|
459
|
-
@subscription_object = new_subscription_object
|
|
537
|
+
@subscription_object = new_subscription_object || lazy_load_block.call
|
|
460
538
|
add_subscription_extension_if_necessary
|
|
461
|
-
add_type_and_traverse(
|
|
462
|
-
nil
|
|
539
|
+
add_type_and_traverse(@subscription_object, root: true)
|
|
463
540
|
end
|
|
541
|
+
nil
|
|
542
|
+
elsif @subscription_object.is_a?(Proc)
|
|
543
|
+
@subscription_object = @subscription_object.call
|
|
544
|
+
add_subscription_extension_if_necessary
|
|
545
|
+
self.visibility.subscription_configured(@subscription_object)
|
|
546
|
+
@subscription_object
|
|
464
547
|
else
|
|
465
548
|
@subscription_object || find_inherited_value(:subscription)
|
|
466
549
|
end
|
|
467
550
|
end
|
|
468
551
|
|
|
469
|
-
# @
|
|
470
|
-
# @return [GraphQL::ObjectType, nil]
|
|
552
|
+
# @api private
|
|
471
553
|
def root_type_for_operation(operation)
|
|
472
554
|
case operation
|
|
473
555
|
when "query"
|
|
@@ -481,10 +563,16 @@ module GraphQL
|
|
|
481
563
|
end
|
|
482
564
|
end
|
|
483
565
|
|
|
566
|
+
# @return [Array<Class>] The root types (query, mutation, subscription) defined for this schema
|
|
484
567
|
def root_types
|
|
485
|
-
|
|
568
|
+
if use_visibility_profile?
|
|
569
|
+
[query, mutation, subscription].compact
|
|
570
|
+
else
|
|
571
|
+
@root_types
|
|
572
|
+
end
|
|
486
573
|
end
|
|
487
574
|
|
|
575
|
+
# @api private
|
|
488
576
|
def warden_class
|
|
489
577
|
if defined?(@warden_class)
|
|
490
578
|
@warden_class
|
|
@@ -495,18 +583,54 @@ module GraphQL
|
|
|
495
583
|
end
|
|
496
584
|
end
|
|
497
585
|
|
|
586
|
+
# @api private
|
|
498
587
|
attr_writer :warden_class
|
|
499
588
|
|
|
589
|
+
# @api private
|
|
590
|
+
def visibility_profile_class
|
|
591
|
+
if defined?(@visibility_profile_class)
|
|
592
|
+
@visibility_profile_class
|
|
593
|
+
elsif superclass.respond_to?(:visibility_profile_class)
|
|
594
|
+
superclass.visibility_profile_class
|
|
595
|
+
else
|
|
596
|
+
GraphQL::Schema::Visibility::Profile
|
|
597
|
+
end
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
# @api private
|
|
601
|
+
attr_writer :visibility_profile_class, :use_visibility_profile
|
|
602
|
+
# @api private
|
|
603
|
+
attr_accessor :visibility
|
|
604
|
+
# @api private
|
|
605
|
+
def use_visibility_profile?
|
|
606
|
+
if defined?(@use_visibility_profile)
|
|
607
|
+
@use_visibility_profile
|
|
608
|
+
elsif superclass.respond_to?(:use_visibility_profile?)
|
|
609
|
+
superclass.use_visibility_profile?
|
|
610
|
+
else
|
|
611
|
+
false
|
|
612
|
+
end
|
|
613
|
+
end
|
|
614
|
+
|
|
500
615
|
# @param type [Module] The type definition whose possible types you want to see
|
|
616
|
+
# @param context [GraphQL::Query::Context] used for filtering visible possible types at runtime
|
|
617
|
+
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
501
618
|
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
|
502
619
|
# @return [Array<Module>] Possible types for `type`, if it's given.
|
|
503
|
-
def possible_types(type = nil, context = GraphQL::Query::NullContext.instance)
|
|
620
|
+
def possible_types(type = nil, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
|
|
621
|
+
if use_visibility_profile
|
|
622
|
+
if type
|
|
623
|
+
return Visibility::Profile.from_context(context, self).possible_types(type)
|
|
624
|
+
else
|
|
625
|
+
raise "Schema.possible_types is not implemented for `use_visibility_profile?`"
|
|
626
|
+
end
|
|
627
|
+
end
|
|
504
628
|
if type
|
|
505
629
|
# TODO duck-typing `.possible_types` would probably be nicer here
|
|
506
630
|
if type.kind.union?
|
|
507
631
|
type.possible_types(context: context)
|
|
508
632
|
else
|
|
509
|
-
stored_possible_types = own_possible_types[type
|
|
633
|
+
stored_possible_types = own_possible_types[type]
|
|
510
634
|
visible_possible_types = if stored_possible_types && type.kind.interface?
|
|
511
635
|
stored_possible_types.select do |possible_type|
|
|
512
636
|
possible_type.interfaces(context).include?(type)
|
|
@@ -515,10 +639,10 @@ module GraphQL
|
|
|
515
639
|
stored_possible_types
|
|
516
640
|
end
|
|
517
641
|
visible_possible_types ||
|
|
518
|
-
introspection_system.possible_types[type
|
|
642
|
+
introspection_system.possible_types[type] ||
|
|
519
643
|
(
|
|
520
644
|
superclass.respond_to?(:possible_types) ?
|
|
521
|
-
superclass.possible_types(type, context) :
|
|
645
|
+
superclass.possible_types(type, context, use_visibility_profile) :
|
|
522
646
|
EMPTY_ARRAY
|
|
523
647
|
)
|
|
524
648
|
end
|
|
@@ -553,14 +677,9 @@ module GraphQL
|
|
|
553
677
|
attr_writer :dataloader_class
|
|
554
678
|
|
|
555
679
|
def references_to(to_type = nil, from: nil)
|
|
556
|
-
@own_references_to ||= {}
|
|
557
680
|
if to_type
|
|
558
|
-
if !to_type.is_a?(String)
|
|
559
|
-
to_type = to_type.graphql_name
|
|
560
|
-
end
|
|
561
|
-
|
|
562
681
|
if from
|
|
563
|
-
refs =
|
|
682
|
+
refs = own_references_to[to_type] ||= []
|
|
564
683
|
refs << from
|
|
565
684
|
else
|
|
566
685
|
get_references_to(to_type) || EMPTY_ARRAY
|
|
@@ -570,20 +689,33 @@ module GraphQL
|
|
|
570
689
|
# and generally speaking, we won't inherit any values.
|
|
571
690
|
# So optimize the most common case -- don't create a duplicate Hash.
|
|
572
691
|
inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
|
|
573
|
-
if inherited_value.
|
|
574
|
-
inherited_value.merge(
|
|
692
|
+
if !inherited_value.empty?
|
|
693
|
+
inherited_value.merge(own_references_to)
|
|
575
694
|
else
|
|
576
|
-
|
|
695
|
+
own_references_to
|
|
577
696
|
end
|
|
578
697
|
end
|
|
579
698
|
end
|
|
580
699
|
|
|
581
|
-
def type_from_ast(ast_node, context:
|
|
582
|
-
|
|
583
|
-
GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
|
|
700
|
+
def type_from_ast(ast_node, context: self.query_class.new(self, "{ __typename }").context)
|
|
701
|
+
GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
|
|
584
702
|
end
|
|
585
703
|
|
|
586
|
-
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance)
|
|
704
|
+
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext.instance, use_visibility_profile = use_visibility_profile?)
|
|
705
|
+
if use_visibility_profile
|
|
706
|
+
profile = Visibility::Profile.from_context(context, self)
|
|
707
|
+
parent_type = case type_or_name
|
|
708
|
+
when String
|
|
709
|
+
profile.type(type_or_name)
|
|
710
|
+
when Module
|
|
711
|
+
type_or_name
|
|
712
|
+
when LateBoundType
|
|
713
|
+
profile.type(type_or_name.name)
|
|
714
|
+
else
|
|
715
|
+
raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
|
|
716
|
+
end
|
|
717
|
+
return profile.field(parent_type, field_name)
|
|
718
|
+
end
|
|
587
719
|
parent_type = case type_or_name
|
|
588
720
|
when LateBoundType
|
|
589
721
|
get_type(type_or_name.name, context)
|
|
@@ -610,20 +742,27 @@ module GraphQL
|
|
|
610
742
|
type.fields(context)
|
|
611
743
|
end
|
|
612
744
|
|
|
745
|
+
# Pass a custom introspection module here to use it for this schema.
|
|
746
|
+
# @param new_introspection_namespace [Module] If given, use this module for custom introspection on the schema
|
|
747
|
+
# @return [Module, nil] The configured namespace, if there is one
|
|
613
748
|
def introspection(new_introspection_namespace = nil)
|
|
614
749
|
if new_introspection_namespace
|
|
615
750
|
@introspection = new_introspection_namespace
|
|
616
751
|
# reset this cached value:
|
|
617
752
|
@introspection_system = nil
|
|
753
|
+
introspection_system
|
|
754
|
+
@introspection
|
|
618
755
|
else
|
|
619
756
|
@introspection || find_inherited_value(:introspection)
|
|
620
757
|
end
|
|
621
758
|
end
|
|
622
759
|
|
|
760
|
+
# @return [Schema::IntrospectionSystem] Based on {introspection}
|
|
623
761
|
def introspection_system
|
|
624
762
|
if !@introspection_system
|
|
625
763
|
@introspection_system = Schema::IntrospectionSystem.new(self)
|
|
626
764
|
@introspection_system.resolve_late_bindings
|
|
765
|
+
self.visibility&.introspection_system_configured(@introspection_system)
|
|
627
766
|
end
|
|
628
767
|
@introspection_system
|
|
629
768
|
end
|
|
@@ -643,6 +782,17 @@ module GraphQL
|
|
|
643
782
|
end
|
|
644
783
|
end
|
|
645
784
|
|
|
785
|
+
# A limit on the number of tokens to accept on incoming query strings.
|
|
786
|
+
# Use this to prevent parsing maliciously-large query strings.
|
|
787
|
+
# @return [nil, Integer]
|
|
788
|
+
def max_query_string_tokens(new_max_tokens = NOT_CONFIGURED)
|
|
789
|
+
if NOT_CONFIGURED.equal?(new_max_tokens)
|
|
790
|
+
defined?(@max_query_string_tokens) ? @max_query_string_tokens : find_inherited_value(:max_query_string_tokens)
|
|
791
|
+
else
|
|
792
|
+
@max_query_string_tokens = new_max_tokens
|
|
793
|
+
end
|
|
794
|
+
end
|
|
795
|
+
|
|
646
796
|
def default_page_size(new_default_page_size = nil)
|
|
647
797
|
if new_default_page_size
|
|
648
798
|
@default_page_size = new_default_page_size
|
|
@@ -689,13 +839,13 @@ module GraphQL
|
|
|
689
839
|
|
|
690
840
|
attr_writer :validate_timeout
|
|
691
841
|
|
|
692
|
-
def validate_timeout(new_validate_timeout =
|
|
693
|
-
if new_validate_timeout
|
|
842
|
+
def validate_timeout(new_validate_timeout = NOT_CONFIGURED)
|
|
843
|
+
if !NOT_CONFIGURED.equal?(new_validate_timeout)
|
|
694
844
|
@validate_timeout = new_validate_timeout
|
|
695
845
|
elsif defined?(@validate_timeout)
|
|
696
846
|
@validate_timeout
|
|
697
847
|
else
|
|
698
|
-
find_inherited_value(:validate_timeout)
|
|
848
|
+
find_inherited_value(:validate_timeout) || 3
|
|
699
849
|
end
|
|
700
850
|
end
|
|
701
851
|
|
|
@@ -716,6 +866,7 @@ module GraphQL
|
|
|
716
866
|
res[:errors]
|
|
717
867
|
end
|
|
718
868
|
|
|
869
|
+
# @param new_query_class [Class<GraphQL::Query>] A subclass to use when executing queries
|
|
719
870
|
def query_class(new_query_class = NOT_CONFIGURED)
|
|
720
871
|
if NOT_CONFIGURED.equal?(new_query_class)
|
|
721
872
|
@query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
|
|
@@ -726,21 +877,20 @@ module GraphQL
|
|
|
726
877
|
|
|
727
878
|
attr_writer :validate_max_errors
|
|
728
879
|
|
|
729
|
-
def validate_max_errors(new_validate_max_errors =
|
|
730
|
-
if new_validate_max_errors
|
|
731
|
-
@validate_max_errors
|
|
732
|
-
elsif defined?(@validate_max_errors)
|
|
733
|
-
@validate_max_errors
|
|
880
|
+
def validate_max_errors(new_validate_max_errors = NOT_CONFIGURED)
|
|
881
|
+
if NOT_CONFIGURED.equal?(new_validate_max_errors)
|
|
882
|
+
defined?(@validate_max_errors) ? @validate_max_errors : find_inherited_value(:validate_max_errors)
|
|
734
883
|
else
|
|
735
|
-
|
|
884
|
+
@validate_max_errors = new_validate_max_errors
|
|
736
885
|
end
|
|
737
886
|
end
|
|
738
887
|
|
|
739
888
|
attr_writer :max_complexity
|
|
740
889
|
|
|
741
|
-
def max_complexity(max_complexity = nil)
|
|
890
|
+
def max_complexity(max_complexity = nil, count_introspection_fields: true)
|
|
742
891
|
if max_complexity
|
|
743
892
|
@max_complexity = max_complexity
|
|
893
|
+
@max_complexity_count_introspection_fields = count_introspection_fields
|
|
744
894
|
elsif defined?(@max_complexity)
|
|
745
895
|
@max_complexity
|
|
746
896
|
else
|
|
@@ -748,22 +898,20 @@ module GraphQL
|
|
|
748
898
|
end
|
|
749
899
|
end
|
|
750
900
|
|
|
901
|
+
def max_complexity_count_introspection_fields
|
|
902
|
+
if defined?(@max_complexity_count_introspection_fields)
|
|
903
|
+
@max_complexity_count_introspection_fields
|
|
904
|
+
else
|
|
905
|
+
find_inherited_value(:max_complexity_count_introspection_fields, true)
|
|
906
|
+
end
|
|
907
|
+
end
|
|
908
|
+
|
|
751
909
|
attr_writer :analysis_engine
|
|
752
910
|
|
|
753
911
|
def analysis_engine
|
|
754
912
|
@analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
|
|
755
913
|
end
|
|
756
914
|
|
|
757
|
-
def using_ast_analysis?
|
|
758
|
-
true
|
|
759
|
-
end
|
|
760
|
-
|
|
761
|
-
def interpreter?
|
|
762
|
-
true
|
|
763
|
-
end
|
|
764
|
-
|
|
765
|
-
attr_writer :interpreter
|
|
766
|
-
|
|
767
915
|
def error_bubbling(new_error_bubbling = nil)
|
|
768
916
|
if !new_error_bubbling.nil?
|
|
769
917
|
warn("error_bubbling(#{new_error_bubbling.inspect}) is deprecated; the default value of `false` will be the only option in GraphQL-Ruby 3.0")
|
|
@@ -841,7 +989,7 @@ module GraphQL
|
|
|
841
989
|
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
|
842
990
|
# @return [Array<Module>] Type definitions added to this schema
|
|
843
991
|
def extra_types(*new_extra_types)
|
|
844
|
-
if new_extra_types.
|
|
992
|
+
if !new_extra_types.empty?
|
|
845
993
|
new_extra_types = new_extra_types.flatten
|
|
846
994
|
@own_extra_types ||= []
|
|
847
995
|
@own_extra_types.concat(new_extra_types)
|
|
@@ -858,16 +1006,35 @@ module GraphQL
|
|
|
858
1006
|
end
|
|
859
1007
|
end
|
|
860
1008
|
|
|
1009
|
+
# Tell the schema about these types so that they can be registered as implementations of interfaces in the schema.
|
|
1010
|
+
#
|
|
1011
|
+
# This method must be used when an object type is connected to the schema as an interface implementor but
|
|
1012
|
+
# not as a return type of a field. In that case, if the object type isn't registered here, GraphQL-Ruby won't be able to find it.
|
|
1013
|
+
#
|
|
1014
|
+
# @param new_orphan_types [Array<Class<GraphQL::Schema::Object>>] Object types to register as implementations of interfaces in the schema.
|
|
1015
|
+
# @return [Array<Class<GraphQL::Schema::Object>>] All previously-registered orphan types for this schema
|
|
861
1016
|
def orphan_types(*new_orphan_types)
|
|
862
|
-
if new_orphan_types.
|
|
1017
|
+
if !new_orphan_types.empty?
|
|
863
1018
|
new_orphan_types = new_orphan_types.flatten
|
|
864
|
-
|
|
1019
|
+
non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
|
|
1020
|
+
if !non_object_types.empty?
|
|
1021
|
+
raise ArgumentError, <<~ERR
|
|
1022
|
+
Only object type classes should be added as `orphan_types(...)`.
|
|
1023
|
+
|
|
1024
|
+
- Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")}
|
|
1025
|
+
- See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
|
|
1026
|
+
|
|
1027
|
+
To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
|
|
1028
|
+
ERR
|
|
1029
|
+
end
|
|
1030
|
+
add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile?
|
|
865
1031
|
own_orphan_types.concat(new_orphan_types.flatten)
|
|
1032
|
+
self.visibility&.orphan_types_configured(new_orphan_types)
|
|
866
1033
|
end
|
|
867
1034
|
|
|
868
1035
|
inherited_ot = find_inherited_value(:orphan_types, nil)
|
|
869
1036
|
if inherited_ot
|
|
870
|
-
if own_orphan_types.
|
|
1037
|
+
if !own_orphan_types.empty?
|
|
871
1038
|
inherited_ot + own_orphan_types
|
|
872
1039
|
else
|
|
873
1040
|
inherited_ot
|
|
@@ -893,6 +1060,8 @@ module GraphQL
|
|
|
893
1060
|
end
|
|
894
1061
|
end
|
|
895
1062
|
|
|
1063
|
+
|
|
1064
|
+
# @param new_default_logger [#log] Something to use for logging messages
|
|
896
1065
|
def default_logger(new_default_logger = NOT_CONFIGURED)
|
|
897
1066
|
if NOT_CONFIGURED.equal?(new_default_logger)
|
|
898
1067
|
if defined?(@default_logger)
|
|
@@ -913,6 +1082,19 @@ module GraphQL
|
|
|
913
1082
|
end
|
|
914
1083
|
end
|
|
915
1084
|
|
|
1085
|
+
# @param context [GraphQL::Query::Context, nil]
|
|
1086
|
+
# @return [Logger] A logger to use for this context configuration, falling back to {.default_logger}
|
|
1087
|
+
def logger_for(context)
|
|
1088
|
+
if context && context[:logger] == false
|
|
1089
|
+
Logger.new(IO::NULL)
|
|
1090
|
+
elsif context && (l = context[:logger])
|
|
1091
|
+
l
|
|
1092
|
+
else
|
|
1093
|
+
default_logger
|
|
1094
|
+
end
|
|
1095
|
+
end
|
|
1096
|
+
|
|
1097
|
+
# @param new_context_class [Class<GraphQL::Query::Context>] A subclass to use when executing queries
|
|
916
1098
|
def context_class(new_context_class = nil)
|
|
917
1099
|
if new_context_class
|
|
918
1100
|
@context_class = new_context_class
|
|
@@ -921,28 +1103,46 @@ module GraphQL
|
|
|
921
1103
|
end
|
|
922
1104
|
end
|
|
923
1105
|
|
|
1106
|
+
# Register a handler for errors raised during execution. The handlers can return a new value or raise a new error.
|
|
1107
|
+
#
|
|
1108
|
+
# @example Handling "not found" with a client-facing error
|
|
1109
|
+
# rescue_from(ActiveRecord::NotFound) { raise GraphQL::ExecutionError, "An object could not be found" }
|
|
1110
|
+
#
|
|
1111
|
+
# @param err_classes [Array<StandardError>] Classes which should be rescued by `handler_block`
|
|
1112
|
+
# @param handler_block The code to run when one of those errors is raised during execution
|
|
1113
|
+
# @yieldparam error [StandardError] An instance of one of the configured `err_classes`
|
|
1114
|
+
# @yieldparam object [Object] The current application object in the query when the error was raised
|
|
1115
|
+
# @yieldparam arguments [GraphQL::Query::Arguments] The current field arguments when the error was raised
|
|
1116
|
+
# @yieldparam context [GraphQL::Query::Context] The context for the currently-running operation
|
|
1117
|
+
# @yieldreturn [Object] Some object to use in the place where this error was raised
|
|
1118
|
+
# @raise [GraphQL::ExecutionError] In the handler, raise to add a client-facing error to the response
|
|
1119
|
+
# @raise [StandardError] In the handler, raise to crash the query with a developer-facing error
|
|
924
1120
|
def rescue_from(*err_classes, &handler_block)
|
|
925
1121
|
err_classes.each do |err_class|
|
|
926
1122
|
Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block)
|
|
927
1123
|
end
|
|
928
1124
|
end
|
|
929
1125
|
|
|
930
|
-
NEW_HANDLER_HASH = ->(h, k) {
|
|
931
|
-
h[k] = {
|
|
932
|
-
class: k,
|
|
933
|
-
handler: nil,
|
|
934
|
-
subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
|
|
938
1126
|
def error_handlers
|
|
939
|
-
@error_handlers ||=
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1127
|
+
@error_handlers ||= begin
|
|
1128
|
+
new_handler_hash = ->(h, k) {
|
|
1129
|
+
h[k] = {
|
|
1130
|
+
class: k,
|
|
1131
|
+
handler: nil,
|
|
1132
|
+
subclass_handlers: Hash.new(&new_handler_hash),
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
{
|
|
1136
|
+
class: nil,
|
|
1137
|
+
handler: nil,
|
|
1138
|
+
subclass_handlers: Hash.new(&new_handler_hash),
|
|
1139
|
+
}
|
|
1140
|
+
end
|
|
944
1141
|
end
|
|
945
1142
|
|
|
1143
|
+
# @api private
|
|
1144
|
+
attr_accessor :using_backtrace
|
|
1145
|
+
|
|
946
1146
|
# @api private
|
|
947
1147
|
def handle_or_reraise(context, err)
|
|
948
1148
|
handler = Execution::Errors.find_handler_for(self, err.class)
|
|
@@ -956,6 +1156,10 @@ module GraphQL
|
|
|
956
1156
|
end
|
|
957
1157
|
handler[:handler].call(err, obj, args, context, field)
|
|
958
1158
|
else
|
|
1159
|
+
if (context[:backtrace] || using_backtrace) && !err.is_a?(GraphQL::ExecutionError)
|
|
1160
|
+
err = GraphQL::Backtrace::TracedError.new(err, context)
|
|
1161
|
+
end
|
|
1162
|
+
|
|
959
1163
|
raise err
|
|
960
1164
|
end
|
|
961
1165
|
end
|
|
@@ -987,8 +1191,24 @@ module GraphQL
|
|
|
987
1191
|
end
|
|
988
1192
|
end
|
|
989
1193
|
|
|
990
|
-
|
|
991
|
-
|
|
1194
|
+
# GraphQL-Ruby calls this method during execution when it needs the application to determine the type to use for an object.
|
|
1195
|
+
#
|
|
1196
|
+
# Usually, this object was returned from a field whose return type is an {GraphQL::Schema::Interface} or a {GraphQL::Schema::Union}.
|
|
1197
|
+
# But this method is called in other cases, too -- for example, when {GraphQL::Schema::Argument#loads} cases an object to be directly loaded from the database.
|
|
1198
|
+
#
|
|
1199
|
+
# @example Returning a GraphQL type based on the object's class name
|
|
1200
|
+
# class MySchema < GraphQL::Schema
|
|
1201
|
+
# def resolve_type(_abs_type, object, _context)
|
|
1202
|
+
# graphql_type_name = "Types::#{object.class.name}Type"
|
|
1203
|
+
# graphql_type_name.constantize # If this raises a NameError, then come implement special cases in this method
|
|
1204
|
+
# end
|
|
1205
|
+
# end
|
|
1206
|
+
# @param abstract_type [Class, Module, nil] The Interface or Union type which is being resolved, if there is one
|
|
1207
|
+
# @param application_object [Object] The object returned from a field whose type must be determined
|
|
1208
|
+
# @param context [GraphQL::Query::Context] The query context for the currently-executing query
|
|
1209
|
+
# @return [Class<GraphQL::Schema::Object] The Object type definition to use for `obj`
|
|
1210
|
+
def resolve_type(abstract_type, application_object, context)
|
|
1211
|
+
raise GraphQL::RequiredImplementationMissingError, "#{self.name}.resolve_type(abstract_type, application_object, context) must be implemented to use Union types, Interface types, `loads:`, or `run_partials` (tried to resolve: #{abstract_type.name})"
|
|
992
1212
|
end
|
|
993
1213
|
# rubocop:enable Lint/DuplicateMethods
|
|
994
1214
|
|
|
@@ -1003,15 +1223,45 @@ module GraphQL
|
|
|
1003
1223
|
child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
|
|
1004
1224
|
end
|
|
1005
1225
|
child_class.singleton_class.prepend(ResolveTypeWithType)
|
|
1006
|
-
super
|
|
1007
|
-
end
|
|
1008
1226
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1227
|
+
if use_visibility_profile?
|
|
1228
|
+
vis = self.visibility
|
|
1229
|
+
child_class.visibility = vis.dup_for(child_class)
|
|
1230
|
+
end
|
|
1231
|
+
super
|
|
1011
1232
|
end
|
|
1012
1233
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1234
|
+
# Fetch an object based on an incoming ID and the current context. This method should return an object
|
|
1235
|
+
# from your application, or return `nil` if there is no object or the object shouldn't be available to this operation.
|
|
1236
|
+
#
|
|
1237
|
+
# @example Fetching an object with Rails's GlobalID
|
|
1238
|
+
# def self.object_from_id(object_id, _context)
|
|
1239
|
+
# GlobalID.find(global_id)
|
|
1240
|
+
# # TODO: use `context[:current_user]` to determine if this object is authorized.
|
|
1241
|
+
# end
|
|
1242
|
+
# @param object_id [String] The ID to fetch an object for. This may be client-provided (as in `node(id: ...)` or `loads:`) or previously stored by the schema (eg, by the `ObjectCache`)
|
|
1243
|
+
# @param context [GraphQL::Query::Context] The context for the currently-executing operation
|
|
1244
|
+
# @return [Object, nil] The application which `object_id` references, or `nil` if there is no object or the current operation shouldn't have access to the object
|
|
1245
|
+
# @see id_from_object which produces these IDs
|
|
1246
|
+
def object_from_id(object_id, context)
|
|
1247
|
+
raise GraphQL::RequiredImplementationMissingError, "#{self.name}.object_from_id(object_id, context) must be implemented to load by ID (tried to load from id `#{object_id}`)"
|
|
1248
|
+
end
|
|
1249
|
+
|
|
1250
|
+
# Return a stable ID string for `object` so that it can be refetched later, using {.object_from_id}.
|
|
1251
|
+
#
|
|
1252
|
+
# [GlobalID](https://github.com/rails/globalid) and [SQIDs](https://sqids.org/ruby) can both be used to create IDs.
|
|
1253
|
+
#
|
|
1254
|
+
# @example Using Rails's GlobalID to generate IDs
|
|
1255
|
+
# def self.id_from_object(application_object, graphql_type, context)
|
|
1256
|
+
# application_object.to_gid_param
|
|
1257
|
+
# end
|
|
1258
|
+
#
|
|
1259
|
+
# @param application_object [Object] Some object encountered by GraphQL-Ruby while running a query
|
|
1260
|
+
# @param graphql_type [Class, Module] The type that GraphQL-Ruby is using for `application_object` during this query
|
|
1261
|
+
# @param context [GraphQL::Query::Context] The context for the operation that is currently running
|
|
1262
|
+
# @return [String] A stable identifier which can be passed to {.object_from_id} later to re-fetch `application_object`
|
|
1263
|
+
def id_from_object(application_object, graphql_type, context)
|
|
1264
|
+
raise GraphQL::RequiredImplementationMissingError, "#{self.name}.id_from_object(application_object, graphql_type, context) must be implemented to create global ids (tried to create an id for `#{application_object.inspect}`)"
|
|
1015
1265
|
end
|
|
1016
1266
|
|
|
1017
1267
|
def visible?(member, ctx)
|
|
@@ -1027,6 +1277,10 @@ module GraphQL
|
|
|
1027
1277
|
Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives)
|
|
1028
1278
|
end
|
|
1029
1279
|
|
|
1280
|
+
# Called when a type is needed by name at runtime
|
|
1281
|
+
def load_type(type_name, ctx)
|
|
1282
|
+
get_type(type_name, ctx)
|
|
1283
|
+
end
|
|
1030
1284
|
# This hook is called when an object fails an `authorized?` check.
|
|
1031
1285
|
# You might report to your bug tracker here, so you can correct
|
|
1032
1286
|
# the field resolvers not to return unauthorized objects.
|
|
@@ -1062,10 +1316,23 @@ module GraphQL
|
|
|
1062
1316
|
unauthorized_object(unauthorized_error)
|
|
1063
1317
|
end
|
|
1064
1318
|
|
|
1065
|
-
|
|
1319
|
+
# Called at runtime when GraphQL-Ruby encounters a mismatch between the application behavior
|
|
1320
|
+
# and the GraphQL type system.
|
|
1321
|
+
#
|
|
1322
|
+
# The default implementation of this method is to follow the GraphQL specification,
|
|
1323
|
+
# but you can override this to report errors to your bug tracker or customize error handling.
|
|
1324
|
+
# @param type_error [GraphQL::Error] several specific error classes are passed here, see the default implementation for details
|
|
1325
|
+
# @param context [GraphQL::Query::Context] the context for the currently-running operation
|
|
1326
|
+
# @return [void]
|
|
1327
|
+
# @raise [GraphQL::ExecutionError] to return this error to the client
|
|
1328
|
+
# @raise [GraphQL::Error] to crash the query and raise a developer-facing error
|
|
1329
|
+
def type_error(type_error, context)
|
|
1066
1330
|
case type_error
|
|
1067
1331
|
when GraphQL::InvalidNullError
|
|
1068
|
-
|
|
1332
|
+
execution_error = GraphQL::ExecutionError.new(type_error.message, ast_node: type_error.ast_node)
|
|
1333
|
+
execution_error.path = context[:current_path]
|
|
1334
|
+
|
|
1335
|
+
context.errors << execution_error
|
|
1069
1336
|
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
|
|
1070
1337
|
raise type_error
|
|
1071
1338
|
when GraphQL::IntegerDecodingError
|
|
@@ -1073,7 +1340,7 @@ module GraphQL
|
|
|
1073
1340
|
end
|
|
1074
1341
|
end
|
|
1075
1342
|
|
|
1076
|
-
# A function to call when {
|
|
1343
|
+
# A function to call when {.execute} receives an invalid query string
|
|
1077
1344
|
#
|
|
1078
1345
|
# The default is to add the error to `context.errors`
|
|
1079
1346
|
# @param parse_err [GraphQL::ParseError] The error encountered during parsing
|
|
@@ -1100,12 +1367,12 @@ module GraphQL
|
|
|
1100
1367
|
# Add several directives at once
|
|
1101
1368
|
# @param new_directives [Class]
|
|
1102
1369
|
def directives(*new_directives)
|
|
1103
|
-
if new_directives.
|
|
1370
|
+
if !new_directives.empty?
|
|
1104
1371
|
new_directives.flatten.each { |d| directive(d) }
|
|
1105
1372
|
end
|
|
1106
1373
|
|
|
1107
1374
|
inherited_dirs = find_inherited_value(:directives, default_directives)
|
|
1108
|
-
if own_directives.
|
|
1375
|
+
if !own_directives.empty?
|
|
1109
1376
|
inherited_dirs.merge(own_directives)
|
|
1110
1377
|
else
|
|
1111
1378
|
inherited_dirs
|
|
@@ -1116,7 +1383,11 @@ module GraphQL
|
|
|
1116
1383
|
# @param new_directive [Class]
|
|
1117
1384
|
# @return void
|
|
1118
1385
|
def directive(new_directive)
|
|
1119
|
-
|
|
1386
|
+
if use_visibility_profile?
|
|
1387
|
+
own_directives[new_directive.graphql_name] = new_directive
|
|
1388
|
+
else
|
|
1389
|
+
add_type_and_traverse(new_directive, root: false)
|
|
1390
|
+
end
|
|
1120
1391
|
end
|
|
1121
1392
|
|
|
1122
1393
|
def default_directives
|
|
@@ -1129,7 +1400,21 @@ module GraphQL
|
|
|
1129
1400
|
}.freeze
|
|
1130
1401
|
end
|
|
1131
1402
|
|
|
1132
|
-
|
|
1403
|
+
# @return [GraphQL::Tracing::DetailedTrace] if it has been configured for this schema
|
|
1404
|
+
attr_accessor :detailed_trace
|
|
1405
|
+
|
|
1406
|
+
# @param query [GraphQL::Query, GraphQL::Execution::Multiplex] Called with a multiplex when multiple queries are executed at once (with {.multiplex})
|
|
1407
|
+
# @return [Boolean] When `true`, save a detailed trace for this query.
|
|
1408
|
+
# @see Tracing::DetailedTrace DetailedTrace saves traces when this method returns true
|
|
1409
|
+
def detailed_trace?(query)
|
|
1410
|
+
raise "#{self} must implement `def.detailed_trace?(query)` to use DetailedTrace. Implement this method in your schema definition."
|
|
1411
|
+
end
|
|
1412
|
+
|
|
1413
|
+
def tracer(new_tracer, silence_deprecation_warning: false)
|
|
1414
|
+
if !silence_deprecation_warning
|
|
1415
|
+
warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
|
|
1416
|
+
warn " #{caller(1, 1).first}"
|
|
1417
|
+
end
|
|
1133
1418
|
default_trace = trace_class_for(:default, build: true)
|
|
1134
1419
|
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
|
1135
1420
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
|
@@ -1142,13 +1427,22 @@ module GraphQL
|
|
|
1142
1427
|
find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers
|
|
1143
1428
|
end
|
|
1144
1429
|
|
|
1145
|
-
# Mix `trace_mod` into this schema's `Trace` class so that its methods
|
|
1146
|
-
#
|
|
1430
|
+
# Mix `trace_mod` into this schema's `Trace` class so that its methods will be called at runtime.
|
|
1431
|
+
#
|
|
1432
|
+
# You can attach a module to run in only _some_ circumstances by using `mode:`. When a module is added with `mode:`,
|
|
1433
|
+
# it will only run for queries with a matching `context[:trace_mode]`.
|
|
1434
|
+
#
|
|
1435
|
+
# Any custom trace modes _also_ include the default `trace_with ...` modules (that is, those added _without_ any particular `mode: ...` configuration).
|
|
1436
|
+
#
|
|
1437
|
+
# @example Adding a trace in a special mode
|
|
1438
|
+
# # only runs when `query.context[:trace_mode]` is `:special`
|
|
1439
|
+
# trace_with SpecialTrace, mode: :special
|
|
1147
1440
|
#
|
|
1148
1441
|
# @param trace_mod [Module] A module that implements tracing methods
|
|
1149
1442
|
# @param mode [Symbol] Trace module will only be used for this trade mode
|
|
1150
1443
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
|
1151
1444
|
# @return [void]
|
|
1445
|
+
# @see GraphQL::Tracing::Trace Tracing::Trace for available tracing methods
|
|
1152
1446
|
def trace_with(trace_mod, mode: :default, **options)
|
|
1153
1447
|
if mode.is_a?(Array)
|
|
1154
1448
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
|
@@ -1198,34 +1492,43 @@ module GraphQL
|
|
|
1198
1492
|
#
|
|
1199
1493
|
# If no `mode:` is given, then {default_trace_mode} will be used.
|
|
1200
1494
|
#
|
|
1495
|
+
# If this schema is using {Tracing::DetailedTrace} and {.detailed_trace?} returns `true`, then
|
|
1496
|
+
# DetailedTrace's mode will override the passed-in `mode`.
|
|
1497
|
+
#
|
|
1201
1498
|
# @param mode [Symbol] Trace modules for this trade mode will be included
|
|
1202
1499
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
|
1203
1500
|
# @return [Tracing::Trace]
|
|
1204
1501
|
def new_trace(mode: nil, **options)
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
own_trace_modes[:default_backtrace] ||= build_trace_mode(:default_backtrace)
|
|
1215
|
-
options_trace_mode = :default
|
|
1216
|
-
:default_backtrace
|
|
1502
|
+
should_sample = if detailed_trace
|
|
1503
|
+
if (query = options[:query])
|
|
1504
|
+
detailed_trace?(query)
|
|
1505
|
+
elsif (multiplex = options[:multiplex])
|
|
1506
|
+
if multiplex.queries.length == 1
|
|
1507
|
+
detailed_trace?(multiplex.queries.first)
|
|
1508
|
+
else
|
|
1509
|
+
detailed_trace?(multiplex)
|
|
1510
|
+
end
|
|
1217
1511
|
end
|
|
1218
1512
|
else
|
|
1219
|
-
|
|
1513
|
+
false
|
|
1514
|
+
end
|
|
1515
|
+
|
|
1516
|
+
if should_sample
|
|
1517
|
+
mode = detailed_trace.trace_mode
|
|
1518
|
+
else
|
|
1519
|
+
target = options[:query] || options[:multiplex]
|
|
1520
|
+
mode ||= target && target.context[:trace_mode]
|
|
1220
1521
|
end
|
|
1221
1522
|
|
|
1222
|
-
|
|
1223
|
-
base_trace_options = trace_options_for(
|
|
1523
|
+
trace_mode = mode || default_trace_mode
|
|
1524
|
+
base_trace_options = trace_options_for(trace_mode)
|
|
1224
1525
|
trace_options = base_trace_options.merge(options)
|
|
1225
1526
|
trace_class_for_mode = trace_class_for(trace_mode, build: true)
|
|
1226
1527
|
trace_class_for_mode.new(**trace_options)
|
|
1227
1528
|
end
|
|
1228
1529
|
|
|
1530
|
+
# @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on queries to this schema
|
|
1531
|
+
# @see GraphQL::Analysis the analysis system
|
|
1229
1532
|
def query_analyzer(new_analyzer)
|
|
1230
1533
|
own_query_analyzers << new_analyzer
|
|
1231
1534
|
end
|
|
@@ -1234,6 +1537,8 @@ module GraphQL
|
|
|
1234
1537
|
find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
|
|
1235
1538
|
end
|
|
1236
1539
|
|
|
1540
|
+
# @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on multiplexes to this schema
|
|
1541
|
+
# @see GraphQL::Analysis the analysis system
|
|
1237
1542
|
def multiplex_analyzer(new_analyzer)
|
|
1238
1543
|
own_multiplex_analyzers << new_analyzer
|
|
1239
1544
|
end
|
|
@@ -1252,7 +1557,7 @@ module GraphQL
|
|
|
1252
1557
|
|
|
1253
1558
|
# Execute a query on itself.
|
|
1254
1559
|
# @see {Query#initialize} for arguments.
|
|
1255
|
-
# @return [
|
|
1560
|
+
# @return [GraphQL::Query::Result] query result, ready to be serialized as JSON
|
|
1256
1561
|
def execute(query_str = nil, **kwargs)
|
|
1257
1562
|
if query_str
|
|
1258
1563
|
kwargs[:query] = query_str
|
|
@@ -1291,8 +1596,9 @@ module GraphQL
|
|
|
1291
1596
|
# @see {Query#initialize} for query keyword arguments
|
|
1292
1597
|
# @see {Execution::Multiplex#run_all} for multiplex keyword arguments
|
|
1293
1598
|
# @param queries [Array<Hash>] Keyword arguments for each query
|
|
1294
|
-
# @
|
|
1295
|
-
# @
|
|
1599
|
+
# @option kwargs [Hash] :context ({}) Multiplex-level context
|
|
1600
|
+
# @option kwargs [nil, Integer] :max_complexity (nil)
|
|
1601
|
+
# @return [Array<GraphQL::Query::Result>] One result for each query in the input
|
|
1296
1602
|
def multiplex(queries, **kwargs)
|
|
1297
1603
|
GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
|
|
1298
1604
|
end
|
|
@@ -1306,7 +1612,8 @@ module GraphQL
|
|
|
1306
1612
|
|
|
1307
1613
|
# @api private
|
|
1308
1614
|
def add_subscription_extension_if_necessary
|
|
1309
|
-
|
|
1615
|
+
# TODO: when there's a proper API for extending root types, migrat this to use it.
|
|
1616
|
+
if !defined?(@subscription_extension_added) && @subscription_object.is_a?(Class) && self.subscriptions
|
|
1310
1617
|
@subscription_extension_added = true
|
|
1311
1618
|
subscription.all_field_definitions.each do |field|
|
|
1312
1619
|
if !field.extensions.any? { |ext| ext.is_a?(Subscriptions::DefaultSubscriptionResolveExtension) }
|
|
@@ -1316,6 +1623,11 @@ module GraphQL
|
|
|
1316
1623
|
end
|
|
1317
1624
|
end
|
|
1318
1625
|
|
|
1626
|
+
# Called when execution encounters a `SystemStackError`. By default, it adds a client-facing error to the response.
|
|
1627
|
+
# You could modify this method to report this error to your bug tracker.
|
|
1628
|
+
# @param query [GraphQL::Query]
|
|
1629
|
+
# @param err [SystemStackError]
|
|
1630
|
+
# @return [void]
|
|
1319
1631
|
def query_stack_error(query, err)
|
|
1320
1632
|
query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
|
|
1321
1633
|
end
|
|
@@ -1350,7 +1662,7 @@ module GraphQL
|
|
|
1350
1662
|
end
|
|
1351
1663
|
end
|
|
1352
1664
|
|
|
1353
|
-
# @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {
|
|
1665
|
+
# @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {.lazy_resolve}.
|
|
1354
1666
|
def lazy_method_name(obj)
|
|
1355
1667
|
lazy_methods.get(obj)
|
|
1356
1668
|
end
|
|
@@ -1374,11 +1686,215 @@ module GraphQL
|
|
|
1374
1686
|
end
|
|
1375
1687
|
end
|
|
1376
1688
|
|
|
1689
|
+
# Returns `DidYouMean` if it's defined.
|
|
1690
|
+
# Override this to return `nil` if you don't want to use `DidYouMean`
|
|
1691
|
+
def did_you_mean(new_dym = NOT_CONFIGURED)
|
|
1692
|
+
if NOT_CONFIGURED.equal?(new_dym)
|
|
1693
|
+
if defined?(@did_you_mean)
|
|
1694
|
+
@did_you_mean
|
|
1695
|
+
else
|
|
1696
|
+
find_inherited_value(:did_you_mean, defined?(DidYouMean) ? DidYouMean : nil)
|
|
1697
|
+
end
|
|
1698
|
+
else
|
|
1699
|
+
@did_you_mean = new_dym
|
|
1700
|
+
end
|
|
1701
|
+
end
|
|
1702
|
+
|
|
1703
|
+
|
|
1704
|
+
# This setting controls how GraphQL-Ruby handles empty selections on Union types.
|
|
1705
|
+
#
|
|
1706
|
+
# To opt into future, spec-compliant behavior where these selections are rejected, set this to `false`.
|
|
1707
|
+
#
|
|
1708
|
+
# If you need to support previous, non-spec behavior which allowed selecting union fields
|
|
1709
|
+
# but *not* selecting any fields on that union, set this to `true` to continue allowing that behavior.
|
|
1710
|
+
#
|
|
1711
|
+
# If this is `true`, then {.legacy_invalid_empty_selections_on_union_with_type} will be called with {Query} objects
|
|
1712
|
+
# with that kind of selections. You must implement that method
|
|
1713
|
+
# @param new_value [Boolean]
|
|
1714
|
+
# @return [true, false, nil]
|
|
1715
|
+
def allow_legacy_invalid_empty_selections_on_union(new_value = NOT_CONFIGURED)
|
|
1716
|
+
if NOT_CONFIGURED.equal?(new_value)
|
|
1717
|
+
if defined?(@allow_legacy_invalid_empty_selections_on_union)
|
|
1718
|
+
@allow_legacy_invalid_empty_selections_on_union
|
|
1719
|
+
else
|
|
1720
|
+
find_inherited_value(:allow_legacy_invalid_empty_selections_on_union)
|
|
1721
|
+
end
|
|
1722
|
+
else
|
|
1723
|
+
@allow_legacy_invalid_empty_selections_on_union = new_value
|
|
1724
|
+
end
|
|
1725
|
+
end
|
|
1726
|
+
|
|
1727
|
+
# This method is called during validation when a previously-allowed, but non-spec
|
|
1728
|
+
# query is encountered where a union field has no child selections on it.
|
|
1729
|
+
#
|
|
1730
|
+
# If `legacy_invalid_empty_selections_on_union_with_type` is overridden, this method will not be called.
|
|
1731
|
+
#
|
|
1732
|
+
# You should implement this method or `legacy_invalid_empty_selections_on_union_with_type`
|
|
1733
|
+
# to log the violation so that you can contact clients and notify them about changing their queries.
|
|
1734
|
+
# Then return a suitable value to tell GraphQL-Ruby how to continue.
|
|
1735
|
+
# @param query [GraphQL::Query]
|
|
1736
|
+
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1737
|
+
# @return [String] A validation error to return for this query
|
|
1738
|
+
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1739
|
+
def legacy_invalid_empty_selections_on_union(query)
|
|
1740
|
+
raise "Implement `def self.legacy_invalid_empty_selections_on_union_with_type(query, type)` or `def self.legacy_invalid_empty_selections_on_union(query)` to handle this scenario"
|
|
1741
|
+
end
|
|
1742
|
+
|
|
1743
|
+
# This method is called during validation when a previously-allowed, but non-spec
|
|
1744
|
+
# query is encountered where a union field has no child selections on it.
|
|
1745
|
+
#
|
|
1746
|
+
# You should implement this method to log the violation so that you can contact clients
|
|
1747
|
+
# and notify them about changing their queries. Then return a suitable value to
|
|
1748
|
+
# tell GraphQL-Ruby how to continue.
|
|
1749
|
+
# @param query [GraphQL::Query]
|
|
1750
|
+
# @param type [Module] A GraphQL type definition
|
|
1751
|
+
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1752
|
+
# @return [String] A validation error to return for this query
|
|
1753
|
+
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1754
|
+
def legacy_invalid_empty_selections_on_union_with_type(query, type)
|
|
1755
|
+
legacy_invalid_empty_selections_on_union(query)
|
|
1756
|
+
end
|
|
1757
|
+
|
|
1758
|
+
# This setting controls how GraphQL-Ruby handles overlapping selections on scalar types when the types
|
|
1759
|
+
# don't match.
|
|
1760
|
+
#
|
|
1761
|
+
# When set to `false`, GraphQL-Ruby will reject those queries with a validation error (as per the GraphQL spec).
|
|
1762
|
+
#
|
|
1763
|
+
# When set to `true`, GraphQL-Ruby will call {.legacy_invalid_return_type_conflicts} when the scenario is encountered.
|
|
1764
|
+
#
|
|
1765
|
+
# @param new_value [Boolean] `true` permits the legacy behavior, `false` rejects it.
|
|
1766
|
+
# @return [true, false, nil]
|
|
1767
|
+
def allow_legacy_invalid_return_type_conflicts(new_value = NOT_CONFIGURED)
|
|
1768
|
+
if NOT_CONFIGURED.equal?(new_value)
|
|
1769
|
+
if defined?(@allow_legacy_invalid_return_type_conflicts)
|
|
1770
|
+
@allow_legacy_invalid_return_type_conflicts
|
|
1771
|
+
else
|
|
1772
|
+
find_inherited_value(:allow_legacy_invalid_return_type_conflicts)
|
|
1773
|
+
end
|
|
1774
|
+
else
|
|
1775
|
+
@allow_legacy_invalid_return_type_conflicts = new_value
|
|
1776
|
+
end
|
|
1777
|
+
end
|
|
1778
|
+
|
|
1779
|
+
# This method is called when the query contains fields which don't contain matching scalar types.
|
|
1780
|
+
# This was previously allowed by GraphQL-Ruby but it's a violation of the GraphQL spec.
|
|
1781
|
+
#
|
|
1782
|
+
# You should implement this method to log the violation so that you observe usage of these fields.
|
|
1783
|
+
# Fixing this scenario might mean adding new fields, and telling clients to use those fields.
|
|
1784
|
+
# (Changing the field return type would be a breaking change, but if it works for your client use cases,
|
|
1785
|
+
# that might work, too.)
|
|
1786
|
+
#
|
|
1787
|
+
# @param query [GraphQL::Query]
|
|
1788
|
+
# @param type1 [Module] A GraphQL type definition
|
|
1789
|
+
# @param type2 [Module] A GraphQL type definition
|
|
1790
|
+
# @param node1 [GraphQL::Language::Nodes::Field] This node is recognized as conflicting. You might call `.line` and `.col` for custom error reporting.
|
|
1791
|
+
# @param node2 [GraphQL::Language::Nodes::Field] The other node recognized as conflicting.
|
|
1792
|
+
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1793
|
+
# @return [String] A validation error to return for this query
|
|
1794
|
+
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1795
|
+
def legacy_invalid_return_type_conflicts(query, type1, type2, node1, node2)
|
|
1796
|
+
raise "Implement #{self}.legacy_invalid_return_type_conflicts to handle this invalid selection"
|
|
1797
|
+
end
|
|
1798
|
+
|
|
1799
|
+
# The legacy complexity implementation included several bugs:
|
|
1800
|
+
#
|
|
1801
|
+
# - In some cases, it used the lexically _last_ field to determine a cost, instead of calculating the maximum among selections
|
|
1802
|
+
# - In some cases, it called field complexity hooks repeatedly (when it should have only called them once)
|
|
1803
|
+
#
|
|
1804
|
+
# The future implementation may produce higher total complexity scores, so it's not active by default yet. You can opt into
|
|
1805
|
+
# the future default behavior by configuring `:future` here. Or, you can choose a mode for each query with {.complexity_cost_calculation_mode_for}.
|
|
1806
|
+
#
|
|
1807
|
+
# The legacy mode is currently maintained alongside the future one, but it will be removed in a future GraphQL-Ruby version.
|
|
1808
|
+
#
|
|
1809
|
+
# If you choose `:compare`, you must also implement {.legacy_complexity_cost_calculation_mismatch} to handle the input somehow.
|
|
1810
|
+
#
|
|
1811
|
+
# @example Opting into the future calculation mode
|
|
1812
|
+
# complexity_cost_calculation_mode(:future)
|
|
1813
|
+
#
|
|
1814
|
+
# @example Choosing the legacy mode (which will work until that mode is removed...)
|
|
1815
|
+
# complexity_cost_calculation_mode(:legacy)
|
|
1816
|
+
#
|
|
1817
|
+
# @example Run both modes for every query, call {.legacy_complexity_cost_calculation_mismatch} when they don't match:
|
|
1818
|
+
# complexity_cost_calculation_mode(:compare)
|
|
1819
|
+
def complexity_cost_calculation_mode(new_mode = NOT_CONFIGURED)
|
|
1820
|
+
if NOT_CONFIGURED.equal?(new_mode)
|
|
1821
|
+
if defined?(@complexity_cost_calculation_mode)
|
|
1822
|
+
@complexity_cost_calculation_mode
|
|
1823
|
+
else
|
|
1824
|
+
find_inherited_value(:complexity_cost_calculation_mode)
|
|
1825
|
+
end
|
|
1826
|
+
else
|
|
1827
|
+
@complexity_cost_calculation_mode = new_mode
|
|
1828
|
+
end
|
|
1829
|
+
end
|
|
1830
|
+
|
|
1831
|
+
# Implement this method to produce a per-query complexity cost calculation mode. (Technically, it's per-multiplex.)
|
|
1832
|
+
#
|
|
1833
|
+
# This is a way to check the compatibility of queries coming to your API without adding overhead of running `:compare`
|
|
1834
|
+
# for every query. You could sample traffic, turn it off/on with feature flags, or anything else.
|
|
1835
|
+
#
|
|
1836
|
+
# @example Sampling traffic
|
|
1837
|
+
# def self.complexity_cost_calculation_mode_for(_context)
|
|
1838
|
+
# if rand < 0.1 # 10% of the time
|
|
1839
|
+
# :compare
|
|
1840
|
+
# else
|
|
1841
|
+
# :legacy
|
|
1842
|
+
# end
|
|
1843
|
+
# end
|
|
1844
|
+
#
|
|
1845
|
+
# @example Using a feature flag to manage future mode
|
|
1846
|
+
# def complexity_cost_calculation_mode_for(context)
|
|
1847
|
+
# current_user = context[:current_user]
|
|
1848
|
+
# if Flipper.enabled?(:future_complexity_cost, current_user)
|
|
1849
|
+
# :future
|
|
1850
|
+
# elsif rand < 0.5 # 50%
|
|
1851
|
+
# :compare
|
|
1852
|
+
# else
|
|
1853
|
+
# :legacy
|
|
1854
|
+
# end
|
|
1855
|
+
# end
|
|
1856
|
+
#
|
|
1857
|
+
# @param multiplex_context [Hash] The context for the currently-running {Execution::Multiplex} (which contains one or more queries)
|
|
1858
|
+
# @return [:future] Use the new calculation algorithm -- may be higher than `:legacy`
|
|
1859
|
+
# @return [:legacy] Use the legacy calculation algorithm, warts and all
|
|
1860
|
+
# @return [:compare] Run both algorithms and call {.legacy_complexity_cost_calculation_mismatch} if they don't match
|
|
1861
|
+
def complexity_cost_calculation_mode_for(multiplex_context)
|
|
1862
|
+
complexity_cost_calculation_mode
|
|
1863
|
+
end
|
|
1864
|
+
|
|
1865
|
+
# Implement this method in your schema to handle mismatches when `:compare` is used.
|
|
1866
|
+
#
|
|
1867
|
+
# @example Logging the mismatch
|
|
1868
|
+
# def self.legacy_cost_calculation_mismatch(multiplex, future_cost, legacy_cost)
|
|
1869
|
+
# client_id = multiplex.context[:api_client].id
|
|
1870
|
+
# operation_names = multiplex.queries.map { |q| q.selected_operation_name || "anonymous" }.join(", ")
|
|
1871
|
+
# Stats.increment(:complexity_mismatch, tags: { client: client_id, ops: operation_names })
|
|
1872
|
+
# legacy_cost
|
|
1873
|
+
# end
|
|
1874
|
+
# @see Query::Context#add_error Adding an error to the response to notify the client
|
|
1875
|
+
# @see Query::Context#response_extensions Adding key-value pairs to the response `"extensions" => { ... }`
|
|
1876
|
+
# @param multiplex [GraphQL::Execution::Multiplex]
|
|
1877
|
+
# @param future_complexity_cost [Integer]
|
|
1878
|
+
# @param legacy_complexity_cost [Integer]
|
|
1879
|
+
# @return [Integer] the cost to use for this query (probably one of `future_complexity_cost` or `legacy_complexity_cost`)
|
|
1880
|
+
def legacy_complexity_cost_calculation_mismatch(multiplex, future_complexity_cost, legacy_complexity_cost)
|
|
1881
|
+
raise "Implement #{self}.legacy_complexity_cost(multiplex, future_complexity_cost, legacy_complexity_cost) to handle this mismatch (#{future_complexity_cost} vs. #{legacy_complexity_cost}) and return a value to use"
|
|
1882
|
+
end
|
|
1883
|
+
|
|
1377
1884
|
private
|
|
1378
1885
|
|
|
1379
1886
|
def add_trace_options_for(mode, new_options)
|
|
1380
|
-
|
|
1381
|
-
|
|
1887
|
+
if mode == :default
|
|
1888
|
+
own_trace_modes.each do |mode_name, t_class|
|
|
1889
|
+
if t_class <= DefaultTraceClass
|
|
1890
|
+
t_opts = trace_options_for(mode_name)
|
|
1891
|
+
t_opts.merge!(new_options)
|
|
1892
|
+
end
|
|
1893
|
+
end
|
|
1894
|
+
else
|
|
1895
|
+
t_opts = trace_options_for(mode)
|
|
1896
|
+
t_opts.merge!(new_options)
|
|
1897
|
+
end
|
|
1382
1898
|
nil
|
|
1383
1899
|
end
|
|
1384
1900
|
|
|
@@ -1425,7 +1941,8 @@ module GraphQL
|
|
|
1425
1941
|
own_union_memberships.merge!(addition.union_memberships)
|
|
1426
1942
|
|
|
1427
1943
|
addition.references.each { |thing, pointers|
|
|
1428
|
-
|
|
1944
|
+
prev_refs = own_references_to[thing] || []
|
|
1945
|
+
own_references_to[thing] = prev_refs | pointers.to_a
|
|
1429
1946
|
}
|
|
1430
1947
|
|
|
1431
1948
|
addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }
|
|
@@ -1453,6 +1970,10 @@ module GraphQL
|
|
|
1453
1970
|
@own_types ||= {}
|
|
1454
1971
|
end
|
|
1455
1972
|
|
|
1973
|
+
def own_references_to
|
|
1974
|
+
@own_references_to ||= {}.compare_by_identity
|
|
1975
|
+
end
|
|
1976
|
+
|
|
1456
1977
|
def non_introspection_types
|
|
1457
1978
|
find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
|
|
1458
1979
|
end
|
|
@@ -1466,7 +1987,7 @@ module GraphQL
|
|
|
1466
1987
|
end
|
|
1467
1988
|
|
|
1468
1989
|
def own_possible_types
|
|
1469
|
-
@own_possible_types ||= {}
|
|
1990
|
+
@own_possible_types ||= {}.compare_by_identity
|
|
1470
1991
|
end
|
|
1471
1992
|
|
|
1472
1993
|
def own_union_memberships
|
|
@@ -1494,15 +2015,15 @@ module GraphQL
|
|
|
1494
2015
|
end
|
|
1495
2016
|
|
|
1496
2017
|
# This is overridden in subclasses to check the inheritance chain
|
|
1497
|
-
def get_references_to(
|
|
1498
|
-
|
|
2018
|
+
def get_references_to(type_defn)
|
|
2019
|
+
own_references_to[type_defn]
|
|
1499
2020
|
end
|
|
1500
2021
|
end
|
|
1501
2022
|
|
|
1502
2023
|
module SubclassGetReferencesTo
|
|
1503
|
-
def get_references_to(
|
|
1504
|
-
own_refs =
|
|
1505
|
-
inherited_refs = superclass.references_to(
|
|
2024
|
+
def get_references_to(type_defn)
|
|
2025
|
+
own_refs = own_references_to[type_defn]
|
|
2026
|
+
inherited_refs = superclass.references_to(type_defn)
|
|
1506
2027
|
if inherited_refs&.any?
|
|
1507
2028
|
if own_refs&.any?
|
|
1508
2029
|
own_refs + inherited_refs
|
|
@@ -1517,5 +2038,12 @@ module GraphQL
|
|
|
1517
2038
|
|
|
1518
2039
|
# Install these here so that subclasses will also install it.
|
|
1519
2040
|
self.connections = GraphQL::Pagination::Connections.new(schema: self)
|
|
2041
|
+
|
|
2042
|
+
# @api private
|
|
2043
|
+
module DefaultTraceClass
|
|
2044
|
+
end
|
|
1520
2045
|
end
|
|
1521
2046
|
end
|
|
2047
|
+
|
|
2048
|
+
require "graphql/schema/loader"
|
|
2049
|
+
require "graphql/schema/printer"
|