support for config groups
authorAlex S <alex.strizhakov@gmail.com>
Sun, 23 Jun 2019 05:16:16 +0000 (08:16 +0300)
committerAlex S <alex.strizhakov@gmail.com>
Sun, 23 Jun 2019 05:16:16 +0000 (08:16 +0300)
12 files changed:
docs/api/admin_api.md
lib/mix/tasks/pleroma/config.ex
lib/pleroma/config/transfer_task.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
lib/pleroma/web/admin_api/config.ex
lib/pleroma/web/admin_api/views/config_view.ex
priv/repo/migrations/20190622151019_add_group_key_to_config.exs [new file with mode: 0644]
test/config/transfer_task_test.exs
test/support/factory.ex
test/tasks/config_test.exs
test/web/admin_api/admin_api_controller_test.exs
test/web/admin_api/config_test.exs

index 63af338213fcf3a422eee088c029e835fe2485a2..c05a353d763f8ef49ec7e919dba531e08aad9123 100644 (file)
@@ -568,8 +568,9 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
 {
   configs: [
     {
+      "group": string,
       "key": string,
-      "value": string or {} or []
+      "value": string or {} or [] or {"tuple": []}
      }
   ]
 }
@@ -597,8 +598,9 @@ Compile time settings (need instance reboot):
 - Method `POST`
 - Params:
   - `configs` => [
+    - `group` (string)
     - `key` (string)
-    - `value` (string, [], {})
+    - `value` (string, [], {} or {"tuple": []})
     - `delete` = true (optional, if parameter must be deleted)
   ]
 
