balls/lib/balls_pds/ecto/schema/collection_object.ex

171 lines
4.6 KiB
Elixir

defmodule BallsPDS.Ecto.Schema.CollectionObject do
use Ecto.Schema
import Ecto.Query
import Ecto.Changeset
alias BallsPDS.Repo
alias BallsPDS.Ecto.Schema.Object
schema "collection_objects" do
belongs_to(:collection, BallsPDS.Ecto.Schema.Object, foreign_key: :collection_id)
belongs_to(:object, BallsPDS.Ecto.Schema.Object, foreign_key: :object_id)
field(:remote_id, :string)
field(:order_num, :integer)
timestamps()
end
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [
:collection_id,
:remote_id,
:object_id
])
|> validate_required([:collection_id])
|> foreign_key_constraint(:collection_id)
|> foreign_key_constraint(:object_id)
end
def delete(%Object{id: collection_id}, object_id) when is_integer(object_id) do
from(co in __MODULE__,
where: co.collection_id == ^collection_id,
where: co.object_id == ^object_id
)
|> Repo.delete_all()
end
# FIXME: handle if local id passed
def delete(%Object{id: collection_id}, remote_id) when is_binary(remote_id) do
from(co in __MODULE__,
where: co.collection_id == ^collection_id,
where: co.remote_id == ^remote_id
)
|> Repo.delete_all()
end
def delete_all(%Object{id: collection_id}) do
from(co in __MODULE__,
where: co.collection_id == ^collection_id
)
|> Repo.delete_all()
end
def delete_all(%Object{id: collection_id}, ids) when is_list(ids) do
{object_ids, remote_ids} =
ids
|> Enum.map(&resolve_id/1)
|> Enum.reduce({[], []}, fn
nil, acc ->
acc
object_id, {object_ids, remote_ids} when is_integer(object_id) ->
{[object_id | object_ids], remote_ids}
remote_id, {object_ids, remote_ids} when is_binary(remote_id) ->
{object_ids, [remote_id | remote_ids]}
end)
from(co in __MODULE__,
where:
co.collection_id == ^collection_id and
(co.object_id in ^object_ids or co.remote_id in ^remote_ids)
)
|> Repo.delete_all()
end
def get_ids(%Object{id: object_id}) do
from(co in __MODULE__,
left_join: o in Object,
on: o.id == co.object_id,
where: co.collection_id == ^object_id,
order_by: co.order_num,
select: {o.id, o.path, co.remote_id}
)
|> Repo.query()
end
def get_highest_order_num(%Object{id: object_id}) do
from(co in __MODULE__,
where: co.collection_id == ^object_id,
select: max(co.order_num)
)
|> Repo.one()
|> case do
nil -> 0
order_num -> order_num
end
end
def insert(%Object{id: collection_id} = collection_object, object_id, order_num)
when is_integer(object_id) do
next_order_num =
if order_num == nil do
current_highest_order_num = get_highest_order_num(collection_object)
current_highest_order_num + 100
else
order_num
end
cs =
changeset(%__MODULE__{}, %{
collection_id: collection_id,
object_id: object_id,
order_num: next_order_num
})
Repo.insert(cs)
end
def insert(%Object{id: collection_id} = collection_object, remote_id, order_num)
when is_binary(remote_id) do
next_order_num =
if order_num == nil do
current_highest_order_num = get_highest_order_num(collection_object)
current_highest_order_num + 100
else
order_num
end
cs =
changeset(%__MODULE__{}, %{
collection_id: collection_id,
remote_id: remote_id,
order_num: next_order_num
})
Repo.insert(cs)
end
def insert_all(%Object{} = collection_object, ids) when is_list(ids) do
ids = ids |> Enum.map(&resolve_id/1) |> Enum.filter(fn val -> val == nil end)
current_highest_order_num = get_highest_order_num(collection_object)
next_order_num = current_highest_order_num + 100
Enum.reduce(ids, next_order_num, fn id, next_order_num ->
insert(collection_object, id, next_order_num)
next_order_num + 100
end)
end
defp resolve_id(ap_id) when is_binary(ap_id) do
owner_id = Application.get_env(:balls_pds, :owner_ap_id)
case ap_id do
^owner_id <> suffix ->
with {:uri, %URI{:query => query}} <- {:uri, URI.parse(suffix)},
{:rel, %{"relativeRef" => relative_ref, "service" => service}} <-
{:rel, URI.decode_query(query)},
{:service, true} <-
{:service, Application.get_env(:balls_pds, :did_service) == service} do
Object.get_by_path(relative_ref)
else
_ -> nil
end
remote_id ->
remote_id
end
end
end