lib/schemas/environment_feature_state.ex

defmodule Flagsmith.Schemas.Environment.FeatureState do
  use TypedEctoSchema
  import Ecto.Changeset

  alias Flagsmith.Schemas.Environment
  alias Flagsmith.Schemas.Types

  @moduledoc """
  Ecto schema representing a Flagsmith full feature state definition (as represented 
  in the environment definition).
  """

  @primary_key {:featurestate_uuid, :binary_id, autogenerate: false}
  typed_embedded_schema do
    field(:enabled, :boolean)
    field(:django_id, :integer)

    field(:feature_state_value, Types.AnyOf, types: [:string, :integer, :float, :boolean])

    embeds_one(:feature, Environment.Feature)
    embeds_one(:feature_segment, __MODULE__.FeatureSegment)
    embeds_many(:multivariate_feature_state_values, Environment.MultivariateFeatureStateValue)
  end

  @doc false
  @spec changeset(map()) :: Ecto.Changeset.t()
  @spec changeset(__MODULE__.t(), map()) :: Ecto.Changeset.t()
  def changeset(struct \\ %__MODULE__{}, params) do
    struct
    |> cast(params, [:featurestate_uuid, :enabled, :django_id, :feature_state_value])
    |> cast_embed(:feature)
    |> cast_embed(:feature_segment)
    |> cast_embed(:multivariate_feature_state_values)
  end

  @doc false
  @spec from_response(element :: map() | list(map())) :: __MODULE__.t() | any()
  def from_response(element) when is_map(element) do
    element
    |> changeset()
    |> apply_changes()
  end

  def from_response(elements) when is_list(elements) do
    Enum.map(elements, fn element ->
      element
      |> changeset()
      |> apply_changes()
    end)
  end

  def from_response(element), do: element

  @doc false
  def get_hashing_id(%__MODULE__{django_id: nil, featurestate_uuid: id}),
    do: id

  def get_hashing_id(%__MODULE__{django_id: django_id}),
    do: "#{django_id}"

  def get_hashing_id(%{id: id}),
    do: id

  @doc false
  def extract_multivariate_value(%Environment.MultivariateFeatureStateValue{
        multivariate_feature_option: %Environment.MultivariateFeatureOption{
          value: value
        }
      }),
      do: {:ok, value}

  def extract_multivariate_value(_), do: {:error, :invalid_multivariate}

  @doc false
  def is_higher_priority?(%__MODULE__{feature_segment: fs_a}, %__MODULE__{feature_segment: fs_b}) do
    case {fs_a, fs_b} do
      {nil, nil} ->
        false

      {nil, _} ->
        false

      {_, nil} ->
        true

      {%__MODULE__.FeatureSegment{priority: priority_a},
       %__MODULE__.FeatureSegment{priority: priority_b}} ->
        priority_a < priority_b
    end
  end
end