Add settings model (#54)
* Adds a basic settings model * Added more settings methods; Hooked up initial settings runner to app boot * Update onboarding flow to use settings model instead of session data
This commit is contained in:
parent
39caf4a94f
commit
d0f55cd463
18 changed files with 381 additions and 76 deletions
1
.iex.exs
1
.iex.exs
|
|
@ -11,6 +11,7 @@ alias Pinchflat.Tasks
|
||||||
alias Pinchflat.Media
|
alias Pinchflat.Media
|
||||||
alias Pinchflat.Profiles
|
alias Pinchflat.Profiles
|
||||||
alias Pinchflat.Sources
|
alias Pinchflat.Sources
|
||||||
|
alias Pinchflat.Settings
|
||||||
|
|
||||||
alias Pinchflat.MediaClient.{SourceDetails, MediaDownloader}
|
alias Pinchflat.MediaClient.{SourceDetails, MediaDownloader}
|
||||||
alias Pinchflat.Metadata.{Zipper, ThumbnailFetcher}
|
alias Pinchflat.Metadata.{Zipper, ThumbnailFetcher}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ defmodule Pinchflat.Application do
|
||||||
children = [
|
children = [
|
||||||
PinchflatWeb.Telemetry,
|
PinchflatWeb.Telemetry,
|
||||||
Pinchflat.Repo,
|
Pinchflat.Repo,
|
||||||
|
# {Task, &run_startup_tasks/0},
|
||||||
|
Pinchflat.StartupTasks,
|
||||||
{Oban, Application.fetch_env!(:pinchflat, Oban)},
|
{Oban, Application.fetch_env!(:pinchflat, Oban)},
|
||||||
{DNSCluster, query: Application.get_env(:pinchflat, :dns_cluster_query) || :ignore},
|
{DNSCluster, query: Application.get_env(:pinchflat, :dns_cluster_query) || :ignore},
|
||||||
{Phoenix.PubSub, name: Pinchflat.PubSub},
|
{Phoenix.PubSub, name: Pinchflat.PubSub},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule Pinchflat.Profiles.MediaProfile do
|
defmodule Pinchflat.Profiles.MediaProfile do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
A media profile is a set of settings that can be applied to many media sources
|
A media profile is a set of configuration options that can be applied to many media sources
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
|
||||||
95
lib/pinchflat/settings.ex
Normal file
95
lib/pinchflat/settings.ex
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
defmodule Pinchflat.Settings do
|
||||||
|
@moduledoc """
|
||||||
|
The Settings context.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import Ecto.Query, warn: false
|
||||||
|
alias Pinchflat.Repo
|
||||||
|
|
||||||
|
alias Pinchflat.Settings.Setting
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the list of settings.
|
||||||
|
|
||||||
|
Returns [%Setting{}, ...]
|
||||||
|
"""
|
||||||
|
def list_settings do
|
||||||
|
Repo.all(Setting)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Creates or updates a setting, returning the parsed value.
|
||||||
|
Raises if an unsupported datatype is used. Optionally allows
|
||||||
|
specifying the datatype.
|
||||||
|
|
||||||
|
Returns value in type of `Ecto.Enum.mappings(Setting, :datatype)`
|
||||||
|
"""
|
||||||
|
def set!(name, value) do
|
||||||
|
set!(name, value, infer_datatype(value))
|
||||||
|
end
|
||||||
|
|
||||||
|
def set!(name, value, datatype) do
|
||||||
|
# Only create if doesn't exist
|
||||||
|
case Repo.get_by(Setting, name: to_string(name)) do
|
||||||
|
nil -> create_setting!(name, value, datatype)
|
||||||
|
setting -> update_setting!(setting, value, datatype)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets the parsed value of a setting. Raises if the setting does not exist.
|
||||||
|
|
||||||
|
Returns value in type of `Ecto.Enum.mappings(Setting, :datatype)`
|
||||||
|
"""
|
||||||
|
def get!(name) do
|
||||||
|
Setting
|
||||||
|
|> Repo.get_by!(name: to_string(name))
|
||||||
|
|> 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
|
||||||
|
|
||||||
|
defp create_setting!(name, value, datatype) do
|
||||||
|
%Setting{}
|
||||||
|
|> change_setting(%{name: to_string(name), value: to_string(value), datatype: datatype})
|
||||||
|
|> Repo.insert!()
|
||||||
|
|> read_setting()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_setting!(setting, value, datatype) do
|
||||||
|
setting
|
||||||
|
|> change_setting(%{value: to_string(value), datatype: datatype})
|
||||||
|
|> Repo.update!()
|
||||||
|
|> read_setting()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp read_setting(%{value: value, datatype: :string}), do: value
|
||||||
|
defp read_setting(%{value: value, datatype: :boolean}), do: value in ["true", "t", "1"]
|
||||||
|
defp read_setting(%{value: value, datatype: :integer}), do: String.to_integer(value)
|
||||||
|
defp read_setting(%{value: value, datatype: :float}), do: String.to_float(value)
|
||||||
|
|
||||||
|
defp infer_datatype(value) when is_boolean(value), do: :boolean
|
||||||
|
defp infer_datatype(value) when is_integer(value), do: :integer
|
||||||
|
defp infer_datatype(value) when is_float(value), do: :float
|
||||||
|
defp infer_datatype(value) when is_binary(value), do: :string
|
||||||
|
end
|
||||||
24
lib/pinchflat/settings/setting.ex
Normal file
24
lib/pinchflat/settings/setting.ex
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
defmodule Pinchflat.Settings.Setting do
|
||||||
|
@moduledoc """
|
||||||
|
A Setting is a key-value pair with a datatype used to track user-level settings.
|
||||||
|
"""
|
||||||
|
|
||||||
|
use Ecto.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
schema "settings" do
|
||||||
|
field :name, :string
|
||||||
|
field :value, :string
|
||||||
|
field :datatype, Ecto.Enum, values: ~w(boolean string integer float)a
|
||||||
|
|
||||||
|
timestamps(type: :utc_datetime)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def changeset(setting, attrs) do
|
||||||
|
setting
|
||||||
|
|> cast(attrs, [:name, :value, :datatype])
|
||||||
|
|> validate_required([:name, :value, :datatype])
|
||||||
|
|> unique_constraint([:name])
|
||||||
|
end
|
||||||
|
end
|
||||||
36
lib/pinchflat/startup_tasks.ex
Normal file
36
lib/pinchflat/startup_tasks.ex
Normal 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
|
||||||
|
|
@ -45,6 +45,7 @@ defmodule PinchflatWeb do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
import PinchflatWeb.Gettext
|
import PinchflatWeb.Gettext
|
||||||
|
|
||||||
|
alias Pinchflat.Settings
|
||||||
alias PinchflatWeb.Layouts
|
alias PinchflatWeb.Layouts
|
||||||
|
|
||||||
unquote(verified_routes())
|
unquote(verified_routes())
|
||||||
|
|
@ -56,6 +57,8 @@ defmodule PinchflatWeb do
|
||||||
use Phoenix.LiveView,
|
use Phoenix.LiveView,
|
||||||
layout: {PinchflatWeb.Layouts, :app}
|
layout: {PinchflatWeb.Layouts, :app}
|
||||||
|
|
||||||
|
alias Pinchflat.Settings
|
||||||
|
|
||||||
unquote(html_helpers())
|
unquote(html_helpers())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -64,6 +67,8 @@ defmodule PinchflatWeb do
|
||||||
quote do
|
quote do
|
||||||
use Phoenix.LiveComponent
|
use Phoenix.LiveComponent
|
||||||
|
|
||||||
|
alias Pinchflat.Settings
|
||||||
|
|
||||||
unquote(html_helpers())
|
unquote(html_helpers())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -76,6 +81,8 @@ defmodule PinchflatWeb do
|
||||||
import Phoenix.Controller,
|
import Phoenix.Controller,
|
||||||
only: [get_csrf_token: 0, view_module: 1, view_template: 1]
|
only: [get_csrf_token: 0, view_module: 1, view_template: 1]
|
||||||
|
|
||||||
|
alias Pinchflat.Settings
|
||||||
|
|
||||||
# Include general helpers for rendering HTML
|
# Include general helpers for rendering HTML
|
||||||
unquote(html_helpers())
|
unquote(html_helpers())
|
||||||
end
|
end
|
||||||
|
|
@ -93,6 +100,7 @@ defmodule PinchflatWeb do
|
||||||
import PinchflatWeb.CustomComponents.TableComponents
|
import PinchflatWeb.CustomComponents.TableComponents
|
||||||
import PinchflatWeb.CustomComponents.ButtonComponents
|
import PinchflatWeb.CustomComponents.ButtonComponents
|
||||||
|
|
||||||
|
alias Pinchflat.Settings
|
||||||
alias Pinchflat.Utils.StringUtils
|
alias Pinchflat.Utils.StringUtils
|
||||||
|
|
||||||
# Shortcut for generating JS commands
|
# Shortcut for generating JS commands
|
||||||
|
|
|
||||||
|
|
@ -13,29 +13,21 @@ defmodule PinchflatWeb.MediaProfiles.MediaProfileController do
|
||||||
def new(conn, _params) do
|
def new(conn, _params) do
|
||||||
changeset = Profiles.change_media_profile(%MediaProfile{})
|
changeset = Profiles.change_media_profile(%MediaProfile{})
|
||||||
|
|
||||||
if get_session(conn, :onboarding) do
|
render(conn, :new, changeset: changeset, layout: get_onboarding_layout())
|
||||||
render(conn, :new, changeset: changeset, layout: {Layouts, :onboarding})
|
|
||||||
else
|
|
||||||
render(conn, :new, changeset: changeset)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(conn, %{"media_profile" => media_profile_params}) do
|
def create(conn, %{"media_profile" => media_profile_params}) do
|
||||||
case Profiles.create_media_profile(media_profile_params) do
|
case Profiles.create_media_profile(media_profile_params) do
|
||||||
{:ok, media_profile} ->
|
{:ok, media_profile} ->
|
||||||
redirect_location =
|
redirect_location =
|
||||||
if get_session(conn, :onboarding), do: ~p"/?onboarding=1", else: ~p"/media_profiles/#{media_profile}"
|
if Settings.get!(:onboarding), do: ~p"/?onboarding=1", else: ~p"/media_profiles/#{media_profile}"
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, "Media profile created successfully.")
|
|> put_flash(:info, "Media profile created successfully.")
|
||||||
|> redirect(to: redirect_location)
|
|> redirect(to: redirect_location)
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
if get_session(conn, :onboarding) do
|
render(conn, :new, changeset: changeset, layout: get_onboarding_layout())
|
||||||
render(conn, :new, changeset: changeset, layout: {Layouts, :onboarding})
|
|
||||||
else
|
|
||||||
render(conn, :new, changeset: changeset)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -85,4 +77,12 @@ defmodule PinchflatWeb.MediaProfiles.MediaProfileController do
|
||||||
|> put_flash(:info, flash_message)
|
|> put_flash(:info, flash_message)
|
||||||
|> redirect(to: ~p"/media_profiles")
|
|> redirect(to: ~p"/media_profiles")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_onboarding_layout do
|
||||||
|
if Settings.get!(:onboarding) do
|
||||||
|
{Layouts, :onboarding}
|
||||||
|
else
|
||||||
|
{Layouts, :app}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="mb-6 flex gap-3 flex-row items-center">
|
<div class="mb-6 flex gap-3 flex-row items-center">
|
||||||
<.link :if={!Plug.Conn.get_session(@conn, :onboarding)} navigate={~p"/media_profiles"}>
|
<.link :if={!Settings.get!(:onboarding)} navigate={~p"/media_profiles"}>
|
||||||
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
|
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
|
||||||
</.link>
|
</.link>
|
||||||
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Media Profile</h2>
|
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Media Profile</h2>
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@ defmodule PinchflatWeb.Pages.PageController do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp render_home_page(conn) do
|
defp render_home_page(conn) do
|
||||||
|
Settings.set!(:onboarding, false)
|
||||||
media_profile_count = Repo.aggregate(MediaProfile, :count, :id)
|
media_profile_count = Repo.aggregate(MediaProfile, :count, :id)
|
||||||
source_count = Repo.aggregate(Source, :count, :id)
|
source_count = Repo.aggregate(Source, :count, :id)
|
||||||
media_item_count = Repo.aggregate(MediaItem, :count, :id)
|
media_item_count = Repo.aggregate(MediaItem, :count, :id)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_session(:onboarding, false)
|
|
||||||
|> render(:home,
|
|> render(:home,
|
||||||
media_profile_count: media_profile_count,
|
media_profile_count: media_profile_count,
|
||||||
source_count: source_count,
|
source_count: source_count,
|
||||||
|
|
@ -34,8 +34,9 @@ defmodule PinchflatWeb.Pages.PageController do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp render_onboarding_page(conn, media_profiles_exist, sources_exist) do
|
defp render_onboarding_page(conn, media_profiles_exist, sources_exist) do
|
||||||
|
Settings.set!(:onboarding, true)
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_session(:onboarding, true)
|
|
||||||
|> render(:onboarding_checklist,
|
|> render(:onboarding_checklist,
|
||||||
media_profiles_exist: media_profiles_exist,
|
media_profiles_exist: media_profiles_exist,
|
||||||
sources_exist: sources_exist,
|
sources_exist: sources_exist,
|
||||||
|
|
|
||||||
|
|
@ -16,37 +16,29 @@ defmodule PinchflatWeb.Sources.SourceController do
|
||||||
def new(conn, _params) do
|
def new(conn, _params) do
|
||||||
changeset = Sources.change_source(%Source{})
|
changeset = Sources.change_source(%Source{})
|
||||||
|
|
||||||
if get_session(conn, :onboarding) do
|
render(conn, :new,
|
||||||
render(conn, :new,
|
changeset: changeset,
|
||||||
changeset: changeset,
|
media_profiles: media_profiles(),
|
||||||
media_profiles: media_profiles(),
|
layout: get_onboarding_layout()
|
||||||
layout: {Layouts, :onboarding}
|
)
|
||||||
)
|
|
||||||
else
|
|
||||||
render(conn, :new, changeset: changeset, media_profiles: media_profiles())
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(conn, %{"source" => source_params}) do
|
def create(conn, %{"source" => source_params}) do
|
||||||
case Sources.create_source(source_params) do
|
case Sources.create_source(source_params) do
|
||||||
{:ok, source} ->
|
{:ok, source} ->
|
||||||
redirect_location =
|
redirect_location =
|
||||||
if get_session(conn, :onboarding), do: ~p"/?onboarding=1", else: ~p"/sources/#{source}"
|
if Settings.get!(:onboarding), do: ~p"/?onboarding=1", else: ~p"/sources/#{source}"
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_flash(:info, "Source created successfully.")
|
|> put_flash(:info, "Source created successfully.")
|
||||||
|> redirect(to: redirect_location)
|
|> redirect(to: redirect_location)
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
if get_session(conn, :onboarding) do
|
render(conn, :new,
|
||||||
render(conn, :new,
|
changeset: changeset,
|
||||||
changeset: changeset,
|
media_profiles: media_profiles(),
|
||||||
media_profiles: media_profiles(),
|
layout: get_onboarding_layout()
|
||||||
layout: {Layouts, :onboarding}
|
)
|
||||||
)
|
|
||||||
else
|
|
||||||
render(conn, :new, changeset: changeset, media_profiles: media_profiles())
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -107,4 +99,12 @@ defmodule PinchflatWeb.Sources.SourceController do
|
||||||
defp media_profiles do
|
defp media_profiles do
|
||||||
Profiles.list_media_profiles()
|
Profiles.list_media_profiles()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_onboarding_layout do
|
||||||
|
if Settings.get!(:onboarding) do
|
||||||
|
{Layouts, :onboarding}
|
||||||
|
else
|
||||||
|
{Layouts, :app}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="mb-6 flex gap-3 flex-row items-center">
|
<div class="mb-6 flex gap-3 flex-row items-center">
|
||||||
<.link :if={!Plug.Conn.get_session(@conn, :onboarding)} navigate={~p"/sources"}>
|
<.link :if={!Settings.get!(:onboarding)} navigate={~p"/sources"}>
|
||||||
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
|
<.icon name="hero-arrow-left" class="w-10 h-10 hover:dark:text-white" />
|
||||||
</.link>
|
</.link>
|
||||||
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Source</h2>
|
<h2 class="text-title-md2 font-bold text-black dark:text-white ml-4">New Source</h2>
|
||||||
|
|
|
||||||
15
priv/repo/migrations/20240306173305_create_settings.exs
Normal file
15
priv/repo/migrations/20240306173305_create_settings.exs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
defmodule Pinchflat.Repo.Migrations.CreateSettings do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:settings) do
|
||||||
|
add :name, :string, null: false
|
||||||
|
add :value, :string, null: false
|
||||||
|
add :datatype, :string, null: false
|
||||||
|
|
||||||
|
timestamps(type: :utc_datetime)
|
||||||
|
end
|
||||||
|
|
||||||
|
create unique_index(:settings, [:name])
|
||||||
|
end
|
||||||
|
end
|
||||||
108
test/pinchflat/settings_test.exs
Normal file
108
test/pinchflat/settings_test.exs
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
defmodule Pinchflat.SettingsTest do
|
||||||
|
use Pinchflat.DataCase
|
||||||
|
|
||||||
|
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")
|
||||||
|
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
|
||||||
|
original = Repo.aggregate(Setting, :count, :id)
|
||||||
|
Settings.set!("foo", "bar")
|
||||||
|
assert Repo.aggregate(Setting, :count, :id) == original + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates an existing setting if one exists" do
|
||||||
|
Settings.set!("foo", "bar")
|
||||||
|
original = Repo.aggregate(Setting, :count, :id)
|
||||||
|
Settings.set!("foo", "baz")
|
||||||
|
assert Repo.aggregate(Setting, :count, :id) == original
|
||||||
|
assert Settings.get!("foo") == "baz"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns the parsed value" do
|
||||||
|
assert Settings.set!("foo", true) == true
|
||||||
|
assert Settings.set!("foo", false) == false
|
||||||
|
assert Settings.set!("foo", 123) == 123
|
||||||
|
assert Settings.set!("foo", 12.34) == 12.34
|
||||||
|
assert Settings.set!("foo", "bar") == "bar"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "allows for atom keys" do
|
||||||
|
assert Settings.set!(:foo, "bar") == "bar"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "blows up when an unsupported datatype is used" do
|
||||||
|
assert_raise FunctionClauseError, fn ->
|
||||||
|
Settings.set!("foo", nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "set/3" do
|
||||||
|
test "allows manual specification of datatype" do
|
||||||
|
assert Settings.set!("foo", "true", :boolean) == true
|
||||||
|
assert Settings.set!("foo", "false", :boolean) == false
|
||||||
|
assert Settings.set!("foo", "123", :integer) == 123
|
||||||
|
assert Settings.set!("foo", "12.34", :float) == 12.34
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "get/1" do
|
||||||
|
test "returns the value of the setting" do
|
||||||
|
Settings.set!("str", "bar")
|
||||||
|
Settings.set!("bool", true)
|
||||||
|
Settings.set!("int", 123)
|
||||||
|
Settings.set!("float", 12.34)
|
||||||
|
|
||||||
|
assert Settings.get!("str") == "bar"
|
||||||
|
assert Settings.get!("bool") == true
|
||||||
|
assert Settings.get!("int") == 123
|
||||||
|
assert Settings.get!("float") == 12.34
|
||||||
|
end
|
||||||
|
|
||||||
|
test "allows for atom keys" do
|
||||||
|
Settings.set!("str", "bar")
|
||||||
|
assert Settings.get!(:str) == "bar"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "blows up when the setting does not exist" do
|
||||||
|
assert_raise Ecto.NoResultsError, fn ->
|
||||||
|
Settings.get!("foo")
|
||||||
|
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
|
||||||
16
test/pinchflat/startup_tasks_test.exs
Normal file
16
test/pinchflat/startup_tasks_test.exs
Normal 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
|
||||||
|
|
@ -6,6 +6,7 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
||||||
import Pinchflat.ProfilesFixtures
|
import Pinchflat.ProfilesFixtures
|
||||||
|
|
||||||
alias Pinchflat.Repo
|
alias Pinchflat.Repo
|
||||||
|
alias Pinchflat.Settings
|
||||||
|
|
||||||
@create_attrs %{name: "some name", output_path_template: "some output_path_template"}
|
@create_attrs %{name: "some name", output_path_template: "some output_path_template"}
|
||||||
@update_attrs %{
|
@update_attrs %{
|
||||||
|
|
@ -14,6 +15,12 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
||||||
}
|
}
|
||||||
@invalid_attrs %{name: nil, output_path_template: nil}
|
@invalid_attrs %{name: nil, output_path_template: nil}
|
||||||
|
|
||||||
|
setup do
|
||||||
|
Settings.set!(:onboarding, false)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
describe "index" do
|
describe "index" do
|
||||||
test "lists all media_profiles", %{conn: conn} do
|
test "lists all media_profiles", %{conn: conn} do
|
||||||
conn = get(conn, ~p"/media_profiles")
|
conn = get(conn, ~p"/media_profiles")
|
||||||
|
|
@ -27,13 +34,11 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
||||||
assert html_response(conn, 200) =~ "New Media Profile"
|
assert html_response(conn, 200) =~ "New Media Profile"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders correct layout when onboarding", %{session_conn: session_conn} do
|
test "renders correct layout when onboarding", %{conn: conn} do
|
||||||
session_conn =
|
Settings.set!(:onboarding, true)
|
||||||
session_conn
|
conn = get(conn, ~p"/media_profiles/new")
|
||||||
|> put_session(:onboarding, true)
|
|
||||||
|> get(~p"/media_profiles/new")
|
|
||||||
|
|
||||||
refute html_response(session_conn, 200) =~ "MENU"
|
refute html_response(conn, 200) =~ "MENU"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -53,22 +58,18 @@ defmodule PinchflatWeb.MediaProfileControllerTest do
|
||||||
assert html_response(conn, 200) =~ "New Media Profile"
|
assert html_response(conn, 200) =~ "New Media Profile"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "redirects to onboarding when onboarding", %{session_conn: session_conn} do
|
test "redirects to onboarding when onboarding", %{conn: conn} do
|
||||||
session_conn =
|
Settings.set!(:onboarding, true)
|
||||||
session_conn
|
conn = post(conn, ~p"/media_profiles", media_profile: @create_attrs)
|
||||||
|> put_session(:onboarding, true)
|
|
||||||
|> post(~p"/media_profiles", media_profile: @create_attrs)
|
|
||||||
|
|
||||||
assert redirected_to(session_conn) == ~p"/?onboarding=1"
|
assert redirected_to(conn) == ~p"/?onboarding=1"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders correct layout on error when onboarding", %{session_conn: session_conn} do
|
test "renders correct layout on error when onboarding", %{conn: conn} do
|
||||||
session_conn =
|
Settings.set!(:onboarding, true)
|
||||||
session_conn
|
conn = post(conn, ~p"/media_profiles", media_profile: @invalid_attrs)
|
||||||
|> put_session(:onboarding, true)
|
|
||||||
|> post(~p"/media_profiles", media_profile: @invalid_attrs)
|
|
||||||
|
|
||||||
refute html_response(session_conn, 200) =~ "MENU"
|
refute html_response(conn, 200) =~ "MENU"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,12 @@ defmodule PinchflatWeb.PageControllerTest do
|
||||||
import Pinchflat.ProfilesFixtures
|
import Pinchflat.ProfilesFixtures
|
||||||
import Pinchflat.SourcesFixtures
|
import Pinchflat.SourcesFixtures
|
||||||
|
|
||||||
|
alias Pinchflat.Settings
|
||||||
|
|
||||||
describe "GET / when testing onboarding" do
|
describe "GET / when testing onboarding" do
|
||||||
test "sets the onboarding session to true when onboarding", %{conn: conn} do
|
test "sets the onboarding session to true when onboarding", %{conn: conn} do
|
||||||
conn = get(conn, ~p"/")
|
_conn = get(conn, ~p"/")
|
||||||
assert get_session(conn, :onboarding)
|
assert Settings.get!(:onboarding)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "displays the onboarding page when no media profiles exist", %{conn: conn} do
|
test "displays the onboarding page when no media profiles exist", %{conn: conn} do
|
||||||
|
|
@ -32,13 +34,13 @@ defmodule PinchflatWeb.PageControllerTest do
|
||||||
|
|
||||||
test "sets the onboarding session to false when not onboarding", %{conn: conn} do
|
test "sets the onboarding session to false when not onboarding", %{conn: conn} do
|
||||||
conn = get(conn, ~p"/")
|
conn = get(conn, ~p"/")
|
||||||
assert get_session(conn, :onboarding)
|
assert Settings.get!(:onboarding)
|
||||||
|
|
||||||
_ = media_profile_fixture()
|
_ = media_profile_fixture()
|
||||||
_ = source_fixture()
|
_ = source_fixture()
|
||||||
|
|
||||||
conn = get(conn, ~p"/")
|
_conn = get(conn, ~p"/")
|
||||||
refute get_session(conn, :onboarding)
|
refute Settings.get!(:onboarding)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "displays the home page when not onboarding", %{conn: conn} do
|
test "displays the home page when not onboarding", %{conn: conn} do
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,11 @@ defmodule PinchflatWeb.SourceControllerTest do
|
||||||
import Pinchflat.ProfilesFixtures
|
import Pinchflat.ProfilesFixtures
|
||||||
|
|
||||||
alias Pinchflat.Repo
|
alias Pinchflat.Repo
|
||||||
|
alias Pinchflat.Settings
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
media_profile = media_profile_fixture()
|
media_profile = media_profile_fixture()
|
||||||
|
Settings.set!(:onboarding, false)
|
||||||
|
|
||||||
{
|
{
|
||||||
:ok,
|
:ok,
|
||||||
|
|
@ -42,13 +44,11 @@ defmodule PinchflatWeb.SourceControllerTest do
|
||||||
assert html_response(conn, 200) =~ "New Source"
|
assert html_response(conn, 200) =~ "New Source"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders correct layout when onboarding", %{session_conn: session_conn} do
|
test "renders correct layout when onboarding", %{conn: conn} do
|
||||||
session_conn =
|
Settings.set!(:onboarding, true)
|
||||||
session_conn
|
conn = get(conn, ~p"/sources/new")
|
||||||
|> put_session(:onboarding, true)
|
|
||||||
|> get(~p"/sources/new")
|
|
||||||
|
|
||||||
refute html_response(session_conn, 200) =~ "MENU"
|
refute html_response(conn, 200) =~ "MENU"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -69,24 +69,20 @@ defmodule PinchflatWeb.SourceControllerTest do
|
||||||
assert html_response(conn, 200) =~ "New Source"
|
assert html_response(conn, 200) =~ "New Source"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "redirects to onboarding when onboarding", %{session_conn: session_conn, create_attrs: create_attrs} do
|
test "redirects to onboarding when onboarding", %{conn: conn, create_attrs: create_attrs} do
|
||||||
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
|
expect(YtDlpRunnerMock, :run, 1, &runner_function_mock/3)
|
||||||
|
|
||||||
session_conn =
|
Settings.set!(:onboarding, true)
|
||||||
session_conn
|
conn = post(conn, ~p"/sources", source: create_attrs)
|
||||||
|> put_session(:onboarding, true)
|
|
||||||
|> post(~p"/sources", source: create_attrs)
|
|
||||||
|
|
||||||
assert redirected_to(session_conn) == ~p"/?onboarding=1"
|
assert redirected_to(conn) == ~p"/?onboarding=1"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "renders correct layout on error when onboarding", %{session_conn: session_conn, invalid_attrs: invalid_attrs} do
|
test "renders correct layout on error when onboarding", %{conn: conn, invalid_attrs: invalid_attrs} do
|
||||||
session_conn =
|
Settings.set!(:onboarding, true)
|
||||||
session_conn
|
conn = post(conn, ~p"/sources", source: invalid_attrs)
|
||||||
|> put_session(:onboarding, true)
|
|
||||||
|> post(~p"/sources", source: invalid_attrs)
|
|
||||||
|
|
||||||
refute html_response(session_conn, 200) =~ "MENU"
|
refute html_response(conn, 200) =~ "MENU"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue