support for updating env after settings deletion
authorAlexander Strizhakov <alex.strizhakov@gmail.com>
Wed, 22 Jan 2020 12:14:11 +0000 (15:14 +0300)
committerAlexander Strizhakov <alex.strizhakov@gmail.com>
Wed, 22 Jan 2020 12:14:11 +0000 (15:14 +0300)
lib/pleroma/config/config_db.ex
lib/pleroma/config/transfer_task.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
test/config/config_db_test.exs
test/config/transfer_task_test.exs
test/web/admin_api/admin_api_controller_test.exs

index 102e2677354b94bda6868c62761af8902f751583..91a1aa0cc2cf3781bf0b759ed2382418bea8a492 100644 (file)
@@ -199,7 +199,7 @@ defmodule Pleroma.ConfigDB do
     end)
   end
 
-  @spec delete(map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()} | {:ok, nil}
+  @spec delete(map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()}
   def delete(params) do
     search_opts = Map.delete(params, :subkeys)
 
@@ -213,11 +213,9 @@ defmodule Pleroma.ConfigDB do
     else
       {:partial_remove, config, []} ->
         Repo.delete(config)
-        {:ok, nil}
 
       {config, nil} ->
         Repo.delete(config)
-        {:ok, nil}
 
       nil ->
         err =
index 00c8790f868516083b9437b378801bfcf578c691..d54f38ee4c022db43703b638a3de24317294474f 100644 (file)
@@ -16,37 +16,36 @@ defmodule Pleroma.Config.TransferTask do
     :ignore
   end
 
-  def load_and_update_env do
+  @spec load_and_update_env([ConfigDB.t()]) :: :ok | false
+  def load_and_update_env(deleted \\ []) do
     with true <- Pleroma.Config.get(:configurable_from_database),
          true <- Ecto.Adapters.SQL.table_exists?(Repo, "config"),
          started_applications <- Application.started_applications() do
       # We need to restart applications for loaded settings take effect
-      ConfigDB
-      |> Repo.all()
-      |> Enum.map(&update_env(&1))
+      in_db = Repo.all(ConfigDB)
+
+      with_deleted = in_db ++ deleted
+
+      with_deleted
+      |> Enum.map(&merge_and_update(&1))
       |> Enum.uniq()
       # TODO: some problem with prometheus after restart!
       |> Enum.reject(&(&1 in [:pleroma, nil, :prometheus]))
       |> Enum.each(&restart(started_applications, &1))
+
+      :ok
     end
   end
 
-  defp update_env(setting) do
+  defp merge_and_update(setting) do
     try do
       key = ConfigDB.from_string(setting.key)
       group = ConfigDB.from_string(setting.group)
-      value = ConfigDB.from_binary(setting.value)
 
       default = Pleroma.Config.Holder.config(group, key)
+      merged_value = merge_value(setting, default, group, key)
 
-      merged_value =
-        if can_be_merged?(default, value) do
-          ConfigDB.merge_group(group, key, default, value)
-        else
-          value
-        end
-
-      :ok = Application.put_env(group, key, merged_value)
+      :ok = update_env(group, key, merged_value)
 
       if group != :logger do
         group
@@ -62,17 +61,36 @@ defmodule Pleroma.Config.TransferTask do
         nil
       end
     rescue
-      e ->
-        Logger.warn(
-          "updating env causes error, group: #{inspect(setting.group)}, key: #{
-            inspect(setting.key)
-          }, value: #{inspect(ConfigDB.from_binary(setting.value))}, error: #{inspect(e)}"
-        )
+      error ->
+        error_msg =
+          "updating env causes error, group: " <>
+            inspect(setting.group) <>
+            " key: " <>
+            inspect(setting.key) <>
+            " value: " <>
+            inspect(ConfigDB.from_binary(setting.value)) <> " error: " <> inspect(error)
+
+        Logger.warn(error_msg)
 
         nil
     end
   end
 
+  defp merge_value(%{__meta__: %{state: :deleted}}, default, _group, _key), do: default
+
+  defp merge_value(setting, default, group, key) do
+    value = ConfigDB.from_binary(setting.value)
+
+    if can_be_merged?(default, value) do
+      ConfigDB.merge_group(group, key, default, value)
+    else
+      value
+    end
+  end
+
+  defp update_env(group, key, nil), do: Application.delete_env(group, key)
+  defp update_env(group, key, value), do: Application.put_env(group, key, value)
+
   defp restart(started_applications, app) do
     with {^app, _, _} <- List.keyfind(started_applications, app, 0),
          :ok <- Application.stop(app) do
