Packages
absinthe
1.1.2
1.11.0
1.10.2
1.10.1
1.10.0
1.9.1
1.9.0
1.8.0
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.8
1.6.7
retired
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.5
1.5.0-rc.4
1.5.0-rc.3
1.5.0-rc.2
1.5.0-rc.1
1.5.0-rc.0
1.5.0-beta.2
1.5.0-beta.1
1.5.0-beta.0
1.5.0-alpha.4
1.5.0-alpha.3
1.5.0-alpha.2
1.5.0-alpha.1
1.5.0-alpha.0
1.4.16
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
retired
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.3
1.4.0-rc.2
1.4.0-rc.1
1.4.0-rc.0
1.4.0-beta.5
1.4.0-beta.4
1.4.0-beta.3
1.4.0-beta.2
1.4.0-beta.1
1.3.2
1.3.1
1.3.0
1.3.0-rc.0
1.3.0-beta.2
1.3.0-beta.1
1.3.0-beta.0
1.2.6
1.2.5
1.2.4
1.2.3
1.2.2
1.2.1
1.2.0
1.2.0-rc.0
1.2.0-beta.0
1.2.0-alpha0
1.2.0-alpha.2
1.2.0-alpha.1
1.1.11
1.1.10
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0
0.5.2
0.5.1
0.5.0
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.2.3
0.2.2
0.2.1
0.1.0
GraphQL for Elixir
Current section
Files
Jump to
Current section
Files
lib/absinthe/validation/prevent_circular_fragments.ex
defmodule Absinthe.Validation.PreventCircularFragments do
alias Absinthe.{Language, Traversal}
@moduledoc false
def validate(doc, {_, errors}) do
doc.definitions
|> Enum.filter(fn
%Language.Fragment{} -> true
_ -> false
end)
|> check(errors)
end
# The overall approach here is to create a digraph with an `acyclic`
# constraint. Then we just add the fragments as vectors, and fragment
# spreads are used to create edges. If at any point :digraph returns
# an error we have a cycle! Thank you :digraph for doing the hard part
# :)
# NOTE: `:digraph` is MUTABLE, as it's backed by `:ets`
def check(fragments, errors) do
graph = :digraph.new([:acyclic])
result = fragments
|> Enum.reduce({errors, graph}, &check_fragment/2)
|> case do
{[], _} -> {:ok, []}
{errors, _} -> {:error, errors}
end
# The table will get deleted when the process exits, but we might
# as well clean up for ourselves explicitly.
:digraph.delete(graph)
result
end
def check([], errors, _), do: errors
def check_fragment(fragment, {errors, graph}) do
_ = :digraph.add_vertex(graph, fragment.name)
Traversal.reduce(fragment, :unused, {errors, graph}, fn
%Language.FragmentSpread{} = spread, traversal, {errors, graph} ->
_ = :digraph.add_vertex(graph, spread.name)
case :digraph.add_edge(graph, fragment.name, spread.name) do
{:error, {:bad_edge, path}} ->
# All just error generation logic
deps = [fragment.name | path]
|> Enum.map(&"`#{&1}'")
|> Enum.join(" => ")
msg = """
Fragment Cycle Error
Fragment `#{fragment.name}' forms a cycle via: (#{deps})
"""
error = %{
message: String.strip(msg),
locations: [%{line: spread.loc.start_line, column: 0}]
}
{:ok, {[error | errors], graph}, traversal}
_ ->
{:ok, {errors, graph}, traversal}
end
_, traversal, acc ->
{:ok, acc, traversal}
end)
end
end