result.ex (2187B)
1 defmodule Absinthe.Phase.Document.Complexity.Result do 2 @moduledoc false 3 4 # Collects complexity errors into the result. 5 6 alias Absinthe.{Blueprint, Phase} 7 8 use Absinthe.Phase 9 10 @doc """ 11 Run the validation. 12 """ 13 @spec run(Blueprint.t(), Keyword.t()) :: Phase.result_t() 14 def run(input, options \\ []) do 15 max = Keyword.get(options, :max_complexity, :infinity) 16 operation = Blueprint.current_operation(input) 17 fun = &handle_node(&1, max, &2) 18 {operation, errors} = Blueprint.prewalk(operation, [], fun) 19 20 blueprint = Blueprint.update_current(input, fn _ -> operation end) 21 blueprint = put_in(blueprint.execution.validation_errors, errors) 22 23 case {errors, Map.new(options)} do 24 {[], _} -> 25 {:ok, blueprint} 26 27 {_errors, %{jump_phases: true, result_phase: abort_phase}} -> 28 {:jump, blueprint, abort_phase} 29 30 _ -> 31 {:error, blueprint} 32 end 33 end 34 35 defp handle_node(%{complexity: complexity} = node, max, errors) 36 when is_integer(complexity) and complexity > max do 37 error = error(node, complexity, max) 38 39 node = 40 node 41 |> flag_invalid(:too_complex) 42 |> put_error(error) 43 44 {node, [error | errors]} 45 end 46 47 defp handle_node(%{complexity: _} = node, _, errors) do 48 {:halt, node, errors} 49 end 50 51 defp handle_node(node, _, errors) do 52 {node, errors} 53 end 54 55 defp error(%{source_location: location} = node, complexity, max) do 56 %Phase.Error{ 57 phase: __MODULE__, 58 message: error_message(node, complexity, max), 59 locations: [location] 60 } 61 end 62 63 def error_message(node, complexity, max) do 64 "#{describe_node(node)} is too complex: complexity is #{complexity} and maximum is #{max}" 65 end 66 67 defp describe_node(%Blueprint.Document.Operation{name: nil}) do 68 "Operation" 69 end 70 71 defp describe_node(%Blueprint.Document.Operation{name: name}) do 72 "Operation #{name}" 73 end 74 75 defp describe_node(%Blueprint.Document.Field{name: name}) do 76 "Field #{name}" 77 end 78 79 defp describe_node(%Blueprint.Document.Fragment.Spread{name: name}) do 80 "Spread #{name}" 81 end 82 83 defp describe_node(%Blueprint.Document.Fragment.Inline{}) do 84 "Inline Fragment" 85 end 86 end