# frozen_string_literal: truemoduleGraphQLmoduleStaticValidation# - Ride along with `GraphQL::Language::Visitor`# - Track type info, expose it to validatorsclassTypeStack# These are jumping-off points for infering types down the treeTYPE_INFERRENCE_ROOTS=[GraphQL::Language::Nodes::OperationDefinition,GraphQL::Language::Nodes::FragmentDefinition,]# @return [GraphQL::Schema] the schema whose types are present in this documentattr_reader:schema# When it enters an object (starting with query or mutation root), it's pushed on this stack.# When it exits, it's popped off.# @return [Array<GraphQL::ObjectType, GraphQL::Union, GraphQL::Interface>]attr_reader:object_types# When it enters a field, it's pushed on this stack (useful for nested fields, args).# When it exits, it's popped off.# @return [Array<GraphQL::Field>] fields which have been enteredattr_reader:field_definitions# Directives are pushed on, then popped off while traversing the tree# @return [Array<GraphQL::Node::Directive>] directives which have been enteredattr_reader:directive_definitions# @return [Array<GraphQL::Node::Argument>] arguments which have been enteredattr_reader:argument_definitions# @return [Array<String>] fields which have been entered (by their AST name)attr_reader:path# @param schema [GraphQL::Schema] the schema whose types to use when climbing this document# @param visitor [GraphQL::Language::Visitor] a visitor to follow & watch the typesdefinitialize(schema,visitor)@schema=schema@object_types=[]@field_definitions=[]@directive_definitions=[]@argument_definitions=[]@path=[]PUSH_STRATEGIES.eachdo|node_class,strategy|visitor[node_class].enter<<EnterWithStrategy.new(self,strategy)visitor[node_class].leave<<LeaveWithStrategy.new(self,strategy)endendprivatemoduleFragmentWithTypeStrategydefpush(stack,node)object_type=ifnode.typestack.schema.types.fetch(node.type.name,nil)elsestack.object_types.lastendif!object_type.nil?object_type=object_type.unwrapendstack.object_types.push(object_type)push_path_member(stack,node)enddefpop(stack,node)stack.object_types.popstack.path.popendendmoduleFragmentDefinitionStrategyextendFragmentWithTypeStrategymodule_functiondefpush_path_member(stack,node)stack.path.push("fragment #{node.name}")endendmoduleInlineFragmentStrategyextendFragmentWithTypeStrategymodule_functiondefpush_path_member(stack,node)stack.path.push("...#{node.type?" on #{node.type.to_query_string}":""}")endendmoduleOperationDefinitionStrategymodule_functiondefpush(stack,node)# eg, QueryType, MutationTypeobject_type=stack.schema.root_type_for_operation(node.operation_type)stack.object_types.push(object_type)stack.path.push("#{node.operation_type}#{node.name?" #{node.name}":""}")enddefpop(stack,node)stack.object_types.popstack.path.popendendmoduleFieldStrategymodule_functiondefpush(stack,node)parent_type=stack.object_types.lastparent_type=parent_type.unwrapfield_definition=stack.schema.get_field(parent_type,node.name)stack.field_definitions.push(field_definition)if!field_definition.nil?next_object_type=field_definition.typestack.object_types.push(next_object_type)elsestack.object_types.push(nil)endstack.path.push(node.alias||node.name)enddefpop(stack,node)stack.field_definitions.popstack.object_types.popstack.path.popendendmoduleDirectiveStrategymodule_functiondefpush(stack,node)directive_defn=stack.schema.directives[node.name]stack.directive_definitions.push(directive_defn)enddefpop(stack,node)stack.directive_definitions.popendendmoduleArgumentStrategymodule_function# Push `argument_defn` onto the stack.# It's possible that `argument_defn` will be nil.# Push it anyways so `pop` has something to pop.defpush(stack,node)ifstack.argument_definitions.lastarg_type=stack.argument_definitions.last.type.unwrapifarg_type.kind.input_object?argument_defn=arg_type.input_fields[node.name]elseargument_defn=nilendelsifstack.directive_definitions.lastargument_defn=stack.directive_definitions.last.arguments[node.name]elsifstack.field_definitions.lastargument_defn=stack.field_definitions.last.arguments[node.name]elseargument_defn=nilendstack.argument_definitions.push(argument_defn)stack.path.push(node.name)enddefpop(stack,node)stack.argument_definitions.popstack.path.popendendmoduleFragmentSpreadStrategymodule_functiondefpush(stack,node)stack.path.push("... #{node.name}")enddefpop(stack,node)stack.path.popendendPUSH_STRATEGIES={GraphQL::Language::Nodes::FragmentDefinition=>FragmentDefinitionStrategy,GraphQL::Language::Nodes::InlineFragment=>InlineFragmentStrategy,GraphQL::Language::Nodes::FragmentSpread=>FragmentSpreadStrategy,GraphQL::Language::Nodes::Argument=>ArgumentStrategy,GraphQL::Language::Nodes::Field=>FieldStrategy,GraphQL::Language::Nodes::Directive=>DirectiveStrategy,GraphQL::Language::Nodes::OperationDefinition=>OperationDefinitionStrategy,}classEnterWithStrategydefinitialize(stack,strategy)@stack=stack@strategy=strategyenddefcall(node,parent)@strategy.push(@stack,node)endendclassLeaveWithStrategydefinitialize(stack,strategy)@stack=stack@strategy=strategyenddefcall(node,parent)@strategy.pop(@stack,node)endendendendend