Togglr SDK for Elixir

Hex.pmHex DocsBuild Status

Official Elixir SDK for Togglr feature flag management system. This SDK provides a simple and efficient way to evaluate feature flags in your Elixir applications with built-in caching, retry logic, and error handling.

Features

Installation

Add togglr_sdk to your list of dependencies in mix.exs:

def deps do
  [
    {:togglr_sdk, "~> 1.0"}
  ]
end

Then run mix deps.get to install the dependencies.

Quick Start

# Create a client
{:ok, client} = TogglrSdk.new_client("your-api-key")

# Create a context
context = TogglrSdk.RequestContext.new()
|> TogglrSdk.RequestContext.with_user_id("123")
|> TogglrSdk.RequestContext.with_country("US")

# Evaluate a feature
{:ok, result} = TogglrSdk.Client.evaluate(client, "new_ui", context)
IO.puts("Feature enabled: #{result.enabled}")

# Check if feature is enabled
{:ok, enabled} = TogglrSdk.Client.is_enabled(client, "new_ui", context)
IO.puts("Feature enabled: #{enabled}")

# Check with default value
{:ok, enabled} = TogglrSdk.Client.is_enabled_or_default(client, "new_ui", context, false)
IO.puts("Feature enabled: #{enabled}")

# Health check
{:ok, healthy} = TogglrSdk.Client.health_check(client)
IO.puts("API healthy: #{healthy}")

# Clean up
:ok = TogglrSdk.Client.close(client)

Configuration

The SDK can be configured with various options:

# Using the convenience function
opts = [
  base_url: "https://api.togglr.com",
  timeout: 60000,
  retries: 5,
  cache_enabled: true,
  cache_max_size: 2000,
  cache_ttl: 120,
  insecure: true  # Skip SSL verification for self-signed certificates
]
{:ok, client} = TogglrSdk.new_client("your-api-key", opts)

# Or using the configuration module directly
config = TogglrSdk.Config.default("your-api-key")
|> TogglrSdk.Config.with_base_url("https://api.togglr.com")
|> TogglrSdk.Config.with_timeout(60000)
|> TogglrSdk.Config.with_retries(5)
|> TogglrSdk.Config.with_cache(true, 2000, 120)
|> TogglrSdk.Config.with_logger(MyLogger)

{:ok, client} = TogglrSdk.Client.new(config)

Configuration Options

TLS Configuration

The SDK supports TLS client certificates and custom CA certificates for secure connections:

# Basic TLS with client certificate
{:ok, client} = TogglrSdk.new_client("your-api-key", [
  base_url: "https://api.togglr.com",
  client_cert: "/path/to/client.crt",
  client_key: "/path/to/client.key"
])

# TLS with custom CA certificate
{:ok, client} = TogglrSdk.new_client("your-api-key", [
  base_url: "https://api.togglr.com",
  ca_cert: "/path/to/ca.crt"
])

# Full TLS configuration
{:ok, client} = TogglrSdk.new_client("your-api-key", [
  base_url: "https://api.togglr.com",
  client_cert: "/path/to/client.crt",
  client_key: "/path/to/client.key",
  ca_cert: "/path/to/ca.crt"
])

# Using configuration builder pattern
config = TogglrSdk.Config.default("your-api-key")
|> TogglrSdk.Config.with_base_url("https://api.togglr.com")
|> TogglrSdk.Config.with_client_cert_and_key("/path/to/client.crt", "/path/to/client.key")
|> TogglrSdk.Config.with_ca_cert("/path/to/ca.crt")

{:ok, client} = TogglrSdk.Client.new(config)

# Insecure mode (skip TLS verification) - use with caution
{:ok, client} = TogglrSdk.new_client("your-api-key", [
  base_url: "https://api.togglr.com",
  insecure: true
])

Available TLS configuration methods:

Usage

Request Context

The request context allows you to provide user attributes and other data for feature evaluation. The SDK provides many convenient with_* methods for common attributes:

# Basic user information
context = TogglrSdk.RequestContext.new()
|> TogglrSdk.RequestContext.with_user_id("123")
|> TogglrSdk.RequestContext.with_user_email("user@example.com")
|> TogglrSdk.RequestContext.with_anonymous(false)

# Location information
|> TogglrSdk.RequestContext.with_country("US")
|> TogglrSdk.RequestContext.with_region("us-west")
|> TogglrSdk.RequestContext.with_city("San Francisco")

# Device information
|> TogglrSdk.RequestContext.with_device_type("mobile")
|> TogglrSdk.RequestContext.with_manufacturer("Apple")
|> TogglrSdk.RequestContext.with_os("iOS")
|> TogglrSdk.RequestContext.with_os_version("15.0")

# Browser information
|> TogglrSdk.RequestContext.with_browser("Safari")
|> TogglrSdk.RequestContext.with_browser_version("15.0")

# User demographics
|> TogglrSdk.RequestContext.with_age(25)
|> TogglrSdk.RequestContext.with_gender("female")

# Technical details
|> TogglrSdk.RequestContext.with_language("en-US")
|> TogglrSdk.RequestContext.with_connection_type("wifi")
|> TogglrSdk.RequestContext.with_ip("192.168.1.1")
|> TogglrSdk.RequestContext.with_app_version("1.2.3")
|> TogglrSdk.RequestContext.with_platform("ios")

# Custom attributes
|> TogglrSdk.RequestContext.set("plan", "premium")
|> TogglrSdk.RequestContext.set_many(%{
  "user.role" => "admin",
  "user.team" => "engineering"
})

Available Context Methods

User Information:

Location Information:

Device Information:

Browser Information:

Technical Details:

Custom Attributes:

Feature Evaluation

# Evaluate a feature
{:ok, result} = TogglrSdk.Client.evaluate(client, "new_ui", context)
# result = %{value: "enabled", enabled: true, found: true}

# Check if feature is enabled
{:ok, enabled} = TogglrSdk.Client.is_enabled(client, "new_ui", context)
# enabled = true

# Check with default value
{:ok, enabled} = TogglrSdk.Client.is_enabled_or_default(client, "new_ui", context, false)
# enabled = true

Error Reporting and Auto-Disable

The SDK supports reporting errors for features, which can trigger automatic disabling based on error rates:

# Report an error for a feature
:ok = TogglrSdk.Client.report_error(
  client,
  "feature_key",
  "timeout",
  "Service did not respond in 5s",
  %{service: "payment-gateway", timeout_ms: 5000}
)

IO.puts("Error reported successfully - queued for processing")

Error Types

Supported error types:

Context Data

You can provide additional context with error reports:

context = %{
  service: "payment-gateway",
  timeout_ms: 5000,
  user_id: "user123",
  region: "us-east-1"
}

:ok = TogglrSdk.Client.report_error(
  client,
  "feature_key",
  "timeout",
  "Service timeout",
  context
)

Feature Health Monitoring

Monitor the health status of features:

# Get detailed health information
{:ok, health} = TogglrSdk.Client.get_feature_health(client, "feature_key")

IO.puts("Feature: #{health.feature_key}")
IO.puts("Enabled: #{health.enabled}")
IO.puts("Auto Disabled: #{health.auto_disabled}")
IO.puts("Error Rate: #{health.error_rate}")
IO.puts("Threshold: #{health.threshold}")
IO.puts("Last Error At: #{health.last_error_at}")

# Simple health check
{:ok, is_healthy} = TogglrSdk.Client.is_feature_healthy(client, "feature_key")
IO.puts("Feature is healthy: #{is_healthy}")

FeatureHealth Model

The TogglrSdk.Models.FeatureHealth struct provides:

ErrorReport Model

The TogglrSdk.Models.ErrorReport struct provides:

