diff --git a/lib/pinchflat/application.ex b/lib/pinchflat/application.ex index 969f2cb..3f823f4 100644 --- a/lib/pinchflat/application.ex +++ b/lib/pinchflat/application.ex @@ -9,8 +9,12 @@ defmodule Pinchflat.Application do @impl true def start(_type, _args) do check_and_update_timezone() + attach_oban_telemetry() + Logger.add_handlers(:pinchflat) - children = [ + # See https://hexdocs.pm/elixir/Supervisor.html + # for other strategies and supported options + [ Pinchflat.PromEx, PinchflatWeb.Telemetry, Pinchflat.Repo, @@ -24,17 +28,11 @@ defmodule Pinchflat.Application do {Finch, name: Pinchflat.Finch}, # Start a worker by calling: Pinchflat.Worker.start_link(arg) # {Pinchflat.Worker, arg}, - # Start to serve requests, typically the last entry - PinchflatWeb.Endpoint + # Start to serve requests, typically the last entry (except for the post-boot tasks) + PinchflatWeb.Endpoint, + Pinchflat.Boot.PostBootStartupTasks ] - - attach_oban_telemetry() - Logger.add_handlers(:pinchflat) - - # See https://hexdocs.pm/elixir/Supervisor.html - # for other strategies and supported options - opts = [strategy: :one_for_one, name: Pinchflat.Supervisor] - Supervisor.start_link(children, opts) + |> Supervisor.start_link(strategy: :one_for_one, name: Pinchflat.Supervisor) end # Tell Phoenix to update the endpoint configuration diff --git a/lib/pinchflat/boot/post_boot_startup_tasks.ex b/lib/pinchflat/boot/post_boot_startup_tasks.ex new file mode 100644 index 0000000..d6ae6eb --- /dev/null +++ b/lib/pinchflat/boot/post_boot_startup_tasks.ex @@ -0,0 +1,46 @@ +defmodule Pinchflat.Boot.PostBootStartupTasks do + @moduledoc """ + This module is responsible for running startup tasks on app boot + AFTER all other boot steps have taken place and the app is ready to serve requests. + + It's a GenServer because that plays REALLY nicely with the existing + Phoenix supervision tree. + """ + + alias Pinchflat.YtDlp.UpdateWorker, as: YtDlpUpdateWorker + + # restart: :temporary means that this process will never be restarted (ie: will run once and then die) + use GenServer, restart: :temporary + import Ecto.Query, warn: false + + def start_link(opts \\ []) do + GenServer.start_link(__MODULE__, %{env: Application.get_env(:pinchflat, :env)}, opts) + end + + @doc """ + Runs post-boot application startup tasks. + + Any code defined here will run every time the application starts. You must + make sure that the code is idempotent and safe to run multiple times. + + This is a good place to set up default settings, create initial records, stuff like that. + Should be fast - anything with the potential to be slow should be kicked off as a job instead. + """ + @impl true + def init(%{env: :test} = state) do + # Do nothing _as part of the app bootup process_. + # Since bootup calls `start_link` and that's where the `env` state is injected, + # you can still call `.init()` manually to run these tasks for testing purposes + {:ok, state} + end + + def init(state) do + update_yt_dlp() + + {:ok, state} + end + + defp update_yt_dlp do + YtDlpUpdateWorker.kickoff() + end +end diff --git a/test/pinchflat/boot/post_boot_startup_tasks_test.exs b/test/pinchflat/boot/post_boot_startup_tasks_test.exs new file mode 100644 index 0000000..f8fa7f5 --- /dev/null +++ b/test/pinchflat/boot/post_boot_startup_tasks_test.exs @@ -0,0 +1,26 @@ +defmodule Pinchflat.Boot.PostBootStartupTasksTest do + use Pinchflat.DataCase + + # import Pinchflat.JobFixtures + + # alias Pinchflat.Settings + alias Pinchflat.YtDlp.UpdateWorker + alias Pinchflat.Boot.PostBootStartupTasks + + setup do + stub(YtDlpRunnerMock, :update, fn -> {:ok, "1"} end) + stub(YtDlpRunnerMock, :version, fn -> {:ok, "1"} end) + + :ok + end + + describe "update_yt_dlp" do + test "enqueues an update job" do + assert [] = all_enqueued(worker: UpdateWorker) + + PostBootStartupTasks.init(%{}) + + assert [%Oban.Job{}] = all_enqueued(worker: UpdateWorker) + end + end +end