defmodule Exbox.Flags do
@moduledoc """
An Elixir wrapper around the LaunchDarkly Erlang client
To use this module you must have the LaunchDarkly Erlang client installed.
To do so, add {:ldclient, "~> 2.0.0", hex: :launchdarkly_server_sdk} to your
list of dependencies in mix.exs.
To start the client, call Exbox.Flags.start/2 when starting your application
with a map of config options and an optional tag:
iex> def start(_type, _args) do
iex> Exbox.Flags.start(%{sdk_key: "sdk-key", private_attributes: [:email]})
To make sure that the client shuts down, you should call Exbox.Flags.stop/1
when your application shuts down:
iex> def stop(_state) do
iex> Exbox.Flags.stop()
"""
@doc """
Starts the LaunchDarkly client with the given config and tag.
Examples:
iex> Exbox.Flags.start(%{sdk_key: "sdk-key"}, :my_tag)
:ok
iex> Exbox.Flags.start(%{sdk_key: "sdk-key"}, :my_tag)
{:error, {:already_started, #PID<0.602.0>}}
"""
@spec start() :: :ok | {:error, atom(), term()}
def start do
Application.fetch_env!(:exbox, :flags)
|> Enum.into(%{})
|> start(:default)
end
@doc """
Starts the LaunchDarkly client with the given config and tag.
"""
@spec start(map(), atom()) :: :ok | {:error, atom(), term()}
def start(%{sdk_key: sdk_key} = config, tag) do
sdk_key
|> String.to_charlist()
|> :ldclient.start_instance(tag, parse_config(config))
rescue
# Well this is annoying. When attempting to start the LDclient twice it errors out rather
# than returning a map of the form {:error, {:already_started, #PID<0.602.0>}} (which is the desired behaviour).
# Having a look at the source code shows that this is a known issue since there is just a TODO stating:
# 'check if Tag already exists and return already_started error'.
# The MatchError is a struct of the form %MatchError{term: {:error, {:already_started, #PID<0.602.0>}}
# so we just return the 'term' attribute, giving us what we want.
e in MatchError ->
e.term
end
@doc """
Starts the LaunchDarkly client with the given config.
"""
@spec start(map()) :: :ok | {:error, atom(), any()}
def start(config), do: start(config, :default)
@doc """
Gets the variation of a flag for the given key, context, default value, and tag.
Examples:
iex> Exbox.Flags.variation("my-flag", %{key: "user-key"}, false, :my_tag)
true
iex> Exbox.Flags.variation("my-flag", %{key: "user-key"}, false, :my_tag)
{:error, {:not_found, "my-flag"}}
"""
@spec variation(String.t(), map(), any(), atom()) :: any()
def variation(key, context, default, tag) do
:ldclient.variation(key, :ldclient_context.new_from_map(context), default, tag)
end
@doc """
Gets the variation of a flag for the given key, context, and default value.
"""
@spec variation(String.t(), map(), any()) :: any()
def variation(key, context_key, default), do: variation(key, context_key, default, :default)
@doc """
Stops the ldclient with the given tag.
"""
@spec stop(atom()) :: :ok
def stop(tag) do
:ldclient.stop_instance(tag)
end
@doc """
Stops the ldclient with the default tag.
"""
@spec stop() :: :ok
def stop, do: stop(:default)
defp parse_config(config) do
config
|> Map.delete(:sdk_key)
|> Map.update(:file_paths, [], fn paths -> Enum.map(paths, &String.to_charlist/1) end)
end
end