HipcallSMS.Adapter behaviour (HipcallSMS v0.3.0)
View SourceSpecification of the SMS delivery adapter.
This module defines the behavior that all SMS adapters must implement and provides utility functions for configuration validation and dependency checking.
Creating an Adapter
To create a new SMS adapter, you need to implement the HipcallSMS.Adapter
behavior
and use the __using__/1
macro to get default implementations of common functions.
Example
defmodule MyApp.Adapters.CustomProvider do
use HipcallSMS.Adapter, required_config: [:api_key, :secret]
@impl HipcallSMS.Adapter
def deliver(%HipcallSMS.SMS{} = sms, config) do
# Implementation for sending SMS through your provider
{:ok, %{id: "msg_123", status: "sent"}}
end
end
Configuration
Adapters can specify required configuration keys that must be present when
delivering an SMS. The validate_config/1
callback will automatically check
for these keys.
Dependencies
Adapters can also specify required dependencies that must be available at runtime. This is useful for HTTP clients or other external libraries.
Summary
Callbacks
Delivers a SMS with the given config.
Gets the account balance from the SMS provider.
Gets a configuration value with a default fallback.
Validates the adapter configuration.
Validates that required dependencies are available.
Functions
Macro for implementing SMS adapters.
Gets a configuration value from the application environment.
Validates that all required configuration keys are present.
Validates that all required dependencies are available.
Types
Callbacks
@callback deliver(sms(), config()) :: delivery_result()
Delivers a SMS with the given config.
This callback must be implemented by all adapters to handle the actual SMS delivery through the provider's API.
Parameters
sms
- The SMS struct containing message detailsconfig
- Configuration keyword list with provider-specific settings
Returns
{:ok, response}
- Success with provider response map{:error, reason}
- Failure with error details
Configuration Override
Client library configuration can be overwritten at runtime by passing a configuration map as the last argument. This is useful for multi-tenant applications or when you need to use different credentials per request.
Examples
# Basic delivery with application config
{:ok, response} = MyAdapter.deliver(sms, [])
# Delivery with runtime config override
config_override = [
adapter: HipcallSMS.Adapters.Twilio,
account_sid: "ACxxxxx",
auth_token: "runtime_token"
]
{:ok, response} = MyAdapter.deliver(sms, config_override)
Gets the account balance from the SMS provider.
This callback retrieves the current account balance information from the provider's API. The response format may vary between providers but should include balance information.
Parameters
config
- Configuration keyword list with provider-specific settings
Returns
{:ok, balance_info}
- Success with balance information map{:error, reason}
- Failure with error details
Examples
# Get balance with application config
{:ok, balance} = MyAdapter.get_balance([])
# Get balance with runtime config override
config_override = [
adapter: HipcallSMS.Adapters.Telnyx,
api_key: "runtime_api_key"
]
{:ok, balance} = MyAdapter.get_balance(config_override)
Gets a configuration value with a default fallback.
This callback retrieves configuration values from the application environment, with support for system environment variables and default values.
Parameters
key
- The configuration key to retrievedefault
- Default value if the key is not found
Returns
The configuration value or the default if not found.
Validates the adapter configuration.
This callback validates that all required configuration keys are present and have valid values.
Parameters
config
- Configuration keyword list to validate
Returns
:ok
- Configuration is valid- Raises
ArgumentError
- If required configuration is missing
Validates that required dependencies are available.
This callback checks that all required dependencies are loaded and available at runtime.
Returns
:ok
- All dependencies are available{:error, missing_deps}
- List of missing dependencies
Functions
Macro for implementing SMS adapters.
This macro provides default implementations for common adapter functions and sets up the required configuration and dependencies.
Options
:required_config
- List of atoms representing required configuration keys:required_deps
- List of modules or{library, module}
tuples for required dependencies
Examples
# Basic adapter with API key requirement
use HipcallSMS.Adapter, required_config: [:api_key]
# Adapter with multiple config requirements
use HipcallSMS.Adapter, required_config: [:account_sid, :auth_token]
# Adapter with dependencies
use HipcallSMS.Adapter,
required_config: [:api_key],
required_deps: [Finch, Jason]
Gets a configuration value from the application environment.
This function retrieves configuration values with support for system environment variables and default fallbacks. It supports several configuration formats:
- Direct values:
"api_key_value"
- System environment variables:
{:system, "ENV_VAR_NAME"}
- System environment integers:
{:system, :integer, "ENV_VAR_NAME"}
Parameters
key
- The configuration key to retrievedefault
- Default value if the key is not found or is nil
Returns
The configuration value or the default if not found.
Examples
# Direct configuration value
# config :hipcall_sms, api_key: "secret123"
iex> HipcallSMS.Adapter.get_config_value(:api_key, nil)
"secret123"
# System environment variable
# config :hipcall_sms, api_key: {:system, "SMS_API_KEY"}
# Environment: SMS_API_KEY=secret123
iex> HipcallSMS.Adapter.get_config_value(:api_key, nil)
"secret123"
# System environment integer
# config :hipcall_sms, timeout: {:system, :integer, "SMS_TIMEOUT"}
# Environment: SMS_TIMEOUT=5000
iex> HipcallSMS.Adapter.get_config_value(:timeout, 3000)
5000
# Default value when not configured
iex> HipcallSMS.Adapter.get_config_value(:missing_key, "default_value")
"default_value"
# Nil value returns default
# config :hipcall_sms, some_key: nil
iex> HipcallSMS.Adapter.get_config_value(:some_key, "default")
"default"
Validates that all required configuration keys are present.
This function checks that all keys in the required_config
list are present
in the provided configuration and have non-nil, non-empty values.
Parameters
required_config
- List of required configuration keys (atoms)config
- Configuration keyword list to validate
Returns
:ok
- All required configuration is present- Raises
ArgumentError
- If any required configuration is missing
Examples
# Valid configuration
iex> HipcallSMS.Adapter.validate_config([:api_key], [api_key: "secret123"])
:ok
# Missing required key
iex> HipcallSMS.Adapter.validate_config([:api_key, :secret], [api_key: "secret123"])
** (ArgumentError) Missing required configuration: [:secret], got: [api_key: "secret123"]
# Empty value treated as missing
iex> HipcallSMS.Adapter.validate_config([:api_key], [api_key: ""])
** (ArgumentError) Missing required configuration: [:api_key], got: [api_key: ""]
@spec validate_dependency([module() | {atom(), module()}]) :: :ok | {:error, [module() | {atom(), module()}]}
Validates that all required dependencies are available.
This function checks that all modules in the required_deps
list are loaded
and available at runtime. Dependencies can be specified as module names or
as {library_name, module}
tuples.
Parameters
required_deps
- List of required dependencies
Returns
:ok
- All dependencies are available{:error, missing_deps}
- List of missing dependencies
Examples
# All dependencies available
iex> HipcallSMS.Adapter.validate_dependency([Jason, Finch])
:ok
# Missing dependency
iex> HipcallSMS.Adapter.validate_dependency([NonExistentModule])
{:error, [NonExistentModule]}
# Mixed dependency specification
iex> HipcallSMS.Adapter.validate_dependency([Jason, {:finch, Finch}])
:ok