Skip to main content

lib/parapet/integrations/rulestead.ex

defmodule Parapet.Integrations.Rulestead do
  @moduledoc """
  Telemetry adapter for buffering Rulestead flag changes natively inside Parapet's spine.

  > #### Experimental {: .warning}
  >
  > This module is **experimental** in v1.x. Its API may change in a minor release with a
  > single-version notice in CHANGELOG.md. See
  > [Stability & Deprecation Policy](stability.html) for details.
  """

  @behaviour Parapet.Integration

  require Logger
  alias Parapet.Spine.SystemEvent

  @doc """
  Activates the Rulestead integration via `Parapet.attach/1`.

  Delegates to `attach/0`, which attaches the telemetry handler for Rulestead flag mutations.
  """
  @impl true
  def setup, do: attach()

  @doc """
  Attaches telemetry handlers to buffer Rulestead flag mutations.
  """
  def attach do
    :telemetry.attach(
      "parapet-rulestead-telemetry",
      [:rulestead, :admin, :ruleset, :published],
      &__MODULE__.handle_event/4,
      nil
    )
  end

  @doc false
  def handle_event(_event, _measurements, metadata, _config) do
    if is_map(metadata) do
      repo = Application.get_env(:parapet, :repo)

      if repo do
        changeset =
          SystemEvent.changeset(%SystemEvent{}, %{
            type: "rulestead_flag_change",
            payload: stringify_keys(metadata)
          })

        case repo.insert(changeset) do
          {:ok, _} ->
            safe_metadata =
              metadata
              |> Map.put(:ruleset, metadata[:ruleset_id])
              |> Map.delete(:ruleset_id)

            :telemetry.execute([:parapet, :rulestead, :flag_change], %{}, safe_metadata)
            :ok

          {:error, error} ->
            Logger.warning(
              "[Parapet.Integrations.Rulestead] Failed to insert SystemEvent: #{inspect(error)}"
            )

            :ok
        end
      else
        :ok
      end
    else
      :ok
    end
  rescue
    e ->
      Logger.error(
        "[Parapet.Integrations.Rulestead] Error in telemetry handler: #{Exception.message(e)}"
      )

      :ok
  end

  defp stringify_keys(map) when is_map(map) do
    Map.new(map, fn {k, v} -> {to_string(k), v} end)
  end
end