Skip to content

Commit

Permalink
Merge pull request #3 from Recruitee/add_on_state_change_mfa
Browse files Browse the repository at this point in the history
Add `:on_state_change` mfa
  • Loading branch information
andrzej-mag authored Jun 21, 2022
2 parents a06208d + 12b5092 commit 3e3957c
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 11 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## v0.3.0 unreleased

### Enhancements
* [Existence] Added `:on_state_change` option.

## v0.2.0 [2022-06-13]

### Enhancements
Expand Down
28 changes: 22 additions & 6 deletions lib/existence.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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),
Expand All @@ -457,4 +470,7 @@ defmodule Existence do
_ -> false
end
end

defp parse_state(:healthy), do: :ok
defp parse_state(_), do: :error
end
26 changes: 21 additions & 5 deletions test/existence_dynamic_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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: %{
Expand All @@ -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

0 comments on commit 3e3957c

Please sign in to comment.