From 29155137fdae15fccfaa68fb9c954e98078ce0c4 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Thu, 16 Jan 2020 08:50:27 +0300 Subject: renaming Pleroma.Web.AdminAPI.Config -> Pleroma.ConfigDB --- lib/mix/tasks/pleroma/config.ex | 8 +- lib/pleroma/config/config_db.ex | 312 +++++++++++ lib/pleroma/config/transfer_task.ex | 14 +- lib/pleroma/web/admin_api/admin_api_controller.ex | 8 +- lib/pleroma/web/admin_api/config.ex | 312 ----------- lib/pleroma/web/admin_api/views/config_view.ex | 2 +- test/config/config_db_test.exs | 651 ++++++++++++++++++++++ test/config/transfer_task_test.exs | 10 +- test/support/factory.ex | 2 +- test/tasks/config_test.exs | 30 +- test/web/admin_api/admin_api_controller_test.exs | 8 +- test/web/admin_api/config_test.exs | 649 --------------------- 12 files changed, 1004 insertions(+), 1002 deletions(-) create mode 100644 lib/pleroma/config/config_db.ex delete mode 100644 lib/pleroma/web/admin_api/config.ex create mode 100644 test/config/config_db_test.exs delete mode 100644 test/web/admin_api/config_test.exs diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index 92487dd51..57952aeba 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -5,8 +5,8 @@ defmodule Mix.Tasks.Pleroma.Config do use Mix.Task import Mix.Pleroma + alias Pleroma.ConfigDB alias Pleroma.Repo - alias Pleroma.Web.AdminAPI.Config @shortdoc "Manages the location of the config" @moduledoc File.read!("docs/administration/CLI_tasks/config.md") @@ -55,7 +55,7 @@ defmodule Mix.Tasks.Pleroma.Config do {:ok, file} <- File.open(config_path, [:write, :utf8]) do IO.write(file, "use Mix.Config\r\n") - Config + ConfigDB |> Repo.all() |> Enum.each(&write_to_file_with_deletion(&1, file, opts[:delete_from_db])) @@ -81,7 +81,7 @@ defmodule Mix.Tasks.Pleroma.Config do end) |> Enum.each(fn {key, value} -> key = inspect(key) - {:ok, _} = Config.update_or_create(%{group: inspect(group), key: key, value: value}) + {:ok, _} = ConfigDB.update_or_create(%{group: inspect(group), key: key, value: value}) Mix.shell().info("settings for key #{key} migrated.") end) @@ -93,7 +93,7 @@ defmodule Mix.Tasks.Pleroma.Config do IO.write( file, "config #{config.group}, #{config.key}, #{ - inspect(Config.from_binary(config.value), limit: :infinity) + inspect(ConfigDB.from_binary(config.value), limit: :infinity) }\r\n\r\n" ) diff --git a/lib/pleroma/config/config_db.ex b/lib/pleroma/config/config_db.ex new file mode 100644 index 000000000..e433ce442 --- /dev/null +++ b/lib/pleroma/config/config_db.ex @@ -0,0 +1,312 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ConfigDB do + use Ecto.Schema + import Ecto.Changeset + import Pleroma.Web.Gettext + alias __MODULE__ + alias Pleroma.Repo + + @type t :: %__MODULE__{} + + schema "config" do + field(:key, :string) + field(:group, :string) + field(:value, :binary) + + timestamps() + end + + @spec get_by_params(map()) :: ConfigDB.t() | nil + def get_by_params(params), do: Repo.get_by(ConfigDB, params) + + @spec changeset(ConfigDB.t(), map()) :: Changeset.t() + def changeset(config, params \\ %{}) do + params = Map.put(params, :value, transform(params[:value])) + + config + |> cast(params, [:key, :group, :value]) + |> validate_required([:key, :group, :value]) + |> unique_constraint(:key, name: :config_group_key_index) + end + + @spec create(map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()} + def create(params) do + %ConfigDB{} + |> changeset(params) + |> Repo.insert() + end + + @spec update(ConfigDB.t(), map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()} + def update(%ConfigDB{} = config, %{value: value}) do + config + |> changeset(%{value: value}) + |> Repo.update() + end + + @full_key_update [ + {:pleroma, :ecto_repos}, + {:pleroma, :assets}, + {:quack, :meta}, + {:mime, :types}, + {:cors_plug, [:max_age, :methods, :expose, :headers]}, + {:auto_linker, :opts}, + {:swarm, :node_blacklist}, + {:logger, :backends} + ] + + defp only_full_update?(%ConfigDB{} = config) do + config_group = ConfigDB.from_string(config.group) + config_key = ConfigDB.from_string(config.key) + + Enum.any?(@full_key_update, fn + {group, key} when is_list(key) -> + config_group == group and config_key in key + + {group, key} -> + config_group == group and config_key == key + end) + end + + defp can_be_partially_updated?(%ConfigDB{} = config), do: not only_full_update?(config) + + @spec update_or_create(map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()} + def update_or_create(params) do + search_opts = Map.take(params, [:group, :key]) + + with %ConfigDB{} = config <- ConfigDB.get_by_params(search_opts), + {:partial_update, true, config} <- + {:partial_update, can_be_partially_updated?(config), config}, + old_value <- from_binary(config.value), + transformed_value <- do_transform(params[:value]), + {:can_be_merged, true, config} <- {:can_be_merged, is_list(transformed_value), config}, + new_value <- DeepMerge.deep_merge(old_value, transformed_value) do + ConfigDB.update(config, %{value: new_value, transformed?: true}) + else + {reason, false, config} when reason in [:partial_update, :can_be_merged] -> + ConfigDB.update(config, params) + + nil -> + ConfigDB.create(params) + end + end + + @spec delete(map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()} | {:ok, nil} + def delete(params) do + search_opts = Map.delete(params, :subkeys) + + with %ConfigDB{} = config <- ConfigDB.get_by_params(search_opts), + {config, sub_keys} when is_list(sub_keys) <- {config, params[:subkeys]}, + old_value <- from_binary(config.value), + keys <- Enum.map(sub_keys, &do_transform_string(&1)), + new_value <- Keyword.drop(old_value, keys) do + ConfigDB.update(config, %{value: new_value}) + else + {config, nil} -> + Repo.delete(config) + {:ok, nil} + + nil -> + err = + dgettext("errors", "Config with params %{params} not found", params: inspect(params)) + + {:error, err} + end + end + + @spec from_binary(binary()) :: term() + def from_binary(binary), do: :erlang.binary_to_term(binary) + + @spec from_binary_with_convert(binary()) :: any() + def from_binary_with_convert(binary) do + binary + |> from_binary() + |> do_convert() + end + + @spec from_string(String.t()) :: atom() | no_return() + def from_string(":" <> entity), do: String.to_existing_atom(entity) + + def from_string(entity) when is_binary(entity) do + if is_module_name?(entity) do + String.to_existing_atom("Elixir.#{entity}") + else + entity + end + end + + defp do_convert(entity) when is_list(entity) do + for v <- entity, into: [], do: do_convert(v) + end + + defp do_convert(%Regex{} = entity), do: inspect(entity) + + defp do_convert(entity) when is_map(entity) do + for {k, v} <- entity, into: %{}, do: {do_convert(k), do_convert(v)} + end + + defp do_convert({:proxy_url, {type, :localhost, port}}) do + %{"tuple" => [":proxy_url", %{"tuple" => [do_convert(type), "localhost", port]}]} + end + + defp do_convert({:proxy_url, {type, host, port}}) when is_tuple(host) do + ip = + host + |> :inet_parse.ntoa() + |> to_string() + + %{ + "tuple" => [ + ":proxy_url", + %{"tuple" => [do_convert(type), ip, port]} + ] + } + end + + defp do_convert({:proxy_url, {type, host, port}}) do + %{ + "tuple" => [ + ":proxy_url", + %{"tuple" => [do_convert(type), to_string(host), port]} + ] + } + end + + defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]} + # TODO: will become useless after removing hackney + defp do_convert({:partial_chain, entity}), do: %{"tuple" => [":partial_chain", inspect(entity)]} + + defp do_convert(entity) when is_tuple(entity), + do: %{"tuple" => do_convert(Tuple.to_list(entity))} + + defp do_convert(entity) when is_boolean(entity) or is_number(entity) or is_nil(entity), + do: entity + + defp do_convert(entity) + when is_atom(entity) and entity in [:"tlsv1.1", :"tlsv1.2", :"tlsv1.3"] do + ":#{to_string(entity)}" + end + + defp do_convert(entity) when is_atom(entity), do: inspect(entity) + + defp do_convert(entity) when is_binary(entity), do: entity + + @spec transform(any()) :: binary() | no_return() + def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity) do + entity + |> do_transform() + |> to_binary() + end + + def transform(entity), do: to_binary(entity) + + @spec to_binary(any()) :: binary() + def to_binary(entity), do: :erlang.term_to_binary(entity) + + defp do_transform(%Regex{} = entity), do: entity + + defp do_transform(%{"tuple" => [":proxy_url", %{"tuple" => [type, host, port]}]}) do + {:proxy_url, {do_transform_string(type), parse_host(host), port}} + end + + defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do + {dispatch_settings, []} = do_eval(entity) + {:dispatch, [dispatch_settings]} + end + + # TODO: will become useless after removing hackney + defp do_transform(%{"tuple" => [":partial_chain", entity]}) do + {partial_chain, []} = do_eval(entity) + {:partial_chain, partial_chain} + end + + defp do_transform(%{"tuple" => entity}) do + Enum.reduce(entity, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end) + end + + defp do_transform(entity) when is_map(entity) do + for {k, v} <- entity, into: %{}, do: {do_transform(k), do_transform(v)} + end + + defp do_transform(entity) when is_list(entity) do + for v <- entity, into: [], do: do_transform(v) + end + + defp do_transform(entity) when is_binary(entity) do + entity + |> String.trim() + |> do_transform_string() + end + + defp do_transform(entity), do: entity + + defp parse_host("localhost"), do: :localhost + + defp parse_host(host) do + charlist = to_charlist(host) + + case :inet.parse_address(charlist) do + {:error, :einval} -> + charlist + + {:ok, ip} -> + ip + end + end + + @delimiters ["/", "|", "\"", "'", {"(", ")"}, {"[", "]"}, {"{", "}"}, {"<", ">"}] + + defp find_valid_delimiter([], _string, _), + do: raise(ArgumentError, message: "valid delimiter for Regex expression not found") + + defp find_valid_delimiter([{leading, closing} = delimiter | others], pattern, regex_delimiter) + when is_tuple(delimiter) do + if String.contains?(pattern, closing) do + find_valid_delimiter(others, pattern, regex_delimiter) + else + {:ok, {leading, closing}} + end + end + + defp find_valid_delimiter([delimiter | others], pattern, regex_delimiter) do + if String.contains?(pattern, delimiter) do + find_valid_delimiter(others, pattern, regex_delimiter) + else + {:ok, {delimiter, delimiter}} + end + end + + @regex_parts ~r/^~r(?'delimiter'[\/|"'([{<]{1})(?'pattern'.+)[\/|"')\]}>]{1}(?'modifier'[uismxfU]*)/u + + defp do_transform_string("~r" <> _pattern = regex) do + with %{"modifier" => modifier, "pattern" => pattern, "delimiter" => regex_delimiter} <- + Regex.named_captures(@regex_parts, regex), + {:ok, {leading, closing}} <- find_valid_delimiter(@delimiters, pattern, regex_delimiter), + {result, _} <- Code.eval_string("~r#{leading}#{pattern}#{closing}#{modifier}") do + result + end + end + + defp do_transform_string(":" <> atom), do: String.to_atom(atom) + + defp do_transform_string(value) do + if is_module_name?(value) do + String.to_existing_atom("Elixir." <> value) + else + value + end + end + + @spec is_module_name?(String.t()) :: boolean() + def is_module_name?(string) do + Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Quack)\./, string) or + string in ["Oban", "Ueberauth", "ExSyslogger"] + end + + defp do_eval(entity) do + cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "") + Code.eval_string(cleaned_string) + end +end diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index 56abfe2a5..b39f9594e 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -5,10 +5,10 @@ defmodule Pleroma.Config.TransferTask do use Task - require Logger - + alias Pleroma.ConfigDB alias Pleroma.Repo - alias Pleroma.Web.AdminAPI.Config + + require Logger def start_link(_) do load_and_update_env() @@ -21,7 +21,7 @@ defmodule Pleroma.Config.TransferTask do true <- Ecto.Adapters.SQL.table_exists?(Repo, "config"), started_applications <- Application.started_applications() do # We need to restart applications for loaded settings take effect - Config + ConfigDB |> Repo.all() |> Enum.map(&update_env(&1)) |> Enum.uniq() @@ -33,9 +33,9 @@ defmodule Pleroma.Config.TransferTask do defp update_env(setting) do try do - key = Config.from_string(setting.key) - group = Config.from_string(setting.group) - value = Config.from_binary(setting.value) + key = ConfigDB.from_string(setting.key) + group = ConfigDB.from_string(setting.group) + value = ConfigDB.from_binary(setting.value) if group != :phoenix and key != :serve_endpoints do :ok = Application.put_env(group, key, value) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 99fcc080c..e1d16c30b 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do import Pleroma.Web.ControllerHelper, only: [json_response: 3] alias Pleroma.Activity + alias Pleroma.ConfigDB alias Pleroma.ModerationLog alias Pleroma.Plugs.OAuthScopesPlug alias Pleroma.ReportNote @@ -17,7 +18,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.AdminAPI.AccountView - alias Pleroma.Web.AdminAPI.Config alias Pleroma.Web.AdminAPI.ConfigView alias Pleroma.Web.AdminAPI.ModerationLogView alias Pleroma.Web.AdminAPI.Report @@ -799,7 +799,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do def config_show(conn, _params) do with :ok <- configurable_from_database(conn) do - configs = Pleroma.Repo.all(Config) + configs = Pleroma.Repo.all(ConfigDB) if configs == [] do errors( @@ -820,13 +820,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do Enum.map(configs, fn %{"group" => group, "key" => key, "delete" => true} = params -> with {:ok, config} <- - Config.delete(%{group: group, key: key, subkeys: params["subkeys"]}) do + ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]}) do config end %{"group" => group, "key" => key, "value" => value} -> with {:ok, config} <- - Config.update_or_create(%{group: group, key: key, value: value}) do + ConfigDB.update_or_create(%{group: group, key: key, value: value}) do config end end) diff --git a/lib/pleroma/web/admin_api/config.ex b/lib/pleroma/web/admin_api/config.ex deleted file mode 100644 index 49f0f8223..000000000 --- a/lib/pleroma/web/admin_api/config.ex +++ /dev/null @@ -1,312 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.Config do - use Ecto.Schema - import Ecto.Changeset - import Pleroma.Web.Gettext - alias __MODULE__ - alias Pleroma.Repo - - @type t :: %__MODULE__{} - - schema "config" do - field(:key, :string) - field(:group, :string) - field(:value, :binary) - - timestamps() - end - - @spec get_by_params(map()) :: Config.t() | nil - def get_by_params(params), do: Repo.get_by(Config, params) - - @spec changeset(Config.t(), map()) :: Changeset.t() - def changeset(config, params \\ %{}) do - params = Map.put(params, :value, transform(params[:value])) - - config - |> cast(params, [:key, :group, :value]) - |> validate_required([:key, :group, :value]) - |> unique_constraint(:key, name: :config_group_key_index) - end - - @spec create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} - def create(params) do - %Config{} - |> changeset(params) - |> Repo.insert() - end - - @spec update(Config.t(), map()) :: {:ok, Config} | {:error, Changeset.t()} - def update(%Config{} = config, %{value: value}) do - config - |> changeset(%{value: value}) - |> Repo.update() - end - - @full_key_update [ - {:pleroma, :ecto_repos}, - {:pleroma, :assets}, - {:quack, :meta}, - {:mime, :types}, - {:cors_plug, [:max_age, :methods, :expose, :headers]}, - {:auto_linker, :opts}, - {:swarm, :node_blacklist}, - {:logger, :backends} - ] - - defp only_full_update?(%Config{} = config) do - config_group = Config.from_string(config.group) - config_key = Config.from_string(config.key) - - Enum.any?(@full_key_update, fn - {group, key} when is_list(key) -> - config_group == group and config_key in key - - {group, key} -> - config_group == group and config_key == key - end) - end - - defp can_be_partially_updated?(%Config{} = config), do: not only_full_update?(config) - - @spec update_or_create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} - def update_or_create(params) do - search_opts = Map.take(params, [:group, :key]) - - with %Config{} = config <- Config.get_by_params(search_opts), - {:partial_update, true, config} <- - {:partial_update, can_be_partially_updated?(config), config}, - old_value <- from_binary(config.value), - transformed_value <- do_transform(params[:value]), - {:can_be_merged, true, config} <- {:can_be_merged, is_list(transformed_value), config}, - new_value <- DeepMerge.deep_merge(old_value, transformed_value) do - Config.update(config, %{value: new_value, transformed?: true}) - else - {reason, false, config} when reason in [:partial_update, :can_be_merged] -> - Config.update(config, params) - - nil -> - Config.create(params) - end - end - - @spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} | {:ok, nil} - def delete(params) do - search_opts = Map.delete(params, :subkeys) - - with %Config{} = config <- Config.get_by_params(search_opts), - {config, sub_keys} when is_list(sub_keys) <- {config, params[:subkeys]}, - old_value <- from_binary(config.value), - keys <- Enum.map(sub_keys, &do_transform_string(&1)), - new_value <- Keyword.drop(old_value, keys) do - Config.update(config, %{value: new_value}) - else - {config, nil} -> - Repo.delete(config) - {:ok, nil} - - nil -> - err = - dgettext("errors", "Config with params %{params} not found", params: inspect(params)) - - {:error, err} - end - end - - @spec from_binary(binary()) :: term() - def from_binary(binary), do: :erlang.binary_to_term(binary) - - @spec from_binary_with_convert(binary()) :: any() - def from_binary_with_convert(binary) do - binary - |> from_binary() - |> do_convert() - end - - @spec from_string(String.t()) :: atom() | no_return() - def from_string(":" <> entity), do: String.to_existing_atom(entity) - - def from_string(entity) when is_binary(entity) do - if is_module_name?(entity) do - String.to_existing_atom("Elixir.#{entity}") - else - entity - end - end - - defp do_convert(entity) when is_list(entity) do - for v <- entity, into: [], do: do_convert(v) - end - - defp do_convert(%Regex{} = entity), do: inspect(entity) - - defp do_convert(entity) when is_map(entity) do - for {k, v} <- entity, into: %{}, do: {do_convert(k), do_convert(v)} - end - - defp do_convert({:proxy_url, {type, :localhost, port}}) do - %{"tuple" => [":proxy_url", %{"tuple" => [do_convert(type), "localhost", port]}]} - end - - defp do_convert({:proxy_url, {type, host, port}}) when is_tuple(host) do - ip = - host - |> :inet_parse.ntoa() - |> to_string() - - %{ - "tuple" => [ - ":proxy_url", - %{"tuple" => [do_convert(type), ip, port]} - ] - } - end - - defp do_convert({:proxy_url, {type, host, port}}) do - %{ - "tuple" => [ - ":proxy_url", - %{"tuple" => [do_convert(type), to_string(host), port]} - ] - } - end - - defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]} - # TODO: will become useless after removing hackney - defp do_convert({:partial_chain, entity}), do: %{"tuple" => [":partial_chain", inspect(entity)]} - - defp do_convert(entity) when is_tuple(entity), - do: %{"tuple" => do_convert(Tuple.to_list(entity))} - - defp do_convert(entity) when is_boolean(entity) or is_number(entity) or is_nil(entity), - do: entity - - defp do_convert(entity) - when is_atom(entity) and entity in [:"tlsv1.1", :"tlsv1.2", :"tlsv1.3"] do - ":#{to_string(entity)}" - end - - defp do_convert(entity) when is_atom(entity), do: inspect(entity) - - defp do_convert(entity) when is_binary(entity), do: entity - - @spec transform(any()) :: binary() | no_return() - def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity) do - entity - |> do_transform() - |> to_binary() - end - - def transform(entity), do: to_binary(entity) - - @spec to_binary(any()) :: binary() - def to_binary(entity), do: :erlang.term_to_binary(entity) - - defp do_transform(%Regex{} = entity), do: entity - - defp do_transform(%{"tuple" => [":proxy_url", %{"tuple" => [type, host, port]}]}) do - {:proxy_url, {do_transform_string(type), parse_host(host), port}} - end - - defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do - {dispatch_settings, []} = do_eval(entity) - {:dispatch, [dispatch_settings]} - end - - # TODO: will become useless after removing hackney - defp do_transform(%{"tuple" => [":partial_chain", entity]}) do - {partial_chain, []} = do_eval(entity) - {:partial_chain, partial_chain} - end - - defp do_transform(%{"tuple" => entity}) do - Enum.reduce(entity, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end) - end - - defp do_transform(entity) when is_map(entity) do - for {k, v} <- entity, into: %{}, do: {do_transform(k), do_transform(v)} - end - - defp do_transform(entity) when is_list(entity) do - for v <- entity, into: [], do: do_transform(v) - end - - defp do_transform(entity) when is_binary(entity) do - entity - |> String.trim() - |> do_transform_string() - end - - defp do_transform(entity), do: entity - - defp parse_host("localhost"), do: :localhost - - defp parse_host(host) do - charlist = to_charlist(host) - - case :inet.parse_address(charlist) do - {:error, :einval} -> - charlist - - {:ok, ip} -> - ip - end - end - - @delimiters ["/", "|", "\"", "'", {"(", ")"}, {"[", "]"}, {"{", "}"}, {"<", ">"}] - - defp find_valid_delimiter([], _string, _), - do: raise(ArgumentError, message: "valid delimiter for Regex expression not found") - - defp find_valid_delimiter([{leading, closing} = delimiter | others], pattern, regex_delimiter) - when is_tuple(delimiter) do - if String.contains?(pattern, closing) do - find_valid_delimiter(others, pattern, regex_delimiter) - else - {:ok, {leading, closing}} - end - end - - defp find_valid_delimiter([delimiter | others], pattern, regex_delimiter) do - if String.contains?(pattern, delimiter) do - find_valid_delimiter(others, pattern, regex_delimiter) - else - {:ok, {delimiter, delimiter}} - end - end - - @regex_parts ~r/^~r(?'delimiter'[\/|"'([{<]{1})(?'pattern'.+)[\/|"')\]}>]{1}(?'modifier'[uismxfU]*)/u - - defp do_transform_string("~r" <> _pattern = regex) do - with %{"modifier" => modifier, "pattern" => pattern, "delimiter" => regex_delimiter} <- - Regex.named_captures(@regex_parts, regex), - {:ok, {leading, closing}} <- find_valid_delimiter(@delimiters, pattern, regex_delimiter), - {result, _} <- Code.eval_string("~r#{leading}#{pattern}#{closing}#{modifier}") do - result - end - end - - defp do_transform_string(":" <> atom), do: String.to_atom(atom) - - defp do_transform_string(value) do - if is_module_name?(value) do - String.to_existing_atom("Elixir." <> value) - else - value - end - end - - @spec is_module_name?(String.t()) :: boolean() - def is_module_name?(string) do - Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Quack)\./, string) or - string in ["Oban", "Ueberauth", "ExSyslogger"] - end - - defp do_eval(entity) do - cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "") - Code.eval_string(cleaned_string) - end -end diff --git a/lib/pleroma/web/admin_api/views/config_view.ex b/lib/pleroma/web/admin_api/views/config_view.ex index 49add0b6e..72b042ab7 100644 --- a/lib/pleroma/web/admin_api/views/config_view.ex +++ b/lib/pleroma/web/admin_api/views/config_view.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigView do %{ key: config.key, group: config.group, - value: Pleroma.Web.AdminAPI.Config.from_binary_with_convert(config.value) + value: Pleroma.ConfigDB.from_binary_with_convert(config.value) } end end diff --git a/test/config/config_db_test.exs b/test/config/config_db_test.exs new file mode 100644 index 000000000..096df9203 --- /dev/null +++ b/test/config/config_db_test.exs @@ -0,0 +1,651 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.ConfigDBTest do + use Pleroma.DataCase, async: true + import Pleroma.Factory + alias Pleroma.ConfigDB + + test "get_by_key/1" do + config = insert(:config) + insert(:config) + + assert config == ConfigDB.get_by_params(%{group: config.group, key: config.key}) + end + + test "create/1" do + {:ok, config} = ConfigDB.create(%{group: "pleroma", key: "some_key", value: "some_value"}) + assert config == ConfigDB.get_by_params(%{group: "pleroma", key: "some_key"}) + end + + test "update/1" do + config = insert(:config) + {:ok, updated} = ConfigDB.update(config, %{value: "some_value"}) + loaded = ConfigDB.get_by_params(%{group: config.group, key: config.key}) + assert loaded == updated + end + + describe "update_or_create/1" do + test "common" do + config = insert(:config) + key2 = "another_key" + + params = [ + %{group: "pleroma", key: key2, value: "another_value"}, + %{group: config.group, key: config.key, value: "new_value"} + ] + + assert Repo.all(ConfigDB) |> length() == 1 + + Enum.each(params, &ConfigDB.update_or_create(&1)) + + assert Repo.all(ConfigDB) |> length() == 2 + + config1 = ConfigDB.get_by_params(%{group: config.group, key: config.key}) + config2 = ConfigDB.get_by_params(%{group: "pleroma", key: key2}) + + assert config1.value == ConfigDB.transform("new_value") + assert config2.value == ConfigDB.transform("another_value") + end + + test "partial update" do + config = insert(:config, value: ConfigDB.to_binary(key1: "val1", key2: :val2)) + + {:ok, _config} = + ConfigDB.update_or_create(%{ + group: config.group, + key: config.key, + value: [key1: :val1, key3: :val3] + }) + + updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) + + value = ConfigDB.from_binary(updated.value) + assert length(value) == 3 + assert value[:key1] == :val1 + assert value[:key2] == :val2 + assert value[:key3] == :val3 + end + + test "deep merge" do + config = insert(:config, value: ConfigDB.to_binary(key1: "val1", key2: [k1: :v1, k2: "v2"])) + + {:ok, config} = + ConfigDB.update_or_create(%{ + group: config.group, + key: config.key, + value: [key1: :val1, key2: [k2: :v2, k3: :v3], key3: :val3] + }) + + updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) + + assert config.value == updated.value + + value = ConfigDB.from_binary(updated.value) + assert value[:key1] == :val1 + assert value[:key2] == [k1: :v1, k2: :v2, k3: :v3] + assert value[:key3] == :val3 + end + + test "only full update for some keys" do + config1 = insert(:config, key: ":ecto_repos", value: ConfigDB.to_binary(repo: Pleroma.Repo)) + + config2 = + insert(:config, group: ":cors_plug", key: ":max_age", value: ConfigDB.to_binary(18)) + + {:ok, _config} = + ConfigDB.update_or_create(%{ + group: config1.group, + key: config1.key, + value: [another_repo: [Pleroma.Repo]] + }) + + {:ok, _config} = + ConfigDB.update_or_create(%{ + group: config2.group, + key: config2.key, + value: 777 + }) + + updated1 = ConfigDB.get_by_params(%{group: config1.group, key: config1.key}) + updated2 = ConfigDB.get_by_params(%{group: config2.group, key: config2.key}) + + assert ConfigDB.from_binary(updated1.value) == [another_repo: [Pleroma.Repo]] + assert ConfigDB.from_binary(updated2.value) == 777 + end + + test "full update if value is not keyword" do + config = + insert(:config, + group: ":tesla", + key: ":adapter", + value: ConfigDB.to_binary(Tesla.Adapter.Hackney) + ) + + {:ok, _config} = + ConfigDB.update_or_create(%{ + group: config.group, + key: config.key, + value: Tesla.Adapter.Httpc + }) + + updated = ConfigDB.get_by_params(%{group: config.group, key: config.key}) + + assert ConfigDB.from_binary(updated.value) == Tesla.Adapter.Httpc + end + end + + test "delete/1" do + config = insert(:config) + {:ok, _} = ConfigDB.delete(%{key: config.key, group: config.group}) + refute ConfigDB.get_by_params(%{key: config.key, group: config.group}) + end + + describe "transform/1" do + test "string" do + binary = ConfigDB.transform("value as string") + assert binary == :erlang.term_to_binary("value as string") + assert ConfigDB.from_binary(binary) == "value as string" + end + + test "boolean" do + binary = ConfigDB.transform(false) + assert binary == :erlang.term_to_binary(false) + assert ConfigDB.from_binary(binary) == false + end + + test "nil" do + binary = ConfigDB.transform(nil) + assert binary == :erlang.term_to_binary(nil) + assert ConfigDB.from_binary(binary) == nil + end + + test "integer" do + binary = ConfigDB.transform(150) + assert binary == :erlang.term_to_binary(150) + assert ConfigDB.from_binary(binary) == 150 + end + + test "atom" do + binary = ConfigDB.transform(":atom") + assert binary == :erlang.term_to_binary(:atom) + assert ConfigDB.from_binary(binary) == :atom + end + + test "ssl options" do + binary = ConfigDB.transform([":tlsv1", ":tlsv1.1", ":tlsv1.2"]) + assert binary == :erlang.term_to_binary([:tlsv1, :"tlsv1.1", :"tlsv1.2"]) + assert ConfigDB.from_binary(binary) == [:tlsv1, :"tlsv1.1", :"tlsv1.2"] + end + + test "pleroma module" do + binary = ConfigDB.transform("Pleroma.Bookmark") + assert binary == :erlang.term_to_binary(Pleroma.Bookmark) + assert ConfigDB.from_binary(binary) == Pleroma.Bookmark + end + + test "pleroma string" do + binary = ConfigDB.transform("Pleroma") + assert binary == :erlang.term_to_binary("Pleroma") + assert ConfigDB.from_binary(binary) == "Pleroma" + end + + test "phoenix module" do + binary = ConfigDB.transform("Phoenix.Socket.V1.JSONSerializer") + assert binary == :erlang.term_to_binary(Phoenix.Socket.V1.JSONSerializer) + assert ConfigDB.from_binary(binary) == Phoenix.Socket.V1.JSONSerializer + end + + test "tesla module" do + binary = ConfigDB.transform("Tesla.Adapter.Hackney") + assert binary == :erlang.term_to_binary(Tesla.Adapter.Hackney) + assert ConfigDB.from_binary(binary) == Tesla.Adapter.Hackney + end + + test "ExSyslogger module" do + binary = ConfigDB.transform("ExSyslogger") + assert binary == :erlang.term_to_binary(ExSyslogger) + assert ConfigDB.from_binary(binary) == ExSyslogger + end + + test "Quack.Logger module" do + binary = ConfigDB.transform("Quack.Logger") + assert binary == :erlang.term_to_binary(Quack.Logger) + assert ConfigDB.from_binary(binary) == Quack.Logger + end + + test "sigil" do + binary = ConfigDB.transform("~r[comp[lL][aA][iI][nN]er]") + assert binary == :erlang.term_to_binary(~r/comp[lL][aA][iI][nN]er/) + assert ConfigDB.from_binary(binary) == ~r/comp[lL][aA][iI][nN]er/ + end + + test "link sigil" do + binary = ConfigDB.transform("~r/https:\/\/example.com/") + assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/) + assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/ + end + + test "link sigil with um modifiers" do + binary = ConfigDB.transform("~r/https:\/\/example.com/um") + assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/um) + assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/um + end + + test "link sigil with i modifier" do + binary = ConfigDB.transform("~r/https:\/\/example.com/i") + assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/i) + assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/i + end + + test "link sigil with s modifier" do + binary = ConfigDB.transform("~r/https:\/\/example.com/s") + assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/s) + assert ConfigDB.from_binary(binary) == ~r/https:\/\/example.com/s + end + + test "raise if valid delimiter not found" do + assert_raise ArgumentError, "valid delimiter for Regex expression not found", fn -> + ConfigDB.transform("~r/https://[]{}<>\"'()|example.com/s") + end + end + + test "2 child tuple" do + binary = ConfigDB.transform(%{"tuple" => ["v1", ":v2"]}) + assert binary == :erlang.term_to_binary({"v1", :v2}) + assert ConfigDB.from_binary(binary) == {"v1", :v2} + end + + test "proxy tuple with localhost" do + binary = + ConfigDB.transform(%{ + "tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}] + }) + + assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, :localhost, 1234}}) + assert ConfigDB.from_binary(binary) == {:proxy_url, {:socks5, :localhost, 1234}} + end + + test "proxy tuple with domain" do + binary = + ConfigDB.transform(%{ + "tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}] + }) + + assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, 'domain.com', 1234}}) + assert ConfigDB.from_binary(binary) == {:proxy_url, {:socks5, 'domain.com', 1234}} + end + + test "proxy tuple with ip" do + binary = + ConfigDB.transform(%{ + "tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}] + }) + + assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}}) + assert ConfigDB.from_binary(binary) == {:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}} + end + + test "tuple with n childs" do + binary = + ConfigDB.transform(%{ + "tuple" => [ + "v1", + ":v2", + "Pleroma.Bookmark", + 150, + false, + "Phoenix.Socket.V1.JSONSerializer" + ] + }) + + assert binary == + :erlang.term_to_binary( + {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer} + ) + + assert ConfigDB.from_binary(binary) == + {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer} + end + + test "tuple with dispatch key" do + binary = ConfigDB.transform(%{"tuple" => [":dispatch", ["{:_, + [ + {\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, + {\"/websocket\", Phoenix.Endpoint.CowboyWebSocket, + {Phoenix.Transports.WebSocket, + {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}}, + {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} + ]}"]]}) + + assert binary == + :erlang.term_to_binary( + {:dispatch, + [ + {:_, + [ + {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, + {"/websocket", Phoenix.Endpoint.CowboyWebSocket, + {Phoenix.Transports.WebSocket, + {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: "/websocket"]}}}, + {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} + ]} + ]} + ) + + assert ConfigDB.from_binary(binary) == + {:dispatch, + [ + {:_, + [ + {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, + {"/websocket", Phoenix.Endpoint.CowboyWebSocket, + {Phoenix.Transports.WebSocket, + {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: "/websocket"]}}}, + {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} + ]} + ]} + end + + test "map with string key" do + binary = ConfigDB.transform(%{"key" => "value"}) + assert binary == :erlang.term_to_binary(%{"key" => "value"}) + assert ConfigDB.from_binary(binary) == %{"key" => "value"} + end + + test "map with atom key" do + binary = ConfigDB.transform(%{":key" => "value"}) + assert binary == :erlang.term_to_binary(%{key: "value"}) + assert ConfigDB.from_binary(binary) == %{key: "value"} + end + + test "list of strings" do + binary = ConfigDB.transform(["v1", "v2", "v3"]) + assert binary == :erlang.term_to_binary(["v1", "v2", "v3"]) + assert ConfigDB.from_binary(binary) == ["v1", "v2", "v3"] + end + + test "list of modules" do + binary = ConfigDB.transform(["Pleroma.Repo", "Pleroma.Activity"]) + assert binary == :erlang.term_to_binary([Pleroma.Repo, Pleroma.Activity]) + assert ConfigDB.from_binary(binary) == [Pleroma.Repo, Pleroma.Activity] + end + + test "list of atoms" do + binary = ConfigDB.transform([":v1", ":v2", ":v3"]) + assert binary == :erlang.term_to_binary([:v1, :v2, :v3]) + assert ConfigDB.from_binary(binary) == [:v1, :v2, :v3] + end + + test "list of mixed values" do + binary = + ConfigDB.transform([ + "v1", + ":v2", + "Pleroma.Repo", + "Phoenix.Socket.V1.JSONSerializer", + 15, + false + ]) + + assert binary == + :erlang.term_to_binary([ + "v1", + :v2, + Pleroma.Repo, + Phoenix.Socket.V1.JSONSerializer, + 15, + false + ]) + + assert ConfigDB.from_binary(binary) == [ + "v1", + :v2, + Pleroma.Repo, + Phoenix.Socket.V1.JSONSerializer, + 15, + false + ] + end + + test "simple keyword" do + binary = ConfigDB.transform([%{"tuple" => [":key", "value"]}]) + assert binary == :erlang.term_to_binary([{:key, "value"}]) + assert ConfigDB.from_binary(binary) == [{:key, "value"}] + assert ConfigDB.from_binary(binary) == [key: "value"] + end + + test "keyword with partial_chain key" do + binary = + ConfigDB.transform([%{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}]) + + assert binary == :erlang.term_to_binary(partial_chain: &:hackney_connect.partial_chain/1) + assert ConfigDB.from_binary(binary) == [partial_chain: &:hackney_connect.partial_chain/1] + end + + test "keyword" do + binary = + ConfigDB.transform([ + %{"tuple" => [":types", "Pleroma.PostgresTypes"]}, + %{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]}, + %{"tuple" => [":migration_lock", nil]}, + %{"tuple" => [":key1", 150]}, + %{"tuple" => [":key2", "string"]} + ]) + + assert binary == + :erlang.term_to_binary( + types: Pleroma.PostgresTypes, + telemetry_event: [Pleroma.Repo.Instrumenter], + migration_lock: nil, + key1: 150, + key2: "string" + ) + + assert ConfigDB.from_binary(binary) == [ + types: Pleroma.PostgresTypes, + telemetry_event: [Pleroma.Repo.Instrumenter], + migration_lock: nil, + key1: 150, + key2: "string" + ] + end + + test "complex keyword with nested mixed childs" do + binary = + ConfigDB.transform([ + %{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]}, + %{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]}, + %{"tuple" => [":link_name", true]}, + %{"tuple" => [":proxy_remote", false]}, + %{"tuple" => [":common_map", %{":key" => "value"}]}, + %{ + "tuple" => [ + ":proxy_opts", + [ + %{"tuple" => [":redirect_on_failure", false]}, + %{"tuple" => [":max_body_length", 1_048_576]}, + %{ + "tuple" => [ + ":http", + [%{"tuple" => [":follow_redirect", true]}, %{"tuple" => [":pool", ":upload"]}] + ] + } + ] + ] + } + ]) + + assert binary == + :erlang.term_to_binary( + uploader: Pleroma.Uploaders.Local, + filters: [Pleroma.Upload.Filter.Dedupe], + link_name: true, + proxy_remote: false, + common_map: %{key: "value"}, + proxy_opts: [ + redirect_on_failure: false, + max_body_length: 1_048_576, + http: [ + follow_redirect: true, + pool: :upload + ] + ] + ) + + assert ConfigDB.from_binary(binary) == + [ + uploader: Pleroma.Uploaders.Local, + filters: [Pleroma.Upload.Filter.Dedupe], + link_name: true, + proxy_remote: false, + common_map: %{key: "value"}, + proxy_opts: [ + redirect_on_failure: false, + max_body_length: 1_048_576, + http: [ + follow_redirect: true, + pool: :upload + ] + ] + ] + end + + test "common keyword" do + binary = + ConfigDB.transform([ + %{"tuple" => [":level", ":warn"]}, + %{"tuple" => [":meta", [":all"]]}, + %{"tuple" => [":path", ""]}, + %{"tuple" => [":val", nil]}, + %{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]} + ]) + + assert binary == + :erlang.term_to_binary( + level: :warn, + meta: [:all], + path: "", + val: nil, + webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE" + ) + + assert ConfigDB.from_binary(binary) == [ + level: :warn, + meta: [:all], + path: "", + val: nil, + webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE" + ] + end + + test "complex keyword with sigil" do + binary = + ConfigDB.transform([ + %{"tuple" => [":federated_timeline_removal", []]}, + %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]}, + %{"tuple" => [":replace", []]} + ]) + + assert binary == + :erlang.term_to_binary( + federated_timeline_removal: [], + reject: [~r/comp[lL][aA][iI][nN]er/], + replace: [] + ) + + assert ConfigDB.from_binary(binary) == + [federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []] + end + + test "complex keyword with tuples with more than 2 values" do + binary = + ConfigDB.transform([ + %{ + "tuple" => [ + ":http", + [ + %{ + "tuple" => [ + ":key1", + [ + %{ + "tuple" => [ + ":_", + [ + %{ + "tuple" => [ + "/api/v1/streaming", + "Pleroma.Web.MastodonAPI.WebsocketHandler", + [] + ] + }, + %{ + "tuple" => [ + "/websocket", + "Phoenix.Endpoint.CowboyWebSocket", + %{ + "tuple" => [ + "Phoenix.Transports.WebSocket", + %{ + "tuple" => [ + "Pleroma.Web.Endpoint", + "Pleroma.Web.UserSocket", + [] + ] + } + ] + } + ] + }, + %{ + "tuple" => [ + ":_", + "Phoenix.Endpoint.Cowboy2Handler", + %{"tuple" => ["Pleroma.Web.Endpoint", []]} + ] + } + ] + ] + } + ] + ] + } + ] + ] + } + ]) + + assert binary == + :erlang.term_to_binary( + http: [ + key1: [ + _: [ + {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, + {"/websocket", Phoenix.Endpoint.CowboyWebSocket, + {Phoenix.Transports.WebSocket, + {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}}, + {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} + ] + ] + ] + ) + + assert ConfigDB.from_binary(binary) == [ + http: [ + key1: [ + {:_, + [ + {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, + {"/websocket", Phoenix.Endpoint.CowboyWebSocket, + {Phoenix.Transports.WebSocket, + {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}}, + {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} + ]} + ] + ] + ] + end + end +end diff --git a/test/config/transfer_task_test.exs b/test/config/transfer_task_test.exs index b684956b6..89de93ca3 100644 --- a/test/config/transfer_task_test.exs +++ b/test/config/transfer_task_test.exs @@ -5,7 +5,7 @@ defmodule Pleroma.Config.TransferTaskTest do use Pleroma.DataCase - alias Pleroma.Web.AdminAPI.Config + alias Pleroma.ConfigDB clear_config(:configurable_from_database) do Pleroma.Config.put(:configurable_from_database, true) @@ -16,19 +16,19 @@ defmodule Pleroma.Config.TransferTaskTest do refute Application.get_env(:idna, :test_key) refute Application.get_env(:quack, :test_key) - Config.create(%{ + ConfigDB.create(%{ group: ":pleroma", key: ":test_key", value: [live: 2, com: 3] }) - Config.create(%{ + ConfigDB.create(%{ group: ":idna", key: ":test_key", value: [live: 15, com: 35] }) - Config.create(%{ + ConfigDB.create(%{ group: ":quack", key: ":test_key", value: [:test_value1, :test_value2] @@ -48,7 +48,7 @@ defmodule Pleroma.Config.TransferTaskTest do end test "non existing atom" do - Config.create(%{ + ConfigDB.create(%{ group: ":pleroma", key: ":undefined_atom_key", value: [live: 2, com: 3] diff --git a/test/support/factory.ex b/test/support/factory.ex index 9ff8004dd..780235cb9 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -394,7 +394,7 @@ defmodule Pleroma.Factory do end def config_factory do - %Pleroma.Web.AdminAPI.Config{ + %Pleroma.ConfigDB{ key: sequence(:key, fn key -> # Atom dynamic registration hack in tests diff --git a/test/tasks/config_test.exs b/test/tasks/config_test.exs index 099b3d8e3..7759f0586 100644 --- a/test/tasks/config_test.exs +++ b/test/tasks/config_test.exs @@ -4,8 +4,8 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do use Pleroma.DataCase + alias Pleroma.ConfigDB alias Pleroma.Repo - alias Pleroma.Web.AdminAPI.Config setup_all do Mix.shell(Mix.Shell.Process) @@ -26,7 +26,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do test "settings are migrated to db" do initial = Application.get_env(:quack, :level) on_exit(fn -> Application.put_env(:quack, :level, initial) end) - assert Repo.all(Config) == [] + assert Repo.all(ConfigDB) == [] Application.put_env(:pleroma, :first_setting, key: "value", key2: [Repo]) Application.put_env(:pleroma, :second_setting, key: "value2", key2: ["Activity"]) @@ -34,14 +34,14 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do Mix.Tasks.Pleroma.Config.run(["migrate_to_db"]) - config1 = Config.get_by_params(%{group: ":pleroma", key: ":first_setting"}) - config2 = Config.get_by_params(%{group: ":pleroma", key: ":second_setting"}) - config3 = Config.get_by_params(%{group: ":quack", key: ":level"}) - refute Config.get_by_params(%{group: ":pleroma", key: "Pleroma.Repo"}) + config1 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":first_setting"}) + config2 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":second_setting"}) + config3 = ConfigDB.get_by_params(%{group: ":quack", key: ":level"}) + refute ConfigDB.get_by_params(%{group: ":pleroma", key: "Pleroma.Repo"}) - assert Config.from_binary(config1.value) == [key: "value", key2: [Repo]] - assert Config.from_binary(config2.value) == [key: "value2", key2: ["Activity"]] - assert Config.from_binary(config3.value) == :info + assert ConfigDB.from_binary(config1.value) == [key: "value", key2: [Repo]] + assert ConfigDB.from_binary(config2.value) == [key: "value2", key2: ["Activity"]] + assert ConfigDB.from_binary(config3.value) == :info end describe "with deletion temp file" do @@ -56,23 +56,23 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do end test "settings are migrated to file and deleted from db", %{temp_file: temp_file} do - Config.create(%{ + ConfigDB.create(%{ group: ":pleroma", key: ":setting_first", value: [key: "value", key2: ["Activity"]] }) - Config.create(%{ + ConfigDB.create(%{ group: ":pleroma", key: ":setting_second", value: [key: "value2", key2: [Repo]] }) - Config.create(%{group: ":quack", key: ":level", value: :info}) + ConfigDB.create(%{group: ":quack", key: ":level", value: :info}) Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "--env", "temp", "-d"]) - assert Repo.all(Config) == [] + assert Repo.all(ConfigDB) == [] file = File.read!(temp_file) assert file =~ "config :pleroma, :setting_first," @@ -81,7 +81,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do end test "load a settings with large values and pass to file", %{temp_file: temp_file} do - Config.create(%{ + ConfigDB.create(%{ group: ":pleroma", key: ":instance", value: [ @@ -151,7 +151,7 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "--env", "temp", "-d"]) - assert Repo.all(Config) == [] + assert Repo.all(ConfigDB) == [] assert File.exists?(temp_file) {:ok, file} = File.read(temp_file) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index faefe729e..8e80f9b47 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -2832,14 +2832,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do test "transfer settings to DB and to file", %{conn: conn} do on_exit(fn -> :ok = File.rm("config/test.exported_from_db.secret.exs") end) - assert Repo.all(Pleroma.Web.AdminAPI.Config) == [] + assert Repo.all(Pleroma.ConfigDB) == [] Mix.Tasks.Pleroma.Config.run(["migrate_to_db"]) - assert Repo.aggregate(Pleroma.Web.AdminAPI.Config, :count, :id) > 0 + assert Repo.aggregate(Pleroma.ConfigDB, :count, :id) > 0 conn = get(conn, "/api/pleroma/admin/config/migrate_from_db") assert json_response(conn, 200) == %{} - assert Repo.all(Pleroma.Web.AdminAPI.Config) == [] + assert Repo.all(Pleroma.ConfigDB) == [] end test "returns error if configuration from database is off", %{conn: conn} do @@ -2852,7 +2852,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do assert json_response(conn, 400) == "To use this endpoint you need to enable configuration from database." - assert Repo.all(Pleroma.Web.AdminAPI.Config) == [] + assert Repo.all(Pleroma.ConfigDB) == [] end end diff --git a/test/web/admin_api/config_test.exs b/test/web/admin_api/config_test.exs deleted file mode 100644 index 2c0601b56..000000000 --- a/test/web/admin_api/config_test.exs +++ /dev/null @@ -1,649 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Web.AdminAPI.ConfigTest do - use Pleroma.DataCase, async: true - import Pleroma.Factory - alias Pleroma.Web.AdminAPI.Config - - test "get_by_key/1" do - config = insert(:config) - insert(:config) - - assert config == Config.get_by_params(%{group: config.group, key: config.key}) - end - - test "create/1" do - {:ok, config} = Config.create(%{group: "pleroma", key: "some_key", value: "some_value"}) - assert config == Config.get_by_params(%{group: "pleroma", key: "some_key"}) - end - - test "update/1" do - config = insert(:config) - {:ok, updated} = Config.update(config, %{value: "some_value"}) - loaded = Config.get_by_params(%{group: config.group, key: config.key}) - assert loaded == updated - end - - describe "update_or_create/1" do - test "common" do - config = insert(:config) - key2 = "another_key" - - params = [ - %{group: "pleroma", key: key2, value: "another_value"}, - %{group: config.group, key: config.key, value: "new_value"} - ] - - assert Repo.all(Config) |> length() == 1 - - Enum.each(params, &Config.update_or_create(&1)) - - assert Repo.all(Config) |> length() == 2 - - config1 = Config.get_by_params(%{group: config.group, key: config.key}) - config2 = Config.get_by_params(%{group: "pleroma", key: key2}) - - assert config1.value == Config.transform("new_value") - assert config2.value == Config.transform("another_value") - end - - test "partial update" do - config = insert(:config, value: Config.to_binary(key1: "val1", key2: :val2)) - - {:ok, _config} = - Config.update_or_create(%{ - group: config.group, - key: config.key, - value: [key1: :val1, key3: :val3] - }) - - updated = Config.get_by_params(%{group: config.group, key: config.key}) - - value = Config.from_binary(updated.value) - assert length(value) == 3 - assert value[:key1] == :val1 - assert value[:key2] == :val2 - assert value[:key3] == :val3 - end - - test "deep merge" do - config = insert(:config, value: Config.to_binary(key1: "val1", key2: [k1: :v1, k2: "v2"])) - - {:ok, config} = - Config.update_or_create(%{ - group: config.group, - key: config.key, - value: [key1: :val1, key2: [k2: :v2, k3: :v3], key3: :val3] - }) - - updated = Config.get_by_params(%{group: config.group, key: config.key}) - - assert config.value == updated.value - - value = Config.from_binary(updated.value) - assert value[:key1] == :val1 - assert value[:key2] == [k1: :v1, k2: :v2, k3: :v3] - assert value[:key3] == :val3 - end - - test "only full update for some keys" do - config1 = insert(:config, key: ":ecto_repos", value: Config.to_binary(repo: Pleroma.Repo)) - config2 = insert(:config, group: ":cors_plug", key: ":max_age", value: Config.to_binary(18)) - - {:ok, _config} = - Config.update_or_create(%{ - group: config1.group, - key: config1.key, - value: [another_repo: [Pleroma.Repo]] - }) - - {:ok, _config} = - Config.update_or_create(%{ - group: config2.group, - key: config2.key, - value: 777 - }) - - updated1 = Config.get_by_params(%{group: config1.group, key: config1.key}) - updated2 = Config.get_by_params(%{group: config2.group, key: config2.key}) - - assert Config.from_binary(updated1.value) == [another_repo: [Pleroma.Repo]] - assert Config.from_binary(updated2.value) == 777 - end - - test "full update if value is not keyword" do - config = - insert(:config, - group: ":tesla", - key: ":adapter", - value: Config.to_binary(Tesla.Adapter.Hackney) - ) - - {:ok, _config} = - Config.update_or_create(%{ - group: config.group, - key: config.key, - value: Tesla.Adapter.Httpc - }) - - updated = Config.get_by_params(%{group: config.group, key: config.key}) - - assert Config.from_binary(updated.value) == Tesla.Adapter.Httpc - end - end - - test "delete/1" do - config = insert(:config) - {:ok, _} = Config.delete(%{key: config.key, group: config.group}) - refute Config.get_by_params(%{key: config.key, group: config.group}) - end - - describe "transform/1" do - test "string" do - binary = Config.transform("value as string") - assert binary == :erlang.term_to_binary("value as string") - assert Config.from_binary(binary) == "value as string" - end - - test "boolean" do - binary = Config.transform(false) - assert binary == :erlang.term_to_binary(false) - assert Config.from_binary(binary) == false - end - - test "nil" do - binary = Config.transform(nil) - assert binary == :erlang.term_to_binary(nil) - assert Config.from_binary(binary) == nil - end - - test "integer" do - binary = Config.transform(150) - assert binary == :erlang.term_to_binary(150) - assert Config.from_binary(binary) == 150 - end - - test "atom" do - binary = Config.transform(":atom") - assert binary == :erlang.term_to_binary(:atom) - assert Config.from_binary(binary) == :atom - end - - test "ssl options" do - binary = Config.transform([":tlsv1", ":tlsv1.1", ":tlsv1.2"]) - assert binary == :erlang.term_to_binary([:tlsv1, :"tlsv1.1", :"tlsv1.2"]) - assert Config.from_binary(binary) == [:tlsv1, :"tlsv1.1", :"tlsv1.2"] - end - - test "pleroma module" do - binary = Config.transform("Pleroma.Bookmark") - assert binary == :erlang.term_to_binary(Pleroma.Bookmark) - assert Config.from_binary(binary) == Pleroma.Bookmark - end - - test "pleroma string" do - binary = Config.transform("Pleroma") - assert binary == :erlang.term_to_binary("Pleroma") - assert Config.from_binary(binary) == "Pleroma" - end - - test "phoenix module" do - binary = Config.transform("Phoenix.Socket.V1.JSONSerializer") - assert binary == :erlang.term_to_binary(Phoenix.Socket.V1.JSONSerializer) - assert Config.from_binary(binary) == Phoenix.Socket.V1.JSONSerializer - end - - test "tesla module" do - binary = Config.transform("Tesla.Adapter.Hackney") - assert binary == :erlang.term_to_binary(Tesla.Adapter.Hackney) - assert Config.from_binary(binary) == Tesla.Adapter.Hackney - end - - test "ExSyslogger module" do - binary = Config.transform("ExSyslogger") - assert binary == :erlang.term_to_binary(ExSyslogger) - assert Config.from_binary(binary) == ExSyslogger - end - - test "Quack.Logger module" do - binary = Config.transform("Quack.Logger") - assert binary == :erlang.term_to_binary(Quack.Logger) - assert Config.from_binary(binary) == Quack.Logger - end - - test "sigil" do - binary = Config.transform("~r[comp[lL][aA][iI][nN]er]") - assert binary == :erlang.term_to_binary(~r/comp[lL][aA][iI][nN]er/) - assert Config.from_binary(binary) == ~r/comp[lL][aA][iI][nN]er/ - end - - test "link sigil" do - binary = Config.transform("~r/https:\/\/example.com/") - assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/) - assert Config.from_binary(binary) == ~r/https:\/\/example.com/ - end - - test "link sigil with um modifiers" do - binary = Config.transform("~r/https:\/\/example.com/um") - assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/um) - assert Config.from_binary(binary) == ~r/https:\/\/example.com/um - end - - test "link sigil with i modifier" do - binary = Config.transform("~r/https:\/\/example.com/i") - assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/i) - assert Config.from_binary(binary) == ~r/https:\/\/example.com/i - end - - test "link sigil with s modifier" do - binary = Config.transform("~r/https:\/\/example.com/s") - assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/s) - assert Config.from_binary(binary) == ~r/https:\/\/example.com/s - end - - test "raise if valid delimiter not found" do - assert_raise ArgumentError, "valid delimiter for Regex expression not found", fn -> - Config.transform("~r/https://[]{}<>\"'()|example.com/s") - end - end - - test "2 child tuple" do - binary = Config.transform(%{"tuple" => ["v1", ":v2"]}) - assert binary == :erlang.term_to_binary({"v1", :v2}) - assert Config.from_binary(binary) == {"v1", :v2} - end - - test "proxy tuple with localhost" do - binary = - Config.transform(%{ - "tuple" => [":proxy_url", %{"tuple" => [":socks5", "localhost", 1234]}] - }) - - assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, :localhost, 1234}}) - assert Config.from_binary(binary) == {:proxy_url, {:socks5, :localhost, 1234}} - end - - test "proxy tuple with domain" do - binary = - Config.transform(%{ - "tuple" => [":proxy_url", %{"tuple" => [":socks5", "domain.com", 1234]}] - }) - - assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, 'domain.com', 1234}}) - assert Config.from_binary(binary) == {:proxy_url, {:socks5, 'domain.com', 1234}} - end - - test "proxy tuple with ip" do - binary = - Config.transform(%{ - "tuple" => [":proxy_url", %{"tuple" => [":socks5", "127.0.0.1", 1234]}] - }) - - assert binary == :erlang.term_to_binary({:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}}) - assert Config.from_binary(binary) == {:proxy_url, {:socks5, {127, 0, 0, 1}, 1234}} - end - - test "tuple with n childs" do - binary = - Config.transform(%{ - "tuple" => [ - "v1", - ":v2", - "Pleroma.Bookmark", - 150, - false, - "Phoenix.Socket.V1.JSONSerializer" - ] - }) - - assert binary == - :erlang.term_to_binary( - {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer} - ) - - assert Config.from_binary(binary) == - {"v1", :v2, Pleroma.Bookmark, 150, false, Phoenix.Socket.V1.JSONSerializer} - end - - test "tuple with dispatch key" do - binary = Config.transform(%{"tuple" => [":dispatch", ["{:_, - [ - {\"/api/v1/streaming\", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, - {\"/websocket\", Phoenix.Endpoint.CowboyWebSocket, - {Phoenix.Transports.WebSocket, - {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: \"/websocket\"]}}}, - {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} - ]}"]]}) - - assert binary == - :erlang.term_to_binary( - {:dispatch, - [ - {:_, - [ - {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, - {"/websocket", Phoenix.Endpoint.CowboyWebSocket, - {Phoenix.Transports.WebSocket, - {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: "/websocket"]}}}, - {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} - ]} - ]} - ) - - assert Config.from_binary(binary) == - {:dispatch, - [ - {:_, - [ - {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, - {"/websocket", Phoenix.Endpoint.CowboyWebSocket, - {Phoenix.Transports.WebSocket, - {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, [path: "/websocket"]}}}, - {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} - ]} - ]} - end - - test "map with string key" do - binary = Config.transform(%{"key" => "value"}) - assert binary == :erlang.term_to_binary(%{"key" => "value"}) - assert Config.from_binary(binary) == %{"key" => "value"} - end - - test "map with atom key" do - binary = Config.transform(%{":key" => "value"}) - assert binary == :erlang.term_to_binary(%{key: "value"}) - assert Config.from_binary(binary) == %{key: "value"} - end - - test "list of strings" do - binary = Config.transform(["v1", "v2", "v3"]) - assert binary == :erlang.term_to_binary(["v1", "v2", "v3"]) - assert Config.from_binary(binary) == ["v1", "v2", "v3"] - end - - test "list of modules" do - binary = Config.transform(["Pleroma.Repo", "Pleroma.Activity"]) - assert binary == :erlang.term_to_binary([Pleroma.Repo, Pleroma.Activity]) - assert Config.from_binary(binary) == [Pleroma.Repo, Pleroma.Activity] - end - - test "list of atoms" do - binary = Config.transform([":v1", ":v2", ":v3"]) - assert binary == :erlang.term_to_binary([:v1, :v2, :v3]) - assert Config.from_binary(binary) == [:v1, :v2, :v3] - end - - test "list of mixed values" do - binary = - Config.transform([ - "v1", - ":v2", - "Pleroma.Repo", - "Phoenix.Socket.V1.JSONSerializer", - 15, - false - ]) - - assert binary == - :erlang.term_to_binary([ - "v1", - :v2, - Pleroma.Repo, - Phoenix.Socket.V1.JSONSerializer, - 15, - false - ]) - - assert Config.from_binary(binary) == [ - "v1", - :v2, - Pleroma.Repo, - Phoenix.Socket.V1.JSONSerializer, - 15, - false - ] - end - - test "simple keyword" do - binary = Config.transform([%{"tuple" => [":key", "value"]}]) - assert binary == :erlang.term_to_binary([{:key, "value"}]) - assert Config.from_binary(binary) == [{:key, "value"}] - assert Config.from_binary(binary) == [key: "value"] - end - - test "keyword with partial_chain key" do - binary = - Config.transform([%{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}]) - - assert binary == :erlang.term_to_binary(partial_chain: &:hackney_connect.partial_chain/1) - assert Config.from_binary(binary) == [partial_chain: &:hackney_connect.partial_chain/1] - end - - test "keyword" do - binary = - Config.transform([ - %{"tuple" => [":types", "Pleroma.PostgresTypes"]}, - %{"tuple" => [":telemetry_event", ["Pleroma.Repo.Instrumenter"]]}, - %{"tuple" => [":migration_lock", nil]}, - %{"tuple" => [":key1", 150]}, - %{"tuple" => [":key2", "string"]} - ]) - - assert binary == - :erlang.term_to_binary( - types: Pleroma.PostgresTypes, - telemetry_event: [Pleroma.Repo.Instrumenter], - migration_lock: nil, - key1: 150, - key2: "string" - ) - - assert Config.from_binary(binary) == [ - types: Pleroma.PostgresTypes, - telemetry_event: [Pleroma.Repo.Instrumenter], - migration_lock: nil, - key1: 150, - key2: "string" - ] - end - - test "complex keyword with nested mixed childs" do - binary = - Config.transform([ - %{"tuple" => [":uploader", "Pleroma.Uploaders.Local"]}, - %{"tuple" => [":filters", ["Pleroma.Upload.Filter.Dedupe"]]}, - %{"tuple" => [":link_name", true]}, - %{"tuple" => [":proxy_remote", false]}, - %{"tuple" => [":common_map", %{":key" => "value"}]}, - %{ - "tuple" => [ - ":proxy_opts", - [ - %{"tuple" => [":redirect_on_failure", false]}, - %{"tuple" => [":max_body_length", 1_048_576]}, - %{ - "tuple" => [ - ":http", - [%{"tuple" => [":follow_redirect", true]}, %{"tuple" => [":pool", ":upload"]}] - ] - } - ] - ] - } - ]) - - assert binary == - :erlang.term_to_binary( - uploader: Pleroma.Uploaders.Local, - filters: [Pleroma.Upload.Filter.Dedupe], - link_name: true, - proxy_remote: false, - common_map: %{key: "value"}, - proxy_opts: [ - redirect_on_failure: false, - max_body_length: 1_048_576, - http: [ - follow_redirect: true, - pool: :upload - ] - ] - ) - - assert Config.from_binary(binary) == - [ - uploader: Pleroma.Uploaders.Local, - filters: [Pleroma.Upload.Filter.Dedupe], - link_name: true, - proxy_remote: false, - common_map: %{key: "value"}, - proxy_opts: [ - redirect_on_failure: false, - max_body_length: 1_048_576, - http: [ - follow_redirect: true, - pool: :upload - ] - ] - ] - end - - test "common keyword" do - binary = - Config.transform([ - %{"tuple" => [":level", ":warn"]}, - %{"tuple" => [":meta", [":all"]]}, - %{"tuple" => [":path", ""]}, - %{"tuple" => [":val", nil]}, - %{"tuple" => [":webhook_url", "https://hooks.slack.com/services/YOUR-KEY-HERE"]} - ]) - - assert binary == - :erlang.term_to_binary( - level: :warn, - meta: [:all], - path: "", - val: nil, - webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE" - ) - - assert Config.from_binary(binary) == [ - level: :warn, - meta: [:all], - path: "", - val: nil, - webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE" - ] - end - - test "complex keyword with sigil" do - binary = - Config.transform([ - %{"tuple" => [":federated_timeline_removal", []]}, - %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]}, - %{"tuple" => [":replace", []]} - ]) - - assert binary == - :erlang.term_to_binary( - federated_timeline_removal: [], - reject: [~r/comp[lL][aA][iI][nN]er/], - replace: [] - ) - - assert Config.from_binary(binary) == - [federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []] - end - - test "complex keyword with tuples with more than 2 values" do - binary = - Config.transform([ - %{ - "tuple" => [ - ":http", - [ - %{ - "tuple" => [ - ":key1", - [ - %{ - "tuple" => [ - ":_", - [ - %{ - "tuple" => [ - "/api/v1/streaming", - "Pleroma.Web.MastodonAPI.WebsocketHandler", - [] - ] - }, - %{ - "tuple" => [ - "/websocket", - "Phoenix.Endpoint.CowboyWebSocket", - %{ - "tuple" => [ - "Phoenix.Transports.WebSocket", - %{ - "tuple" => [ - "Pleroma.Web.Endpoint", - "Pleroma.Web.UserSocket", - [] - ] - } - ] - } - ] - }, - %{ - "tuple" => [ - ":_", - "Phoenix.Endpoint.Cowboy2Handler", - %{"tuple" => ["Pleroma.Web.Endpoint", []]} - ] - } - ] - ] - } - ] - ] - } - ] - ] - } - ]) - - assert binary == - :erlang.term_to_binary( - http: [ - key1: [ - _: [ - {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, - {"/websocket", Phoenix.Endpoint.CowboyWebSocket, - {Phoenix.Transports.WebSocket, - {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}}, - {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} - ] - ] - ] - ) - - assert Config.from_binary(binary) == [ - http: [ - key1: [ - {:_, - [ - {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []}, - {"/websocket", Phoenix.Endpoint.CowboyWebSocket, - {Phoenix.Transports.WebSocket, - {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, []}}}, - {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}} - ]} - ] - ] - ] - end - end -end -- cgit v1.2.3