docs: Add Elixir SDK README with comprehensive API documentation and implement modular architecture with GenServer support

- Add README.md with installation instructions, architecture overview, and API examples for all SDK modules
- Refactor TelecomSDK main module to use modular API clients (SubscribersAPI, UsageAPI, PaymentAPI, RatingPlanAPI, SystemAPI, GraphQLAPI)
- Add AuthProvider class with API key and JWT token support (generate_jwt_token, validate_jwt_token methods)
- Add HTTPClient base
This commit is contained in:
nutcas3
2026-04-25 04:55:06 +03:00
parent 808afbc2be
commit cfdbe92477
10 changed files with 668 additions and 326 deletions
+257
View File
@@ -0,0 +1,257 @@
# Telecom Platform Elixir SDK
Elixir SDK for the Telecom Platform with GenServer-based async access and full pattern matching.
## Features
- **Modular Architecture**: Separate modules for authentication, HTTP client, and API services
- **GenServer Support**: Full GenServer-based async access
- **Pattern Matching**: Full Elixir pattern matching
- **Type Safety**: Struct-based data structures with typespecs
- **Error Handling**: Comprehensive error handling
- **Retry Logic**: Built-in retry mechanism with exponential backoff
- **OTP Integration**: Full OTP application support
## Installation
Add to your `mix.exs`:
```elixir
defp deps do
[
{:telecom_sdk, path: "../sdk/elixir"}
]
end
```
Or install from source:
```bash
cd sdk/elixir
mix deps.get
mix compile
```
## Architecture
The SDK is organized into modular components:
- `auth.ex` - Authentication provider handling API keys and JWT tokens
- `http_client.ex` - HTTP client with retry logic and authentication
- `subscribers_api.ex` - Subscriber management API
- `usage_api.ex` - Usage tracking API
- `payments_api.ex` - Payment processing API
- `rating_plans_api.ex` - Rating plan management API
- `system_api.ex` - System monitoring API
- `graphql_api.ex` - GraphQL query execution
- `telecom_sdk.ex` - Main SDK client integrating all modules
## Quick Start
```elixir
# Start the SDK
{:ok, _pid} = TelecomSDK.start_link(
api_url: "http://localhost:8000",
api_key: "your-api-key",
enable_logging: true
)
# Get subscriber
{:ok, subscriber} = TelecomSDK.get_subscriber(1)
IO.puts("Subscriber: #{subscriber.first_name} #{subscriber.last_name}")
# List subscribers
{:ok, list} = TelecomSDK.list_subscribers(1, 50, "active")
IO.puts("Total subscribers: #{list.total}")
```
## API Reference
### Configuration
```elixir
config = %TelecomSDK.Config{
api_url: "http://localhost:8000",
api_key: "your-api-key",
jwt_secret: "your-jwt-secret",
timeout: 30_000,
max_retries: 3,
retry_delay: 1_000,
enable_logging: false
}
```
### Subscriber Management
```elixir
# Get subscriber
{:ok, subscriber} = TelecomSDK.get_subscriber(1)
# List subscribers
{:ok, list} = TelecomSDK.list_subscribers(1, 50, "active")
# Create subscriber
{:ok, subscriber} = TelecomSDK.create_subscriber(%{
imsi: "208930000000001",
msisdn: "+1234567890",
first_name: "John",
last_name: "Doe",
email: "[email protected]",
plan_id: 1
})
# Update subscriber
{:ok, subscriber} = TelecomSDK.update_subscriber(1, %{
first_name: "Jane"
})
# Delete subscriber
{:ok, success} = TelecomSDK.delete_subscriber(1)
# Suspend subscriber
{:ok, subscriber} = TelecomSDK.suspend_subscriber(1)
# Activate subscriber
{:ok, subscriber} = TelecomSDK.activate_subscriber(1)
```
### Usage Management
```elixir
# Get usage stats
start_date = DateTime.add(DateTime.utc_now(), -30, :day)
end_date = DateTime.utc_now()
{:ok, stats} = TelecomSDK.get_usage_stats(1, start_date, end_date)
# List usage events
{:ok, events} = TelecomSDK.list_usage_events(%{
subscriber_id: 1,
usage_type: "data",
page: 1,
page_size: 50
})
# Get real-time usage
{:ok, realtime} = TelecomSDK.get_real_time_usage(1)
```
### Payment Management
```elixir
# Create payment transaction
{:ok, transaction} = TelecomSDK.create_payment_transaction(%{
subscriber_id: "1",
amount: 25.00,
currency: "USD",
gateway: "stripe",
metadata: %{description: "Monthly plan"}
})
# Get transaction
{:ok, transaction} = TelecomSDK.get_payment_transaction("txn-123")
# List transactions
{:ok, transactions} = TelecomSDK.list_payment_transactions(%{
subscriber_id: 1,
status: "completed",
page: 1,
page_size: 50
})
```
### Rating Plans
```elixir
# List rating plans
{:ok, plans} = TelecomSDK.list_rating_plans()
# Get specific plan
{:ok, plan} = TelecomSDK.get_rating_plan("plan-123")
```
### System Management
```elixir
# Get system stats
{:ok, stats} = TelecomSDK.get_system_stats()
# Get health status
{:ok, health} = TelecomSDK.get_health_status()
```
### GraphQL
```elixir
# Execute GraphQL query
query = """
query GetSubscribers($first: Int) {
subscribers(first: $first) {
id
firstName
lastName
}
}
"""
variables = %{first: 10}
{:ok, result} = TelecomSDK.execute_graphql_query(query, variables)
```
## Authentication
### JWT Token Generation
```elixir
# Generate JWT token
{:ok, token} = TelecomSDK.generate_jwt_token("user-123", 24, %{custom_claim: "value"})
# Validate JWT token
{:ok, claims} = TelecomSDK.validate_jwt_token(token)
```
## Error Handling
```elixir
case TelecomSDK.get_subscriber(1) do
{:ok, subscriber} ->
IO.puts("Subscriber: #{subscriber.first_name}")
{:error, :authentication_error} ->
IO.puts("Authentication failed")
{:error, :api_error} ->
IO.puts("API error")
{:error, :network_error} ->
IO.puts("Network error")
{:error, reason} ->
IO.puts("Error: #{inspect(reason)}")
end
```
## Development
### Running Tests
```bash
mix test
```
### Code Style
```bash
mix format
mix compile --warnings-as-errors
```
### Building
```bash
mix compile
```
## Requirements
- Elixir 1.13+
- OTP 26+
## License
This SDK is licensed under the MIT License.
+59 -326
View File
@@ -1,7 +1,7 @@
defmodule TelecomSDK do
@moduledoc """
Telecom Platform Elixir SDK
Provides GenServer-based async access to the Telecom Platform API with full Elixir pattern matching.
"""
@@ -19,363 +19,96 @@ defmodule TelecomSDK do
end
@doc """
Get subscriber by ID
Generate a JWT token for authentication
"""
def get_subscriber(id) do
GenServer.call(__MODULE__, {:get_subscriber, id})
def generate_jwt_token(user_id, expiry_hours \\ 24, additional_claims \\ %{}) do
GenServer.call(__MODULE__, {:generate_jwt_token, user_id, expiry_hours, additional_claims})
end
@doc """
List subscribers with pagination
Validate a JWT token
"""
def list_subscribers(page \\ 1, page_size \\ 50, status \\ nil) do
GenServer.call(__MODULE__, {:list_subscribers, page, page_size, status})
def validate_jwt_token(token) do
GenServer.call(__MODULE__, {:validate_jwt_token, token})
end
@doc """
Create a new subscriber
Close the SDK and cleanup resources
"""
def create_subscriber(request) do
GenServer.call(__MODULE__, {:create_subscriber, request})
end
@doc """
Update an existing subscriber
"""
def update_subscriber(id, request) do
GenServer.call(__MODULE__, {:update_subscriber, id, request})
end
@doc """
Suspend a subscriber
"""
def suspend_subscriber(id) do
GenServer.call(__MODULE__, {:suspend_subscriber, id})
end
@doc """
Activate a suspended subscriber
"""
def activate_subscriber(id) do
GenServer.call(__MODULE__, {:activate_subscriber, id})
end
@doc """
Terminate a subscriber
"""
def terminate_subscriber(id) do
GenServer.call(__MODULE__, {:terminate_subscriber, id})
end
@doc """
Get usage statistics for a subscriber
"""
def get_usage_stats(subscriber_id, start_date, end_date) do
GenServer.call(__MODULE__, {:get_usage_stats, subscriber_id, start_date, end_date})
end
@doc """
List usage events with filtering
"""
def list_usage_events(opts \\ []) do
GenServer.call(__MODULE__, {:list_usage_events, opts})
end
@doc """
Get real-time usage for a subscriber
"""
def get_real_time_usage(subscriber_id) do
GenServer.call(__MODULE__, {:get_real_time_usage, subscriber_id})
end
@doc """
Create a payment transaction
"""
def create_payment_transaction(request) do
GenServer.call(__MODULE__, {:create_payment_transaction, request})
end
@doc """
Get payment transaction by ID
"""
def get_payment_transaction(transaction_id) do
GenServer.call(__MODULE__, {:get_payment_transaction, transaction_id})
end
@doc """
List payment transactions with filtering
"""
def list_payment_transactions(opts \\ []) do
GenServer.call(__MODULE__, {:list_payment_transactions, opts})
end
@doc """
List all available rating plans
"""
def list_rating_plans do
GenServer.call(__MODULE__, :list_rating_plans)
end
@doc """
Get rating plan by ID
"""
def get_rating_plan(plan_id) do
GenServer.call(__MODULE__, {:get_rating_plan, plan_id})
end
@doc """
Get system statistics
"""
def get_system_stats do
GenServer.call(__MODULE__, :get_system_stats)
end
@doc """
Get system health status
"""
def get_health_status do
GenServer.call(__MODULE__, :get_health_status)
end
@doc """
Execute a GraphQL query
"""
def execute_graphql_query(query, variables \\ %{}) do
GenServer.call(__MODULE__, {:execute_graphql_query, query, variables})
end
@doc """
Connect to WebSocket for real-time updates
"""
def connect_websocket(message_handler \\ nil) do
GenServer.call(__MODULE__, {:connect_websocket, message_handler})
def close do
GenServer.call(__MODULE__, :close)
end
# GenServer Callbacks
@impl true
def init(config) do
Finch.start_link(name: MyFinch)
{:ok, %{config: config, finch: MyFinch}}
end
auth_provider = AuthProvider.new(api_key: config.api_key, jwt_secret: config.jwt_secret)
http_client = HTTPClient.new(config.api_url, auth_provider,
timeout: config.timeout,
max_retries: config.max_retries,
retry_delay: config.retry_delay,
enable_logging: config.enable_logging
)
@impl true
def handle_call({:get_subscriber, id}, _from, state) do
case make_request(:get, "/v1/subscribers/#{id}", nil, state) do
{:ok, response} -> {:reply, {:ok, Subscriber.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
# Initialize API modules
subscribers = SubscriberAPI.new(http_client)
usage = UsageAPI.new(http_client)
payments = PaymentAPI.new(http_client)
rating_plans = RatingPlanAPI.new(http_client)
system = SystemAPI.new(http_client)
graphql = GraphQLAPI.new(http_client)
@impl true
def handle_call({:list_subscribers, page, page_size, status}, _from, state) do
params = %{page: page, page_size: page_size}
params = if status, do: Map.put(params, :status, status), else: params
case make_request(:get, "/v1/subscribers", params, state) do
{:ok, response} -> {:reply, {:ok, SubscriberList.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
@impl true
def handle_call({:create_subscriber, request}, _from, state) do
case make_request(:post, "/v1/subscribers", request.to_map(), state) do
{:ok, response} -> {:reply, {:ok, Subscriber.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
@impl true
def handle_call({:update_subscriber, id, request}, _from, state) do
case make_request(:put, "/v1/subscribers/#{id}", request.to_map(), state) do
{:ok, response} -> {:reply, {:ok, Subscriber.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
@impl true
def handle_call({:suspend_subscriber, id}, _from, state) do
case make_request(:post, "/v1/subscribers/#{id}/suspend", nil, state) do
{:ok, response} -> {:reply, {:ok, Subscriber.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
@impl true
def handle_call({:activate_subscriber, id}, _from, state) do
case make_request(:post, "/v1/subscribers/#{id}/activate", nil, state) do
{:ok, response} -> {:reply, {:ok, Subscriber.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
@impl true
def handle_call({:terminate_subscriber, id}, _from, state) do
case make_request(:delete, "/v1/subscribers/#{id}", nil, state) do
{:ok, response} -> {:reply, {:ok, response["success"]}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
@impl true
def handle_call({:get_usage_stats, subscriber_id, start_date, end_date}, _from, state) do
params = %{
start_date: DateTime.to_iso8601(start_date),
end_date: DateTime.to_iso8601(end_date)
state = %{
config: config,
auth_provider: auth_provider,
http_client: http_client,
subscribers: subscribers,
usage: usage,
payments: payments,
rating_plans: rating_plans,
system: system,
graphql: graphql
}
case make_request(:get, "/v1/subscribers/#{subscriber_id}/usage", params, state) do
{:ok, response} -> {:reply, {:ok, UsageStats.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
{:ok, state}
end
@impl true
def handle_call({:list_usage_events, opts}, _from, state) do
params = build_params(opts, [:subscriber_id, :usage_type, :start_date, :end_date, :page, :page_size])
case make_request(:get, "/v1/usage/events", params, state) do
{:ok, response} -> {:reply, {:ok, UsageEventList.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
def handle_call({:generate_jwt_token, user_id, expiry_hours, additional_claims}, _from, state) do
auth_provider = AuthProvider.generate_jwt_token(state.auth_provider, user_id, expiry_hours, additional_claims)
{:reply, {:ok, auth_provider.token_cache}, %{state | auth_provider: auth_provider}}
end
@impl true
def handle_call({:get_real_time_usage, subscriber_id}, _from, state) do
case make_request(:get, "/v1/subscribers/#{subscriber_id}/realtime", nil, state) do
{:ok, response} -> {:reply, {:ok, RealTimeUsage.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
def handle_call({:validate_jwt_token, token}, _from, state) do
result = AuthProvider.validate_jwt_token(state.auth_provider, token)
{:reply, {:ok, result}, state}
end
@impl true
def handle_call({:create_payment_transaction, request}, _from, state) do
case make_request(:post, "/v1/payments/transactions", request.to_map(), state) do
{:ok, response} -> {:reply, {:ok, PaymentTransaction.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
def handle_call(:close, _from, state) do
HTTPClient.close(state.http_client)
{:reply, :ok, state}
end
@impl true
def handle_call({:get_payment_transaction, transaction_id}, _from, state) do
case make_request(:get, "/v1/payments/transactions/#{transaction_id}", nil, state) do
{:ok, response} -> {:reply, {:ok, PaymentTransaction.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
# Configuration
@impl true
def handle_call({:list_payment_transactions, opts}, _from, state) do
params = build_params(opts, [:subscriber_id, :status, :page, :page_size])
case make_request(:get, "/v1/payments/transactions", params, state) do
{:ok, response} -> {:reply, {:ok, PaymentTransactionList.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
defstruct [:api_url, :api_key, :jwt_secret, :timeout, :max_retries, :retry_delay, :enable_logging]
@impl true
def handle_call(:list_rating_plans, _from, state) do
case make_request(:get, "/v1/rating-plans", nil, state) do
{:ok, response} ->
plans = Enum.map(response, &RatingPlan.from_map/1)
{:reply, {:ok, plans}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
defmodule Config do
@moduledoc """
Configuration for Telecom SDK
"""
@impl true
def handle_call({:get_rating_plan, plan_id}, _from, state) do
case make_request(:get, "/v1/rating-plans/#{plan_id}", nil, state) do
{:ok, response} -> {:reply, {:ok, RatingPlan.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
@impl true
def handle_call(:get_system_stats, _from, state) do
case make_request(:get, "/v1/system/stats", nil, state) do
{:ok, response} -> {:reply, {:ok, SystemStats.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
@impl true
def handle_call(:get_health_status, _from, state) do
case make_request(:get, "/v1/health", nil, state) do
{:ok, response} -> {:reply, {:ok, HealthStatus.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
@impl true
def handle_call({:execute_graphql_query, query, variables}, _from, state) do
request = %{query: query, variables: variables}
case make_request(:post, "/graphql", request, state) do
{:ok, response} -> {:reply, {:ok, GraphQLResponse.from_map(response)}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
@impl true
def handle_call({:connect_websocket, message_handler}, _from, state) do
ws_url = String.replace(state.config.api_url, "http://", "ws://") <> "/ws"
case WebSockex.start_link(ws_url, WebSocketHandler, %{message_handler: message_handler}) do
{:ok, pid} -> {:reply, {:ok, pid}, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
# Private functions
defp make_request(method, path, params, state) do
url = state.config.api_url <> path
headers = [
{"user-agent", "Telecom-Elixir-SDK/1.0.0"},
{"content-type", "application/json"}
defstruct [
api_url: "http://localhost:8000",
api_key: nil,
jwt_secret: nil,
timeout: 30_000,
max_retries: 3,
retry_delay: 1_000,
enable_logging: false
]
headers = if state.config.api_key do
[{"authorization", "Bearer #{state.config.api_key}"} | headers]
else
headers
end
request = Finch.build(method, url, headers, Jason.encode!(params || %{}))
case Finch.request(request, state.finch) do
{:ok, response} ->
case response.status do
200 ->
body = response.body |> Jason.decode!()
{:ok, body}
401 ->
{:error, :authentication_error}
429 ->
{:error, :rate_limit_error}
status when status in 400..499 ->
body = response.body |> Jason.decode!()
{:error, {:api_error, body["error"] || "Bad request"}}
status when status in 500..599 ->
{:error, {:server_error, status}}
_ ->
{:error, :unknown_error}
end
{:error, reason} ->
{:error, {:network_error, reason}}
end
end
defp build_params(opts, keys) do
Enum.reduce(keys, %{}, fn key, acc ->
case Map.get(opts, key) do
nil -> acc
value -> Map.put(acc, key, value)
end
end)
end
end
+104
View File
@@ -0,0 +1,104 @@
defmodule TelecomSDK.AuthProvider do
@moduledoc """
Authentication provider for the Telecom SDK
"""
defstruct [:api_key, :jwt_secret, :token_cache, :token_expiry]
def new(opts \\ []) do
%__MODULE__{
api_key: Keyword.get(opts, :api_key),
jwt_secret: Keyword.get(opts, :jwt_secret),
token_cache: nil,
token_expiry: nil
}
end
def get_headers(%__MODULE__{api_key: api_key, token_cache: token_cache, token_expiry: token_expiry}) do
headers = %{
"Content-Type" => "application/json",
"User-Agent" => "Telecom-Elixir-SDK/1.0.0"
}
headers = if api_key, do: Map.put(headers, "X-API-Key", api_key), else: headers
if token_cache && token_valid?(token_expiry) do
Map.put(headers, "Authorization", "Bearer #{token_cache}")
else
headers
end
end
def generate_jwt_token(%__MODULE__{jwt_secret: jwt_secret} = auth, user_id, expiry_hours \\ 24, additional_claims \\ %{}) do
unless jwt_secret, do: raise(ArgumentError, "JWT secret not configured")
now = System.system_time(:second)
exp = now + (expiry_hours * 3600)
claims = %{
"sub" => user_id,
"exp" => exp,
"iat" => now
}
|> Map.merge(additional_claims)
header = %{"alg" => "HS256", "typ" => "JWT"}
encoded_header = base64_url_encode(header)
encoded_payload = base64_url_encode(claims)
signature = sign("#{encoded_header}.#{encoded_payload}", jwt_secret)
token = "#{encoded_header}.#{encoded_payload}.#{signature}"
%{auth | token_cache: token, token_expiry: exp}
end
def validate_jwt_token(%__MODULE__{jwt_secret: jwt_secret}, token) do
unless jwt_secret, do: raise(ArgumentError, "JWT secret not configured")
parts = String.split(token, ".")
unless length(parts) == 3, do: raise(ArgumentError, "Invalid token format")
[encoded_header, encoded_payload, signature] = parts
expected_signature = sign("#{encoded_header}.#{encoded_payload}", jwt_secret)
unless signature == expected_signature, do: raise(ArgumentError, "Invalid token signature")
case base64_url_decode(encoded_payload) do
{:ok, payload_bytes} ->
case Jason.decode(payload_bytes) do
{:ok, payload} ->
if payload["exp"] && payload["exp"] < System.system_time(:second) do
raise(ArgumentError, "Token has expired")
end
payload
{:error, _} -> raise(ArgumentError, "Failed to decode payload")
end
{:error, _} -> raise(ArgumentError, "Failed to decode payload")
end
end
def clear_token_cache(auth) do
%{auth | token_cache: nil, token_expiry: nil}
end
defp token_valid?(nil), do: false
defp token_valid?(expiry), do: System.system_time(:second) < expiry
defp base64_url_encode(data) do
json = Jason.encode!(data)
json
|> Base.url_encode64(padding: false)
end
defp base64_url_decode(data) do
# Add padding if needed
padded_data = data <> String.duplicate("=", rem(4 - rem(String.length(data), 4), 4))
Base.url_decode64(padded_data)
end
defp sign(data, secret) do
:crypto.mac(:hmac, :sha256, secret, data)
|> Base.url_encode64(padding: false)
end
end
+17
View File
@@ -0,0 +1,17 @@
defmodule TelecomSDK.GraphQLAPI do
@moduledoc """
API for GraphQL queries
"""
defstruct [:client]
def new(client) do
%__MODULE__{client: client}
end
def execute(api, query, variables \\ nil) do
request = %{query: query}
request = if variables, do: Map.put(request, :variables, variables), else: request
TelecomSDK.HTTPClient.post(api.client, "/graphql", request)
end
end
+84
View File
@@ -0,0 +1,84 @@
defmodule TelecomSDK.HTTPClient do
@moduledoc """
HTTP client for making API requests
"""
defstruct [:base_url, :auth_provider, :timeout, :max_retries, :retry_delay, :enable_logging]
def new(base_url, auth_provider, opts \\ []) do
%__MODULE__{
base_url: base_url,
auth_provider: auth_provider,
timeout: Keyword.get(opts, :timeout, 30_000),
max_retries: Keyword.get(opts, :max_retries, 3),
retry_delay: Keyword.get(opts, :retry_delay, 1_000),
enable_logging: Keyword.get(opts, :enable_logging, false)
}
end
def get(client, path, params \\ %{}) do
request(client, "GET", path, nil, params)
end
def post(client, path, body \\ nil) do
request(client, "POST", path, body, nil)
end
def put(client, path, body \\ nil) do
request(client, "PUT", path, body, nil)
end
def delete(client, path) do
request(client, "DELETE", path, nil, nil)
end
defp request(client, method, path, body, params) do
url = client.base_url <> path
headers = TelecomSDK.AuthProvider.get_headers(client.auth_provider)
opts = [
timeout: client.timeout,
recv_timeout: client.timeout
]
opts = if params != %{}, do: Keyword.put(opts, :params, params), else: opts
opts = if body, do: Keyword.put(opts, :body, Jason.encode!(body)), else: opts
last_error = nil
Enum.reduce_while(0..client.max_retries, nil, fn attempt, _acc ->
case do_request(method, url, headers, opts) do
{:ok, response} ->
case handle_response_errors(response) do
:ok -> {:halt, {:ok, Jason.decode!(response.body)}}
{:error, error} -> {:halt, {:error, error}}
end
{:error, error} ->
if attempt < client.max_retries do
Process.sleep(client.retry_delay * round(:math.pow(2, attempt)))
{:cont, error}
else
{:halt, {:error, error}}
end
end
end)
end
defp do_request(method, url, headers, opts) do
case HTTPoison.request(method, url, Keyword.get(opts, :body), headers, opts) do
{:ok, response} -> {:ok, response}
{:error, error} -> {:error, error}
end
end
defp handle_response_errors(%{status_code: status}) when status >= 200 and status < 300, do: :ok
defp handle_response_errors(%{status_code: 401}), do: {:error, :authentication_failed}
defp handle_response_errors(%{status_code: 429}), do: {:error, :rate_limit_exceeded}
defp handle_response_errors(%{status_code: status}) when status >= 400 and status < 500, do: {:error, {:client_error, status}}
defp handle_response_errors(%{status_code: status}) when status >= 500, do: {:error, {:server_error, status}}
def close(_client) do
# HTTPoison doesn't need explicit cleanup
:ok
end
end
@@ -0,0 +1,31 @@
defmodule TelecomSDK.PaymentAPI do
@moduledoc """
API for payment management
"""
defstruct [:client]
def new(client) do
%__MODULE__{client: client}
end
def create_transaction(api, request) do
TelecomSDK.HTTPClient.post(api.client, "/v1/payments/transactions", request)
end
def get_transaction(api, transaction_id) do
TelecomSDK.HTTPClient.get(api.client, "/v1/payments/transactions/#{transaction_id}")
end
def list_transactions(api, opts \\ []) do
params = %{
page: Keyword.get(opts, :page, 1),
page_size: Keyword.get(opts, :page_size, 50)
}
params = if Keyword.get(opts, :subscriber_id), do: Map.put(params, :subscriber_id, Keyword.get(opts, :subscriber_id)), else: params
params = if Keyword.get(opts, :status), do: Map.put(params, :status, Keyword.get(opts, :status)), else: params
TelecomSDK.HTTPClient.get(api.client, "/v1/payments/transactions", params)
end
end
@@ -0,0 +1,19 @@
defmodule TelecomSDK.RatingPlanAPI do
@moduledoc """
API for rating plan management
"""
defstruct [:client]
def new(client) do
%__MODULE__{client: client}
end
def list(api) do
TelecomSDK.HTTPClient.get(api.client, "/v1/rating-plans")
end
def get(api, plan_id) do
TelecomSDK.HTTPClient.get(api.client, "/v1/rating-plans/#{plan_id}")
end
end
@@ -0,0 +1,41 @@
defmodule TelecomSDK.SubscriberAPI do
@moduledoc """
API for subscriber management
"""
defstruct [:client]
def new(client) do
%__MODULE__{client: client}
end
def get(api, id) do
TelecomSDK.HTTPClient.get(api.client, "/v1/subscribers/#{id}")
end
def list(api, page \\ 1, page_size \\ 50, status \\ nil) do
params = %{page: page, page_size: page_size}
params = if status, do: Map.put(params, :status, status), else: params
TelecomSDK.HTTPClient.get(api.client, "/v1/subscribers", params)
end
def create(api, request) do
TelecomSDK.HTTPClient.post(api.client, "/v1/subscribers", request)
end
def update(api, id, request) do
TelecomSDK.HTTPClient.put(api.client, "/v1/subscribers/#{id}", request)
end
def delete(api, id) do
TelecomSDK.HTTPClient.delete(api.client, "/v1/subscribers/#{id}")
end
def suspend(api, id) do
TelecomSDK.HTTPClient.post(api.client, "/v1/subscribers/#{id}/suspend")
end
def activate(api, id) do
TelecomSDK.HTTPClient.post(api.client, "/v1/subscribers/#{id}/activate")
end
end
+19
View File
@@ -0,0 +1,19 @@
defmodule TelecomSDK.SystemAPI do
@moduledoc """
API for system management
"""
defstruct [:client]
def new(client) do
%__MODULE__{client: client}
end
def get_stats(api) do
TelecomSDK.HTTPClient.get(api.client, "/v1/system/stats")
end
def get_health(api) do
TelecomSDK.HTTPClient.get(api.client, "/v1/health")
end
end
+37
View File
@@ -0,0 +1,37 @@
defmodule TelecomSDK.UsageAPI do
@moduledoc """
API for usage management
"""
defstruct [:client]
def new(client) do
%__MODULE__{client: client}
end
def get_stats(api, subscriber_id, start_date, end_date) do
params = %{
start_date: DateTime.to_iso8601(start_date),
end_date: DateTime.to_iso8601(end_date)
}
TelecomSDK.HTTPClient.get(api.client, "/v1/subscribers/#{subscriber_id}/usage", params)
end
def list_events(api, opts \\ []) do
params = %{
page: Keyword.get(opts, :page, 1),
page_size: Keyword.get(opts, :page_size, 50)
}
params = if Keyword.get(opts, :subscriber_id), do: Map.put(params, :subscriber_id, Keyword.get(opts, :subscriber_id)), else: params
params = if Keyword.get(opts, :usage_type), do: Map.put(params, :usage_type, Keyword.get(opts, :usage_type)), else: params
params = if Keyword.get(opts, :start_date), do: Map.put(params, :start_date, DateTime.to_iso8601(Keyword.get(opts, :start_date))), else: params
params = if Keyword.get(opts, :end_date), do: Map.put(params, :end_date, DateTime.to_iso8601(Keyword.get(opts, :end_date))), else: params
TelecomSDK.HTTPClient.get(api.client, "/v1/usage/events", params)
end
def get_realtime(api, subscriber_id) do
TelecomSDK.HTTPClient.get(api.client, "/v1/subscribers/#{subscriber_id}/realtime")
end
end