Current section

Files

Jump to
neuron lib neuron fragment.ex
Raw

lib/neuron/fragment.ex

defmodule Neuron.Fragment do
alias Neuron.Store
@moduledoc """
This module can be used to register fragments to be used in your GraphQL queries
"""
@doc ~S"""
registers a fragment that will automatically be added to future mutations and queries that require it
## Example
iex> Neuron.Fragment.register("
...> NameParts on Person {
...> firstName
...> lastName
...> }
...> ")
:ok
"""
@spec register(query_string :: String.t()) :: :ok
def register(query_string), do: register(:global, query_string)
@spec register(context :: :global | :process, query_string :: String.t()) :: :ok
def register(context, query_string) do
fragment = query_string |> process_fragment_string
stored_fragments = Store.get(context, :fragments, [])
Store.set(context, :fragments, [fragment | stored_fragments])
end
defp process_fragment_string(query_string) do
fragment_name = Regex.run(~r/^(?:\W*)(\w+)/, query_string) |> List.last() |> String.to_atom()
fragment = query_string |> construct_fragment_string
dependencies = fragment |> find_in_query()
{fragment_name, {fragment, dependencies}}
end
@doc false
def insert_into_query(query_string) do
stored_fragments = Store.get(:global, :fragments, []) ++ Store.get(:process, :fragments, [])
fragments_to_add =
query_string
|> find_in_query
|> Enum.map(&List.keyfind(stored_fragments, &1, 0, &1))
fragments_to_add =
fragments_to_add
|> Enum.reject(&is_atom/1)
|> Enum.reduce(fragments_to_add, &load_missing_fragments(&1, stored_fragments, &2))
missing_fragments = fragments_to_add |> Enum.filter(&is_atom/1) |> Enum.map(&Atom.to_string/1)
if Enum.any?(missing_fragments),
do: raise(Neuron.MissingFragmentsError, missing_fragments: missing_fragments)
fragments_to_add
|> Enum.map(&elem(&1, 1))
|> Enum.map(&elem(&1, 0))
|> Enum.reduce(query_string, &"#{&1} \n #{&2}")
end
defp find_in_query(query_string) do
Regex.scan(~r/(?<=\.\.\.)\w+(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/, query_string)
|> List.flatten()
|> Enum.map(&String.to_atom/1)
|> Enum.uniq()
end
defp construct_fragment_string(fragment_string) do
"fragment #{fragment_string}"
end
defp load_missing_fragments({_, {_, []}}, _available, loaded), do: loaded
defp load_missing_fragments({_, {_, [name]}}, available, loaded),
do: load_fragment(name, available, loaded)
defp load_missing_fragments({_, {_, extra}}, available, loaded) do
extra |> Enum.reduce(loaded, &load_fragment(&1, available, &2))
end
defp load_fragment(name, available, loaded) do
loaded
|> Keyword.get(name)
|> case do
nil ->
List.keyfind(available, name, 0, name)
|> case do
{^name, _} = fragment ->
load_missing_fragments(fragment, available, [fragment | loaded])
^name ->
[name | loaded]
end
_ ->
loaded
end
end
end