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
|
@@ -71,15 +71,23 @@ module GraphQL
|
|
|
71
71
|
def execute_field(query:, field:, **_rest)
|
|
72
72
|
timeout_state = query.context.namespace(@timeout).fetch(:state)
|
|
73
73
|
# If the `:state` is `false`, then `max_seconds(query)` opted out of timeout for this query.
|
|
74
|
-
if timeout_state
|
|
74
|
+
if timeout_state == false
|
|
75
|
+
super
|
|
76
|
+
elsif Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) > timeout_state.fetch(:timeout_at)
|
|
75
77
|
error = GraphQL::Schema::Timeout::TimeoutError.new(field)
|
|
76
78
|
# Only invoke the timeout callback for the first timeout
|
|
77
79
|
if !timeout_state[:timed_out]
|
|
78
80
|
timeout_state[:timed_out] = true
|
|
79
81
|
@timeout.handle_timeout(error, query)
|
|
82
|
+
timeout_state = query.context.namespace(@timeout).fetch(:state)
|
|
80
83
|
end
|
|
81
84
|
|
|
82
|
-
|
|
85
|
+
# `handle_timeout` may have set this to be `false`
|
|
86
|
+
if timeout_state != false
|
|
87
|
+
error
|
|
88
|
+
else
|
|
89
|
+
super
|
|
90
|
+
end
|
|
83
91
|
else
|
|
84
92
|
super
|
|
85
93
|
end
|
|
@@ -102,6 +110,15 @@ module GraphQL
|
|
|
102
110
|
# override to do something interesting
|
|
103
111
|
end
|
|
104
112
|
|
|
113
|
+
# Call this method (eg, from {#handle_timeout}) to disable timeout tracking
|
|
114
|
+
# for the given query.
|
|
115
|
+
# @param query [GraphQL::Query]
|
|
116
|
+
# @return [void]
|
|
117
|
+
def disable_timeout(query)
|
|
118
|
+
query.context.namespace(self)[:state] = false
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
|
|
105
122
|
# This error is raised when a query exceeds `max_seconds`.
|
|
106
123
|
# Since it's a child of {GraphQL::ExecutionError},
|
|
107
124
|
# its message will be added to the response's `errors` key.
|
|
@@ -5,13 +5,13 @@ module GraphQL
|
|
|
5
5
|
module TypeExpression
|
|
6
6
|
# Fetch a type from a type map by its AST specification.
|
|
7
7
|
# Return `nil` if not found.
|
|
8
|
-
# @param type_owner [#
|
|
8
|
+
# @param type_owner [#type] A thing for looking up types by name
|
|
9
9
|
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
|
10
10
|
# @return [Class, GraphQL::Schema::NonNull, GraphQL::Schema:List]
|
|
11
11
|
def self.build_type(type_owner, ast_node)
|
|
12
12
|
case ast_node
|
|
13
13
|
when GraphQL::Language::Nodes::TypeName
|
|
14
|
-
type_owner.
|
|
14
|
+
type_owner.type(ast_node.name) # rubocop:disable Development/ContextIsPassedCop -- this is a `context` or `warden`, it's already query-aware
|
|
15
15
|
when GraphQL::Language::Nodes::NonNullType
|
|
16
16
|
ast_inner_type = ast_node.of_type
|
|
17
17
|
inner_type = build_type(type_owner, ast_inner_type)
|
data/lib/graphql/schema/union.rb
CHANGED
|
@@ -10,8 +10,8 @@ module GraphQL
|
|
|
10
10
|
super
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
def possible_types(*types, context: GraphQL::Query::NullContext, **options)
|
|
14
|
-
if types.
|
|
13
|
+
def possible_types(*types, context: GraphQL::Query::NullContext.instance, **options)
|
|
14
|
+
if !types.empty?
|
|
15
15
|
types.each do |t|
|
|
16
16
|
assert_valid_union_member(t)
|
|
17
17
|
type_memberships << type_membership_class.new(self, t, **options)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
class Schema
|
|
5
|
+
class Validator
|
|
6
|
+
# Use this to validate each member of an array value.
|
|
7
|
+
#
|
|
8
|
+
# @example validate format of all strings in an array
|
|
9
|
+
#
|
|
10
|
+
# argument :handles, [String],
|
|
11
|
+
# validates: { all: { format: { with: /\A[a-z0-9_]+\Z/ } } }
|
|
12
|
+
#
|
|
13
|
+
# @example multiple validators can be combined
|
|
14
|
+
#
|
|
15
|
+
# argument :handles, [String],
|
|
16
|
+
# validates: { all: { format: { with: /\A[a-z0-9_]+\Z/ }, length: { maximum: 32 } } }
|
|
17
|
+
#
|
|
18
|
+
# @example any type can be used
|
|
19
|
+
#
|
|
20
|
+
# argument :choices, [Integer],
|
|
21
|
+
# validates: { all: { inclusion: { in: 1..12 } } }
|
|
22
|
+
#
|
|
23
|
+
class AllValidator < Validator
|
|
24
|
+
def initialize(validated:, allow_blank: false, allow_null: false, **validators)
|
|
25
|
+
super(validated: validated, allow_blank: allow_blank, allow_null: allow_null)
|
|
26
|
+
|
|
27
|
+
@validators = Validator.from_config(validated, validators)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def validate(object, context, value)
|
|
31
|
+
return EMPTY_ARRAY if permitted_empty_value?(value)
|
|
32
|
+
|
|
33
|
+
all_errors = EMPTY_ARRAY
|
|
34
|
+
|
|
35
|
+
value.each do |subvalue|
|
|
36
|
+
@validators.each do |validator|
|
|
37
|
+
errors = validator.validate(object, context, subvalue)
|
|
38
|
+
if errors &&
|
|
39
|
+
(errors.is_a?(Array) && errors != EMPTY_ARRAY) ||
|
|
40
|
+
(errors.is_a?(String))
|
|
41
|
+
if all_errors.frozen? # It's empty
|
|
42
|
+
all_errors = []
|
|
43
|
+
end
|
|
44
|
+
if errors.is_a?(String)
|
|
45
|
+
all_errors << errors
|
|
46
|
+
else
|
|
47
|
+
all_errors.concat(errors)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
unless all_errors.frozen?
|
|
54
|
+
all_errors.uniq!
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
all_errors
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -8,6 +8,13 @@ module GraphQL
|
|
|
8
8
|
#
|
|
9
9
|
# (This is for specifying mutually exclusive sets of arguments.)
|
|
10
10
|
#
|
|
11
|
+
# If you use {GraphQL::Schema::Visibility} to hide all the arguments in a `one_of: [..]` set,
|
|
12
|
+
# then a developer-facing {GraphQL::Error} will be raised during execution. Pass `allow_all_hidden: true` to
|
|
13
|
+
# skip validation in this case instead.
|
|
14
|
+
#
|
|
15
|
+
# This validator also implements `argument ... required: :nullable`. If an argument has `required: :nullable`
|
|
16
|
+
# but it's hidden with {GraphQL::Schema::Visibility}, then this validator doesn't run.
|
|
17
|
+
#
|
|
11
18
|
# @example Require exactly one of these arguments
|
|
12
19
|
#
|
|
13
20
|
# field :update_amount, IngredientAmount, null: false do
|
|
@@ -35,34 +42,64 @@ module GraphQL
|
|
|
35
42
|
# end
|
|
36
43
|
#
|
|
37
44
|
class RequiredValidator < Validator
|
|
38
|
-
# @param one_of [
|
|
45
|
+
# @param one_of [Array<Symbol>] A list of arguments, exactly one of which is required for this field
|
|
46
|
+
# @param argument [Symbol] An argument that is required for this field
|
|
47
|
+
# @param allow_all_hidden [Boolean] If `true`, then this validator won't run if all the `one_of: ...` arguments have been hidden
|
|
39
48
|
# @param message [String]
|
|
40
|
-
def initialize(one_of: nil, argument: nil,
|
|
49
|
+
def initialize(one_of: nil, argument: nil, allow_all_hidden: nil, message: nil, **default_options)
|
|
41
50
|
@one_of = if one_of
|
|
42
51
|
one_of
|
|
43
52
|
elsif argument
|
|
44
|
-
[argument]
|
|
53
|
+
[ argument ]
|
|
45
54
|
else
|
|
46
55
|
raise ArgumentError, "`one_of:` or `argument:` must be given in `validates required: {...}`"
|
|
47
56
|
end
|
|
57
|
+
@allow_all_hidden = allow_all_hidden.nil? ? !!argument : allow_all_hidden
|
|
48
58
|
@message = message
|
|
49
59
|
super(**default_options)
|
|
50
60
|
end
|
|
51
61
|
|
|
52
|
-
def validate(_object,
|
|
53
|
-
|
|
62
|
+
def validate(_object, context, value)
|
|
63
|
+
fully_matched_conditions = 0
|
|
64
|
+
partially_matched_conditions = 0
|
|
65
|
+
|
|
66
|
+
visible_keywords = context.types.arguments(@validated).map(&:keyword)
|
|
67
|
+
no_visible_conditions = true
|
|
54
68
|
|
|
55
69
|
if !value.nil?
|
|
56
70
|
@one_of.each do |one_of_condition|
|
|
57
71
|
case one_of_condition
|
|
58
72
|
when Symbol
|
|
73
|
+
if no_visible_conditions && visible_keywords.include?(one_of_condition)
|
|
74
|
+
no_visible_conditions = false
|
|
75
|
+
end
|
|
76
|
+
|
|
59
77
|
if value.key?(one_of_condition)
|
|
60
|
-
|
|
78
|
+
fully_matched_conditions += 1
|
|
61
79
|
end
|
|
62
80
|
when Array
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
any_match = false
|
|
82
|
+
full_match = true
|
|
83
|
+
|
|
84
|
+
one_of_condition.each do |k|
|
|
85
|
+
if no_visible_conditions && visible_keywords.include?(k)
|
|
86
|
+
no_visible_conditions = false
|
|
87
|
+
end
|
|
88
|
+
if value.key?(k)
|
|
89
|
+
any_match = true
|
|
90
|
+
else
|
|
91
|
+
full_match = false
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
partial_match = !full_match && any_match
|
|
96
|
+
|
|
97
|
+
if full_match
|
|
98
|
+
fully_matched_conditions += 1
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
if partial_match
|
|
102
|
+
partially_matched_conditions += 1
|
|
66
103
|
end
|
|
67
104
|
else
|
|
68
105
|
raise ArgumentError, "Unknown one_of condition: #{one_of_condition.inspect}"
|
|
@@ -70,11 +107,55 @@ module GraphQL
|
|
|
70
107
|
end
|
|
71
108
|
end
|
|
72
109
|
|
|
73
|
-
if
|
|
110
|
+
if no_visible_conditions
|
|
111
|
+
if @allow_all_hidden
|
|
112
|
+
return nil
|
|
113
|
+
else
|
|
114
|
+
raise GraphQL::Error, <<~ERR
|
|
115
|
+
#{@validated.path} validates `required: ...` but all required arguments were hidden.
|
|
116
|
+
|
|
117
|
+
Update your schema definition to allow the client to see some fields or skip validation by adding `required: { ..., allow_all_hidden: true }`
|
|
118
|
+
ERR
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
if fully_matched_conditions == 1 && partially_matched_conditions == 0
|
|
74
123
|
nil # OK
|
|
75
124
|
else
|
|
76
|
-
@message
|
|
125
|
+
@message || build_message(context)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def build_message(context)
|
|
130
|
+
argument_definitions = context.types.arguments(@validated)
|
|
131
|
+
|
|
132
|
+
required_names = @one_of.map do |arg_keyword|
|
|
133
|
+
if arg_keyword.is_a?(Array)
|
|
134
|
+
names = arg_keyword.map { |arg| arg_keyword_to_graphql_name(argument_definitions, arg) }
|
|
135
|
+
names.compact! # hidden arguments are `nil`
|
|
136
|
+
"(" + names.join(" and ") + ")"
|
|
137
|
+
else
|
|
138
|
+
arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
|
|
139
|
+
end
|
|
77
140
|
end
|
|
141
|
+
required_names.compact! # remove entries for hidden arguments
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
case required_names.size
|
|
145
|
+
when 0
|
|
146
|
+
# The required definitions were hidden from the client.
|
|
147
|
+
# Another option here would be to raise an error in the application....
|
|
148
|
+
"%{validated} is missing a required argument."
|
|
149
|
+
when 1
|
|
150
|
+
"%{validated} must include the following argument: #{required_names.first}."
|
|
151
|
+
else
|
|
152
|
+
"%{validated} must include exactly one of the following arguments: #{required_names.join(", ")}."
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def arg_keyword_to_graphql_name(argument_definitions, arg_keyword)
|
|
157
|
+
argument_definition = argument_definitions.find { |defn| defn.keyword == arg_keyword }
|
|
158
|
+
argument_definition&.graphql_name
|
|
78
159
|
end
|
|
79
160
|
end
|
|
80
161
|
end
|
|
@@ -143,7 +143,7 @@ module GraphQL
|
|
|
143
143
|
end
|
|
144
144
|
end
|
|
145
145
|
|
|
146
|
-
if all_errors.
|
|
146
|
+
if !all_errors.empty?
|
|
147
147
|
raise ValidationFailedError.new(errors: all_errors)
|
|
148
148
|
end
|
|
149
149
|
nil
|
|
@@ -169,3 +169,5 @@ require "graphql/schema/validator/allow_null_validator"
|
|
|
169
169
|
GraphQL::Schema::Validator.install(:allow_null, GraphQL::Schema::Validator::AllowNullValidator)
|
|
170
170
|
require "graphql/schema/validator/allow_blank_validator"
|
|
171
171
|
GraphQL::Schema::Validator.install(:allow_blank, GraphQL::Schema::Validator::AllowBlankValidator)
|
|
172
|
+
require "graphql/schema/validator/all_validator"
|
|
173
|
+
GraphQL::Schema::Validator.install(:all, GraphQL::Schema::Validator::AllValidator)
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module GraphQL
|
|
3
|
+
class Schema
|
|
4
|
+
class Visibility
|
|
5
|
+
# You can use this to see how {GraphQL::Schema::Warden} and {GraphQL::Schema::Visibility::Profile}
|
|
6
|
+
# handle `.visible?` differently in your schema.
|
|
7
|
+
#
|
|
8
|
+
# It runs the same method on both implementations and raises an error when the results diverge.
|
|
9
|
+
#
|
|
10
|
+
# To fix the error, modify your schema so that both implementations return the same thing.
|
|
11
|
+
# Or, open an issue on GitHub to discuss the difference.
|
|
12
|
+
#
|
|
13
|
+
# This plugin adds overhead to runtime and may cause unexpected crashes -- **don't** use it in production!
|
|
14
|
+
#
|
|
15
|
+
# This plugin adds two keys to `context` when running:
|
|
16
|
+
#
|
|
17
|
+
# - `visibility_migration_running: true`
|
|
18
|
+
# - For the {Schema::Warden} which it instantiates, it adds `visibility_migration_warden_running: true`.
|
|
19
|
+
#
|
|
20
|
+
# Use those keys to modify your `visible?` behavior as needed.
|
|
21
|
+
#
|
|
22
|
+
# Also, in a pinch, you can set `skip_visibility_migration_error: true` in context to turn off this behavior per-query.
|
|
23
|
+
# (In that case, it uses {Profile} directly.)
|
|
24
|
+
#
|
|
25
|
+
# @example Adding this plugin
|
|
26
|
+
#
|
|
27
|
+
# use GraphQL::Schema::Visibility, migration_errors: true
|
|
28
|
+
#
|
|
29
|
+
class Migration < GraphQL::Schema::Visibility::Profile
|
|
30
|
+
class RuntimeTypesMismatchError < GraphQL::Error
|
|
31
|
+
def initialize(method_called, warden_result, profile_result, method_args)
|
|
32
|
+
super(<<~ERR)
|
|
33
|
+
Mismatch in types for `##{method_called}(#{method_args.map(&:inspect).join(", ")})`:
|
|
34
|
+
|
|
35
|
+
#{compare_results(warden_result, profile_result)}
|
|
36
|
+
|
|
37
|
+
Update your `.visible?` implementation to make these implementations return the same value.
|
|
38
|
+
|
|
39
|
+
See: https://graphql-ruby.org/authorization/visibility_migration.html
|
|
40
|
+
ERR
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
def compare_results(warden_result, profile_result)
|
|
45
|
+
if warden_result.is_a?(Array) && profile_result.is_a?(Array)
|
|
46
|
+
all_results = warden_result | profile_result
|
|
47
|
+
all_results.sort_by!(&:graphql_name)
|
|
48
|
+
|
|
49
|
+
entries_text = all_results.map { |entry| "#{entry.graphql_name} (#{entry})"}
|
|
50
|
+
width = entries_text.map(&:size).max
|
|
51
|
+
yes = " ✔ "
|
|
52
|
+
no = " "
|
|
53
|
+
res = "".dup
|
|
54
|
+
res << "#{"Result".center(width)} Warden Profile \n"
|
|
55
|
+
all_results.each_with_index do |entry, idx|
|
|
56
|
+
res << "#{entries_text[idx].ljust(width)}#{warden_result.include?(entry) ? yes : no}#{profile_result.include?(entry) ? yes : no}\n"
|
|
57
|
+
end
|
|
58
|
+
res << "\n"
|
|
59
|
+
else
|
|
60
|
+
"- Warden returned: #{humanize(warden_result)}\n\n- Visibility::Profile returned: #{humanize(profile_result)}"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
def humanize(val)
|
|
64
|
+
case val
|
|
65
|
+
when Array
|
|
66
|
+
"#{val.size}: #{val.map { |v| humanize(v) }.sort.inspect}"
|
|
67
|
+
when Module
|
|
68
|
+
if val.respond_to?(:graphql_name)
|
|
69
|
+
"#{val.graphql_name} (#{val.inspect})"
|
|
70
|
+
else
|
|
71
|
+
val.inspect
|
|
72
|
+
end
|
|
73
|
+
else
|
|
74
|
+
val.inspect
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def initialize(context:, schema:, name: nil, visibility:)
|
|
80
|
+
@name = name
|
|
81
|
+
@skip_error = context[:skip_visibility_migration_error] || context.is_a?(Query::NullContext) || context.is_a?(Hash)
|
|
82
|
+
@profile_types = GraphQL::Schema::Visibility::Profile.new(context: context, schema: schema, visibility: visibility)
|
|
83
|
+
if !@skip_error
|
|
84
|
+
context[:visibility_migration_running] = true
|
|
85
|
+
warden_ctx_vals = context.to_h.dup
|
|
86
|
+
warden_ctx_vals[:visibility_migration_warden_running] = true
|
|
87
|
+
if schema.const_defined?(:WardenCompatSchema, false) # don't use a defn from a superclass
|
|
88
|
+
warden_schema = schema.const_get(:WardenCompatSchema, false)
|
|
89
|
+
else
|
|
90
|
+
warden_schema = Class.new(schema)
|
|
91
|
+
warden_schema.use_visibility_profile = false
|
|
92
|
+
# TODO public API
|
|
93
|
+
warden_schema.send(:add_type_and_traverse, [warden_schema.query, warden_schema.mutation, warden_schema.subscription].compact, root: true)
|
|
94
|
+
warden_schema.send(:add_type_and_traverse, warden_schema.directives.values + warden_schema.orphan_types, root: false)
|
|
95
|
+
schema.const_set(:WardenCompatSchema, warden_schema)
|
|
96
|
+
end
|
|
97
|
+
warden_ctx = GraphQL::Query::Context.new(query: context.query, values: warden_ctx_vals)
|
|
98
|
+
warden_ctx.warden = GraphQL::Schema::Warden.new(schema: warden_schema, context: warden_ctx)
|
|
99
|
+
warden_ctx.warden.skip_warning = true
|
|
100
|
+
warden_ctx.types = @warden_types = warden_ctx.warden.visibility_profile
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def loaded_types
|
|
105
|
+
@profile_types.loaded_types
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
PUBLIC_PROFILE_METHODS = [
|
|
109
|
+
:enum_values,
|
|
110
|
+
:interfaces,
|
|
111
|
+
:all_types,
|
|
112
|
+
:all_types_h,
|
|
113
|
+
:fields,
|
|
114
|
+
:loadable?,
|
|
115
|
+
:loadable_possible_types,
|
|
116
|
+
:type,
|
|
117
|
+
:arguments,
|
|
118
|
+
:argument,
|
|
119
|
+
:directive_exists?,
|
|
120
|
+
:directives,
|
|
121
|
+
:field,
|
|
122
|
+
:query_root,
|
|
123
|
+
:mutation_root,
|
|
124
|
+
:possible_types,
|
|
125
|
+
:subscription_root,
|
|
126
|
+
:reachable_type?,
|
|
127
|
+
:visible_enum_value?,
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
PUBLIC_PROFILE_METHODS.each do |profile_method|
|
|
131
|
+
define_method(profile_method) do |*args|
|
|
132
|
+
call_method_and_compare(profile_method, args)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def call_method_and_compare(method, args)
|
|
137
|
+
res_1 = @profile_types.public_send(method, *args)
|
|
138
|
+
if @skip_error
|
|
139
|
+
return res_1
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
res_2 = @warden_types.public_send(method, *args)
|
|
143
|
+
normalized_res_1 = res_1.is_a?(Array) ? Set.new(res_1) : res_1
|
|
144
|
+
normalized_res_2 = res_2.is_a?(Array) ? Set.new(res_2) : res_2
|
|
145
|
+
if !equivalent_schema_members?(normalized_res_1, normalized_res_2)
|
|
146
|
+
# Raise the errors with the orignally returned values:
|
|
147
|
+
err = RuntimeTypesMismatchError.new(method, res_2, res_1, args)
|
|
148
|
+
raise err
|
|
149
|
+
else
|
|
150
|
+
res_1
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def equivalent_schema_members?(member1, member2)
|
|
155
|
+
if member1.class != member2.class
|
|
156
|
+
return false
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
case member1
|
|
160
|
+
when Set
|
|
161
|
+
member1_array = member1.to_a.sort_by(&:graphql_name)
|
|
162
|
+
member2_array = member2.to_a.sort_by(&:graphql_name)
|
|
163
|
+
member1_array.each_with_index do |inner_member1, idx|
|
|
164
|
+
inner_member2 = member2_array[idx]
|
|
165
|
+
equivalent_schema_members?(inner_member1, inner_member2)
|
|
166
|
+
end
|
|
167
|
+
when GraphQL::Schema::Field
|
|
168
|
+
member1.ensure_loaded
|
|
169
|
+
member2.ensure_loaded
|
|
170
|
+
if member1.introspection? && member2.introspection?
|
|
171
|
+
member1.inspect == member2.inspect
|
|
172
|
+
else
|
|
173
|
+
member1 == member2
|
|
174
|
+
end
|
|
175
|
+
when Module
|
|
176
|
+
if member1.introspection? && member2.introspection?
|
|
177
|
+
member1.graphql_name == member2.graphql_name
|
|
178
|
+
else
|
|
179
|
+
member1 == member2
|
|
180
|
+
end
|
|
181
|
+
else
|
|
182
|
+
member1 == member2
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|