From 4584d060b8231938ae9370f695de2c68fd604be1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20W=C3=B6ginger?= Date: Sat, 7 Dec 2024 18:45:23 +0100 Subject: [PATCH] a show and an episode automatically get virtual nodes assigned. these are created when a show and an episode are created --- lib/radiator/outline.ex | 8 +++- lib/radiator/outline/node_repository.ex | 37 +++++++++++++++++-- lib/radiator/podcast.ex | 36 +++++++++++++++--- lib/radiator/podcast/show.ex | 14 ++++++- ...41207181658_add_virtual_nodes_to_shows.exs | 13 +++++++ test/radiator/podcast_test.exs | 11 ++++-- 6 files changed, 101 insertions(+), 18 deletions(-) create mode 100644 priv/repo/migrations/20241207181658_add_virtual_nodes_to_shows.exs diff --git a/lib/radiator/outline.ex b/lib/radiator/outline.ex index 81e8d5b4..ca25a48c 100644 --- a/lib/radiator/outline.ex +++ b/lib/radiator/outline.ex @@ -482,10 +482,14 @@ defmodule Radiator.Outline do Repo.transaction(fn -> old_next_node = - NodeRepository.get_node_by_parent_and_prev(get_node_id(parent_node), node.uuid) + NodeRepository.get_node_by_parent_and_prev( + get_node_id(parent_node), + node.uuid, + node.episode_id + ) new_next_node = - NodeRepository.get_node_by_parent_and_prev(new_parent_id, new_prev_id) + NodeRepository.get_node_by_parent_and_prev(new_parent_id, new_prev_id, node.episode_id) {:ok, node} = NodeRepository.move_node_if(node, new_parent_id, new_prev_id) diff --git a/lib/radiator/outline/node_repository.ex b/lib/radiator/outline/node_repository.ex index fc12c84d..95d4d570 100644 --- a/lib/radiator/outline/node_repository.ex +++ b/lib/radiator/outline/node_repository.ex @@ -26,6 +26,31 @@ defmodule Radiator.Outline.NodeRepository do |> Repo.insert() end + @doc """ + Creates the internal nodes for a show, this is the global root + and the global inbox. + """ + def create_virtual_nodes_for_show(show_id) do + # create a root node for a show + {:ok, show_root} = + create_node(%{ + show_id: show_id, + parent_id: nil, + prev_id: nil, + _type: "global_root" + }) + + {:ok, global_inbox} = + create_node(%{ + show_id: show_id, + parent_id: show_root.uuid, + prev_id: nil, + _type: "global_inbox" + }) + + {show_root, global_inbox} + end + @doc """ Deletes a node from the repository. @@ -150,17 +175,21 @@ defmodule Radiator.Outline.NodeRepository do end @doc """ - Gets a single node defined by the given prev_id and parent_id. + Gets a single node defined by the given prev_id and parent_id and the + episode id. + TODO: episode id should become a general container id (outline_tree_id) + Returns `nil` if the Node cannot be found. ## Examples - iex> get_node_by_parent_and_prev("5adf3b360fb0", "380d56cf") + iex> get_node_by_parent_and_prev("5adf3b360fb0", "380d56cf", 23) nil - iex> get_node_by_parent_and_prev("5e3f5a0422a4", "b78a976d") + iex> get_node_by_parent_and_prev("5e3f5a0422a4", "b78a976d", 23) %Node{uuid: "33b2a1dac9b1", parent_id: "5e3f5a0422a4", prev_id: "b78a976d"} """ - def get_node_by_parent_and_prev(parent_id, prev_id) do + def get_node_by_parent_and_prev(parent_id, prev_id, episode_id) do Node + |> where(episode_id: ^episode_id) |> where_prev_node_equals(prev_id) |> where_parent_node_equals(parent_id) |> Repo.one() diff --git a/lib/radiator/podcast.ex b/lib/radiator/podcast.ex index e840eaaa..44388f26 100644 --- a/lib/radiator/podcast.ex +++ b/lib/radiator/podcast.ex @@ -5,9 +5,9 @@ defmodule Radiator.Podcast do """ import Ecto.Query, warn: false - alias Radiator.Repo - + alias Radiator.Outline.NodeRepository alias Radiator.Podcast.{Episode, Network, Show, ShowHosts} + alias Radiator.Repo @doc """ Returns the list of networks. @@ -182,9 +182,33 @@ defmodule Radiator.Podcast do """ def create_show(attrs \\ %{}) do - %Show{} - |> Show.changeset(attrs) - |> Repo.insert() + # also need to create the nodes for the show + # start a transaction = + Repo.transaction(fn -> + result = + %Show{} + |> Show.changeset(attrs) + |> Repo.insert() + + case result do + {:ok, show} -> + # create the nodes for the show + {show_root, global_inbox} = NodeRepository.create_virtual_nodes_for_show(show.id) + + {:ok, show} = + show + |> Show.changeset_tree(%{ + global_root_id: show_root.uuid, + global_inbox_id: global_inbox.uuid + }) + |> Repo.update() + + show + + {:error, changeset} -> + Repo.rollback(changeset) + end + end) end @doc """ @@ -314,7 +338,7 @@ defmodule Radiator.Podcast do """ def delete_show(%Show{} = show) do - Repo.delete(show) + Repo.delete(show, allow_stale: true) end @doc """ diff --git a/lib/radiator/podcast/show.ex b/lib/radiator/podcast/show.ex index 1bd5fe3c..29bbf101 100644 --- a/lib/radiator/podcast/show.ex +++ b/lib/radiator/podcast/show.ex @@ -14,9 +14,10 @@ defmodule Radiator.Podcast.Show do field :description, :string belongs_to :network, Network - + belongs_to :global_root, Node, type: :binary_id, references: :uuid + belongs_to :global_inbox, Node, type: :binary_id, references: :uuid has_many(:episodes, Episode) - has_many(:outline_nodes, Node) + has_many(:outline_nodes, Node, on_delete: :delete_all) many_to_many(:hosts, User, join_through: "show_hosts") timestamps(type: :utc_datetime) @@ -28,4 +29,13 @@ defmodule Radiator.Podcast.Show do |> cast(attrs, [:title, :description, :network_id]) |> validate_required([:title]) end + + @doc """ + changeset for updating the show's root and inbox nodes + """ + def changeset_tree(show, attrs) do + show + |> cast(attrs, [:global_root_id, :global_inbox_id]) + |> validate_required([:global_root_id, :global_inbox_id]) + end end diff --git a/priv/repo/migrations/20241207181658_add_virtual_nodes_to_shows.exs b/priv/repo/migrations/20241207181658_add_virtual_nodes_to_shows.exs new file mode 100644 index 00000000..945334db --- /dev/null +++ b/priv/repo/migrations/20241207181658_add_virtual_nodes_to_shows.exs @@ -0,0 +1,13 @@ +defmodule Radiator.Repo.Migrations.AddVirtualNodesToShows do + use Ecto.Migration + + def change do + alter table(:shows) do + add :global_root_id, + references(:outline_nodes, on_delete: :delete_all, type: :binary_id, column: :uuid) + + add :global_inbox_id, + references(:outline_nodes, on_delete: :delete_all, type: :binary_id, column: :uuid) + end + end +end diff --git a/test/radiator/podcast_test.exs b/test/radiator/podcast_test.exs index cfa37071..d59a6af8 100644 --- a/test/radiator/podcast_test.exs +++ b/test/radiator/podcast_test.exs @@ -17,9 +17,10 @@ defmodule Radiator.PodcastTest do test "list_networks/1 returns all networks with preloaded shows" do show = show_fixture() + show_id = show.id assert [%Network{shows: shows}] = Podcast.list_networks(preload: :shows) - assert shows == [show] + assert [%Show{id: ^show_id}] = shows end test "get_network!/1 returns the network with given id" do @@ -69,12 +70,13 @@ defmodule Radiator.PodcastTest do test "list_shows/0 returns all shows" do show = show_fixture() - assert Podcast.list_shows() == [show] + show_id = show.id + assert [%Show{id: ^show_id}] = Podcast.list_shows() end test "get_show!/1 returns the show with given id" do show = show_fixture() - assert Podcast.get_show!(show.id) == show + assert Podcast.get_show!(show.id).id == show.id end test "get_show!/2 returns the show with preloaded episodes" do @@ -137,8 +139,9 @@ defmodule Radiator.PodcastTest do test "update_show/2 with invalid data returns error changeset" do show = show_fixture() + show_id = show.id assert {:error, %Ecto.Changeset{}} = Podcast.update_show(show, @invalid_attrs) - assert show == Podcast.get_show!(show.id) + assert %Show{id: ^show_id} = Podcast.get_show!(show.id) end test "update_show/2 with valid data updates the show by removing hosts" do