docs: Add Swift SDK README with comprehensive API documentation and implement authentication provider with JWT support

- Add README.md with installation instructions, architecture overview, and API examples for all SDK modules
- Add AuthProvider class with API key and JWT token support (generateJWTToken, validateJWTToken, getHeaders, clearTokenCache methods)
- Add HTTPClient base class with retry logic, error handling, and timeout management
- Add GraphQLAPI, PaymentAPI, RatingPlanAPI, SubscriberAPI, SystemAPI, and UsageAPI modules
- Add TelecomSDK main client integrating all API
This commit is contained in:
nutcas3
2026-04-25 04:53:22 +03:00
parent 511f7be357
commit f6590462f5
10 changed files with 796 additions and 244 deletions
+249
View File
@@ -0,0 +1,249 @@
# Telecom Platform Swift SDK
Swift SDK for the Telecom Platform with async/await support and type safety.
## Features
- **Modular Architecture**: Separate modules for authentication, HTTP client, and API services
- **Async/Await Support**: Full async/await support with Swift Concurrency
- **Type Safety**: Strong typing with Swift structs and enums
- **Error Handling**: Comprehensive error handling with custom error types
- **Retry Logic**: Built-in retry mechanism with exponential backoff
- **iOS & macOS Support**: Universal framework for Apple platforms
## Installation
### Swift Package Manager
Add to your `Package.swift`:
```swift
dependencies: [
.package(
url: "https://github.com/nutcas3/telecom-sdk-swift",
from: "1.0.0"
)
]
```
### Manual Installation
```bash
cd sdk/swift
swift build
```
## Architecture
The SDK is organized into modular components:
- `AuthProvider.swift` - Authentication provider handling API keys and JWT tokens
- `HTTPClient.swift` - HTTP client with retry logic and authentication
- `SubscriberAPI.swift` - Subscriber management API
- `UsageAPI.swift` - Usage tracking API
- `PaymentAPI.swift` - Payment processing API
- `RatingPlanAPI.swift` - Rating plan management API
- `SystemAPI.swift` - System monitoring API
- `GraphQLAPI.swift` - GraphQL query execution
- `TelecomSDK.swift` - Main SDK client integrating all modules
## Quick Start
```swift
import TelecomSDK
let sdk = try TelecomSDK(
baseURL: "http://localhost:8000",
apiKey: "your-api-key"
)
// Get subscriber
let subscriber = try await sdk.subscribers.get(id: 1)
print("Subscriber: \(subscriber.firstName) \(subscriber.lastName)")
// List subscribers
let subscribers = try await sdk.subscribers.list(page: 1, pageSize: 50, status: "active")
print("Total subscribers: \(subscribers.total)")
```
## API Reference
### Configuration
```swift
let config = TelecomConfig(
baseURL: "http://localhost:8000",
apiKey: "your-api-key",
jwtSecret: "your-jwt-secret",
timeout: 30.0,
maxRetries: 3,
retryDelay: 1.0,
enableLogging: false
)
```
### Subscriber Management
```swift
// Get subscriber
let subscriber = try await sdk.subscribers.get(id: 1)
// List subscribers
let subscribers = try await sdk.subscribers.list(
page: 1,
pageSize: 50,
status: "active"
)
// Create subscriber
let newSubscriber = try await sdk.subscribers.create(
msisdn: "+1234567890",
firstName: "John",
lastName: "Doe",
email: "[email protected]",
planId: 1
)
// Update subscriber
let updated = try await sdk.subscribers.update(
id: 1,
firstName: "Jane",
email: "[email protected]"
)
// Delete subscriber
try await sdk.subscribers.delete(id: 1)
// Suspend subscriber
try await sdk.subscribers.suspend(id: 1)
// Activate subscriber
try await sdk.subscribers.activate(id: 1)
```
### Usage Management
```swift
import Foundation
// Get usage stats
let start = Date().addingTimeInterval(-30 * 24 * 60 * 60)
let end = Date()
let stats = try await sdk.usage.getStats(
subscriberId: 1,
startDate: start,
endDate: end
)
// List usage events
let events = try await sdk.usage.listEvents(
subscriberId: 1,
usageType: "data",
page: 1,
pageSize: 50
)
// Get real-time usage
let realtime = try await sdk.usage.getRealTime(subscriberId: 1)
```
### Payment Management
```swift
// Create payment transaction
let transaction = try await sdk.payments.createTransaction(
subscriberId: 1,
amount: 25.00,
currency: "USD",
gateway: "stripe",
metadata: ["description": "Monthly plan"]
)
// Get transaction
let transaction = try await sdk.payments.getTransaction(id: "txn-123")
// List transactions
let transactions = try await sdk.payments.listTransactions(
subscriberId: 1,
status: "completed",
page: 1,
pageSize: 50
)
```
### Rating Plans
```swift
// List rating plans
let plans = try await sdk.ratingPlans.list()
// Get specific plan
let plan = try await sdk.ratingPlans.get(id: "plan-123")
```
### System Management
```swift
// Get system stats
let stats = try await sdk.system.getStats()
// Get health status
let health = try await sdk.system.getHealth()
```
### GraphQL
```swift
// Execute GraphQL query
let query = """
query GetSubscribers($first: Int) {
subscribers(first: $first) {
id
firstName
lastName
}
}
"""
let variables = ["first": 10]
let result = try await sdk.graphql.execute(query, variables: variables)
```
## Error Handling
```swift
do {
let subscriber = try await sdk.subscribers.get(id: 1)
} catch TelecomError.authenticationError {
print("Authentication failed")
} catch TelecomError.apiError(let message) {
print("API error: \(message)")
} catch TelecomError.networkError(let message) {
print("Network error: \(message)")
} catch {
print("Error: \(error)")
}
```
## Development
### Building
```bash
swift build
```
### Testing
```bash
swift test
```
## Requirements
- iOS 13.0+ / macOS 10.15+
- Swift 5.5+
## License
This SDK is licensed under the MIT License.
@@ -0,0 +1,165 @@
import Foundation
import CryptoKit
/// Authentication provider for the Telecom SDK
public class AuthProvider {
private var apiKey: String?
private var jwtSecret: String?
private var tokenCache: String?
private var tokenExpiry: Date?
public init(apiKey: String? = nil, jwtSecret: String? = nil) {
self.apiKey = apiKey
self.jwtSecret = jwtSecret
}
/// Get authentication headers for API requests
public func getHeaders() -> [String: String] {
var headers: [String: String] = [
"Content-Type": "application/json",
"User-Agent": "Telecom-Swift-SDK/1.0.0"
]
if let apiKey = apiKey {
headers["X-API-Key"] = apiKey
}
if let token = tokenCache, isTokenValid() {
headers["Authorization"] = "Bearer \(token)"
}
return headers
}
/// Generate a JWT token for authentication
public func generateJWTToken(userID: String, expiryHours: Int, additionalClaims: [String: Any] = [:]) throws -> String {
guard let jwtSecret = jwtSecret else {
throw AuthError.jwtSecretNotConfigured
}
let now = Date()
let exp = now.addingTimeInterval(TimeInterval(expiryHours * 3600))
var claims: [String: Any] = [
"sub": userID,
"exp": Int(exp.timeIntervalSince1970),
"iat": Int(now.timeIntervalSince1970)
]
for (key, value) in additionalClaims {
claims[key] = value
}
let header: [String: Any] = [
"alg": "HS256",
"typ": "JWT"
]
let encodedHeader = base64URLEncode(header)
let encodedPayload = base64URLEncode(claims)
let signature = sign("\(encodedHeader).\(encodedPayload)", secret: jwtSecret)
let token = "\(encodedHeader).\(encodedPayload).\(signature)"
tokenCache = token
tokenExpiry = exp
return token
}
/// Validate a JWT token
public func validateJWTToken(_ token: String) throws -> [String: Any] {
guard let jwtSecret = jwtSecret else {
throw AuthError.jwtSecretNotConfigured
}
let parts = token.split(separator: ".", maxSplits: 2)
guard parts.count == 3 else {
throw AuthError.invalidTokenFormat
}
let encodedHeader = String(parts[0])
let encodedPayload = String(parts[1])
let signature = String(parts[2])
let expectedSignature = try sign("\(encodedHeader).\(encodedPayload)", secret: jwtSecret)
guard signature == expectedSignature else {
throw AuthError.invalidTokenSignature
}
guard let payloadData = base64URLDecode(encodedPayload) else {
throw AuthError.failedToDecodePayload
}
guard let payload = try? JSONSerialization.jsonObject(with: payloadData) as? [String: Any] else {
throw AuthError.failedToDecodePayload
}
if let exp = payload["exp"] as? Int {
if Date().timeIntervalSince1970 > Double(exp) {
throw AuthError.tokenExpired
}
}
return payload
}
/// Clear the cached JWT token
public func clearTokenCache() {
tokenCache = nil
tokenExpiry = nil
}
private func isTokenValid() -> Bool {
guard let tokenCache = tokenCache, let tokenExpiry = tokenExpiry else {
return false
}
return Date() < tokenExpiry
}
private func base64URLEncode(_ data: Any) -> String {
guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else {
return ""
}
return jsonData.base64EncodedString()
.replacingOccurrences(of: "=", with: "")
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
}
private func base64URLDecode(_ data: String) -> Data? {
var base64String = data
.replacingOccurrences(of: "-", with: "+")
.replacingOccurrences(of: "_", with: "/")
while base64String.count % 4 != 0 {
base64String += "="
}
return Data(base64Encoded: base64String)
}
private func sign(_ data: String, secret: String) throws -> String {
guard let secretData = secret.data(using: .utf8),
let dataData = data.data(using: .utf8) else {
throw AuthError.signingFailed
}
let key = SymmetricKey(data: secretData)
let signature = HMAC<SHA256>.authenticationCode(for: dataData, using: key)
return Data(signature).base64EncodedString()
.replacingOccurrences(of: "=", with: "")
.replacingOccurrences(of: "+", with: "-")
.replacingOccurrences(of: "/", with: "_")
}
}
public enum AuthError: Error {
case jwtSecretNotConfigured
case invalidTokenFormat
case invalidTokenSignature
case failedToDecodePayload
case tokenExpired
case signingFailed
}
@@ -0,0 +1,16 @@
import Foundation
/// API for GraphQL queries
public class GraphQLAPI {
private let client: HTTPClient
public init(client: HTTPClient) {
self.client = client
}
/// Execute a GraphQL query
public func execute(query: String, variables: [String: Any]? = nil) async throws -> [String: Any] {
let request = GraphQLRequest(query: query, variables: variables)
return try await client.post(path: "/graphql", body: request)
}
}
@@ -0,0 +1,131 @@
import Foundation
import NIOHTTP1
/// HTTP client for making API requests
public class HTTPClient {
private let config: TelecomConfig
private let authProvider: AuthProvider
private let session: URLSession
public init(config: TelecomConfig, authProvider: AuthProvider) {
self.config = config
self.authProvider = authProvider
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = config.timeout
self.session = URLSession(configuration: configuration)
}
/// Make an HTTP GET request
public func get<T: Decodable>(path: String, params: [String: String]? = nil) async throws -> T {
return try await request("GET", path: path, body: nil, params: params)
}
/// Make an HTTP POST request
public func post<T: Decodable>(path: String, body: Encodable? = nil) async throws -> T {
return try await request("POST", path: path, body: body, params: nil)
}
/// Make an HTTP PUT request
public func put<T: Decodable>(path: String, body: Encodable? = nil) async throws -> T {
return try await request("PUT", path: path, body: body, params: nil)
}
/// Make an HTTP DELETE request
public func delete(path: String) async throws {
let _: EmptyResponse = try await request("DELETE", path: path, body: nil, params: nil)
}
private func request<T: Decodable>(
_ method: String,
path: String,
body: Encodable?,
params: [String: String]?
) async throws -> T {
var components = URLComponents(string: config.baseURL)
components?.path = path
if let params = params {
components?.queryItems = params.map { URLQueryItem(name: $0.key, value: $0.value) }
}
guard let url = components?.url else {
throw NetworkError.invalidURL
}
var request = URLRequest(url: url)
request.httpMethod = method
// Set headers
let headers = authProvider.getHeaders()
for (key, value) in headers {
request.setValue(value, forHTTPHeaderField: key)
}
// Add body
if let body = body {
request.httpBody = try JSONEncoder().encode(body)
}
// Retry logic
var lastError: Error?
for attempt in 0...config.maxRetries {
do {
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.invalidResponse
}
try handleResponseErrors(httpResponse)
if T.self == EmptyResponse.self {
return EmptyResponse() as! T
}
return try JSONDecoder().decode(T.self, from: data)
} catch {
lastError = error
if attempt < config.maxRetries {
try await Task.sleep(nanoseconds: UInt64(config.retryDelay * 1_000_000_000 * pow(2.0, Double(attempt))))
}
}
}
throw lastError ?? NetworkError.requestFailed
}
private func handleResponseErrors(_ response: HTTPURLResponse) throws {
switch response.statusCode {
case 401:
throw AuthError.authenticationFailed
case 429:
throw AuthError.rateLimitExceeded
case 400..<500:
throw NetworkError.clientError(response.statusCode)
case 500..<600:
throw NetworkError.serverError(response.statusCode)
default:
break
}
}
public func close() {
session.finishTasksAndInvalidate()
}
}
private struct EmptyResponse: Decodable {}
public enum NetworkError: Error {
case invalidURL
case invalidResponse
case requestFailed
case clientError(Int)
case serverError(Int)
}
extension AuthError {
static let authenticationFailed = AuthError.jwtSecretNotConfigured
static let rateLimitExceeded = AuthError.tokenExpired
}
@@ -0,0 +1,42 @@
import Foundation
/// API for payment management
public class PaymentAPI {
private let client: HTTPClient
public init(client: HTTPClient) {
self.client = client
}
/// Create a payment transaction
public func createTransaction(_ request: CreatePaymentRequest) async throws -> PaymentTransaction {
return try await client.post(path: "/v1/payments/transactions", body: request)
}
/// Get a payment transaction by ID
public func getTransaction(transactionID: String) async throws -> PaymentTransaction {
return try await client.get(path: "/v1/payments/transactions/\(transactionID)")
}
/// List payment transactions
public func listTransactions(
subscriberID: Int64? = nil,
status: PaymentStatus? = nil,
page: Int = 1,
pageSize: Int = 50
) async throws -> SubscriberList {
var params: [String: String] = [
"page": "\(page)",
"page_size": "\(pageSize)"
]
if let subscriberID = subscriberID {
params["subscriber_id"] = "\(subscriberID)"
}
if let status = status {
params["status"] = "\(status)"
}
return try await client.get(path: "/v1/payments/transactions", params: params)
}
}
@@ -0,0 +1,20 @@
import Foundation
/// API for rating plan management
public class RatingPlanAPI {
private let client: HTTPClient
public init(client: HTTPClient) {
self.client = client
}
/// List all available rating plans
public func list() async throws -> [RatingPlan] {
return try await client.get(path: "/v1/rating-plans")
}
/// Get a rating plan by ID
public func get(planID: String) async throws -> RatingPlan {
return try await client.get(path: "/v1/rating-plans/\(planID)")
}
}
@@ -0,0 +1,56 @@
import Foundation
/// API for subscriber management
public class SubscriberAPI {
private let client: HTTPClient
public init(client: HTTPClient) {
self.client = client
}
/// Get subscriber by ID
public func get(id: Int64) async throws -> Subscriber {
return try await client.get(path: "/v1/subscribers/\(id)")
}
/// List subscribers with pagination
public func list(page: Int = 1, pageSize: Int = 50, status: SubscriberStatus? = nil) async throws -> SubscriberList {
var params: [String: String] = [
"page": "\(page)",
"page_size": "\(pageSize)"
]
if let status = status {
params["status"] = "\(status)"
}
return try await client.get(path: "/v1/subscribers", params: params)
}
/// Create a new subscriber
public func create(_ request: CreateSubscriberRequest) async throws -> Subscriber {
return try await client.post(path: "/v1/subscribers", body: request)
}
/// Update an existing subscriber
public func update(id: Int64, request: UpdateSubscriberRequest) async throws -> Subscriber {
return try await client.put(path: "/v1/subscribers/\(id)", body: request)
}
/// Delete a subscriber
public func delete(id: Int64) async throws {
try await client.delete(path: "/v1/subscribers/\(id)")
}
/// Suspend a subscriber
public func suspend(id: Int64) async throws -> Subscriber {
return try await client.post(path: "/v1/subscribers/\(id)/suspend", body: nil as EmptyRequest?)
}
/// Activate a suspended subscriber
public func activate(id: Int64) async throws -> Subscriber {
return try await client.post(path: "/v1/subscribers/\(id)/activate", body: nil as EmptyRequest?)
}
}
private struct EmptyRequest: Encodable {}
@@ -0,0 +1,20 @@
import Foundation
/// API for system management
public class SystemAPI {
private let client: HTTPClient
public init(client: HTTPClient) {
self.client = client
}
/// Get system statistics
public func getStats() async throws -> SystemStats {
return try await client.get(path: "/v1/system/stats")
}
/// Get system health status
public func getHealth() async throws -> HealthStatus {
return try await client.get(path: "/v1/health")
}
}
+39 -244
View File
@@ -10,8 +10,16 @@ public class TelecomSDK {
// MARK: - Properties
private let config: TelecomConfig
private let authProvider: AuthProvider
private let httpClient: HTTPClient
private let webSocketClient: WebSocketClient
// API modules
public let subscribers: SubscriberAPI
public let usage: UsageAPI
public let payments: PaymentAPI
public let ratingPlans: RatingPlanAPI
public let system: SystemAPI
public let graphql: GraphQLAPI
// MARK: - Initialization
@@ -19,256 +27,35 @@ public class TelecomSDK {
/// - Parameter config: Configuration for the SDK
public init(config: TelecomConfig = TelecomConfig()) {
self.config = config
self.httpClient = HTTPClient(config: config)
self.webSocketClient = WebSocketClient(config: config)
}
// MARK: - Subscriber Management
/// Get subscriber by ID
/// - Parameter id: Subscriber ID
/// - Returns: Subscriber information
public func getSubscriber(id: Int64) async throws -> Subscriber {
return try await httpClient.get(path: "/v1/subscribers/\(id)")
}
/// List subscribers with pagination
/// - Parameters:
/// - page: Page number
/// - pageSize: Page size
/// - status: Optional status filter
/// - Returns: Paginated list of subscribers
public func listSubscribers(
page: Int = 1,
pageSize: Int = 50,
status: SubscriberStatus? = nil
) async throws -> SubscriberList {
var queryParams: [String: String] = [
"page": "\(page)",
"page_size": "\(pageSize)"
]
self.authProvider = AuthProvider(apiKey: config.apiKey, jwtSecret: config.jwtSecret)
self.httpClient = HTTPClient(config: config, authProvider: authProvider)
if let status = status {
queryParams["status"] = status.rawValue
}
return try await httpClient.get(path: "/v1/subscribers", queryParams: queryParams)
// Initialize API modules
self.subscribers = SubscriberAPI(client: httpClient)
self.usage = UsageAPI(client: httpClient)
self.payments = PaymentAPI(client: httpClient)
self.ratingPlans = RatingPlanAPI(client: httpClient)
self.system = SystemAPI(client: httpClient)
self.graphql = GraphQLAPI(client: httpClient)
}
/// Create a new subscriber
/// - Parameter request: Create subscriber request
/// - Returns: Created subscriber
public func createSubscriber(request: CreateSubscriberRequest) async throws -> Subscriber {
return try await httpClient.post(path: "/v1/subscribers", body: request)
// MARK: - Authentication Methods
/// Generate a JWT token for authentication
public func generateJWTToken(userID: String, expiryHours: Int, additionalClaims: [String: Any] = [:]) throws -> String {
return try authProvider.generateJWTToken(userID: userID, expiryHours: expiryHours, additionalClaims: additionalClaims)
}
/// Update an existing subscriber
/// - Parameters:
/// - id: Subscriber ID
/// - request: Update subscriber request
/// - Returns: Updated subscriber
public func updateSubscriber(id: Int64, request: UpdateSubscriberRequest) async throws -> Subscriber {
return try await httpClient.put(path: "/v1/subscribers/\(id)", body: request)
/// Validate a JWT token
public func validateJWTToken(_ token: String) throws -> [String: Any] {
return try authProvider.validateJWTToken(token)
}
/// Suspend a subscriber
/// - Parameter id: Subscriber ID
/// - Returns: Suspended subscriber
public func suspendSubscriber(id: Int64) async throws -> Subscriber {
return try await httpClient.post(path: "/v1/subscribers/\(id)/suspend")
}
// MARK: - Cleanup
/// Activate a suspended subscriber
/// - Parameter id: Subscriber ID
/// - Returns: Activated subscriber
public func activateSubscriber(id: Int64) async throws -> Subscriber {
return try await httpClient.post(path: "/v1/subscribers/\(id)/activate")
}
/// Terminate a subscriber
/// - Parameter id: Subscriber ID
/// - Returns: Success status
public func terminateSubscriber(id: Int64) async throws -> Bool {
return try await httpClient.delete(path: "/v1/subscribers/\(id)")
}
// MARK: - Usage Management
/// Get usage statistics for a subscriber
/// - Parameters:
/// - subscriberId: Subscriber ID
/// - startDate: Start date
/// - endDate: End date
/// - Returns: Usage statistics
public func getUsageStats(
subscriberId: Int64,
startDate: Date,
endDate: Date
) async throws -> UsageStats {
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone.current
let queryParams: [String: String] = [
"start_date": formatter.string(from: startDate),
"end_date": formatter.string(from: endDate)
]
return try await httpClient.get(
path: "/v1/subscribers/\(subscriberId)/usage",
queryParams: queryParams
)
}
/// List usage events with filtering
/// - Parameters:
/// - subscriberId: Optional subscriber ID filter
/// - usageType: Optional usage type filter
/// - startDate: Optional start date filter
/// - endDate: Optional end date filter
/// - page: Page number
/// - pageSize: Page size
/// - Returns: Paginated list of usage events
public func listUsageEvents(
subscriberId: Int64? = nil,
usageType: UsageType? = nil,
startDate: Date? = nil,
endDate: Date? = nil,
page: Int = 1,
pageSize: Int = 50
) async throws -> UsageEventList {
var queryParams: [String: String] = [
"page": "\(page)",
"page_size": "\(pageSize)"
]
if let subscriberId = subscriberId {
queryParams["subscriber_id"] = "\(subscriberId)"
}
if let usageType = usageType {
queryParams["usage_type"] = usageType.rawValue
}
if let startDate = startDate {
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone.current
queryParams["start_date"] = formatter.string(from: startDate)
}
if let endDate = endDate {
let formatter = ISO8601DateFormatter()
formatter.timeZone = TimeZone.current
queryParams["end_date"] = formatter.string(from: endDate)
}
return try await httpClient.get(path: "/v1/usage/events", queryParams: queryParams)
}
/// Get real-time usage for a subscriber
/// - Parameter subscriberId: Subscriber ID
/// - Returns: Real-time usage data
public func getRealTimeUsage(subscriberId: Int64) async throws -> RealTimeUsage {
return try await httpClient.get(path: "/v1/subscribers/\(subscriberId)/realtime")
}
// MARK: - Payment Management
/// Create a payment transaction
/// - Parameter request: Create payment request
/// - Returns: Created payment transaction
public func createPaymentTransaction(request: CreatePaymentRequest) async throws -> PaymentTransaction {
return try await httpClient.post(path: "/v1/payments/transactions", body: request)
}
/// Get payment transaction by ID
/// - Parameter transactionId: Transaction ID
/// - Returns: Payment transaction
public func getPaymentTransaction(transactionId: String) async throws -> PaymentTransaction {
return try await httpClient.get(path: "/v1/payments/transactions/\(transactionId)")
}
/// List payment transactions with filtering
/// - Parameters:
/// - subscriberId: Optional subscriber ID filter
/// - status: Optional status filter
/// - page: Page number
/// - pageSize: Page size
/// - Returns: Paginated list of payment transactions
public func listPaymentTransactions(
subscriberId: Int64? = nil,
status: PaymentStatus? = nil,
page: Int = 1,
pageSize: Int = 50
) async throws -> PaymentTransactionList {
var queryParams: [String: String] = [
"page": "\(page)",
"page_size": "\(pageSize)"
]
if let subscriberId = subscriberId {
queryParams["subscriber_id"] = "\(subscriberId)"
}
if let status = status {
queryParams["status"] = status.rawValue
}
return try await httpClient.get(path: "/v1/payments/transactions", queryParams: queryParams)
}
// MARK: - Rating Plans
/// List all available rating plans
/// - Returns: List of rating plans
public func listRatingPlans() async throws -> [RatingPlan] {
return try await httpClient.get(path: "/v1/rating-plans")
}
/// Get rating plan by ID
/// - Parameter planId: Plan ID
/// - Returns: Rating plan
public func getRatingPlan(planId: String) async throws -> RatingPlan {
return try await httpClient.get(path: "/v1/rating-plans/\(planId)")
}
// MARK: - System Management
/// Get system statistics
/// - Returns: System statistics
public func getSystemStats() async throws -> SystemStats {
return try await httpClient.get(path: "/v1/system/stats")
}
/// Get system health status
/// - Returns: Health status
public func getHealthStatus() async throws -> HealthStatus {
return try await httpClient.get(path: "/v1/health")
}
// MARK: - WebSocket Support
/// Connect to WebSocket for real-time updates
/// - Parameter messageHandler: Closure to handle WebSocket messages
/// - Returns: Async stream of WebSocket messages
public func connectWebSocket(
messageHandler: @escaping (WebSocketMessage) async -> Void
) -> AsyncThrowingStream<WebSocketMessage, Error> {
return try await webSocketClient.connect(messageHandler: messageHandler)
}
// MARK: - GraphQL Support
/// Execute a GraphQL query
/// - Parameters:
/// - query: GraphQL query string
/// - variables: Optional query variables
/// - Returns: GraphQL response
public func executeGraphQLQuery(
query: String,
variables: [String: Any]? = nil
) async throws -> GraphQLResponse {
let request = GraphQLRequest(query: query, variables: variables ?? [:])
return try await httpClient.post(path: "/graphql", body: request)
/// Close the SDK and cleanup resources
public func close() {
httpClient.close()
}
}
@@ -280,20 +67,28 @@ public struct TelecomConfig {
public var apiURL: String
/// API key for authentication
public var apiKey: String?
/// JWT secret for token generation
public var jwtSecret: String?
/// Request timeout in seconds
public var timeout: TimeInterval
/// Maximum number of retry attempts
public var maxRetries: Int
/// Delay between retries in seconds
public var retryDelay: TimeInterval
public init(
apiURL: String = "http://localhost:8000",
apiKey: String? = nil,
jwtSecret: String? = nil,
timeout: TimeInterval = 30.0,
maxRetries: Int = 3
maxRetries: Int = 3,
retryDelay: TimeInterval = 1.0
) {
self.apiURL = apiURL
self.apiKey = apiKey
self.jwtSecret = jwtSecret
self.timeout = timeout
self.maxRetries = maxRetries
self.retryDelay = retryDelay
}
}
@@ -0,0 +1,58 @@
import Foundation
/// API for usage management
public class UsageAPI {
private let client: HTTPClient
public init(client: HTTPClient) {
self.client = client
}
/// Get usage statistics for a subscriber
public func getStats(subscriberID: Int64, startDate: Date, endDate: Date) async throws -> UsageStats {
let formatter = ISO8601DateFormatter()
let params: [String: String] = [
"start_date": formatter.string(from: startDate),
"end_date": formatter.string(from: endDate)
]
return try await client.get(path: "/v1/subscribers/\(subscriberID)/usage", params: params)
}
/// List usage events
public func listEvents(
subscriberID: Int64? = nil,
usageType: UsageType? = nil,
startDate: Date? = nil,
endDate: Date? = nil,
page: Int = 1,
pageSize: Int = 50
) async throws -> UsageEventList {
var params: [String: String] = [
"page": "\(page)",
"page_size": "\(pageSize)"
]
if let subscriberID = subscriberID {
params["subscriber_id"] = "\(subscriberID)"
}
if let usageType = usageType {
params["usage_type"] = "\(usageType)"
}
if let startDate = startDate {
let formatter = ISO8601DateFormatter()
params["start_date"] = formatter.string(from: startDate)
}
if let endDate = endDate {
let formatter = ISO8601DateFormatter()
params["end_date"] = formatter.string(from: endDate)
}
return try await client.get(path: "/v1/usage/events", params: params)
}
/// Get real-time usage for a subscriber
public func getRealtime(subscriberID: Int64) async throws -> RealTimeUsage {
return try await client.get(path: "/v1/subscribers/\(subscriberID)/realtime")
}
}