index 1b09d137b3ca8639c2f465d69c6272a0230557ca..2314d32741fe0c42dbc6d979b364aa0dd103e5cd 100644 (file)
@@ -871,26 +871,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
   def config_update(conn, %{"configs" => configs}) do
     with :ok <- configurable_from_database(conn) do
-      updated =
+      {_errors, results} =
         Enum.map(configs, fn
           %{"group" => group, "key" => key, "delete" => true} = params ->
-            with {:ok, config} <-
-                   ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]}) do
-              config
-            end
+            ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
 
           %{"group" => group, "key" => key, "value" => value} ->
-            with {:ok, config} <-
-                   ConfigDB.update_or_create(%{group: group, key: key, value: value}) do
-              config
-            end
+            ConfigDB.update_or_create(%{group: group, key: key, value: value})
         end)
-        |> Enum.reject(&is_nil(&1))
-        |> Enum.map(fn config ->
+        |> Enum.split_with(fn result -> elem(result, 0) == :error end)
+
+      {deleted, updated} =
+        results
+        |> Enum.map(fn {:ok, config} ->
           Map.put(config, :db, ConfigDB.get_db_keys(config))
         end)
+        |> Enum.split_with(fn config ->
+          Ecto.get_meta(config, :state) == :deleted
+        end)
 
-      Pleroma.Config.TransferTask.load_and_update_env()
+      Pleroma.Config.TransferTask.load_and_update_env(deleted)
 
       Mix.Tasks.Pleroma.Config.run([
         "migrate_from_db",
index 19619620eadf20fc7bcf8df73fd80d64120cc1f5..61a0b1d5d5e5cf734049b60ef3119910dc626335 100644 (file)
@@ -27,7 +27,7 @@ defmodule Pleroma.ConfigDBTest do
   end
 
   test "get_all_as_keyword/0" do
-    insert(:config)
+    saved = insert(:config)
     insert(:config, group: ":quack", key: ":level", value: ConfigDB.to_binary(:info))
     insert(:config, group: ":quack", key: ":meta", value: ConfigDB.to_binary([:none]))
 
@@ -37,14 +37,17 @@ defmodule Pleroma.ConfigDBTest do
       value: ConfigDB.to_binary("https://hooks.slack.com/services/KEY/some_val")
     )
 
-    assert [
-             pleroma: [{_, %{another: _, another_key: _}}],
-             quack: [
-               level: :info,
-               meta: [:none],
-               webhook_url: "https://hooks.slack.com/services/KEY/some_val"
-             ]
-           ] = ConfigDB.get_all_as_keyword()
+    config = ConfigDB.get_all_as_keyword()
+
+    assert config[:pleroma] == [
+             {ConfigDB.from_string(saved.key), ConfigDB.from_binary(saved.value)}
+           ]
+
+    assert config[:quack] == [
+             level: :info,
+             meta: [:none],
+             webhook_url: "https://hooks.slack.com/services/KEY/some_val"
+           ]
   end
 
   describe "update_or_create/1" do
@@ -191,10 +194,44 @@ defmodule Pleroma.ConfigDBTest do
     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})
+  describe "delete/1" do
+    test "error on deleting non existing setting" do
+      {:error, error} = ConfigDB.delete(%{group: ":pleroma", key: ":key"})
+      assert error =~ "Config with params %{group: \":pleroma\", key: \":key\"} not found"
+    end
+
+    test "full delete" do
+      config = insert(:config)
+      {:ok, deleted} = ConfigDB.delete(%{group: config.group, key: config.key})
+      assert Ecto.get_meta(deleted, :state) == :deleted
+      refute ConfigDB.get_by_params(%{group: config.group, key: config.key})
+    end
+
+    test "partial subkeys delete" do
+      config = insert(:config, value: ConfigDB.to_binary(groups: [a: 1, b: 2], key: [a: 1]))
+
+      {:ok, deleted} =
+        ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]})
+
+      assert Ecto.get_meta(deleted, :state) == :loaded
+
+      assert deleted.value == ConfigDB.to_binary(key: [a: 1])
+
+      updated = ConfigDB.get_by_params(%{group: config.group, key: config.key})
+
+      assert updated.value == deleted.value
+    end
+
+    test "full delete if remaining value after subkeys deletion is empty list" do
+      config = insert(:config, value: ConfigDB.to_binary(groups: [a: 1, b: 2]))
+
+      {:ok, deleted} =
+        ConfigDB.delete(%{group: config.group, key: config.key, subkeys: [":groups"]})
+
+      assert Ecto.get_meta(deleted, :state) == :deleted
+
+      refute ConfigDB.get_by_params(%{group: config.group, key: config.key})
+    end
   end
 
   describe "transform/1" do