@@ -608,6 +610,7 @@ Compile time settings (need instance reboot):
 {
   configs: [
     {
+      "group": "pleroma",
       "key": "Pleroma.Upload",
       "value": {
         "uploader": "Pleroma.Uploaders.Local",
@@ -636,6 +639,7 @@ Compile time settings (need instance reboot):
 {
   configs: [
     {
+      "group": string,
       "key": string,
       "value": string or {} or [] or {"tuple": []}
      }
index cc54253624ea06d69a0edb21adc03df84f1a11cb..4ed2c978940fcac2c4fd844f2924c52a80a6124c 100644 (file)
@@ -24,7 +24,7 @@ defmodule Mix.Tasks.Pleroma.Config do
       |> Enum.reject(fn {k, _v} -> k in [Pleroma.Repo, :env] end)
       |> Enum.each(fn {k, v} ->
         key = to_string(k) |> String.replace("Elixir.", "")
-        {:ok, _} = Config.update_or_create(%{key: key, value: v})
+        {:ok, _} = Config.update_or_create(%{group: "pleroma", key: key, value: v})
         Mix.shell().info("#{key} is migrated.")
       end)
 
@@ -51,7 +51,9 @@ defmodule Mix.Tasks.Pleroma.Config do
 
         IO.write(
           file,
-          "config :pleroma, #{config.key}#{mark} #{inspect(Config.from_binary(config.value))}\r\n"
+          "config :#{config.group}, #{config.key}#{mark} #{
+            inspect(Config.from_binary(config.value))
+          }\r\n"
         )
 
         {:ok, _} = Repo.delete(config)
index a8cbfa52a69f2c81cc696ee032dce999ac00b20b..cf880aa22939efeacd7182a52d2c74d13889d862 100644 (file)
@@ -11,8 +11,17 @@ defmodule Pleroma.Config.TransferTask do
   def load_and_update_env do
     if Pleroma.Config.get([:instance, :dynamic_configuration]) and
          Ecto.Adapters.SQL.table_exists?(Pleroma.Repo, "config") do
-      Pleroma.Repo.all(Config)
-      |> Enum.each(&update_env(&1))
+      for_restart =
+        Pleroma.Repo.all(Config)
+        |> Enum.map(&update_env(&1))
+
+      # We need to restart applications for loaded settings take effect
+      for_restart
+      |> Enum.reject(&(&1 in [:pleroma, :ok]))
+      |> Enum.each(fn app ->
+        Application.stop(app)
+        :ok = Application.start(app)
+      end)
     end
   end
 
@@ -25,11 +34,15 @@ defmodule Pleroma.Config.TransferTask do
           setting.key
         end
 
+      group = String.to_existing_atom(setting.group)
+
       Application.put_env(
-        :pleroma,
+        group,
         String.to_existing_atom(key),
         Config.from_binary(setting.value)
       )
+
+      group
     rescue
       e ->
         require Logger
index 03dfdca8299f8b253b4b446a0817c882a78cf4ae..953a22ea09468f023eca96ba858622770f35194b 100644 (file)
@@ -377,12 +377,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
       if Pleroma.Config.get([:instance, :dynamic_configuration]) do
         updated =
           Enum.map(configs, fn
-            %{"key" => key, "value" => value} ->
-              {:ok, config} = Config.update_or_create(%{key: key, value: value})
+            %{"group" => group, "key" => key, "value" => value} ->
+              {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
               config
 
-            %{"key" => key, "delete" => "true"} ->
-              {:ok, _} = Config.delete(key)
+            %{"group" => group, "key" => key, "delete" => "true"} ->
+              {:ok, _} = Config.delete(%{group: group, key: key})
               nil
           end)
           |> Enum.reject(&is_nil(&1))
index 2e149bf256182008e3fc40cd930efd44d3bc95dd..8b9b658a9a0c8efbdd32d26a8e81888a9e39f12d 100644 (file)
@@ -12,26 +12,27 @@ defmodule Pleroma.Web.AdminAPI.Config do
 
   schema "config" do
     field(:key, :string)
+    field(:group, :string)
     field(:value, :binary)
 
     timestamps()
   end
 
-  @spec get_by_key(String.t()) :: Config.t() | nil
-  def get_by_key(key), do: Repo.get_by(Config, key: key)
+  @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
     config
-    |> cast(params, [:key, :value])
-    |> validate_required([:key, :value])
-    |> unique_constraint(:key)
+    |> 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(%{key: key, value: value}) do
+  def create(params) do
     %Config{}
-    |> changeset(%{key: key, value: transform(value)})
+    |> changeset(Map.put(params, :value, transform(params[:value])))
     |> Repo.insert()
   end
 
@@ -43,20 +44,20 @@ defmodule Pleroma.Web.AdminAPI.Config do
   end
 
   @spec update_or_create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
-  def update_or_create(%{key: key} = params) do
-    with %Config{} = config <- Config.get_by_key(key) do
+  def update_or_create(params) do
+    with %Config{} = config <- Config.get_by_params(Map.take(params, [:group, :key])) do
       Config.update(config, params)
     else
       nil -> Config.create(params)
     end
   end
 
-  @spec delete(String.t()) :: {:ok, Config.t()} | {:error, Changeset.t()}
-  def delete(key) do
-    with %Config{} = config <- Config.get_by_key(key) do
+  @spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
+  def delete(params) do
+    with %Config{} = config <- Config.get_by_params(params) do
       Repo.delete(config)
     else
-      nil -> {:error, "Config with key #{key} not found"}
+      nil -> {:error, "Config with params #{inspect(params)} not found"}
     end
   end
 
@@ -90,6 +91,8 @@ defmodule Pleroma.Web.AdminAPI.Config do
   end
 
   @spec transform(any()) :: binary()
+  def transform(%{"tuple" => _} = entity), do: :erlang.term_to_binary(do_transform(entity))
+
   def transform(entity) when is_map(entity) do
     tuples =
       for {k, v} <- entity,
index c8560033eaa76bd6aaaa251592f23e0fac52cfb2..3ccc9ca46cd0d188a4f5204fd60bbdea1d25d17e 100644 (file)
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigView do
   def render("show.json", %{config: config}) do
     %{
       key: config.key,
+      group: config.group,
       value: Pleroma.Web.AdminAPI.Config.from_binary_to_map(config.value)
     }
   end
diff --git a/priv/repo/migrations/20190622151019_add_group_key_to_config.exs b/priv/repo/migrations/20190622151019_add_group_key_to_config.exs
new file mode 100644 (file)
index 0000000..d7a3785
--- /dev/null
@@ -0,0 +1,12 @@
+defmodule Pleroma.Repo.Migrations.AddGroupKeyToConfig do
+  use Ecto.Migration
+
+  def change do
+    alter table("config") do
+      add(:group, :string)
+    end
+
+    drop(unique_index("config", :key))
+    create(unique_index("config", [:group, :key]))
+  end
+end
index 9b8a8dd4548454d28b72b6fb14a7f1a3850bffea..c0e433263577f5a914c76970a7eefb83192d9284 100644 (file)
@@ -13,19 +13,37 @@ defmodule Pleroma.Config.TransferTaskTest do
 
   test "transfer config values from db to env" do
     refute Application.get_env(:pleroma, :test_key)
-    Pleroma.Web.AdminAPI.Config.create(%{key: "test_key", value: [live: 2, com: 3]})
+    refute Application.get_env(:idna, :test_key)
+
+    Pleroma.Web.AdminAPI.Config.create(%{
+      group: "pleroma",
+      key: "test_key",
+      value: [live: 2, com: 3]
+    })
+
+    Pleroma.Web.AdminAPI.Config.create(%{
+      group: "idna",
+      key: "test_key",
+      value: [live: 15, com: 35]
+    })
 
     Pleroma.Config.TransferTask.start_link()
 
     assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3]
+    assert Application.get_env(:idna, :test_key) == [live: 15, com: 35]
 
     on_exit(fn ->
       Application.delete_env(:pleroma, :test_key)
+      Application.delete_env(:idna, :test_key)
     end)
   end
 
   test "non existing atom" do
-    Pleroma.Web.AdminAPI.Config.create(%{key: "undefined_atom_key", value: [live: 2, com: 3]})
+    Pleroma.Web.AdminAPI.Config.create(%{
+      group: "pleroma",
+      key: "undefined_atom_key",
+      value: [live: 2, com: 3]
+    })
 
     assert ExUnit.CaptureLog.capture_log(fn ->
              Pleroma.Config.TransferTask.start_link()
index 5be34660efeade3106feb36a168a5f53e4d6ae15..c2812e8f7d8497de72dbdb84ca64912a6cdf688a 100644 (file)
@@ -314,6 +314,7 @@ defmodule Pleroma.Factory do
   def config_factory do
     %Pleroma.Web.AdminAPI.Config{
       key: sequence(:key, &"some_key_#{&1}"),
+      group: "pleroma",
       value:
         sequence(
           :value,
index d448b04446e285dbb5b6209c72c6b24f442c309f..9c9a31bf48f9c22be1f18398f26dcbff5f9ddd1a 100644 (file)
@@ -30,17 +30,26 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
 
     Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
 
-    first_db = Config.get_by_key("first_setting")
-    second_db = Config.get_by_key("second_setting")
-    refute Config.get_by_key("Pleroma.Repo")
+    first_db = Config.get_by_params(%{group: "pleroma", key: "first_setting"})
+    second_db = Config.get_by_params(%{group: "pleroma", key: "second_setting"})
+    refute Config.get_by_params(%{group: "pleroma", key: "Pleroma.Repo"})
 
     assert Config.from_binary(first_db.value) == [key: "value", key2: [Pleroma.Repo]]
     assert Config.from_binary(second_db.value) == [key: "value2", key2: [Pleroma.Activity]]
   end
 
   test "settings are migrated to file and deleted from db", %{temp_file: temp_file} do
-    Config.create(%{key: "setting_first", value: [key: "value", key2: [Pleroma.Activity]]})
-    Config.create(%{key: "setting_second", value: [key: "valu2", key2: [Pleroma.Repo]]})
+    Config.create(%{
+      group: "pleroma",
+      key: "setting_first",
+      value: [key: "value", key2: [Pleroma.Activity]]
+    })
+
+    Config.create(%{
+      group: "pleroma",
+      key: "setting_second",
+      value: [key: "valu2", key2: [Pleroma.Repo]]
+    })
 
     Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "temp"])
 
index 49889d6d7e18cad4171de2d242c7db31cb858f89..4278ac59dc4d906814bfadc14e237ac3c44febd6 100644 (file)
@@ -1363,8 +1363,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       conn =
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
-            %{key: "key1", value: "value1"},
+            %{group: "pleroma", key: "key1", value: "value1"},
             %{
+              group: "pleroma",
               key: "key2",
               value: %{
                 "nested_1" => "nested_value1",
@@ -1375,6 +1376,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
               }
             },
             %{
+              group: "pleroma",
               key: "key3",
               value: [
                 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
@@ -1382,8 +1384,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
               ]
             },
             %{
+              group: "pleroma",
               key: "key4",
               value: %{"nested_5" => ":upload", "endpoint" => "https://example.com"}
+            },
+            %{
+              group: "idna",
+              key: "key5",
+              value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
             }
           ]
         })
@@ -1391,10 +1399,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert json_response(conn, 200) == %{
                "configs" => [
                  %{
+                   "group" => "pleroma",
                    "key" => "key1",
                    "value" => "value1"
                  },
                  %{
+                   "group" => "pleroma",
                    "key" => "key2",
                    "value" => [
                      %{"nested_1" => "nested_value1"},
@@ -1407,6 +1417,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    ]
                  },
                  %{
+                   "group" => "pleroma",
                    "key" => "key3",
                    "value" => [
                      [%{"nested_3" => "nested_3"}, %{"nested_33" => "nested_33"}],
@@ -1414,8 +1425,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    ]
                  },
                  %{
+                   "group" => "pleroma",
                    "key" => "key4",
                    "value" => [%{"endpoint" => "https://example.com"}, %{"nested_5" => "upload"}]
+                 },
+                 %{
+                   "group" => "idna",
+                   "key" => "key5",
+                   "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
                  }
                ]
              }
@@ -1439,6 +1456,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                endpoint: "https://example.com",
                nested_5: :upload
              ]
+
+      assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
     end
 
     test "update config setting & delete", %{conn: conn} do
@@ -1448,14 +1467,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       conn =
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
-            %{key: config1.key, value: "another_value"},
-            %{key: config2.key, delete: "true"}
+            %{group: config1.group, key: config1.key, value: "another_value"},
+            %{group: config2.group, key: config2.key, delete: "true"}
           ]
         })
 
       assert json_response(conn, 200) == %{
                "configs" => [
                  %{
+                   "group" => "pleroma",
                    "key" => config1.key,
                    "value" => "another_value"
                  }
@@ -1471,6 +1491,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{
+              "group" => "pleroma",
               "key" => "Pleroma.Captcha.NotReal",
               "value" => %{
                 "enabled" => ":false",
@@ -1484,6 +1505,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert json_response(conn, 200) == %{
                "configs" => [
                  %{
+                   "group" => "pleroma",
                    "key" => "Pleroma.Captcha.NotReal",
                    "value" => [
                      %{"enabled" => false},
@@ -1500,6 +1522,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{
+              "group" => "pleroma",
               "key" => "Pleroma.Web.Endpoint.NotReal",
               "value" => [
                 %{
@@ -1557,6 +1580,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert json_response(conn, 200) == %{
                "configs" => [
                  %{
+                   "group" => "pleroma",
                    "key" => "Pleroma.Web.Endpoint.NotReal",
                    "value" => [
                      %{
index 39050c27682b85ff6516da3fb86687c4f51e89b3..10cb3b68a859effe119877aeabc60926899a0836 100644 (file)
@@ -7,18 +7,18 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
     config = insert(:config)
     insert(:config)
 
-    assert config == Config.get_by_key(config.key)
+    assert config == Config.get_by_params(%{group: config.group, key: config.key})
   end
 
   test "create/1" do
-    {:ok, config} = Config.create(%{key: "some_key", value: "some_value"})
-    assert config == Config.get_by_key("some_key")
+    {: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_key(config.key)
+    loaded = Config.get_by_params(%{group: config.group, key: config.key})
     assert loaded == updated
   end
 
@@ -27,8 +27,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
     key2 = "another_key"
 
     params = [
-      %{key: key2, value: "another_value"},
-      %{key: config.key, value: "new_value"}
+      %{group: "pleroma", key: key2, value: "another_value"},
+      %{group: config.group, key: config.key, value: "new_value"}
     ]
 
     assert Repo.all(Config) |> length() == 1
@@ -37,8 +37,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
 
     assert Repo.all(Config) |> length() == 2
 
-    config1 = Config.get_by_key(config.key)
-    config2 = Config.get_by_key(key2)
+    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")
@@ -46,8 +46,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
 
   test "delete/1" do
     config = insert(:config)
-    {:ok, _} = Config.delete(config.key)
-    refute Config.get_by_key(config.key)
+    {: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