no_interface_cycles.ex (1794B)
1 defmodule Absinthe.Phase.Schema.Validation.NoInterfaceCyles do 2 @moduledoc false 3 4 use Absinthe.Phase 5 alias Absinthe.Blueprint 6 alias Absinthe.Blueprint.Schema 7 8 def run(blueprint, _opts) do 9 blueprint = check(blueprint) 10 11 {:ok, blueprint} 12 end 13 14 defp check(blueprint) do 15 graph = :digraph.new([:cyclic]) 16 17 try do 18 _ = build_interface_graph(blueprint, graph) 19 20 Blueprint.prewalk(blueprint, &validate_schema(&1, graph)) 21 after 22 :digraph.delete(graph) 23 end 24 end 25 26 defp validate_schema(%Schema.InterfaceTypeDefinition{} = interface, graph) do 27 if cycle = :digraph.get_cycle(graph, interface.identifier) do 28 interface |> put_error(error(interface, cycle)) 29 else 30 interface 31 end 32 end 33 34 defp validate_schema(node, _graph) do 35 node 36 end 37 38 defp build_interface_graph(blueprint, graph) do 39 _ = Blueprint.prewalk(blueprint, &vertex(&1, graph)) 40 end 41 42 defp vertex(%Schema.InterfaceTypeDefinition{} = implementor, graph) do 43 :digraph.add_vertex(graph, implementor.identifier) 44 45 for interface <- implementor.interfaces do 46 edge(implementor, interface, graph) 47 end 48 49 implementor 50 end 51 52 defp vertex(implementor, _graph) do 53 implementor 54 end 55 56 # Add an edge, modeling the relationship between two interfaces 57 defp edge(implementor, interface, graph) do 58 :digraph.add_vertex(graph, interface) 59 60 :digraph.add_edge(graph, implementor.identifier, interface) 61 62 true 63 end 64 65 defp error(type, deps) do 66 %Absinthe.Phase.Error{ 67 message: 68 String.trim(""" 69 Interface Cycle Error 70 71 Interface `#{type.identifier}' forms a cycle via: (#{inspect(deps)}) 72 """), 73 locations: [type.__reference__.location], 74 phase: __MODULE__, 75 extra: type.identifier 76 } 77 end 78 end