index 20dc068636dc570db401ef72bef976103f4c98e6..b9072e0fcaf2a33c5507986217d02c4d7508cc01 100644 (file)
@@ -116,6 +116,6 @@ defmodule Pleroma.Config.TransferTaskTest do
     assert ExUnit.CaptureLog.capture_log(fn ->
              TransferTask.start_link([])
            end) =~
-             "updating env causes error, group: \":pleroma\", key: \":undefined_atom_key\", value: [live: 2, com: 3], error: %ArgumentError{message: \"argument error\"}"
+             "updating env causes error, group: \":pleroma\" key: \":undefined_atom_key\" value: [live: 2, com: 3] error: %ArgumentError{message: \"argument error\"}"
   end
 end
index f4cdaebf9411cd3c0b5520e449ea205474da2e05..5c767219ac39d72b02a0eaf2c824a2ed359b018d 100644 (file)
@@ -2053,6 +2053,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
 
     @tag capture_log: true
     test "create new config setting in db", %{conn: conn} do
+      ueberauth = Application.get_env(:ueberauth, Ueberauth)
+      on_exit(fn -> Application.put_env(:ueberauth, Ueberauth, ueberauth) end)
+
       conn =
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
@@ -2420,25 +2423,26 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
              }
     end
 
-    test "update config setting & delete", %{conn: conn} do
+    test "update config setting & delete with fallback to default value", %{
+      conn: conn,
+      admin: admin,
+      token: token
+    } do
+      ueberauth = Application.get_env(:ueberauth, Ueberauth)
       config1 = insert(:config, key: ":keyaa1")
       config2 = insert(:config, key: ":keyaa2")
 
-      insert(:config,
-        group: "ueberauth",
-        key: "Ueberauth.Strategy.Microsoft.OAuth"
-      )
+      config3 =
+        insert(:config,
+          group: ":ueberauth",
+          key: "Ueberauth"
+        )
 
       conn =
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{group: config1.group, key: config1.key, value: "another_value"},
-            %{group: config2.group, key: config2.key, delete: true},
-            %{
-              group: "ueberauth",
-              key: "Ueberauth.Strategy.Microsoft.OAuth",
-              delete: true
-            }
+            %{group: config2.group, key: config2.key, value: "another_value"}
           ]
         })
 
@@ -2449,12 +2453,41 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    "key" => config1.key,
                    "value" => "another_value",
                    "db" => [":keyaa1"]
+                 },
+                 %{
+                   "group" => ":pleroma",
+                   "key" => config2.key,
+                   "value" => "another_value",
+                   "db" => [":keyaa2"]
                  }
                ]
              }
 
       assert Application.get_env(:pleroma, :keyaa1) == "another_value"
-      refute Application.get_env(:pleroma, :keyaa2)
+      assert Application.get_env(:pleroma, :keyaa2) == "another_value"
+      assert Application.get_env(:ueberauth, Ueberauth) == ConfigDB.from_binary(config3.value)
+
+      conn =
+        build_conn()
+        |> assign(:user, admin)
+        |> assign(:token, token)
+        |> post("/api/pleroma/admin/config", %{
+          configs: [
+            %{group: config2.group, key: config2.key, delete: true},
+            %{
+              group: ":ueberauth",
+              key: "Ueberauth",
+              delete: true
+            }
+          ]
+        })
+
+      assert json_response(conn, 200) == %{
+               "configs" => []
+             }
+
+      assert Application.get_env(:ueberauth, Ueberauth) == ueberauth
+      refute Keyword.has_key?(Application.get_all_env(:pleroma), :keyaa2)
     end
 
     test "common config example", %{conn: conn} do