total rebase
[anni] / lib / pleroma / migrators / support / base_migrator_state.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Migrators.Support.BaseMigratorState do
6   @moduledoc """
7   Base background migrator state functionality.
8   """
9
10   @callback data_migration() :: Pleroma.DataMigration.t()
11
12   defmacro __using__(_opts) do
13     quote do
14       use Agent
15
16       alias Pleroma.DataMigration
17
18       @behaviour Pleroma.Migrators.Support.BaseMigratorState
19       @reg_name {:global, __MODULE__}
20
21       def start_link(_) do
22         Agent.start_link(fn -> load_state_from_db() end, name: @reg_name)
23       end
24
25       def data_migration, do: raise("data_migration/0 is not implemented")
26       defoverridable data_migration: 0
27
28       defp load_state_from_db do
29         data_migration = data_migration()
30
31         data =
32           if data_migration do
33             Map.new(data_migration.data, fn {k, v} -> {String.to_atom(k), v} end)
34           else
35             %{}
36           end
37
38         %{
39           data_migration_id: data_migration && data_migration.id,
40           data: data
41         }
42       end
43
44       def persist_to_db do
45         %{data_migration_id: data_migration_id, data: data} = state()
46
47         if data_migration_id do
48           DataMigration.update_one_by_id(data_migration_id, data: data)
49         else
50           {:error, :nil_data_migration_id}
51         end
52       end
53
54       def reset do
55         %{data_migration_id: data_migration_id} = state()
56
57         with false <- is_nil(data_migration_id),
58              :ok <-
59                DataMigration.update_one_by_id(data_migration_id,
60                  state: :pending,
61                  data: %{}
62                ) do
63           reinit()
64         else
65           true -> {:error, :nil_data_migration_id}
66           e -> e
67         end
68       end
69
70       def reinit do
71         Agent.update(@reg_name, fn _state -> load_state_from_db() end)
72       end
73
74       def state do
75         Agent.get(@reg_name, & &1)
76       end
77
78       def get_data_key(key, default \\ nil) do
79         get_in(state(), [:data, key]) || default
80       end
81
82       def put_data_key(key, value) do
83         _ = persist_non_data_change(key, value)
84
85         Agent.update(@reg_name, fn state ->
86           put_in(state, [:data, key], value)
87         end)
88       end
89
90       def increment_data_key(key, increment \\ 1) do
91         Agent.update(@reg_name, fn state ->
92           initial_value = get_in(state, [:data, key]) || 0
93           updated_value = initial_value + increment
94           put_in(state, [:data, key], updated_value)
95         end)
96       end
97
98       defp persist_non_data_change(:state, value) do
99         with true <- get_data_key(:state) != value,
100              true <- value in Pleroma.DataMigration.State.__valid_values__(),
101              %{data_migration_id: data_migration_id} when not is_nil(data_migration_id) <-
102                state() do
103           DataMigration.update_one_by_id(data_migration_id, state: value)
104         else
105           false -> :ok
106           _ -> {:error, :nil_data_migration_id}
107         end
108       end
109
110       defp persist_non_data_change(_, _) do
111         nil
112       end
113
114       def data_migration_id, do: Map.get(state(), :data_migration_id)
115     end
116   end
117 end