Current section
Files
Jump to
Current section
Files
lib/feature_flipper/consul_client.ex
defmodule FeatureFlipper.ConsulClient do
@moduledoc """
HTTP client for interacting with Consul KV store.
"""
use Tesla
require Logger
plug(Tesla.Middleware.BaseUrl)
plug(Tesla.Middleware.JSON)
plug(Tesla.Middleware.Timeout, timeout: 5_000)
plug(Tesla.Middleware.Retry, delay: 500, max_retries: 3)
@doc """
Get feature flipper data from Consul KV store.
"""
def get_feature_flippers(config) do
base_path = build_consul_path(config)
case get_kv_data(config.consul_client_http_addr, base_path) do
{:ok, data} ->
parsed_data = parse_consul_response(data)
{:ok, parsed_data}
{:error, reason} ->
Logger.error("Failed to fetch from Consul: #{inspect(reason)}")
{:error, reason}
end
end
defp build_consul_path(config) do
path = "#{config.consul_kv_path}/feature-flippers"
case config.version_suffix do
nil -> path
suffix -> "#{path}/#{suffix}"
end
end
defp get_kv_data(consul_addr, path) do
client =
Tesla.client([
{Tesla.Middleware.BaseUrl, consul_addr}
])
case Tesla.get(client, "/v1/kv/#{path}?raw=true") do
{:ok, %Tesla.Env{status: 200, body: body}} ->
{:ok, Jason.decode!(body)}
{:ok, %Tesla.Env{status: 404}} ->
Logger.info("No feature flipper data found in Consul at path: #{path}")
{:ok, []}
{:ok, %Tesla.Env{status: status, body: body}} ->
{:error, {:http_error, status, body}}
{:error, reason} ->
{:error, {:network_error, reason}}
end
end
defp parse_consul_response(consul_data) when is_map(consul_data) do
consul_data
|> Enum.map(fn item ->
case parse_consul_item(item) do
{:ok, key, value} -> {key, value}
{:error, _reason} -> nil
end
end)
|> Enum.into(%{})
end
defp parse_consul_response(_), do: %{}
defp parse_consul_item({key, value}) do
key = extract_key_name(key)
value = convert_to_boolean(value)
{:ok, key, value}
end
defp parse_consul_item(_), do: {:error, :invalid_format}
defp extract_key_name(full_key) do
full_key
|> String.split("/")
|> List.last()
end
defp convert_to_boolean(value) when is_binary(value) do
case String.downcase(String.trim(value)) do
"true" -> true
_ -> false
end
end
defp convert_to_boolean(value) when is_boolean(value), do: value
defp convert_to_boolean(_), do: false
end