mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-11-08 04:55:15 +00:00
docs: Add Ruby SDK README with comprehensive API documentation and implement modular architecture with HTTParty integration
- Add README.md with installation instructions, architecture overview, and API examples for all SDK modules - Refactor TelecomSDK::Client to use modular API clients (SubscriberAPI, 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 class with retry
This commit is contained in:
@@ -0,0 +1,259 @@
|
|||||||
|
# Telecom Platform Ruby SDK
|
||||||
|
|
||||||
|
Ruby SDK for the Telecom Platform with full Ruby patterns and HTTP support.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Modular Architecture**: Separate modules for authentication, HTTP client, and API services
|
||||||
|
- **HTTParty Integration**: Built on HTTParty for robust HTTP requests
|
||||||
|
- **Type Safety**: Hash-based data structures with validation
|
||||||
|
- **Error Handling**: Comprehensive error handling with custom exceptions
|
||||||
|
- **Retry Logic**: Built-in retry mechanism with exponential backoff
|
||||||
|
- **WebSocket Support**: Real-time updates via WebSocket
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add to your `Gemfile`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
gem 'telecom-sdk'
|
||||||
|
```
|
||||||
|
|
||||||
|
Or install manually:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd sdk/ruby
|
||||||
|
gem build telecom_sdk.gemspec
|
||||||
|
gem install telecom_sdk-1.0.0.gem
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The SDK is organized into modular components:
|
||||||
|
|
||||||
|
- `auth.rb` - Authentication provider handling API keys and JWT tokens
|
||||||
|
- `http_client.rb` - HTTP client with retry logic and authentication
|
||||||
|
- `subscribers_api.rb` - Subscriber management API
|
||||||
|
- `usage_api.rb` - Usage tracking API
|
||||||
|
- `payments_api.rb` - Payment processing API
|
||||||
|
- `rating_plans_api.rb` - Rating plan management API
|
||||||
|
- `system_api.rb` - System monitoring API
|
||||||
|
- `graphql_api.rb` - GraphQL query execution
|
||||||
|
- `telecom_sdk.rb` - Main SDK client integrating all modules
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'telecom_sdk'
|
||||||
|
|
||||||
|
sdk = TelecomSDK::Client.new(
|
||||||
|
api_url: "http://localhost:8000",
|
||||||
|
api_key: "your-api-key",
|
||||||
|
enable_logging: true
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get subscriber
|
||||||
|
subscriber = sdk.subscribers.get(1)
|
||||||
|
puts "Subscriber: #{subscriber['first_name']} #{subscriber['last_name']}"
|
||||||
|
|
||||||
|
# List subscribers
|
||||||
|
subscribers = sdk.subscribers.list(page: 1, page_size: 50, status: "active")
|
||||||
|
puts "Total subscribers: #{subscribers['total']}"
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Reference
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
client = TelecomSDK::Client.new(
|
||||||
|
api_url: "http://localhost:8000",
|
||||||
|
api_key: "your-api-key",
|
||||||
|
jwt_secret: "your-jwt-secret",
|
||||||
|
timeout: 30,
|
||||||
|
max_retries: 3,
|
||||||
|
retry_delay: 1,
|
||||||
|
enable_logging: false
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Subscriber Management
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Get subscriber
|
||||||
|
subscriber = sdk.subscribers.get(1)
|
||||||
|
|
||||||
|
# List subscribers
|
||||||
|
subscribers = sdk.subscribers.list(page: 1, page_size: 50, status: "active")
|
||||||
|
|
||||||
|
# Create subscriber
|
||||||
|
subscriber = sdk.subscribers.create(
|
||||||
|
imsi: "208930000000001",
|
||||||
|
msisdn: "+1234567890",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Doe",
|
||||||
|
email: "[email protected]",
|
||||||
|
plan_id: 1
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update subscriber
|
||||||
|
subscriber = sdk.subscribers.update(1, first_name: "Jane")
|
||||||
|
|
||||||
|
# Delete subscriber
|
||||||
|
sdk.subscribers.delete(1)
|
||||||
|
|
||||||
|
# Suspend subscriber
|
||||||
|
subscriber = sdk.subscribers.suspend(1)
|
||||||
|
|
||||||
|
# Activate subscriber
|
||||||
|
subscriber = sdk.subscribers.activate(1)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Usage Management
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'time'
|
||||||
|
|
||||||
|
# Get usage stats
|
||||||
|
start_date = Time.now - 30 * 24 * 60 * 60
|
||||||
|
end_date = Time.now
|
||||||
|
stats = sdk.usage.get_stats(1, start_date, end_date)
|
||||||
|
|
||||||
|
# List usage events
|
||||||
|
events = sdk.usage.list_events(
|
||||||
|
subscriber_id: 1,
|
||||||
|
usage_type: "data",
|
||||||
|
page: 1,
|
||||||
|
page_size: 50
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get real-time usage
|
||||||
|
realtime = sdk.usage.get_real_time(1)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Payment Management
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Create payment transaction
|
||||||
|
transaction = sdk.payments.create_transaction(
|
||||||
|
subscriber_id: 1,
|
||||||
|
amount: 25.00,
|
||||||
|
currency: "USD",
|
||||||
|
gateway: "stripe",
|
||||||
|
metadata: { description: "Monthly plan" }
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get transaction
|
||||||
|
transaction = sdk.payments.get_transaction("txn-123")
|
||||||
|
|
||||||
|
# List transactions
|
||||||
|
transactions = sdk.payments.list_transactions(
|
||||||
|
subscriber_id: 1,
|
||||||
|
status: "completed",
|
||||||
|
page: 1,
|
||||||
|
page_size: 50
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rating Plans
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# List rating plans
|
||||||
|
plans = sdk.rating_plans.list
|
||||||
|
|
||||||
|
# Get specific plan
|
||||||
|
plan = sdk.rating_plans.get("plan-123")
|
||||||
|
```
|
||||||
|
|
||||||
|
### System Management
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Get system stats
|
||||||
|
stats = sdk.system.get_stats
|
||||||
|
|
||||||
|
# Get health status
|
||||||
|
health = sdk.system.get_health
|
||||||
|
```
|
||||||
|
|
||||||
|
### GraphQL
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Execute GraphQL query
|
||||||
|
query = <<-GRAPHQL
|
||||||
|
query GetSubscribers($first: Int) {
|
||||||
|
subscribers(first: $first) {
|
||||||
|
id
|
||||||
|
firstName
|
||||||
|
lastName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GRAPHQL
|
||||||
|
|
||||||
|
variables = { first: 10 }
|
||||||
|
result = sdk.graphql.execute(query, variables)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
begin
|
||||||
|
subscriber = sdk.subscribers.get(1)
|
||||||
|
rescue TelecomSDK::AuthenticationError
|
||||||
|
puts "Authentication failed"
|
||||||
|
rescue TelecomSDK::APIError => e
|
||||||
|
puts "API error: #{e.message}"
|
||||||
|
rescue TelecomSDK::NetworkError => e
|
||||||
|
puts "Network error: #{e.message}"
|
||||||
|
rescue TelecomSDK::ValidationError => e
|
||||||
|
puts "Validation error: #{e.message}"
|
||||||
|
rescue TelecomSDK::RateLimitError => e
|
||||||
|
puts "Rate limit error: #{e.message}"
|
||||||
|
rescue TelecomSDK::ServerError => e
|
||||||
|
puts "Server error: #{e.message}"
|
||||||
|
rescue TelecomSDK::Error => e
|
||||||
|
puts "Telecom error: #{e.message}"
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
### JWT Token Generation
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Generate JWT token
|
||||||
|
token = sdk.generate_jwt_token("user-123", 24, { custom_claim: "value" })
|
||||||
|
|
||||||
|
# Validate JWT token
|
||||||
|
claims = sdk.validate_jwt_token(token)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bundle install
|
||||||
|
bundle exec rspec
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bundle exec rubocop
|
||||||
|
```
|
||||||
|
|
||||||
|
### Building Gem
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gem build telecom_sdk.gemspec
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Ruby 2.7+
|
||||||
|
- HTTParty
|
||||||
|
- WebSocket Client Simple
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This SDK is licensed under the MIT License.
|
||||||
+41
-185
@@ -3,6 +3,15 @@ require "json"
|
|||||||
require "websocket-client-simple"
|
require "websocket-client-simple"
|
||||||
require "uri"
|
require "uri"
|
||||||
|
|
||||||
|
require_relative "telecom_sdk/auth"
|
||||||
|
require_relative "telecom_sdk/http_client"
|
||||||
|
require_relative "telecom_sdk/subscribers_api"
|
||||||
|
require_relative "telecom_sdk/usage_api"
|
||||||
|
require_relative "telecom_sdk/payments_api"
|
||||||
|
require_relative "telecom_sdk/rating_plans_api"
|
||||||
|
require_relative "telecom_sdk/system_api"
|
||||||
|
require_relative "telecom_sdk/graphql_api"
|
||||||
|
|
||||||
module TelecomSDK
|
module TelecomSDK
|
||||||
class Error < StandardError; end
|
class Error < StandardError; end
|
||||||
class AuthenticationError < Error; end
|
class AuthenticationError < Error; end
|
||||||
@@ -13,210 +22,57 @@ module TelecomSDK
|
|||||||
class ServerError < Error; end
|
class ServerError < Error; end
|
||||||
|
|
||||||
class Client
|
class Client
|
||||||
include HTTParty
|
attr_reader :config, :subscribers, :usage, :payments, :rating_plans, :system, :graphql
|
||||||
|
|
||||||
attr_reader :config
|
|
||||||
|
|
||||||
def initialize(config = {})
|
def initialize(config = {})
|
||||||
@config = Config.new(config)
|
@config = Config.new(config)
|
||||||
self.class.base_uri @config.api_url
|
@auth_provider = AuthProvider.new(api_key: @config.api_key, jwt_secret: @config.jwt_secret)
|
||||||
self.class.default_options.update(
|
@http_client = HTTPClient.new(
|
||||||
|
@config.api_url,
|
||||||
|
@auth_provider,
|
||||||
timeout: @config.timeout,
|
timeout: @config.timeout,
|
||||||
headers: {
|
max_retries: @config.max_retries,
|
||||||
"User-Agent" => "Telecom-Ruby-SDK/1.0.0",
|
retry_delay: @config.retry_delay,
|
||||||
"Content-Type" => "application/json"
|
enable_logging: @config.enable_logging
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if @config.api_key
|
# Initialize API modules
|
||||||
self.class.default_options[:headers]["Authorization"] = "Bearer #{@config.api_key}"
|
@subscribers = SubscriberAPI.new(@http_client)
|
||||||
end
|
@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)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Subscriber Management
|
# Authentication methods
|
||||||
|
|
||||||
def get_subscriber(id)
|
def generate_jwt_token(user_id, expiry_hours = 24, additional_claims = {})
|
||||||
response = self.class.get("/v1/subscribers/#{id}")
|
@auth_provider.generate_jwt_token(user_id, expiry_hours, additional_claims)
|
||||||
handle_response(response)
|
|
||||||
Subscriber.new(response.parsed_response)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_subscribers(page: 1, page_size: 50, status: nil)
|
def validate_jwt_token(token)
|
||||||
params = { page: page, page_size: page_size }
|
@auth_provider.validate_jwt_token(token)
|
||||||
params[:status] = status if status
|
|
||||||
|
|
||||||
response = self.class.get("/v1/subscribers", query: params)
|
|
||||||
handle_response(response)
|
|
||||||
SubscriberList.new(response.parsed_response)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_subscriber(request)
|
# Cleanup
|
||||||
response = self.class.post("/v1/subscribers", body: request.to_json)
|
|
||||||
handle_response(response)
|
|
||||||
Subscriber.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_subscriber(id, request)
|
def close
|
||||||
response = self.class.put("/v1/subscribers/#{id}", body: request.to_json)
|
@http_client.close
|
||||||
handle_response(response)
|
|
||||||
Subscriber.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def suspend_subscriber(id)
|
|
||||||
response = self.class.post("/v1/subscribers/#{id}/suspend")
|
|
||||||
handle_response(response)
|
|
||||||
Subscriber.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def activate_subscriber(id)
|
|
||||||
response = self.class.post("/v1/subscribers/#{id}/activate")
|
|
||||||
handle_response(response)
|
|
||||||
Subscriber.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def terminate_subscriber(id)
|
|
||||||
response = self.class.delete("/v1/subscribers/#{id}")
|
|
||||||
handle_response(response)
|
|
||||||
response.parsed_response["success"]
|
|
||||||
end
|
|
||||||
|
|
||||||
# Usage Management
|
|
||||||
|
|
||||||
def get_usage_stats(subscriber_id, start_date, end_date)
|
|
||||||
params = {
|
|
||||||
start_date: start_date.iso8601,
|
|
||||||
end_date: end_date.iso8601
|
|
||||||
}
|
|
||||||
|
|
||||||
response = self.class.get("/v1/subscribers/#{subscriber_id}/usage", query: params)
|
|
||||||
handle_response(response)
|
|
||||||
UsageStats.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_usage_events(subscriber_id: nil, usage_type: nil, start_date: nil, end_date: nil, page: 1, page_size: 50)
|
|
||||||
params = { page: page, page_size: page_size }
|
|
||||||
params[:subscriber_id] = subscriber_id if subscriber_id
|
|
||||||
params[:usage_type] = usage_type if usage_type
|
|
||||||
params[:start_date] = start_date.iso8601 if start_date
|
|
||||||
params[:end_date] = end_date.iso8601 if end_date
|
|
||||||
|
|
||||||
response = self.class.get("/v1/usage/events", query: params)
|
|
||||||
handle_response(response)
|
|
||||||
UsageEventList.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_real_time_usage(subscriber_id)
|
|
||||||
response = self.class.get("/v1/subscribers/#{subscriber_id}/realtime")
|
|
||||||
handle_response(response)
|
|
||||||
RealTimeUsage.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Payment Management
|
|
||||||
|
|
||||||
def create_payment_transaction(request)
|
|
||||||
response = self.class.post("/v1/payments/transactions", body: request.to_json)
|
|
||||||
handle_response(response)
|
|
||||||
PaymentTransaction.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_payment_transaction(transaction_id)
|
|
||||||
response = self.class.get("/v1/payments/transactions/#{transaction_id}")
|
|
||||||
handle_response(response)
|
|
||||||
PaymentTransaction.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def list_payment_transactions(subscriber_id: nil, status: nil, page: 1, page_size: 50)
|
|
||||||
params = { page: page, page_size: page_size }
|
|
||||||
params[:subscriber_id] = subscriber_id if subscriber_id
|
|
||||||
params[:status] = status if status
|
|
||||||
|
|
||||||
response = self.class.get("/v1/payments/transactions", query: params)
|
|
||||||
handle_response(response)
|
|
||||||
PaymentTransactionList.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Rating Plans
|
|
||||||
|
|
||||||
def list_rating_plans
|
|
||||||
response = self.class.get("/v1/rating-plans")
|
|
||||||
handle_response(response)
|
|
||||||
response.parsed_response.map { |plan| RatingPlan.new(plan) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_rating_plan(plan_id)
|
|
||||||
response = self.class.get("/v1/rating-plans/#{plan_id}")
|
|
||||||
handle_response(response)
|
|
||||||
RatingPlan.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
# System Management
|
|
||||||
|
|
||||||
def get_system_stats
|
|
||||||
response = self.class.get("/v1/system/stats")
|
|
||||||
handle_response(response)
|
|
||||||
SystemStats.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_health_status
|
|
||||||
response = self.class.get("/v1/health")
|
|
||||||
handle_response(response)
|
|
||||||
HealthStatus.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
# WebSocket Support
|
|
||||||
|
|
||||||
def connect_websocket(message_handler: nil)
|
|
||||||
ws_url = @config.api_url.gsub("http://", "ws://") + "/ws"
|
|
||||||
|
|
||||||
WebSocket::Client::Simple.connect(ws_url) do |ws|
|
|
||||||
ws.on :message do |msg|
|
|
||||||
begin
|
|
||||||
data = JSON.parse(msg.data)
|
|
||||||
ws_message = WebSocketMessage.new(data)
|
|
||||||
message_handler.call(ws_message) if message_handler
|
|
||||||
rescue JSON::ParserError => e
|
|
||||||
raise APIError, "Failed to parse WebSocket message: #{e.message}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# GraphQL Support
|
|
||||||
|
|
||||||
def execute_graphql_query(query, variables = {})
|
|
||||||
request = { query: query, variables: variables }
|
|
||||||
response = self.class.post("/graphql", body: request.to_json)
|
|
||||||
handle_response(response)
|
|
||||||
GraphQLResponse.new(response.parsed_response)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def handle_response(response)
|
|
||||||
case response.code
|
|
||||||
when 401
|
|
||||||
raise AuthenticationError, "Authentication failed"
|
|
||||||
when 429
|
|
||||||
raise RateLimitError, "Rate limit exceeded"
|
|
||||||
when 400..499
|
|
||||||
error_data = response.parsed_response["error"] || "Bad request"
|
|
||||||
raise APIError, "API error: #{error_data}"
|
|
||||||
when 500..599
|
|
||||||
raise ServerError, "Server error: #{response.code}"
|
|
||||||
end
|
|
||||||
|
|
||||||
response
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class Config
|
class Config
|
||||||
attr_accessor :api_url, :api_key, :timeout, :max_retries
|
attr_accessor :api_url, :api_key, :jwt_secret, :timeout, :max_retries, :retry_delay, :enable_logging
|
||||||
|
|
||||||
def initialize(options = {})
|
def initialize(config = {})
|
||||||
@api_url = options[:api_url] || "http://localhost:8000"
|
@api_url = config[:api_url] || "http://localhost:8000"
|
||||||
@api_key = options[:api_key]
|
@api_key = config[:api_key]
|
||||||
@timeout = options[:timeout] || 30
|
@jwt_secret = config[:jwt_secret]
|
||||||
@max_retries = options[:max_retries] || 3
|
@timeout = config[:timeout] || 30
|
||||||
|
@max_retries = config[:max_retries] || 3
|
||||||
|
@retry_delay = config[:retry_delay] || 1
|
||||||
|
@enable_logging = config[:enable_logging] || false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
require "openssl"
|
||||||
|
require "base64"
|
||||||
|
require "json"
|
||||||
|
|
||||||
|
module TelecomSDK
|
||||||
|
# Authentication provider for the Telecom SDK
|
||||||
|
class AuthProvider
|
||||||
|
attr_reader :api_key, :jwt_secret, :token_cache, :token_expiry
|
||||||
|
|
||||||
|
def initialize(api_key: nil, jwt_secret: nil)
|
||||||
|
@api_key = api_key
|
||||||
|
@jwt_secret = jwt_secret
|
||||||
|
@token_cache = nil
|
||||||
|
@token_expiry = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get authentication headers for API requests
|
||||||
|
def get_headers
|
||||||
|
headers = {
|
||||||
|
"Content-Type" => "application/json",
|
||||||
|
"User-Agent" => "Telecom-Ruby-SDK/1.0.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
headers["X-API-Key"] = @api_key if @api_key
|
||||||
|
|
||||||
|
if @token_cache && token_valid?
|
||||||
|
headers["Authorization"] = "Bearer #{@token_cache}"
|
||||||
|
end
|
||||||
|
|
||||||
|
headers
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate a JWT token for authentication
|
||||||
|
def generate_jwt_token(user_id, expiry_hours = 24, additional_claims = {})
|
||||||
|
raise AuthenticationError, "JWT secret not configured" unless @jwt_secret
|
||||||
|
|
||||||
|
now = Time.now.to_i
|
||||||
|
exp = now + (expiry_hours * 3600)
|
||||||
|
|
||||||
|
claims = {
|
||||||
|
"sub" => user_id,
|
||||||
|
"exp" => exp,
|
||||||
|
"iat" => now
|
||||||
|
}.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}")
|
||||||
|
|
||||||
|
@token_cache = "#{encoded_header}.#{encoded_payload}.#{signature}"
|
||||||
|
@token_expiry = exp
|
||||||
|
|
||||||
|
@token_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
# Validate a JWT token
|
||||||
|
def validate_jwt_token(token)
|
||||||
|
raise AuthenticationError, "JWT secret not configured" unless @jwt_secret
|
||||||
|
|
||||||
|
parts = token.split(".")
|
||||||
|
raise AuthenticationError, "Invalid token format" if parts.size != 3
|
||||||
|
|
||||||
|
encoded_header, encoded_payload, signature = parts
|
||||||
|
expected_signature = sign("#{encoded_header}.#{encoded_payload}")
|
||||||
|
|
||||||
|
raise AuthenticationError, "Invalid token signature" unless signature == expected_signature
|
||||||
|
|
||||||
|
payload_bytes = base64_url_decode(encoded_payload)
|
||||||
|
raise AuthenticationError, "Failed to decode payload" unless payload_bytes
|
||||||
|
|
||||||
|
payload = JSON.parse(payload_bytes)
|
||||||
|
|
||||||
|
if payload["exp"] && payload["exp"] < Time.now.to_i
|
||||||
|
raise AuthenticationError, "Token has expired"
|
||||||
|
end
|
||||||
|
|
||||||
|
payload
|
||||||
|
end
|
||||||
|
|
||||||
|
# Clear the cached JWT token
|
||||||
|
def clear_token_cache
|
||||||
|
@token_cache = nil
|
||||||
|
@token_expiry = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def token_valid?
|
||||||
|
@token_cache && @token_expiry && Time.now.to_i < @token_expiry
|
||||||
|
end
|
||||||
|
|
||||||
|
def base64_url_encode(data)
|
||||||
|
json = data.to_json
|
||||||
|
Base64.urlsafe_encode64(json).gsub("=", "")
|
||||||
|
end
|
||||||
|
|
||||||
|
def base64_url_decode(data)
|
||||||
|
# Add padding if needed
|
||||||
|
padded_data = data + "=" * (4 - data.length % 4)
|
||||||
|
Base64.urlsafe_decode64(padded_data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def sign(data)
|
||||||
|
hmac = OpenSSL::HMAC.new(@jwt_secret, OpenSSL::Digest::SHA256.new)
|
||||||
|
hmac.update(data)
|
||||||
|
Base64.urlsafe_encode64(hmac.digest).gsub("=", "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
module TelecomSDK
|
||||||
|
# API for GraphQL queries
|
||||||
|
class GraphQLAPI
|
||||||
|
def initialize(client)
|
||||||
|
@client = client
|
||||||
|
end
|
||||||
|
|
||||||
|
def execute(query, variables = nil)
|
||||||
|
request = { query: query }
|
||||||
|
request[:variables] = variables if variables
|
||||||
|
@client.post("/graphql", request)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
require "httparty"
|
||||||
|
|
||||||
|
module TelecomSDK
|
||||||
|
# HTTP client for making API requests
|
||||||
|
class HTTPClient
|
||||||
|
include HTTParty
|
||||||
|
|
||||||
|
def initialize(base_url, auth_provider, timeout: 30, max_retries: 3, retry_delay: 1, enable_logging: false)
|
||||||
|
@base_url = base_url
|
||||||
|
@auth_provider = auth_provider
|
||||||
|
@timeout = timeout
|
||||||
|
@max_retries = max_retries
|
||||||
|
@retry_delay = retry_delay
|
||||||
|
@enable_logging = enable_logging
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(path, params = {})
|
||||||
|
request("GET", path, nil, params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def post(path, body = nil)
|
||||||
|
request("POST", path, body, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put(path, body = nil)
|
||||||
|
request("PUT", path, body, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(path)
|
||||||
|
request("DELETE", path, nil, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def request(method, path, body, params)
|
||||||
|
url = "#{@base_url}#{path}"
|
||||||
|
headers = @auth_provider.get_headers
|
||||||
|
|
||||||
|
options = {
|
||||||
|
timeout: @timeout,
|
||||||
|
headers: headers
|
||||||
|
}
|
||||||
|
|
||||||
|
options[:query] = params if params
|
||||||
|
options[:body] = body.to_json if body
|
||||||
|
|
||||||
|
last_error = nil
|
||||||
|
(0..@max_retries).each do |attempt|
|
||||||
|
begin
|
||||||
|
response = HTTParty.send(method.downcase, url, options)
|
||||||
|
handle_response_errors(response)
|
||||||
|
return response.parsed_response
|
||||||
|
rescue StandardError => e
|
||||||
|
last_error = e
|
||||||
|
if attempt < @max_retries
|
||||||
|
sleep(@retry_delay * (2 ** attempt))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
raise last_error || NetworkError, "Request failed after #{@max_retries} retries"
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_response_errors(response)
|
||||||
|
case response.code
|
||||||
|
when 401
|
||||||
|
raise AuthenticationError, "Authentication failed"
|
||||||
|
when 429
|
||||||
|
raise RateLimitError, "Rate limit exceeded"
|
||||||
|
when 400..499
|
||||||
|
raise APIError, "API error: #{response.parsed_response}"
|
||||||
|
when 500..599
|
||||||
|
raise ServerError, "Server error: #{response.code}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
# HTTParty doesn't need explicit cleanup
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
module TelecomSDK
|
||||||
|
# API for payment management
|
||||||
|
class PaymentAPI
|
||||||
|
def initialize(client)
|
||||||
|
@client = client
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_transaction(request)
|
||||||
|
@client.post("/v1/payments/transactions", request)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_transaction(transaction_id)
|
||||||
|
@client.get("/v1/payments/transactions/#{transaction_id}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_transactions(subscriber_id: nil, status: nil, page: 1, page_size: 50)
|
||||||
|
params = { page: page, page_size: page_size }
|
||||||
|
params[:subscriber_id] = subscriber_id if subscriber_id
|
||||||
|
params[:status] = status if status
|
||||||
|
@client.get("/v1/payments/transactions", params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
module TelecomSDK
|
||||||
|
# API for rating plan management
|
||||||
|
class RatingPlanAPI
|
||||||
|
def initialize(client)
|
||||||
|
@client = client
|
||||||
|
end
|
||||||
|
|
||||||
|
def list
|
||||||
|
@client.get("/v1/rating-plans")
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(plan_id)
|
||||||
|
@client.get("/v1/rating-plans/#{plan_id}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
module TelecomSDK
|
||||||
|
# API for subscriber management
|
||||||
|
class SubscriberAPI
|
||||||
|
def initialize(client)
|
||||||
|
@client = client
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(id)
|
||||||
|
@client.get("/v1/subscribers/#{id}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def list(page: 1, page_size: 50, status: nil)
|
||||||
|
params = { page: page, page_size: page_size }
|
||||||
|
params[:status] = status if status
|
||||||
|
@client.get("/v1/subscribers", params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(request)
|
||||||
|
@client.post("/v1/subscribers", request)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(id, request)
|
||||||
|
@client.put("/v1/subscribers/#{id}", request)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(id)
|
||||||
|
@client.delete("/v1/subscribers/#{id}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def suspend(id)
|
||||||
|
@client.post("/v1/subscribers/#{id}/suspend")
|
||||||
|
end
|
||||||
|
|
||||||
|
def activate(id)
|
||||||
|
@client.post("/v1/subscribers/#{id}/activate")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
module TelecomSDK
|
||||||
|
# API for system management
|
||||||
|
class SystemAPI
|
||||||
|
def initialize(client)
|
||||||
|
@client = client
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_stats
|
||||||
|
@client.get("/v1/system/stats")
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_health
|
||||||
|
@client.get("/v1/health")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
module TelecomSDK
|
||||||
|
# API for usage management
|
||||||
|
class UsageAPI
|
||||||
|
def initialize(client)
|
||||||
|
@client = client
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_stats(subscriber_id, start_date, end_date)
|
||||||
|
params = {
|
||||||
|
start_date: start_date.iso8601,
|
||||||
|
end_date: end_date.iso8601
|
||||||
|
}
|
||||||
|
@client.get("/v1/subscribers/#{subscriber_id}/usage", params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def list_events(subscriber_id: nil, usage_type: nil, start_date: nil, end_date: nil, page: 1, page_size: 50)
|
||||||
|
params = { page: page, page_size: page_size }
|
||||||
|
params[:subscriber_id] = subscriber_id if subscriber_id
|
||||||
|
params[:usage_type] = usage_type if usage_type
|
||||||
|
params[:start_date] = start_date.iso8601 if start_date
|
||||||
|
params[:end_date] = end_date.iso8601 if end_date
|
||||||
|
@client.get("/v1/usage/events", params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_realtime(subscriber_id)
|
||||||
|
@client.get("/v1/subscribers/#{subscriber_id}/realtime")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user