graphql 1.10.1 → 1.13.0
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.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +18 -2
- data/lib/generators/graphql/install_generator.rb +36 -6
- data/lib/generators/graphql/loader_generator.rb +1 -0
- data/lib/generators/graphql/mutation_generator.rb +2 -1
- data/lib/generators/graphql/object_generator.rb +54 -9
- data/lib/generators/graphql/relay.rb +63 -0
- data/lib/generators/graphql/relay_generator.rb +21 -0
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +8 -0
- data/lib/generators/graphql/templates/base_edge.erb +8 -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_mutation.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -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/enum.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +16 -12
- data/lib/generators/graphql/templates/interface.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/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/node_type.erb +9 -0
- data/lib/generators/graphql/templates/object.erb +3 -1
- data/lib/generators/graphql/templates/query_type.erb +3 -3
- data/lib/generators/graphql/templates/scalar.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +21 -33
- data/lib/generators/graphql/templates/union.erb +3 -1
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis/analyze_query.rb +7 -0
- data/lib/graphql/analysis/ast/field_usage.rb +24 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +126 -109
- data/lib/graphql/analysis/ast/visitor.rb +13 -5
- data/lib/graphql/analysis/ast.rb +11 -2
- data/lib/graphql/argument.rb +3 -3
- data/lib/graphql/backtrace/inspect_result.rb +0 -1
- data/lib/graphql/backtrace/legacy_tracer.rb +56 -0
- data/lib/graphql/backtrace/table.rb +34 -3
- data/lib/graphql/backtrace/traced_error.rb +0 -1
- data/lib/graphql/backtrace/tracer.rb +40 -9
- data/lib/graphql/backtrace.rb +28 -19
- data/lib/graphql/backwards_compatibility.rb +2 -1
- data/lib/graphql/base_type.rb +1 -1
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
- data/lib/graphql/compatibility/execution_specification.rb +1 -0
- data/lib/graphql/compatibility/lazy_execution_specification.rb +2 -0
- data/lib/graphql/compatibility/query_parser_specification.rb +2 -0
- data/lib/graphql/compatibility/schema_parser_specification.rb +2 -0
- data/lib/graphql/dataloader/null_dataloader.rb +22 -0
- data/lib/graphql/dataloader/request.rb +19 -0
- data/lib/graphql/dataloader/request_all.rb +19 -0
- data/lib/graphql/dataloader/source.rb +155 -0
- data/lib/graphql/dataloader.rb +308 -0
- data/lib/graphql/define/assign_global_id_field.rb +2 -2
- data/lib/graphql/define/defined_object_proxy.rb +1 -1
- data/lib/graphql/define/instance_definable.rb +34 -4
- data/lib/graphql/define/type_definer.rb +5 -5
- data/lib/graphql/deprecated_dsl.rb +18 -5
- data/lib/graphql/deprecation.rb +9 -0
- data/lib/graphql/directive.rb +4 -4
- data/lib/graphql/enum_type.rb +7 -1
- data/lib/graphql/execution/errors.rb +110 -7
- data/lib/graphql/execution/execute.rb +8 -1
- data/lib/graphql/execution/instrumentation.rb +1 -1
- data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
- data/lib/graphql/execution/interpreter/arguments.rb +88 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +103 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +18 -0
- data/lib/graphql/execution/interpreter/resolve.rb +37 -25
- data/lib/graphql/execution/interpreter/runtime.rb +685 -421
- data/lib/graphql/execution/interpreter.rb +42 -13
- data/lib/graphql/execution/lazy.rb +5 -1
- data/lib/graphql/execution/lookahead.rb +25 -110
- data/lib/graphql/execution/multiplex.rb +37 -25
- data/lib/graphql/field.rb +5 -1
- data/lib/graphql/function.rb +4 -0
- data/lib/graphql/input_object_type.rb +6 -0
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/integer_encoding_error.rb +18 -2
- data/lib/graphql/interface_type.rb +7 -0
- data/lib/graphql/internal_representation/document.rb +2 -2
- data/lib/graphql/internal_representation/rewrite.rb +1 -1
- data/lib/graphql/internal_representation/scope.rb +2 -2
- data/lib/graphql/internal_representation/visit.rb +2 -2
- data/lib/graphql/introspection/directive_type.rb +8 -4
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +9 -5
- data/lib/graphql/introspection/input_value_type.rb +15 -3
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/schema_type.rb +4 -4
- data/lib/graphql/introspection/type_type.rb +16 -12
- data/lib/graphql/introspection.rb +96 -0
- data/lib/graphql/invalid_null_error.rb +18 -0
- data/lib/graphql/language/block_string.rb +20 -5
- data/lib/graphql/language/cache.rb +37 -0
- data/lib/graphql/language/document_from_schema_definition.rb +73 -25
- data/lib/graphql/language/lexer.rb +4 -3
- data/lib/graphql/language/lexer.rl +3 -3
- data/lib/graphql/language/nodes.rb +51 -89
- data/lib/graphql/language/parser.rb +552 -530
- data/lib/graphql/language/parser.y +114 -99
- data/lib/graphql/language/printer.rb +7 -2
- data/lib/graphql/language/sanitized_printer.rb +222 -0
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/language/visitor.rb +2 -2
- data/lib/graphql/language.rb +2 -0
- data/lib/graphql/name_validator.rb +2 -7
- data/lib/graphql/object_type.rb +44 -35
- data/lib/graphql/pagination/active_record_relation_connection.rb +14 -1
- data/lib/graphql/pagination/array_connection.rb +2 -2
- data/lib/graphql/pagination/connection.rb +75 -20
- data/lib/graphql/pagination/connections.rb +83 -31
- data/lib/graphql/pagination/relation_connection.rb +34 -14
- data/lib/graphql/parse_error.rb +0 -1
- data/lib/graphql/query/arguments.rb +4 -3
- data/lib/graphql/query/arguments_cache.rb +1 -2
- data/lib/graphql/query/context.rb +42 -7
- data/lib/graphql/query/executor.rb +0 -1
- data/lib/graphql/query/fingerprint.rb +26 -0
- data/lib/graphql/query/input_validation_result.rb +23 -6
- data/lib/graphql/query/literal_input.rb +1 -1
- data/lib/graphql/query/null_context.rb +24 -8
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/serial_execution.rb +1 -0
- data/lib/graphql/query/validation_pipeline.rb +5 -2
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query/variables.rb +14 -4
- data/lib/graphql/query.rb +68 -13
- data/lib/graphql/railtie.rb +9 -1
- data/lib/graphql/rake_task.rb +12 -9
- data/lib/graphql/relay/array_connection.rb +10 -12
- data/lib/graphql/relay/base_connection.rb +26 -13
- data/lib/graphql/relay/connection_instrumentation.rb +4 -4
- data/lib/graphql/relay/connection_type.rb +1 -1
- data/lib/graphql/relay/edges_instrumentation.rb +0 -1
- data/lib/graphql/relay/mutation.rb +1 -0
- data/lib/graphql/relay/node.rb +3 -0
- data/lib/graphql/relay/range_add.rb +23 -9
- data/lib/graphql/relay/relation_connection.rb +8 -10
- data/lib/graphql/relay/type_extensions.rb +2 -0
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/scalar_type.rb +16 -1
- data/lib/graphql/schema/addition.rb +247 -0
- data/lib/graphql/schema/argument.rb +210 -12
- data/lib/graphql/schema/base_64_encoder.rb +2 -0
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +3 -1
- data/lib/graphql/schema/build_from_definition.rb +213 -86
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive/deprecated.rb +1 -1
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +57 -0
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +14 -2
- data/lib/graphql/schema/directive.rb +78 -2
- data/lib/graphql/schema/enum.rb +80 -9
- data/lib/graphql/schema/enum_value.rb +17 -6
- data/lib/graphql/schema/field/connection_extension.rb +46 -30
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +285 -133
- data/lib/graphql/schema/find_inherited_value.rb +4 -1
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +97 -89
- data/lib/graphql/schema/interface.rb +24 -19
- data/lib/graphql/schema/late_bound_type.rb +2 -2
- data/lib/graphql/schema/list.rb +7 -1
- data/lib/graphql/schema/loader.rb +137 -103
- data/lib/graphql/schema/member/accepts_definition.rb +8 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +15 -19
- data/lib/graphql/schema/member/build_type.rb +14 -7
- data/lib/graphql/schema/member/has_arguments.rb +205 -12
- data/lib/graphql/schema/member/has_ast_node.rb +4 -1
- data/lib/graphql/schema/member/has_deprecation_reason.rb +25 -0
- data/lib/graphql/schema/member/has_directives.rb +98 -0
- data/lib/graphql/schema/member/has_fields.rb +95 -30
- data/lib/graphql/schema/member/has_interfaces.rb +90 -0
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
- data/lib/graphql/schema/member/has_validators.rb +31 -0
- data/lib/graphql/schema/member/instrumentation.rb +0 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +3 -3
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/middleware_chain.rb +1 -1
- data/lib/graphql/schema/mutation.rb +4 -0
- data/lib/graphql/schema/non_null.rb +5 -0
- data/lib/graphql/schema/object.rb +47 -46
- data/lib/graphql/schema/possible_types.rb +9 -4
- data/lib/graphql/schema/printer.rb +16 -34
- data/lib/graphql/schema/relay_classic_mutation.rb +32 -4
- data/lib/graphql/schema/resolver/has_payload_type.rb +34 -4
- data/lib/graphql/schema/resolver.rb +123 -63
- data/lib/graphql/schema/scalar.rb +11 -1
- data/lib/graphql/schema/subscription.rb +57 -21
- data/lib/graphql/schema/timeout.rb +29 -15
- data/lib/graphql/schema/timeout_middleware.rb +3 -1
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +18 -4
- data/lib/graphql/schema/union.rb +41 -1
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema/validation.rb +12 -2
- data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
- data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +33 -0
- data/lib/graphql/schema/validator/format_validator.rb +48 -0
- data/lib/graphql/schema/validator/inclusion_validator.rb +35 -0
- data/lib/graphql/schema/validator/length_validator.rb +59 -0
- data/lib/graphql/schema/validator/numericality_validator.rb +82 -0
- data/lib/graphql/schema/validator/required_validator.rb +68 -0
- data/lib/graphql/schema/validator.rb +174 -0
- data/lib/graphql/schema/warden.rb +153 -28
- data/lib/graphql/schema.rb +364 -330
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +8 -5
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/error.rb +3 -1
- data/lib/graphql/static_validation/literal_validator.rb +51 -26
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +44 -87
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +22 -6
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +28 -22
- data/lib/graphql/static_validation/rules/arguments_are_defined_error.rb +4 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -43
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +6 -7
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +9 -10
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +8 -8
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +4 -2
- data/lib/graphql/static_validation/validation_context.rb +9 -3
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +42 -8
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/string_encoding_error.rb +13 -3
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +118 -19
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +81 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
- data/lib/graphql/subscriptions/event.rb +81 -30
- data/lib/graphql/subscriptions/instrumentation.rb +0 -1
- data/lib/graphql/subscriptions/serialize.rb +33 -6
- data/lib/graphql/subscriptions/subscription_root.rb +15 -4
- data/lib/graphql/subscriptions.rb +88 -45
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +2 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +173 -0
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
- data/lib/graphql/tracing/platform_tracing.rb +43 -17
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/tracing/scout_tracing.rb +11 -0
- data/lib/graphql/tracing/skylight_tracing.rb +1 -1
- data/lib/graphql/tracing/statsd_tracing.rb +42 -0
- data/lib/graphql/tracing.rb +9 -33
- data/lib/graphql/types/big_int.rb +5 -1
- data/lib/graphql/types/int.rb +10 -3
- data/lib/graphql/types/iso_8601_date.rb +3 -3
- data/lib/graphql/types/iso_8601_date_time.rb +25 -10
- data/lib/graphql/types/relay/base_connection.rb +6 -90
- data/lib/graphql/types/relay/base_edge.rb +2 -34
- data/lib/graphql/types/relay/connection_behaviors.rb +156 -0
- data/lib/graphql/types/relay/default_relay.rb +27 -0
- data/lib/graphql/types/relay/edge_behaviors.rb +53 -0
- data/lib/graphql/types/relay/has_node_field.rb +41 -0
- data/lib/graphql/types/relay/has_nodes_field.rb +41 -0
- data/lib/graphql/types/relay/node.rb +2 -4
- data/lib/graphql/types/relay/node_behaviors.rb +15 -0
- data/lib/graphql/types/relay/node_field.rb +2 -20
- data/lib/graphql/types/relay/nodes_field.rb +2 -20
- data/lib/graphql/types/relay/page_info.rb +2 -14
- data/lib/graphql/types/relay/page_info_behaviors.rb +25 -0
- data/lib/graphql/types/relay.rb +11 -3
- data/lib/graphql/types/string.rb +8 -2
- data/lib/graphql/unauthorized_error.rb +2 -2
- data/lib/graphql/union_type.rb +2 -0
- data/lib/graphql/upgrader/member.rb +1 -0
- data/lib/graphql/upgrader/schema.rb +1 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +65 -31
- data/readme.md +3 -6
- metadata +77 -112
- data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
- data/lib/graphql/literal_validation_error.rb +0 -6
- data/lib/graphql/types/relay/base_field.rb +0 -22
- data/lib/graphql/types/relay/base_interface.rb +0 -29
- data/lib/graphql/types/relay/base_object.rb +0 -26
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
require 'rails/generators'
|
2
3
|
require 'rails/generators/base'
|
3
4
|
require 'graphql'
|
4
5
|
require 'active_support'
|
@@ -12,7 +13,6 @@ module Graphql
|
|
12
13
|
|
13
14
|
argument :type_name,
|
14
15
|
type: :string,
|
15
|
-
required: true,
|
16
16
|
banner: "TypeName",
|
17
17
|
desc: "Name of this object type (expressed as Ruby or GraphQL)"
|
18
18
|
|
@@ -3,6 +3,11 @@ module GraphQL
|
|
3
3
|
module Analysis
|
4
4
|
module_function
|
5
5
|
|
6
|
+
def use(schema_class)
|
7
|
+
schema = schema_class.is_a?(Class) ? schema_class : schema_class.target
|
8
|
+
schema.analysis_engine = self
|
9
|
+
end
|
10
|
+
|
6
11
|
# @return [void]
|
7
12
|
def analyze_multiplex(multiplex, analyzers)
|
8
13
|
multiplex.trace("analyze_multiplex", { multiplex: multiplex }) do
|
@@ -38,6 +43,8 @@ module GraphQL
|
|
38
43
|
# @param analyzers [Array<#call>] Objects that respond to `#call(memo, visit_type, irep_node)`
|
39
44
|
# @return [Array<Any>] Results from those analyzers
|
40
45
|
def analyze_query(query, analyzers, multiplex_states: [])
|
46
|
+
GraphQL::Deprecation.warn "Legacy analysis will be removed in GraphQL-Ruby 2.0, please upgrade to AST Analysis: https://graphql-ruby.org/queries/ast_analysis.html (schema: #{query.schema})"
|
47
|
+
|
41
48
|
query.trace("analyze_query", { query: query }) do
|
42
49
|
analyzers_to_run = analyzers.select do |analyzer|
|
43
50
|
if analyzer.respond_to?(:analyze?)
|
@@ -7,6 +7,7 @@ module GraphQL
|
|
7
7
|
super
|
8
8
|
@used_fields = Set.new
|
9
9
|
@used_deprecated_fields = Set.new
|
10
|
+
@used_deprecated_arguments = Set.new
|
10
11
|
end
|
11
12
|
|
12
13
|
def on_leave_field(node, parent, visitor)
|
@@ -14,14 +15,36 @@ module GraphQL
|
|
14
15
|
field = "#{visitor.parent_type_definition.graphql_name}.#{field_defn.graphql_name}"
|
15
16
|
@used_fields << field
|
16
17
|
@used_deprecated_fields << field if field_defn.deprecation_reason
|
18
|
+
|
19
|
+
extract_deprecated_arguments(visitor.query.arguments_for(node, visitor.field_definition).argument_values)
|
17
20
|
end
|
18
21
|
|
19
22
|
def result
|
20
23
|
{
|
21
24
|
used_fields: @used_fields.to_a,
|
22
|
-
used_deprecated_fields: @used_deprecated_fields.to_a
|
25
|
+
used_deprecated_fields: @used_deprecated_fields.to_a,
|
26
|
+
used_deprecated_arguments: @used_deprecated_arguments.to_a,
|
23
27
|
}
|
24
28
|
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def extract_deprecated_arguments(argument_values)
|
33
|
+
argument_values.each_pair do |_argument_name, argument|
|
34
|
+
if argument.definition.deprecation_reason
|
35
|
+
@used_deprecated_arguments << argument.definition.path
|
36
|
+
end
|
37
|
+
|
38
|
+
if argument.definition.type.kind.input_object?
|
39
|
+
extract_deprecated_arguments(argument.value.arguments.argument_values) # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
|
40
|
+
elsif argument.definition.type.list? && !argument.value.nil?
|
41
|
+
argument
|
42
|
+
.value
|
43
|
+
.select { |value| value.respond_to?(:arguments) }
|
44
|
+
.each { |value| extract_deprecated_arguments(value.arguments.argument_values) } # rubocop:disable Development/ContextIsPassedCop -- runtime args instance
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
25
48
|
end
|
26
49
|
end
|
27
50
|
end
|
@@ -21,16 +21,24 @@ module GraphQL
|
|
21
21
|
# since the lexical binding isn't important.
|
22
22
|
HASH_CHILDREN = ->(h, k) { h[k] = {} }
|
23
23
|
|
24
|
-
|
24
|
+
attr_reader :field_definition, :response_path, :query
|
25
|
+
|
26
|
+
# @param parent_type [Class] The owner of `field_definition`
|
25
27
|
# @param field_definition [GraphQL::Field, GraphQL::Schema::Field] Used for getting the `.complexity` configuration
|
26
|
-
# @param query [
|
27
|
-
|
28
|
+
# @param query [GraphQL::Query] Used for `query.possible_types`
|
29
|
+
# @param response_path [Array<String>] The path to the response key for the field
|
30
|
+
def initialize(parent_type, field_definition, query, response_path)
|
31
|
+
@parent_type = parent_type
|
28
32
|
@field_definition = field_definition
|
29
33
|
@query = query
|
30
|
-
@
|
34
|
+
@response_path = response_path
|
31
35
|
@scoped_children = nil
|
36
|
+
@nodes = []
|
32
37
|
end
|
33
38
|
|
39
|
+
# @return [Array<GraphQL::Language::Nodes::Field>]
|
40
|
+
attr_reader :nodes
|
41
|
+
|
34
42
|
# Returns true if this field has no selections, ie, it's a scalar.
|
35
43
|
# We need a quick way to check whether we should continue traversing.
|
36
44
|
def terminal?
|
@@ -46,16 +54,7 @@ module GraphQL
|
|
46
54
|
end
|
47
55
|
|
48
56
|
def own_complexity(child_complexity)
|
49
|
-
|
50
|
-
case defined_complexity
|
51
|
-
when Proc
|
52
|
-
arguments = @query.arguments_for(@node, @field_definition)
|
53
|
-
defined_complexity.call(@query.context, arguments, child_complexity)
|
54
|
-
when Numeric
|
55
|
-
defined_complexity + child_complexity
|
56
|
-
else
|
57
|
-
raise("Invalid complexity: #{defined_complexity.inspect} on #{@field_definition.name}")
|
58
|
-
end
|
57
|
+
@field_definition.calculate_complexity(query: @query, nodes: @nodes, child_complexity: child_complexity)
|
59
58
|
end
|
60
59
|
end
|
61
60
|
|
@@ -73,9 +72,10 @@ module GraphQL
|
|
73
72
|
# `node` and `visitor.field_definition` may appear from a cache,
|
74
73
|
# but I think that's ok. If the arguments _didn't_ match,
|
75
74
|
# then the query would have been rejected as invalid.
|
76
|
-
complexities_on_type = @complexities_on_type_by_query[visitor.query] ||= [ScopedTypeComplexity.new(nil, nil, query)]
|
75
|
+
complexities_on_type = @complexities_on_type_by_query[visitor.query] ||= [ScopedTypeComplexity.new(nil, nil, query, visitor.response_path)]
|
77
76
|
|
78
|
-
complexity = complexities_on_type.last.scoped_children[parent_type][field_key] ||= ScopedTypeComplexity.new(
|
77
|
+
complexity = complexities_on_type.last.scoped_children[parent_type][field_key] ||= ScopedTypeComplexity.new(parent_type, visitor.field_definition, visitor.query, visitor.response_path)
|
78
|
+
complexity.nodes.push(node)
|
79
79
|
# Push it on the stack.
|
80
80
|
complexities_on_type.push(complexity)
|
81
81
|
end
|
@@ -89,123 +89,140 @@ module GraphQL
|
|
89
89
|
complexities_on_type.pop
|
90
90
|
end
|
91
91
|
|
92
|
+
private
|
93
|
+
|
92
94
|
# @return [Integer]
|
93
95
|
def max_possible_complexity
|
94
96
|
@complexities_on_type_by_query.reduce(0) do |total, (query, complexities_on_type)|
|
95
97
|
root_complexity = complexities_on_type.last
|
96
98
|
# Use this entry point to calculate the total complexity
|
97
|
-
total_complexity_for_query =
|
99
|
+
total_complexity_for_query = merged_max_complexity_for_scopes(query, [root_complexity.scoped_children])
|
98
100
|
total + total_complexity_for_query
|
99
101
|
end
|
100
102
|
end
|
101
103
|
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
def applies_to?(query, left_scope, right_scope)
|
112
|
-
if left_scope == right_scope
|
113
|
-
# This can happen when several branches are being analyzed together
|
114
|
-
true
|
115
|
-
else
|
116
|
-
# Check if these two scopes have _any_ types in common.
|
117
|
-
possible_right_types = query.possible_types(right_scope)
|
118
|
-
possible_left_types = query.possible_types(left_scope)
|
119
|
-
!(possible_right_types & possible_left_types).empty?
|
120
|
-
end
|
104
|
+
# @param query [GraphQL::Query] Used for `query.possible_types`
|
105
|
+
# @param scoped_children_hashes [Array<Hash>] Array of scoped children hashes
|
106
|
+
# @return [Integer]
|
107
|
+
def merged_max_complexity_for_scopes(query, scoped_children_hashes)
|
108
|
+
# Figure out what scopes are possible here.
|
109
|
+
# Use a hash, but ignore the values; it's just a fast way to work with the keys.
|
110
|
+
all_scopes = {}
|
111
|
+
scoped_children_hashes.each do |h|
|
112
|
+
all_scopes.merge!(h)
|
121
113
|
end
|
122
114
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
#
|
133
|
-
#
|
134
|
-
|
135
|
-
|
136
|
-
scope.kind.abstract? && (
|
137
|
-
query.possible_types(scope).all? { |t| all_scopes.include?(t) }
|
138
|
-
)
|
115
|
+
# If an abstract scope is present, but _all_ of its concrete types
|
116
|
+
# are also in the list, remove it from the list of scopes to check,
|
117
|
+
# because every possible type is covered by a concrete type.
|
118
|
+
# (That is, there are no remainder types to check.)
|
119
|
+
prev_keys = all_scopes.keys
|
120
|
+
prev_keys.each do |scope|
|
121
|
+
next unless scope.kind.abstract?
|
122
|
+
|
123
|
+
missing_concrete_types = query.possible_types(scope).select { |t| !all_scopes.key?(t) }
|
124
|
+
# This concrete type is possible _only_ as a member of the abstract type.
|
125
|
+
# So, attribute to it the complexity which belongs to the abstract type.
|
126
|
+
missing_concrete_types.each do |concrete_scope|
|
127
|
+
all_scopes[concrete_scope] = all_scopes[scope]
|
139
128
|
end
|
129
|
+
all_scopes.delete(scope)
|
130
|
+
end
|
140
131
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
end
|
132
|
+
# This will hold `{ type => int }` pairs, one for each possible branch
|
133
|
+
complexity_by_scope = {}
|
134
|
+
|
135
|
+
# For each scope,
|
136
|
+
# find the lexical selections that might apply to it,
|
137
|
+
# and gather them together into an array.
|
138
|
+
# Then, treat the set of selection hashes
|
139
|
+
# as a set and calculate the complexity for them as a unit
|
140
|
+
all_scopes.each do |scope, _|
|
141
|
+
# These will be the selections on `scope`
|
142
|
+
children_for_scope = []
|
143
|
+
scoped_children_hashes.each do |sc_h|
|
144
|
+
sc_h.each do |inner_scope, children_hash|
|
145
|
+
if applies_to?(query, scope, inner_scope)
|
146
|
+
children_for_scope << children_hash
|
157
147
|
end
|
158
148
|
end
|
159
|
-
|
160
|
-
# Calculate the complexity for `scope`, merging all
|
161
|
-
# possible lexical branches.
|
162
|
-
complexity_value = merged_max_complexity(query, children_for_scope)
|
163
|
-
complexity_by_scope[scope] = complexity_value
|
164
149
|
end
|
165
150
|
|
166
|
-
#
|
167
|
-
|
151
|
+
# Calculate the complexity for `scope`, merging all
|
152
|
+
# possible lexical branches.
|
153
|
+
complexity_value = merged_max_complexity(query, children_for_scope)
|
154
|
+
complexity_by_scope[scope] = complexity_value
|
168
155
|
end
|
169
156
|
|
170
|
-
#
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
157
|
+
# Return the max complexity among all scopes
|
158
|
+
complexity_by_scope.each_value.max
|
159
|
+
end
|
160
|
+
|
161
|
+
def applies_to?(query, left_scope, right_scope)
|
162
|
+
if left_scope == right_scope
|
163
|
+
# This can happen when several branches are being analyzed together
|
164
|
+
true
|
165
|
+
else
|
166
|
+
# Check if these two scopes have _any_ types in common.
|
167
|
+
possible_right_types = query.possible_types(right_scope)
|
168
|
+
possible_left_types = query.possible_types(left_scope)
|
169
|
+
!(possible_right_types & possible_left_types).empty?
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# A hook which is called whenever a field's max complexity is calculated.
|
174
|
+
# Override this method to capture individual field complexity details.
|
175
|
+
#
|
176
|
+
# @param scoped_type_complexity [ScopedTypeComplexity]
|
177
|
+
# @param max_complexity [Numeric] Field's maximum complexity including child complexity
|
178
|
+
# @param child_complexity [Numeric, nil] Field's child complexity
|
179
|
+
def field_complexity(scoped_type_complexity, max_complexity:, child_complexity: nil)
|
180
|
+
end
|
197
181
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
182
|
+
# @param children_for_scope [Array<Hash>] An array of `scoped_children[scope]` hashes
|
183
|
+
# (`{field_key => complexity}`)
|
184
|
+
# @return [Integer] Complexity value for all these selections in the current scope
|
185
|
+
def merged_max_complexity(query, children_for_scope)
|
186
|
+
all_keys = []
|
187
|
+
children_for_scope.each do |c|
|
188
|
+
all_keys.concat(c.keys)
|
189
|
+
end
|
190
|
+
all_keys.uniq!
|
191
|
+
complexity_for_keys = {}
|
192
|
+
|
193
|
+
all_keys.each do |child_key|
|
194
|
+
scoped_children_for_key = nil
|
195
|
+
complexity_for_key = nil
|
196
|
+
children_for_scope.each do |children_hash|
|
197
|
+
next unless children_hash.key?(child_key)
|
198
|
+
|
199
|
+
complexity_for_key = children_hash[child_key]
|
200
|
+
if complexity_for_key.terminal?
|
201
|
+
# Assume that all terminals would return the same complexity
|
202
|
+
# Since it's a terminal, its child complexity is zero.
|
203
|
+
complexity = complexity_for_key.own_complexity(0)
|
204
|
+
complexity_for_keys[child_key] = complexity
|
205
|
+
|
206
|
+
field_complexity(complexity_for_key, max_complexity: complexity, child_complexity: nil)
|
207
|
+
else
|
208
|
+
scoped_children_for_key ||= []
|
209
|
+
scoped_children_for_key << complexity_for_key.scoped_children
|
203
210
|
end
|
204
211
|
end
|
205
212
|
|
206
|
-
|
207
|
-
|
213
|
+
next unless scoped_children_for_key
|
214
|
+
|
215
|
+
child_complexity = merged_max_complexity_for_scopes(query, scoped_children_for_key)
|
216
|
+
# This is the _last_ one we visited; assume it's representative.
|
217
|
+
max_complexity = complexity_for_key.own_complexity(child_complexity)
|
218
|
+
|
219
|
+
field_complexity(complexity_for_key, max_complexity: max_complexity, child_complexity: child_complexity)
|
220
|
+
|
221
|
+
complexity_for_keys[child_key] = max_complexity
|
208
222
|
end
|
223
|
+
|
224
|
+
# Calculate the child complexity by summing the complexity of all selections
|
225
|
+
complexity_for_keys.each_value.inject(0, &:+)
|
209
226
|
end
|
210
227
|
end
|
211
228
|
end
|
@@ -19,6 +19,7 @@ module GraphQL
|
|
19
19
|
@field_definitions = []
|
20
20
|
@argument_definitions = []
|
21
21
|
@directive_definitions = []
|
22
|
+
@rescued_errors = []
|
22
23
|
@query = query
|
23
24
|
@schema = query.schema
|
24
25
|
@response_path = []
|
@@ -32,6 +33,9 @@ module GraphQL
|
|
32
33
|
# @return [Array<GraphQL::ObjectType>] Types whose scope we've entered
|
33
34
|
attr_reader :object_types
|
34
35
|
|
36
|
+
# @return [Array<GraphQL::AnalysisError]
|
37
|
+
attr_reader :rescued_errors
|
38
|
+
|
35
39
|
def visit
|
36
40
|
return unless @document
|
37
41
|
super
|
@@ -96,7 +100,7 @@ module GraphQL
|
|
96
100
|
def on_field(node, parent)
|
97
101
|
@response_path.push(node.alias || node.name)
|
98
102
|
parent_type = @object_types.last
|
99
|
-
field_definition = @schema.get_field(parent_type, node.name)
|
103
|
+
field_definition = @schema.get_field(parent_type, node.name, @query.context)
|
100
104
|
@field_definitions.push(field_definition)
|
101
105
|
if !field_definition.nil?
|
102
106
|
next_object_type = field_definition.type.unwrap
|
@@ -134,14 +138,14 @@ module GraphQL
|
|
134
138
|
argument_defn = if (arg = @argument_definitions.last)
|
135
139
|
arg_type = arg.type.unwrap
|
136
140
|
if arg_type.kind.input_object?
|
137
|
-
arg_type.
|
141
|
+
arg_type.get_argument(node.name, @query.context)
|
138
142
|
else
|
139
143
|
nil
|
140
144
|
end
|
141
145
|
elsif (directive_defn = @directive_definitions.last)
|
142
|
-
directive_defn.
|
146
|
+
directive_defn.get_argument(node.name, @query.context)
|
143
147
|
elsif (field_defn = @field_definitions.last)
|
144
|
-
field_defn.
|
148
|
+
field_defn.get_argument(node.name, @query.context)
|
145
149
|
else
|
146
150
|
nil
|
147
151
|
end
|
@@ -239,7 +243,11 @@ module GraphQL
|
|
239
243
|
|
240
244
|
def call_analyzers(method, node, parent)
|
241
245
|
@analyzers.each do |analyzer|
|
242
|
-
|
246
|
+
begin
|
247
|
+
analyzer.public_send(method, node, parent, self)
|
248
|
+
rescue AnalysisError => err
|
249
|
+
@rescued_errors << err
|
250
|
+
end
|
243
251
|
end
|
244
252
|
end
|
245
253
|
|
data/lib/graphql/analysis/ast.rb
CHANGED
@@ -13,7 +13,12 @@ module GraphQL
|
|
13
13
|
module_function
|
14
14
|
|
15
15
|
def use(schema_class)
|
16
|
-
schema_class.analysis_engine
|
16
|
+
if schema_class.analysis_engine == self
|
17
|
+
definition_line = caller(2, 1).first
|
18
|
+
GraphQL::Deprecation.warn("GraphQL::Analysis::AST is now the default; remove `use GraphQL::Analysis::AST` from the schema definition (#{definition_line})")
|
19
|
+
else
|
20
|
+
schema_class.analysis_engine = self
|
21
|
+
end
|
17
22
|
end
|
18
23
|
|
19
24
|
# Analyze a multiplex, and all queries within.
|
@@ -67,7 +72,11 @@ module GraphQL
|
|
67
72
|
|
68
73
|
visitor.visit
|
69
74
|
|
70
|
-
|
75
|
+
if visitor.rescued_errors.any?
|
76
|
+
visitor.rescued_errors
|
77
|
+
else
|
78
|
+
query_analyzers.map(&:result)
|
79
|
+
end
|
71
80
|
else
|
72
81
|
[]
|
73
82
|
end
|
data/lib/graphql/argument.rb
CHANGED
@@ -3,14 +3,14 @@ module GraphQL
|
|
3
3
|
# @api deprecated
|
4
4
|
class Argument
|
5
5
|
include GraphQL::Define::InstanceDefinable
|
6
|
-
accepts_definitions :name, :type, :description, :default_value, :as, :prepare, :method_access
|
6
|
+
accepts_definitions :name, :type, :description, :default_value, :as, :prepare, :method_access, :deprecation_reason
|
7
7
|
attr_reader :default_value
|
8
|
-
attr_accessor :description, :name, :as
|
8
|
+
attr_accessor :description, :name, :as, :deprecation_reason
|
9
9
|
attr_accessor :ast_node
|
10
10
|
attr_accessor :method_access
|
11
11
|
alias :graphql_name :name
|
12
12
|
|
13
|
-
ensure_defined(:name, :description, :default_value, :type=, :type, :as, :expose_as, :prepare, :method_access)
|
13
|
+
ensure_defined(:name, :description, :default_value, :type=, :type, :as, :expose_as, :prepare, :method_access, :deprecation_reason)
|
14
14
|
|
15
15
|
# @api private
|
16
16
|
module DefaultPrepare
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Backtrace
|
4
|
+
module LegacyTracer
|
5
|
+
module_function
|
6
|
+
|
7
|
+
# Implement the {GraphQL::Tracing} API.
|
8
|
+
def trace(key, metadata)
|
9
|
+
case key
|
10
|
+
when "lex", "parse"
|
11
|
+
# No context here, don't have a query yet
|
12
|
+
nil
|
13
|
+
when "execute_multiplex", "analyze_multiplex"
|
14
|
+
# No query context yet
|
15
|
+
nil
|
16
|
+
when "validate", "analyze_query", "execute_query", "execute_query_lazy"
|
17
|
+
query = metadata[:query] || metadata[:queries].first
|
18
|
+
push_data = query
|
19
|
+
multiplex = query.multiplex
|
20
|
+
when "execute_field", "execute_field_lazy"
|
21
|
+
# The interpreter passes `query:`, legacy passes `context:`
|
22
|
+
context = metadata[:context] || ((q = metadata[:query]) && q.context)
|
23
|
+
push_data = context
|
24
|
+
multiplex = context.query.multiplex
|
25
|
+
else
|
26
|
+
# Custom key, no backtrace data for this
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
|
30
|
+
if push_data
|
31
|
+
multiplex.context[:last_graphql_backtrace_context] = push_data
|
32
|
+
end
|
33
|
+
|
34
|
+
if key == "execute_multiplex"
|
35
|
+
begin
|
36
|
+
yield
|
37
|
+
rescue StandardError => err
|
38
|
+
# This is an unhandled error from execution,
|
39
|
+
# Re-raise it with a GraphQL trace.
|
40
|
+
potential_context = metadata[:multiplex].context[:last_graphql_backtrace_context]
|
41
|
+
|
42
|
+
if potential_context.is_a?(GraphQL::Query::Context) || potential_context.is_a?(GraphQL::Query::Context::FieldResolutionContext)
|
43
|
+
raise TracedError.new(err, potential_context)
|
44
|
+
else
|
45
|
+
raise
|
46
|
+
end
|
47
|
+
ensure
|
48
|
+
metadata[:multiplex].context.delete(:last_graphql_backtrace_context)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
yield
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
# test_via: ../backtrace.rb
|
3
2
|
module GraphQL
|
4
3
|
class Backtrace
|
5
4
|
# A class for turning a context into a human-readable table or array
|
@@ -79,6 +78,25 @@ module GraphQL
|
|
79
78
|
# @return [Array] 5 items for a backtrace table (not `key`)
|
80
79
|
def build_rows(context_entry, rows:, top: false)
|
81
80
|
case context_entry
|
81
|
+
when Backtrace::Frame
|
82
|
+
field_alias = context_entry.ast_node.respond_to?(:alias) && context_entry.ast_node.alias
|
83
|
+
value = if top && @override_value
|
84
|
+
@override_value
|
85
|
+
else
|
86
|
+
value_at(@context.query.context.namespace(:interpreter)[:runtime], context_entry.path)
|
87
|
+
end
|
88
|
+
rows << [
|
89
|
+
"#{context_entry.ast_node ? context_entry.ast_node.position.join(":") : ""}",
|
90
|
+
"#{context_entry.field.path}#{field_alias ? " as #{field_alias}" : ""}",
|
91
|
+
"#{context_entry.object.object.inspect}",
|
92
|
+
context_entry.arguments.to_h.inspect, # rubocop:disable Development/ContextIsPassedCop -- unrelated method
|
93
|
+
Backtrace::InspectResult.inspect_result(value),
|
94
|
+
]
|
95
|
+
if (parent = context_entry.parent_frame)
|
96
|
+
build_rows(parent, rows: rows)
|
97
|
+
else
|
98
|
+
rows
|
99
|
+
end
|
82
100
|
when GraphQL::Query::Context::FieldResolutionContext
|
83
101
|
ctx = context_entry
|
84
102
|
field_name = "#{ctx.irep_node.owner_type.name}.#{ctx.field.name}"
|
@@ -112,16 +130,29 @@ module GraphQL
|
|
112
130
|
if object.is_a?(GraphQL::Schema::Object)
|
113
131
|
object = object.object
|
114
132
|
end
|
133
|
+
value = value_at(context_entry.namespace(:interpreter)[:runtime], [])
|
115
134
|
rows << [
|
116
135
|
"#{position}",
|
117
136
|
"#{op_type}#{op_name ? " #{op_name}" : ""}",
|
118
137
|
"#{object.inspect}",
|
119
138
|
query.variables.to_h.inspect,
|
120
|
-
Backtrace::InspectResult.inspect_result(
|
139
|
+
Backtrace::InspectResult.inspect_result(value),
|
121
140
|
]
|
122
141
|
else
|
123
|
-
raise "Unexpected get_rows subject #{context_entry.inspect}"
|
142
|
+
raise "Unexpected get_rows subject #{context_entry.class} (#{context_entry.inspect})"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def value_at(runtime, path)
|
147
|
+
response = runtime.final_result
|
148
|
+
path.each do |key|
|
149
|
+
if response && (response = response[key])
|
150
|
+
next
|
151
|
+
else
|
152
|
+
break
|
153
|
+
end
|
124
154
|
end
|
155
|
+
response
|
125
156
|
end
|
126
157
|
end
|
127
158
|
end
|