graphql 2.0.31 → 2.6.1
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 +102 -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/field_resolve_step.rb +631 -0
- data/lib/graphql/execution/finalize.rb +217 -0
- data/lib/graphql/execution/input_values.rb +261 -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/handles_raw_value.rb +6 -0
- 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 +365 -435
- data/lib/graphql/execution/interpreter.rb +87 -163
- data/lib/graphql/execution/lazy.rb +1 -1
- data/lib/graphql/execution/load_argument_step.rb +64 -0
- data/lib/graphql/execution/lookahead.rb +105 -31
- data/lib/graphql/execution/multiplex.rb +7 -6
- data/lib/graphql/execution/next.rb +90 -0
- data/lib/graphql/execution/prepare_object_step.rb +128 -0
- data/lib/graphql/execution/runner.rb +410 -0
- data/lib/graphql/execution/selections_step.rb +91 -0
- data/lib/graphql/execution.rb +8 -4
- data/lib/graphql/execution_error.rb +17 -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 +323 -193
- data/lib/graphql/language/nodes.rb +139 -77
- data/lib/graphql/language/parser.rb +807 -1985
- data/lib/graphql/language/printer.rb +324 -151
- data/lib/graphql/language/sanitized_printer.rb +21 -23
- data/lib/graphql/language/static_visitor.rb +171 -0
- data/lib/graphql/language/visitor.rb +62 -119
- data/lib/graphql/language.rb +71 -1
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +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 +88 -144
- data/lib/graphql/query/null_context.rb +15 -18
- data/lib/graphql/query/partial.rb +194 -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 +135 -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/runtime_error.rb +6 -0
- data/lib/graphql/schema/addition.rb +26 -13
- data/lib/graphql/schema/always_visible.rb +7 -2
- data/lib/graphql/schema/argument.rb +78 -14
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +140 -66
- data/lib/graphql/schema/directive/flagged.rb +4 -2
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive.rb +78 -12
- data/lib/graphql/schema/enum.rb +110 -27
- data/lib/graphql/schema/enum_value.rb +11 -3
- data/lib/graphql/schema/field/connection_extension.rb +4 -51
- data/lib/graphql/schema/field/scope_extension.rb +19 -7
- data/lib/graphql/schema/field.rb +245 -119
- data/lib/graphql/schema/field_extension.rb +12 -9
- data/lib/graphql/schema/has_single_input_argument.rb +160 -0
- data/lib/graphql/schema/input_object.rb +123 -65
- data/lib/graphql/schema/interface.rb +60 -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 +8 -4
- data/lib/graphql/schema/loader.rb +3 -4
- data/lib/graphql/schema/member/base_dsl_methods.rb +18 -12
- 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 +5 -5
- data/lib/graphql/schema/member/has_fields.rb +121 -17
- data/lib/graphql/schema/member/has_interfaces.rb +27 -13
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/member/relay_shortcuts.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +18 -5
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/non_null.rb +1 -1
- 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 +128 -32
- data/lib/graphql/schema/scalar.rb +4 -9
- data/lib/graphql/schema/subscription.rb +63 -12
- 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 +464 -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/wrapper.rb +7 -1
- data/lib/graphql/schema.rb +954 -212
- data/lib/graphql/static_validation/all_rules.rb +3 -3
- data/lib/graphql/static_validation/base_visitor.rb +96 -71
- data/lib/graphql/static_validation/literal_validator.rb +6 -7
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +2 -2
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +18 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +6 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +6 -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 +14 -3
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +59 -15
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +391 -262
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +6 -6
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +15 -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 +28 -8
- 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 +14 -2
- data/lib/graphql/static_validation/validation_context.rb +22 -6
- 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 +45 -19
- data/lib/graphql/subscriptions/event.rb +22 -4
- data/lib/graphql/subscriptions/serialize.rb +3 -1
- data/lib/graphql/subscriptions.rb +56 -17
- data/lib/graphql/testing/helpers.rb +161 -0
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +3 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +11 -3
- data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -158
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/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 +184 -34
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +864 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +82 -0
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +118 -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 +45 -3
- data/lib/graphql/types/relay/edge_behaviors.rb +19 -1
- 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 +9 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +69 -54
- data/readme.md +12 -2
- metadata +236 -40
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -276
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/backtrace/inspect_result.rb +0 -50
- data/lib/graphql/backtrace/trace.rb +0 -96
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/language/parser.y +0 -560
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
class Schema
|
|
4
|
+
module RactorShareable
|
|
5
|
+
def self.extended(schema_class)
|
|
6
|
+
schema_class.extend(SchemaExtension)
|
|
7
|
+
schema_class.freeze_schema
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module SchemaExtension
|
|
11
|
+
|
|
12
|
+
def freeze_error_handlers(handlers)
|
|
13
|
+
handlers[:subclass_handlers].default_proc = nil
|
|
14
|
+
handlers[:subclass_handlers].each do |_class, subclass_handlers|
|
|
15
|
+
freeze_error_handlers(subclass_handlers)
|
|
16
|
+
end
|
|
17
|
+
Ractor.make_shareable(handlers)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def freeze_schema
|
|
21
|
+
# warm some ivars:
|
|
22
|
+
default_analysis_engine
|
|
23
|
+
default_execution_strategy
|
|
24
|
+
GraphQL.default_parser
|
|
25
|
+
default_logger
|
|
26
|
+
freeze_error_handlers(error_handlers)
|
|
27
|
+
# TODO: this freezes errors of parent classes which could cause trouble
|
|
28
|
+
parent_class = superclass
|
|
29
|
+
while parent_class.respond_to?(:error_handlers)
|
|
30
|
+
freeze_error_handlers(parent_class.error_handlers)
|
|
31
|
+
parent_class = parent_class.superclass
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
own_tracers.freeze
|
|
35
|
+
@frozen_tracers = tracers.freeze
|
|
36
|
+
own_trace_modes.each do |m|
|
|
37
|
+
trace_options_for(m)
|
|
38
|
+
build_trace_mode(m)
|
|
39
|
+
end
|
|
40
|
+
build_trace_mode(:default)
|
|
41
|
+
Ractor.make_shareable(@trace_options_for_mode)
|
|
42
|
+
Ractor.make_shareable(own_trace_modes)
|
|
43
|
+
Ractor.make_shareable(own_multiplex_analyzers)
|
|
44
|
+
@frozen_multiplex_analyzers = Ractor.make_shareable(multiplex_analyzers)
|
|
45
|
+
Ractor.make_shareable(own_query_analyzers)
|
|
46
|
+
@frozen_query_analyzers = Ractor.make_shareable(query_analyzers)
|
|
47
|
+
Ractor.make_shareable(own_plugins)
|
|
48
|
+
own_plugins.each do |(plugin, options)|
|
|
49
|
+
Ractor.make_shareable(plugin)
|
|
50
|
+
Ractor.make_shareable(options)
|
|
51
|
+
end
|
|
52
|
+
@frozen_plugins = Ractor.make_shareable(plugins)
|
|
53
|
+
Ractor.make_shareable(own_references_to)
|
|
54
|
+
@frozen_directives = Ractor.make_shareable(directives)
|
|
55
|
+
|
|
56
|
+
Ractor.make_shareable(visibility)
|
|
57
|
+
Ractor.make_shareable(introspection_system)
|
|
58
|
+
extend(FrozenMethods)
|
|
59
|
+
|
|
60
|
+
Ractor.make_shareable(self)
|
|
61
|
+
superclass.respond_to?(:freeze_schema) && superclass.freeze_schema
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
module FrozenMethods
|
|
65
|
+
def tracers; @frozen_tracers; end
|
|
66
|
+
def multiplex_analyzers; @frozen_multiplex_analyzers; end
|
|
67
|
+
def query_analyzers; @frozen_query_analyzers; end
|
|
68
|
+
def plugins; @frozen_plugins; end
|
|
69
|
+
def directives; @frozen_directives; end
|
|
70
|
+
|
|
71
|
+
# This actually accumulates info during execution...
|
|
72
|
+
# How to support it?
|
|
73
|
+
def lazy?(_obj); false; end
|
|
74
|
+
def sync_lazy(obj); obj; end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
require "graphql/types/string"
|
|
3
2
|
|
|
4
3
|
module GraphQL
|
|
5
4
|
class Schema
|
|
@@ -21,6 +20,10 @@ module GraphQL
|
|
|
21
20
|
# @see {GraphQL::Schema::Mutation} for an example, it's basically the same.
|
|
22
21
|
#
|
|
23
22
|
class RelayClassicMutation < GraphQL::Schema::Mutation
|
|
23
|
+
include GraphQL::Schema::HasSingleInputArgument
|
|
24
|
+
|
|
25
|
+
argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
|
|
26
|
+
|
|
24
27
|
# The payload should always include this field
|
|
25
28
|
field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.")
|
|
26
29
|
# Relay classic default:
|
|
@@ -31,34 +34,14 @@ module GraphQL
|
|
|
31
34
|
def resolve_with_support(**inputs)
|
|
32
35
|
input = inputs[:input].to_kwargs
|
|
33
36
|
|
|
34
|
-
new_extras = field ? field.extras : []
|
|
35
|
-
all_extras = self.class.extras + new_extras
|
|
36
|
-
|
|
37
|
-
# Transfer these from the top-level hash to the
|
|
38
|
-
# shortcutted `input:` object
|
|
39
|
-
all_extras.each do |ext|
|
|
40
|
-
# It's possible that the `extra` was not passed along by this point,
|
|
41
|
-
# don't re-add it if it wasn't given here.
|
|
42
|
-
if inputs.key?(ext)
|
|
43
|
-
input[ext] = inputs[ext]
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
37
|
if input
|
|
48
38
|
# This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
|
|
49
39
|
input_kwargs = input.to_h
|
|
50
40
|
client_mutation_id = input_kwargs.delete(:client_mutation_id)
|
|
51
|
-
|
|
52
|
-
# Relay Classic Mutations with no `argument`s
|
|
53
|
-
# don't require `input:`
|
|
54
|
-
input_kwargs = {}
|
|
41
|
+
inputs[:input] = input_kwargs
|
|
55
42
|
end
|
|
56
43
|
|
|
57
|
-
return_value =
|
|
58
|
-
super(**input_kwargs)
|
|
59
|
-
else
|
|
60
|
-
super()
|
|
61
|
-
end
|
|
44
|
+
return_value = super(**inputs)
|
|
62
45
|
|
|
63
46
|
context.query.after_lazy(return_value) do |return_hash|
|
|
64
47
|
# It might be an error
|
|
@@ -68,112 +51,6 @@ module GraphQL
|
|
|
68
51
|
return_hash
|
|
69
52
|
end
|
|
70
53
|
end
|
|
71
|
-
|
|
72
|
-
class << self
|
|
73
|
-
def dummy
|
|
74
|
-
@dummy ||= begin
|
|
75
|
-
d = Class.new(GraphQL::Schema::Resolver)
|
|
76
|
-
d.argument_class(self.argument_class)
|
|
77
|
-
# TODO make this lazier?
|
|
78
|
-
d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
|
|
79
|
-
d
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def field_arguments(context = GraphQL::Query::NullContext)
|
|
84
|
-
dummy.arguments(context)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def get_field_argument(name, context = GraphQL::Query::NullContext)
|
|
88
|
-
dummy.get_argument(name, context)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def own_field_arguments
|
|
92
|
-
dummy.own_arguments
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def all_field_argument_definitions
|
|
96
|
-
dummy.all_argument_definitions
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Also apply this argument to the input type:
|
|
100
|
-
def argument(*args, own_argument: false, **kwargs, &block)
|
|
101
|
-
it = input_type # make sure any inherited arguments are already added to it
|
|
102
|
-
arg = super(*args, **kwargs, &block)
|
|
103
|
-
|
|
104
|
-
# This definition might be overriding something inherited;
|
|
105
|
-
# if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
|
|
106
|
-
prev_args = it.own_arguments[arg.graphql_name]
|
|
107
|
-
case prev_args
|
|
108
|
-
when GraphQL::Schema::Argument
|
|
109
|
-
if prev_args.owner != self
|
|
110
|
-
it.own_arguments.delete(arg.graphql_name)
|
|
111
|
-
end
|
|
112
|
-
when Array
|
|
113
|
-
prev_args.reject! { |a| a.owner != self }
|
|
114
|
-
if prev_args.empty?
|
|
115
|
-
it.own_arguments.delete(arg.graphql_name)
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
it.add_argument(arg)
|
|
120
|
-
arg
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# The base class for generated input object types
|
|
124
|
-
# @param new_class [Class] The base class to use for generating input object definitions
|
|
125
|
-
# @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
|
|
126
|
-
def input_object_class(new_class = nil)
|
|
127
|
-
if new_class
|
|
128
|
-
@input_object_class = new_class
|
|
129
|
-
end
|
|
130
|
-
@input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# @param new_input_type [Class, nil] If provided, it configures this mutation to accept `new_input_type` instead of generating an input type
|
|
134
|
-
# @return [Class] The generated {Schema::InputObject} class for this mutation's `input`
|
|
135
|
-
def input_type(new_input_type = nil)
|
|
136
|
-
if new_input_type
|
|
137
|
-
@input_type = new_input_type
|
|
138
|
-
end
|
|
139
|
-
@input_type ||= generate_input_type
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
private
|
|
143
|
-
|
|
144
|
-
# Generate the input type for the `input:` argument
|
|
145
|
-
# To customize how input objects are generated, override this method
|
|
146
|
-
# @return [Class] a subclass of {.input_object_class}
|
|
147
|
-
def generate_input_type
|
|
148
|
-
mutation_args = all_argument_definitions
|
|
149
|
-
mutation_class = self
|
|
150
|
-
Class.new(input_object_class) do
|
|
151
|
-
class << self
|
|
152
|
-
def default_graphql_name
|
|
153
|
-
"#{self.mutation.graphql_name}Input"
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
def description(new_desc = nil)
|
|
157
|
-
super || "Autogenerated input type of #{self.mutation.graphql_name}"
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
mutation(mutation_class)
|
|
161
|
-
# these might be inherited:
|
|
162
|
-
mutation_args.each do |arg|
|
|
163
|
-
add_argument(arg)
|
|
164
|
-
end
|
|
165
|
-
argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
private
|
|
171
|
-
|
|
172
|
-
def authorize_arguments(args, values)
|
|
173
|
-
# remove the `input` wrapper to match values
|
|
174
|
-
input_args = args["input"].type.unwrap.arguments(context)
|
|
175
|
-
super(input_args, values)
|
|
176
|
-
end
|
|
177
54
|
end
|
|
178
55
|
end
|
|
179
56
|
end
|
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
|
8
8
|
# - Arguments, via `.argument(...)` helper, which will be applied to the field.
|
|
9
9
|
# - Return type, via `.type(..., null: ...)`, which will be applied to the field.
|
|
10
10
|
# - Description, via `.description(...)`, which will be applied to the field
|
|
11
|
+
# - Comment, via `.comment(...)`, which will be applied to the field
|
|
11
12
|
# - Resolution, via `#resolve(**args)` method, which will be called to resolve the field.
|
|
12
13
|
# - `#object` and `#context` accessors for use during `#resolve`.
|
|
13
14
|
#
|
|
@@ -19,12 +20,16 @@ module GraphQL
|
|
|
19
20
|
# @see {GraphQL::Function} `Resolver` is a replacement for `GraphQL::Function`
|
|
20
21
|
class Resolver
|
|
21
22
|
include Schema::Member::GraphQLTypeNames
|
|
22
|
-
# Really we only need description from here, but:
|
|
23
|
+
# Really we only need description & comment from here, but:
|
|
23
24
|
extend Schema::Member::BaseDSLMethods
|
|
24
25
|
extend GraphQL::Schema::Member::HasArguments
|
|
26
|
+
extend GraphQL::Schema::Member::HasAuthorization
|
|
25
27
|
extend GraphQL::Schema::Member::HasValidators
|
|
26
28
|
include Schema::Member::HasPath
|
|
27
29
|
extend Schema::Member::HasPath
|
|
30
|
+
extend Schema::Member::HasDirectives
|
|
31
|
+
include Schema::Member::HasDataloader
|
|
32
|
+
extend Schema::Member::HasDeprecationReason
|
|
28
33
|
|
|
29
34
|
# @param object [Object] The application object that this field is being resolved on
|
|
30
35
|
# @param context [GraphQL::Query::Context]
|
|
@@ -35,26 +40,98 @@ module GraphQL
|
|
|
35
40
|
@field = field
|
|
36
41
|
# Since this hash is constantly rebuilt, cache it for this call
|
|
37
42
|
@arguments_by_keyword = {}
|
|
38
|
-
|
|
43
|
+
context.types.arguments(self.class).each do |arg|
|
|
39
44
|
@arguments_by_keyword[arg.keyword] = arg
|
|
40
45
|
end
|
|
41
46
|
@prepared_arguments = nil
|
|
42
47
|
end
|
|
43
48
|
|
|
49
|
+
attr_accessor :exec_result, :exec_index, :field_resolve_step, :raw_arguments
|
|
50
|
+
|
|
44
51
|
# @return [Object] The application object this field is being resolved on
|
|
45
|
-
|
|
52
|
+
attr_accessor :object
|
|
46
53
|
|
|
47
54
|
# @return [GraphQL::Query::Context]
|
|
48
55
|
attr_reader :context
|
|
49
56
|
|
|
50
|
-
# @return [GraphQL::Dataloader]
|
|
51
|
-
def dataloader
|
|
52
|
-
context.dataloader
|
|
53
|
-
end
|
|
54
|
-
|
|
55
57
|
# @return [GraphQL::Schema::Field]
|
|
56
58
|
attr_reader :field
|
|
57
59
|
|
|
60
|
+
attr_writer :prepared_arguments
|
|
61
|
+
|
|
62
|
+
def call
|
|
63
|
+
if self.class < Schema::HasSingleInputArgument
|
|
64
|
+
@prepared_arguments = @prepared_arguments[:input]
|
|
65
|
+
end
|
|
66
|
+
q = context.query
|
|
67
|
+
trace_objs = [object]
|
|
68
|
+
q.current_trace.begin_execute_field(field, @prepared_arguments, trace_objs, q)
|
|
69
|
+
is_ready = ready?(**@prepared_arguments)
|
|
70
|
+
runner = @field_resolve_step.runner
|
|
71
|
+
if runner.resolves_lazies && runner.schema.lazy?(is_ready)
|
|
72
|
+
is_ready, new_return_value = runner.schema.sync_lazy(is_ready)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
if is_ready.is_a?(Array)
|
|
76
|
+
is_ready, new_return_value = is_ready
|
|
77
|
+
if is_ready != false
|
|
78
|
+
raise "Unexpected result from #ready? (expected `true`, `false` or `[false, {...}]`): [#{is_ready.inspect}, #{new_return_value.inspect}]"
|
|
79
|
+
else
|
|
80
|
+
new_return_value
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
if is_ready
|
|
85
|
+
begin
|
|
86
|
+
is_authed, new_return_value = authorized?(**@prepared_arguments)
|
|
87
|
+
rescue GraphQL::UnauthorizedError => err
|
|
88
|
+
new_return_value = q.schema.unauthorized_object(err)
|
|
89
|
+
is_authed = true # the error was handled
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
if runner.resolves_lazies && runner.schema.lazy?(is_authed)
|
|
94
|
+
is_authed, new_return_value = runner.schema.sync_lazy(is_authed)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
result = if is_authed
|
|
98
|
+
Schema::Validator.validate!(self.class.validators, object, context, @prepared_arguments, as: @field)
|
|
99
|
+
if q.subscription? && @field.owner == context.schema.subscription
|
|
100
|
+
# This needs to use arguments without `loads:`. TODO extract this into subscription-related code somehow?
|
|
101
|
+
@original_arguments = @field_resolve_step.runner.input_values[q].argument_values(@field, @field_resolve_step.ast_node.arguments, nil)
|
|
102
|
+
end
|
|
103
|
+
call_resolve(@prepared_arguments)
|
|
104
|
+
elsif new_return_value.nil?
|
|
105
|
+
err = UnauthorizedFieldError.new(object: object, type: @field_resolve_step.parent_type, context: context, field: @field)
|
|
106
|
+
context.schema.unauthorized_field(err)
|
|
107
|
+
else
|
|
108
|
+
new_return_value
|
|
109
|
+
end
|
|
110
|
+
q = context.query
|
|
111
|
+
q.current_trace.end_execute_field(field, @prepared_arguments, trace_objs, q, [result])
|
|
112
|
+
exec_result[exec_index] = result
|
|
113
|
+
rescue GraphQL::UnauthorizedError => auth_err
|
|
114
|
+
exec_result[exec_index] = begin
|
|
115
|
+
context.schema.unauthorized_object(auth_err)
|
|
116
|
+
rescue GraphQL::ExecutionError => exec_err
|
|
117
|
+
exec_err
|
|
118
|
+
end
|
|
119
|
+
rescue GraphQL::RuntimeError => err
|
|
120
|
+
exec_result[exec_index] = err
|
|
121
|
+
rescue StandardError => stderr
|
|
122
|
+
exec_result[exec_index] = begin
|
|
123
|
+
context.query.handle_or_reraise(stderr)
|
|
124
|
+
rescue GraphQL::ExecutionError => ex_err
|
|
125
|
+
ex_err
|
|
126
|
+
end
|
|
127
|
+
ensure
|
|
128
|
+
field_pending_steps = field_resolve_step.pending_steps
|
|
129
|
+
field_pending_steps.delete(self)
|
|
130
|
+
if field_pending_steps.size == 0 && field_resolve_step.field_results
|
|
131
|
+
field_resolve_step.runner.add_step(field_resolve_step)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
58
135
|
def arguments
|
|
59
136
|
@prepared_arguments || raise("Arguments have not been prepared yet, still waiting for #load_arguments to resolve. (Call `.arguments` later in the code.)")
|
|
60
137
|
end
|
|
@@ -65,7 +142,7 @@ module GraphQL
|
|
|
65
142
|
# @api private
|
|
66
143
|
def resolve_with_support(**args)
|
|
67
144
|
# First call the ready? hook which may raise
|
|
68
|
-
raw_ready_val = if args.
|
|
145
|
+
raw_ready_val = if !args.empty?
|
|
69
146
|
ready?(**args)
|
|
70
147
|
else
|
|
71
148
|
ready?
|
|
@@ -86,7 +163,7 @@ module GraphQL
|
|
|
86
163
|
@prepared_arguments = loaded_args
|
|
87
164
|
Schema::Validator.validate!(self.class.validators, object, context, loaded_args, as: @field)
|
|
88
165
|
# Then call `authorized?`, which may raise or may return a lazy object
|
|
89
|
-
raw_authorized_val = if loaded_args.
|
|
166
|
+
raw_authorized_val = if !loaded_args.empty?
|
|
90
167
|
authorized?(**loaded_args)
|
|
91
168
|
else
|
|
92
169
|
authorized?
|
|
@@ -103,11 +180,7 @@ module GraphQL
|
|
|
103
180
|
end
|
|
104
181
|
elsif authorized_val
|
|
105
182
|
# Finally, all the hooks have passed, so resolve it
|
|
106
|
-
|
|
107
|
-
public_send(self.class.resolve_method, **loaded_args)
|
|
108
|
-
else
|
|
109
|
-
public_send(self.class.resolve_method)
|
|
110
|
-
end
|
|
183
|
+
call_resolve(loaded_args)
|
|
111
184
|
else
|
|
112
185
|
raise GraphQL::UnauthorizedFieldError.new(context: context, object: object, type: field.owner, field: field)
|
|
113
186
|
end
|
|
@@ -117,6 +190,15 @@ module GraphQL
|
|
|
117
190
|
end
|
|
118
191
|
end
|
|
119
192
|
|
|
193
|
+
# @api private {GraphQL::Schema::Mutation} uses this to clear the dataloader cache
|
|
194
|
+
def call_resolve(args_hash)
|
|
195
|
+
if !args_hash.empty?
|
|
196
|
+
public_send(self.class.resolve_method, **args_hash)
|
|
197
|
+
else
|
|
198
|
+
public_send(self.class.resolve_method)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
120
202
|
# Do the work. Everything happens here.
|
|
121
203
|
# @return [Object] An object corresponding to the return type
|
|
122
204
|
def resolve(**args)
|
|
@@ -146,10 +228,14 @@ module GraphQL
|
|
|
146
228
|
# @return [Boolean, early_return_data] If `false`, execution will stop (and `early_return_data` will be returned instead, if present.)
|
|
147
229
|
def authorized?(**inputs)
|
|
148
230
|
arg_owner = @field # || self.class
|
|
149
|
-
args =
|
|
231
|
+
args = context.types.arguments(arg_owner)
|
|
150
232
|
authorize_arguments(args, inputs)
|
|
151
233
|
end
|
|
152
234
|
|
|
235
|
+
def self.authorizes?(context)
|
|
236
|
+
self.instance_method(:authorized?).owner != GraphQL::Schema::Resolver
|
|
237
|
+
end
|
|
238
|
+
|
|
153
239
|
# Called when an object loaded by `loads:` fails the `.authorized?` check for its resolved GraphQL object type.
|
|
154
240
|
#
|
|
155
241
|
# By default, the error is re-raised and passed along to {{Schema.unauthorized_object}}.
|
|
@@ -163,19 +249,22 @@ module GraphQL
|
|
|
163
249
|
private
|
|
164
250
|
|
|
165
251
|
def authorize_arguments(args, inputs)
|
|
166
|
-
args.
|
|
252
|
+
args.each do |argument|
|
|
167
253
|
arg_keyword = argument.keyword
|
|
168
254
|
if inputs.key?(arg_keyword) && !(arg_value = inputs[arg_keyword]).nil? && (arg_value != argument.default_value)
|
|
169
|
-
|
|
170
|
-
if
|
|
171
|
-
return
|
|
172
|
-
|
|
173
|
-
|
|
255
|
+
auth_result = argument.authorized?(self, arg_value, context)
|
|
256
|
+
if auth_result.is_a?(Array)
|
|
257
|
+
# only return this second value if the application returned a second value
|
|
258
|
+
arg_auth, err = auth_result
|
|
259
|
+
if !arg_auth
|
|
260
|
+
return arg_auth, err
|
|
261
|
+
end
|
|
262
|
+
elsif auth_result == false
|
|
263
|
+
return auth_result
|
|
174
264
|
end
|
|
175
|
-
else
|
|
176
|
-
true
|
|
177
265
|
end
|
|
178
266
|
end
|
|
267
|
+
true
|
|
179
268
|
end
|
|
180
269
|
|
|
181
270
|
def load_arguments(args)
|
|
@@ -198,23 +287,27 @@ module GraphQL
|
|
|
198
287
|
end
|
|
199
288
|
|
|
200
289
|
# Avoid returning a lazy if none are needed
|
|
201
|
-
if prepare_lazies.
|
|
290
|
+
if !prepare_lazies.empty?
|
|
202
291
|
GraphQL::Execution::Lazy.all(prepare_lazies).then { prepared_args }
|
|
203
292
|
else
|
|
204
293
|
prepared_args
|
|
205
294
|
end
|
|
206
295
|
end
|
|
207
296
|
|
|
208
|
-
def get_argument(name, context = GraphQL::Query::NullContext)
|
|
297
|
+
def get_argument(name, context = GraphQL::Query::NullContext.instance)
|
|
209
298
|
self.class.get_argument(name, context)
|
|
210
299
|
end
|
|
211
300
|
|
|
212
301
|
class << self
|
|
213
|
-
def field_arguments(context = GraphQL::Query::NullContext)
|
|
302
|
+
def field_arguments(context = GraphQL::Query::NullContext.instance)
|
|
214
303
|
arguments(context)
|
|
215
304
|
end
|
|
216
305
|
|
|
217
|
-
def
|
|
306
|
+
def any_field_arguments?
|
|
307
|
+
any_arguments?
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def get_field_argument(name, context = GraphQL::Query::NullContext.instance)
|
|
218
311
|
get_argument(name, context)
|
|
219
312
|
end
|
|
220
313
|
|
|
@@ -380,7 +473,7 @@ module GraphQL
|
|
|
380
473
|
if superclass.respond_to?(:extensions)
|
|
381
474
|
s_exts = superclass.extensions
|
|
382
475
|
if own_exts
|
|
383
|
-
if s_exts.
|
|
476
|
+
if !s_exts.empty?
|
|
384
477
|
own_exts + s_exts
|
|
385
478
|
else
|
|
386
479
|
own_exts
|
|
@@ -393,11 +486,14 @@ module GraphQL
|
|
|
393
486
|
end
|
|
394
487
|
end
|
|
395
488
|
|
|
489
|
+
def inherited(child_class)
|
|
490
|
+
child_class.description(description)
|
|
491
|
+
super
|
|
492
|
+
end
|
|
493
|
+
|
|
396
494
|
private
|
|
397
495
|
|
|
398
|
-
|
|
399
|
-
@own_extensions
|
|
400
|
-
end
|
|
496
|
+
attr_reader :own_extensions
|
|
401
497
|
end
|
|
402
498
|
end
|
|
403
499
|
end
|
|
@@ -19,9 +19,9 @@ module GraphQL
|
|
|
19
19
|
|
|
20
20
|
def specified_by_url(new_url = nil)
|
|
21
21
|
if new_url
|
|
22
|
-
|
|
23
|
-
elsif
|
|
24
|
-
|
|
22
|
+
directive(GraphQL::Schema::Directive::SpecifiedBy, url: new_url)
|
|
23
|
+
elsif (directive = directives.find { |dir| dir.graphql_name == "specifiedBy" })
|
|
24
|
+
directive.arguments[:url] # rubocop:disable Development/ContextIsPassedCop
|
|
25
25
|
elsif superclass.respond_to?(:specified_by_url)
|
|
26
26
|
superclass.specified_by_url
|
|
27
27
|
else
|
|
@@ -50,12 +50,7 @@ module GraphQL
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
if coerced_result.nil?
|
|
53
|
-
|
|
54
|
-
""
|
|
55
|
-
else
|
|
56
|
-
" #{GraphQL::Language.serialize(value)}"
|
|
57
|
-
end
|
|
58
|
-
Query::InputValidationResult.from_problem("Could not coerce value#{str_value} to #{graphql_name}")
|
|
53
|
+
Query::InputValidationResult.from_problem("Could not coerce value #{GraphQL::Language.serialize(value)} to #{graphql_name}")
|
|
59
54
|
elsif coerced_result.is_a?(GraphQL::CoercionError)
|
|
60
55
|
Query::InputValidationResult.from_problem(coerced_result.message, message: coerced_result.message, extensions: coerced_result.extensions)
|
|
61
56
|
else
|
|
@@ -15,33 +15,47 @@ module GraphQL
|
|
|
15
15
|
extend GraphQL::Schema::Resolver::HasPayloadType
|
|
16
16
|
extend GraphQL::Schema::Member::HasFields
|
|
17
17
|
NO_UPDATE = :no_update
|
|
18
|
-
# The generated payload type is required; If there's no payload,
|
|
19
|
-
# propagate null.
|
|
20
18
|
null false
|
|
21
19
|
|
|
20
|
+
# @api private
|
|
22
21
|
def initialize(object:, context:, field:)
|
|
23
22
|
super
|
|
24
23
|
# Figure out whether this is an update or an initial subscription
|
|
25
24
|
@mode = context.query.subscription_update? ? :update : :subscribe
|
|
25
|
+
@subscription_written = false
|
|
26
|
+
@original_arguments = nil
|
|
27
|
+
if (subs_ns = context.namespace(:subscriptions)) &&
|
|
28
|
+
(sub_insts = subs_ns[:subscriptions])
|
|
29
|
+
sub_insts[context.current_path] = self
|
|
30
|
+
end
|
|
26
31
|
end
|
|
27
32
|
|
|
33
|
+
# @api private
|
|
28
34
|
def resolve_with_support(**args)
|
|
35
|
+
@original_arguments = args # before `loads:` have been run
|
|
29
36
|
result = nil
|
|
30
37
|
unsubscribed = true
|
|
31
|
-
catch :graphql_subscription_unsubscribed do
|
|
38
|
+
unsubscribed_result = catch :graphql_subscription_unsubscribed do
|
|
32
39
|
result = super
|
|
33
40
|
unsubscribed = false
|
|
34
41
|
end
|
|
35
42
|
|
|
36
43
|
|
|
37
44
|
if unsubscribed
|
|
38
|
-
|
|
45
|
+
if unsubscribed_result
|
|
46
|
+
context.namespace(:subscriptions)[:final_update] = true
|
|
47
|
+
unsubscribed_result
|
|
48
|
+
else
|
|
49
|
+
context.skip
|
|
50
|
+
end
|
|
39
51
|
else
|
|
40
52
|
result
|
|
41
53
|
end
|
|
42
54
|
end
|
|
43
55
|
|
|
44
|
-
# Implement the {Resolve} API
|
|
56
|
+
# Implement the {Resolve} API.
|
|
57
|
+
# You can implement this if you want code to run for _both_ the initial subscription
|
|
58
|
+
# and for later updates. Or, implement {#subscribe} and {#update}
|
|
45
59
|
def resolve(**args)
|
|
46
60
|
# Dispatch based on `@mode`, which will raise a `NoMethodError` if we ever
|
|
47
61
|
# have an unexpected `@mode`
|
|
@@ -49,8 +63,9 @@ module GraphQL
|
|
|
49
63
|
end
|
|
50
64
|
|
|
51
65
|
# Wrap the user-defined `#subscribe` hook
|
|
66
|
+
# @api private
|
|
52
67
|
def resolve_subscribe(**args)
|
|
53
|
-
ret_val = args.
|
|
68
|
+
ret_val = !args.empty? ? subscribe(**args) : subscribe
|
|
54
69
|
if ret_val == :no_response
|
|
55
70
|
context.skip
|
|
56
71
|
else
|
|
@@ -66,8 +81,9 @@ module GraphQL
|
|
|
66
81
|
end
|
|
67
82
|
|
|
68
83
|
# Wrap the user-provided `#update` hook
|
|
84
|
+
# @api private
|
|
69
85
|
def resolve_update(**args)
|
|
70
|
-
ret_val = args.
|
|
86
|
+
ret_val = !args.empty? ? update(**args) : update
|
|
71
87
|
if ret_val == NO_UPDATE
|
|
72
88
|
context.namespace(:subscriptions)[:no_update] = true
|
|
73
89
|
context.skip
|
|
@@ -94,19 +110,20 @@ module GraphQL
|
|
|
94
110
|
end
|
|
95
111
|
|
|
96
112
|
# Call this to halt execution and remove this subscription from the system
|
|
97
|
-
|
|
113
|
+
# @param update_value [Object] if present, deliver this update before unsubscribing
|
|
114
|
+
# @return [void]
|
|
115
|
+
def unsubscribe(update_value = nil)
|
|
98
116
|
context.namespace(:subscriptions)[:unsubscribed] = true
|
|
99
|
-
throw :graphql_subscription_unsubscribed
|
|
117
|
+
throw :graphql_subscription_unsubscribed, update_value
|
|
100
118
|
end
|
|
101
119
|
|
|
102
|
-
READING_SCOPE = ::Object.new
|
|
103
120
|
# Call this method to provide a new subscription_scope; OR
|
|
104
121
|
# call it without an argument to get the subscription_scope
|
|
105
122
|
# @param new_scope [Symbol]
|
|
106
123
|
# @param optional [Boolean] If true, then don't require `scope:` to be provided to updates to this subscription.
|
|
107
124
|
# @return [Symbol]
|
|
108
|
-
def self.subscription_scope(new_scope =
|
|
109
|
-
if new_scope !=
|
|
125
|
+
def self.subscription_scope(new_scope = NOT_CONFIGURED, optional: false)
|
|
126
|
+
if new_scope != NOT_CONFIGURED
|
|
110
127
|
@subscription_scope = new_scope
|
|
111
128
|
@subscription_scope_optional = optional
|
|
112
129
|
elsif defined?(@subscription_scope)
|
|
@@ -143,6 +160,40 @@ module GraphQL
|
|
|
143
160
|
def self.topic_for(arguments:, field:, scope:)
|
|
144
161
|
Subscriptions::Serialize.dump_recursive([scope, field.graphql_name, arguments])
|
|
145
162
|
end
|
|
163
|
+
|
|
164
|
+
# Calls through to `schema.subscriptions` to register this subscription with the backend.
|
|
165
|
+
# This is automatically called by GraphQL-Ruby after a query finishes successfully,
|
|
166
|
+
# but if you need to commit the subscription during `#subscribe`, you can call it there.
|
|
167
|
+
# (This method also sets a flag showing that this subscription was already written.)
|
|
168
|
+
#
|
|
169
|
+
# If you call this method yourself, you may also need to {#unsubscribe}
|
|
170
|
+
# or call `subscriptions.delete_subscription` to clean up the database if the query crashes with an error
|
|
171
|
+
# later in execution.
|
|
172
|
+
# @return [void]
|
|
173
|
+
def write_subscription
|
|
174
|
+
if subscription_written?
|
|
175
|
+
raise GraphQL::Error, "`write_subscription` was called but `#{self.class}#subscription_written?` is already true. Remove a call to `write subscription`."
|
|
176
|
+
else
|
|
177
|
+
@subscription_written = true
|
|
178
|
+
context.schema.subscriptions.write_subscription(context.query, [event])
|
|
179
|
+
end
|
|
180
|
+
nil
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @return [Boolean] `true` if {#write_subscription} was called already
|
|
184
|
+
def subscription_written?
|
|
185
|
+
@subscription_written
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# @return [Subscriptions::Event] This object is used as a representation of this subscription for the backend
|
|
189
|
+
def event
|
|
190
|
+
@event ||= Subscriptions::Event.new(
|
|
191
|
+
name: field.name,
|
|
192
|
+
arguments: @original_arguments,
|
|
193
|
+
context: context,
|
|
194
|
+
field: field,
|
|
195
|
+
)
|
|
196
|
+
end
|
|
146
197
|
end
|
|
147
198
|
end
|
|
148
199
|
end
|