graphql 2.6.14 → 2.6.15
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/templates/graphql_controller.erb +7 -1
- data/lib/graphql/analysis/max_query_complexity.rb +2 -1
- data/lib/graphql/analysis/max_query_depth.rb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +2 -1
- data/lib/graphql/analysis/query_depth.rb +1 -1
- data/lib/graphql/analysis/visitor.rb +14 -2
- data/lib/graphql/analysis.rb +4 -5
- data/lib/graphql/analysis_error.rb +1 -1
- data/lib/graphql/coercion_error.rb +1 -1
- data/lib/graphql/current.rb +5 -1
- data/lib/graphql/dashboard.rb +1 -1
- data/lib/graphql/date_encoding_error.rb +3 -1
- data/lib/graphql/duration_encoding_error.rb +3 -1
- data/lib/graphql/execution/directive_checks.rb +2 -1
- data/lib/graphql/execution/errors.rb +18 -7
- data/lib/graphql/execution/field_resolve_step.rb +3 -3
- data/lib/graphql/execution/input_values.rb +160 -120
- data/lib/graphql/execution/interpreter/runtime.rb +9 -5
- data/lib/graphql/execution/lookahead.rb +19 -8
- data/lib/graphql/execution/next.rb +1 -0
- data/lib/graphql/execution/runner.rb +19 -12
- data/lib/graphql/execution/selections_step.rb +1 -1
- data/lib/graphql/execution/variable_values.rb +248 -0
- data/lib/graphql/float_decoding_error.rb +3 -1
- data/lib/graphql/integer_decoding_error.rb +3 -1
- data/lib/graphql/introspection/base_object.rb +1 -1
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +3 -1
- data/lib/graphql/introspection/dynamic_fields.rb +3 -1
- data/lib/graphql/introspection/enum_value_type.rb +3 -1
- data/lib/graphql/introspection/field_type.rb +5 -1
- data/lib/graphql/introspection/type_kind_enum.rb +1 -1
- data/lib/graphql/introspection.rb +2 -1
- data/lib/graphql/language/comment.rb +2 -1
- data/lib/graphql/language/generation.rb +2 -1
- data/lib/graphql/language/lexer.rb +29 -21
- data/lib/graphql/language/parser.rb +10 -7
- data/lib/graphql/language.rb +1 -1
- data/lib/graphql/name_validator.rb +2 -1
- data/lib/graphql/pagination.rb +1 -1
- data/lib/graphql/query/fingerprint.rb +2 -1
- data/lib/graphql/query/partial.rb +4 -0
- data/lib/graphql/query.rb +8 -12
- data/lib/graphql/railtie.rb +1 -1
- data/lib/graphql/relay.rb +1 -1
- data/lib/graphql/rubocop.rb +1 -1
- data/lib/graphql/runtime_error.rb +1 -1
- data/lib/graphql/runtime_type_error.rb +1 -1
- data/lib/graphql/schema/always_visible.rb +3 -1
- data/lib/graphql/schema/base_64_encoder.rb +3 -1
- data/lib/graphql/schema/built_in_types.rb +2 -2
- data/lib/graphql/schema/directive/deprecated.rb +1 -1
- data/lib/graphql/schema/directive/include.rb +2 -1
- data/lib/graphql/schema/directive/skip.rb +2 -1
- data/lib/graphql/schema/directive/specified_by.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +2 -1
- data/lib/graphql/schema/field.rb +3 -0
- data/lib/graphql/schema/find_inherited_value.rb +5 -1
- data/lib/graphql/schema/member/graphql_type_names.rb +1 -1
- data/lib/graphql/schema/member/has_directives.rb +20 -22
- data/lib/graphql/schema/member/has_path.rb +2 -1
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +2 -1
- data/lib/graphql/schema/member.rb +3 -2
- data/lib/graphql/schema/mutation.rb +6 -1
- data/lib/graphql/schema/object.rb +1 -1
- data/lib/graphql/schema/resolver.rb +2 -2
- data/lib/graphql/schema/type_expression.rb +3 -1
- data/lib/graphql/schema/validator/all_validator.rb +6 -3
- data/lib/graphql/static_validation/all_rules.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +125 -53
- data/lib/graphql/static_validation/definition_dependencies.rb +14 -2
- data/lib/graphql/static_validation/interpreter_visitor.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +4 -1
- 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 +8 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -1
- data/lib/graphql/static_validation/rules/fragments_are_named.rb +2 -1
- data/lib/graphql/static_validation/rules/fragments_are_named_error.rb +4 -1
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +6 -3
- data/lib/graphql/static_validation/rules/fragments_are_used.rb +2 -1
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +3 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +3 -2
- data/lib/graphql/static_validation/rules/mutation_root_exists_error.rb +4 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present_error.rb +4 -1
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +4 -1
- data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +9 -2
- data/lib/graphql/static_validation/rules/query_root_exists.rb +3 -2
- data/lib/graphql/static_validation/rules/query_root_exists_error.rb +4 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb +10 -4
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +8 -6
- data/lib/graphql/static_validation/rules/subscription_root_exists_error.rb +4 -1
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +5 -4
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +2 -1
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +2 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/validation_context.rb +49 -14
- data/lib/graphql/static_validation/validation_timeout_error.rb +4 -1
- data/lib/graphql/static_validation.rb +1 -2
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +14 -13
- data/lib/graphql/testing.rb +1 -1
- data/lib/graphql/tracing/legacy_trace.rb +1 -1
- data/lib/graphql/tracing/null_trace.rb +1 -1
- data/lib/graphql/types/big_int.rb +4 -1
- data/lib/graphql/types/boolean.rb +3 -1
- data/lib/graphql/types/id.rb +3 -1
- data/lib/graphql/types/iso_8601_date.rb +1 -0
- data/lib/graphql/types/iso_8601_date_time.rb +6 -2
- data/lib/graphql/types/iso_8601_duration.rb +3 -0
- data/lib/graphql/types/relay/base_connection.rb +1 -1
- data/lib/graphql/types/relay/base_edge.rb +1 -1
- data/lib/graphql/types/relay/node.rb +1 -1
- data/lib/graphql/types/relay/page_info.rb +1 -1
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -1
- data/lib/graphql/types/relay.rb +1 -1
- data/lib/graphql/types/string.rb +1 -0
- data/lib/graphql/types.rb +1 -1
- data/lib/graphql/version.rb +2 -2
- metadata +3 -4
- data/lib/graphql/query/variables.rb +0 -99
- data/lib/graphql/static_validation/literal_validator.rb +0 -157
|
@@ -6,6 +6,7 @@ module GraphQL
|
|
|
6
6
|
# @requires_ancestor: GraphQL::StaticValidation::BaseVisitor
|
|
7
7
|
# @requires_ancestor: GraphQL::StaticValidation::RequiredInputObjectAttributesArePresent
|
|
8
8
|
module OneOfInputObjectsAreValid
|
|
9
|
+
#: (GraphQL::Language::Nodes::InputObject node, GraphQL::Language::Nodes::AbstractNode parent) -> void
|
|
9
10
|
def on_input_object(node, parent)
|
|
10
11
|
return super unless parent.is_a?(GraphQL::Language::Nodes::Argument)
|
|
11
12
|
|
|
@@ -18,6 +19,7 @@ module GraphQL
|
|
|
18
19
|
|
|
19
20
|
private
|
|
20
21
|
|
|
22
|
+
#: (GraphQL::Language::Nodes::InputObject ast_node, GraphQL::StaticValidation::ValidationContext context, singleton(GraphQL::Schema::InputObject) parent_type) -> void
|
|
21
23
|
def validate_one_of_input_object(ast_node, context, parent_type)
|
|
22
24
|
present_fields = ast_node.arguments.map(&:name)
|
|
23
25
|
input_object_type = parent_type.to_type_signature
|
|
@@ -35,7 +37,9 @@ module GraphQL
|
|
|
35
37
|
end
|
|
36
38
|
|
|
37
39
|
field = present_fields.first
|
|
38
|
-
|
|
40
|
+
# `present_fields.count == 1` was checked above:
|
|
41
|
+
argument = ast_node.arguments.first #: as !nil
|
|
42
|
+
value = argument.value
|
|
39
43
|
|
|
40
44
|
if value.is_a?(GraphQL::Language::Nodes::NullValue)
|
|
41
45
|
add_error(
|
|
@@ -51,7 +55,10 @@ module GraphQL
|
|
|
51
55
|
|
|
52
56
|
if value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
|
|
53
57
|
variable_name = value.name
|
|
54
|
-
|
|
58
|
+
variable_definition = @declared_variables && @declared_variables[variable_name]
|
|
59
|
+
# Undeclared variables are reported by VariablesAreUsedAndDefined
|
|
60
|
+
return if variable_definition.nil?
|
|
61
|
+
variable_type = variable_definition.type
|
|
55
62
|
|
|
56
63
|
unless variable_type.is_a?(GraphQL::Language::Nodes::NonNullType)
|
|
57
64
|
add_error(
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module StaticValidation
|
|
5
5
|
#--
|
|
6
6
|
# @requires_ancestor: GraphQL::StaticValidation::BaseVisitor
|
|
7
7
|
module QueryRootExists
|
|
8
|
+
#: (GraphQL::Language::Nodes::OperationDefinition node, GraphQL::Language::Nodes::AbstractNode? _parent) -> void
|
|
8
9
|
def on_operation_definition(node, _parent)
|
|
9
|
-
if (node.operation_type == 'query' || node.operation_type.nil?) && context.
|
|
10
|
+
if (node.operation_type == 'query' || node.operation_type.nil?) && context.types.query_root.nil?
|
|
10
11
|
add_error(GraphQL::StaticValidation::QueryRootExistsError.new(
|
|
11
12
|
'Schema is not configured for queries',
|
|
12
13
|
nodes: node
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module StaticValidation
|
|
5
5
|
class QueryRootExistsError < StaticValidation::Error
|
|
6
6
|
|
|
7
|
+
#: (String message, ?path: Array[String | Integer]?, ?nodes: GraphQL::Language::Nodes::AbstractNode | Array[GraphQL::Language::Nodes::AbstractNode]) -> void
|
|
7
8
|
def initialize(message, path: nil, nodes: [])
|
|
8
9
|
super(message, path: path, nodes: nodes)
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
# A hash representation of this Message
|
|
13
|
+
#: -> Hash[String, untyped]
|
|
12
14
|
def to_h
|
|
13
15
|
extensions = {
|
|
14
16
|
"code" => code,
|
|
@@ -19,6 +21,7 @@ module GraphQL
|
|
|
19
21
|
})
|
|
20
22
|
end
|
|
21
23
|
|
|
24
|
+
#: -> String
|
|
22
25
|
def code
|
|
23
26
|
"missingQueryConfiguration"
|
|
24
27
|
end
|
data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present_error.rb
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module StaticValidation
|
|
5
5
|
class RequiredInputObjectAttributesArePresentError < StaticValidation::Error
|
|
6
|
+
#: String
|
|
6
7
|
attr_reader :argument_type
|
|
8
|
+
#: String
|
|
7
9
|
attr_reader :argument_name
|
|
10
|
+
#: String
|
|
8
11
|
attr_reader :input_object_type
|
|
9
12
|
|
|
13
|
+
#: (String message, path: Array[String | Integer]?, nodes: GraphQL::Language::Nodes::AbstractNode | Array[GraphQL::Language::Nodes::AbstractNode], argument_type: String, argument_name: String, input_object_type: String) -> void
|
|
10
14
|
def initialize(message, path:, nodes:, argument_type:, argument_name:, input_object_type:)
|
|
11
15
|
super(message, path: path, nodes: nodes)
|
|
12
|
-
@argument_type = argument_type
|
|
13
|
-
@argument_name = argument_name
|
|
14
|
-
@input_object_type = input_object_type
|
|
16
|
+
@argument_type = argument_type #: String
|
|
17
|
+
@argument_name = argument_name #: String
|
|
18
|
+
@input_object_type = input_object_type #: String
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
# A hash representation of this Message
|
|
22
|
+
#: () -> Hash[String, untyped]
|
|
18
23
|
def to_h
|
|
19
24
|
extensions = {
|
|
20
25
|
"code" => code,
|
|
@@ -28,6 +33,7 @@ module GraphQL
|
|
|
28
33
|
})
|
|
29
34
|
end
|
|
30
35
|
|
|
36
|
+
#: () -> String
|
|
31
37
|
def code
|
|
32
38
|
"missingRequiredInputObjectAttribute"
|
|
33
39
|
end
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module StaticValidation
|
|
5
5
|
#--
|
|
6
6
|
# @requires_ancestor: GraphQL::StaticValidation::BaseVisitor
|
|
7
7
|
module SubscriptionRootExistsAndSingleSubscriptionSelection
|
|
8
|
+
#: (GraphQL::Language::Nodes::OperationDefinition node, GraphQL::Language::Nodes::AbstractNode parent) -> void
|
|
8
9
|
def on_operation_definition(node, parent)
|
|
9
10
|
if node.operation_type == "subscription"
|
|
10
11
|
if context.types.subscription_root.nil?
|
|
@@ -34,11 +35,12 @@ module GraphQL
|
|
|
34
35
|
|
|
35
36
|
private
|
|
36
37
|
|
|
38
|
+
#: (GraphQL::Language::Nodes::OperationDefinition node) -> Array[GraphQL::Language::Nodes::Field]
|
|
37
39
|
def subscription_root_fields(node)
|
|
38
40
|
root_type = context.types.subscription_root
|
|
39
|
-
fields = []
|
|
40
|
-
selections = node.selections.dup
|
|
41
|
-
visited_fragments = {}
|
|
41
|
+
fields = [] #: Array[GraphQL::Language::Nodes::Field]
|
|
42
|
+
selections = node.selections.dup #: Array[GraphQL::Language::Nodes::Field | GraphQL::Language::Nodes::FragmentSpread | GraphQL::Language::Nodes::InlineFragment | GraphQL::Language::Nodes::FragmentDefinition]
|
|
43
|
+
visited_fragments = {} #: Hash[String, bool]
|
|
42
44
|
|
|
43
45
|
while (selection = selections.pop)
|
|
44
46
|
case selection
|
|
@@ -50,8 +52,8 @@ module GraphQL
|
|
|
50
52
|
fragment = context.fragments[selection.name]
|
|
51
53
|
selections << fragment if fragment
|
|
52
54
|
when GraphQL::Language::Nodes::InlineFragment, GraphQL::Language::Nodes::FragmentDefinition
|
|
53
|
-
fragment_type = (selection_type = selection.type) ?
|
|
54
|
-
if fragment_type && fragment_type.kind.composite? &&
|
|
55
|
+
fragment_type = (selection_type = selection.type) ? types.type(selection_type.name) : root_type
|
|
56
|
+
if fragment_type && fragment_type.kind.composite? && types.possible_types(fragment_type).include?(root_type)
|
|
55
57
|
selections.concat(selection.selections)
|
|
56
58
|
end
|
|
57
59
|
end
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module StaticValidation
|
|
5
5
|
class SubscriptionRootExistsError < StaticValidation::Error
|
|
6
6
|
|
|
7
|
+
#: (String message, ?path: Array[String | Integer]?, ?nodes: GraphQL::Language::Nodes::AbstractNode | Array[GraphQL::Language::Nodes::AbstractNode]) -> void
|
|
7
8
|
def initialize(message, path: nil, nodes: [])
|
|
8
9
|
super(message, path: path, nodes: nodes)
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
# A hash representation of this Message
|
|
13
|
+
#: -> Hash[String, untyped]
|
|
12
14
|
def to_h
|
|
13
15
|
extensions = {
|
|
14
16
|
"code" => code,
|
|
@@ -19,6 +21,7 @@ module GraphQL
|
|
|
19
21
|
})
|
|
20
22
|
end
|
|
21
23
|
|
|
24
|
+
#: -> String
|
|
22
25
|
def code
|
|
23
26
|
"missingSubscriptionConfiguration"
|
|
24
27
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module StaticValidation
|
|
5
5
|
#--
|
|
@@ -20,7 +20,7 @@ module GraphQL
|
|
|
20
20
|
:on_enum_value_definition,
|
|
21
21
|
:on_input_object_type_definition,
|
|
22
22
|
:on_field,
|
|
23
|
-
]
|
|
23
|
+
] #: Array[Symbol]
|
|
24
24
|
|
|
25
25
|
VALIDATE_DIRECTIVE_LOCATION_ON_NODE = <<~RUBY
|
|
26
26
|
def %{method_name}(node, parent)
|
|
@@ -37,12 +37,13 @@ module GraphQL
|
|
|
37
37
|
|
|
38
38
|
private
|
|
39
39
|
|
|
40
|
+
#: (GraphQL::Language::Nodes::FragmentDefinition | GraphQL::Language::Nodes::FragmentSpread | GraphQL::Language::Nodes::InlineFragment | GraphQL::Language::Nodes::OperationDefinition | GraphQL::Language::Nodes::ScalarTypeDefinition | GraphQL::Language::Nodes::ObjectTypeDefinition | GraphQL::Language::Nodes::InputValueDefinition | GraphQL::Language::Nodes::FieldDefinition | GraphQL::Language::Nodes::InterfaceTypeDefinition | GraphQL::Language::Nodes::UnionTypeDefinition | GraphQL::Language::Nodes::EnumTypeDefinition | GraphQL::Language::Nodes::EnumValueDefinition | GraphQL::Language::Nodes::InputObjectTypeDefinition | GraphQL::Language::Nodes::Field node) -> void
|
|
40
41
|
def validate_directive_location(node)
|
|
41
|
-
used_directives = {}
|
|
42
|
+
used_directives = {} #: Hash[String, GraphQL::Language::Nodes::Directive]
|
|
42
43
|
node.directives.each do |ast_directive|
|
|
43
44
|
directive_name = ast_directive.name
|
|
44
45
|
if (first_node = used_directives[directive_name])
|
|
45
|
-
@directives_are_unique_errors_by_first_node ||= {}
|
|
46
|
+
@directives_are_unique_errors_by_first_node ||= {} #: Hash[GraphQL::Language::Nodes::Directive, GraphQL::StaticValidation::UniqueDirectivesPerLocationError]?
|
|
46
47
|
err = @directives_are_unique_errors_by_first_node[first_node] ||= begin
|
|
47
48
|
error = GraphQL::StaticValidation::UniqueDirectivesPerLocationError.new(
|
|
48
49
|
"The directive \"#{directive_name}\" can only be used once at this location.",
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module StaticValidation
|
|
5
5
|
#--
|
|
6
6
|
# @requires_ancestor: GraphQL::StaticValidation::BaseVisitor
|
|
7
7
|
module VariableDefaultValuesAreCorrectlyTyped
|
|
8
|
+
#: (GraphQL::Language::Nodes::VariableDefinition node, GraphQL::Language::Nodes::AbstractNode? parent) -> void
|
|
8
9
|
def on_variable_definition(node, parent)
|
|
9
10
|
if !node.default_value.nil?
|
|
10
11
|
value = node.default_value
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module StaticValidation
|
|
5
5
|
#--
|
|
6
6
|
# @requires_ancestor: GraphQL::StaticValidation::BaseVisitor
|
|
7
7
|
module VariableNamesAreUnique
|
|
8
|
+
#: (GraphQL::Language::Nodes::OperationDefinition node, GraphQL::Language::Nodes::AbstractNode? parent) -> void
|
|
8
9
|
def on_operation_definition(node, parent)
|
|
9
10
|
var_defns = node.variables
|
|
10
11
|
if !var_defns.empty?
|
|
@@ -12,7 +12,7 @@ module GraphQL
|
|
|
12
12
|
def initialize(*)
|
|
13
13
|
super
|
|
14
14
|
# holds { name => ast_node } pairs
|
|
15
|
-
@declared_variables = {}
|
|
15
|
+
@declared_variables = {} #: Hash[String, GraphQL::Language::Nodes::VariableDefinition]
|
|
16
16
|
@operation_declared_variables = {}.compare_by_identity #: Hash[GraphQL::Language::Nodes::OperationDefinition, Hash[String, GraphQL::Language::Nodes::VariableDefinition]]
|
|
17
17
|
@variable_usages_fragment_definition = nil
|
|
18
18
|
@fragment_variable_usages = nil #: Hash[GraphQL::Language::Nodes::FragmentDefinition, Array[FragmentVariableUsage]]?
|
|
@@ -7,7 +7,7 @@ module GraphQL
|
|
|
7
7
|
module VariablesAreInputTypes
|
|
8
8
|
def on_variable_definition(node, parent)
|
|
9
9
|
type_name = get_type_name(node.type)
|
|
10
|
-
type = context.
|
|
10
|
+
type = context.types.type(type_name)
|
|
11
11
|
|
|
12
12
|
if type.nil?
|
|
13
13
|
suggestion = if @schema.did_you_mean
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module StaticValidation
|
|
5
5
|
# The validation context gets passed to each validator.
|
|
@@ -12,22 +12,40 @@ module GraphQL
|
|
|
12
12
|
class ValidationContext
|
|
13
13
|
extend Forwardable
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
#: GraphQL::Query
|
|
16
|
+
attr_reader :query
|
|
17
|
+
|
|
18
|
+
#: Array[GraphQL::StaticValidation::Error]
|
|
19
|
+
attr_reader :errors
|
|
20
|
+
|
|
21
|
+
#: GraphQL::StaticValidation::BaseVisitor
|
|
22
|
+
attr_reader :visitor
|
|
23
|
+
|
|
24
|
+
#: Array[^(GraphQL::Language::Nodes::OperationDefinition | GraphQL::Language::Nodes::FragmentDefinition, Array[GraphQL::Language::Nodes::FragmentSpread], GraphQL::Language::Nodes::FragmentDefinition) -> void]
|
|
25
|
+
attr_reader :on_dependency_resolve_handlers
|
|
26
|
+
|
|
27
|
+
#: Integer | Float
|
|
28
|
+
attr_reader :max_errors
|
|
29
|
+
|
|
30
|
+
#: GraphQL::Schema::Visibility::Profile | GraphQL::Schema::Warden::VisibilityProfile
|
|
31
|
+
attr_reader :types
|
|
32
|
+
|
|
33
|
+
#: singleton(GraphQL::Schema)
|
|
34
|
+
attr_reader :schema
|
|
18
35
|
|
|
19
36
|
|
|
20
37
|
def_delegators :@query, :document, :fragments, :operations
|
|
21
38
|
|
|
39
|
+
#: (GraphQL::Query query, singleton(GraphQL::StaticValidation::BaseVisitor) visitor_class, Integer? max_errors) -> void
|
|
22
40
|
def initialize(query, visitor_class, max_errors)
|
|
23
|
-
@query = query
|
|
24
|
-
@types = query.types
|
|
25
|
-
@schema = query.schema
|
|
26
|
-
@
|
|
27
|
-
@errors = []
|
|
28
|
-
@max_errors = max_errors || Float::INFINITY
|
|
29
|
-
@on_dependency_resolve_handlers = []
|
|
30
|
-
@visitor = visitor_class.new(document, self)
|
|
41
|
+
@query = query #: GraphQL::Query
|
|
42
|
+
@types = query.types #: GraphQL::Schema::Visibility::Profile | GraphQL::Schema::Warden::VisibilityProfile
|
|
43
|
+
@schema = query.schema #: singleton(GraphQL::Schema)
|
|
44
|
+
@input_values = query.input_values #: GraphQL::Execution::InputValues
|
|
45
|
+
@errors = [] #: Array[GraphQL::StaticValidation::Error]
|
|
46
|
+
@max_errors = max_errors || Float::INFINITY #: Integer | Float
|
|
47
|
+
@on_dependency_resolve_handlers = [] #: Array[^(GraphQL::Language::Nodes::OperationDefinition | GraphQL::Language::Nodes::FragmentDefinition, Array[GraphQL::Language::Nodes::FragmentSpread], GraphQL::Language::Nodes::FragmentDefinition) -> void]
|
|
48
|
+
@visitor = visitor_class.new(document, self) #: GraphQL::StaticValidation::BaseVisitor
|
|
31
49
|
end
|
|
32
50
|
|
|
33
51
|
# TODO stop using def_delegators because of Array allocations
|
|
@@ -35,22 +53,39 @@ module GraphQL
|
|
|
35
53
|
:path, :type_definition, :field_definition, :argument_definition,
|
|
36
54
|
:parent_type_definition, :directive_definition, :dependencies
|
|
37
55
|
|
|
56
|
+
#: () { (GraphQL::Language::Nodes::OperationDefinition | GraphQL::Language::Nodes::FragmentDefinition, Array[GraphQL::Language::Nodes::FragmentSpread], GraphQL::Language::Nodes::FragmentDefinition) -> void } -> void
|
|
38
57
|
def on_dependency_resolve(&handler)
|
|
39
58
|
@on_dependency_resolve_handlers << handler
|
|
40
59
|
end
|
|
41
60
|
|
|
61
|
+
INVALID_RESULT = GraphQL::Query::InputValidationResult.new(valid: false, problems: EmptyObjects::EMPTY_ARRAY).freeze #: GraphQL::Query::InputValidationResult
|
|
62
|
+
private_constant :INVALID_RESULT
|
|
63
|
+
|
|
64
|
+
#: (untyped ast_value, GraphQL::Schema::Member::type_definition | GraphQL::Schema::NonNull | GraphQL::Schema::List type) -> GraphQL::Query::InputValidationResult
|
|
42
65
|
def validate_literal(ast_value, type)
|
|
43
|
-
@
|
|
66
|
+
@input_values.value_from_ast(ast_value, type, validate: true)
|
|
67
|
+
GraphQL::Query::InputValidationResult::VALID
|
|
68
|
+
rescue Execution::InputValues::InvalidLiteralError => err
|
|
69
|
+
if err.explanation
|
|
70
|
+
Query::InputValidationResult.from_problem(err.explanation)
|
|
71
|
+
else
|
|
72
|
+
INVALID_RESULT
|
|
73
|
+
end
|
|
74
|
+
rescue CoercionError => coercion_err
|
|
75
|
+
Query::InputValidationResult.from_problem(coercion_err.message, message: coercion_err.message, extensions: coercion_err.extensions)
|
|
44
76
|
end
|
|
45
77
|
|
|
78
|
+
#: () -> bool
|
|
46
79
|
def too_many_errors?
|
|
47
80
|
@errors.length >= @max_errors
|
|
48
81
|
end
|
|
49
82
|
|
|
83
|
+
#: () -> Hash[String, singleton(GraphQL::Schema::Directive)]
|
|
50
84
|
def schema_directives
|
|
51
|
-
@schema_directives ||= schema.directives
|
|
85
|
+
@schema_directives ||= schema.directives #: Hash[String, singleton(GraphQL::Schema::Directive)]?
|
|
52
86
|
end
|
|
53
87
|
|
|
88
|
+
#: (String name, Array[String] options) -> String?
|
|
54
89
|
def did_you_mean_suggestion(name, options)
|
|
55
90
|
if did_you_mean = schema.did_you_mean
|
|
56
91
|
suggestions = did_you_mean.const_get(:SpellChecker).new(dictionary: options).correct(name)
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module StaticValidation
|
|
5
5
|
class ValidationTimeoutError < StaticValidation::Error
|
|
6
|
+
#: (String message, ?path: Array[String | Integer]?, ?nodes: GraphQL::Language::Nodes::AbstractNode | Array[GraphQL::Language::Nodes::AbstractNode]) -> void
|
|
6
7
|
def initialize(message, path: nil, nodes: [])
|
|
7
8
|
super(message, path: path, nodes: nodes)
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
# A hash representation of this Message
|
|
12
|
+
#: -> Hash[String, untyped]
|
|
11
13
|
def to_h
|
|
12
14
|
extensions = {
|
|
13
15
|
"code" => code
|
|
@@ -18,6 +20,7 @@ module GraphQL
|
|
|
18
20
|
})
|
|
19
21
|
end
|
|
20
22
|
|
|
23
|
+
#: -> String
|
|
21
24
|
def code
|
|
22
25
|
"validationTimeout"
|
|
23
26
|
end
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
require "graphql/static_validation/error"
|
|
4
4
|
require "graphql/static_validation/definition_dependencies"
|
|
5
5
|
require "graphql/static_validation/validator"
|
|
6
6
|
require "graphql/static_validation/validation_context"
|
|
7
7
|
require "graphql/static_validation/validation_timeout_error"
|
|
8
|
-
require "graphql/static_validation/literal_validator"
|
|
9
8
|
require "graphql/static_validation/base_visitor"
|
|
10
9
|
|
|
11
10
|
rules_glob = File.expand_path("../static_validation/rules/*.rb", __FILE__)
|
|
@@ -106,13 +106,7 @@ module GraphQL
|
|
|
106
106
|
# A per-process map of subscriptions to deliver.
|
|
107
107
|
# This is provided by Rails, so let's use it
|
|
108
108
|
@subscriptions = Concurrent::Map.new
|
|
109
|
-
@events = Concurrent::Map.new
|
|
110
|
-
h.compute_if_absent(k) do
|
|
111
|
-
Concurrent::Map.new do |h2, k2|
|
|
112
|
-
h2.compute_if_absent(k2) { Concurrent::Array.new }
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|
|
109
|
+
@events = Concurrent::Map.new
|
|
116
110
|
@action_cable = action_cable
|
|
117
111
|
@action_cable_coder = action_cable_coder
|
|
118
112
|
@serializer = serializer
|
|
@@ -164,7 +158,11 @@ module GraphQL
|
|
|
164
158
|
# Setup a new listener to run all events with this topic in this process
|
|
165
159
|
setup_stream(channel, event)
|
|
166
160
|
# Add this event to the list of events to be updated
|
|
167
|
-
@events
|
|
161
|
+
@events.compute(event.topic) do |events_by_fingerprint|
|
|
162
|
+
events_by_fingerprint ||= Concurrent::Map.new
|
|
163
|
+
events_by_fingerprint.compute_if_absent(event.fingerprint) { Concurrent::Array.new } << event
|
|
164
|
+
events_by_fingerprint
|
|
165
|
+
end
|
|
168
166
|
end
|
|
169
167
|
end
|
|
170
168
|
|
|
@@ -184,6 +182,8 @@ module GraphQL
|
|
|
184
182
|
event_stream = stream_event_name(initial_event)
|
|
185
183
|
channel.stream_from(event_stream, coder: @action_cable_coder) do |message|
|
|
186
184
|
events_by_fingerprint = @events[topic]
|
|
185
|
+
next if events_by_fingerprint.nil?
|
|
186
|
+
|
|
187
187
|
object = nil #: untyped
|
|
188
188
|
events_by_fingerprint.each do |_fingerprint, events|
|
|
189
189
|
if !events.empty? && events.first == initial_event
|
|
@@ -252,11 +252,12 @@ module GraphQL
|
|
|
252
252
|
if query
|
|
253
253
|
events = query.context.namespace(:subscriptions)[:events]
|
|
254
254
|
events.each do |event|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
255
|
+
@events.compute_if_present(event.topic) do |events_by_fingerprint|
|
|
256
|
+
events_by_fingerprint.compute_if_present(event.fingerprint) do |events_for_fingerprint|
|
|
257
|
+
events_for_fingerprint.delete(event)
|
|
258
|
+
events_for_fingerprint.empty? ? nil : events_for_fingerprint
|
|
259
|
+
end
|
|
260
|
+
events_by_fingerprint.empty? ? nil : events_by_fingerprint
|
|
260
261
|
end
|
|
261
262
|
end
|
|
262
263
|
end
|
data/lib/graphql/testing.rb
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
|
|
4
4
|
module GraphQL
|
|
5
5
|
module Types
|
|
6
6
|
class BigInt < GraphQL::Schema::Scalar
|
|
7
7
|
description "Represents non-fractional signed whole numeric values. Since the value may exceed the size of a 32-bit integer, it's encoded as a string."
|
|
8
8
|
|
|
9
|
+
#: (untyped value, GraphQL::Query::Context _ctx) -> (::Numeric | false | nil)
|
|
9
10
|
def self.coerce_input(value, _ctx)
|
|
10
11
|
value && parse_int(value)
|
|
11
12
|
rescue ArgumentError
|
|
12
13
|
nil
|
|
13
14
|
end
|
|
14
15
|
|
|
16
|
+
#: (untyped value, GraphQL::Query::Context _ctx) -> ::String
|
|
15
17
|
def self.coerce_result(value, _ctx)
|
|
16
18
|
value.to_i.to_s
|
|
17
19
|
end
|
|
18
20
|
|
|
21
|
+
#: (untyped value) -> ::Numeric
|
|
19
22
|
def self.parse_int(value)
|
|
20
23
|
value.is_a?(Numeric) ? value : Integer(value, 10)
|
|
21
24
|
end
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Types
|
|
5
5
|
class Boolean < GraphQL::Schema::Scalar
|
|
6
6
|
description "Represents `true` or `false` values."
|
|
7
7
|
|
|
8
|
+
#: (untyped value, GraphQL::Query::Context _ctx) -> bool?
|
|
8
9
|
def self.coerce_input(value, _ctx)
|
|
9
10
|
(value == true || value == false) ? value : nil
|
|
10
11
|
end
|
|
11
12
|
|
|
13
|
+
#: (untyped value, GraphQL::Query::Context _ctx) -> bool
|
|
12
14
|
def self.coerce_result(value, _ctx)
|
|
13
15
|
!!value
|
|
14
16
|
end
|
data/lib/graphql/types/id.rb
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
module GraphQL
|
|
4
4
|
module Types
|
|
5
5
|
class ID < GraphQL::Schema::Scalar
|
|
6
6
|
graphql_name "ID"
|
|
7
7
|
description "Represents a unique identifier that is Base64 obfuscated. It is often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"VXNlci0xMA==\"`) or integer (such as `4`) input value will be accepted as an ID."
|
|
8
8
|
default_scalar true
|
|
9
|
+
#: (untyped value, GraphQL::Query::Context _ctx) -> ::String
|
|
9
10
|
def self.coerce_result(value, _ctx)
|
|
10
11
|
value.is_a?(::String) ? value : value.to_s
|
|
11
12
|
end
|
|
12
13
|
|
|
14
|
+
#: (untyped value, GraphQL::Query::Context _ctx) -> ::String?
|
|
13
15
|
def self.coerce_input(value, _ctx)
|
|
14
16
|
case value
|
|
15
17
|
when ::String
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
-
# typed:
|
|
2
|
+
# typed: strict
|
|
3
3
|
|
|
4
4
|
require 'time'
|
|
5
5
|
|
|
@@ -30,6 +30,7 @@ module GraphQL
|
|
|
30
30
|
#
|
|
31
31
|
# :call-seq:
|
|
32
32
|
# time_precision() -> Integer
|
|
33
|
+
#: -> Integer
|
|
33
34
|
def self.time_precision
|
|
34
35
|
@time_precision || DEFAULT_TIME_PRECISION
|
|
35
36
|
end
|
|
@@ -40,8 +41,9 @@ module GraphQL
|
|
|
40
41
|
#
|
|
41
42
|
# :call-seq:
|
|
42
43
|
# time_precision=(Integer value)
|
|
44
|
+
#: (Integer value) -> void
|
|
43
45
|
def self.time_precision=(value)
|
|
44
|
-
@time_precision = value
|
|
46
|
+
@time_precision = value #: Integer?
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
# **Parameters**
|
|
@@ -54,6 +56,7 @@ module GraphQL
|
|
|
54
56
|
#
|
|
55
57
|
# :call-seq:
|
|
56
58
|
# coerce_result(Time | Date | DateTime | String value, _ctx) -> String
|
|
59
|
+
#: (untyped value, GraphQL::Query::Context _ctx) -> ::String
|
|
57
60
|
def self.coerce_result(value, _ctx)
|
|
58
61
|
case value
|
|
59
62
|
when Date
|
|
@@ -78,6 +81,7 @@ module GraphQL
|
|
|
78
81
|
#
|
|
79
82
|
# :call-seq:
|
|
80
83
|
# coerce_input(String str_value, _ctx) -> Time
|
|
84
|
+
#: (untyped str_value, GraphQL::Query::Context _ctx) -> ::Time?
|
|
81
85
|
def self.coerce_input(str_value, _ctx)
|
|
82
86
|
Time.iso8601(str_value)
|
|
83
87
|
rescue ArgumentError, TypeError
|
|
@@ -23,6 +23,7 @@ module GraphQL
|
|
|
23
23
|
#
|
|
24
24
|
# :call-seq:
|
|
25
25
|
# seconds_precision() -> Integer | nil
|
|
26
|
+
#: -> Integer?
|
|
26
27
|
def self.seconds_precision
|
|
27
28
|
# ActiveSupport::Duration precision defaults to whatever input was given
|
|
28
29
|
@seconds_precision
|
|
@@ -34,6 +35,7 @@ module GraphQL
|
|
|
34
35
|
#
|
|
35
36
|
# :call-seq:
|
|
36
37
|
# seconds_precision=(Integer | nil value)
|
|
38
|
+
#: (Integer? value) -> Integer?
|
|
37
39
|
def self.seconds_precision=(value)
|
|
38
40
|
@seconds_precision = value
|
|
39
41
|
end
|
|
@@ -52,6 +54,7 @@ module GraphQL
|
|
|
52
54
|
#
|
|
53
55
|
# :call-seq:
|
|
54
56
|
# coerce_result(ActiveSupport::Duration | String value, _ctx) -> String | GraphQL::Error
|
|
57
|
+
#: (untyped value, GraphQL::Query::Context _ctx) -> ::String
|
|
55
58
|
def self.coerce_result(value, _ctx)
|
|
56
59
|
unless defined?(ActiveSupport::Duration)
|
|
57
60
|
raise GraphQL::Error, "ActiveSupport >= 5.0 must be loaded to use the built-in ISO8601Duration type."
|