Current section
Files
Jump to
Current section
Files
lib/jsonapi_kit/query/include_builder.ex
defmodule Solage.QueryIncludeBuilder do
@moduledoc """
Parse the `include` param to return a sane representation of all the includes.
The representation looks like this and is meant to be understood by the Ecto library.
```
[:model1, {:model1, :nested_assocation}, :model2]
```
It can also reject unallowed includes and add always included params.
If the `allowed_includes` options is not passed, that means that every include is allowed.
## Examples
```
# With allowed includes option
iex> Solage.QueryIncludeBuilder.build("user,post", %Solage.QueryConfig{options: [allowed_includes: ~w(user)]})
[:user]
# With always includes option
iex> Solage.QueryIncludeBuilder.build("user", %Solage.QueryConfig{options: [always_includes: ~w(post)]})
[:user, :post]
# Nested results
iex> Solage.QueryIncludeBuilder.build("user,user.post", %Solage.QueryConfig{})
[:user, {:user, :post}]
```
"""
@behaviour Solage.QueryBuilder
@include_separator ","
@nested_separator "."
def build(nil, _opts), do: []
def build("", _opts), do: []
def build(param, config) do
param
|> String.split(@include_separator)
|> reject_unallowed_includes(config)
|> add_always_includes(config)
|> Enum.map(&handle_nested_include/1)
|> List.flatten
end
defp handle_nested_include(key) do
[item | path] = key
|> String.split(@nested_separator)
|> Enum.map(&String.to_existing_atom/1)
|> Enum.reverse
put_as_tree([], path, item)
end
defp put_as_tree(_acc, [], val), do: [val]
defp put_as_tree(acc, [head | tail], val) do
build_tree(Keyword.put(acc, head, val), tail)
end
defp build_tree(acc, []), do: acc
defp build_tree(acc, [head | tail]) do
build_tree(Keyword.put([], head, acc), tail)
end
defp reject_unallowed_includes(items, config) do
case config.options[:allowed_includes] do
nil -> items
allowed_includes ->
items
|> Enum.reject(fn(item) -> not Enum.member?(allowed_includes, item) end)
end
end
defp add_always_includes(items, config) do
case config.options[:always_includes] do
nil -> items
always_includes -> items ++ always_includes
end
end
end