graphql 2.0.31 → 2.5.18
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/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +49 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +8 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +5 -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/query_depth.rb +58 -0
- 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/coercion_error.rb +1 -9
- 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 +24 -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 +161 -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 +112 -0
- data/lib/graphql/dataloader/null_dataloader.rb +51 -10
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +35 -12
- data/lib/graphql/dataloader.rb +224 -149
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/duration_encoding_error.rb +16 -0
- 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 +228 -0
- data/lib/graphql/execution/interpreter/runtime.rb +363 -434
- data/lib/graphql/execution/interpreter.rb +91 -164
- data/lib/graphql/execution/lookahead.rb +105 -31
- 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/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +8 -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/block_string.rb +34 -18
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +90 -61
- data/lib/graphql/language/lexer.rb +319 -193
- data/lib/graphql/language/nodes.rb +139 -77
- data/lib/graphql/language/parser.rb +807 -1985
- data/lib/graphql/language/printer.rb +324 -151
- data/lib/graphql/language/sanitized_printer.rb +21 -23
- data/lib/graphql/language/static_visitor.rb +171 -0
- data/lib/graphql/language/visitor.rb +62 -119
- data/lib/graphql/language.rb +71 -1
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +28 -1
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +81 -144
- data/lib/graphql/query/null_context.rb +7 -16
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query/validation_pipeline.rb +4 -4
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +126 -81
- data/lib/graphql/railtie.rb +16 -6
- data/lib/graphql/rake_task.rb +3 -12
- 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 +73 -14
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +123 -66
- data/lib/graphql/schema/directive/flagged.rb +4 -2
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive.rb +55 -3
- data/lib/graphql/schema/enum.rb +110 -27
- data/lib/graphql/schema/enum_value.rb +11 -3
- data/lib/graphql/schema/field/connection_extension.rb +2 -16
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +173 -119
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +160 -0
- data/lib/graphql/schema/input_object.rb +123 -65
- data/lib/graphql/schema/interface.rb +34 -16
- data/lib/graphql/schema/introspection_system.rb +8 -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 -4
- data/lib/graphql/schema/member/base_dsl_methods.rb +17 -1
- data/lib/graphql/schema/member/has_arguments.rb +132 -100
- 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 +5 -5
- data/lib/graphql/schema/member/has_fields.rb +111 -16
- data/lib/graphql/schema/member/has_interfaces.rb +27 -13
- 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/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +18 -5
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +33 -8
- data/lib/graphql/schema/printer.rb +9 -7
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
- data/lib/graphql/schema/resolver.rb +45 -31
- data/lib/graphql/schema/scalar.rb +4 -9
- data/lib/graphql/schema/subscription.rb +63 -10
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/union.rb +2 -2
- data/lib/graphql/schema/unique_within_type.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 +275 -103
- data/lib/graphql/schema.rb +924 -210
- data/lib/graphql/static_validation/all_rules.rb +3 -3
- data/lib/graphql/static_validation/base_visitor.rb +7 -6
- data/lib/graphql/static_validation/literal_validator.rb +6 -7
- 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 +5 -5
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
- 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 +21 -5
- data/lib/graphql/static_validation/validator.rb +9 -1
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
- 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 +21 -4
- data/lib/graphql/subscriptions/serialize.rb +3 -1
- data/lib/graphql/subscriptions.rb +21 -17
- data/lib/graphql/testing/helpers.rb +161 -0
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +3 -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 +11 -3
- data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- 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 -158
- 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 +75 -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 +184 -34
- 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_trace.rb +5 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +82 -0
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +112 -1
- data/lib/graphql/tracing.rb +31 -28
- data/lib/graphql/type_kinds.rb +2 -1
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +43 -3
- data/lib/graphql/types/relay/edge_behaviors.rb +18 -1
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/types.rb +18 -10
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +68 -54
- data/readme.md +12 -2
- metadata +220 -40
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- 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 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -276
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/backtrace/inspect_result.rb +0 -50
- data/lib/graphql/backtrace/trace.rb +0 -96
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/language/parser.y +0 -560
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- 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/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
data/lib/graphql/schema.rb
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require "logger"
|
|
2
3
|
require "graphql/schema/addition"
|
|
3
4
|
require "graphql/schema/always_visible"
|
|
4
5
|
require "graphql/schema/base_64_encoder"
|
|
5
6
|
require "graphql/schema/find_inherited_value"
|
|
6
7
|
require "graphql/schema/finder"
|
|
7
|
-
require "graphql/schema/invalid_type_error"
|
|
8
8
|
require "graphql/schema/introspection_system"
|
|
9
9
|
require "graphql/schema/late_bound_type"
|
|
10
|
-
require "graphql/schema/
|
|
10
|
+
require "graphql/schema/ractor_shareable"
|
|
11
11
|
require "graphql/schema/timeout"
|
|
12
12
|
require "graphql/schema/type_expression"
|
|
13
13
|
require "graphql/schema/unique_within_type"
|
|
@@ -37,12 +37,15 @@ require "graphql/schema/directive/skip"
|
|
|
37
37
|
require "graphql/schema/directive/feature"
|
|
38
38
|
require "graphql/schema/directive/flagged"
|
|
39
39
|
require "graphql/schema/directive/transform"
|
|
40
|
+
require "graphql/schema/directive/specified_by"
|
|
40
41
|
require "graphql/schema/type_membership"
|
|
41
42
|
|
|
42
43
|
require "graphql/schema/resolver"
|
|
43
44
|
require "graphql/schema/mutation"
|
|
45
|
+
require "graphql/schema/has_single_input_argument"
|
|
44
46
|
require "graphql/schema/relay_classic_mutation"
|
|
45
47
|
require "graphql/schema/subscription"
|
|
48
|
+
require "graphql/schema/visibility"
|
|
46
49
|
|
|
47
50
|
module GraphQL
|
|
48
51
|
# A GraphQL schema which may be queried with {GraphQL::Query}.
|
|
@@ -58,11 +61,7 @@ module GraphQL
|
|
|
58
61
|
# Any undiscoverable types may be provided with the `types` configuration.
|
|
59
62
|
#
|
|
60
63
|
# Schemas can restrict large incoming queries with `max_depth` and `max_complexity` configurations.
|
|
61
|
-
# (These configurations can be overridden by specific calls to {Schema
|
|
62
|
-
#
|
|
63
|
-
# Schemas can specify how queries should be executed against them.
|
|
64
|
-
# `query_execution_strategy`, `mutation_execution_strategy` and `subscription_execution_strategy`
|
|
65
|
-
# each apply to corresponding root types.
|
|
64
|
+
# (These configurations can be overridden by specific calls to {Schema.execute})
|
|
66
65
|
#
|
|
67
66
|
# @example defining a schema
|
|
68
67
|
# class MySchema < GraphQL::Schema
|
|
@@ -74,6 +73,9 @@ module GraphQL
|
|
|
74
73
|
class Schema
|
|
75
74
|
extend GraphQL::Schema::Member::HasAstNode
|
|
76
75
|
extend GraphQL::Schema::FindInheritedValue
|
|
76
|
+
extend Autoload
|
|
77
|
+
|
|
78
|
+
autoload :BUILT_IN_TYPES, "graphql/schema/built_in_types"
|
|
77
79
|
|
|
78
80
|
class DuplicateNamesError < GraphQL::Error
|
|
79
81
|
attr_reader :duplicated_name
|
|
@@ -110,7 +112,7 @@ module GraphQL
|
|
|
110
112
|
# @param parser [Object] An object for handling definition string parsing (must respond to `parse`)
|
|
111
113
|
# @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
|
|
112
114
|
# @return [Class] the schema described by `document`
|
|
113
|
-
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: {})
|
|
114
116
|
# If the file ends in `.graphql` or `.graphqls`, treat it like a filepath
|
|
115
117
|
if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls")
|
|
116
118
|
GraphQL::Schema::BuildFromDefinition.from_definition_path(
|
|
@@ -119,6 +121,7 @@ module GraphQL
|
|
|
119
121
|
default_resolve: default_resolve,
|
|
120
122
|
parser: parser,
|
|
121
123
|
using: using,
|
|
124
|
+
base_types: base_types,
|
|
122
125
|
)
|
|
123
126
|
else
|
|
124
127
|
GraphQL::Schema::BuildFromDefinition.from_definition(
|
|
@@ -127,6 +130,7 @@ module GraphQL
|
|
|
127
130
|
default_resolve: default_resolve,
|
|
128
131
|
parser: parser,
|
|
129
132
|
using: using,
|
|
133
|
+
base_types: base_types,
|
|
130
134
|
)
|
|
131
135
|
end
|
|
132
136
|
end
|
|
@@ -144,53 +148,95 @@ module GraphQL
|
|
|
144
148
|
@subscriptions = new_implementation
|
|
145
149
|
end
|
|
146
150
|
|
|
151
|
+
# @param new_mode [Symbol] If configured, this will be used when `context: { trace_mode: ... }` isn't set.
|
|
152
|
+
def default_trace_mode(new_mode = NOT_CONFIGURED)
|
|
153
|
+
if !NOT_CONFIGURED.equal?(new_mode)
|
|
154
|
+
@default_trace_mode = new_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
|
|
158
|
+
@default_trace_mode
|
|
159
|
+
elsif superclass.respond_to?(:default_trace_mode)
|
|
160
|
+
superclass.default_trace_mode
|
|
161
|
+
else
|
|
162
|
+
:default
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
147
166
|
def trace_class(new_class = nil)
|
|
148
167
|
if new_class
|
|
168
|
+
# If any modules were already added for `:default`,
|
|
169
|
+
# re-apply them here
|
|
170
|
+
mods = trace_modules_for(:default)
|
|
171
|
+
mods.each { |mod| new_class.include(mod) }
|
|
172
|
+
new_class.include(DefaultTraceClass)
|
|
149
173
|
trace_mode(:default, new_class)
|
|
150
|
-
backtrace_class = Class.new(new_class)
|
|
151
|
-
backtrace_class.include(GraphQL::Backtrace::Trace)
|
|
152
|
-
trace_mode(:default_backtrace, backtrace_class)
|
|
153
174
|
end
|
|
154
|
-
trace_class_for(:default)
|
|
175
|
+
trace_class_for(:default, build: true)
|
|
155
176
|
end
|
|
156
177
|
|
|
157
178
|
# @return [Class] Return the trace class to use for this mode, looking one up on the superclass if this Schema doesn't have one defined.
|
|
158
|
-
def trace_class_for(mode)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
end
|
|
168
|
-
Class.new(superclass_base_class)
|
|
169
|
-
when :default_backtrace
|
|
170
|
-
schema_base_class = trace_class_for(:default)
|
|
171
|
-
Class.new(schema_base_class) do
|
|
172
|
-
include(GraphQL::Backtrace::Trace)
|
|
173
|
-
end
|
|
174
|
-
else
|
|
175
|
-
mods = trace_modules_for(mode)
|
|
176
|
-
Class.new(trace_class_for(:default)) do
|
|
177
|
-
mods.any? && include(*mods)
|
|
178
|
-
end
|
|
179
|
-
end
|
|
179
|
+
def trace_class_for(mode, build: false)
|
|
180
|
+
if (trace_class = own_trace_modes[mode])
|
|
181
|
+
trace_class
|
|
182
|
+
elsif superclass.respond_to?(:trace_class_for) && (trace_class = superclass.trace_class_for(mode, build: false))
|
|
183
|
+
trace_class
|
|
184
|
+
elsif build
|
|
185
|
+
own_trace_modes[mode] = build_trace_mode(mode)
|
|
186
|
+
else
|
|
187
|
+
nil
|
|
180
188
|
end
|
|
181
189
|
end
|
|
182
190
|
|
|
183
191
|
# Configure `trace_class` to be used whenever `context: { trace_mode: mode_name }` is requested.
|
|
184
|
-
#
|
|
192
|
+
# {default_trace_mode} is used when no `trace_mode: ...` is requested.
|
|
193
|
+
#
|
|
194
|
+
# When a `trace_class` is added this way, it will _not_ receive other modules added with `trace_with(...)`
|
|
195
|
+
# unless `trace_mode` is explicitly given. (This class will not receive any default trace modules.)
|
|
196
|
+
#
|
|
197
|
+
# Subclasses of the schema will use `trace_class` as a base class for this mode and those
|
|
198
|
+
# subclass also will _not_ receive default tracing modules.
|
|
199
|
+
#
|
|
185
200
|
# @param mode_name [Symbol]
|
|
186
201
|
# @param trace_class [Class] subclass of GraphQL::Tracing::Trace
|
|
187
202
|
# @return void
|
|
188
203
|
def trace_mode(mode_name, trace_class)
|
|
189
|
-
|
|
190
|
-
@trace_modes[mode_name] = trace_class
|
|
204
|
+
own_trace_modes[mode_name] = trace_class
|
|
191
205
|
nil
|
|
192
206
|
end
|
|
193
207
|
|
|
208
|
+
def own_trace_modes
|
|
209
|
+
@own_trace_modes ||= {}
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def build_trace_mode(mode)
|
|
213
|
+
case mode
|
|
214
|
+
when :default
|
|
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, build: true)) || GraphQL::Tracing::Trace
|
|
217
|
+
const_set(:DefaultTrace, Class.new(base_class) do
|
|
218
|
+
include DefaultTraceClass
|
|
219
|
+
end)
|
|
220
|
+
else
|
|
221
|
+
# First, see if the superclass has a custom-defined class for this.
|
|
222
|
+
# Then, if it doesn't, use this class's default trace
|
|
223
|
+
base_class = (superclass.respond_to?(:trace_class_for) && superclass.trace_class_for(mode)) || trace_class_for(:default, build: true)
|
|
224
|
+
# Prepare the default trace class if it hasn't been initialized yet
|
|
225
|
+
base_class ||= (own_trace_modes[:default] = build_trace_mode(:default))
|
|
226
|
+
mods = trace_modules_for(mode)
|
|
227
|
+
if base_class < DefaultTraceClass
|
|
228
|
+
mods = trace_modules_for(:default) + mods
|
|
229
|
+
end
|
|
230
|
+
# Copy the existing default options into this mode's options
|
|
231
|
+
default_options = trace_options_for(:default)
|
|
232
|
+
add_trace_options_for(mode, default_options)
|
|
233
|
+
|
|
234
|
+
Class.new(base_class) do
|
|
235
|
+
!mods.empty? && include(*mods)
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
194
240
|
def own_trace_modules
|
|
195
241
|
@own_trace_modules ||= Hash.new { |h, k| h[k] = [] }
|
|
196
242
|
end
|
|
@@ -206,7 +252,7 @@ module GraphQL
|
|
|
206
252
|
|
|
207
253
|
|
|
208
254
|
# Returns the JSON response of {Introspection::INTROSPECTION_QUERY}.
|
|
209
|
-
# @see
|
|
255
|
+
# @see #as_json Return a Hash representation of the schema
|
|
210
256
|
# @return [String]
|
|
211
257
|
def to_json(**args)
|
|
212
258
|
JSON.pretty_generate(as_json(**args))
|
|
@@ -214,15 +260,13 @@ module GraphQL
|
|
|
214
260
|
|
|
215
261
|
# Return the Hash response of {Introspection::INTROSPECTION_QUERY}.
|
|
216
262
|
# @param context [Hash]
|
|
217
|
-
# @param only [<#call(member, ctx)>]
|
|
218
|
-
# @param except [<#call(member, ctx)>]
|
|
219
263
|
# @param include_deprecated_args [Boolean] If true, deprecated arguments will be included in the JSON response
|
|
220
264
|
# @param include_schema_description [Boolean] If true, the schema's description will be queried and included in the response
|
|
221
265
|
# @param include_is_repeatable [Boolean] If true, `isRepeatable: true|false` will be included with the schema's directives
|
|
222
266
|
# @param include_specified_by_url [Boolean] If true, scalar types' `specifiedByUrl:` will be included in the response
|
|
223
267
|
# @param include_is_one_of [Boolean] If true, `isOneOf: true|false` will be included with input objects
|
|
224
268
|
# @return [Hash] GraphQL result
|
|
225
|
-
def as_json(
|
|
269
|
+
def as_json(context: {}, include_deprecated_args: true, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
|
|
226
270
|
introspection_query = Introspection.query(
|
|
227
271
|
include_deprecated_args: include_deprecated_args,
|
|
228
272
|
include_schema_description: include_schema_description,
|
|
@@ -231,16 +275,14 @@ module GraphQL
|
|
|
231
275
|
include_specified_by_url: include_specified_by_url,
|
|
232
276
|
)
|
|
233
277
|
|
|
234
|
-
execute(introspection_query,
|
|
278
|
+
execute(introspection_query, context: context).to_h
|
|
235
279
|
end
|
|
236
280
|
|
|
237
281
|
# Return the GraphQL IDL for the schema
|
|
238
282
|
# @param context [Hash]
|
|
239
|
-
# @param only [<#call(member, ctx)>]
|
|
240
|
-
# @param except [<#call(member, ctx)>]
|
|
241
283
|
# @return [String]
|
|
242
|
-
def to_definition(
|
|
243
|
-
GraphQL::Schema::Printer.print_schema(self,
|
|
284
|
+
def to_definition(context: {})
|
|
285
|
+
GraphQL::Schema::Printer.print_schema(self, context: context)
|
|
244
286
|
end
|
|
245
287
|
|
|
246
288
|
# Return the GraphQL::Language::Document IDL AST for the schema
|
|
@@ -268,26 +310,15 @@ module GraphQL
|
|
|
268
310
|
@find_cache[path] ||= @finder.find(path)
|
|
269
311
|
end
|
|
270
312
|
|
|
271
|
-
def default_filter
|
|
272
|
-
GraphQL::Filter.new(except: default_mask)
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def default_mask(new_mask = nil)
|
|
276
|
-
if new_mask
|
|
277
|
-
line = caller(2, 10).find { |l| !l.include?("lib/graphql") }
|
|
278
|
-
GraphQL::Deprecation.warn("GraphQL::Filter and Schema.mask are deprecated and will be removed in v2.1.0. Implement `visible?` on your schema members instead (https://graphql-ruby.org/authorization/visibility.html).\n #{line}")
|
|
279
|
-
@own_default_mask = new_mask
|
|
280
|
-
else
|
|
281
|
-
@own_default_mask || find_inherited_value(:default_mask, Schema::NullMask)
|
|
282
|
-
end
|
|
283
|
-
end
|
|
284
|
-
|
|
285
313
|
def static_validator
|
|
286
314
|
GraphQL::StaticValidation::Validator.new(schema: self)
|
|
287
315
|
end
|
|
288
316
|
|
|
317
|
+
# Add `plugin` to this schema
|
|
318
|
+
# @param plugin [#use] A Schema plugin
|
|
319
|
+
# @return void
|
|
289
320
|
def use(plugin, **kwargs)
|
|
290
|
-
if kwargs.
|
|
321
|
+
if !kwargs.empty?
|
|
291
322
|
plugin.use(self, **kwargs)
|
|
292
323
|
else
|
|
293
324
|
plugin.use(self)
|
|
@@ -302,7 +333,11 @@ module GraphQL
|
|
|
302
333
|
# Build a map of `{ name => type }` and return it
|
|
303
334
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
|
304
335
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
|
305
|
-
def types(context = GraphQL::Query::NullContext)
|
|
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
|
|
306
341
|
all_types = non_introspection_types.merge(introspection_system.types)
|
|
307
342
|
visible_types = {}
|
|
308
343
|
all_types.each do |k, v|
|
|
@@ -328,27 +363,37 @@ module GraphQL
|
|
|
328
363
|
end
|
|
329
364
|
|
|
330
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}
|
|
331
368
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
|
332
|
-
def get_type(type_name, context = GraphQL::Query::NullContext)
|
|
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
|
|
333
374
|
local_entry = own_types[type_name]
|
|
334
375
|
type_defn = case local_entry
|
|
335
376
|
when nil
|
|
336
377
|
nil
|
|
337
378
|
when Array
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
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
|
|
348
393
|
end
|
|
349
394
|
end
|
|
395
|
+
visible_t
|
|
350
396
|
end
|
|
351
|
-
visible_t
|
|
352
397
|
when Module
|
|
353
398
|
local_entry
|
|
354
399
|
else
|
|
@@ -357,7 +402,12 @@ module GraphQL
|
|
|
357
402
|
|
|
358
403
|
type_defn ||
|
|
359
404
|
introspection_system.types[type_name] || # todo context-specific introspection?
|
|
360
|
-
(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)
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
|
|
409
|
+
def has_defined_type?(type_name)
|
|
410
|
+
own_types.key?(type_name) || introspection_system.types.key?(type_name) || (superclass.respond_to?(:has_defined_type?) ? superclass.has_defined_type?(type_name) : false)
|
|
361
411
|
end
|
|
362
412
|
|
|
363
413
|
# @api private
|
|
@@ -379,55 +429,127 @@ module GraphQL
|
|
|
379
429
|
end
|
|
380
430
|
end
|
|
381
431
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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?
|
|
388
442
|
if @query_object
|
|
389
|
-
|
|
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
|
|
390
457
|
else
|
|
391
|
-
@query_object = new_query_object
|
|
392
|
-
add_type_and_traverse(
|
|
393
|
-
nil
|
|
458
|
+
@query_object = new_query_object || lazy_load_block.call
|
|
459
|
+
add_type_and_traverse(@query_object, root: true)
|
|
394
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
|
|
395
466
|
else
|
|
396
467
|
@query_object || find_inherited_value(:query)
|
|
397
468
|
end
|
|
398
469
|
end
|
|
399
470
|
|
|
400
|
-
|
|
401
|
-
|
|
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?
|
|
402
481
|
if @mutation_object
|
|
403
|
-
|
|
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
|
|
404
496
|
else
|
|
405
|
-
@mutation_object = new_mutation_object
|
|
406
|
-
add_type_and_traverse(
|
|
407
|
-
nil
|
|
497
|
+
@mutation_object = new_mutation_object || lazy_load_block.call
|
|
498
|
+
add_type_and_traverse(@mutation_object, root: true)
|
|
408
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
|
|
409
505
|
else
|
|
410
506
|
@mutation_object || find_inherited_value(:mutation)
|
|
411
507
|
end
|
|
412
508
|
end
|
|
413
509
|
|
|
414
|
-
|
|
415
|
-
|
|
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?
|
|
416
520
|
if @subscription_object
|
|
417
|
-
|
|
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
|
|
418
536
|
else
|
|
419
|
-
@subscription_object = new_subscription_object
|
|
537
|
+
@subscription_object = new_subscription_object || lazy_load_block.call
|
|
420
538
|
add_subscription_extension_if_necessary
|
|
421
|
-
add_type_and_traverse(
|
|
422
|
-
nil
|
|
539
|
+
add_type_and_traverse(@subscription_object, root: true)
|
|
423
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
|
|
424
547
|
else
|
|
425
548
|
@subscription_object || find_inherited_value(:subscription)
|
|
426
549
|
end
|
|
427
550
|
end
|
|
428
551
|
|
|
429
|
-
# @
|
|
430
|
-
# @return [GraphQL::ObjectType, nil]
|
|
552
|
+
# @api private
|
|
431
553
|
def root_type_for_operation(operation)
|
|
432
554
|
case operation
|
|
433
555
|
when "query"
|
|
@@ -441,10 +563,16 @@ module GraphQL
|
|
|
441
563
|
end
|
|
442
564
|
end
|
|
443
565
|
|
|
566
|
+
# @return [Array<Class>] The root types (query, mutation, subscription) defined for this schema
|
|
444
567
|
def root_types
|
|
445
|
-
|
|
568
|
+
if use_visibility_profile?
|
|
569
|
+
[query, mutation, subscription].compact
|
|
570
|
+
else
|
|
571
|
+
@root_types
|
|
572
|
+
end
|
|
446
573
|
end
|
|
447
574
|
|
|
575
|
+
# @api private
|
|
448
576
|
def warden_class
|
|
449
577
|
if defined?(@warden_class)
|
|
450
578
|
@warden_class
|
|
@@ -455,18 +583,54 @@ module GraphQL
|
|
|
455
583
|
end
|
|
456
584
|
end
|
|
457
585
|
|
|
586
|
+
# @api private
|
|
458
587
|
attr_writer :warden_class
|
|
459
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
|
+
|
|
460
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}
|
|
461
618
|
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
|
462
619
|
# @return [Array<Module>] Possible types for `type`, if it's given.
|
|
463
|
-
def possible_types(type = nil, context = GraphQL::Query::NullContext)
|
|
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
|
|
464
628
|
if type
|
|
465
629
|
# TODO duck-typing `.possible_types` would probably be nicer here
|
|
466
630
|
if type.kind.union?
|
|
467
631
|
type.possible_types(context: context)
|
|
468
632
|
else
|
|
469
|
-
stored_possible_types = own_possible_types[type
|
|
633
|
+
stored_possible_types = own_possible_types[type]
|
|
470
634
|
visible_possible_types = if stored_possible_types && type.kind.interface?
|
|
471
635
|
stored_possible_types.select do |possible_type|
|
|
472
636
|
possible_type.interfaces(context).include?(type)
|
|
@@ -475,10 +639,10 @@ module GraphQL
|
|
|
475
639
|
stored_possible_types
|
|
476
640
|
end
|
|
477
641
|
visible_possible_types ||
|
|
478
|
-
introspection_system.possible_types[type
|
|
642
|
+
introspection_system.possible_types[type] ||
|
|
479
643
|
(
|
|
480
644
|
superclass.respond_to?(:possible_types) ?
|
|
481
|
-
superclass.possible_types(type, context) :
|
|
645
|
+
superclass.possible_types(type, context, use_visibility_profile) :
|
|
482
646
|
EMPTY_ARRAY
|
|
483
647
|
)
|
|
484
648
|
end
|
|
@@ -513,38 +677,45 @@ module GraphQL
|
|
|
513
677
|
attr_writer :dataloader_class
|
|
514
678
|
|
|
515
679
|
def references_to(to_type = nil, from: nil)
|
|
516
|
-
@own_references_to ||= Hash.new { |h, k| h[k] = [] }
|
|
517
680
|
if to_type
|
|
518
|
-
if !to_type.is_a?(String)
|
|
519
|
-
to_type = to_type.graphql_name
|
|
520
|
-
end
|
|
521
|
-
|
|
522
681
|
if from
|
|
523
|
-
|
|
682
|
+
refs = own_references_to[to_type] ||= []
|
|
683
|
+
refs << from
|
|
524
684
|
else
|
|
525
|
-
|
|
526
|
-
inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY
|
|
527
|
-
own_refs + inherited_refs
|
|
685
|
+
get_references_to(to_type) || EMPTY_ARRAY
|
|
528
686
|
end
|
|
529
687
|
else
|
|
530
688
|
# `@own_references_to` can be quite large for big schemas,
|
|
531
689
|
# and generally speaking, we won't inherit any values.
|
|
532
690
|
# So optimize the most common case -- don't create a duplicate Hash.
|
|
533
691
|
inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
|
|
534
|
-
if inherited_value.
|
|
535
|
-
inherited_value.merge(
|
|
692
|
+
if !inherited_value.empty?
|
|
693
|
+
inherited_value.merge(own_references_to)
|
|
536
694
|
else
|
|
537
|
-
|
|
695
|
+
own_references_to
|
|
538
696
|
end
|
|
539
697
|
end
|
|
540
698
|
end
|
|
541
699
|
|
|
542
|
-
def type_from_ast(ast_node, context:
|
|
543
|
-
|
|
544
|
-
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)
|
|
545
702
|
end
|
|
546
703
|
|
|
547
|
-
def get_field(type_or_name, field_name, context = GraphQL::Query::NullContext)
|
|
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
|
|
548
719
|
parent_type = case type_or_name
|
|
549
720
|
when LateBoundType
|
|
550
721
|
get_type(type_or_name.name, context)
|
|
@@ -567,24 +738,31 @@ module GraphQL
|
|
|
567
738
|
end
|
|
568
739
|
end
|
|
569
740
|
|
|
570
|
-
def get_fields(type, context = GraphQL::Query::NullContext)
|
|
741
|
+
def get_fields(type, context = GraphQL::Query::NullContext.instance)
|
|
571
742
|
type.fields(context)
|
|
572
743
|
end
|
|
573
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
|
|
574
748
|
def introspection(new_introspection_namespace = nil)
|
|
575
749
|
if new_introspection_namespace
|
|
576
750
|
@introspection = new_introspection_namespace
|
|
577
751
|
# reset this cached value:
|
|
578
752
|
@introspection_system = nil
|
|
753
|
+
introspection_system
|
|
754
|
+
@introspection
|
|
579
755
|
else
|
|
580
756
|
@introspection || find_inherited_value(:introspection)
|
|
581
757
|
end
|
|
582
758
|
end
|
|
583
759
|
|
|
760
|
+
# @return [Schema::IntrospectionSystem] Based on {introspection}
|
|
584
761
|
def introspection_system
|
|
585
762
|
if !@introspection_system
|
|
586
763
|
@introspection_system = Schema::IntrospectionSystem.new(self)
|
|
587
764
|
@introspection_system.resolve_late_bindings
|
|
765
|
+
self.visibility&.introspection_system_configured(@introspection_system)
|
|
588
766
|
end
|
|
589
767
|
@introspection_system
|
|
590
768
|
end
|
|
@@ -604,6 +782,17 @@ module GraphQL
|
|
|
604
782
|
end
|
|
605
783
|
end
|
|
606
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
|
+
|
|
607
796
|
def default_page_size(new_default_page_size = nil)
|
|
608
797
|
if new_default_page_size
|
|
609
798
|
@default_page_size = new_default_page_size
|
|
@@ -612,39 +801,51 @@ module GraphQL
|
|
|
612
801
|
end
|
|
613
802
|
end
|
|
614
803
|
|
|
615
|
-
def query_execution_strategy(new_query_execution_strategy = nil)
|
|
804
|
+
def query_execution_strategy(new_query_execution_strategy = nil, deprecation_warning: true)
|
|
805
|
+
if deprecation_warning
|
|
806
|
+
warn "GraphQL::Schema.query_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
|
807
|
+
warn " #{caller(1, 1).first}"
|
|
808
|
+
end
|
|
616
809
|
if new_query_execution_strategy
|
|
617
810
|
@query_execution_strategy = new_query_execution_strategy
|
|
618
811
|
else
|
|
619
|
-
@query_execution_strategy ||
|
|
812
|
+
@query_execution_strategy || (superclass.respond_to?(:query_execution_strategy) ? superclass.query_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
|
620
813
|
end
|
|
621
814
|
end
|
|
622
815
|
|
|
623
|
-
def mutation_execution_strategy(new_mutation_execution_strategy = nil)
|
|
816
|
+
def mutation_execution_strategy(new_mutation_execution_strategy = nil, deprecation_warning: true)
|
|
817
|
+
if deprecation_warning
|
|
818
|
+
warn "GraphQL::Schema.mutation_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
|
819
|
+
warn " #{caller(1, 1).first}"
|
|
820
|
+
end
|
|
624
821
|
if new_mutation_execution_strategy
|
|
625
822
|
@mutation_execution_strategy = new_mutation_execution_strategy
|
|
626
823
|
else
|
|
627
|
-
@mutation_execution_strategy ||
|
|
824
|
+
@mutation_execution_strategy || (superclass.respond_to?(:mutation_execution_strategy) ? superclass.mutation_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
|
628
825
|
end
|
|
629
826
|
end
|
|
630
827
|
|
|
631
|
-
def subscription_execution_strategy(new_subscription_execution_strategy = nil)
|
|
828
|
+
def subscription_execution_strategy(new_subscription_execution_strategy = nil, deprecation_warning: true)
|
|
829
|
+
if deprecation_warning
|
|
830
|
+
warn "GraphQL::Schema.subscription_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
|
831
|
+
warn " #{caller(1, 1).first}"
|
|
832
|
+
end
|
|
632
833
|
if new_subscription_execution_strategy
|
|
633
834
|
@subscription_execution_strategy = new_subscription_execution_strategy
|
|
634
835
|
else
|
|
635
|
-
@subscription_execution_strategy ||
|
|
836
|
+
@subscription_execution_strategy || (superclass.respond_to?(:subscription_execution_strategy) ? superclass.subscription_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
|
636
837
|
end
|
|
637
838
|
end
|
|
638
839
|
|
|
639
840
|
attr_writer :validate_timeout
|
|
640
841
|
|
|
641
|
-
def validate_timeout(new_validate_timeout =
|
|
642
|
-
if new_validate_timeout
|
|
842
|
+
def validate_timeout(new_validate_timeout = NOT_CONFIGURED)
|
|
843
|
+
if !NOT_CONFIGURED.equal?(new_validate_timeout)
|
|
643
844
|
@validate_timeout = new_validate_timeout
|
|
644
845
|
elsif defined?(@validate_timeout)
|
|
645
846
|
@validate_timeout
|
|
646
847
|
else
|
|
647
|
-
find_inherited_value(:validate_timeout)
|
|
848
|
+
find_inherited_value(:validate_timeout) || 3
|
|
648
849
|
end
|
|
649
850
|
end
|
|
650
851
|
|
|
@@ -657,7 +858,7 @@ module GraphQL
|
|
|
657
858
|
else
|
|
658
859
|
string_or_document
|
|
659
860
|
end
|
|
660
|
-
query =
|
|
861
|
+
query = query_class.new(self, document: doc, context: context)
|
|
661
862
|
validator_opts = { schema: self }
|
|
662
863
|
rules && (validator_opts[:rules] = rules)
|
|
663
864
|
validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
|
|
@@ -665,23 +866,31 @@ module GraphQL
|
|
|
665
866
|
res[:errors]
|
|
666
867
|
end
|
|
667
868
|
|
|
869
|
+
# @param new_query_class [Class<GraphQL::Query>] A subclass to use when executing queries
|
|
870
|
+
def query_class(new_query_class = NOT_CONFIGURED)
|
|
871
|
+
if NOT_CONFIGURED.equal?(new_query_class)
|
|
872
|
+
@query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
|
|
873
|
+
else
|
|
874
|
+
@query_class = new_query_class
|
|
875
|
+
end
|
|
876
|
+
end
|
|
877
|
+
|
|
668
878
|
attr_writer :validate_max_errors
|
|
669
879
|
|
|
670
|
-
def validate_max_errors(new_validate_max_errors =
|
|
671
|
-
if new_validate_max_errors
|
|
672
|
-
@validate_max_errors
|
|
673
|
-
elsif defined?(@validate_max_errors)
|
|
674
|
-
@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)
|
|
675
883
|
else
|
|
676
|
-
|
|
884
|
+
@validate_max_errors = new_validate_max_errors
|
|
677
885
|
end
|
|
678
886
|
end
|
|
679
887
|
|
|
680
888
|
attr_writer :max_complexity
|
|
681
889
|
|
|
682
|
-
def max_complexity(max_complexity = nil)
|
|
890
|
+
def max_complexity(max_complexity = nil, count_introspection_fields: true)
|
|
683
891
|
if max_complexity
|
|
684
892
|
@max_complexity = max_complexity
|
|
893
|
+
@max_complexity_count_introspection_fields = count_introspection_fields
|
|
685
894
|
elsif defined?(@max_complexity)
|
|
686
895
|
@max_complexity
|
|
687
896
|
else
|
|
@@ -689,24 +898,23 @@ module GraphQL
|
|
|
689
898
|
end
|
|
690
899
|
end
|
|
691
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
|
+
|
|
692
909
|
attr_writer :analysis_engine
|
|
693
910
|
|
|
694
911
|
def analysis_engine
|
|
695
912
|
@analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
|
|
696
913
|
end
|
|
697
914
|
|
|
698
|
-
def using_ast_analysis?
|
|
699
|
-
true
|
|
700
|
-
end
|
|
701
|
-
|
|
702
|
-
def interpreter?
|
|
703
|
-
true
|
|
704
|
-
end
|
|
705
|
-
|
|
706
|
-
attr_writer :interpreter
|
|
707
|
-
|
|
708
915
|
def error_bubbling(new_error_bubbling = nil)
|
|
709
916
|
if !new_error_bubbling.nil?
|
|
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")
|
|
710
918
|
@error_bubbling = new_error_bubbling
|
|
711
919
|
else
|
|
712
920
|
@error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
|
|
@@ -717,9 +925,10 @@ module GraphQL
|
|
|
717
925
|
|
|
718
926
|
attr_writer :max_depth
|
|
719
927
|
|
|
720
|
-
def max_depth(new_max_depth = nil)
|
|
928
|
+
def max_depth(new_max_depth = nil, count_introspection_fields: true)
|
|
721
929
|
if new_max_depth
|
|
722
930
|
@max_depth = new_max_depth
|
|
931
|
+
@count_introspection_fields = count_introspection_fields
|
|
723
932
|
elsif defined?(@max_depth)
|
|
724
933
|
@max_depth
|
|
725
934
|
else
|
|
@@ -727,6 +936,14 @@ module GraphQL
|
|
|
727
936
|
end
|
|
728
937
|
end
|
|
729
938
|
|
|
939
|
+
def count_introspection_fields
|
|
940
|
+
if defined?(@count_introspection_fields)
|
|
941
|
+
@count_introspection_fields
|
|
942
|
+
else
|
|
943
|
+
find_inherited_value(:count_introspection_fields, true)
|
|
944
|
+
end
|
|
945
|
+
end
|
|
946
|
+
|
|
730
947
|
def disable_introspection_entry_points
|
|
731
948
|
@disable_introspection_entry_points = true
|
|
732
949
|
# TODO: this clears the cache made in `def types`. But this is not a great solution.
|
|
@@ -769,14 +986,62 @@ module GraphQL
|
|
|
769
986
|
end
|
|
770
987
|
end
|
|
771
988
|
|
|
989
|
+
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
|
990
|
+
# @return [Array<Module>] Type definitions added to this schema
|
|
991
|
+
def extra_types(*new_extra_types)
|
|
992
|
+
if !new_extra_types.empty?
|
|
993
|
+
new_extra_types = new_extra_types.flatten
|
|
994
|
+
@own_extra_types ||= []
|
|
995
|
+
@own_extra_types.concat(new_extra_types)
|
|
996
|
+
end
|
|
997
|
+
inherited_et = find_inherited_value(:extra_types, nil)
|
|
998
|
+
if inherited_et
|
|
999
|
+
if @own_extra_types
|
|
1000
|
+
inherited_et + @own_extra_types
|
|
1001
|
+
else
|
|
1002
|
+
inherited_et
|
|
1003
|
+
end
|
|
1004
|
+
else
|
|
1005
|
+
@own_extra_types || EMPTY_ARRAY
|
|
1006
|
+
end
|
|
1007
|
+
end
|
|
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
|
|
772
1016
|
def orphan_types(*new_orphan_types)
|
|
773
|
-
if new_orphan_types.
|
|
1017
|
+
if !new_orphan_types.empty?
|
|
774
1018
|
new_orphan_types = new_orphan_types.flatten
|
|
775
|
-
|
|
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?
|
|
776
1031
|
own_orphan_types.concat(new_orphan_types.flatten)
|
|
1032
|
+
self.visibility&.orphan_types_configured(new_orphan_types)
|
|
777
1033
|
end
|
|
778
1034
|
|
|
779
|
-
find_inherited_value(:orphan_types,
|
|
1035
|
+
inherited_ot = find_inherited_value(:orphan_types, nil)
|
|
1036
|
+
if inherited_ot
|
|
1037
|
+
if !own_orphan_types.empty?
|
|
1038
|
+
inherited_ot + own_orphan_types
|
|
1039
|
+
else
|
|
1040
|
+
inherited_ot
|
|
1041
|
+
end
|
|
1042
|
+
else
|
|
1043
|
+
own_orphan_types
|
|
1044
|
+
end
|
|
780
1045
|
end
|
|
781
1046
|
|
|
782
1047
|
def default_execution_strategy
|
|
@@ -795,6 +1060,41 @@ module GraphQL
|
|
|
795
1060
|
end
|
|
796
1061
|
end
|
|
797
1062
|
|
|
1063
|
+
|
|
1064
|
+
# @param new_default_logger [#log] Something to use for logging messages
|
|
1065
|
+
def default_logger(new_default_logger = NOT_CONFIGURED)
|
|
1066
|
+
if NOT_CONFIGURED.equal?(new_default_logger)
|
|
1067
|
+
if defined?(@default_logger)
|
|
1068
|
+
@default_logger
|
|
1069
|
+
elsif superclass.respond_to?(:default_logger)
|
|
1070
|
+
superclass.default_logger
|
|
1071
|
+
elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger)
|
|
1072
|
+
rails_logger
|
|
1073
|
+
else
|
|
1074
|
+
def_logger = Logger.new($stdout)
|
|
1075
|
+
def_logger.info! # It doesn't output debug info by default
|
|
1076
|
+
def_logger
|
|
1077
|
+
end
|
|
1078
|
+
elsif new_default_logger == nil
|
|
1079
|
+
@default_logger = Logger.new(IO::NULL)
|
|
1080
|
+
else
|
|
1081
|
+
@default_logger = new_default_logger
|
|
1082
|
+
end
|
|
1083
|
+
end
|
|
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
|
|
798
1098
|
def context_class(new_context_class = nil)
|
|
799
1099
|
if new_context_class
|
|
800
1100
|
@context_class = new_context_class
|
|
@@ -803,28 +1103,46 @@ module GraphQL
|
|
|
803
1103
|
end
|
|
804
1104
|
end
|
|
805
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
|
|
806
1120
|
def rescue_from(*err_classes, &handler_block)
|
|
807
1121
|
err_classes.each do |err_class|
|
|
808
1122
|
Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block)
|
|
809
1123
|
end
|
|
810
1124
|
end
|
|
811
1125
|
|
|
812
|
-
NEW_HANDLER_HASH = ->(h, k) {
|
|
813
|
-
h[k] = {
|
|
814
|
-
class: k,
|
|
815
|
-
handler: nil,
|
|
816
|
-
subclass_handlers: Hash.new(&NEW_HANDLER_HASH),
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
|
|
820
1126
|
def error_handlers
|
|
821
|
-
@error_handlers ||=
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
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
|
|
826
1141
|
end
|
|
827
1142
|
|
|
1143
|
+
# @api private
|
|
1144
|
+
attr_accessor :using_backtrace
|
|
1145
|
+
|
|
828
1146
|
# @api private
|
|
829
1147
|
def handle_or_reraise(context, err)
|
|
830
1148
|
handler = Execution::Errors.find_handler_for(self, err.class)
|
|
@@ -838,6 +1156,10 @@ module GraphQL
|
|
|
838
1156
|
end
|
|
839
1157
|
handler[:handler].call(err, obj, args, context, field)
|
|
840
1158
|
else
|
|
1159
|
+
if (context[:backtrace] || using_backtrace) && !err.is_a?(GraphQL::ExecutionError)
|
|
1160
|
+
err = GraphQL::Backtrace::TracedError.new(err, context)
|
|
1161
|
+
end
|
|
1162
|
+
|
|
841
1163
|
raise err
|
|
842
1164
|
end
|
|
843
1165
|
end
|
|
@@ -869,29 +1191,77 @@ module GraphQL
|
|
|
869
1191
|
end
|
|
870
1192
|
end
|
|
871
1193
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
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})"
|
|
878
1212
|
end
|
|
879
1213
|
# rubocop:enable Lint/DuplicateMethods
|
|
880
1214
|
|
|
881
1215
|
def inherited(child_class)
|
|
882
1216
|
if self == GraphQL::Schema
|
|
883
1217
|
child_class.directives(default_directives.values)
|
|
1218
|
+
child_class.extend(SubclassGetReferencesTo)
|
|
1219
|
+
end
|
|
1220
|
+
# Make sure the child class has these built out, so that
|
|
1221
|
+
# subclasses can be modified by later calls to `trace_with`
|
|
1222
|
+
own_trace_modes.each do |name, _class|
|
|
1223
|
+
child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
|
|
884
1224
|
end
|
|
885
1225
|
child_class.singleton_class.prepend(ResolveTypeWithType)
|
|
886
|
-
super
|
|
887
|
-
end
|
|
888
1226
|
|
|
889
|
-
|
|
890
|
-
|
|
1227
|
+
if use_visibility_profile?
|
|
1228
|
+
vis = self.visibility
|
|
1229
|
+
child_class.visibility = vis.dup_for(child_class)
|
|
1230
|
+
end
|
|
1231
|
+
super
|
|
891
1232
|
end
|
|
892
1233
|
|
|
893
|
-
|
|
894
|
-
|
|
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}`)"
|
|
895
1265
|
end
|
|
896
1266
|
|
|
897
1267
|
def visible?(member, ctx)
|
|
@@ -907,6 +1277,10 @@ module GraphQL
|
|
|
907
1277
|
Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives)
|
|
908
1278
|
end
|
|
909
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
|
|
910
1284
|
# This hook is called when an object fails an `authorized?` check.
|
|
911
1285
|
# You might report to your bug tracker here, so you can correct
|
|
912
1286
|
# the field resolvers not to return unauthorized objects.
|
|
@@ -942,10 +1316,23 @@ module GraphQL
|
|
|
942
1316
|
unauthorized_object(unauthorized_error)
|
|
943
1317
|
end
|
|
944
1318
|
|
|
945
|
-
|
|
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)
|
|
946
1330
|
case type_error
|
|
947
1331
|
when GraphQL::InvalidNullError
|
|
948
|
-
|
|
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
|
|
949
1336
|
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
|
|
950
1337
|
raise type_error
|
|
951
1338
|
when GraphQL::IntegerDecodingError
|
|
@@ -953,7 +1340,7 @@ module GraphQL
|
|
|
953
1340
|
end
|
|
954
1341
|
end
|
|
955
1342
|
|
|
956
|
-
# A function to call when {
|
|
1343
|
+
# A function to call when {.execute} receives an invalid query string
|
|
957
1344
|
#
|
|
958
1345
|
# The default is to add the error to `context.errors`
|
|
959
1346
|
# @param parse_err [GraphQL::ParseError] The error encountered during parsing
|
|
@@ -968,24 +1355,39 @@ module GraphQL
|
|
|
968
1355
|
end
|
|
969
1356
|
|
|
970
1357
|
def instrument(instrument_step, instrumenter, options = {})
|
|
1358
|
+
warn <<~WARN
|
|
1359
|
+
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
|
1360
|
+
(From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
|
|
1361
|
+
|
|
1362
|
+
WARN
|
|
1363
|
+
trace_with(Tracing::LegacyHooksTrace)
|
|
971
1364
|
own_instrumenters[instrument_step] << instrumenter
|
|
972
1365
|
end
|
|
973
1366
|
|
|
974
1367
|
# Add several directives at once
|
|
975
1368
|
# @param new_directives [Class]
|
|
976
1369
|
def directives(*new_directives)
|
|
977
|
-
if new_directives.
|
|
1370
|
+
if !new_directives.empty?
|
|
978
1371
|
new_directives.flatten.each { |d| directive(d) }
|
|
979
1372
|
end
|
|
980
1373
|
|
|
981
|
-
find_inherited_value(:directives, default_directives)
|
|
1374
|
+
inherited_dirs = find_inherited_value(:directives, default_directives)
|
|
1375
|
+
if !own_directives.empty?
|
|
1376
|
+
inherited_dirs.merge(own_directives)
|
|
1377
|
+
else
|
|
1378
|
+
inherited_dirs
|
|
1379
|
+
end
|
|
982
1380
|
end
|
|
983
1381
|
|
|
984
1382
|
# Attach a single directive to this schema
|
|
985
1383
|
# @param new_directive [Class]
|
|
986
1384
|
# @return void
|
|
987
1385
|
def directive(new_directive)
|
|
988
|
-
|
|
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
|
|
989
1391
|
end
|
|
990
1392
|
|
|
991
1393
|
def default_directives
|
|
@@ -994,11 +1396,27 @@ module GraphQL
|
|
|
994
1396
|
"skip" => GraphQL::Schema::Directive::Skip,
|
|
995
1397
|
"deprecated" => GraphQL::Schema::Directive::Deprecated,
|
|
996
1398
|
"oneOf" => GraphQL::Schema::Directive::OneOf,
|
|
1399
|
+
"specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
|
|
997
1400
|
}.freeze
|
|
998
1401
|
end
|
|
999
1402
|
|
|
1000
|
-
|
|
1001
|
-
|
|
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
|
|
1418
|
+
default_trace = trace_class_for(:default, build: true)
|
|
1419
|
+
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
|
1002
1420
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
|
1003
1421
|
end
|
|
1004
1422
|
|
|
@@ -1009,24 +1427,46 @@ module GraphQL
|
|
|
1009
1427
|
find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers
|
|
1010
1428
|
end
|
|
1011
1429
|
|
|
1012
|
-
# Mix `trace_mod` into this schema's `Trace` class so that its methods
|
|
1013
|
-
#
|
|
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
|
|
1014
1440
|
#
|
|
1015
1441
|
# @param trace_mod [Module] A module that implements tracing methods
|
|
1016
1442
|
# @param mode [Symbol] Trace module will only be used for this trade mode
|
|
1017
1443
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
|
1018
1444
|
# @return [void]
|
|
1445
|
+
# @see GraphQL::Tracing::Trace Tracing::Trace for available tracing methods
|
|
1019
1446
|
def trace_with(trace_mod, mode: :default, **options)
|
|
1020
1447
|
if mode.is_a?(Array)
|
|
1021
1448
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
|
1022
1449
|
else
|
|
1023
|
-
tc =
|
|
1450
|
+
tc = own_trace_modes[mode] ||= build_trace_mode(mode)
|
|
1024
1451
|
tc.include(trace_mod)
|
|
1025
|
-
|
|
1026
|
-
|
|
1452
|
+
own_trace_modules[mode] << trace_mod
|
|
1453
|
+
add_trace_options_for(mode, options)
|
|
1454
|
+
if mode == :default
|
|
1455
|
+
# This module is being added as a default tracer. If any other mode classes
|
|
1456
|
+
# have already been created, but get their default behavior from a superclass,
|
|
1457
|
+
# Then mix this into this schema's subclass.
|
|
1458
|
+
# (But don't mix it into mode classes that aren't default-based.)
|
|
1459
|
+
own_trace_modes.each do |other_mode_name, other_mode_class|
|
|
1460
|
+
if other_mode_class < DefaultTraceClass
|
|
1461
|
+
# Don't add it back to the inheritance tree if it's already there
|
|
1462
|
+
if !(other_mode_class < trace_mod)
|
|
1463
|
+
other_mode_class.include(trace_mod)
|
|
1464
|
+
end
|
|
1465
|
+
# Add any options so they'll be available
|
|
1466
|
+
add_trace_options_for(other_mode_name, options)
|
|
1467
|
+
end
|
|
1468
|
+
end
|
|
1027
1469
|
end
|
|
1028
|
-
t_opts = trace_options_for(mode)
|
|
1029
|
-
t_opts.merge!(options)
|
|
1030
1470
|
end
|
|
1031
1471
|
nil
|
|
1032
1472
|
end
|
|
@@ -1036,37 +1476,59 @@ module GraphQL
|
|
|
1036
1476
|
def trace_options_for(mode)
|
|
1037
1477
|
@trace_options_for_mode ||= {}
|
|
1038
1478
|
@trace_options_for_mode[mode] ||= begin
|
|
1479
|
+
# It may be time to create an options hash for a mode that wasn't registered yet.
|
|
1480
|
+
# Mix in the default options in that case.
|
|
1481
|
+
default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default)
|
|
1482
|
+
# Make sure this returns a new object so that other hashes aren't modified later
|
|
1039
1483
|
if superclass.respond_to?(:trace_options_for)
|
|
1040
|
-
superclass.trace_options_for(mode).
|
|
1484
|
+
superclass.trace_options_for(mode).merge(default_options)
|
|
1041
1485
|
else
|
|
1042
|
-
|
|
1486
|
+
default_options.dup
|
|
1043
1487
|
end
|
|
1044
1488
|
end
|
|
1045
1489
|
end
|
|
1046
1490
|
|
|
1047
1491
|
# Create a trace instance which will include the trace modules specified for the optional mode.
|
|
1048
1492
|
#
|
|
1493
|
+
# If no `mode:` is given, then {default_trace_mode} will be used.
|
|
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
|
+
#
|
|
1049
1498
|
# @param mode [Symbol] Trace modules for this trade mode will be included
|
|
1050
1499
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
|
1051
1500
|
# @return [Tracing::Trace]
|
|
1052
1501
|
def new_trace(mode: nil, **options)
|
|
1053
|
-
|
|
1054
|
-
|
|
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
|
|
1511
|
+
end
|
|
1512
|
+
else
|
|
1513
|
+
false
|
|
1514
|
+
end
|
|
1055
1515
|
|
|
1056
|
-
|
|
1057
|
-
mode
|
|
1058
|
-
elsif target && target.context[:backtrace]
|
|
1059
|
-
:default_backtrace
|
|
1516
|
+
if should_sample
|
|
1517
|
+
mode = detailed_trace.trace_mode
|
|
1060
1518
|
else
|
|
1061
|
-
:
|
|
1519
|
+
target = options[:query] || options[:multiplex]
|
|
1520
|
+
mode ||= target && target.context[:trace_mode]
|
|
1062
1521
|
end
|
|
1063
1522
|
|
|
1523
|
+
trace_mode = mode || default_trace_mode
|
|
1064
1524
|
base_trace_options = trace_options_for(trace_mode)
|
|
1065
1525
|
trace_options = base_trace_options.merge(options)
|
|
1066
|
-
trace_class_for_mode = trace_class_for(trace_mode)
|
|
1526
|
+
trace_class_for_mode = trace_class_for(trace_mode, build: true)
|
|
1067
1527
|
trace_class_for_mode.new(**trace_options)
|
|
1068
1528
|
end
|
|
1069
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
|
|
1070
1532
|
def query_analyzer(new_analyzer)
|
|
1071
1533
|
own_query_analyzers << new_analyzer
|
|
1072
1534
|
end
|
|
@@ -1075,6 +1537,8 @@ module GraphQL
|
|
|
1075
1537
|
find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
|
|
1076
1538
|
end
|
|
1077
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
|
|
1078
1542
|
def multiplex_analyzer(new_analyzer)
|
|
1079
1543
|
own_multiplex_analyzers << new_analyzer
|
|
1080
1544
|
end
|
|
@@ -1093,7 +1557,7 @@ module GraphQL
|
|
|
1093
1557
|
|
|
1094
1558
|
# Execute a query on itself.
|
|
1095
1559
|
# @see {Query#initialize} for arguments.
|
|
1096
|
-
# @return [
|
|
1560
|
+
# @return [GraphQL::Query::Result] query result, ready to be serialized as JSON
|
|
1097
1561
|
def execute(query_str = nil, **kwargs)
|
|
1098
1562
|
if query_str
|
|
1099
1563
|
kwargs[:query] = query_str
|
|
@@ -1132,8 +1596,9 @@ module GraphQL
|
|
|
1132
1596
|
# @see {Query#initialize} for query keyword arguments
|
|
1133
1597
|
# @see {Execution::Multiplex#run_all} for multiplex keyword arguments
|
|
1134
1598
|
# @param queries [Array<Hash>] Keyword arguments for each query
|
|
1135
|
-
# @
|
|
1136
|
-
# @
|
|
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
|
|
1137
1602
|
def multiplex(queries, **kwargs)
|
|
1138
1603
|
GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
|
|
1139
1604
|
end
|
|
@@ -1147,7 +1612,8 @@ module GraphQL
|
|
|
1147
1612
|
|
|
1148
1613
|
# @api private
|
|
1149
1614
|
def add_subscription_extension_if_necessary
|
|
1150
|
-
|
|
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
|
|
1151
1617
|
@subscription_extension_added = true
|
|
1152
1618
|
subscription.all_field_definitions.each do |field|
|
|
1153
1619
|
if !field.extensions.any? { |ext| ext.is_a?(Subscriptions::DefaultSubscriptionResolveExtension) }
|
|
@@ -1157,6 +1623,11 @@ module GraphQL
|
|
|
1157
1623
|
end
|
|
1158
1624
|
end
|
|
1159
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]
|
|
1160
1631
|
def query_stack_error(query, err)
|
|
1161
1632
|
query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
|
|
1162
1633
|
end
|
|
@@ -1191,7 +1662,7 @@ module GraphQL
|
|
|
1191
1662
|
end
|
|
1192
1663
|
end
|
|
1193
1664
|
|
|
1194
|
-
# @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}.
|
|
1195
1666
|
def lazy_method_name(obj)
|
|
1196
1667
|
lazy_methods.get(obj)
|
|
1197
1668
|
end
|
|
@@ -1215,8 +1686,218 @@ module GraphQL
|
|
|
1215
1686
|
end
|
|
1216
1687
|
end
|
|
1217
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
|
+
|
|
1218
1884
|
private
|
|
1219
1885
|
|
|
1886
|
+
def add_trace_options_for(mode, new_options)
|
|
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
|
|
1898
|
+
nil
|
|
1899
|
+
end
|
|
1900
|
+
|
|
1220
1901
|
# @param t [Module, Array<Module>]
|
|
1221
1902
|
# @return [void]
|
|
1222
1903
|
def add_type_and_traverse(t, root:)
|
|
@@ -1260,7 +1941,8 @@ module GraphQL
|
|
|
1260
1941
|
own_union_memberships.merge!(addition.union_memberships)
|
|
1261
1942
|
|
|
1262
1943
|
addition.references.each { |thing, pointers|
|
|
1263
|
-
|
|
1944
|
+
prev_refs = own_references_to[thing] || []
|
|
1945
|
+
own_references_to[thing] = prev_refs | pointers.to_a
|
|
1264
1946
|
}
|
|
1265
1947
|
|
|
1266
1948
|
addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }
|
|
@@ -1278,7 +1960,7 @@ module GraphQL
|
|
|
1278
1960
|
else
|
|
1279
1961
|
@lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
|
|
1280
1962
|
@lazy_methods.set(GraphQL::Execution::Lazy, :value)
|
|
1281
|
-
@lazy_methods.set(GraphQL::Dataloader::Request, :
|
|
1963
|
+
@lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
|
|
1282
1964
|
end
|
|
1283
1965
|
end
|
|
1284
1966
|
@lazy_methods
|
|
@@ -1288,6 +1970,10 @@ module GraphQL
|
|
|
1288
1970
|
@own_types ||= {}
|
|
1289
1971
|
end
|
|
1290
1972
|
|
|
1973
|
+
def own_references_to
|
|
1974
|
+
@own_references_to ||= {}.compare_by_identity
|
|
1975
|
+
end
|
|
1976
|
+
|
|
1291
1977
|
def non_introspection_types
|
|
1292
1978
|
find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
|
|
1293
1979
|
end
|
|
@@ -1301,7 +1987,7 @@ module GraphQL
|
|
|
1301
1987
|
end
|
|
1302
1988
|
|
|
1303
1989
|
def own_possible_types
|
|
1304
|
-
@own_possible_types ||= {}
|
|
1990
|
+
@own_possible_types ||= {}.compare_by_identity
|
|
1305
1991
|
end
|
|
1306
1992
|
|
|
1307
1993
|
def own_union_memberships
|
|
@@ -1327,9 +2013,37 @@ module GraphQL
|
|
|
1327
2013
|
def own_multiplex_analyzers
|
|
1328
2014
|
@own_multiplex_analyzers ||= []
|
|
1329
2015
|
end
|
|
2016
|
+
|
|
2017
|
+
# This is overridden in subclasses to check the inheritance chain
|
|
2018
|
+
def get_references_to(type_defn)
|
|
2019
|
+
own_references_to[type_defn]
|
|
2020
|
+
end
|
|
2021
|
+
end
|
|
2022
|
+
|
|
2023
|
+
module SubclassGetReferencesTo
|
|
2024
|
+
def get_references_to(type_defn)
|
|
2025
|
+
own_refs = own_references_to[type_defn]
|
|
2026
|
+
inherited_refs = superclass.references_to(type_defn)
|
|
2027
|
+
if inherited_refs&.any?
|
|
2028
|
+
if own_refs&.any?
|
|
2029
|
+
own_refs + inherited_refs
|
|
2030
|
+
else
|
|
2031
|
+
inherited_refs
|
|
2032
|
+
end
|
|
2033
|
+
else
|
|
2034
|
+
own_refs
|
|
2035
|
+
end
|
|
2036
|
+
end
|
|
1330
2037
|
end
|
|
1331
2038
|
|
|
1332
2039
|
# Install these here so that subclasses will also install it.
|
|
1333
2040
|
self.connections = GraphQL::Pagination::Connections.new(schema: self)
|
|
2041
|
+
|
|
2042
|
+
# @api private
|
|
2043
|
+
module DefaultTraceClass
|
|
2044
|
+
end
|
|
1334
2045
|
end
|
|
1335
2046
|
end
|
|
2047
|
+
|
|
2048
|
+
require "graphql/schema/loader"
|
|
2049
|
+
require "graphql/schema/printer"
|