diff --git a/CHANGELOG.md b/CHANGELOG.md index 9489ed9..cb56ed0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## v0.3.0 unreleased + +### Enhancements +* [Existence] Added `:on_state_change` option. + ## v0.2.0 [2022-06-13] ### Enhancements diff --git a/lib/existence.ex b/lib/existence.ex index 042db51..0a4bea4 100644 --- a/lib/existence.ex +++ b/lib/existence.ex @@ -91,6 +91,11 @@ defmodule Existence do * `:checks` - keyword list with user defined dependencies checks parameters, see description below for details. Default: `[]`. * `:state` - initial overall `Existence` instance health-check state. Default: `:error`. + * `:on_state_change` - MFA tuple pointing at user function which will be executed on the overall + state change. + User function should be of two arity. As a first argument it will receive current state as + `:ok | :error` atom. As a second argument function will receive static arg given in the MFA tuple. + Default: `nil`. Dependencies checks are defined using a keyword list with configuration parameters defined as a maps. @@ -331,7 +336,11 @@ defmodule Existence do Process.send_after(self(), {:spawn_check, check_id}, Map.fetch!(params, :initial_delay)) end) - data = %{checks: checks, ets_tab: ets_tab} + data = %{ + checks: checks, + ets_tab: ets_tab, + on_state_change: Keyword.get(args, :on_state_change) + } case Keyword.get(args, :state, :error) do :ok -> {:ok, :healthy, data} @@ -439,11 +448,15 @@ defmodule Existence do Map.put(data, :checks, checks) end - defp set_state(%{ets_tab: ets_tab}, state) do - case state do - :healthy -> :ets.insert(ets_tab, {:state, :ok}) - _ -> :ets.insert(ets_tab, {:state, :error}) - end + defp set_state(%{ets_tab: ets_tab, on_state_change: {m, f, a}}, state) do + state = parse_state(state) + :ets.insert(ets_tab, {:state, state}) + apply(m, f, [state, a]) + end + + defp set_state(%{ets_tab: ets_tab, on_state_change: nil}, state) do + state = parse_state(state) + :ets.insert(ets_tab, {:state, state}) end defp set_check_state(%{ets_tab: ets_tab}, check_id, result), @@ -457,4 +470,7 @@ defmodule Existence do _ -> false end end + + defp parse_state(:healthy), do: :ok + defp parse_state(_), do: :error end diff --git a/test/existence_dynamic_test.exs b/test/existence_dynamic_test.exs index 2123706..947215f 100644 --- a/test/existence_dynamic_test.exs +++ b/test/existence_dynamic_test.exs @@ -5,8 +5,11 @@ defmodule Existence.DynamicTest do def check_1(), do: :persistent_term.get(:check_1, :ok) def check_2(), do: :persistent_term.get(:check_2, :ok) + def on_state_change(state, "args"), do: :persistent_term.put(:on_state_change, state) setup do + :ok = reset_on_state_changed() + [ checks: [ check_1: %{ @@ -19,43 +22,56 @@ defmodule Existence.DynamicTest do initial_delay: 0, interval: 10 } - ] + ], + on_state_change: {__MODULE__, :on_state_change, "args"} ] |> start() end + defp get_on_state_changed(), do: :persistent_term.get(:on_state_change) + defp reset_on_state_changed(), do: :persistent_term.put(:on_state_change, nil) + test "returns valid overall state and valid checks states on dynamic checks changes" do assert :ok == Existence.get_state() assert :ok == Existence.get_state!() assert [check_1: :ok, check_2: :ok] == Existence.get_checks() |> Enum.sort() assert [check_1: :ok, check_2: :ok] == Existence.get_checks!() |> Enum.sort() + assert :ok == get_on_state_changed() - :persistent_term.put(:check_1, :error) + :ok = reset_on_state_changed() + :ok = :persistent_term.put(:check_1, :error) Process.sleep(20) assert :error == Existence.get_state() assert :error == Existence.get_state!() assert [check_1: :error, check_2: :ok] == Existence.get_checks() |> Enum.sort() assert [check_1: :error, check_2: :ok] == Existence.get_checks!() |> Enum.sort() + assert :error == get_on_state_changed() - :persistent_term.put(:check_2, :error) + :ok = reset_on_state_changed() + :ok = :persistent_term.put(:check_2, :error) Process.sleep(20) assert :error == Existence.get_state() assert :error == Existence.get_state!() assert [check_1: :error, check_2: :error] == Existence.get_checks() |> Enum.sort() assert [check_1: :error, check_2: :error] == Existence.get_checks!() |> Enum.sort() + assert nil == get_on_state_changed() - :persistent_term.put(:check_1, :ok) + :ok = reset_on_state_changed() + :ok = :persistent_term.put(:check_1, :ok) Process.sleep(20) assert :error == Existence.get_state() assert :error == Existence.get_state!() assert [check_1: :ok, check_2: :error] == Existence.get_checks() |> Enum.sort() assert [check_1: :ok, check_2: :error] == Existence.get_checks!() |> Enum.sort() + assert nil == get_on_state_changed() - :persistent_term.put(:check_2, :ok) + :ok = reset_on_state_changed() + :ok = :persistent_term.put(:check_2, :ok) Process.sleep(20) assert :ok == Existence.get_state() assert :ok == Existence.get_state!() assert [check_1: :ok, check_2: :ok] == Existence.get_checks() |> Enum.sort() assert [check_1: :ok, check_2: :ok] == Existence.get_checks!() |> Enum.sort() + assert :ok == get_on_state_changed() end end