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
|
@@ -14,7 +14,7 @@ module GraphQL
|
|
|
14
14
|
#
|
|
15
15
|
# @api private
|
|
16
16
|
class ValidationPipeline
|
|
17
|
-
attr_reader :max_depth, :max_complexity
|
|
17
|
+
attr_reader :max_depth, :max_complexity, :validate_timeout_remaining
|
|
18
18
|
|
|
19
19
|
def initialize(query:, parse_error:, operation_name_error:, max_depth:, max_complexity:)
|
|
20
20
|
@validation_errors = []
|
|
@@ -71,7 +71,7 @@ module GraphQL
|
|
|
71
71
|
validator = @query.static_validator || @schema.static_validator
|
|
72
72
|
validation_result = validator.validate(@query, validate: @query.validate, timeout: @schema.validate_timeout, max_errors: @schema.validate_max_errors)
|
|
73
73
|
@validation_errors.concat(validation_result[:errors])
|
|
74
|
-
|
|
74
|
+
@validate_timeout_remaining = validation_result[:remaining_timeout]
|
|
75
75
|
if @validation_errors.empty?
|
|
76
76
|
@validation_errors.concat(@query.variables.errors)
|
|
77
77
|
end
|
|
@@ -100,10 +100,10 @@ module GraphQL
|
|
|
100
100
|
# Depending on the analysis engine, we must use different analyzers
|
|
101
101
|
# remove this once everything has switched over to AST analyzers
|
|
102
102
|
if max_depth
|
|
103
|
-
qa << GraphQL::Analysis::
|
|
103
|
+
qa << GraphQL::Analysis::MaxQueryDepth
|
|
104
104
|
end
|
|
105
105
|
if max_complexity
|
|
106
|
-
qa << GraphQL::Analysis::
|
|
106
|
+
qa << GraphQL::Analysis::MaxQueryComplexity
|
|
107
107
|
end
|
|
108
108
|
qa
|
|
109
109
|
else
|
|
@@ -26,7 +26,7 @@ module GraphQL
|
|
|
26
26
|
# - Then, fall back to the default value from the query string
|
|
27
27
|
# If it's still nil, raise an error if it's required.
|
|
28
28
|
variable_type = schema.type_from_ast(ast_variable.type, context: ctx)
|
|
29
|
-
if variable_type.nil?
|
|
29
|
+
if variable_type.nil? || !variable_type.unwrap.kind.input?
|
|
30
30
|
# Pass -- it will get handled by a validator
|
|
31
31
|
else
|
|
32
32
|
variable_name = ast_variable.name
|
|
@@ -80,12 +80,12 @@ module GraphQL
|
|
|
80
80
|
else
|
|
81
81
|
val
|
|
82
82
|
end
|
|
83
|
-
end
|
|
83
|
+
end
|
|
84
84
|
|
|
85
85
|
def add_max_errors_reached_message
|
|
86
86
|
message = "Too many errors processing variables, max validation error limit reached. Execution aborted"
|
|
87
87
|
validation_result = GraphQL::Query::InputValidationResult.from_problem(message)
|
|
88
|
-
errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
|
|
88
|
+
errors << GraphQL::Query::VariableValidationError.new(nil, nil, nil, validation_result, msg: message)
|
|
89
89
|
end
|
|
90
90
|
end
|
|
91
91
|
end
|
data/lib/graphql/query.rb
CHANGED
|
@@ -1,19 +1,56 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
require "graphql/query/context"
|
|
3
|
-
require "graphql/query/fingerprint"
|
|
4
|
-
require "graphql/query/null_context"
|
|
5
|
-
require "graphql/query/result"
|
|
6
|
-
require "graphql/query/variables"
|
|
7
|
-
require "graphql/query/input_validation_result"
|
|
8
|
-
require "graphql/query/variable_validation_error"
|
|
9
|
-
require "graphql/query/validation_pipeline"
|
|
10
2
|
|
|
11
3
|
module GraphQL
|
|
12
4
|
# A combination of query string and {Schema} instance which can be reduced to a {#result}.
|
|
13
5
|
class Query
|
|
6
|
+
extend Autoload
|
|
14
7
|
include Tracing::Traceable
|
|
15
8
|
extend Forwardable
|
|
16
9
|
|
|
10
|
+
autoload :Context, "graphql/query/context"
|
|
11
|
+
autoload :Fingerprint, "graphql/query/fingerprint"
|
|
12
|
+
autoload :NullContext, "graphql/query/null_context"
|
|
13
|
+
autoload :Partial, "graphql/query/partial"
|
|
14
|
+
autoload :Result, "graphql/query/result"
|
|
15
|
+
autoload :Variables, "graphql/query/variables"
|
|
16
|
+
autoload :InputValidationResult, "graphql/query/input_validation_result"
|
|
17
|
+
autoload :VariableValidationError, "graphql/query/variable_validation_error"
|
|
18
|
+
autoload :ValidationPipeline, "graphql/query/validation_pipeline"
|
|
19
|
+
|
|
20
|
+
# Code shared with {Partial}
|
|
21
|
+
module Runnable
|
|
22
|
+
def after_lazy(value, &block)
|
|
23
|
+
if !defined?(@runtime_instance)
|
|
24
|
+
@runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if @runtime_instance
|
|
28
|
+
@runtime_instance.minimal_after_lazy(value, &block)
|
|
29
|
+
else
|
|
30
|
+
@schema.after_lazy(value, &block)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Node-level cache for calculating arguments. Used during execution and query analysis.
|
|
35
|
+
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
|
36
|
+
# @param definition [GraphQL::Schema::Field]
|
|
37
|
+
# @param parent_object [GraphQL::Schema::Object]
|
|
38
|
+
# @return [Hash{Symbol => Object}]
|
|
39
|
+
def arguments_for(ast_node, definition, parent_object: nil)
|
|
40
|
+
arguments_cache.fetch(ast_node, definition, parent_object)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def arguments_cache
|
|
44
|
+
@arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @api private
|
|
48
|
+
def handle_or_reraise(err)
|
|
49
|
+
@schema.handle_or_reraise(context, err)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
include Runnable
|
|
17
54
|
class OperationNameMissingError < GraphQL::ExecutionError
|
|
18
55
|
def initialize(name)
|
|
19
56
|
msg = if name.nil?
|
|
@@ -48,7 +85,7 @@ module GraphQL
|
|
|
48
85
|
# @return [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules.
|
|
49
86
|
attr_reader :static_validator
|
|
50
87
|
|
|
51
|
-
# @param
|
|
88
|
+
# @param new_validator [GraphQL::StaticValidation::Validator] if present, the query will validate with these rules. This can't be reasssigned after validation.
|
|
52
89
|
def static_validator=(new_validator)
|
|
53
90
|
if defined?(@validation_pipeline) && @validation_pipeline && @validation_pipeline.has_validated?
|
|
54
91
|
raise ArgumentError, "Can't reassign Query#static_validator= after validation has run, remove this assignment."
|
|
@@ -95,17 +132,29 @@ module GraphQL
|
|
|
95
132
|
# @param root_value [Object] the object used to resolve fields on the root type
|
|
96
133
|
# @param max_depth [Numeric] the maximum number of nested selections allowed for this query (falls back to schema-level value)
|
|
97
134
|
# @param max_complexity [Numeric] the maximum field complexity for this query (falls back to schema-level value)
|
|
98
|
-
# @param
|
|
99
|
-
|
|
100
|
-
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, validate: true, static_validator: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, except: nil, only: nil, warden: nil)
|
|
135
|
+
# @param visibility_profile [Symbol] Another way to assign `context[:visibility_profile]`
|
|
136
|
+
def initialize(schema, query_string = nil, query: nil, document: nil, context: nil, variables: nil, multiplex: nil, validate: true, static_validator: nil, visibility_profile: nil, subscription_topic: nil, operation_name: nil, root_value: nil, max_depth: schema.max_depth, max_complexity: schema.max_complexity, warden: nil, use_visibility_profile: nil)
|
|
101
137
|
# Even if `variables: nil` is passed, use an empty hash for simpler logic
|
|
102
138
|
variables ||= {}
|
|
139
|
+
@multiplex = multiplex
|
|
103
140
|
@schema = schema
|
|
104
|
-
|
|
105
|
-
|
|
141
|
+
@context = schema.context_class.new(query: self, values: context)
|
|
142
|
+
if visibility_profile
|
|
143
|
+
@context[:visibility_profile] ||= visibility_profile
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
if use_visibility_profile.nil?
|
|
147
|
+
use_visibility_profile = warden ? false : schema.use_visibility_profile?
|
|
106
148
|
end
|
|
107
|
-
|
|
108
|
-
|
|
149
|
+
|
|
150
|
+
if use_visibility_profile
|
|
151
|
+
@visibility_profile = @schema.visibility.profile_for(@context)
|
|
152
|
+
@warden = Schema::Warden::NullWarden.new(context: @context, schema: @schema)
|
|
153
|
+
else
|
|
154
|
+
@visibility_profile = nil
|
|
155
|
+
@warden = warden
|
|
156
|
+
end
|
|
157
|
+
|
|
109
158
|
@subscription_topic = subscription_topic
|
|
110
159
|
@root_value = root_value
|
|
111
160
|
@fragments = nil
|
|
@@ -115,21 +164,10 @@ module GraphQL
|
|
|
115
164
|
context_tracers = (context ? context.fetch(:tracers, []) : [])
|
|
116
165
|
@tracers = schema.tracers + context_tracers
|
|
117
166
|
|
|
118
|
-
|
|
119
|
-
if context && context[:backtrace] && !@tracers.include?(GraphQL::Backtrace::Tracer)
|
|
120
|
-
if schema.trace_class <= GraphQL::Tracing::CallLegacyTracers
|
|
121
|
-
context_tracers += [GraphQL::Backtrace::Tracer]
|
|
122
|
-
@tracers << GraphQL::Backtrace::Tracer
|
|
123
|
-
elsif !(current_trace.class <= GraphQL::Backtrace::Trace)
|
|
124
|
-
raise "Invariant: `backtrace: true` should have provided a trace class with Backtrace mixed in, but it didnt. (Found: #{current_trace.class.ancestors}). This is a bug in GraphQL-Ruby, please report it on GitHub."
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
if context_tracers.any? && !(schema.trace_class <= GraphQL::Tracing::CallLegacyTracers)
|
|
167
|
+
if !context_tracers.empty? && !(schema.trace_class <= GraphQL::Tracing::CallLegacyTracers)
|
|
129
168
|
raise ArgumentError, "context[:tracers] are not supported without `trace_with(GraphQL::Tracing::CallLegacyTracers)` in the schema configuration, please add it."
|
|
130
169
|
end
|
|
131
170
|
|
|
132
|
-
|
|
133
171
|
@analysis_errors = []
|
|
134
172
|
if variables.is_a?(String)
|
|
135
173
|
raise ArgumentError, "Query variables should be a Hash, not a String. Try JSON.parse to prepare variables."
|
|
@@ -168,6 +206,8 @@ module GraphQL
|
|
|
168
206
|
|
|
169
207
|
@result_values = nil
|
|
170
208
|
@executed = false
|
|
209
|
+
|
|
210
|
+
@logger = schema.logger_for(context)
|
|
171
211
|
end
|
|
172
212
|
|
|
173
213
|
# If a document was provided to `GraphQL::Schema#execute` instead of the raw query string, we will need to get it from the document
|
|
@@ -175,9 +215,8 @@ module GraphQL
|
|
|
175
215
|
@query_string ||= (document ? document.to_query_string : nil)
|
|
176
216
|
end
|
|
177
217
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
end
|
|
218
|
+
# @return [Symbol, nil]
|
|
219
|
+
attr_reader :visibility_profile
|
|
181
220
|
|
|
182
221
|
attr_accessor :multiplex
|
|
183
222
|
|
|
@@ -194,9 +233,11 @@ module GraphQL
|
|
|
194
233
|
# @return [GraphQL::Execution::Lookahead]
|
|
195
234
|
def lookahead
|
|
196
235
|
@lookahead ||= begin
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
236
|
+
if selected_operation.nil?
|
|
237
|
+
GraphQL::Execution::Lookahead::NULL_LOOKAHEAD
|
|
238
|
+
else
|
|
239
|
+
GraphQL::Execution::Lookahead.new(query: self, root_type: root_type, ast_nodes: [selected_operation])
|
|
240
|
+
end
|
|
200
241
|
end
|
|
201
242
|
end
|
|
202
243
|
|
|
@@ -221,8 +262,20 @@ module GraphQL
|
|
|
221
262
|
with_prepared_ast { @operations }
|
|
222
263
|
end
|
|
223
264
|
|
|
265
|
+
# Run subtree partials of this query and return their results.
|
|
266
|
+
# Each partial is identified with a `path:` and `object:`
|
|
267
|
+
# where the path references a field in the AST and the object will be treated
|
|
268
|
+
# as the return value from that field. Subfields of the field named by `path`
|
|
269
|
+
# will be executed with `object` as the starting point
|
|
270
|
+
# @param partials_hashes [Array<Hash{Symbol => Object}>] Hashes with `path:` and `object:` keys
|
|
271
|
+
# @return [Array<GraphQL::Query::Result>]
|
|
272
|
+
def run_partials(partials_hashes)
|
|
273
|
+
partials = partials_hashes.map { |partial_options| Partial.new(query: self, **partial_options) }
|
|
274
|
+
Execution::Interpreter.run_all(@schema, partials, context: @context)
|
|
275
|
+
end
|
|
276
|
+
|
|
224
277
|
# Get the result for this query, executing it once
|
|
225
|
-
# @return [
|
|
278
|
+
# @return [GraphQL::Query::Result] A Hash-like GraphQL response, with `"data"` and/or `"errors"` keys
|
|
226
279
|
def result
|
|
227
280
|
if !@executed
|
|
228
281
|
Execution::Interpreter.run_all(@schema, [self], context: @context)
|
|
@@ -263,19 +316,6 @@ module GraphQL
|
|
|
263
316
|
end
|
|
264
317
|
end
|
|
265
318
|
|
|
266
|
-
# Node-level cache for calculating arguments. Used during execution and query analysis.
|
|
267
|
-
# @param ast_node [GraphQL::Language::Nodes::AbstractNode]
|
|
268
|
-
# @param definition [GraphQL::Schema::Field]
|
|
269
|
-
# @param parent_object [GraphQL::Schema::Object]
|
|
270
|
-
# @return Hash{Symbol => Object}
|
|
271
|
-
def arguments_for(ast_node, definition, parent_object: nil)
|
|
272
|
-
arguments_cache.fetch(ast_node, definition, parent_object)
|
|
273
|
-
end
|
|
274
|
-
|
|
275
|
-
def arguments_cache
|
|
276
|
-
@arguments_cache ||= Execution::Interpreter::ArgumentsCache.new(self)
|
|
277
|
-
end
|
|
278
|
-
|
|
279
319
|
# A version of the given query string, with:
|
|
280
320
|
# - Variables inlined to the query
|
|
281
321
|
# - Strings replaced with `<REDACTED>`
|
|
@@ -304,7 +344,7 @@ module GraphQL
|
|
|
304
344
|
|
|
305
345
|
# @return [String] An opaque hash for identifying this query's given query string and selected operation
|
|
306
346
|
def operation_fingerprint
|
|
307
|
-
@operation_fingerprint ||= "#{selected_operation_name || "anonymous"}/#{Fingerprint.generate(query_string)}"
|
|
347
|
+
@operation_fingerprint ||= "#{selected_operation_name || "anonymous"}/#{Fingerprint.generate(query_string || "")}"
|
|
308
348
|
end
|
|
309
349
|
|
|
310
350
|
# @return [String] An opaque hash for identifying this query's given a variable values (not including defaults)
|
|
@@ -317,7 +357,7 @@ module GraphQL
|
|
|
317
357
|
end
|
|
318
358
|
|
|
319
359
|
def_delegators :validation_pipeline, :validation_errors,
|
|
320
|
-
:analyzers, :ast_analyzers, :max_depth, :max_complexity
|
|
360
|
+
:analyzers, :ast_analyzers, :max_depth, :max_complexity, :validate_timeout_remaining
|
|
321
361
|
|
|
322
362
|
attr_accessor :analysis_errors
|
|
323
363
|
def valid?
|
|
@@ -328,7 +368,38 @@ module GraphQL
|
|
|
328
368
|
with_prepared_ast { @warden }
|
|
329
369
|
end
|
|
330
370
|
|
|
331
|
-
|
|
371
|
+
def get_type(type_name)
|
|
372
|
+
types.type(type_name) # rubocop:disable Development/ContextIsPassedCop
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def get_field(owner, field_name)
|
|
376
|
+
types.field(owner, field_name) # rubocop:disable Development/ContextIsPassedCop
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def possible_types(type)
|
|
380
|
+
types.possible_types(type) # rubocop:disable Development/ContextIsPassedCop
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def root_type_for_operation(op_type)
|
|
384
|
+
case op_type
|
|
385
|
+
when "query", nil
|
|
386
|
+
types.query_root # rubocop:disable Development/ContextIsPassedCop
|
|
387
|
+
when "mutation"
|
|
388
|
+
types.mutation_root # rubocop:disable Development/ContextIsPassedCop
|
|
389
|
+
when "subscription"
|
|
390
|
+
types.subscription_root # rubocop:disable Development/ContextIsPassedCop
|
|
391
|
+
else
|
|
392
|
+
raise ArgumentError, "unexpected root type name: #{op_type.inspect}; expected nil, 'query', 'mutation', or 'subscription'"
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
def root_type
|
|
397
|
+
root_type_for_operation(selected_operation.operation_type)
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def types
|
|
401
|
+
@visibility_profile || warden.visibility_profile
|
|
402
|
+
end
|
|
332
403
|
|
|
333
404
|
# @param abstract_type [GraphQL::UnionType, GraphQL::InterfaceType]
|
|
334
405
|
# @param value [Object] Any runtime value
|
|
@@ -354,37 +425,11 @@ module GraphQL
|
|
|
354
425
|
with_prepared_ast { @query }
|
|
355
426
|
end
|
|
356
427
|
|
|
357
|
-
# @return [void]
|
|
358
|
-
def merge_filters(only: nil, except: nil)
|
|
359
|
-
if @prepared_ast
|
|
360
|
-
raise "Can't add filters after preparing the query"
|
|
361
|
-
else
|
|
362
|
-
@filter ||= @schema.default_filter
|
|
363
|
-
@filter = @filter.merge(only: only, except: except)
|
|
364
|
-
end
|
|
365
|
-
nil
|
|
366
|
-
end
|
|
367
|
-
|
|
368
428
|
def subscription?
|
|
369
429
|
with_prepared_ast { @subscription }
|
|
370
430
|
end
|
|
371
431
|
|
|
372
|
-
|
|
373
|
-
def handle_or_reraise(err)
|
|
374
|
-
schema.handle_or_reraise(context, err)
|
|
375
|
-
end
|
|
376
|
-
|
|
377
|
-
def after_lazy(value, &block)
|
|
378
|
-
if !defined?(@runtime_instance)
|
|
379
|
-
@runtime_instance = context.namespace(:interpreter_runtime)[:runtime]
|
|
380
|
-
end
|
|
381
|
-
|
|
382
|
-
if @runtime_instance
|
|
383
|
-
@runtime_instance.minimal_after_lazy(value, &block)
|
|
384
|
-
else
|
|
385
|
-
@schema.after_lazy(value, &block)
|
|
386
|
-
end
|
|
387
|
-
end
|
|
432
|
+
attr_reader :logger
|
|
388
433
|
|
|
389
434
|
private
|
|
390
435
|
|
|
@@ -400,11 +445,11 @@ module GraphQL
|
|
|
400
445
|
|
|
401
446
|
def prepare_ast
|
|
402
447
|
@prepared_ast = true
|
|
403
|
-
@warden ||= @schema.warden_class.new(
|
|
448
|
+
@warden ||= @schema.warden_class.new(schema: @schema, context: @context)
|
|
404
449
|
parse_error = nil
|
|
405
450
|
@document ||= begin
|
|
406
451
|
if query_string
|
|
407
|
-
GraphQL.parse(query_string, trace: self.current_trace)
|
|
452
|
+
GraphQL.parse(query_string, trace: self.current_trace, max_tokens: @schema.max_query_string_tokens)
|
|
408
453
|
end
|
|
409
454
|
rescue GraphQL::ParseError => err
|
|
410
455
|
parse_error = err
|
|
@@ -436,7 +481,7 @@ module GraphQL
|
|
|
436
481
|
@mutation = false
|
|
437
482
|
@subscription = false
|
|
438
483
|
operation_name_error = nil
|
|
439
|
-
if
|
|
484
|
+
if !@operations.empty?
|
|
440
485
|
@selected_operation = find_operation(@operations, @operation_name)
|
|
441
486
|
if @selected_operation.nil?
|
|
442
487
|
operation_name_error = GraphQL::Query::OperationNameMissingError.new(@operation_name)
|
data/lib/graphql/railtie.rb
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
module GraphQL
|
|
4
|
+
# Support {GraphQL::Parser::Cache} and {GraphQL.eager_load!}
|
|
5
|
+
#
|
|
6
|
+
# @example Enable the parser cache with default directory
|
|
7
|
+
#
|
|
8
|
+
# config.graphql.parser_cache = true
|
|
9
|
+
#
|
|
3
10
|
class Railtie < Rails::Railtie
|
|
4
|
-
config.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
config.graphql = ActiveSupport::OrderedOptions.new
|
|
12
|
+
config.graphql.parser_cache = false
|
|
13
|
+
config.eager_load_namespaces << GraphQL
|
|
14
|
+
|
|
15
|
+
initializer("graphql.cache") do |app|
|
|
16
|
+
if config.graphql.parser_cache
|
|
17
|
+
Language::Parser.cache ||= Language::Cache.new(
|
|
18
|
+
app.root.join("tmp/cache/graphql")
|
|
19
|
+
)
|
|
10
20
|
end
|
|
11
21
|
end
|
|
12
22
|
end
|
data/lib/graphql/rake_task.rb
CHANGED
|
@@ -9,8 +9,7 @@ module GraphQL
|
|
|
9
9
|
# By default, schemas are looked up by name as constants using `schema_name:`.
|
|
10
10
|
# You can provide a `load_schema` function to return your schema another way.
|
|
11
11
|
#
|
|
12
|
-
# `load_context
|
|
13
|
-
# you can keep an eye on how filters affect your schema.
|
|
12
|
+
# Use `load_context:` and `visible?` to dump schemas under certain visibility constraints.
|
|
14
13
|
#
|
|
15
14
|
# @example Dump a Schema to .graphql + .json files
|
|
16
15
|
# require "graphql/rake_task"
|
|
@@ -36,8 +35,6 @@ module GraphQL
|
|
|
36
35
|
schema_name: nil,
|
|
37
36
|
load_schema: ->(task) { Object.const_get(task.schema_name) },
|
|
38
37
|
load_context: ->(task) { {} },
|
|
39
|
-
only: nil,
|
|
40
|
-
except: nil,
|
|
41
38
|
directory: ".",
|
|
42
39
|
idl_outfile: "schema.graphql",
|
|
43
40
|
json_outfile: "schema.json",
|
|
@@ -68,12 +65,6 @@ module GraphQL
|
|
|
68
65
|
# @return [<#call(task)>] A callable for loading the query context
|
|
69
66
|
attr_accessor :load_context
|
|
70
67
|
|
|
71
|
-
# @return [<#call(member, ctx)>, nil] A filter for this task
|
|
72
|
-
attr_accessor :only
|
|
73
|
-
|
|
74
|
-
# @return [<#call(member, ctx)>, nil] A filter for this task
|
|
75
|
-
attr_accessor :except
|
|
76
|
-
|
|
77
68
|
# @return [String] target for IDL task
|
|
78
69
|
attr_accessor :idl_outfile
|
|
79
70
|
|
|
@@ -117,10 +108,10 @@ module GraphQL
|
|
|
117
108
|
include_is_repeatable: include_is_repeatable,
|
|
118
109
|
include_specified_by_url: include_specified_by_url,
|
|
119
110
|
include_schema_description: include_schema_description,
|
|
120
|
-
|
|
111
|
+
context: context
|
|
121
112
|
)
|
|
122
113
|
when :to_definition
|
|
123
|
-
schema.to_definition(
|
|
114
|
+
schema.to_definition(context: context)
|
|
124
115
|
else
|
|
125
116
|
raise ArgumentError, "Unexpected schema dump method: #{method_name.inspect}"
|
|
126
117
|
end
|
|
@@ -9,7 +9,7 @@ module GraphQL
|
|
|
9
9
|
|
|
10
10
|
# Return the source of `send_node`, but without the keyword argument represented by `pair_node`
|
|
11
11
|
def source_without_keyword_argument(send_node, pair_node)
|
|
12
|
-
# work back to the
|
|
12
|
+
# work back to the preceding comma
|
|
13
13
|
first_pos = pair_node.location.expression.begin_pos
|
|
14
14
|
end_pos = pair_node.location.expression.end_pos
|
|
15
15
|
node_source = send_node.source_range.source
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require_relative "./base_cop"
|
|
3
|
+
|
|
4
|
+
module GraphQL
|
|
5
|
+
module Rubocop
|
|
6
|
+
module GraphQL
|
|
7
|
+
# Identify (and auto-correct) any field whose type configuration isn't given
|
|
8
|
+
# in the configuration block.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad, immediately causes Rails to load `app/graphql/types/thing.rb`
|
|
12
|
+
# field :thing, Types::Thing
|
|
13
|
+
#
|
|
14
|
+
# # good, defers loading until the file is needed
|
|
15
|
+
# field :thing do
|
|
16
|
+
# type(Types::Thing)
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
class FieldTypeInBlock < BaseCop
|
|
20
|
+
MSG = "type configuration can be moved to a block to defer loading the type's file"
|
|
21
|
+
|
|
22
|
+
BUILT_IN_SCALAR_NAMES = ["Float", "Int", "Integer", "String", "ID", "Boolean"]
|
|
23
|
+
def_node_matcher :field_config_with_inline_type, <<-Pattern
|
|
24
|
+
(
|
|
25
|
+
send {nil? _} :field sym ${const array} ...
|
|
26
|
+
)
|
|
27
|
+
Pattern
|
|
28
|
+
|
|
29
|
+
def_node_matcher :field_config_with_inline_type_and_block, <<-Pattern
|
|
30
|
+
(
|
|
31
|
+
block
|
|
32
|
+
(send {nil? _} :field sym ${const array} ...) ...
|
|
33
|
+
(args)
|
|
34
|
+
_
|
|
35
|
+
|
|
36
|
+
)
|
|
37
|
+
Pattern
|
|
38
|
+
|
|
39
|
+
def on_block(node)
|
|
40
|
+
ignore_node(node)
|
|
41
|
+
field_config_with_inline_type_and_block(node) do |type_const|
|
|
42
|
+
type_const_str = get_type_argument_str(node, type_const)
|
|
43
|
+
if ignore_inline_type_str?(type_const_str)
|
|
44
|
+
# Do nothing ...
|
|
45
|
+
else
|
|
46
|
+
add_offense(type_const) do |corrector|
|
|
47
|
+
cleaned_node_source = delete_type_argument(node, type_const)
|
|
48
|
+
field_indent = determine_field_indent(node)
|
|
49
|
+
cleaned_node_source.sub!(/(\{|do)/, "\\1\n#{field_indent} type #{type_const_str}")
|
|
50
|
+
corrector.replace(node, cleaned_node_source)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def on_send(node)
|
|
57
|
+
return if part_of_ignored_node?(node)
|
|
58
|
+
field_config_with_inline_type(node) do |type_const|
|
|
59
|
+
type_const_str = get_type_argument_str(node, type_const)
|
|
60
|
+
if ignore_inline_type_str?(type_const_str)
|
|
61
|
+
# Do nothing -- not loading from another file
|
|
62
|
+
else
|
|
63
|
+
add_offense(type_const) do |corrector|
|
|
64
|
+
cleaned_node_source = delete_type_argument(node, type_const)
|
|
65
|
+
field_indent = determine_field_indent(node)
|
|
66
|
+
cleaned_node_source += " do\n#{field_indent} type #{type_const_str}\n#{field_indent}end"
|
|
67
|
+
corrector.replace(node, cleaned_node_source)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def ignore_inline_type_str?(type_str)
|
|
77
|
+
if BUILT_IN_SCALAR_NAMES.include?(type_str)
|
|
78
|
+
true
|
|
79
|
+
elsif (inner_type_str = type_str.sub(/\[([A-Za-z]+)(, null: (true|false))?\]/, '\1')) && BUILT_IN_SCALAR_NAMES.include?(inner_type_str)
|
|
80
|
+
true
|
|
81
|
+
else
|
|
82
|
+
false
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def get_type_argument_str(send_node, type_const)
|
|
87
|
+
first_pos = type_const.location.expression.begin_pos
|
|
88
|
+
end_pos = type_const.location.expression.end_pos
|
|
89
|
+
node_source = send_node.source_range.source
|
|
90
|
+
node_first_pos = send_node.location.expression.begin_pos
|
|
91
|
+
|
|
92
|
+
relative_first_pos = first_pos - node_first_pos
|
|
93
|
+
end_removal_pos = end_pos - node_first_pos
|
|
94
|
+
|
|
95
|
+
node_source[relative_first_pos...end_removal_pos]
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def delete_type_argument(send_node, type_const)
|
|
99
|
+
first_pos = type_const.location.expression.begin_pos
|
|
100
|
+
end_pos = type_const.location.expression.end_pos
|
|
101
|
+
node_source = send_node.source_range.source
|
|
102
|
+
node_first_pos = send_node.location.expression.begin_pos
|
|
103
|
+
|
|
104
|
+
relative_first_pos = first_pos - node_first_pos
|
|
105
|
+
end_removal_pos = end_pos - node_first_pos
|
|
106
|
+
|
|
107
|
+
begin_removal_pos = relative_first_pos
|
|
108
|
+
while node_source[begin_removal_pos] != ","
|
|
109
|
+
begin_removal_pos -= 1
|
|
110
|
+
if begin_removal_pos < 1
|
|
111
|
+
raise "Invariant: somehow backtracked to beginning of node looking for a comma (node source: #{node_source.inspect})"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
node_source[0...begin_removal_pos] + node_source[end_removal_pos..-1]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def determine_field_indent(send_node)
|
|
119
|
+
type_defn_node = send_node
|
|
120
|
+
|
|
121
|
+
while (type_defn_node && !(type_defn_node.class_definition? || type_defn_node.module_definition?))
|
|
122
|
+
type_defn_node = type_defn_node.parent
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
if type_defn_node.nil?
|
|
126
|
+
raise "Invariant: Something went wrong in GraphQL-Ruby, couldn't find surrounding class definition for field (#{send_node}).\n\nPlease report this error on GitHub."
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
type_defn_source = type_defn_node.source
|
|
130
|
+
indent_test_idx = send_node.location.expression.begin_pos - type_defn_node.source_range.begin_pos - 1
|
|
131
|
+
field_indent = "".dup
|
|
132
|
+
while type_defn_source[indent_test_idx] == " "
|
|
133
|
+
field_indent << " "
|
|
134
|
+
indent_test_idx -= 1
|
|
135
|
+
if indent_test_idx == 0
|
|
136
|
+
raise "Invariant: somehow backtracted to beginning of class when looking for field indent (source: #{node_source.inspect})"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
field_indent
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require_relative "./base_cop"
|
|
3
|
+
|
|
4
|
+
module GraphQL
|
|
5
|
+
module Rubocop
|
|
6
|
+
module GraphQL
|
|
7
|
+
# Identify (and auto-correct) any root types in your schema file.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# # bad, immediately causes Rails to load `app/graphql/types/query.rb`
|
|
11
|
+
# query Types::Query
|
|
12
|
+
#
|
|
13
|
+
# # good, defers loading until the file is needed
|
|
14
|
+
# query { Types::Query }
|
|
15
|
+
#
|
|
16
|
+
class RootTypesInBlock < BaseCop
|
|
17
|
+
MSG = "type configuration can be moved to a block to defer loading the type's file"
|
|
18
|
+
|
|
19
|
+
def_node_matcher :root_type_config_without_block, <<-Pattern
|
|
20
|
+
(
|
|
21
|
+
send nil? {:query :mutation :subscription} const
|
|
22
|
+
)
|
|
23
|
+
Pattern
|
|
24
|
+
|
|
25
|
+
def on_send(node)
|
|
26
|
+
root_type_config_without_block(node) do
|
|
27
|
+
add_offense(node) do |corrector|
|
|
28
|
+
new_node_source = node.source_range.source
|
|
29
|
+
new_node_source.sub!(/(query|mutation|subscription)/, '\1 {')
|
|
30
|
+
new_node_source << " }"
|
|
31
|
+
corrector.replace(node, new_node_source)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|