repeatable_directives.ex (2027B)
1 defmodule Absinthe.Phase.Document.Validation.RepeatableDirectives do 2 @moduledoc false 3 4 alias Absinthe.{Blueprint, Phase} 5 6 use Absinthe.Phase 7 use Absinthe.Phase.Validation 8 9 @doc """ 10 Run the validation. 11 """ 12 @spec run(Blueprint.t(), Keyword.t()) :: Phase.result_t() 13 def run(input, _options \\ []) do 14 result = Blueprint.postwalk(input, &handle_node/1) 15 {:ok, result} 16 end 17 18 defp handle_node(%Blueprint.Directive{} = node) do 19 node 20 end 21 22 defp handle_node(%{directives: []} = node) do 23 node 24 end 25 26 defp handle_node(%{directives: _} = node) do 27 node 28 |> check_directives 29 |> inherit_invalid(node.directives, :bad_directive) 30 end 31 32 defp handle_node(node) do 33 node 34 end 35 36 defp check_directives(node) do 37 directives = 38 for directive <- node.directives do 39 case directive do 40 %{schema_node: nil} -> 41 directive 42 43 %{schema_node: %{repeatable: true}} -> 44 directive 45 46 directive -> 47 check_duplicates( 48 directive, 49 Enum.filter( 50 node.directives, 51 &compare_directive_schema_node(directive.schema_node, &1.schema_node) 52 ) 53 ) 54 end 55 end 56 57 %{node | directives: directives} 58 end 59 60 defp compare_directive_schema_node(_, nil), do: false 61 62 defp compare_directive_schema_node(%{identifier: identifier}, %{identifier: identifier}), 63 do: true 64 65 defp compare_directive_schema_node(_, _), do: false 66 67 # Generate the error for the node 68 @spec error_repeated(Blueprint.node_t()) :: Phase.Error.t() 69 defp error_repeated(node) do 70 %Phase.Error{ 71 phase: __MODULE__, 72 message: "Directive `#{node.name}' cannot be applied repeatedly.", 73 locations: [node.source_location] 74 } 75 end 76 77 defp check_duplicates(directive, [_single]) do 78 directive 79 end 80 81 defp check_duplicates(directive, _multiple) do 82 directive 83 |> flag_invalid(:duplicate_directive) 84 |> put_error(error_repeated(directive)) 85 end 86 end