module GraphQL::StaticValidation::VariablesAreUsedAndDefined
def create_errors(node_variables)
Determine all the error messages,
def create_errors(node_variables) # Declared but not used: node_variables .select { |name, usage| usage.declared? && !usage.used? } .each { |var_name, usage| declared_by_error_name = usage.declared_by.name || "anonymous #{usage.declared_by.operation_type}" add_error(GraphQL::StaticValidation::VariablesAreUsedAndDefinedError.new( "Variable $#{var_name} is declared by #{declared_by_error_name} but not used", nodes: usage.declared_by, path: usage.path, name: var_name, error_type: VariablesAreUsedAndDefinedError::VIOLATIONS[:VARIABLE_NOT_USED] )) } # Used but not declared: node_variables .select { |name, usage| usage.used? && !usage.declared? } .each { |var_name, usage| used_by_error_name = usage.used_by.name || "anonymous #{usage.used_by.operation_type}" add_error(GraphQL::StaticValidation::VariablesAreUsedAndDefinedError.new( "Variable $#{var_name} is used by #{used_by_error_name} but not declared", nodes: usage.ast_node, path: usage.path, name: var_name, error_type: VariablesAreUsedAndDefinedError::VIOLATIONS[:VARIABLE_NOT_DEFINED] )) } end
def follow_spreads(node, parent_variables, spreads_for_context, fragment_definitions, visited_fragments)
Use those fragments to update {VariableUsage}s in `parent_variables`.
Follow spreads in `node`, looking them up from `spreads_for_context` and finding their match in `fragment_definitions`.
def follow_spreads(node, parent_variables, spreads_for_context, fragment_definitions, visited_fragments) spreads = spreads_for_context[node] - visited_fragments spreads.each do |spread_name| def_node = nil variables = nil # Implement `.find` by hand to avoid Ruby's internal allocations fragment_definitions.each do |frag_def_node, vars| if frag_def_node.name == spread_name def_node = frag_def_node variables = vars break end end next if !def_node visited_fragments << spread_name variables.each do |name, child_usage| parent_usage = parent_variables[name] if child_usage.used? parent_usage.ast_node = child_usage.ast_node parent_usage.used_by = child_usage.used_by parent_usage.path = child_usage.path end end follow_spreads(def_node, parent_variables, spreads_for_context, fragment_definitions, visited_fragments) end end
def initialize(*)
def initialize(*) super @variable_usages_for_context = Hash.new {|hash, key| hash[key] = Hash.new {|h, k| h[k] = VariableUsage.new } } @spreads_for_context = Hash.new {|hash, key| hash[key] = [] } @variable_context_stack = [] end
def on_document(node, parent)
def on_document(node, parent) super fragment_definitions = @variable_usages_for_context.select { |key, value| key.is_a?(GraphQL::Language::Nodes::FragmentDefinition) } operation_definitions = @variable_usages_for_context.select { |key, value| key.is_a?(GraphQL::Language::Nodes::OperationDefinition) } operation_definitions.each do |node, node_variables| follow_spreads(node, node_variables, @spreads_for_context, fragment_definitions, []) create_errors(node_variables) end end
def on_fragment_definition(node, parent)
def on_fragment_definition(node, parent) # initialize the hash of vars for this context: @variable_usages_for_context[node] @variable_context_stack.push(node) super @variable_context_stack.pop end
def on_fragment_spread(node, parent)
- find the context on the stack
For FragmentSpreads:
def on_fragment_spread(node, parent) variable_context = @variable_context_stack.last @spreads_for_context[variable_context] << node.name super end
def on_operation_definition(node, parent)
def on_operation_definition(node, parent) # initialize the hash of vars for this context: @variable_usages_for_context[node] @variable_context_stack.push(node) # mark variables as defined: var_hash = @variable_usages_for_context[node] node.variables.each { |var| var_usage = var_hash[var.name] var_usage.declared_by = node var_usage.path = context.path } super @variable_context_stack.pop end
def on_variable_identifier(node, parent)
- mark the variable as used
For VariableIdentifiers:
def on_variable_identifier(node, parent) usage_context = @variable_context_stack.last declared_variables = @variable_usages_for_context[usage_context] usage = declared_variables[node.name] usage.used_by = usage_context usage.ast_node = node usage.path = context.path super end