# Create an error report
error_report = TogglrSdk.Models.ErrorReport.new(
  "timeout",
  "Service timeout",
  %{service: "api", timeout_ms: 5000}
)

# Convert to map for API requests
error_data = TogglrSdk.Models.ErrorReport.to_map(error_report)

Error Handling

The SDK provides specific exceptions for different error scenarios:

case TogglrSdk.Client.evaluate(client, "feature", context) do
  {:ok, result} ->
    # Handle success
    process_result(result)

  {:error, %TogglrSdk.Exceptions.FeatureNotFoundException{feature_key: feature_key}} ->
    # Handle feature not found
    IO.puts("Feature '#{feature_key}' not found")

  {:error, %TogglrSdk.Exceptions.UnauthorizedException{}} ->
    # Handle authentication error
    IO.puts("Authentication failed")

  {:error, %TogglrSdk.Exceptions.BadRequestException{}} ->
    # Handle bad request
    IO.puts("Invalid request")

  {:error, reason} ->
    # Handle other errors
    IO.puts("Error: #{inspect(reason)}")
end

Caching

The SDK includes built-in LRU caching with TTL for improved performance:

# Enable caching with custom settings
config = TogglrSdk.Config.default("your-api-key")
|> TogglrSdk.Config.with_cache(true, 2000, 120)  # enabled, max_size, ttl_seconds

{:ok, client} = TogglrSdk.Client.new(config)

Cache statistics are available through the cache module:

# Get cache statistics
stats = TogglrSdk.Cache.stats()
# stats = %{size: 150, hits: 1200, misses: 300}

Retry Logic

Configure exponential backoff for retries:

# Create custom backoff configuration
backoff = TogglrSdk.BackoffConfig.new(0.5, 10.0, 1.5)  # base_delay, max_delay, multiplier

config = TogglrSdk.Config.default("your-api-key")
|> TogglrSdk.Config.with_backoff(backoff)
|> TogglrSdk.Config.with_retries(5)

{:ok, client} = TogglrSdk.Client.new(config)

Telemetry

The SDK emits telemetry events for monitoring and observability:

defmodule MyTelemetry do
  def handle_event([:togglr_sdk, :evaluate, :start], measurements, metadata, _config) do
    # Handle evaluation start
    IO.puts("Starting feature evaluation: #{metadata.feature_key}")
  end

  def handle_event([:togglr_sdk, :evaluate, :stop], measurements, metadata, _config) do
    # Handle evaluation complete
    IO.puts("Feature evaluation completed in #{measurements.duration}ms")
  end

  def handle_event([:togglr_sdk, :evaluate, :exception], measurements, metadata, _config) do
    # Handle evaluation error
    IO.puts("Feature evaluation failed: #{inspect(metadata.kind)}")
  end
end

# Attach telemetry handler
:telemetry.attach("togglr-sdk", [:togglr_sdk, :evaluate], &MyTelemetry.handle_event/4, nil)

Development

Prerequisites

Setup

# Clone the repository
git clone https://github.com/togglr/togglr-sdk-elixir.git
cd togglr-sdk-elixir

# Install dependencies
mix deps.get

# Run tests
mix test

# Run linting
mix credo --strict
mix dialyzer

# Format code
mix format

# Generate documentation
mix docs

OpenAPI Client Generation

This project automatically generates the API client from OpenAPI specifications. See DEVELOPMENT.md for detailed information about the generation process.

Quick commands:

# Generate API client
make generate

# Full regeneration (clean + generate)
make regenerate

# Apply modifications only
make post-generate

Building

# Build the package
mix hex.build

# Publish to Hex (requires authentication)
mix hex.publish

Examples

See the examples/ directory for complete examples:

Run examples with:

elixir examples/simple_example.exs
elixir examples/tls_example.exs

Requirements

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For support, please open an issue on GitHub or contact the Togglr team.

Changelog

See CHANGELOG.md for a list of changes and version history.