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