134 lines
3.8 KiB
Elixir
134 lines
3.8 KiB
Elixir
defmodule BDS.Media.Linking do
|
|
@moduledoc false
|
|
|
|
require Logger
|
|
|
|
import Ecto.Query
|
|
|
|
alias BDS.Media.Media
|
|
alias BDS.Media.Sidecars
|
|
alias BDS.Persistence
|
|
alias BDS.Posts.PostMedia
|
|
alias BDS.Projects
|
|
alias BDS.Repo
|
|
|
|
@spec list_linked_posts(String.t()) ::
|
|
[%{post_id: String.t(), title: String.t(), sort_order: integer()}]
|
|
def list_linked_posts(media_id) when is_binary(media_id) do
|
|
Repo.all(
|
|
from post in BDS.Posts.Post,
|
|
join: pm in PostMedia,
|
|
on: pm.post_id == post.id,
|
|
where: pm.media_id == ^media_id,
|
|
order_by: [asc: pm.sort_order, asc: post.updated_at],
|
|
select: %{
|
|
post_id: post.id,
|
|
title: fragment("COALESCE(?, ?, ?)", post.title, post.slug, post.id),
|
|
sort_order: pm.sort_order
|
|
}
|
|
)
|
|
end
|
|
|
|
@spec link_media_to_post(String.t(), String.t()) ::
|
|
{:ok, :linked} | {:error, :not_found | term()}
|
|
def link_media_to_post(media_id, post_id) when is_binary(media_id) and is_binary(post_id) do
|
|
case {Repo.get(Media, media_id), Repo.get(BDS.Posts.Post, post_id)} do
|
|
{nil, _post} ->
|
|
{:error, :not_found}
|
|
|
|
{_media, nil} ->
|
|
{:error, :not_found}
|
|
|
|
{%Media{} = media, %BDS.Posts.Post{} = post} ->
|
|
project = Projects.get_project!(media.project_id)
|
|
|
|
case Repo.transaction(fn ->
|
|
if Repo.exists?(
|
|
from pm in PostMedia,
|
|
where: pm.post_id == ^post.id and pm.media_id == ^media.id
|
|
) do
|
|
:already_linked
|
|
else
|
|
sort_order = next_sort_order(media.id)
|
|
|
|
%PostMedia{}
|
|
|> PostMedia.changeset(%{
|
|
id: Ecto.UUID.generate(),
|
|
project_id: media.project_id,
|
|
post_id: post.id,
|
|
media_id: media.id,
|
|
sort_order: sort_order,
|
|
created_at: Persistence.now_ms()
|
|
})
|
|
|> Repo.insert!()
|
|
|
|
:linked
|
|
end
|
|
end) do
|
|
{:ok, _result} ->
|
|
log_sidecar_error(Sidecars.write_sidecar(project, media), media.id)
|
|
{:ok, :linked}
|
|
|
|
{:error, reason} ->
|
|
{:error, reason}
|
|
end
|
|
end
|
|
end
|
|
|
|
@spec unlink_media_from_post(String.t(), String.t()) ::
|
|
{:ok, :unlinked} | {:error, :not_found | term()}
|
|
def unlink_media_from_post(media_id, post_id) when is_binary(media_id) and is_binary(post_id) do
|
|
case Repo.get(Media, media_id) do
|
|
nil ->
|
|
{:error, :not_found}
|
|
|
|
%Media{} = media ->
|
|
project = Projects.get_project!(media.project_id)
|
|
|
|
case Repo.transaction(fn ->
|
|
{_count, _} =
|
|
Repo.delete_all(
|
|
from pm in PostMedia,
|
|
where: pm.media_id == ^media.id and pm.post_id == ^post_id
|
|
)
|
|
|
|
:ok
|
|
end) do
|
|
{:ok, :ok} ->
|
|
log_sidecar_error(Sidecars.write_sidecar(project, media), media.id)
|
|
{:ok, :unlinked}
|
|
|
|
{:error, reason} ->
|
|
{:error, reason}
|
|
end
|
|
end
|
|
end
|
|
|
|
@spec linked_post_ids(String.t()) :: [String.t()]
|
|
def linked_post_ids(media_id) do
|
|
Repo.all(
|
|
from pm in PostMedia,
|
|
where: pm.media_id == ^media_id,
|
|
order_by: [asc: pm.sort_order, asc: pm.post_id],
|
|
select: pm.post_id
|
|
)
|
|
end
|
|
|
|
defp log_sidecar_error(:ok, _media_id), do: :ok
|
|
|
|
defp log_sidecar_error({:error, reason}, media_id) do
|
|
Logger.warning("Sidecar write failed for media #{media_id}: #{inspect(reason)}")
|
|
end
|
|
|
|
defp next_sort_order(media_id) do
|
|
case Repo.one(
|
|
from pm in PostMedia,
|
|
where: pm.media_id == ^media_id,
|
|
select: max(pm.sort_order)
|
|
) do
|
|
value when is_integer(value) -> value + 1
|
|
_other -> 0
|
|
end
|
|
end
|
|
end
|