Added more settings methods; Hooked up initial settings runner to app boot

This commit is contained in:
Kieran Eglin 2024-03-06 12:12:03 -08:00
parent 97f899e11d
commit 970f12792a
No known key found for this signature in database
GPG key ID: 193984967FCF432D
6 changed files with 107 additions and 6 deletions

View file

@ -11,6 +11,7 @@ alias Pinchflat.Tasks
alias Pinchflat.Media
alias Pinchflat.Profiles
alias Pinchflat.Sources
alias Pinchflat.Settings
alias Pinchflat.MediaClient.{SourceDetails, MediaDownloader}
alias Pinchflat.Metadata.{Zipper, ThumbnailFetcher}

View file

@ -10,6 +10,8 @@ defmodule Pinchflat.Application do
children = [
PinchflatWeb.Telemetry,
Pinchflat.Repo,
# {Task, &run_startup_tasks/0},
Pinchflat.StartupTasks,
{Oban, Application.fetch_env!(:pinchflat, Oban)},
{DNSCluster, query: Application.get_env(:pinchflat, :dns_cluster_query) || :ignore},
{Phoenix.PubSub, name: Pinchflat.PubSub},

View file

@ -19,7 +19,8 @@ defmodule Pinchflat.Settings do
@doc """
Creates or updates a setting, returning the parsed value.
Raises if an unsupported datatype is used.
Raises if an unsupported datatype is used. Optionally allows
specifying the datatype.
Returns value in type of `Ecto.Enum.mappings(Setting, :datatype)`
"""
@ -46,6 +47,24 @@ defmodule Pinchflat.Settings do
|> read_setting()
end
@doc """
Attempts to find a setting by name or creates a setting with value
if one doesn't exist, returning the parsed value. Optionally allows
specifying the datatype.
Returns value in type of `Ecto.Enum.mappings(Setting, :datatype)`
"""
def fetch!(name, value) do
fetch!(name, value, infer_datatype(value))
end
def fetch!(name, value, datatype) do
case Repo.get_by(Setting, name: to_string(name)) do
nil -> create_setting!(name, value, datatype)
setting -> read_setting(setting)
end
end
defp change_setting(setting, attrs) do
Setting.changeset(setting, attrs)
end

View file

@ -0,0 +1,36 @@
defmodule Pinchflat.StartupTasks do
@moduledoc """
This module is responsible for running startup tasks on app boot.
It's a GenServer because that plays REALLY nicely with the existing
Phoenix supervision tree.
"""
# restart: :temporary means that this process will never be restarted (ie: will run once and then die)
use GenServer, restart: :temporary
alias Pinchflat.Settings
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, %{}, opts)
end
@doc """
Runs 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
"""
@impl true
def init(state) do
apply_default_settings()
{:ok, state}
end
defp apply_default_settings do
Settings.fetch!(:onboarding, true)
end
end

View file

@ -4,25 +4,32 @@ defmodule Pinchflat.SettingsTest do
alias Pinchflat.Settings
alias Pinchflat.Settings.Setting
# NOTE: We're treating some of these tests differently
# than in other modules because certain settings
# are always created on app boot (including in the test env),
# so we can't treat these like a clean slate.
describe "list_settings/0" do
test "returns all settings" do
Settings.set!("foo", "bar")
assert [_] = Settings.list_settings()
results = Settings.list_settings()
assert Enum.all?(results, fn setting -> match?(%Setting{}, setting) end)
end
end
describe "set/2" do
test "creates a new setting if one does not exist" do
assert Repo.aggregate(Setting, :count, :id) == 0
original = Repo.aggregate(Setting, :count, :id)
Settings.set!("foo", "bar")
assert Repo.aggregate(Setting, :count, :id) == 1
assert Repo.aggregate(Setting, :count, :id) == original + 1
end
test "updates an existing setting if one exists" do
Settings.set!("foo", "bar")
assert Repo.aggregate(Setting, :count, :id) == 1
original = Repo.aggregate(Setting, :count, :id)
Settings.set!("foo", "baz")
assert Repo.aggregate(Setting, :count, :id) == 1
assert Repo.aggregate(Setting, :count, :id) == original
assert Settings.get!("foo") == "baz"
end
@ -78,4 +85,24 @@ defmodule Pinchflat.SettingsTest do
end
end
end
describe "fetch/2" do
test "creates a setting if one doesn't exist" do
original = Repo.aggregate(Setting, :count, :id)
assert Settings.fetch!("foo", "bar") == "bar"
assert Repo.aggregate(Setting, :count, :id) == original + 1
end
test "returns an existing setting if one does exist" do
Settings.set!("foo", "bar")
assert Settings.fetch!("foo", "baz") == "bar"
end
end
describe "fetch/3" do
test "allows manual specification of datatype" do
assert Settings.fetch!("foo", "true", :boolean) == true
end
end
end

View file

@ -0,0 +1,16 @@
defmodule Pinchflat.StartupTasksTest do
use Pinchflat.DataCase
alias Pinchflat.Settings
# Since this runs on app boot (even in the test env),
# any actions in the `init/1` function will already have
# run. So we can only test the side effects of those actions,
# rather than the actions themselves.
describe "apply_default_settings" do
test "sets default settings" do
assert Settings.get!(:onboarding) == true
end
end
end