Current section

Files

Jump to
ex_dgraph lib exdgraph mutation.ex
Raw

lib/exdgraph/mutation.ex

defmodule ExDgraph.Mutation do
@moduledoc """
Provides the functions for the callbacks from the DBConnection behaviour.
"""
alias ExDgraph.{Exception, MutationStatement, Transform}
@doc false
def mutation(conn, statement) do
case mutation_commit(conn, statement) do
{:error, f} -> {:error, code: f.code, message: f.message}
r -> {:ok, r}
end
end
@doc false
def mutation!(conn, statement) do
case mutation(conn, statement) do
{:ok, r} ->
r
{:error, code: code, message: message} ->
raise Exception, code: code, message: message
end
end
@doc false
def set_map(conn, map) do
map_with_tmp_uids = insert_tmp_uids(map)
json = Poison.encode!(map_with_tmp_uids)
case set_map_commit(conn, json, map_with_tmp_uids) do
{:error, f} -> {:error, code: f.code, message: f.message}
r -> {:ok, r}
end
end
@doc false
def set_map!(conn, map) do
case set_map(conn, map) do
{:ok, r} ->
r
{:error, code: code, message: message} ->
raise Exception, code: code, message: message
end
end
@doc false
def set_struct(conn, struct) do
uids_and_schema_map = set_tmp_ids_and_schema(struct)
json = Poison.encode!(uids_and_schema_map)
case set_struct_commit(conn, json, uids_and_schema_map) do
{:error, f} -> {:error, code: f.code, message: f.message}
r -> {:ok, r}
end
end
@doc false
def set_struct!(conn, struct) do
case set_struct(conn, struct) do
{:ok, r} ->
r
{:error, code: code, message: message} ->
raise Exception, code: code, message: message
end
end
defp mutation_commit(conn, statement) do
exec = fn conn ->
q = %MutationStatement{statement: statement}
case DBConnection.execute(conn, q, %{}) do
{:ok, resp} -> Transform.transform_mutation(resp)
other -> other
end
end
# Response.transform(DBConnection.run(conn, exec, run_opts()))
DBConnection.run(conn, exec, run_opts())
end
defp set_map_commit(conn, json, map_with_tmp_uids) do
exec = fn conn ->
q = %MutationStatement{set_json: json}
case DBConnection.execute(conn, q, %{}) do
{:ok, resp} ->
parsed_response = Transform.transform_mutation(resp)
# Now exchange the tmp ids for the ones returned from the db
result_with_uids = replace_tmp_uids(map_with_tmp_uids, parsed_response.uids)
Map.put(parsed_response, :result, result_with_uids)
other ->
other
end
end
# Response.transform(DBConnection.run(conn, exec, run_opts()))
DBConnection.run(conn, exec, run_opts())
end
defp set_struct_commit(conn, json, struct_with_tmp_uids) do
exec = fn conn ->
q = %MutationStatement{set_json: json}
case DBConnection.execute(conn, q, %{}) do
{:ok, resp} ->
parsed_response = Transform.transform_mutation(resp)
# Now exchange the tmp ids for the ones returned from the db
result_with_uids = replace_tmp_struct_uids(struct_with_tmp_uids, parsed_response.uids)
Map.put(parsed_response, :result, result_with_uids)
other ->
other
end
end
DBConnection.run(conn, exec, run_opts())
end
defp insert_tmp_uids(map) when is_list(map), do: Enum.map(map, &insert_tmp_uids/1)
defp insert_tmp_uids(map) when is_map(map) do
map
|> Map.update(:uid, "_:#{UUID.uuid4()}", fn existing_uuid -> existing_uuid end)
|> Enum.reduce(%{}, fn {key, map_value}, a ->
Map.merge(a, %{key => insert_tmp_uids(map_value)})
end)
end
defp insert_tmp_uids(value), do: value
defp set_tmp_ids_and_schema(map) when is_list(map), do: Enum.map(map, &set_tmp_ids_and_schema/1)
defp set_tmp_ids_and_schema(%x{} = map) do
schema = x |> get_schema_name()
map
|> Map.from_struct()
|> Map.update(:uid, "_:#{UUID.uuid4()}", fn
nil -> "_:#{UUID.uuid4()}"
existing_uuid -> existing_uuid
end)
|> Enum.reduce(%{}, fn {key, map_value}, a ->
set_schema(schema, {key, map_value}, a, ExDgraph.config(:enforce_struct_schema))
end)
end
defp set_tmp_ids_and_schema(map) when is_map(map) do
map
|> Map.update(:uid, "_:#{UUID.uuid4()}", fn existing_uuid -> existing_uuid end)
|> Enum.reduce(%{}, fn {key, map_value}, a ->
Map.merge(a, %{key => set_tmp_ids_and_schema(map_value)})
end)
end
defp set_tmp_ids_and_schema(value), do: value
defp replace_tmp_uids(map, uids) when is_list(map),
do: Enum.map(map, &replace_tmp_uids(&1, uids))
defp replace_tmp_uids(map, uids) when is_map(map) do
map
|> Map.update(:uid, map[:uid], fn existing_uuid ->
case String.slice(existing_uuid, 0, 2) == "_:" do
true -> uids[String.replace_leading(existing_uuid, "_:", "")]
false -> existing_uuid
end
end)
|> Enum.reduce(%{}, fn {key, map_value}, a ->
Map.merge(a, %{key => replace_tmp_uids(map_value, uids)})
end)
end
defp replace_tmp_uids(value, _uids), do: value
defp replace_tmp_struct_uids(map, uids) when is_list(map),
do: Enum.map(map, &replace_tmp_struct_uids(&1, uids))
defp replace_tmp_struct_uids(map, uids) when is_map(map) do
map
|> Map.update(:uid, map[:uid], fn existing_uuid ->
case String.slice(existing_uuid, 0, 2) == "_:" do
true -> uids[String.replace_leading(existing_uuid, "_:", "")]
false -> existing_uuid
end
end)
|> Enum.reduce(%{}, fn {key, map_value}, a ->
# delete the schema prefix
key = key |> to_string() |> String.split(".") |> List.last() |> String.to_existing_atom()
Map.merge(a, %{key => replace_tmp_struct_uids(map_value, uids)})
end)
end
defp replace_tmp_struct_uids(value, _uids), do: value
defp get_schema_name(schema) do
schema |> to_string() |> String.split(".") |> List.last() |> String.downcase()
end
defp set_schema(_schema_name, {:uid, map_value}, result, _is_enforced_schema),
do: Map.merge(result, %{:uid => set_tmp_ids_and_schema(map_value)})
defp set_schema(schema_name, {key, map_value}, result, is_enforced_schema)
when is_enforced_schema == true,
do: Map.merge(result, %{"#{schema_name}.#{key}" => set_tmp_ids_and_schema(map_value)})
defp set_schema(_schema_name, {key, map_value}, result, _is_enforced_schema),
do: Map.merge(result, %{key => set_tmp_ids_and_schema(map_value)})
defp run_opts do
[pool: ExDgraph.config(:pool)]
end
end