Packages
ash_json_api
1.4.44
1.7.1
1.7.0
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0-rc.2
1.6.0-rc.1
1.6.0-rc.0
1.5.1
1.5.0
1.4.45
1.4.44
1.4.43
1.4.42
1.4.41
1.4.40
1.4.39
1.4.38
1.4.37
1.4.36
1.4.35
1.4.34
1.4.33
1.4.32
1.4.31
1.4.30
1.4.29
1.4.28
1.4.27
1.4.26
1.4.25
1.4.24
1.4.23
1.4.22
1.4.21
1.4.20
1.4.19
1.4.18
1.4.17
1.4.16
1.4.15
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
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.3.8
1.3.7
1.3.6
1.3.5
1.3.4
retired
1.3.3
1.3.2
1.3.1
1.3.0
1.2.2
1.2.1
1.2.0
1.1.2
1.1.1
1.1.0
1.0.0
1.0.0-rc.6
1.0.0-rc.5
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
1.0.0-rc.0
0.34.2
0.34.1
0.34.0
0.33.1
0.33.0
0.32.1
0.32.0
0.31.3
0.31.2
retired
0.31.1
0.31.0
0.30.1
0.30.0-rc.4
0.30.0-rc.3
0.30.0-rc.2
0.30.0-rc.1
0.30.0-rc.0
0.29.1
0.29.0
0.28.6
0.28.5
0.28.4
0.28.3
0.28.2
0.28.1
0.28.0
0.27.6
0.27.5
0.27.1
0.27.0
0.25.0
0.24.4
0.24.2
0.24.1
0.24.0
0.23.0
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.0
0.11.1
0.10.0
0.9.0
0.8.0
0.6.0
0.5.0
0.4.0
0.3.0
0.2.4
0.2.3
0.2.1
0.2.0
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
The JSON:API extension for the Ash Framework.
Current section
Files
Jump to
Current section
Files
lib/ash_json_api/controllers/helpers.ex
defmodule AshJsonApi.Controllers.Helpers do
@moduledoc false
require Logger
alias AshJsonApi.Controllers.Response
alias AshJsonApi.{Error, Request}
alias AshJsonApi.Includes.Includer
require Ash.Query
def render_or_render_errors(request, conn, function) do
chain(request, fn request ->
conn =
if request.route.modify_conn do
request.route.modify_conn.(
conn,
AshJsonApi.Controllers.Helpers.subject(request),
request.assigns.result,
request
)
else
conn
end
Request.assign(request, :conn, conn)
end)
|> chain(fn request -> function.(request.assigns[:conn], request) end,
fallback: fn request ->
Response.render_errors(request.assigns[:conn] || conn, request)
end
)
end
def fetch_includes(request) do
chain(request, fn request ->
{new_result, includes} = Includer.get_includes(request.assigns.result, request)
request
|> Request.assign(:result, new_result)
|> Request.assign(:includes, includes)
end)
end
def fetch_records(request) do
chain(request, fn request ->
if request.action.type == :action do
action_input =
request.resource
|> Ash.ActionInput.new()
|> Ash.ActionInput.set_context(request.context || %{})
|> Ash.ActionInput.for_action(
request.action.name,
request.arguments,
Request.opts(request)
)
request = Request.assign(request, :action_input, action_input)
with {:ok, result} <- Ash.run_action(action_input),
{:ok, result} <- load_action_data(result, request) do
Request.assign(request, :result, result)
else
{:error, error} ->
Request.add_error(request, error, :read)
end
else
filter =
case request.filter do
empty when empty in [%{}, nil] ->
nil
other ->
other
end
query =
request.resource
|> Ash.Query.load(request.includes_keyword)
|> Ash.Query.set_context(request.context)
|> Ash.Query.do_filter(filter)
|> Ash.Query.sort(request.sort)
|> Ash.Query.load(fields(request, request.resource))
|> Ash.Query.for_read(request.action.name, request.arguments, Request.opts(request))
request = Request.assign(request, :query, query)
query
|> Ash.read(Request.opts(request))
|> case do
{:ok, result} ->
Request.assign(request, :result, result)
{:error, error} ->
Request.add_error(request, error, :read)
end
end
end)
end
defp load_action_data(result, request) do
# If the resouce has no primary read action, we can skip loading
if is_nil(Ash.Resource.Info.primary_action(request.resource, :read)) do
{:ok, result}
else
with {:ok, result} <-
Ash.load(
result,
fields(
request,
request.resource
),
Request.load_opts(request, reuse_values?: true)
),
{:ok, result} <-
Ash.load(result, request.includes_keyword, Request.load_opts(request)) do
{:ok, result}
else
{:error, error} ->
{:error, error}
end
end
end
def run_action(request) do
chain(request, fn request ->
case path_args_and_filter(
request.path_params,
request.resource,
request.action,
request.route
) do
{:ok, route_params, _filter} ->
action_input =
request.resource
|> Ash.ActionInput.new()
|> Ash.ActionInput.set_context(request.context || %{})
|> Ash.ActionInput.for_action(
request.action.name,
Map.merge(request.arguments, route_params),
Request.opts(request)
)
request = Request.assign(request, :action_input, action_input)
case Ash.run_action(action_input) do
:ok ->
Request.assign(request, :result, :ok)
{:ok, result} ->
Request.assign(request, :result, result)
{:error, error} ->
Request.add_error(request, error, :action)
end
{:error, error} ->
Request.add_error(request, error, :action)
end
end)
end
def fetch_metadata(request) do
chain(request, fn request ->
if is_function(request.route.metadata, 3) do
subject = subject(request)
metadata = request.route.metadata.(subject, request.assigns.result, request)
Request.assign(request, :metadata, metadata)
else
Request.assign(request, :metadata, %{})
end
end)
end
def subject(request) do
Map.get(
request.assigns,
:query,
Map.get(request.assigns, :changeset, Map.get(request.assigns, :action_input))
)
end
def create_record(request) do
chain(request, fn %{resource: resource} = request ->
cond do
request.action.type == :read ->
case path_args_and_filter(
request.path_params,
request.resource,
request.action,
request.route
) do
{:ok, route_params, _filter} ->
query =
request.resource
|> Ash.Query.load(
fields(request, request.resource) ++ (request.includes_keyword || [])
)
|> Ash.Query.set_context(request.context || %{})
|> Ash.Query.for_read(
request.action.name,
Map.merge(Map.merge(request.attributes, request.arguments), route_params),
Request.opts(request)
)
request = Request.assign(request, :query, query)
case Ash.read(query) do
{:ok, %resource{} = result} when resource == request.resource ->
Request.assign(request, :result, result)
{:ok, [result]} ->
Request.assign(request, :result, result)
{:ok, %page{results: [result]}} when page in [Ash.Page.Keyset, Ash.Page.Offset] ->
Request.assign(request, :result, result)
{:ok, []} ->
Request.add_error(request, Ash.Error.Query.NotFound.exception(), :create)
{:ok, %page{results: []}} when page in [Ash.Page.Keyset, Ash.Page.Offset] ->
Request.add_error(request, Ash.Error.Query.NotFound.exception(), :create)
{:ok, [result | _]} ->
Logger.warning(
"Got multiple results for #{inspect(request.resource)}.#{request.action} in `:post` handler. Expected zero or one. Extra results are being ignored."
)
Request.assign(request, :result, result)
{:ok, %page{results: [result | _]}}
when page in [Ash.Page.Keyset, Ash.Page.Offset] ->
Request.assign(request, :result, result)
Logger.warning(
"Got multiple results for #{inspect(request.resource)}.#{request.action} in `:post` handler. Expected zero or one. Extra results are being ignored."
)
{:error, error} ->
Request.add_error(request, error, :create)
end
{:error, error} ->
Request.add_error(request, error, :create)
end
request.action.type == :action ->
case path_args_and_filter(
request.path_params,
request.resource,
request.action,
request.route
) do
{:ok, route_params, _filter} ->
action_input =
request.resource
|> Ash.ActionInput.new()
|> Ash.ActionInput.set_context(request.context || %{})
|> Ash.ActionInput.for_action(
request.action.name,
Map.merge(Map.merge(request.attributes, request.arguments), route_params),
Request.opts(request)
)
request = Request.assign(request, :action_input, action_input)
with {:ok, result} <- Ash.run_action(action_input),
{:ok, result} <-
Ash.load(
result,
fields(request, request.resource),
Request.load_opts(request, reuse_values?: true)
),
{:ok, result} <-
Ash.load(
result,
request.includes_keyword || [],
Request.load_opts(request)
) do
Request.assign(request, :result, result)
else
{:error, error} ->
Request.add_error(request, error, :create)
end
{:error, error} ->
Request.add_error(request, error, :create)
end
true ->
opts =
if request.route.upsert? do
if request.route.upsert_identity do
[
upsert?: true,
upsert_identity: request.route.upsert_identity
]
else
[
upsert?: true
]
end
else
[]
end
case path_args_and_filter(
request.path_params,
request.resource,
request.action,
request.route
) do
{:ok, route_params, _filter} ->
changeset =
resource
|> Ash.Changeset.new()
|> Ash.Changeset.set_context(request.context)
|> Ash.Changeset.for_create(
request.action.name,
Map.merge(Map.merge(request.attributes, request.arguments), route_params),
Request.opts(request)
)
|> Ash.Changeset.load(
fields(request, request.resource) ++ (request.includes_keyword || [])
)
request = Request.assign(request, :changeset, changeset)
changeset
|> Ash.create(Request.opts(request, opts))
|> case do
{:ok, record} ->
Request.assign(request, :result, record)
{:error, error} ->
Request.add_error(request, error, :create)
end
{:error, error} ->
Request.add_error(request, error, :create)
end
end
end)
end
def update_record(request, attributes \\ &Map.merge(&1.attributes, &1.arguments)) do
chain(request, fn request ->
if request.action.type == :action do
case path_args_and_filter(
request.path_params,
request.resource,
request.action,
request.route
) do
{:ok, route_params, _filter} ->
action_input =
request.resource
|> Ash.ActionInput.new()
|> Ash.ActionInput.set_context(request.context || %{})
|> Ash.ActionInput.for_action(
request.action.name,
Map.merge(attributes.(request), route_params),
Request.opts(request)
)
request = Request.assign(request, :action_input, action_input)
with {:ok, result} <- Ash.run_action(action_input),
{:ok, result} <-
Ash.load(
result,
fields(request, request.resource),
Request.load_opts(request, reuse_values?: true)
),
{:ok, result} <-
Ash.load(
result,
request.includes_keyword || [],
Request.load_opts(request)
) do
Request.assign(request, :result, result)
else
{:error, error} ->
Request.add_error(request, error, :create)
end
{:error, error} ->
Request.add_error(request, error, :create)
end
else
request
|> fetch_query()
|> case do
{:error, error} ->
Request.add_error(request, error, :fetch_from_path)
{:ok, filter, query} ->
request = Request.assign(request, :query, query)
query
|> Ash.bulk_update(
request.action.name,
attributes.(request),
Request.opts(request,
authorize_changeset_with: Request.authorize_bulk_with(query.resource),
return_errors?: true,
notify?: true,
strategy: [:atomic, :stream, :atomic_batches],
allow_stream_with: :full_read,
return_records?: true,
context: request.context || %{},
load: fields(request, request.resource) ++ (request.includes_keyword || [])
)
)
|> case do
%Ash.BulkResult{status: :success, records: [result | _]} ->
request
|> Request.assign(:result, result)
|> Request.assign(:record_from_path, result)
%Ash.BulkResult{status: :success, records: []} ->
error = Error.NotFound.exception(filter: filter, resource: request.resource)
Request.add_error(request, error, :fetch_from_path)
%Ash.BulkResult{status: :error, errors: errors} ->
Request.add_error(request, errors, :update)
end
end
end
end)
end
def add_to_relationship(request, relationship_name) do
chain(request, fn %{assigns: %{result: result}} ->
action = Ash.Resource.Info.primary_action!(request.resource, :update).name
result
|> Ash.Changeset.new()
|> Ash.Changeset.manage_relationship(relationship_name, request.resource_identifiers,
type: :append
)
|> Ash.Changeset.set_context(request.context)
|> Ash.Changeset.for_update(action, %{}, Request.opts(request))
|> Ash.Changeset.load(fields(request, request.resource))
|> Ash.update(Request.opts(request))
|> case do
{:ok, updated} ->
request
|> Request.assign(:result, Map.get(updated, relationship_name))
{:error, error} ->
Request.add_error(request, error, :add_to_relationship)
end
end)
end
def replace_relationship(request, relationship_name) do
chain(request, fn %{assigns: %{result: result}} ->
action = Ash.Resource.Info.primary_action!(request.resource, :update).name
result
|> Ash.Changeset.new()
|> Ash.Changeset.manage_relationship(relationship_name, request.resource_identifiers,
type: :append_and_remove
)
|> Ash.Changeset.set_context(request.context)
|> Ash.Changeset.for_update(action, %{}, Request.opts(request))
|> Ash.Changeset.load(fields(request, request.resource))
|> Ash.update(Request.opts(request))
|> case do
{:ok, updated} ->
request
|> Request.assign(:result, Map.get(updated, relationship_name))
{:error, error} ->
Request.add_error(request, error, :replace_relationship)
end
end)
end
def delete_from_relationship(request, relationship_name) do
chain(request, fn %{assigns: %{result: result}} ->
action = Ash.Resource.Info.primary_action!(request.resource, :update).name
result
|> Ash.Changeset.new()
|> Ash.Changeset.manage_relationship(relationship_name, request.resource_identifiers,
type: :remove
)
|> Ash.Changeset.set_context(request.context)
|> Ash.Changeset.for_update(action, Request.opts(request))
|> Ash.update(Request.opts(request))
|> case do
{:ok, record} ->
record
|> Ash.load(fields(request, request.resource), Request.load_opts(request))
|> case do
{:ok, updated} ->
request
|> Request.assign(:result, Map.get(updated, relationship_name))
{:error, error} ->
Request.add_error(request, error, :delete_from_relationship)
end
{:error, error} ->
{:error, error}
end
end)
end
def destroy_record(request) do
chain(request, fn request ->
if request.action.type == :action do
case path_args_and_filter(
request.path_params,
request.resource,
request.action,
request.route
) do
{:ok, route_params, _filter} ->
action_input =
request.resource
|> Ash.ActionInput.new()
|> Ash.ActionInput.set_context(request.context || %{})
|> Ash.ActionInput.for_action(
request.action.name,
route_params,
Request.opts(request)
)
request = Request.assign(request, :action_input, action_input)
with {:ok, result} <- Ash.run_action(action_input),
{:ok, result} <-
Ash.load(
result,
fields(request, request.resource),
Request.load_opts(request, reuse_values?: true)
),
{:ok, result} <-
Ash.load(
result,
request.includes_keyword || [],
Request.load_opts(request)
) do
Request.assign(request, :result, result)
else
{:error, error} ->
Request.add_error(request, error, :create)
end
{:error, error} ->
Request.add_error(request, error, :create)
end
else
request
|> fetch_query()
|> case do
{:error, error} ->
Request.add_error(request, error, :fetch_from_path)
{:ok, filter, query} ->
request = Request.assign(request, :query, query)
query
|> Ash.bulk_destroy(
request.action.name,
%{},
Request.opts(request,
return_errors?: true,
authorize_changeset_with: Request.authorize_bulk_with(query.resource),
notify?: true,
strategy: [:atomic, :stream, :atomic_batches],
allow_stream_with: :full_read,
return_records?: true,
context: request.context || %{},
load: fields(request, request.resource) ++ (request.includes_keyword || [])
)
)
|> case do
%Ash.BulkResult{status: :success, records: [result | _]} ->
request
|> Request.assign(:result, result)
|> Request.assign(:record_from_path, result)
%Ash.BulkResult{status: :success, records: []} ->
error = Error.NotFound.exception(filter: filter, resource: request.resource)
Request.add_error(request, error, :fetch_from_path)
%Ash.BulkResult{status: :error, errors: errors} ->
Request.add_error(request, errors, :update)
end
end
end
end)
end
defp path_args_and_filter(path_params, resource, action, route) do
primary_key_fields = AshJsonApi.Resource.Info.primary_key_fields(resource)
Enum.reduce_while(path_params, {:ok, %{}, %{}}, fn
{key, value}, {:ok, params, filter} ->
# Check if this parameter should be parsed as a composite key
if route != nil and to_string(route.path_param_is_composite_key) == key and
primary_key_fields != [] do
primary_key_delimiter = AshJsonApi.Resource.Info.primary_key_delimiter(resource)
values = String.split(value, primary_key_delimiter)
if Enum.count(values) != Enum.count(primary_key_fields) do
{:halt,
{:error, Ash.Error.Query.NotFound.exception(primary_key: value, resource: resource)}}
else
filter =
primary_key_fields
|> Enum.zip(values)
|> Enum.reduce(filter, fn {key, value}, filter ->
Map.put(filter, key, value)
end)
{:cont, {:ok, params, filter}}
end
else
# Normal parameter handling
case Enum.find(action.arguments, &(to_string(&1.name) == key)) do
nil ->
case Ash.Resource.Info.attribute(resource, key) do
nil ->
{:halt,
{:error,
Ash.Error.Invalid.NoSuchInput.exception(
resource: resource,
action: action.name,
input: key,
inputs: []
)}}
attribute ->
{:cont, {:ok, params, Map.put(filter, attribute.name, value)}}
end
argument ->
{:cont, {:ok, Map.put(params, argument.name, value), filter}}
end
end
end)
end
def fetch_query(%{resource: request_resource} = request, through_resource \\ nil, load \\ nil) do
resource = through_resource || request_resource
action =
if through_resource || request.action.type != :read do
if request.route.read_action do
Ash.Resource.Info.action(request.resource, request.route.read_action)
else
Ash.Resource.Info.primary_action!(resource, :read)
end
else
request.action
end
case path_args_and_filter(request.path_params, resource, action, request.route) do
{:error, error} ->
{:error, error}
{:ok, params, filter} ->
action = action.name
fields_to_load =
if through_resource do
[]
else
fields(request, request.resource)
end
query =
if filter do
case Ash.Filter.parse_input(resource, filter) do
{:ok, parsed} ->
{:ok, Ash.Query.filter(resource, ^parsed)}
{:error, error} ->
{:error, error}
end
else
{:ok, resource}
end
case query do
{:error, error} ->
{:error, Request.add_error(request, error, :filter)}
{:ok, query} ->
query =
if load do
Ash.Query.load(query, load)
else
query
end
{:ok, filter,
query
|> Ash.Query.load(fields_to_load ++ (request.includes_keyword || []))
|> Ash.Query.set_context(request.context)
|> Ash.Query.for_read(
action,
params,
Keyword.put(Request.opts(request), :page, false)
)
|> Ash.Query.limit(1)}
end
end
end
def fetch_record_from_path(request, through_resource \\ nil, load \\ nil) do
chain(request, fn %{resource: request_resource} = request ->
resource = through_resource || request_resource
action =
if request.route.type != :get && (through_resource || request.action.type != :read) do
if request.route.read_action do
Ash.Resource.Info.action(request.resource, request.route.read_action)
else
Ash.Resource.Info.primary_action!(resource, :read)
end
else
request.action
end
if action.type == :action do
action_input =
request.resource
|> Ash.ActionInput.new()
|> Ash.ActionInput.set_context(request.context || %{})
|> Ash.ActionInput.for_action(
request.action.name,
request.arguments,
Request.opts(request)
)
request = Request.assign(request, :action_input, action_input)
{load_lazy, load} =
if through_resource do
{[], []}
else
{fields(request, request.resource), request.includes_keyword || []}
end
with {:ok, result} <- Ash.run_action(action_input),
{:ok, result} <-
Ash.load(result, load_lazy, Request.load_opts(request, reuse_values?: true)),
{:ok, result} <-
Ash.load(result, load, Request.load_opts(request)) do
request
|> Request.assign(:result, result)
|> Request.assign(:record_from_path, result)
else
{:error, error} ->
Request.add_error(request, error, :read)
end
else
case path_args_and_filter(request.path_params, resource, action, request.route) do
{:ok, params, filter} ->
action = action.name
fields_to_load =
if through_resource do
[]
else
fields(request, request.resource) ++ (request.includes_keyword || [])
end
query =
if filter do
case Ash.Filter.parse_input(resource, filter) do
{:ok, parsed} ->
{:ok, Ash.Query.filter(resource, ^parsed)}
{:error, error} ->
{:error, error}
end
else
{:ok, resource}
end
case query do
{:error, error} ->
{:error, Request.add_error(request, error, :filter)}
{:ok, query} ->
query =
if load do
Ash.Query.load(query, load)
else
query
end
query =
query
|> Ash.Query.set_context(request.context)
|> Ash.Query.for_read(
action,
Map.merge(request.arguments, params),
Keyword.put(Request.opts(request), :page, false)
)
|> Ash.Query.load(fields_to_load)
request = Request.assign(request, :query, query)
query
|> Ash.read_one(Request.opts(request))
|> case do
{:ok, nil} ->
error = Error.NotFound.exception(filter: filter, resource: resource)
Request.add_error(request, error, :fetch_from_path)
{:ok, record} ->
request
|> Request.assign(:result, record)
|> Request.assign(:record_from_path, record)
{:error, error} ->
Request.add_error(request, error, :fetch_from_path)
end
end
{:error, error} ->
Request.add_error(request, error, :fetch_from_path)
end
end
end)
end
def fetch_related(request, through_resource \\ nil) do
relationship =
Ash.Resource.Info.public_relationship(
through_resource || request.resource,
request.relationship
)
sort = request.sort || default_sort(request.resource)
load_params =
if Map.get(request.assigns, :page) do
[page: request.assigns.page]
else
[]
end
destination_query =
relationship.destination
|> Ash.Query.new()
|> Ash.Query.filter(^request.filter)
|> Ash.Query.sort(sort)
|> Ash.Query.load(request.includes_keyword)
|> Ash.Query.load(fields(request, request.resource))
|> Ash.Query.set_context(request.context)
|> Ash.Query.put_context(:override_domain_params, load_params)
request = Request.assign(request, :query, destination_query)
request
|> fetch_record_from_path(through_resource, [{relationship.name, destination_query}])
|> chain(fn %{
assigns: %{result: record},
relationship: relationship
} = request ->
paginated_result =
record
|> Map.get(relationship)
|> paginator_or_list()
request
|> Request.assign(:result, paginated_result)
end)
end
defp paginator_or_list(result) do
case result do
%{results: _} = paginator ->
paginator
other ->
List.wrap(other)
end
end
defp fields(request, resource) do
fields =
Map.get(request.fields, resource) || request.route.default_fields ||
AshJsonApi.Resource.Info.default_fields(resource) ||
Enum.map(Ash.Resource.Info.public_attributes(resource), & &1.name)
field_inputs = request.field_inputs[resource] || %{}
Enum.map(fields, fn field ->
case Map.get(field_inputs, field) do
nil -> field
value -> {field, value}
end
end)
end
defp default_sort(resource) do
created_at =
Ash.Resource.Info.public_attribute(resource, :created_at) ||
Ash.Resource.Info.public_attribute(resource, :inserted_at)
if created_at do
[{created_at.name, :asc}]
else
Ash.Resource.Info.primary_key(resource)
end
end
def fetch_id_path_param(request) do
chain(request, fn request ->
case request.path_params do
%{"id" => id} ->
Request.assign(request, :id, id)
_ ->
Request.add_error(
request,
"id path parameter not present in get route: #{request.url}",
:id_path_param
)
end
end)
end
# This doesn't need to use chain, because its stateless and safe to
# do anytime. Returning multiple errors is a nice feature of JSON API
def fetch_pagination_parameters(request) do
if request.action.type == :read do
request
|> add_pagination_parameter(:limit, :integer)
|> add_pagination_parameter(:offset, :integer)
|> add_pagination_parameter(:after, :string)
|> add_pagination_parameter(:before, :string)
|> add_pagination_parameter(:count, :boolean)
else
request
end
end
defp add_pagination_parameter(request, parameter, type) do
with %{"page" => page} <- request.query_params,
{:ok, value} <- Map.fetch(page, to_string(parameter)) do
case cast_pagination_parameter(value, type) do
{:ok, value} ->
Request.update_assign(
request,
:page,
[{parameter, value}],
&Keyword.put(&1, parameter, value)
)
:error ->
Request.add_error(
request,
Error.InvalidPagination.exception(source_parameter: "page[#{parameter}]"),
:read
)
end
else
_ ->
request
end
end
defp cast_pagination_parameter(value, :integer) do
case Integer.parse(value) do
{integer, ""} ->
{:ok, integer}
_ ->
:error
end
end
defp cast_pagination_parameter("true", :boolean), do: {:ok, true}
defp cast_pagination_parameter("false", :boolean), do: {:ok, false}
defp cast_pagination_parameter(_, :boolean), do: :error
defp cast_pagination_parameter(value, :string) when is_binary(value) do
{:ok, value}
end
defp cast_pagination_parameter(_, _), do: :error
def chain(request, func, opts \\ []) do
case request.errors do
[] ->
func.(request)
_ ->
case Keyword.fetch(opts, :fallback) do
{:ok, fallback} ->
fallback.(request)
_ ->
request
end
end
end
def resource_identifiers(request, argument) do
identifiers =
if map_type?(argument.type) do
request.resource_identifiers
else
Enum.map(request.resource_identifiers, fn identifier ->
identifier["id"]
end)
end
case argument.type do
{:array, _} ->
%{argument.name => identifiers}
_ ->
%{argument.name => Enum.at(identifiers, 0)}
end
end
defp map_type?(type) do
case type do
:map -> true
Ash.Type.Map -> true
Ash.Type.Struct -> true
:struct -> true
type -> Ash.Type.embedded_type?(type)
end
end
end