no_undefined_variables.ex (1815B)
1 defmodule Absinthe.Phase.Document.Validation.NoUndefinedVariables do 2 @moduledoc false 3 4 # Validates document to ensure that the only variables that are used in a 5 # document are defined on the operation. 6 7 alias Absinthe.{Blueprint, Phase} 8 9 use Absinthe.Phase 10 use Absinthe.Phase.Validation 11 12 @doc """ 13 Run the validation. 14 """ 15 @spec run(Blueprint.t(), Keyword.t()) :: Phase.result_t() 16 def run(input, _options \\ []) do 17 result = Blueprint.prewalk(input, &handle_node(&1, input.operations)) 18 {:ok, result} 19 end 20 21 def handle_node(%Blueprint.Input.Variable{} = node, operations) do 22 errors = 23 for op <- operations do 24 for var <- op.variable_uses, var.name == node.name do 25 if Enum.find(op.variable_definitions, &(&1.name == var.name)) do 26 [] 27 else 28 [error(node, op)] 29 end 30 end 31 end 32 |> List.flatten() 33 34 node = %{node | errors: errors ++ node.errors} 35 36 case errors do 37 [] -> 38 node 39 40 _ -> 41 flag_invalid(node, :no_definition) 42 end 43 end 44 45 def handle_node(node, _) do 46 node 47 end 48 49 # Generate the error for the node 50 @spec error(Blueprint.Input.Variable.t(), Blueprint.Document.Operation.t()) :: Phase.Error.t() 51 defp error(node, operation) do 52 %Phase.Error{ 53 phase: __MODULE__, 54 message: error_message(node.name, operation.name), 55 locations: [node.source_location, operation.source_location] 56 } 57 end 58 59 @doc """ 60 Generate an error message for an undefined variable. 61 """ 62 @spec error_message(String.t(), nil | String.t()) :: String.t() 63 def error_message(name, nil) do 64 ~s(Variable "#{name}" is not defined.) 65 end 66 67 def error_message(name, operation_name) do 68 ~s(Variable "#{name}" is not defined by operation "#{operation_name}".) 69 end 70 end