graphql 2.0.32 → 2.5.22
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/detailed_trace_generator.rb +77 -0
- 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/create_graphql_detailed_traces.erb +10 -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/application_controller.rb +41 -0
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -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/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +97 -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 +96 -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 +55 -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 +7 -6
- data/lib/graphql/execution/next/field_resolve_step.rb +711 -0
- data/lib/graphql/execution/next/load_argument_step.rb +60 -0
- data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
- data/lib/graphql/execution/next/runner.rb +389 -0
- data/lib/graphql/execution/next/selections_step.rb +37 -0
- data/lib/graphql/execution/next.rb +70 -0
- data/lib/graphql/execution.rb +1 -0
- data/lib/graphql/execution_error.rb +13 -10
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +20 -6
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/schema_type.rb +8 -11
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +26 -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 +136 -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 +23 -83
- 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 +30 -1
- data/lib/graphql/pagination/connections.rb +32 -0
- 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 +82 -144
- data/lib/graphql/query/null_context.rb +15 -18
- 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 +75 -9
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +123 -60
- 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 +54 -2
- data/lib/graphql/schema/enum.rb +110 -27
- data/lib/graphql/schema/enum_value.rb +10 -2
- data/lib/graphql/schema/field/connection_extension.rb +15 -49
- data/lib/graphql/schema/field/scope_extension.rb +23 -7
- data/lib/graphql/schema/field.rb +245 -118
- data/lib/graphql/schema/field_extension.rb +34 -1
- data/lib/graphql/schema/has_single_input_argument.rb +160 -0
- data/lib/graphql/schema/input_object.rb +116 -60
- 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 +18 -2
- data/lib/graphql/schema/member/has_arguments.rb +132 -100
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +99 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +4 -4
- data/lib/graphql/schema/member/has_fields.rb +115 -15
- data/lib/graphql/schema/member/has_interfaces.rb +26 -12
- 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 +17 -4
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +34 -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 +90 -32
- 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 +950 -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 +7 -3
- data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
- data/lib/graphql/tracing/appsignal_trace.rb +32 -59
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -162
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -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 +156 -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 +183 -37
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +864 -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 +73 -73
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -58
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +82 -0
- data/lib/graphql/tracing/statsd_trace.rb +33 -45
- 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 +44 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +18 -0
- data/lib/graphql/types/relay/has_node_field.rb +13 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
- data/lib/graphql/types/relay/node_behaviors.rb +13 -2
- 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/unauthorized_error.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +71 -54
- data/readme.md +12 -2
- metadata +233 -37
- 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)
|
|
@@ -299,10 +330,20 @@ module GraphQL
|
|
|
299
330
|
find_inherited_value(:plugins, EMPTY_ARRAY) + own_plugins
|
|
300
331
|
end
|
|
301
332
|
|
|
333
|
+
attr_writer :null_context
|
|
334
|
+
|
|
335
|
+
def null_context
|
|
336
|
+
@null_context || GraphQL::Query::NullContext.instance
|
|
337
|
+
end
|
|
338
|
+
|
|
302
339
|
# Build a map of `{ name => type }` and return it
|
|
303
340
|
# @return [Hash<String => Class>] A dictionary of type classes by their GraphQL name
|
|
304
341
|
# @see get_type Which is more efficient for finding _one type_ by name, because it doesn't merge hashes.
|
|
305
|
-
def types(context =
|
|
342
|
+
def types(context = null_context)
|
|
343
|
+
if use_visibility_profile?
|
|
344
|
+
types = Visibility::Profile.from_context(context, self)
|
|
345
|
+
return types.all_types_h
|
|
346
|
+
end
|
|
306
347
|
all_types = non_introspection_types.merge(introspection_system.types)
|
|
307
348
|
visible_types = {}
|
|
308
349
|
all_types.each do |k, v|
|
|
@@ -328,27 +369,37 @@ module GraphQL
|
|
|
328
369
|
end
|
|
329
370
|
|
|
330
371
|
# @param type_name [String]
|
|
372
|
+
# @param context [GraphQL::Query::Context] Used for filtering definitions at query-time
|
|
373
|
+
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
331
374
|
# @return [Module, nil] A type, or nil if there's no type called `type_name`
|
|
332
|
-
def get_type(type_name, context =
|
|
375
|
+
def get_type(type_name, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
376
|
+
if use_visibility_profile
|
|
377
|
+
profile = Visibility::Profile.from_context(context, self)
|
|
378
|
+
return profile.type(type_name)
|
|
379
|
+
end
|
|
333
380
|
local_entry = own_types[type_name]
|
|
334
381
|
type_defn = case local_entry
|
|
335
382
|
when nil
|
|
336
383
|
nil
|
|
337
384
|
when Array
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
385
|
+
if context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
|
|
386
|
+
local_entry
|
|
387
|
+
else
|
|
388
|
+
visible_t = nil
|
|
389
|
+
warden = Warden.from_context(context)
|
|
390
|
+
local_entry.each do |t|
|
|
391
|
+
if warden.visible_type?(t, context)
|
|
392
|
+
if visible_t.nil?
|
|
393
|
+
visible_t = t
|
|
394
|
+
else
|
|
395
|
+
raise DuplicateNamesError.new(
|
|
396
|
+
duplicated_name: type_name, duplicated_definition_1: visible_t.inspect, duplicated_definition_2: t.inspect
|
|
397
|
+
)
|
|
398
|
+
end
|
|
348
399
|
end
|
|
349
400
|
end
|
|
401
|
+
visible_t
|
|
350
402
|
end
|
|
351
|
-
visible_t
|
|
352
403
|
when Module
|
|
353
404
|
local_entry
|
|
354
405
|
else
|
|
@@ -357,7 +408,12 @@ module GraphQL
|
|
|
357
408
|
|
|
358
409
|
type_defn ||
|
|
359
410
|
introspection_system.types[type_name] || # todo context-specific introspection?
|
|
360
|
-
(superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context) : nil)
|
|
411
|
+
(superclass.respond_to?(:get_type) ? superclass.get_type(type_name, context, use_visibility_profile) : nil)
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# @return [Boolean] Does this schema have _any_ definition for a type named `type_name`, regardless of visibility?
|
|
415
|
+
def has_defined_type?(type_name)
|
|
416
|
+
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
417
|
end
|
|
362
418
|
|
|
363
419
|
# @api private
|
|
@@ -379,55 +435,127 @@ module GraphQL
|
|
|
379
435
|
end
|
|
380
436
|
end
|
|
381
437
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
438
|
+
# Get or set the root `query { ... }` object for this schema.
|
|
439
|
+
#
|
|
440
|
+
# @example Using `Types::Query` as the entry-point
|
|
441
|
+
# query { Types::Query }
|
|
442
|
+
#
|
|
443
|
+
# @param new_query_object [Class<GraphQL::Schema::Object>] The root type to use for queries
|
|
444
|
+
# @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root query type.
|
|
445
|
+
# @return [Class<GraphQL::Schema::Object>, nil] The configured query root type, if there is one.
|
|
446
|
+
def query(new_query_object = nil, &lazy_load_block)
|
|
447
|
+
if new_query_object || block_given?
|
|
388
448
|
if @query_object
|
|
389
|
-
|
|
449
|
+
dup_defn = new_query_object || yield
|
|
450
|
+
raise GraphQL::Error, "Second definition of `query(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@query_object.inspect}"
|
|
451
|
+
elsif use_visibility_profile?
|
|
452
|
+
if block_given?
|
|
453
|
+
if visibility.preload?
|
|
454
|
+
@query_object = lazy_load_block.call
|
|
455
|
+
self.visibility.query_configured(@query_object)
|
|
456
|
+
else
|
|
457
|
+
@query_object = lazy_load_block
|
|
458
|
+
end
|
|
459
|
+
else
|
|
460
|
+
@query_object = new_query_object
|
|
461
|
+
self.visibility.query_configured(@query_object)
|
|
462
|
+
end
|
|
390
463
|
else
|
|
391
|
-
@query_object = new_query_object
|
|
392
|
-
add_type_and_traverse(
|
|
393
|
-
nil
|
|
464
|
+
@query_object = new_query_object || lazy_load_block.call
|
|
465
|
+
add_type_and_traverse(@query_object, root: true)
|
|
394
466
|
end
|
|
467
|
+
nil
|
|
468
|
+
elsif @query_object.is_a?(Proc)
|
|
469
|
+
@query_object = @query_object.call
|
|
470
|
+
self.visibility&.query_configured(@query_object)
|
|
471
|
+
@query_object
|
|
395
472
|
else
|
|
396
473
|
@query_object || find_inherited_value(:query)
|
|
397
474
|
end
|
|
398
475
|
end
|
|
399
476
|
|
|
400
|
-
|
|
401
|
-
|
|
477
|
+
# Get or set the root `mutation { ... }` object for this schema.
|
|
478
|
+
#
|
|
479
|
+
# @example Using `Types::Mutation` as the entry-point
|
|
480
|
+
# mutation { Types::Mutation }
|
|
481
|
+
#
|
|
482
|
+
# @param new_mutation_object [Class<GraphQL::Schema::Object>] The root type to use for mutations
|
|
483
|
+
# @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root mutation type.
|
|
484
|
+
# @return [Class<GraphQL::Schema::Object>, nil] The configured mutation root type, if there is one.
|
|
485
|
+
def mutation(new_mutation_object = nil, &lazy_load_block)
|
|
486
|
+
if new_mutation_object || block_given?
|
|
402
487
|
if @mutation_object
|
|
403
|
-
|
|
488
|
+
dup_defn = new_mutation_object || yield
|
|
489
|
+
raise GraphQL::Error, "Second definition of `mutation(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@mutation_object.inspect}"
|
|
490
|
+
elsif use_visibility_profile?
|
|
491
|
+
if block_given?
|
|
492
|
+
if visibility.preload?
|
|
493
|
+
@mutation_object = lazy_load_block.call
|
|
494
|
+
self.visibility.mutation_configured(@mutation_object)
|
|
495
|
+
else
|
|
496
|
+
@mutation_object = lazy_load_block
|
|
497
|
+
end
|
|
498
|
+
else
|
|
499
|
+
@mutation_object = new_mutation_object
|
|
500
|
+
self.visibility.mutation_configured(@mutation_object)
|
|
501
|
+
end
|
|
404
502
|
else
|
|
405
|
-
@mutation_object = new_mutation_object
|
|
406
|
-
add_type_and_traverse(
|
|
407
|
-
nil
|
|
503
|
+
@mutation_object = new_mutation_object || lazy_load_block.call
|
|
504
|
+
add_type_and_traverse(@mutation_object, root: true)
|
|
408
505
|
end
|
|
506
|
+
nil
|
|
507
|
+
elsif @mutation_object.is_a?(Proc)
|
|
508
|
+
@mutation_object = @mutation_object.call
|
|
509
|
+
self.visibility&.mutation_configured(@mutation_object)
|
|
510
|
+
@mutation_object
|
|
409
511
|
else
|
|
410
512
|
@mutation_object || find_inherited_value(:mutation)
|
|
411
513
|
end
|
|
412
514
|
end
|
|
413
515
|
|
|
414
|
-
|
|
415
|
-
|
|
516
|
+
# Get or set the root `subscription { ... }` object for this schema.
|
|
517
|
+
#
|
|
518
|
+
# @example Using `Types::Subscription` as the entry-point
|
|
519
|
+
# subscription { Types::Subscription }
|
|
520
|
+
#
|
|
521
|
+
# @param new_subscription_object [Class<GraphQL::Schema::Object>] The root type to use for subscriptions
|
|
522
|
+
# @param lazy_load_block If a block is given, then it will be called when GraphQL-Ruby needs the root subscription type.
|
|
523
|
+
# @return [Class<GraphQL::Schema::Object>, nil] The configured subscription root type, if there is one.
|
|
524
|
+
def subscription(new_subscription_object = nil, &lazy_load_block)
|
|
525
|
+
if new_subscription_object || block_given?
|
|
416
526
|
if @subscription_object
|
|
417
|
-
|
|
527
|
+
dup_defn = new_subscription_object || yield
|
|
528
|
+
raise GraphQL::Error, "Second definition of `subscription(...)` (#{dup_defn.inspect}) is invalid, already configured with #{@subscription_object.inspect}"
|
|
529
|
+
elsif use_visibility_profile?
|
|
530
|
+
if block_given?
|
|
531
|
+
if visibility.preload?
|
|
532
|
+
@subscription_object = lazy_load_block.call
|
|
533
|
+
visibility.subscription_configured(@subscription_object)
|
|
534
|
+
else
|
|
535
|
+
@subscription_object = lazy_load_block
|
|
536
|
+
end
|
|
537
|
+
else
|
|
538
|
+
@subscription_object = new_subscription_object
|
|
539
|
+
self.visibility.subscription_configured(@subscription_object)
|
|
540
|
+
end
|
|
541
|
+
add_subscription_extension_if_necessary
|
|
418
542
|
else
|
|
419
|
-
@subscription_object = new_subscription_object
|
|
543
|
+
@subscription_object = new_subscription_object || lazy_load_block.call
|
|
420
544
|
add_subscription_extension_if_necessary
|
|
421
|
-
add_type_and_traverse(
|
|
422
|
-
nil
|
|
545
|
+
add_type_and_traverse(@subscription_object, root: true)
|
|
423
546
|
end
|
|
547
|
+
nil
|
|
548
|
+
elsif @subscription_object.is_a?(Proc)
|
|
549
|
+
@subscription_object = @subscription_object.call
|
|
550
|
+
add_subscription_extension_if_necessary
|
|
551
|
+
self.visibility.subscription_configured(@subscription_object)
|
|
552
|
+
@subscription_object
|
|
424
553
|
else
|
|
425
554
|
@subscription_object || find_inherited_value(:subscription)
|
|
426
555
|
end
|
|
427
556
|
end
|
|
428
557
|
|
|
429
|
-
# @
|
|
430
|
-
# @return [GraphQL::ObjectType, nil]
|
|
558
|
+
# @api private
|
|
431
559
|
def root_type_for_operation(operation)
|
|
432
560
|
case operation
|
|
433
561
|
when "query"
|
|
@@ -441,10 +569,16 @@ module GraphQL
|
|
|
441
569
|
end
|
|
442
570
|
end
|
|
443
571
|
|
|
572
|
+
# @return [Array<Class>] The root types (query, mutation, subscription) defined for this schema
|
|
444
573
|
def root_types
|
|
445
|
-
|
|
574
|
+
if use_visibility_profile?
|
|
575
|
+
[query, mutation, subscription].compact
|
|
576
|
+
else
|
|
577
|
+
@root_types
|
|
578
|
+
end
|
|
446
579
|
end
|
|
447
580
|
|
|
581
|
+
# @api private
|
|
448
582
|
def warden_class
|
|
449
583
|
if defined?(@warden_class)
|
|
450
584
|
@warden_class
|
|
@@ -455,18 +589,54 @@ module GraphQL
|
|
|
455
589
|
end
|
|
456
590
|
end
|
|
457
591
|
|
|
592
|
+
# @api private
|
|
458
593
|
attr_writer :warden_class
|
|
459
594
|
|
|
595
|
+
# @api private
|
|
596
|
+
def visibility_profile_class
|
|
597
|
+
if defined?(@visibility_profile_class)
|
|
598
|
+
@visibility_profile_class
|
|
599
|
+
elsif superclass.respond_to?(:visibility_profile_class)
|
|
600
|
+
superclass.visibility_profile_class
|
|
601
|
+
else
|
|
602
|
+
GraphQL::Schema::Visibility::Profile
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
# @api private
|
|
607
|
+
attr_writer :visibility_profile_class, :use_visibility_profile
|
|
608
|
+
# @api private
|
|
609
|
+
attr_accessor :visibility
|
|
610
|
+
# @api private
|
|
611
|
+
def use_visibility_profile?
|
|
612
|
+
if defined?(@use_visibility_profile)
|
|
613
|
+
@use_visibility_profile
|
|
614
|
+
elsif superclass.respond_to?(:use_visibility_profile?)
|
|
615
|
+
superclass.use_visibility_profile?
|
|
616
|
+
else
|
|
617
|
+
false
|
|
618
|
+
end
|
|
619
|
+
end
|
|
620
|
+
|
|
460
621
|
# @param type [Module] The type definition whose possible types you want to see
|
|
622
|
+
# @param context [GraphQL::Query::Context] used for filtering visible possible types at runtime
|
|
623
|
+
# @param use_visibility_profile Private, for migration to {Schema::Visibility}
|
|
461
624
|
# @return [Hash<String, Module>] All possible types, if no `type` is given.
|
|
462
625
|
# @return [Array<Module>] Possible types for `type`, if it's given.
|
|
463
|
-
def possible_types(type = nil, context =
|
|
626
|
+
def possible_types(type = nil, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
627
|
+
if use_visibility_profile
|
|
628
|
+
if type
|
|
629
|
+
return Visibility::Profile.from_context(context, self).possible_types(type)
|
|
630
|
+
else
|
|
631
|
+
raise "Schema.possible_types is not implemented for `use_visibility_profile?`"
|
|
632
|
+
end
|
|
633
|
+
end
|
|
464
634
|
if type
|
|
465
635
|
# TODO duck-typing `.possible_types` would probably be nicer here
|
|
466
636
|
if type.kind.union?
|
|
467
637
|
type.possible_types(context: context)
|
|
468
638
|
else
|
|
469
|
-
stored_possible_types = own_possible_types[type
|
|
639
|
+
stored_possible_types = own_possible_types[type]
|
|
470
640
|
visible_possible_types = if stored_possible_types && type.kind.interface?
|
|
471
641
|
stored_possible_types.select do |possible_type|
|
|
472
642
|
possible_type.interfaces(context).include?(type)
|
|
@@ -475,10 +645,10 @@ module GraphQL
|
|
|
475
645
|
stored_possible_types
|
|
476
646
|
end
|
|
477
647
|
visible_possible_types ||
|
|
478
|
-
introspection_system.possible_types[type
|
|
648
|
+
introspection_system.possible_types[type] ||
|
|
479
649
|
(
|
|
480
650
|
superclass.respond_to?(:possible_types) ?
|
|
481
|
-
superclass.possible_types(type, context) :
|
|
651
|
+
superclass.possible_types(type, context, use_visibility_profile) :
|
|
482
652
|
EMPTY_ARRAY
|
|
483
653
|
)
|
|
484
654
|
end
|
|
@@ -513,38 +683,45 @@ module GraphQL
|
|
|
513
683
|
attr_writer :dataloader_class
|
|
514
684
|
|
|
515
685
|
def references_to(to_type = nil, from: nil)
|
|
516
|
-
@own_references_to ||= Hash.new { |h, k| h[k] = [] }
|
|
517
686
|
if to_type
|
|
518
|
-
if !to_type.is_a?(String)
|
|
519
|
-
to_type = to_type.graphql_name
|
|
520
|
-
end
|
|
521
|
-
|
|
522
687
|
if from
|
|
523
|
-
|
|
688
|
+
refs = own_references_to[to_type] ||= []
|
|
689
|
+
refs << from
|
|
524
690
|
else
|
|
525
|
-
|
|
526
|
-
inherited_refs = find_inherited_value(:references_to, EMPTY_HASH)[to_type] || EMPTY_ARRAY
|
|
527
|
-
own_refs + inherited_refs
|
|
691
|
+
get_references_to(to_type) || EMPTY_ARRAY
|
|
528
692
|
end
|
|
529
693
|
else
|
|
530
694
|
# `@own_references_to` can be quite large for big schemas,
|
|
531
695
|
# and generally speaking, we won't inherit any values.
|
|
532
696
|
# So optimize the most common case -- don't create a duplicate Hash.
|
|
533
697
|
inherited_value = find_inherited_value(:references_to, EMPTY_HASH)
|
|
534
|
-
if inherited_value.
|
|
535
|
-
inherited_value.merge(
|
|
698
|
+
if !inherited_value.empty?
|
|
699
|
+
inherited_value.merge(own_references_to)
|
|
536
700
|
else
|
|
537
|
-
|
|
701
|
+
own_references_to
|
|
538
702
|
end
|
|
539
703
|
end
|
|
540
704
|
end
|
|
541
705
|
|
|
542
|
-
def type_from_ast(ast_node, context:
|
|
543
|
-
|
|
544
|
-
GraphQL::Schema::TypeExpression.build_type(type_owner, ast_node)
|
|
706
|
+
def type_from_ast(ast_node, context: self.query_class.new(self, "{ __typename }").context)
|
|
707
|
+
GraphQL::Schema::TypeExpression.build_type(context.query.types, ast_node)
|
|
545
708
|
end
|
|
546
709
|
|
|
547
|
-
def get_field(type_or_name, field_name, context =
|
|
710
|
+
def get_field(type_or_name, field_name, context = null_context, use_visibility_profile = use_visibility_profile?)
|
|
711
|
+
if use_visibility_profile
|
|
712
|
+
profile = Visibility::Profile.from_context(context, self)
|
|
713
|
+
parent_type = case type_or_name
|
|
714
|
+
when String
|
|
715
|
+
profile.type(type_or_name)
|
|
716
|
+
when Module
|
|
717
|
+
type_or_name
|
|
718
|
+
when LateBoundType
|
|
719
|
+
profile.type(type_or_name.name)
|
|
720
|
+
else
|
|
721
|
+
raise GraphQL::InvariantError, "Unexpected field owner for #{field_name.inspect}: #{type_or_name.inspect} (#{type_or_name.class})"
|
|
722
|
+
end
|
|
723
|
+
return profile.field(parent_type, field_name)
|
|
724
|
+
end
|
|
548
725
|
parent_type = case type_or_name
|
|
549
726
|
when LateBoundType
|
|
550
727
|
get_type(type_or_name.name, context)
|
|
@@ -567,24 +744,31 @@ module GraphQL
|
|
|
567
744
|
end
|
|
568
745
|
end
|
|
569
746
|
|
|
570
|
-
def get_fields(type, context =
|
|
747
|
+
def get_fields(type, context = null_context)
|
|
571
748
|
type.fields(context)
|
|
572
749
|
end
|
|
573
750
|
|
|
751
|
+
# Pass a custom introspection module here to use it for this schema.
|
|
752
|
+
# @param new_introspection_namespace [Module] If given, use this module for custom introspection on the schema
|
|
753
|
+
# @return [Module, nil] The configured namespace, if there is one
|
|
574
754
|
def introspection(new_introspection_namespace = nil)
|
|
575
755
|
if new_introspection_namespace
|
|
576
756
|
@introspection = new_introspection_namespace
|
|
577
757
|
# reset this cached value:
|
|
578
758
|
@introspection_system = nil
|
|
759
|
+
introspection_system
|
|
760
|
+
@introspection
|
|
579
761
|
else
|
|
580
762
|
@introspection || find_inherited_value(:introspection)
|
|
581
763
|
end
|
|
582
764
|
end
|
|
583
765
|
|
|
766
|
+
# @return [Schema::IntrospectionSystem] Based on {introspection}
|
|
584
767
|
def introspection_system
|
|
585
768
|
if !@introspection_system
|
|
586
769
|
@introspection_system = Schema::IntrospectionSystem.new(self)
|
|
587
770
|
@introspection_system.resolve_late_bindings
|
|
771
|
+
self.visibility&.introspection_system_configured(@introspection_system)
|
|
588
772
|
end
|
|
589
773
|
@introspection_system
|
|
590
774
|
end
|
|
@@ -604,6 +788,17 @@ module GraphQL
|
|
|
604
788
|
end
|
|
605
789
|
end
|
|
606
790
|
|
|
791
|
+
# A limit on the number of tokens to accept on incoming query strings.
|
|
792
|
+
# Use this to prevent parsing maliciously-large query strings.
|
|
793
|
+
# @return [nil, Integer]
|
|
794
|
+
def max_query_string_tokens(new_max_tokens = NOT_CONFIGURED)
|
|
795
|
+
if NOT_CONFIGURED.equal?(new_max_tokens)
|
|
796
|
+
defined?(@max_query_string_tokens) ? @max_query_string_tokens : find_inherited_value(:max_query_string_tokens)
|
|
797
|
+
else
|
|
798
|
+
@max_query_string_tokens = new_max_tokens
|
|
799
|
+
end
|
|
800
|
+
end
|
|
801
|
+
|
|
607
802
|
def default_page_size(new_default_page_size = nil)
|
|
608
803
|
if new_default_page_size
|
|
609
804
|
@default_page_size = new_default_page_size
|
|
@@ -612,39 +807,51 @@ module GraphQL
|
|
|
612
807
|
end
|
|
613
808
|
end
|
|
614
809
|
|
|
615
|
-
def query_execution_strategy(new_query_execution_strategy = nil)
|
|
810
|
+
def query_execution_strategy(new_query_execution_strategy = nil, deprecation_warning: true)
|
|
811
|
+
if deprecation_warning
|
|
812
|
+
warn "GraphQL::Schema.query_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
|
813
|
+
warn " #{caller(1, 1).first}"
|
|
814
|
+
end
|
|
616
815
|
if new_query_execution_strategy
|
|
617
816
|
@query_execution_strategy = new_query_execution_strategy
|
|
618
817
|
else
|
|
619
|
-
@query_execution_strategy ||
|
|
818
|
+
@query_execution_strategy || (superclass.respond_to?(:query_execution_strategy) ? superclass.query_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
|
620
819
|
end
|
|
621
820
|
end
|
|
622
821
|
|
|
623
|
-
def mutation_execution_strategy(new_mutation_execution_strategy = nil)
|
|
822
|
+
def mutation_execution_strategy(new_mutation_execution_strategy = nil, deprecation_warning: true)
|
|
823
|
+
if deprecation_warning
|
|
824
|
+
warn "GraphQL::Schema.mutation_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
|
825
|
+
warn " #{caller(1, 1).first}"
|
|
826
|
+
end
|
|
624
827
|
if new_mutation_execution_strategy
|
|
625
828
|
@mutation_execution_strategy = new_mutation_execution_strategy
|
|
626
829
|
else
|
|
627
|
-
@mutation_execution_strategy ||
|
|
830
|
+
@mutation_execution_strategy || (superclass.respond_to?(:mutation_execution_strategy) ? superclass.mutation_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
|
628
831
|
end
|
|
629
832
|
end
|
|
630
833
|
|
|
631
|
-
def subscription_execution_strategy(new_subscription_execution_strategy = nil)
|
|
834
|
+
def subscription_execution_strategy(new_subscription_execution_strategy = nil, deprecation_warning: true)
|
|
835
|
+
if deprecation_warning
|
|
836
|
+
warn "GraphQL::Schema.subscription_execution_strategy is deprecated without replacement. Use `GraphQL::Query.new` directly to create and execute a custom query instead."
|
|
837
|
+
warn " #{caller(1, 1).first}"
|
|
838
|
+
end
|
|
632
839
|
if new_subscription_execution_strategy
|
|
633
840
|
@subscription_execution_strategy = new_subscription_execution_strategy
|
|
634
841
|
else
|
|
635
|
-
@subscription_execution_strategy ||
|
|
842
|
+
@subscription_execution_strategy || (superclass.respond_to?(:subscription_execution_strategy) ? superclass.subscription_execution_strategy(deprecation_warning: false) : self.default_execution_strategy)
|
|
636
843
|
end
|
|
637
844
|
end
|
|
638
845
|
|
|
639
846
|
attr_writer :validate_timeout
|
|
640
847
|
|
|
641
|
-
def validate_timeout(new_validate_timeout =
|
|
642
|
-
if new_validate_timeout
|
|
848
|
+
def validate_timeout(new_validate_timeout = NOT_CONFIGURED)
|
|
849
|
+
if !NOT_CONFIGURED.equal?(new_validate_timeout)
|
|
643
850
|
@validate_timeout = new_validate_timeout
|
|
644
851
|
elsif defined?(@validate_timeout)
|
|
645
852
|
@validate_timeout
|
|
646
853
|
else
|
|
647
|
-
find_inherited_value(:validate_timeout)
|
|
854
|
+
find_inherited_value(:validate_timeout) || 3
|
|
648
855
|
end
|
|
649
856
|
end
|
|
650
857
|
|
|
@@ -657,7 +864,7 @@ module GraphQL
|
|
|
657
864
|
else
|
|
658
865
|
string_or_document
|
|
659
866
|
end
|
|
660
|
-
query =
|
|
867
|
+
query = query_class.new(self, document: doc, context: context)
|
|
661
868
|
validator_opts = { schema: self }
|
|
662
869
|
rules && (validator_opts[:rules] = rules)
|
|
663
870
|
validator = GraphQL::StaticValidation::Validator.new(**validator_opts)
|
|
@@ -665,23 +872,31 @@ module GraphQL
|
|
|
665
872
|
res[:errors]
|
|
666
873
|
end
|
|
667
874
|
|
|
875
|
+
# @param new_query_class [Class<GraphQL::Query>] A subclass to use when executing queries
|
|
876
|
+
def query_class(new_query_class = NOT_CONFIGURED)
|
|
877
|
+
if NOT_CONFIGURED.equal?(new_query_class)
|
|
878
|
+
@query_class || (superclass.respond_to?(:query_class) ? superclass.query_class : GraphQL::Query)
|
|
879
|
+
else
|
|
880
|
+
@query_class = new_query_class
|
|
881
|
+
end
|
|
882
|
+
end
|
|
883
|
+
|
|
668
884
|
attr_writer :validate_max_errors
|
|
669
885
|
|
|
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
|
|
886
|
+
def validate_max_errors(new_validate_max_errors = NOT_CONFIGURED)
|
|
887
|
+
if NOT_CONFIGURED.equal?(new_validate_max_errors)
|
|
888
|
+
defined?(@validate_max_errors) ? @validate_max_errors : find_inherited_value(:validate_max_errors)
|
|
675
889
|
else
|
|
676
|
-
|
|
890
|
+
@validate_max_errors = new_validate_max_errors
|
|
677
891
|
end
|
|
678
892
|
end
|
|
679
893
|
|
|
680
894
|
attr_writer :max_complexity
|
|
681
895
|
|
|
682
|
-
def max_complexity(max_complexity = nil)
|
|
896
|
+
def max_complexity(max_complexity = nil, count_introspection_fields: true)
|
|
683
897
|
if max_complexity
|
|
684
898
|
@max_complexity = max_complexity
|
|
899
|
+
@max_complexity_count_introspection_fields = count_introspection_fields
|
|
685
900
|
elsif defined?(@max_complexity)
|
|
686
901
|
@max_complexity
|
|
687
902
|
else
|
|
@@ -689,24 +904,23 @@ module GraphQL
|
|
|
689
904
|
end
|
|
690
905
|
end
|
|
691
906
|
|
|
907
|
+
def max_complexity_count_introspection_fields
|
|
908
|
+
if defined?(@max_complexity_count_introspection_fields)
|
|
909
|
+
@max_complexity_count_introspection_fields
|
|
910
|
+
else
|
|
911
|
+
find_inherited_value(:max_complexity_count_introspection_fields, true)
|
|
912
|
+
end
|
|
913
|
+
end
|
|
914
|
+
|
|
692
915
|
attr_writer :analysis_engine
|
|
693
916
|
|
|
694
917
|
def analysis_engine
|
|
695
918
|
@analysis_engine || find_inherited_value(:analysis_engine, self.default_analysis_engine)
|
|
696
919
|
end
|
|
697
920
|
|
|
698
|
-
def using_ast_analysis?
|
|
699
|
-
true
|
|
700
|
-
end
|
|
701
|
-
|
|
702
|
-
def interpreter?
|
|
703
|
-
true
|
|
704
|
-
end
|
|
705
|
-
|
|
706
|
-
attr_writer :interpreter
|
|
707
|
-
|
|
708
921
|
def error_bubbling(new_error_bubbling = nil)
|
|
709
922
|
if !new_error_bubbling.nil?
|
|
923
|
+
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
924
|
@error_bubbling = new_error_bubbling
|
|
711
925
|
else
|
|
712
926
|
@error_bubbling.nil? ? find_inherited_value(:error_bubbling) : @error_bubbling
|
|
@@ -717,9 +931,10 @@ module GraphQL
|
|
|
717
931
|
|
|
718
932
|
attr_writer :max_depth
|
|
719
933
|
|
|
720
|
-
def max_depth(new_max_depth = nil)
|
|
934
|
+
def max_depth(new_max_depth = nil, count_introspection_fields: true)
|
|
721
935
|
if new_max_depth
|
|
722
936
|
@max_depth = new_max_depth
|
|
937
|
+
@count_introspection_fields = count_introspection_fields
|
|
723
938
|
elsif defined?(@max_depth)
|
|
724
939
|
@max_depth
|
|
725
940
|
else
|
|
@@ -727,6 +942,14 @@ module GraphQL
|
|
|
727
942
|
end
|
|
728
943
|
end
|
|
729
944
|
|
|
945
|
+
def count_introspection_fields
|
|
946
|
+
if defined?(@count_introspection_fields)
|
|
947
|
+
@count_introspection_fields
|
|
948
|
+
else
|
|
949
|
+
find_inherited_value(:count_introspection_fields, true)
|
|
950
|
+
end
|
|
951
|
+
end
|
|
952
|
+
|
|
730
953
|
def disable_introspection_entry_points
|
|
731
954
|
@disable_introspection_entry_points = true
|
|
732
955
|
# TODO: this clears the cache made in `def types`. But this is not a great solution.
|
|
@@ -769,14 +992,62 @@ module GraphQL
|
|
|
769
992
|
end
|
|
770
993
|
end
|
|
771
994
|
|
|
995
|
+
# @param new_extra_types [Module] Type definitions to include in printing and introspection, even though they aren't referenced in the schema
|
|
996
|
+
# @return [Array<Module>] Type definitions added to this schema
|
|
997
|
+
def extra_types(*new_extra_types)
|
|
998
|
+
if !new_extra_types.empty?
|
|
999
|
+
new_extra_types = new_extra_types.flatten
|
|
1000
|
+
@own_extra_types ||= []
|
|
1001
|
+
@own_extra_types.concat(new_extra_types)
|
|
1002
|
+
end
|
|
1003
|
+
inherited_et = find_inherited_value(:extra_types, nil)
|
|
1004
|
+
if inherited_et
|
|
1005
|
+
if @own_extra_types
|
|
1006
|
+
inherited_et + @own_extra_types
|
|
1007
|
+
else
|
|
1008
|
+
inherited_et
|
|
1009
|
+
end
|
|
1010
|
+
else
|
|
1011
|
+
@own_extra_types || EMPTY_ARRAY
|
|
1012
|
+
end
|
|
1013
|
+
end
|
|
1014
|
+
|
|
1015
|
+
# Tell the schema about these types so that they can be registered as implementations of interfaces in the schema.
|
|
1016
|
+
#
|
|
1017
|
+
# This method must be used when an object type is connected to the schema as an interface implementor but
|
|
1018
|
+
# 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.
|
|
1019
|
+
#
|
|
1020
|
+
# @param new_orphan_types [Array<Class<GraphQL::Schema::Object>>] Object types to register as implementations of interfaces in the schema.
|
|
1021
|
+
# @return [Array<Class<GraphQL::Schema::Object>>] All previously-registered orphan types for this schema
|
|
772
1022
|
def orphan_types(*new_orphan_types)
|
|
773
|
-
if new_orphan_types.
|
|
1023
|
+
if !new_orphan_types.empty?
|
|
774
1024
|
new_orphan_types = new_orphan_types.flatten
|
|
775
|
-
|
|
1025
|
+
non_object_types = new_orphan_types.reject { |ot| ot.is_a?(Class) && ot < GraphQL::Schema::Object }
|
|
1026
|
+
if !non_object_types.empty?
|
|
1027
|
+
raise ArgumentError, <<~ERR
|
|
1028
|
+
Only object type classes should be added as `orphan_types(...)`.
|
|
1029
|
+
|
|
1030
|
+
- Remove these no-op types from `orphan_types`: #{non_object_types.map { |t| "#{t.inspect} (#{t.kind.name})"}.join(", ")}
|
|
1031
|
+
- See https://graphql-ruby.org/type_definitions/interfaces.html#orphan-types
|
|
1032
|
+
|
|
1033
|
+
To add other types to your schema, you might want `extra_types`: https://graphql-ruby.org/schema/definition.html#extra-types
|
|
1034
|
+
ERR
|
|
1035
|
+
end
|
|
1036
|
+
add_type_and_traverse(new_orphan_types, root: false) unless use_visibility_profile?
|
|
776
1037
|
own_orphan_types.concat(new_orphan_types.flatten)
|
|
1038
|
+
self.visibility&.orphan_types_configured(new_orphan_types)
|
|
777
1039
|
end
|
|
778
1040
|
|
|
779
|
-
find_inherited_value(:orphan_types,
|
|
1041
|
+
inherited_ot = find_inherited_value(:orphan_types, nil)
|
|
1042
|
+
if inherited_ot
|
|
1043
|
+
if !own_orphan_types.empty?
|
|
1044
|
+
inherited_ot + own_orphan_types
|
|
1045
|
+
else
|
|
1046
|
+
inherited_ot
|
|
1047
|
+
end
|
|
1048
|
+
else
|
|
1049
|
+
own_orphan_types
|
|
1050
|
+
end
|
|
780
1051
|
end
|
|
781
1052
|
|
|
782
1053
|
def default_execution_strategy
|
|
@@ -795,6 +1066,41 @@ module GraphQL
|
|
|
795
1066
|
end
|
|
796
1067
|
end
|
|
797
1068
|
|
|
1069
|
+
|
|
1070
|
+
# @param new_default_logger [#log] Something to use for logging messages
|
|
1071
|
+
def default_logger(new_default_logger = NOT_CONFIGURED)
|
|
1072
|
+
if NOT_CONFIGURED.equal?(new_default_logger)
|
|
1073
|
+
if defined?(@default_logger)
|
|
1074
|
+
@default_logger
|
|
1075
|
+
elsif superclass.respond_to?(:default_logger)
|
|
1076
|
+
superclass.default_logger
|
|
1077
|
+
elsif defined?(Rails) && Rails.respond_to?(:logger) && (rails_logger = Rails.logger)
|
|
1078
|
+
rails_logger
|
|
1079
|
+
else
|
|
1080
|
+
def_logger = Logger.new($stdout)
|
|
1081
|
+
def_logger.info! # It doesn't output debug info by default
|
|
1082
|
+
def_logger
|
|
1083
|
+
end
|
|
1084
|
+
elsif new_default_logger == nil
|
|
1085
|
+
@default_logger = Logger.new(IO::NULL)
|
|
1086
|
+
else
|
|
1087
|
+
@default_logger = new_default_logger
|
|
1088
|
+
end
|
|
1089
|
+
end
|
|
1090
|
+
|
|
1091
|
+
# @param context [GraphQL::Query::Context, nil]
|
|
1092
|
+
# @return [Logger] A logger to use for this context configuration, falling back to {.default_logger}
|
|
1093
|
+
def logger_for(context)
|
|
1094
|
+
if context && context[:logger] == false
|
|
1095
|
+
Logger.new(IO::NULL)
|
|
1096
|
+
elsif context && (l = context[:logger])
|
|
1097
|
+
l
|
|
1098
|
+
else
|
|
1099
|
+
default_logger
|
|
1100
|
+
end
|
|
1101
|
+
end
|
|
1102
|
+
|
|
1103
|
+
# @param new_context_class [Class<GraphQL::Query::Context>] A subclass to use when executing queries
|
|
798
1104
|
def context_class(new_context_class = nil)
|
|
799
1105
|
if new_context_class
|
|
800
1106
|
@context_class = new_context_class
|
|
@@ -803,28 +1109,46 @@ module GraphQL
|
|
|
803
1109
|
end
|
|
804
1110
|
end
|
|
805
1111
|
|
|
1112
|
+
# Register a handler for errors raised during execution. The handlers can return a new value or raise a new error.
|
|
1113
|
+
#
|
|
1114
|
+
# @example Handling "not found" with a client-facing error
|
|
1115
|
+
# rescue_from(ActiveRecord::NotFound) { raise GraphQL::ExecutionError, "An object could not be found" }
|
|
1116
|
+
#
|
|
1117
|
+
# @param err_classes [Array<StandardError>] Classes which should be rescued by `handler_block`
|
|
1118
|
+
# @param handler_block The code to run when one of those errors is raised during execution
|
|
1119
|
+
# @yieldparam error [StandardError] An instance of one of the configured `err_classes`
|
|
1120
|
+
# @yieldparam object [Object] The current application object in the query when the error was raised
|
|
1121
|
+
# @yieldparam arguments [GraphQL::Query::Arguments] The current field arguments when the error was raised
|
|
1122
|
+
# @yieldparam context [GraphQL::Query::Context] The context for the currently-running operation
|
|
1123
|
+
# @yieldreturn [Object] Some object to use in the place where this error was raised
|
|
1124
|
+
# @raise [GraphQL::ExecutionError] In the handler, raise to add a client-facing error to the response
|
|
1125
|
+
# @raise [StandardError] In the handler, raise to crash the query with a developer-facing error
|
|
806
1126
|
def rescue_from(*err_classes, &handler_block)
|
|
807
1127
|
err_classes.each do |err_class|
|
|
808
1128
|
Execution::Errors.register_rescue_from(err_class, error_handlers[:subclass_handlers], handler_block)
|
|
809
1129
|
end
|
|
810
1130
|
end
|
|
811
1131
|
|
|
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
1132
|
def error_handlers
|
|
821
|
-
@error_handlers ||=
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
1133
|
+
@error_handlers ||= begin
|
|
1134
|
+
new_handler_hash = ->(h, k) {
|
|
1135
|
+
h[k] = {
|
|
1136
|
+
class: k,
|
|
1137
|
+
handler: nil,
|
|
1138
|
+
subclass_handlers: Hash.new(&new_handler_hash),
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
{
|
|
1142
|
+
class: nil,
|
|
1143
|
+
handler: nil,
|
|
1144
|
+
subclass_handlers: Hash.new(&new_handler_hash),
|
|
1145
|
+
}
|
|
1146
|
+
end
|
|
826
1147
|
end
|
|
827
1148
|
|
|
1149
|
+
# @api private
|
|
1150
|
+
attr_accessor :using_backtrace
|
|
1151
|
+
|
|
828
1152
|
# @api private
|
|
829
1153
|
def handle_or_reraise(context, err)
|
|
830
1154
|
handler = Execution::Errors.find_handler_for(self, err.class)
|
|
@@ -838,6 +1162,10 @@ module GraphQL
|
|
|
838
1162
|
end
|
|
839
1163
|
handler[:handler].call(err, obj, args, context, field)
|
|
840
1164
|
else
|
|
1165
|
+
if (context[:backtrace] || using_backtrace) && !err.is_a?(GraphQL::ExecutionError)
|
|
1166
|
+
err = GraphQL::Backtrace::TracedError.new(err, context)
|
|
1167
|
+
end
|
|
1168
|
+
|
|
841
1169
|
raise err
|
|
842
1170
|
end
|
|
843
1171
|
end
|
|
@@ -869,29 +1197,78 @@ module GraphQL
|
|
|
869
1197
|
end
|
|
870
1198
|
end
|
|
871
1199
|
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1200
|
+
# GraphQL-Ruby calls this method during execution when it needs the application to determine the type to use for an object.
|
|
1201
|
+
#
|
|
1202
|
+
# Usually, this object was returned from a field whose return type is an {GraphQL::Schema::Interface} or a {GraphQL::Schema::Union}.
|
|
1203
|
+
# 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.
|
|
1204
|
+
#
|
|
1205
|
+
# @example Returning a GraphQL type based on the object's class name
|
|
1206
|
+
# class MySchema < GraphQL::Schema
|
|
1207
|
+
# def resolve_type(_abs_type, object, _context)
|
|
1208
|
+
# graphql_type_name = "Types::#{object.class.name}Type"
|
|
1209
|
+
# graphql_type_name.constantize # If this raises a NameError, then come implement special cases in this method
|
|
1210
|
+
# end
|
|
1211
|
+
# end
|
|
1212
|
+
# @param abstract_type [Class, Module, nil] The Interface or Union type which is being resolved, if there is one
|
|
1213
|
+
# @param application_object [Object] The object returned from a field whose type must be determined
|
|
1214
|
+
# @param context [GraphQL::Query::Context] The query context for the currently-executing query
|
|
1215
|
+
# @return [Class<GraphQL::Schema::Object] The Object type definition to use for `obj`
|
|
1216
|
+
def resolve_type(abstract_type, application_object, context)
|
|
1217
|
+
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
1218
|
end
|
|
879
1219
|
# rubocop:enable Lint/DuplicateMethods
|
|
880
1220
|
|
|
881
1221
|
def inherited(child_class)
|
|
882
1222
|
if self == GraphQL::Schema
|
|
883
1223
|
child_class.directives(default_directives.values)
|
|
1224
|
+
child_class.extend(SubclassGetReferencesTo)
|
|
1225
|
+
end
|
|
1226
|
+
# Make sure the child class has these built out, so that
|
|
1227
|
+
# subclasses can be modified by later calls to `trace_with`
|
|
1228
|
+
own_trace_modes.each do |name, _class|
|
|
1229
|
+
child_class.own_trace_modes[name] = child_class.build_trace_mode(name)
|
|
884
1230
|
end
|
|
885
1231
|
child_class.singleton_class.prepend(ResolveTypeWithType)
|
|
886
|
-
super
|
|
887
|
-
end
|
|
888
1232
|
|
|
889
|
-
|
|
890
|
-
|
|
1233
|
+
if use_visibility_profile?
|
|
1234
|
+
vis = self.visibility
|
|
1235
|
+
child_class.visibility = vis.dup_for(child_class)
|
|
1236
|
+
end
|
|
1237
|
+
child_class.null_context = Query::NullContext.new(schema: child_class)
|
|
1238
|
+
super
|
|
891
1239
|
end
|
|
892
1240
|
|
|
893
|
-
|
|
894
|
-
|
|
1241
|
+
# Fetch an object based on an incoming ID and the current context. This method should return an object
|
|
1242
|
+
# from your application, or return `nil` if there is no object or the object shouldn't be available to this operation.
|
|
1243
|
+
#
|
|
1244
|
+
# @example Fetching an object with Rails's GlobalID
|
|
1245
|
+
# def self.object_from_id(object_id, _context)
|
|
1246
|
+
# GlobalID.find(global_id)
|
|
1247
|
+
# # TODO: use `context[:current_user]` to determine if this object is authorized.
|
|
1248
|
+
# end
|
|
1249
|
+
# @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`)
|
|
1250
|
+
# @param context [GraphQL::Query::Context] The context for the currently-executing operation
|
|
1251
|
+
# @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
|
|
1252
|
+
# @see id_from_object which produces these IDs
|
|
1253
|
+
def object_from_id(object_id, context)
|
|
1254
|
+
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}`)"
|
|
1255
|
+
end
|
|
1256
|
+
|
|
1257
|
+
# Return a stable ID string for `object` so that it can be refetched later, using {.object_from_id}.
|
|
1258
|
+
#
|
|
1259
|
+
# [GlobalID](https://github.com/rails/globalid) and [SQIDs](https://sqids.org/ruby) can both be used to create IDs.
|
|
1260
|
+
#
|
|
1261
|
+
# @example Using Rails's GlobalID to generate IDs
|
|
1262
|
+
# def self.id_from_object(application_object, graphql_type, context)
|
|
1263
|
+
# application_object.to_gid_param
|
|
1264
|
+
# end
|
|
1265
|
+
#
|
|
1266
|
+
# @param application_object [Object] Some object encountered by GraphQL-Ruby while running a query
|
|
1267
|
+
# @param graphql_type [Class, Module] The type that GraphQL-Ruby is using for `application_object` during this query
|
|
1268
|
+
# @param context [GraphQL::Query::Context] The context for the operation that is currently running
|
|
1269
|
+
# @return [String] A stable identifier which can be passed to {.object_from_id} later to re-fetch `application_object`
|
|
1270
|
+
def id_from_object(application_object, graphql_type, context)
|
|
1271
|
+
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
1272
|
end
|
|
896
1273
|
|
|
897
1274
|
def visible?(member, ctx)
|
|
@@ -907,6 +1284,10 @@ module GraphQL
|
|
|
907
1284
|
Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives)
|
|
908
1285
|
end
|
|
909
1286
|
|
|
1287
|
+
# Called when a type is needed by name at runtime
|
|
1288
|
+
def load_type(type_name, ctx)
|
|
1289
|
+
get_type(type_name, ctx)
|
|
1290
|
+
end
|
|
910
1291
|
# This hook is called when an object fails an `authorized?` check.
|
|
911
1292
|
# You might report to your bug tracker here, so you can correct
|
|
912
1293
|
# the field resolvers not to return unauthorized objects.
|
|
@@ -942,10 +1323,24 @@ module GraphQL
|
|
|
942
1323
|
unauthorized_object(unauthorized_error)
|
|
943
1324
|
end
|
|
944
1325
|
|
|
945
|
-
|
|
1326
|
+
# Called at runtime when GraphQL-Ruby encounters a mismatch between the application behavior
|
|
1327
|
+
# and the GraphQL type system.
|
|
1328
|
+
#
|
|
1329
|
+
# The default implementation of this method is to follow the GraphQL specification,
|
|
1330
|
+
# but you can override this to report errors to your bug tracker or customize error handling.
|
|
1331
|
+
# @param type_error [GraphQL::Error] several specific error classes are passed here, see the default implementation for details
|
|
1332
|
+
# @param context [GraphQL::Query::Context] the context for the currently-running operation
|
|
1333
|
+
# @return [void]
|
|
1334
|
+
# @raise [GraphQL::ExecutionError] to return this error to the client
|
|
1335
|
+
# @raise [GraphQL::Error] to crash the query and raise a developer-facing error
|
|
1336
|
+
def type_error(type_error, context)
|
|
946
1337
|
case type_error
|
|
947
1338
|
when GraphQL::InvalidNullError
|
|
948
|
-
|
|
1339
|
+
execution_error = GraphQL::ExecutionError.new(type_error.message, ast_nodes: type_error.ast_nodes)
|
|
1340
|
+
execution_error.path = type_error.path || context[:current_path]
|
|
1341
|
+
|
|
1342
|
+
context.errors << execution_error
|
|
1343
|
+
execution_error
|
|
949
1344
|
when GraphQL::UnresolvedTypeError, GraphQL::StringEncodingError, GraphQL::IntegerEncodingError
|
|
950
1345
|
raise type_error
|
|
951
1346
|
when GraphQL::IntegerDecodingError
|
|
@@ -953,7 +1348,7 @@ module GraphQL
|
|
|
953
1348
|
end
|
|
954
1349
|
end
|
|
955
1350
|
|
|
956
|
-
# A function to call when {
|
|
1351
|
+
# A function to call when {.execute} receives an invalid query string
|
|
957
1352
|
#
|
|
958
1353
|
# The default is to add the error to `context.errors`
|
|
959
1354
|
# @param parse_err [GraphQL::ParseError] The error encountered during parsing
|
|
@@ -967,25 +1362,58 @@ module GraphQL
|
|
|
967
1362
|
lazy_methods.set(lazy_class, value_method)
|
|
968
1363
|
end
|
|
969
1364
|
|
|
1365
|
+
def uses_raw_value?
|
|
1366
|
+
!!@uses_raw_value
|
|
1367
|
+
end
|
|
1368
|
+
|
|
1369
|
+
def uses_raw_value(new_val)
|
|
1370
|
+
@uses_raw_value = new_val
|
|
1371
|
+
end
|
|
1372
|
+
|
|
1373
|
+
def resolves_lazies?
|
|
1374
|
+
lazy_method_count = 0
|
|
1375
|
+
lazy_methods.each do |k, v|
|
|
1376
|
+
if !v.nil?
|
|
1377
|
+
lazy_method_count += 1
|
|
1378
|
+
end
|
|
1379
|
+
end
|
|
1380
|
+
lazy_method_count > 2
|
|
1381
|
+
end
|
|
1382
|
+
|
|
970
1383
|
def instrument(instrument_step, instrumenter, options = {})
|
|
1384
|
+
warn <<~WARN
|
|
1385
|
+
Schema.instrument is deprecated, use `trace_with` instead: https://graphql-ruby.org/queries/tracing.html"
|
|
1386
|
+
(From `#{self}.instrument(#{instrument_step}, #{instrumenter})` at #{caller(1, 1).first})
|
|
1387
|
+
|
|
1388
|
+
WARN
|
|
1389
|
+
trace_with(Tracing::LegacyHooksTrace)
|
|
971
1390
|
own_instrumenters[instrument_step] << instrumenter
|
|
972
1391
|
end
|
|
973
1392
|
|
|
974
1393
|
# Add several directives at once
|
|
975
1394
|
# @param new_directives [Class]
|
|
976
1395
|
def directives(*new_directives)
|
|
977
|
-
if new_directives.
|
|
1396
|
+
if !new_directives.empty?
|
|
978
1397
|
new_directives.flatten.each { |d| directive(d) }
|
|
979
1398
|
end
|
|
980
1399
|
|
|
981
|
-
find_inherited_value(:directives, default_directives)
|
|
1400
|
+
inherited_dirs = find_inherited_value(:directives, default_directives)
|
|
1401
|
+
if !own_directives.empty?
|
|
1402
|
+
inherited_dirs.merge(own_directives)
|
|
1403
|
+
else
|
|
1404
|
+
inherited_dirs
|
|
1405
|
+
end
|
|
982
1406
|
end
|
|
983
1407
|
|
|
984
1408
|
# Attach a single directive to this schema
|
|
985
1409
|
# @param new_directive [Class]
|
|
986
1410
|
# @return void
|
|
987
1411
|
def directive(new_directive)
|
|
988
|
-
|
|
1412
|
+
if use_visibility_profile?
|
|
1413
|
+
own_directives[new_directive.graphql_name] = new_directive
|
|
1414
|
+
else
|
|
1415
|
+
add_type_and_traverse(new_directive, root: false)
|
|
1416
|
+
end
|
|
989
1417
|
end
|
|
990
1418
|
|
|
991
1419
|
def default_directives
|
|
@@ -994,11 +1422,27 @@ module GraphQL
|
|
|
994
1422
|
"skip" => GraphQL::Schema::Directive::Skip,
|
|
995
1423
|
"deprecated" => GraphQL::Schema::Directive::Deprecated,
|
|
996
1424
|
"oneOf" => GraphQL::Schema::Directive::OneOf,
|
|
1425
|
+
"specifiedBy" => GraphQL::Schema::Directive::SpecifiedBy,
|
|
997
1426
|
}.freeze
|
|
998
1427
|
end
|
|
999
1428
|
|
|
1000
|
-
|
|
1001
|
-
|
|
1429
|
+
# @return [GraphQL::Tracing::DetailedTrace] if it has been configured for this schema
|
|
1430
|
+
attr_accessor :detailed_trace
|
|
1431
|
+
|
|
1432
|
+
# @param query [GraphQL::Query, GraphQL::Execution::Multiplex] Called with a multiplex when multiple queries are executed at once (with {.multiplex})
|
|
1433
|
+
# @return [Boolean] When `true`, save a detailed trace for this query.
|
|
1434
|
+
# @see Tracing::DetailedTrace DetailedTrace saves traces when this method returns true
|
|
1435
|
+
def detailed_trace?(query)
|
|
1436
|
+
raise "#{self} must implement `def.detailed_trace?(query)` to use DetailedTrace. Implement this method in your schema definition."
|
|
1437
|
+
end
|
|
1438
|
+
|
|
1439
|
+
def tracer(new_tracer, silence_deprecation_warning: false)
|
|
1440
|
+
if !silence_deprecation_warning
|
|
1441
|
+
warn("`Schema.tracer(#{new_tracer.inspect})` is deprecated; use module-based `trace_with` instead. See: https://graphql-ruby.org/queries/tracing.html")
|
|
1442
|
+
warn " #{caller(1, 1).first}"
|
|
1443
|
+
end
|
|
1444
|
+
default_trace = trace_class_for(:default, build: true)
|
|
1445
|
+
if default_trace.nil? || !(default_trace < GraphQL::Tracing::CallLegacyTracers)
|
|
1002
1446
|
trace_with(GraphQL::Tracing::CallLegacyTracers)
|
|
1003
1447
|
end
|
|
1004
1448
|
|
|
@@ -1009,24 +1453,46 @@ module GraphQL
|
|
|
1009
1453
|
find_inherited_value(:tracers, EMPTY_ARRAY) + own_tracers
|
|
1010
1454
|
end
|
|
1011
1455
|
|
|
1012
|
-
# Mix `trace_mod` into this schema's `Trace` class so that its methods
|
|
1013
|
-
#
|
|
1456
|
+
# Mix `trace_mod` into this schema's `Trace` class so that its methods will be called at runtime.
|
|
1457
|
+
#
|
|
1458
|
+
# You can attach a module to run in only _some_ circumstances by using `mode:`. When a module is added with `mode:`,
|
|
1459
|
+
# it will only run for queries with a matching `context[:trace_mode]`.
|
|
1460
|
+
#
|
|
1461
|
+
# Any custom trace modes _also_ include the default `trace_with ...` modules (that is, those added _without_ any particular `mode: ...` configuration).
|
|
1462
|
+
#
|
|
1463
|
+
# @example Adding a trace in a special mode
|
|
1464
|
+
# # only runs when `query.context[:trace_mode]` is `:special`
|
|
1465
|
+
# trace_with SpecialTrace, mode: :special
|
|
1014
1466
|
#
|
|
1015
1467
|
# @param trace_mod [Module] A module that implements tracing methods
|
|
1016
1468
|
# @param mode [Symbol] Trace module will only be used for this trade mode
|
|
1017
1469
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
|
1018
1470
|
# @return [void]
|
|
1471
|
+
# @see GraphQL::Tracing::Trace Tracing::Trace for available tracing methods
|
|
1019
1472
|
def trace_with(trace_mod, mode: :default, **options)
|
|
1020
1473
|
if mode.is_a?(Array)
|
|
1021
1474
|
mode.each { |m| trace_with(trace_mod, mode: m, **options) }
|
|
1022
1475
|
else
|
|
1023
|
-
tc =
|
|
1476
|
+
tc = own_trace_modes[mode] ||= build_trace_mode(mode)
|
|
1024
1477
|
tc.include(trace_mod)
|
|
1025
|
-
|
|
1026
|
-
|
|
1478
|
+
own_trace_modules[mode] << trace_mod
|
|
1479
|
+
add_trace_options_for(mode, options)
|
|
1480
|
+
if mode == :default
|
|
1481
|
+
# This module is being added as a default tracer. If any other mode classes
|
|
1482
|
+
# have already been created, but get their default behavior from a superclass,
|
|
1483
|
+
# Then mix this into this schema's subclass.
|
|
1484
|
+
# (But don't mix it into mode classes that aren't default-based.)
|
|
1485
|
+
own_trace_modes.each do |other_mode_name, other_mode_class|
|
|
1486
|
+
if other_mode_class < DefaultTraceClass
|
|
1487
|
+
# Don't add it back to the inheritance tree if it's already there
|
|
1488
|
+
if !(other_mode_class < trace_mod)
|
|
1489
|
+
other_mode_class.include(trace_mod)
|
|
1490
|
+
end
|
|
1491
|
+
# Add any options so they'll be available
|
|
1492
|
+
add_trace_options_for(other_mode_name, options)
|
|
1493
|
+
end
|
|
1494
|
+
end
|
|
1027
1495
|
end
|
|
1028
|
-
t_opts = trace_options_for(mode)
|
|
1029
|
-
t_opts.merge!(options)
|
|
1030
1496
|
end
|
|
1031
1497
|
nil
|
|
1032
1498
|
end
|
|
@@ -1036,37 +1502,59 @@ module GraphQL
|
|
|
1036
1502
|
def trace_options_for(mode)
|
|
1037
1503
|
@trace_options_for_mode ||= {}
|
|
1038
1504
|
@trace_options_for_mode[mode] ||= begin
|
|
1505
|
+
# It may be time to create an options hash for a mode that wasn't registered yet.
|
|
1506
|
+
# Mix in the default options in that case.
|
|
1507
|
+
default_options = mode == :default ? EMPTY_HASH : trace_options_for(:default)
|
|
1508
|
+
# Make sure this returns a new object so that other hashes aren't modified later
|
|
1039
1509
|
if superclass.respond_to?(:trace_options_for)
|
|
1040
|
-
superclass.trace_options_for(mode).
|
|
1510
|
+
superclass.trace_options_for(mode).merge(default_options)
|
|
1041
1511
|
else
|
|
1042
|
-
|
|
1512
|
+
default_options.dup
|
|
1043
1513
|
end
|
|
1044
1514
|
end
|
|
1045
1515
|
end
|
|
1046
1516
|
|
|
1047
1517
|
# Create a trace instance which will include the trace modules specified for the optional mode.
|
|
1048
1518
|
#
|
|
1519
|
+
# If no `mode:` is given, then {default_trace_mode} will be used.
|
|
1520
|
+
#
|
|
1521
|
+
# If this schema is using {Tracing::DetailedTrace} and {.detailed_trace?} returns `true`, then
|
|
1522
|
+
# DetailedTrace's mode will override the passed-in `mode`.
|
|
1523
|
+
#
|
|
1049
1524
|
# @param mode [Symbol] Trace modules for this trade mode will be included
|
|
1050
1525
|
# @param options [Hash] Keywords that will be passed to the tracing class during `#initialize`
|
|
1051
1526
|
# @return [Tracing::Trace]
|
|
1052
1527
|
def new_trace(mode: nil, **options)
|
|
1053
|
-
|
|
1054
|
-
|
|
1528
|
+
should_sample = if detailed_trace
|
|
1529
|
+
if (query = options[:query])
|
|
1530
|
+
detailed_trace?(query)
|
|
1531
|
+
elsif (multiplex = options[:multiplex])
|
|
1532
|
+
if multiplex.queries.length == 1
|
|
1533
|
+
detailed_trace?(multiplex.queries.first)
|
|
1534
|
+
else
|
|
1535
|
+
detailed_trace?(multiplex)
|
|
1536
|
+
end
|
|
1537
|
+
end
|
|
1538
|
+
else
|
|
1539
|
+
false
|
|
1540
|
+
end
|
|
1055
1541
|
|
|
1056
|
-
|
|
1057
|
-
mode
|
|
1058
|
-
elsif target && target.context[:backtrace]
|
|
1059
|
-
:default_backtrace
|
|
1542
|
+
if should_sample
|
|
1543
|
+
mode = detailed_trace.trace_mode
|
|
1060
1544
|
else
|
|
1061
|
-
:
|
|
1545
|
+
target = options[:query] || options[:multiplex]
|
|
1546
|
+
mode ||= target && target.context[:trace_mode]
|
|
1062
1547
|
end
|
|
1063
1548
|
|
|
1549
|
+
trace_mode = mode || default_trace_mode
|
|
1064
1550
|
base_trace_options = trace_options_for(trace_mode)
|
|
1065
1551
|
trace_options = base_trace_options.merge(options)
|
|
1066
|
-
trace_class_for_mode = trace_class_for(trace_mode)
|
|
1552
|
+
trace_class_for_mode = trace_class_for(trace_mode, build: true)
|
|
1067
1553
|
trace_class_for_mode.new(**trace_options)
|
|
1068
1554
|
end
|
|
1069
1555
|
|
|
1556
|
+
# @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on queries to this schema
|
|
1557
|
+
# @see GraphQL::Analysis the analysis system
|
|
1070
1558
|
def query_analyzer(new_analyzer)
|
|
1071
1559
|
own_query_analyzers << new_analyzer
|
|
1072
1560
|
end
|
|
@@ -1075,6 +1563,8 @@ module GraphQL
|
|
|
1075
1563
|
find_inherited_value(:query_analyzers, EMPTY_ARRAY) + own_query_analyzers
|
|
1076
1564
|
end
|
|
1077
1565
|
|
|
1566
|
+
# @param new_analyzer [Class<GraphQL::Analysis::Analyzer>] An analyzer to run on multiplexes to this schema
|
|
1567
|
+
# @see GraphQL::Analysis the analysis system
|
|
1078
1568
|
def multiplex_analyzer(new_analyzer)
|
|
1079
1569
|
own_multiplex_analyzers << new_analyzer
|
|
1080
1570
|
end
|
|
@@ -1093,7 +1583,7 @@ module GraphQL
|
|
|
1093
1583
|
|
|
1094
1584
|
# Execute a query on itself.
|
|
1095
1585
|
# @see {Query#initialize} for arguments.
|
|
1096
|
-
# @return [
|
|
1586
|
+
# @return [GraphQL::Query::Result] query result, ready to be serialized as JSON
|
|
1097
1587
|
def execute(query_str = nil, **kwargs)
|
|
1098
1588
|
if query_str
|
|
1099
1589
|
kwargs[:query] = query_str
|
|
@@ -1132,8 +1622,9 @@ module GraphQL
|
|
|
1132
1622
|
# @see {Query#initialize} for query keyword arguments
|
|
1133
1623
|
# @see {Execution::Multiplex#run_all} for multiplex keyword arguments
|
|
1134
1624
|
# @param queries [Array<Hash>] Keyword arguments for each query
|
|
1135
|
-
# @
|
|
1136
|
-
# @
|
|
1625
|
+
# @option kwargs [Hash] :context ({}) Multiplex-level context
|
|
1626
|
+
# @option kwargs [nil, Integer] :max_complexity (nil)
|
|
1627
|
+
# @return [Array<GraphQL::Query::Result>] One result for each query in the input
|
|
1137
1628
|
def multiplex(queries, **kwargs)
|
|
1138
1629
|
GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
|
|
1139
1630
|
end
|
|
@@ -1147,7 +1638,8 @@ module GraphQL
|
|
|
1147
1638
|
|
|
1148
1639
|
# @api private
|
|
1149
1640
|
def add_subscription_extension_if_necessary
|
|
1150
|
-
|
|
1641
|
+
# TODO: when there's a proper API for extending root types, migrat this to use it.
|
|
1642
|
+
if !defined?(@subscription_extension_added) && @subscription_object.is_a?(Class) && self.subscriptions
|
|
1151
1643
|
@subscription_extension_added = true
|
|
1152
1644
|
subscription.all_field_definitions.each do |field|
|
|
1153
1645
|
if !field.extensions.any? { |ext| ext.is_a?(Subscriptions::DefaultSubscriptionResolveExtension) }
|
|
@@ -1157,6 +1649,11 @@ module GraphQL
|
|
|
1157
1649
|
end
|
|
1158
1650
|
end
|
|
1159
1651
|
|
|
1652
|
+
# Called when execution encounters a `SystemStackError`. By default, it adds a client-facing error to the response.
|
|
1653
|
+
# You could modify this method to report this error to your bug tracker.
|
|
1654
|
+
# @param query [GraphQL::Query]
|
|
1655
|
+
# @param err [SystemStackError]
|
|
1656
|
+
# @return [void]
|
|
1160
1657
|
def query_stack_error(query, err)
|
|
1161
1658
|
query.context.errors.push(GraphQL::ExecutionError.new("This query is too large to execute."))
|
|
1162
1659
|
end
|
|
@@ -1191,7 +1688,7 @@ module GraphQL
|
|
|
1191
1688
|
end
|
|
1192
1689
|
end
|
|
1193
1690
|
|
|
1194
|
-
# @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {
|
|
1691
|
+
# @return [Symbol, nil] The method name to lazily resolve `obj`, or nil if `obj`'s class wasn't registered with {.lazy_resolve}.
|
|
1195
1692
|
def lazy_method_name(obj)
|
|
1196
1693
|
lazy_methods.get(obj)
|
|
1197
1694
|
end
|
|
@@ -1215,8 +1712,218 @@ module GraphQL
|
|
|
1215
1712
|
end
|
|
1216
1713
|
end
|
|
1217
1714
|
|
|
1715
|
+
# Returns `DidYouMean` if it's defined.
|
|
1716
|
+
# Override this to return `nil` if you don't want to use `DidYouMean`
|
|
1717
|
+
def did_you_mean(new_dym = NOT_CONFIGURED)
|
|
1718
|
+
if NOT_CONFIGURED.equal?(new_dym)
|
|
1719
|
+
if defined?(@did_you_mean)
|
|
1720
|
+
@did_you_mean
|
|
1721
|
+
else
|
|
1722
|
+
find_inherited_value(:did_you_mean, defined?(DidYouMean) ? DidYouMean : nil)
|
|
1723
|
+
end
|
|
1724
|
+
else
|
|
1725
|
+
@did_you_mean = new_dym
|
|
1726
|
+
end
|
|
1727
|
+
end
|
|
1728
|
+
|
|
1729
|
+
|
|
1730
|
+
# This setting controls how GraphQL-Ruby handles empty selections on Union types.
|
|
1731
|
+
#
|
|
1732
|
+
# To opt into future, spec-compliant behavior where these selections are rejected, set this to `false`.
|
|
1733
|
+
#
|
|
1734
|
+
# If you need to support previous, non-spec behavior which allowed selecting union fields
|
|
1735
|
+
# but *not* selecting any fields on that union, set this to `true` to continue allowing that behavior.
|
|
1736
|
+
#
|
|
1737
|
+
# If this is `true`, then {.legacy_invalid_empty_selections_on_union_with_type} will be called with {Query} objects
|
|
1738
|
+
# with that kind of selections. You must implement that method
|
|
1739
|
+
# @param new_value [Boolean]
|
|
1740
|
+
# @return [true, false, nil]
|
|
1741
|
+
def allow_legacy_invalid_empty_selections_on_union(new_value = NOT_CONFIGURED)
|
|
1742
|
+
if NOT_CONFIGURED.equal?(new_value)
|
|
1743
|
+
if defined?(@allow_legacy_invalid_empty_selections_on_union)
|
|
1744
|
+
@allow_legacy_invalid_empty_selections_on_union
|
|
1745
|
+
else
|
|
1746
|
+
find_inherited_value(:allow_legacy_invalid_empty_selections_on_union)
|
|
1747
|
+
end
|
|
1748
|
+
else
|
|
1749
|
+
@allow_legacy_invalid_empty_selections_on_union = new_value
|
|
1750
|
+
end
|
|
1751
|
+
end
|
|
1752
|
+
|
|
1753
|
+
# This method is called during validation when a previously-allowed, but non-spec
|
|
1754
|
+
# query is encountered where a union field has no child selections on it.
|
|
1755
|
+
#
|
|
1756
|
+
# If `legacy_invalid_empty_selections_on_union_with_type` is overridden, this method will not be called.
|
|
1757
|
+
#
|
|
1758
|
+
# You should implement this method or `legacy_invalid_empty_selections_on_union_with_type`
|
|
1759
|
+
# to log the violation so that you can contact clients and notify them about changing their queries.
|
|
1760
|
+
# Then return a suitable value to tell GraphQL-Ruby how to continue.
|
|
1761
|
+
# @param query [GraphQL::Query]
|
|
1762
|
+
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1763
|
+
# @return [String] A validation error to return for this query
|
|
1764
|
+
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1765
|
+
def legacy_invalid_empty_selections_on_union(query)
|
|
1766
|
+
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"
|
|
1767
|
+
end
|
|
1768
|
+
|
|
1769
|
+
# This method is called during validation when a previously-allowed, but non-spec
|
|
1770
|
+
# query is encountered where a union field has no child selections on it.
|
|
1771
|
+
#
|
|
1772
|
+
# You should implement this method to log the violation so that you can contact clients
|
|
1773
|
+
# and notify them about changing their queries. Then return a suitable value to
|
|
1774
|
+
# tell GraphQL-Ruby how to continue.
|
|
1775
|
+
# @param query [GraphQL::Query]
|
|
1776
|
+
# @param type [Module] A GraphQL type definition
|
|
1777
|
+
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1778
|
+
# @return [String] A validation error to return for this query
|
|
1779
|
+
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1780
|
+
def legacy_invalid_empty_selections_on_union_with_type(query, type)
|
|
1781
|
+
legacy_invalid_empty_selections_on_union(query)
|
|
1782
|
+
end
|
|
1783
|
+
|
|
1784
|
+
# This setting controls how GraphQL-Ruby handles overlapping selections on scalar types when the types
|
|
1785
|
+
# don't match.
|
|
1786
|
+
#
|
|
1787
|
+
# When set to `false`, GraphQL-Ruby will reject those queries with a validation error (as per the GraphQL spec).
|
|
1788
|
+
#
|
|
1789
|
+
# When set to `true`, GraphQL-Ruby will call {.legacy_invalid_return_type_conflicts} when the scenario is encountered.
|
|
1790
|
+
#
|
|
1791
|
+
# @param new_value [Boolean] `true` permits the legacy behavior, `false` rejects it.
|
|
1792
|
+
# @return [true, false, nil]
|
|
1793
|
+
def allow_legacy_invalid_return_type_conflicts(new_value = NOT_CONFIGURED)
|
|
1794
|
+
if NOT_CONFIGURED.equal?(new_value)
|
|
1795
|
+
if defined?(@allow_legacy_invalid_return_type_conflicts)
|
|
1796
|
+
@allow_legacy_invalid_return_type_conflicts
|
|
1797
|
+
else
|
|
1798
|
+
find_inherited_value(:allow_legacy_invalid_return_type_conflicts)
|
|
1799
|
+
end
|
|
1800
|
+
else
|
|
1801
|
+
@allow_legacy_invalid_return_type_conflicts = new_value
|
|
1802
|
+
end
|
|
1803
|
+
end
|
|
1804
|
+
|
|
1805
|
+
# This method is called when the query contains fields which don't contain matching scalar types.
|
|
1806
|
+
# This was previously allowed by GraphQL-Ruby but it's a violation of the GraphQL spec.
|
|
1807
|
+
#
|
|
1808
|
+
# You should implement this method to log the violation so that you observe usage of these fields.
|
|
1809
|
+
# Fixing this scenario might mean adding new fields, and telling clients to use those fields.
|
|
1810
|
+
# (Changing the field return type would be a breaking change, but if it works for your client use cases,
|
|
1811
|
+
# that might work, too.)
|
|
1812
|
+
#
|
|
1813
|
+
# @param query [GraphQL::Query]
|
|
1814
|
+
# @param type1 [Module] A GraphQL type definition
|
|
1815
|
+
# @param type2 [Module] A GraphQL type definition
|
|
1816
|
+
# @param node1 [GraphQL::Language::Nodes::Field] This node is recognized as conflicting. You might call `.line` and `.col` for custom error reporting.
|
|
1817
|
+
# @param node2 [GraphQL::Language::Nodes::Field] The other node recognized as conflicting.
|
|
1818
|
+
# @return [:return_validation_error] Let GraphQL-Ruby return the (new) normal validation error for this query
|
|
1819
|
+
# @return [String] A validation error to return for this query
|
|
1820
|
+
# @return [nil] Don't send the client an error, continue the legacy behavior (allow this query to execute)
|
|
1821
|
+
def legacy_invalid_return_type_conflicts(query, type1, type2, node1, node2)
|
|
1822
|
+
raise "Implement #{self}.legacy_invalid_return_type_conflicts to handle this invalid selection"
|
|
1823
|
+
end
|
|
1824
|
+
|
|
1825
|
+
# The legacy complexity implementation included several bugs:
|
|
1826
|
+
#
|
|
1827
|
+
# - In some cases, it used the lexically _last_ field to determine a cost, instead of calculating the maximum among selections
|
|
1828
|
+
# - In some cases, it called field complexity hooks repeatedly (when it should have only called them once)
|
|
1829
|
+
#
|
|
1830
|
+
# The future implementation may produce higher total complexity scores, so it's not active by default yet. You can opt into
|
|
1831
|
+
# the future default behavior by configuring `:future` here. Or, you can choose a mode for each query with {.complexity_cost_calculation_mode_for}.
|
|
1832
|
+
#
|
|
1833
|
+
# The legacy mode is currently maintained alongside the future one, but it will be removed in a future GraphQL-Ruby version.
|
|
1834
|
+
#
|
|
1835
|
+
# If you choose `:compare`, you must also implement {.legacy_complexity_cost_calculation_mismatch} to handle the input somehow.
|
|
1836
|
+
#
|
|
1837
|
+
# @example Opting into the future calculation mode
|
|
1838
|
+
# complexity_cost_calculation_mode(:future)
|
|
1839
|
+
#
|
|
1840
|
+
# @example Choosing the legacy mode (which will work until that mode is removed...)
|
|
1841
|
+
# complexity_cost_calculation_mode(:legacy)
|
|
1842
|
+
#
|
|
1843
|
+
# @example Run both modes for every query, call {.legacy_complexity_cost_calculation_mismatch} when they don't match:
|
|
1844
|
+
# complexity_cost_calculation_mode(:compare)
|
|
1845
|
+
def complexity_cost_calculation_mode(new_mode = NOT_CONFIGURED)
|
|
1846
|
+
if NOT_CONFIGURED.equal?(new_mode)
|
|
1847
|
+
if defined?(@complexity_cost_calculation_mode)
|
|
1848
|
+
@complexity_cost_calculation_mode
|
|
1849
|
+
else
|
|
1850
|
+
find_inherited_value(:complexity_cost_calculation_mode)
|
|
1851
|
+
end
|
|
1852
|
+
else
|
|
1853
|
+
@complexity_cost_calculation_mode = new_mode
|
|
1854
|
+
end
|
|
1855
|
+
end
|
|
1856
|
+
|
|
1857
|
+
# Implement this method to produce a per-query complexity cost calculation mode. (Technically, it's per-multiplex.)
|
|
1858
|
+
#
|
|
1859
|
+
# This is a way to check the compatibility of queries coming to your API without adding overhead of running `:compare`
|
|
1860
|
+
# for every query. You could sample traffic, turn it off/on with feature flags, or anything else.
|
|
1861
|
+
#
|
|
1862
|
+
# @example Sampling traffic
|
|
1863
|
+
# def self.complexity_cost_calculation_mode_for(_context)
|
|
1864
|
+
# if rand < 0.1 # 10% of the time
|
|
1865
|
+
# :compare
|
|
1866
|
+
# else
|
|
1867
|
+
# :legacy
|
|
1868
|
+
# end
|
|
1869
|
+
# end
|
|
1870
|
+
#
|
|
1871
|
+
# @example Using a feature flag to manage future mode
|
|
1872
|
+
# def complexity_cost_calculation_mode_for(context)
|
|
1873
|
+
# current_user = context[:current_user]
|
|
1874
|
+
# if Flipper.enabled?(:future_complexity_cost, current_user)
|
|
1875
|
+
# :future
|
|
1876
|
+
# elsif rand < 0.5 # 50%
|
|
1877
|
+
# :compare
|
|
1878
|
+
# else
|
|
1879
|
+
# :legacy
|
|
1880
|
+
# end
|
|
1881
|
+
# end
|
|
1882
|
+
#
|
|
1883
|
+
# @param multiplex_context [Hash] The context for the currently-running {Execution::Multiplex} (which contains one or more queries)
|
|
1884
|
+
# @return [:future] Use the new calculation algorithm -- may be higher than `:legacy`
|
|
1885
|
+
# @return [:legacy] Use the legacy calculation algorithm, warts and all
|
|
1886
|
+
# @return [:compare] Run both algorithms and call {.legacy_complexity_cost_calculation_mismatch} if they don't match
|
|
1887
|
+
def complexity_cost_calculation_mode_for(multiplex_context)
|
|
1888
|
+
complexity_cost_calculation_mode
|
|
1889
|
+
end
|
|
1890
|
+
|
|
1891
|
+
# Implement this method in your schema to handle mismatches when `:compare` is used.
|
|
1892
|
+
#
|
|
1893
|
+
# @example Logging the mismatch
|
|
1894
|
+
# def self.legacy_cost_calculation_mismatch(multiplex, future_cost, legacy_cost)
|
|
1895
|
+
# client_id = multiplex.context[:api_client].id
|
|
1896
|
+
# operation_names = multiplex.queries.map { |q| q.selected_operation_name || "anonymous" }.join(", ")
|
|
1897
|
+
# Stats.increment(:complexity_mismatch, tags: { client: client_id, ops: operation_names })
|
|
1898
|
+
# legacy_cost
|
|
1899
|
+
# end
|
|
1900
|
+
# @see Query::Context#add_error Adding an error to the response to notify the client
|
|
1901
|
+
# @see Query::Context#response_extensions Adding key-value pairs to the response `"extensions" => { ... }`
|
|
1902
|
+
# @param multiplex [GraphQL::Execution::Multiplex]
|
|
1903
|
+
# @param future_complexity_cost [Integer]
|
|
1904
|
+
# @param legacy_complexity_cost [Integer]
|
|
1905
|
+
# @return [Integer] the cost to use for this query (probably one of `future_complexity_cost` or `legacy_complexity_cost`)
|
|
1906
|
+
def legacy_complexity_cost_calculation_mismatch(multiplex, future_complexity_cost, legacy_complexity_cost)
|
|
1907
|
+
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"
|
|
1908
|
+
end
|
|
1909
|
+
|
|
1218
1910
|
private
|
|
1219
1911
|
|
|
1912
|
+
def add_trace_options_for(mode, new_options)
|
|
1913
|
+
if mode == :default
|
|
1914
|
+
own_trace_modes.each do |mode_name, t_class|
|
|
1915
|
+
if t_class <= DefaultTraceClass
|
|
1916
|
+
t_opts = trace_options_for(mode_name)
|
|
1917
|
+
t_opts.merge!(new_options)
|
|
1918
|
+
end
|
|
1919
|
+
end
|
|
1920
|
+
else
|
|
1921
|
+
t_opts = trace_options_for(mode)
|
|
1922
|
+
t_opts.merge!(new_options)
|
|
1923
|
+
end
|
|
1924
|
+
nil
|
|
1925
|
+
end
|
|
1926
|
+
|
|
1220
1927
|
# @param t [Module, Array<Module>]
|
|
1221
1928
|
# @return [void]
|
|
1222
1929
|
def add_type_and_traverse(t, root:)
|
|
@@ -1260,7 +1967,8 @@ module GraphQL
|
|
|
1260
1967
|
own_union_memberships.merge!(addition.union_memberships)
|
|
1261
1968
|
|
|
1262
1969
|
addition.references.each { |thing, pointers|
|
|
1263
|
-
|
|
1970
|
+
prev_refs = own_references_to[thing] || []
|
|
1971
|
+
own_references_to[thing] = prev_refs | pointers.to_a
|
|
1264
1972
|
}
|
|
1265
1973
|
|
|
1266
1974
|
addition.directives.each { |dir_class| own_directives[dir_class.graphql_name] = dir_class }
|
|
@@ -1278,7 +1986,7 @@ module GraphQL
|
|
|
1278
1986
|
else
|
|
1279
1987
|
@lazy_methods = GraphQL::Execution::Lazy::LazyMethodMap.new
|
|
1280
1988
|
@lazy_methods.set(GraphQL::Execution::Lazy, :value)
|
|
1281
|
-
@lazy_methods.set(GraphQL::Dataloader::Request, :
|
|
1989
|
+
@lazy_methods.set(GraphQL::Dataloader::Request, :load_with_deprecation_warning)
|
|
1282
1990
|
end
|
|
1283
1991
|
end
|
|
1284
1992
|
@lazy_methods
|
|
@@ -1288,6 +1996,10 @@ module GraphQL
|
|
|
1288
1996
|
@own_types ||= {}
|
|
1289
1997
|
end
|
|
1290
1998
|
|
|
1999
|
+
def own_references_to
|
|
2000
|
+
@own_references_to ||= {}.compare_by_identity
|
|
2001
|
+
end
|
|
2002
|
+
|
|
1291
2003
|
def non_introspection_types
|
|
1292
2004
|
find_inherited_value(:non_introspection_types, EMPTY_HASH).merge(own_types)
|
|
1293
2005
|
end
|
|
@@ -1301,7 +2013,7 @@ module GraphQL
|
|
|
1301
2013
|
end
|
|
1302
2014
|
|
|
1303
2015
|
def own_possible_types
|
|
1304
|
-
@own_possible_types ||= {}
|
|
2016
|
+
@own_possible_types ||= {}.compare_by_identity
|
|
1305
2017
|
end
|
|
1306
2018
|
|
|
1307
2019
|
def own_union_memberships
|
|
@@ -1327,9 +2039,37 @@ module GraphQL
|
|
|
1327
2039
|
def own_multiplex_analyzers
|
|
1328
2040
|
@own_multiplex_analyzers ||= []
|
|
1329
2041
|
end
|
|
2042
|
+
|
|
2043
|
+
# This is overridden in subclasses to check the inheritance chain
|
|
2044
|
+
def get_references_to(type_defn)
|
|
2045
|
+
own_references_to[type_defn]
|
|
2046
|
+
end
|
|
2047
|
+
end
|
|
2048
|
+
|
|
2049
|
+
module SubclassGetReferencesTo
|
|
2050
|
+
def get_references_to(type_defn)
|
|
2051
|
+
own_refs = own_references_to[type_defn]
|
|
2052
|
+
inherited_refs = superclass.references_to(type_defn)
|
|
2053
|
+
if inherited_refs&.any?
|
|
2054
|
+
if own_refs&.any?
|
|
2055
|
+
own_refs + inherited_refs
|
|
2056
|
+
else
|
|
2057
|
+
inherited_refs
|
|
2058
|
+
end
|
|
2059
|
+
else
|
|
2060
|
+
own_refs
|
|
2061
|
+
end
|
|
2062
|
+
end
|
|
1330
2063
|
end
|
|
1331
2064
|
|
|
1332
2065
|
# Install these here so that subclasses will also install it.
|
|
1333
2066
|
self.connections = GraphQL::Pagination::Connections.new(schema: self)
|
|
2067
|
+
|
|
2068
|
+
# @api private
|
|
2069
|
+
module DefaultTraceClass
|
|
2070
|
+
end
|
|
1334
2071
|
end
|
|
1335
2072
|
end
|
|
2073
|
+
|
|
2074
|
+
require "graphql/schema/loader"
|
|
2075
|
+
require "graphql/schema/printer"
|