2020-05-02 10:35:12 +12:00
|
|
|
defmodule AshGraphql.Graphql.Resolver do
|
2020-08-14 10:55:34 +12:00
|
|
|
@moduledoc false
|
2020-10-10 14:10:22 +13:00
|
|
|
|
|
|
|
require Ash.Query
|
2021-04-14 09:49:10 +12:00
|
|
|
require Logger
|
2020-10-10 14:17:34 +13:00
|
|
|
|
2020-05-02 10:35:12 +12:00
|
|
|
def resolve(
|
2020-11-18 20:14:33 +13:00
|
|
|
%{arguments: arguments, context: context} = resolution,
|
|
|
|
{api, resource, %{type: :get, action: action, identity: identity}}
|
2020-05-02 10:35:12 +12:00
|
|
|
) do
|
2020-09-29 02:42:36 +13:00
|
|
|
opts = [
|
|
|
|
actor: Map.get(context, :actor),
|
|
|
|
authorize?: AshGraphql.Api.authorize?(api),
|
2021-02-24 06:28:19 +13:00
|
|
|
action: action,
|
|
|
|
verbose?: AshGraphql.Api.debug?(api)
|
2020-09-29 02:42:36 +13:00
|
|
|
]
|
2020-06-06 06:41:00 +12:00
|
|
|
|
2020-11-18 20:14:33 +13:00
|
|
|
filter =
|
|
|
|
if identity do
|
2021-01-13 09:14:35 +13:00
|
|
|
{:ok,
|
|
|
|
resource
|
2021-02-19 04:16:00 +13:00
|
|
|
|> Ash.Resource.Info.identities()
|
2021-01-13 09:14:35 +13:00
|
|
|
|> Enum.find(&(&1.name == identity))
|
|
|
|
|> Map.get(:keys)
|
|
|
|
|> Enum.map(fn key ->
|
|
|
|
{key, Map.get(arguments, key)}
|
|
|
|
end)}
|
2020-11-18 20:14:33 +13:00
|
|
|
else
|
2021-01-13 09:14:35 +13:00
|
|
|
case AshGraphql.Resource.decode_primary_key(resource, Map.get(arguments, :id) || "") do
|
|
|
|
{:ok, value} -> {:ok, [id: value]}
|
|
|
|
{:error, error} -> {:error, error}
|
|
|
|
end
|
2020-11-18 20:14:33 +13:00
|
|
|
end
|
|
|
|
|
2020-10-28 19:16:16 +13:00
|
|
|
result =
|
2021-01-13 09:14:35 +13:00
|
|
|
case filter do
|
|
|
|
{:ok, filter} ->
|
|
|
|
resource
|
|
|
|
|> Ash.Query.new()
|
|
|
|
|> Ash.Query.set_tenant(Map.get(context, :tenant))
|
2021-04-20 08:17:35 +12:00
|
|
|
|> Ash.Query.set_context(Map.get(context, :ash_context) || %{})
|
2021-01-13 09:14:35 +13:00
|
|
|
|> Ash.Query.filter(^filter)
|
2021-01-22 17:06:06 +13:00
|
|
|
|> set_query_arguments(action, arguments)
|
2021-03-23 06:03:18 +13:00
|
|
|
|> select_fields(resource, resolution)
|
|
|
|
|> load_fields(resource, api, resolution)
|
|
|
|
|> case do
|
|
|
|
{:ok, query} ->
|
|
|
|
api.read_one(query, opts)
|
|
|
|
|
|
|
|
{:error, error} ->
|
|
|
|
{:error, error}
|
|
|
|
end
|
2021-01-13 09:14:35 +13:00
|
|
|
|
|
|
|
{:error, error} ->
|
|
|
|
{:error, error}
|
|
|
|
end
|
2020-05-02 10:35:12 +12:00
|
|
|
|
|
|
|
Absinthe.Resolution.put_result(resolution, to_resolution(result))
|
2021-04-04 19:10:50 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
def resolve(
|
|
|
|
%{arguments: args, context: context} = resolution,
|
|
|
|
{api, resource, %{type: :read_one, action: action}}
|
|
|
|
) do
|
|
|
|
opts = [
|
|
|
|
actor: Map.get(context, :actor),
|
|
|
|
authorize?: AshGraphql.Api.authorize?(api),
|
|
|
|
action: action,
|
|
|
|
verbose?: AshGraphql.Api.debug?(api)
|
|
|
|
]
|
|
|
|
|
|
|
|
query =
|
|
|
|
case Map.fetch(args, :filter) do
|
|
|
|
{:ok, filter} ->
|
|
|
|
Ash.Query.filter(resource, ^filter)
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
Ash.Query.new(resource)
|
|
|
|
end
|
|
|
|
|
|
|
|
result =
|
|
|
|
query
|
|
|
|
|> Ash.Query.set_tenant(Map.get(context, :tenant))
|
2021-04-20 08:17:35 +12:00
|
|
|
|> Ash.Query.set_context(Map.get(context, :ash_context) || %{})
|
2021-04-04 19:10:50 +12:00
|
|
|
|> set_query_arguments(action, args)
|
|
|
|
|> select_fields(resource, resolution)
|
|
|
|
|> load_fields(resource, api, resolution)
|
|
|
|
|> case do
|
|
|
|
{:ok, query} ->
|
|
|
|
api.read_one(query, opts)
|
|
|
|
|
|
|
|
{:error, error} ->
|
|
|
|
{:error, error}
|
|
|
|
end
|
|
|
|
|
|
|
|
Absinthe.Resolution.put_result(resolution, to_resolution(result))
|
2020-05-02 10:35:12 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
def resolve(
|
2021-04-29 08:54:06 +12:00
|
|
|
%{arguments: args, context: context} = resolution,
|
2020-11-18 20:14:33 +13:00
|
|
|
{api, resource, %{type: :list, action: action}}
|
2020-05-02 10:35:12 +12:00
|
|
|
) do
|
2020-09-29 02:42:36 +13:00
|
|
|
opts = [
|
|
|
|
actor: Map.get(context, :actor),
|
|
|
|
authorize?: AshGraphql.Api.authorize?(api),
|
2021-02-24 06:28:19 +13:00
|
|
|
action: action,
|
|
|
|
verbose?: AshGraphql.Api.debug?(api)
|
2020-09-29 02:42:36 +13:00
|
|
|
]
|
2020-06-06 06:41:00 +12:00
|
|
|
|
2020-11-06 14:59:06 +13:00
|
|
|
page_opts =
|
|
|
|
args
|
|
|
|
|> Map.take([:limit, :offset, :after, :before])
|
|
|
|
|> Enum.reject(fn {_, val} -> is_nil(val) end)
|
|
|
|
|
|
|
|
opts =
|
|
|
|
case page_opts do
|
|
|
|
[] ->
|
|
|
|
opts
|
|
|
|
|
|
|
|
page_opts ->
|
2021-04-29 08:54:06 +12:00
|
|
|
if Enum.any?(fields(resolution), &(&1 == :count)) do
|
2020-11-06 14:59:06 +13:00
|
|
|
page_opts = Keyword.put(page_opts, :count, true)
|
|
|
|
Keyword.put(opts, :page, page_opts)
|
|
|
|
else
|
|
|
|
Keyword.put(opts, :page, page_opts)
|
|
|
|
end
|
|
|
|
end
|
2020-08-14 09:39:59 +12:00
|
|
|
|
|
|
|
query =
|
|
|
|
case Map.fetch(args, :filter) do
|
|
|
|
{:ok, filter} ->
|
2021-04-27 09:35:03 +12:00
|
|
|
aggregates =
|
|
|
|
resource
|
|
|
|
|> Ash.Resource.Info.public_aggregates()
|
|
|
|
|> Enum.map(& &1.name)
|
|
|
|
|
|
|
|
query =
|
|
|
|
resource
|
|
|
|
|> Ash.Query.load(aggregates)
|
|
|
|
|> Ash.Query.filter(^filter)
|
|
|
|
|
|
|
|
used =
|
|
|
|
if query.filter do
|
|
|
|
query.filter
|
|
|
|
|> Ash.Filter.used_aggregates()
|
|
|
|
|> Enum.map(& &1.name)
|
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
|
|
|
|
Ash.Query.unload(query, aggregates -- used)
|
2020-08-14 09:39:59 +12:00
|
|
|
|
|
|
|
_ ->
|
2020-11-06 14:59:06 +13:00
|
|
|
Ash.Query.new(resource)
|
2020-08-14 09:39:59 +12:00
|
|
|
end
|
|
|
|
|
2020-11-12 16:41:54 +13:00
|
|
|
query =
|
|
|
|
case Map.fetch(args, :sort) do
|
|
|
|
{:ok, sort} ->
|
|
|
|
keyword_sort =
|
|
|
|
Enum.map(sort, fn %{order: order, field: field} ->
|
|
|
|
{field, order}
|
|
|
|
end)
|
|
|
|
|
2021-04-27 09:37:43 +12:00
|
|
|
fields =
|
|
|
|
keyword_sort
|
|
|
|
|> Keyword.keys()
|
|
|
|
|> Enum.filter(&Ash.Resource.Info.public_aggregate(resource, &1))
|
2021-04-27 09:35:03 +12:00
|
|
|
|
|
|
|
query
|
|
|
|
|> Ash.Query.load(fields)
|
2021-04-27 09:37:43 +12:00
|
|
|
|> Ash.Query.sort(keyword_sort)
|
2020-11-12 16:41:54 +13:00
|
|
|
|
|
|
|
_ ->
|
|
|
|
query
|
|
|
|
end
|
|
|
|
|
2021-04-10 08:29:50 +12:00
|
|
|
nested =
|
|
|
|
if Ash.Resource.Info.action(resource, action, :read).pagination do
|
|
|
|
"results"
|
|
|
|
else
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
|
2020-08-14 09:39:59 +12:00
|
|
|
result =
|
|
|
|
query
|
2020-10-28 19:16:16 +13:00
|
|
|
|> Ash.Query.set_tenant(Map.get(context, :tenant))
|
2021-04-20 08:17:35 +12:00
|
|
|
|> Ash.Query.set_context(Map.get(context, :ash_context) || %{})
|
2021-01-22 17:06:06 +13:00
|
|
|
|> set_query_arguments(action, args)
|
2021-04-10 08:29:50 +12:00
|
|
|
|> select_fields(resource, resolution, nested)
|
|
|
|
|> load_fields(resource, api, resolution, nested)
|
2020-05-30 20:57:39 +12:00
|
|
|
|> case do
|
2021-03-23 06:03:18 +13:00
|
|
|
{:ok, query} ->
|
|
|
|
query
|
|
|
|
|> api.read(opts)
|
|
|
|
|> case do
|
|
|
|
{:ok, %{results: results, count: count}} ->
|
|
|
|
{:ok, %{results: results, count: count}}
|
|
|
|
|
|
|
|
{:ok, results} ->
|
|
|
|
if Ash.Resource.Info.action(resource, action, :read).pagination do
|
|
|
|
{:ok, %{results: results, count: Enum.count(results)}}
|
|
|
|
else
|
|
|
|
{:ok, results}
|
|
|
|
end
|
|
|
|
|
|
|
|
error ->
|
|
|
|
error
|
2020-12-31 07:26:03 +13:00
|
|
|
end
|
2020-05-30 20:57:39 +12:00
|
|
|
|
2021-03-23 06:03:18 +13:00
|
|
|
{:error, error} ->
|
|
|
|
{:error, error}
|
2020-05-02 10:35:12 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
Absinthe.Resolution.put_result(resolution, to_resolution(result))
|
|
|
|
end
|
|
|
|
|
2020-08-14 09:39:59 +12:00
|
|
|
def mutate(
|
|
|
|
%{arguments: %{input: input}, context: context} = resolution,
|
2021-04-06 06:41:16 +12:00
|
|
|
{api, resource, %{type: :create, action: action, upsert?: upsert?}}
|
2020-08-14 09:39:59 +12:00
|
|
|
) do
|
2020-09-29 02:42:36 +13:00
|
|
|
opts = [
|
|
|
|
actor: Map.get(context, :actor),
|
|
|
|
authorize?: AshGraphql.Api.authorize?(api),
|
2021-02-24 06:28:19 +13:00
|
|
|
action: action,
|
2021-04-06 06:41:16 +12:00
|
|
|
verbose?: AshGraphql.Api.debug?(api),
|
|
|
|
upsert?: upsert?
|
2020-09-29 02:42:36 +13:00
|
|
|
]
|
2020-08-14 09:39:59 +12:00
|
|
|
|
|
|
|
result =
|
2021-04-06 06:41:16 +12:00
|
|
|
resource
|
|
|
|
|> Ash.Changeset.new()
|
2020-11-03 15:15:30 +13:00
|
|
|
|> Ash.Changeset.set_tenant(Map.get(context, :tenant))
|
2021-04-20 08:17:35 +12:00
|
|
|
|> Ash.Changeset.set_context(Map.get(context, :ash_context) || %{})
|
2021-04-19 02:57:00 +12:00
|
|
|
|> Ash.Changeset.for_create(action, input, actor: Map.get(context, :actor))
|
2021-04-10 08:29:50 +12:00
|
|
|
|> select_fields(resource, resolution, "result")
|
2020-10-28 19:16:16 +13:00
|
|
|
|> api.create(opts)
|
|
|
|
|> case do
|
2020-09-24 12:54:57 +12:00
|
|
|
{:ok, value} ->
|
2021-04-10 08:29:50 +12:00
|
|
|
case load_fields(value, resource, api, resolution, "result") do
|
2021-03-23 06:03:18 +13:00
|
|
|
{:ok, result} ->
|
|
|
|
{:ok, %{result: result, errors: []}}
|
|
|
|
|
|
|
|
{:error, error} ->
|
|
|
|
{:ok, %{result: nil, errors: to_errors(List.wrap(error))}}
|
|
|
|
end
|
2020-09-24 12:54:57 +12:00
|
|
|
|
2021-03-16 08:51:17 +13:00
|
|
|
{:error, %{changeset: changeset}} ->
|
|
|
|
{:ok, %{result: nil, errors: to_errors(changeset.errors)}}
|
2020-08-14 09:39:59 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
Absinthe.Resolution.put_result(resolution, to_resolution(result))
|
|
|
|
end
|
|
|
|
|
|
|
|
def mutate(
|
2020-11-18 20:14:33 +13:00
|
|
|
%{arguments: %{input: input} = arguments, context: context} = resolution,
|
2021-04-20 07:26:20 +12:00
|
|
|
{api, resource,
|
|
|
|
%{type: :update, action: action, identity: identity, read_action: read_action}}
|
2020-08-14 09:39:59 +12:00
|
|
|
) do
|
2021-04-20 07:26:20 +12:00
|
|
|
filter = identity_filter(identity, resource, arguments)
|
2020-11-18 20:14:33 +13:00
|
|
|
|
2021-01-13 09:14:35 +13:00
|
|
|
case filter do
|
|
|
|
{:ok, filter} ->
|
|
|
|
resource
|
|
|
|
|> Ash.Query.filter(^filter)
|
|
|
|
|> Ash.Query.set_tenant(Map.get(context, :tenant))
|
2021-04-20 08:17:35 +12:00
|
|
|
|> Ash.Query.set_context(Map.get(context, :ash_context) || %{})
|
2021-01-22 17:06:06 +13:00
|
|
|
|> set_query_arguments(action, arguments)
|
2021-04-20 07:26:20 +12:00
|
|
|
|> api.read_one!(action: read_action, verbose?: AshGraphql.Api.debug?(api))
|
2021-01-13 09:14:35 +13:00
|
|
|
|> case do
|
|
|
|
nil ->
|
2021-04-17 05:49:51 +12:00
|
|
|
result = not_found(filter, resource)
|
|
|
|
|
|
|
|
Absinthe.Resolution.put_result(resolution, result)
|
2021-01-13 09:14:35 +13:00
|
|
|
|
|
|
|
initial ->
|
|
|
|
opts = [
|
|
|
|
actor: Map.get(context, :actor),
|
|
|
|
authorize?: AshGraphql.Api.authorize?(api),
|
2021-02-24 06:28:19 +13:00
|
|
|
action: action,
|
|
|
|
verbose?: AshGraphql.Api.debug?(api)
|
2021-01-13 09:14:35 +13:00
|
|
|
]
|
|
|
|
|
|
|
|
result =
|
2021-04-06 06:41:16 +12:00
|
|
|
initial
|
|
|
|
|> Ash.Changeset.new()
|
2021-01-13 09:14:35 +13:00
|
|
|
|> Ash.Changeset.set_tenant(Map.get(context, :tenant))
|
2021-04-20 08:17:35 +12:00
|
|
|
|> Ash.Changeset.set_context(Map.get(context, :ash_context) || %{})
|
2021-04-19 02:57:00 +12:00
|
|
|
|> Ash.Changeset.for_update(action, input, actor: Map.get(context, :actor))
|
2021-01-13 09:14:35 +13:00
|
|
|
|> Ash.Changeset.set_arguments(arguments)
|
2021-04-10 08:29:50 +12:00
|
|
|
|> select_fields(resource, resolution, "result")
|
2021-01-13 09:14:35 +13:00
|
|
|
|> api.update(opts)
|
2021-03-23 06:03:18 +13:00
|
|
|
|> update_result(resource, api, resolution)
|
2021-01-13 09:14:35 +13:00
|
|
|
|
|
|
|
Absinthe.Resolution.put_result(resolution, to_resolution(result))
|
|
|
|
end
|
|
|
|
|
|
|
|
{:error, error} ->
|
|
|
|
Absinthe.Resolution.put_result(resolution, to_resolution({:error, error}))
|
2020-08-14 09:39:59 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-10-28 19:16:16 +13:00
|
|
|
def mutate(
|
2020-11-18 20:14:33 +13:00
|
|
|
%{arguments: arguments, context: context} = resolution,
|
2021-04-20 07:26:20 +12:00
|
|
|
{api, resource,
|
|
|
|
%{type: :destroy, action: action, identity: identity, read_action: read_action}}
|
2020-10-28 19:16:16 +13:00
|
|
|
) do
|
2021-04-20 07:26:20 +12:00
|
|
|
filter = identity_filter(identity, resource, arguments)
|
2020-11-18 20:14:33 +13:00
|
|
|
|
2021-01-13 09:14:35 +13:00
|
|
|
case filter do
|
|
|
|
{:ok, filter} ->
|
|
|
|
resource
|
|
|
|
|> Ash.Query.filter(^filter)
|
|
|
|
|> Ash.Query.set_tenant(Map.get(context, :tenant))
|
2021-04-20 08:17:35 +12:00
|
|
|
|> Ash.Query.set_context(Map.get(context, :ash_context) || %{})
|
2021-01-22 17:06:06 +13:00
|
|
|
|> set_query_arguments(action, arguments)
|
2021-04-20 07:26:20 +12:00
|
|
|
|> api.read_one!(action: read_action, verbose?: AshGraphql.Api.debug?(api))
|
2021-01-13 09:14:35 +13:00
|
|
|
|> case do
|
|
|
|
nil ->
|
2021-04-17 05:49:51 +12:00
|
|
|
result = not_found(filter, resource)
|
|
|
|
|
|
|
|
Absinthe.Resolution.put_result(resolution, result)
|
2021-01-13 09:14:35 +13:00
|
|
|
|
|
|
|
initial ->
|
|
|
|
opts = destroy_opts(api, context, action)
|
|
|
|
|
|
|
|
result =
|
|
|
|
initial
|
|
|
|
|> Ash.Changeset.new()
|
|
|
|
|> Ash.Changeset.set_tenant(Map.get(context, :tenant))
|
2021-04-20 08:17:35 +12:00
|
|
|
|> Ash.Changeset.set_context(Map.get(context, :ash_context) || %{})
|
2021-04-10 08:29:50 +12:00
|
|
|
|> select_fields(resource, resolution, "result")
|
2021-01-13 09:14:35 +13:00
|
|
|
|> api.destroy(opts)
|
2021-03-23 06:03:18 +13:00
|
|
|
|> destroy_result(initial, resource, resolution)
|
2021-01-13 09:14:35 +13:00
|
|
|
|
|
|
|
Absinthe.Resolution.put_result(resolution, to_resolution(result))
|
|
|
|
end
|
|
|
|
|
|
|
|
{:error, error} ->
|
|
|
|
Absinthe.Resolution.put_result(resolution, to_resolution({:error, error}))
|
|
|
|
end
|
|
|
|
end
|
2020-08-14 09:39:59 +12:00
|
|
|
|
2021-04-20 07:26:20 +12:00
|
|
|
def identity_filter(false, _resource, _arguments) do
|
|
|
|
{:ok, nil}
|
|
|
|
end
|
|
|
|
|
|
|
|
def identity_filter(nil, resource, arguments) do
|
|
|
|
case AshGraphql.Resource.decode_primary_key(resource, Map.get(arguments, :id) || "") do
|
|
|
|
{:ok, value} -> {:ok, [id: value]}
|
|
|
|
{:error, error} -> {:error, error}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def identity_filter(identity, resource, arguments) do
|
|
|
|
{:ok,
|
|
|
|
resource
|
|
|
|
|> Ash.Resource.Info.identities()
|
|
|
|
|> Enum.find(&(&1.name == identity))
|
|
|
|
|> Map.get(:keys)
|
|
|
|
|> Enum.map(fn key ->
|
|
|
|
{key, Map.get(arguments, key)}
|
|
|
|
end)}
|
|
|
|
end
|
|
|
|
|
2021-03-16 08:51:17 +13:00
|
|
|
defp not_found(filter, resource) do
|
|
|
|
{:ok,
|
|
|
|
%{
|
|
|
|
result: nil,
|
2021-04-17 05:49:51 +12:00
|
|
|
errors:
|
2021-03-16 08:51:17 +13:00
|
|
|
to_errors(
|
|
|
|
Ash.Error.Query.NotFound.exception(
|
|
|
|
primary_key: Map.new(filter),
|
|
|
|
resource: resource
|
|
|
|
)
|
|
|
|
)
|
|
|
|
}}
|
|
|
|
end
|
|
|
|
|
2021-04-17 04:55:34 +12:00
|
|
|
defp clear_fields(nil, _, _), do: nil
|
|
|
|
|
2021-03-23 06:03:18 +13:00
|
|
|
defp clear_fields(result, resource, resolution) do
|
|
|
|
resolution
|
2021-04-17 04:59:04 +12:00
|
|
|
|> fields("result")
|
2021-04-19 15:19:46 +12:00
|
|
|
|> Enum.map(fn identifier ->
|
|
|
|
Ash.Resource.Info.aggregate(resource, identifier) ||
|
|
|
|
Ash.Resource.Info.calculation(resource, identifier)
|
2021-03-23 06:03:18 +13:00
|
|
|
end)
|
|
|
|
|> Enum.filter(& &1)
|
|
|
|
|> Enum.map(& &1.name)
|
|
|
|
|> Enum.reduce(result, fn field, result ->
|
|
|
|
Map.put(result, field, nil)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2021-04-10 08:29:50 +12:00
|
|
|
defp load_fields(query_or_record, resource, api, resolution, nested \\ nil) do
|
2021-03-23 06:03:18 +13:00
|
|
|
loading =
|
|
|
|
resolution
|
2021-04-10 08:29:50 +12:00
|
|
|
|> fields(nested)
|
2021-04-19 15:19:46 +12:00
|
|
|
|> Enum.map(fn identifier ->
|
|
|
|
Ash.Resource.Info.aggregate(resource, identifier) ||
|
|
|
|
Ash.Resource.Info.calculation(resource, identifier)
|
2021-03-23 06:03:18 +13:00
|
|
|
end)
|
|
|
|
|> Enum.filter(& &1)
|
|
|
|
|> Enum.map(& &1.name)
|
|
|
|
|
|
|
|
case query_or_record do
|
|
|
|
%Ash.Query{} = query ->
|
|
|
|
{:ok, Ash.Query.load(query, loading)}
|
|
|
|
|
|
|
|
record ->
|
|
|
|
api.load(record, loading)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-04-10 08:29:50 +12:00
|
|
|
defp select_fields(query_or_changeset, resource, resolution, nested \\ nil) do
|
2021-03-23 06:03:18 +13:00
|
|
|
subfields =
|
|
|
|
resolution
|
2021-04-10 08:29:50 +12:00
|
|
|
|> fields(nested)
|
2021-04-16 08:28:04 +12:00
|
|
|
|> Enum.map(&field_or_relationship(resource, &1))
|
2021-03-23 06:03:18 +13:00
|
|
|
|> Enum.filter(& &1)
|
|
|
|
|> Enum.map(& &1.name)
|
|
|
|
|
|
|
|
case query_or_changeset do
|
|
|
|
%Ash.Query{} = query ->
|
|
|
|
Ash.Query.select(query, subfields)
|
|
|
|
|
|
|
|
%Ash.Changeset{} = changeset ->
|
|
|
|
Ash.Changeset.select(changeset, subfields)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-04-19 15:19:46 +12:00
|
|
|
defp field_or_relationship(resource, identifier) do
|
|
|
|
case Ash.Resource.Info.attribute(resource, identifier) do
|
2021-04-16 08:28:04 +12:00
|
|
|
nil ->
|
2021-04-19 15:19:46 +12:00
|
|
|
case Ash.Resource.Info.relationship(resource, identifier) do
|
2021-04-16 08:28:04 +12:00
|
|
|
nil ->
|
|
|
|
nil
|
|
|
|
|
|
|
|
rel ->
|
|
|
|
Ash.Resource.Info.attribute(resource, rel.source_field)
|
|
|
|
end
|
|
|
|
|
|
|
|
attr ->
|
|
|
|
attr
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-04-29 08:54:06 +12:00
|
|
|
defp fields(resolution, nested \\ nil) do
|
2021-04-10 08:29:50 +12:00
|
|
|
if nested do
|
2021-04-19 15:19:46 +12:00
|
|
|
projected_once =
|
|
|
|
resolution
|
|
|
|
|> Absinthe.Resolution.project()
|
|
|
|
|> Enum.find(&(&1.name == nested))
|
|
|
|
|
|
|
|
type = Absinthe.Schema.lookup_type(resolution.schema, projected_once.schema_node.type)
|
|
|
|
|
|
|
|
projected_once
|
2021-03-23 06:03:18 +13:00
|
|
|
|> Map.get(:selections)
|
2021-04-19 15:19:46 +12:00
|
|
|
|> Absinthe.Resolution.Projector.project(
|
|
|
|
type,
|
|
|
|
resolution.path ++ [projected_once],
|
|
|
|
resolution.fields_cache,
|
|
|
|
resolution
|
|
|
|
)
|
|
|
|
|> elem(0)
|
|
|
|
|> Enum.map(fn %{schema_node: %{identifier: identifier}} ->
|
|
|
|
identifier
|
|
|
|
end)
|
2021-03-23 06:03:18 +13:00
|
|
|
else
|
2021-04-19 15:19:46 +12:00
|
|
|
resolution
|
|
|
|
|> Absinthe.Resolution.project()
|
|
|
|
|> Enum.map(fn %{schema_node: %{identifier: identifier}} ->
|
|
|
|
identifier
|
|
|
|
end)
|
2021-03-23 06:03:18 +13:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-22 17:06:06 +13:00
|
|
|
defp set_query_arguments(query, action, arg_values) do
|
2021-02-19 04:16:00 +13:00
|
|
|
action = Ash.Resource.Info.action(query.resource, action, :read)
|
2021-01-22 17:06:06 +13:00
|
|
|
|
|
|
|
action.arguments
|
|
|
|
|> Enum.reject(& &1.private?)
|
|
|
|
|> Enum.reduce(query, fn argument, query ->
|
|
|
|
Ash.Query.set_argument(query, argument.name, Map.get(arg_values, argument.name))
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2021-01-13 09:14:35 +13:00
|
|
|
defp destroy_opts(api, context, action) do
|
|
|
|
if AshGraphql.Api.authorize?(api) do
|
2021-02-24 06:28:19 +13:00
|
|
|
[actor: Map.get(context, :actor), action: action, verbose?: AshGraphql.Api.debug?(api)]
|
2021-01-13 09:14:35 +13:00
|
|
|
else
|
2021-02-24 06:28:19 +13:00
|
|
|
[action: action, verbose?: AshGraphql.Api.debug?(api)]
|
2021-01-13 09:14:35 +13:00
|
|
|
end
|
|
|
|
end
|
2020-08-14 09:39:59 +12:00
|
|
|
|
2021-03-23 06:03:18 +13:00
|
|
|
defp update_result(result, resource, api, resolution) do
|
2021-01-13 09:14:35 +13:00
|
|
|
case result do
|
|
|
|
{:ok, value} ->
|
2021-04-10 08:29:50 +12:00
|
|
|
case load_fields(value, resource, api, resolution, "result") do
|
2021-03-23 06:03:18 +13:00
|
|
|
{:ok, result} ->
|
|
|
|
{:ok, %{result: result, errors: []}}
|
|
|
|
|
|
|
|
{:error, error} ->
|
|
|
|
{:ok, %{result: nil, errors: to_errors(List.wrap(error))}}
|
|
|
|
end
|
2021-01-13 09:14:35 +13:00
|
|
|
|
|
|
|
{:error, error} ->
|
2021-03-23 06:03:18 +13:00
|
|
|
{:ok, %{result: nil, errors: to_errors(List.wrap(error))}}
|
2021-01-13 09:14:35 +13:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-03-23 06:03:18 +13:00
|
|
|
defp destroy_result(result, initial, resource, resolution) do
|
2021-01-13 09:14:35 +13:00
|
|
|
case result do
|
2021-03-16 08:51:17 +13:00
|
|
|
:ok ->
|
2021-03-23 06:03:18 +13:00
|
|
|
{:ok, %{result: clear_fields(initial, resource, resolution), errors: []}}
|
2021-03-16 08:51:17 +13:00
|
|
|
|
|
|
|
{:error, %{changeset: changeset}} ->
|
|
|
|
{:ok, %{result: nil, errors: to_errors(changeset.error)}}
|
2021-01-13 09:14:35 +13:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-14 09:39:59 +12:00
|
|
|
defp to_errors(errors) do
|
|
|
|
errors
|
|
|
|
|> List.wrap()
|
2021-04-27 05:14:25 +12:00
|
|
|
|> Enum.flat_map(fn
|
|
|
|
%Ash.Error.Invalid{errors: errors} ->
|
|
|
|
List.wrap(errors)
|
|
|
|
|
|
|
|
errors ->
|
|
|
|
List.wrap(errors)
|
|
|
|
end)
|
2020-08-14 09:39:59 +12:00
|
|
|
|> Enum.map(fn error ->
|
2021-03-16 08:51:17 +13:00
|
|
|
if AshGraphql.Error.impl_for(error) do
|
|
|
|
AshGraphql.Error.to_error(error)
|
|
|
|
else
|
2021-04-14 09:49:10 +12:00
|
|
|
uuid = Ash.UUID.generate()
|
|
|
|
Logger.warn("`#{uuid}`: AshGraphql.Error not implemented for error:\n\n#{inspect(error)}")
|
|
|
|
|
2021-03-16 08:51:17 +13:00
|
|
|
%{
|
2021-04-14 09:49:10 +12:00
|
|
|
message: "something went wrong. Unique error id: `#{uuid}`"
|
2021-03-16 08:51:17 +13:00
|
|
|
}
|
2020-08-14 09:39:59 +12:00
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2020-09-24 12:54:57 +12:00
|
|
|
def resolve_assoc(
|
2020-10-28 19:16:16 +13:00
|
|
|
%{source: parent, arguments: args, context: %{loader: loader} = context} = resolution,
|
2020-09-24 12:54:57 +12:00
|
|
|
{api, relationship}
|
|
|
|
) do
|
2021-02-24 06:28:19 +13:00
|
|
|
api_opts = [
|
|
|
|
actor: Map.get(context, :actor),
|
|
|
|
authorize?: AshGraphql.Api.authorize?(api),
|
|
|
|
verbose?: AshGraphql.Api.debug?(api)
|
|
|
|
]
|
2020-09-29 02:42:36 +13:00
|
|
|
|
2021-04-16 08:28:04 +12:00
|
|
|
related_query =
|
|
|
|
args
|
|
|
|
|> apply_load_arguments(Ash.Query.new(relationship.destination))
|
|
|
|
|> select_fields(relationship.destination, resolution)
|
|
|
|
|
2020-09-29 02:42:36 +13:00
|
|
|
opts = [
|
2021-04-16 08:28:04 +12:00
|
|
|
query: related_query,
|
2020-10-28 19:16:16 +13:00
|
|
|
api_opts: api_opts,
|
2020-11-12 16:41:54 +13:00
|
|
|
args: args,
|
2020-10-28 19:16:16 +13:00
|
|
|
tenant: Map.get(context, :tenant)
|
2020-09-29 02:42:36 +13:00
|
|
|
]
|
|
|
|
|
2020-09-24 12:54:57 +12:00
|
|
|
{batch_key, parent} = {{relationship.name, opts}, parent}
|
2020-08-14 09:39:59 +12:00
|
|
|
|
2020-09-29 02:42:36 +13:00
|
|
|
do_dataloader(resolution, loader, api, batch_key, args, parent)
|
2020-08-14 09:39:59 +12:00
|
|
|
end
|
|
|
|
|
2020-09-24 12:54:57 +12:00
|
|
|
defp do_dataloader(
|
|
|
|
resolution,
|
|
|
|
loader,
|
|
|
|
api,
|
|
|
|
batch_key,
|
2020-10-28 19:16:16 +13:00
|
|
|
_args,
|
2020-09-29 02:42:36 +13:00
|
|
|
parent
|
2020-09-24 12:54:57 +12:00
|
|
|
) do
|
|
|
|
loader = Dataloader.load(loader, api, batch_key, parent)
|
|
|
|
|
|
|
|
fun = fn loader ->
|
2020-10-28 19:16:16 +13:00
|
|
|
{:ok, Dataloader.get(loader, api, batch_key, parent)}
|
2020-09-24 12:54:57 +12:00
|
|
|
end
|
2020-08-14 09:39:59 +12:00
|
|
|
|
2020-09-24 12:54:57 +12:00
|
|
|
Absinthe.Resolution.put_result(
|
|
|
|
resolution,
|
|
|
|
{:middleware, Absinthe.Middleware.Dataloader, {loader, fun}}
|
|
|
|
)
|
|
|
|
end
|
2020-08-14 09:39:59 +12:00
|
|
|
|
2020-09-24 12:54:57 +12:00
|
|
|
defp apply_load_arguments(arguments, query) do
|
|
|
|
Enum.reduce(arguments, query, fn
|
|
|
|
{:limit, limit}, query ->
|
|
|
|
Ash.Query.limit(query, limit)
|
2020-08-15 02:20:47 +12:00
|
|
|
|
2020-09-24 12:54:57 +12:00
|
|
|
{:offset, offset}, query ->
|
|
|
|
Ash.Query.offset(query, offset)
|
2020-08-15 02:20:47 +12:00
|
|
|
|
2020-09-24 12:54:57 +12:00
|
|
|
{:filter, value}, query ->
|
2020-08-15 02:20:47 +12:00
|
|
|
decode_and_filter(query, value)
|
2020-11-12 16:41:54 +13:00
|
|
|
|
|
|
|
{:sort, value}, query ->
|
|
|
|
keyword_sort =
|
|
|
|
Enum.map(value, fn %{order: order, field: field} ->
|
|
|
|
{field, order}
|
|
|
|
end)
|
|
|
|
|
|
|
|
Ash.Query.sort(query, keyword_sort)
|
2020-08-15 02:20:47 +12:00
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp decode_and_filter(query, value) do
|
2020-12-31 07:10:10 +13:00
|
|
|
Ash.Query.filter(query, ^value)
|
2020-08-15 02:20:47 +12:00
|
|
|
end
|
|
|
|
|
2020-05-02 10:35:12 +12:00
|
|
|
defp to_resolution({:ok, value}), do: {:ok, value}
|
2020-08-14 09:39:59 +12:00
|
|
|
|
|
|
|
defp to_resolution({:error, error}),
|
|
|
|
do: {:error, error |> List.wrap() |> Enum.map(&Exception.message(&1))}
|
2020-05-02 10:35:12 +12:00
|
|
|
end
|