Current section
Files
Jump to
Current section
Files
lib/exhal/navigation.ex
defmodule ExHal.Navigation do
alias ExHal.Link
alias ExHal.Error
alias ExHal.Client
@doc """
Follows a link in a HAL document.
Returns `{:ok, %ExHal.Document{...}}` if response is successful;
`{:error, %ExHal.Error{...}}` if not
"""
def follow_link(a_doc, name, opts \\ %{tmpl_vars: %{}, strict: false, headers: []}) do
opts = Map.new(opts)
pick_volunteer? = !(Map.get opts, :strict, false)
tmpl_vars = Map.get(opts, :tmpl_vars, %{})
case figure_link(a_doc, name, pick_volunteer?) do
{:error, e} -> {:error, e}
{:ok, link} -> _follow_link(a_doc.client, link, tmpl_vars, opts)
end
end
@doc """
Follows all links of a particular rel in a HAL document.
Returns `[{:ok, %ExHal.Document{...}}, {:error, %ExHal.Error{...}, ...]`
"""
def follow_links(a_doc, name, opts) when is_map(opts) or is_list(opts) do
follow_links(a_doc, name, fn _name -> [{:error, %Error{reason: "no such link: #{name}"}}] end, opts)
end
def follow_links(a_doc, name, missing_link_handler, opts \\ %{}) do
opts = Map.new(opts)
tmpl_vars = Map.get(opts, :tmpl_vars, %{})
case ExHal.get_links_lazy(a_doc, name, fn -> :missing end) do
:missing -> missing_link_handler.(name)
links -> Enum.map(links, &_follow_link(a_doc.client, &1, tmpl_vars, opts))
end
end
def follow_links(a_doc, name) do
follow_links(a_doc, name, %{})
end
@doc """
Posts data to the named link in a HAL document.
Returns `{:ok, %ExHal.Document{...}}` if response is successful and body is HAL;
`{:error, %ExHal.Error{...}}` if response is an error if not
"""
def post(a_doc, name, body, opts \\ %{tmpl_vars: %{}, strict: true}) do
pick_volunteer? = !(Map.get opts, :strict, true)
tmpl_vars = Map.get(opts, :tmpl_vars, %{})
case figure_link(a_doc, name, pick_volunteer?) do
{:error, e} -> {:error, e}
{:ok, link} -> Client.post(a_doc.client, Link.target_url!(link, tmpl_vars), body, opts)
end
end
@doc """
PUTs data to the named link in a HAL document.
Returns `{:ok, %ExHal.Document{...}}` if response is successful and body is HAL;
`{:error, %ExHal.Error{...}}` if response is an error if not
"""
def put(a_doc, name, body, opts \\ %{tmpl_vars: %{}, strict: true}) do
pick_volunteer? = !(Map.get opts, :strict, true)
tmpl_vars = Map.get(opts, :tmpl_vars, %{})
case figure_link(a_doc, name, pick_volunteer?) do
{:error, e} -> {:error, e}
{:ok, link} -> Client.put(a_doc.client, Link.target_url!(link, tmpl_vars), body, opts)
end
end
@doc """
PATCHs data to the named link in a HAL document.
Returns `{:ok, %ExHal.Document{...}}` if response is successful and body is HAL;
`{:error, %ExHal.Error{...}}` if response is an error if not
"""
def patch(a_doc, name, body, opts \\ %{tmpl_vars: %{}, strict: true}) do
pick_volunteer? = !(Map.get opts, :strict, true)
tmpl_vars = Map.get(opts, :tmpl_vars, %{})
case figure_link(a_doc, name, pick_volunteer?) do
{:error, e} -> {:error, e}
{:ok, link} -> Client.patch(a_doc.client, Link.target_url!(link, tmpl_vars), body, opts)
end
end
@doc """
Returns `{:ok, url}` if a matching link is found or `{:error, %ExHal.Error{...}}` if not.
* a_doc - `ExHal.Document` in which to search for links
* name - the rel of the link of interest
* opts
* `:tmpl_vars` - `Map` of variables with which to expand any templates found. Default: `%{}`
* `:strict` - true if the existence of multiple matching links should cause a failure. Default: `false`
"""
def link_target(a_doc, name, opts \\ %{}) do
opts = Map.new(opts)
tmpl_vars = Map.get(opts, :tmpl_vars, %{})
strict? = Map.get(opts, :strict, false)
case figure_link(a_doc, name, !strict?) do
{:ok, link} -> find_link_target(link, tmpl_vars)
(r = _) -> r
end
end
@doc """
Returns `{:ok, [url1, ...]}` if a matching link is found or `{:error, %ExHal.Error{...}}` if not.
* a_doc - `ExHal.Document` in which to search for links
* name - the rel of the link of interest
* opts
* `:tmpl_vars` - `Map` of variables with which to expand any templates found. Default: `%{}`
"""
def link_targets(a_doc, name, opts \\ %{}) do
opts = Map.new(opts)
tmpl_vars = Map.get(opts, :tmpl_vars, %{})
case ExHal.get_links_lazy(a_doc, name, fn -> :missing end) do
:missing -> {:error, %Error{reason: "no such link: #{name}"}}
links ->
{:ok, links
|> Enum.map(fn(link) -> {:ok, target} = find_link_target(link, tmpl_vars); target end)}
end
end
def link_targets_lazy(a_doc, name, opts \\ %{}, fun) do
case link_targets(a_doc, name, opts) do
{:ok, links} -> links
{:error, _} -> fun.()
end
end
def link_target_lazy(a_doc, name, opts \\ %{}, fun) do
case link_target(a_doc, name, opts) do
{:ok, target} -> target
{:error, _} -> fun.()
end
end
# privates
defp find_link_target(link, tmpl_vars) do
case Link.target_url(link, tmpl_vars) do
:error -> {:error, %Error{reason: "link has no href member"}}
successful -> successful
end
end
defp figure_link(a_doc, name, pick_volunteer?) do
case ExHal.get_links_lazy(a_doc, name, fn -> :missing end) do
:missing -> {:error, %Error{reason: "no such link: #{name}"}}
(ls = [_|[_|_]]) -> if pick_volunteer? do
{:ok, List.first(ls)}
else
{:error, %Error{reason: "multiple choices"}}
end
[l] -> {:ok, l}
end
end
defp _follow_link(client, link, tmpl_vars, opts) do
cond do
Link.embedded?(link) ->
{:ok, link.target}
:else ->
Client.get(client, Link.target_url!(link, tmpl_vars), opts)
end
end
end