docs: Add Kotlin 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, System
This commit is contained in:
nutcas3
2026-04-25 04:52:19 +03:00
parent 8c4f4e17da
commit 511f7be357
10 changed files with 880 additions and 299 deletions
+259
View File
@@ -0,0 +1,259 @@
# Telecom Platform Kotlin SDK
Kotlin SDK for the Telecom Platform with coroutines support and type safety.
## Features
- **Modular Architecture**: Separate modules for authentication, HTTP client, and API services
- **Coroutines Support**: Full async/await support with Kotlin coroutines
- **Type Safety**: Strong typing with Kotlin data classes
- **Error Handling**: Comprehensive error handling with sealed classes
- **Retry Logic**: Built-in retry mechanism with exponential backoff
- **Multiplatform Support**: Works on JVM, Android, and more
## Installation
### Gradle (Kotlin DSL)
```kotlin
dependencies {
implementation("com.telecom:telecom-sdk:1.0.0")
}
```
### Gradle (Groovy DSL)
```groovy
dependencies {
implementation 'com.telecom:telecom-sdk:1.0.0'
}
```
### Maven
```xml
<dependency>
<groupId>com.telecom</groupId>
<artifactId>telecom-sdk</artifactId>
<version>1.0.0</version>
</dependency>
```
## Architecture
The SDK is organized into modular components:
- `AuthProvider.kt` - Authentication provider handling API keys and JWT tokens
- `HTTPClient.kt` - HTTP client with retry logic and authentication
- `SubscriberAPI.kt` - Subscriber management API
- `UsageAPI.kt` - Usage tracking API
- `PaymentAPI.kt` - Payment processing API
- `RatingPlanAPI.kt` - Rating plan management API
- `SystemAPI.kt` - System monitoring API
- `GraphQLAPI.kt` - GraphQL query execution
- `TelecomSDK.kt` - Main SDK client integrating all modules
## Quick Start
```kotlin
import com.telecom.TelecomSDK
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
val sdk = TelecomSDK(
baseURL = "http://localhost:8000",
apiKey = "your-api-key"
)
// Get subscriber
val subscriber = sdk.subscribers.get(1)
println("Subscriber: ${subscriber.firstName} ${subscriber.lastName}")
// List subscribers
val subscribers = sdk.subscribers.list(1, 50, "active")
println("Total subscribers: ${subscribers.total}")
}
```
## API Reference
### Configuration
```kotlin
val config = TelecomConfig(
apiURL = "http://localhost:8000",
apiKey = "your-api-key",
jwtSecret = "your-jwt-secret",
timeout = Duration.ofSeconds(30),
maxRetries = 3,
retryDelay = Duration.ofSeconds(1),
enableLogging = false
)
```
### Subscriber Management
```kotlin
// Get subscriber
val subscriber = sdk.subscribers.get(1)
// List subscribers
val subscribers = sdk.subscribers.list(
page = 1,
pageSize = 50,
status = "active"
)
// Create subscriber
val newSubscriber = sdk.subscribers.create(
msisdn = "+1234567890",
firstName = "John",
lastName = "Doe",
email = "[email protected]",
planId = 1
)
// Update subscriber
val updated = sdk.subscribers.update(
id = 1,
firstName = "Jane",
email = "[email protected]"
)
// Delete subscriber
sdk.subscribers.delete(1)
// Suspend subscriber
sdk.subscribers.suspend(1)
// Activate subscriber
sdk.subscribers.activate(1)
```
### Usage Management
```kotlin
import java.time.Duration
import java.time.Instant
// Get usage stats
val start = Instant.now().minus(Duration.ofDays(30))
val end = Instant.now()
val stats = sdk.usage.getStats(1, start, end)
// List usage events
val events = sdk.usage.listEvents(
subscriberId = 1,
usageType = "data",
page = 1,
pageSize = 50
)
// Get real-time usage
val realtime = sdk.usage.getRealTime(1)
```
### Payment Management
```kotlin
// Create payment transaction
val transaction = sdk.payments.createTransaction(
subscriberId = "1",
amount = 25.00,
currency = "USD",
gateway = "stripe",
metadata = mapOf("description" to "Monthly plan")
)
// Get transaction
val transaction = sdk.payments.getTransaction("txn-123")
// List transactions
val transactions = sdk.payments.listTransactions(
subscriberId = 1,
status = "completed",
page = 1,
pageSize = 50
)
```
### Rating Plans
```kotlin
// List rating plans
val plans = sdk.ratingPlans.list()
// Get specific plan
val plan = sdk.ratingPlans.get("plan-123")
```
### System Management
```kotlin
// Get system stats
val stats = sdk.system.getStats()
// Get health status
val health = sdk.system.getHealth()
```
### GraphQL
```kotlin
// Execute GraphQL query
val query = """
query GetSubscribers($first: Int) {
subscribers(first: $first) {
id
firstName
lastName
}
}
"""
val variables = mapOf("first" to 10)
val result = sdk.graphql.execute(query, variables)
```
## Error Handling
```kotlin
try {
val subscriber = sdk.subscribers.get(1)
} catch (e: AuthError.JWTSecretNotConfigured) {
println("JWT secret not configured")
} catch (e: AuthError.InvalidTokenFormat) {
println("Invalid token format")
} catch (e: AuthError.InvalidTokenSignature) {
println("Invalid token signature")
} catch (e: AuthError.TokenExpired) {
println("Token expired")
} catch (e: AuthError.SigningFailed) {
println("Signing failed")
} catch (e: Exception) {
println("Error: ${e.message}")
}
```
## Development
### Building
```bash
./gradlew build
```
### Testing
```bash
./gradlew test
```
## Requirements
- Kotlin 1.9+
- Java 11+
## License
This SDK is licensed under the MIT License.
@@ -0,0 +1,215 @@
package com.telecom
import java.security.MessageDigest
import java.util.Base64
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
/**
* Authentication provider for the Telecom SDK
*/
class AuthProvider(
private val apiKey: String? = null,
private val jwtSecret: String? = null
) {
private var tokenCache: String? = null
private var tokenExpiry: Long? = null
/**
* Get authentication headers for API requests
*/
fun getHeaders(): Map<String, String> {
val headers = mutableMapOf(
"Content-Type" to "application/json",
"User-Agent" to "Telecom-Kotlin-SDK/1.0.0"
)
apiKey?.let { headers["X-API-Key"] = it }
if (tokenCache != null && isTokenValid()) {
headers["Authorization"] = "Bearer $tokenCache"
}
return headers
}
/**
* Generate a JWT token for authentication
*/
fun generateJWTToken(
userId: String,
expiryHours: Int,
additionalClaims: Map<String, Any> = emptyMap()
): String {
val jwtSecret = jwtSecret ?: throw AuthError.JWTSecretNotConfigured
val now = System.currentTimeMillis() / 1000
val exp = now + (expiryHours * 3600)
val claims = mutableMapOf<String, Any>(
"sub" to userId,
"exp" to exp,
"iat" to now
)
claims.putAll(additionalClaims)
val header = mapOf("alg" to "HS256", "typ" to "JWT")
val encodedHeader = base64URLEncode(header)
val encodedPayload = base64URLEncode(claims)
val signature = sign("$encodedHeader.$encodedPayload", jwtSecret)
val token = "$encodedHeader.$encodedPayload.$signature"
tokenCache = token
tokenExpiry = exp
return token
}
/**
* Validate a JWT token
*/
fun validateJWTToken(token: String): Map<String, Any> {
val jwtSecret = jwtSecret ?: throw AuthError.JWTSecretNotConfigured
val parts = token.split(".")
if (parts.size != 3) {
throw AuthError.InvalidTokenFormat
}
val (encodedHeader, encodedPayload, signature) = parts
val expectedSignature = sign("$encodedHeader.$encodedPayload", jwtSecret)
if (signature != expectedSignature) {
throw AuthError.InvalidTokenSignature
}
val payloadBytes = base64URLDecode(encodedPayload) ?: throw AuthError.FailedToDecodePayload
val payload = parseJson(payloadBytes) as? Map<String, Any> ?: throw AuthError.FailedToDecodePayload
val exp = payload["exp"] as? Number
if (exp != null && exp.toLong() < System.currentTimeMillis() / 1000) {
throw AuthError.TokenExpired
}
return payload
}
/**
* Clear the cached JWT token
*/
fun clearTokenCache() {
tokenCache = null
tokenExpiry = null
}
private fun isTokenValid(): Boolean {
if (tokenCache == null || tokenExpiry == null) return false
return System.currentTimeMillis() / 1000 < (tokenExpiry ?: 0)
}
private fun base64URLEncode(data: Any): String {
val json = toJson(data)
return Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(json.toByteArray())
}
private fun base64URLDecode(data: String): ByteArray? {
val base64String = data
.replace("-", "+")
.replace("_", "/")
val paddingNeeded = (4 - base64String.length % 4) % 4
val paddedString = base64String + "=".repeat(paddingNeeded)
return try {
Base64.getUrlDecoder().decode(paddedString)
} catch (e: Exception) {
null
}
}
private fun sign(data: String, secret: String): String {
return try {
val key = SecretKeySpec(secret.toByteArray(), "HmacSHA256")
val mac = Mac.getInstance("HmacSHA256")
mac.init(key)
val signature = mac.doFinal(data.toByteArray())
Base64.getUrlEncoder()
.withoutPadding()
.encodeToString(signature)
} catch (e: Exception) {
throw AuthError.SigningFailed
}
}
private fun toJson(data: Any): String {
return when (data) {
is Map<*, *> -> {
val entries = data.entries.joinToString(",") { (k, v) ->
"\"${k}\":${toJson(v)}"
}
"{$entries}"
}
is List<*> -> {
val items = data.joinToString(",") { toJson(it) }
"[$items]"
}
is String -> "\"$data\""
is Number -> data.toString()
is Boolean -> data.toString()
else -> "\"$data\""
}
}
private fun parseJson(json: String): Any? {
// JSON parser using kotlinx.serialization
// In production, use proper kotlinx.serialization with typed models
// For SDK compatibility without external dependencies, using basic parsing
return try {
when {
json.trim().startsWith("{") -> {
// Parse JSON object
val content = json.trim().removeSurrounding("{", "}")
val pairs = content.split(",")
val map = mutableMapOf<String, Any>()
pairs.forEach { pair ->
val (key, value) = pair.split(":", limit = 2)
val cleanKey = key.trim().removeSurrounding("\"")
val cleanValue = value.trim()
map[cleanKey] = when {
cleanValue.startsWith("\"") -> cleanValue.removeSurrounding("\"")
cleanValue == "true" -> true
cleanValue == "false" -> false
cleanValue.toIntOrNull() != null -> cleanValue.toInt()
cleanValue.toDoubleOrNull() != null -> cleanValue.toDouble()
else -> cleanValue
}
}
map
}
json.trim().startsWith("[") -> {
// Parse JSON array
val content = json.trim().removeSurrounding("[", "]")
if (content.isBlank()) {
emptyList()
} else {
content.split(",").map { it.trim().removeSurrounding("\"") }
}
}
else -> json.trim()
}
} catch (e: Exception) {
null
}
}
}
sealed class AuthError(message: String) : Exception(message) {
object JWTSecretNotConfigured : AuthError("JWT secret not configured")
object InvalidTokenFormat : AuthError("Invalid token format")
object InvalidTokenSignature : AuthError("Invalid token signature")
object FailedToDecodePayload : AuthError("Failed to decode payload")
object TokenExpired : AuthError("Token has expired")
object SigningFailed : AuthError("Signing failed")
}
@@ -0,0 +1,15 @@
package com.telecom
/**
* API for GraphQL queries
*/
class GraphQLAPI(private val client: HTTPClient) {
/**
* Execute a GraphQL query
*/
suspend fun execute(query: String, variables: Map<String, Any>? = null): Map<String, Any> {
val request = GraphQLRequest(query, variables)
return client.post("/graphql", request)
}
}
@@ -0,0 +1,137 @@
package com.telecom
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.serialization.json.Json
/**
* HTTP client for making API requests
*/
class HTTPClient(
private val config: TelecomConfig,
private val authProvider: AuthProvider
) {
private val client: HttpClient = HttpClient {
defaultRequest {
url(config.apiURL)
header("User-Agent", "Telecom-Kotlin-SDK/1.0.0")
}
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
isLenient = true
})
}
install(HttpTimeout) {
requestTimeoutMillis = config.timeout.toMillis()
}
}
/**
* Make an HTTP GET request
*/
suspend fun <T> get(path: String, params: Map<String, String>? = null): T {
return request<T>("GET", path, null, params)
}
/**
* Make an HTTP POST request
*/
suspend fun <T> post(path: String, body: Any? = null): T {
return request<T>("POST", path, body, null)
}
/**
* Make an HTTP PUT request
*/
suspend fun <T> put(path: String, body: Any? = null): T {
return request<T>("PUT", path, body, null)
}
/**
* Make an HTTP DELETE request
*/
suspend fun delete(path: String) {
request<Unit>("DELETE", path, null, null)
}
private suspend fun <T> request(
method: String,
path: String,
body: Any?,
params: Map<String, String>?
): T {
var lastError: Throwable? = null
for (attempt in 0..config.maxRetries) {
try {
val response = client.request(path) {
this.method = HttpMethod.parse(method)
// Set headers
val headers = authProvider.getHeaders()
for ((key, value) in headers) {
header(key, value)
}
// Add query parameters
params?.let {
it.forEach { (k, v) ->
parameter(k, v)
}
}
// Add body
body?.let {
setBody(it)
contentType(ContentType.Application.Json)
}
}
handleResponseErrors(response.status)
return response.body()
} catch (e: Exception) {
lastError = e
if (attempt < config.maxRetries) {
kotlinx.coroutines.delay(config.retryDelay * (1 shl attempt))
}
}
}
throw lastError ?: NetworkError.RequestFailed
}
private fun handleResponseErrors(status: HttpStatusCode) {
when (status) {
HttpStatusCode.Unauthorized -> throw AuthError.AuthenticationFailed
HttpStatusCode.TooManyRequests -> throw AuthError.RateLimitExceeded
in 400..499 -> throw NetworkError.ClientError(status.value)
in 500..599 -> throw NetworkError.ServerError(status.value)
else -> {}
}
}
fun close() {
client.close()
}
}
sealed class NetworkError(message: String) : Exception(message) {
object RequestFailed : NetworkError("Request failed")
class ClientError(code: Int) : NetworkError("Client error: $code")
class ServerError(code: Int) : NetworkError("Server error: $code")
}
// Extend AuthError with additional errors
object AuthError {
object AuthenticationFailed : AuthError("Authentication failed")
object RateLimitExceeded : AuthError("Rate limit exceeded")
}
@@ -0,0 +1,41 @@
package com.telecom
/**
* API for payment management
*/
class PaymentAPI(private val client: HTTPClient) {
/**
* Create a payment transaction
*/
suspend fun createTransaction(request: CreatePaymentRequest): PaymentTransaction {
return client.post("/v1/payments/transactions", request)
}
/**
* Get a payment transaction by ID
*/
suspend fun getTransaction(transactionId: String): PaymentTransaction {
return client.get("/v1/payments/transactions/$transactionId")
}
/**
* List payment transactions
*/
suspend fun listTransactions(
subscriberId: Long? = null,
status: PaymentStatus? = null,
page: Int = 1,
pageSize: Int = 50
): SubscriberList {
val params = mutableMapOf(
"page" to page.toString(),
"page_size" to pageSize.toString()
)
subscriberId?.let { params["subscriber_id"] = it.toString() }
status?.let { params["status"] = it.toString() }
return client.get("/v1/payments/transactions", params)
}
}
@@ -0,0 +1,21 @@
package com.telecom
/**
* API for rating plan management
*/
class RatingPlanAPI(private val client: HTTPClient) {
/**
* List all available rating plans
*/
suspend fun list(): List<RatingPlan> {
return client.get("/v1/rating-plans")
}
/**
* Get a rating plan by ID
*/
suspend fun get(planId: String): RatingPlan {
return client.get("/v1/rating-plans/$planId")
}
}
@@ -0,0 +1,63 @@
package com.telecom
/**
* API for subscriber management
*/
class SubscriberAPI(private val client: HTTPClient) {
/**
* Get subscriber by ID
*/
suspend fun get(id: Long): Subscriber {
return client.get("/v1/subscribers/$id")
}
/**
* List subscribers with pagination
*/
suspend fun list(page: Int = 1, pageSize: Int = 50, status: SubscriberStatus? = null): SubscriberList {
val params = mutableMapOf(
"page" to page.toString(),
"page_size" to pageSize.toString()
)
status?.let { params["status"] = it.toString() }
return client.get("/v1/subscribers", params)
}
/**
* Create a new subscriber
*/
suspend fun create(request: CreateSubscriberRequest): Subscriber {
return client.post("/v1/subscribers", request)
}
/**
* Update an existing subscriber
*/
suspend fun update(id: Long, request: UpdateSubscriberRequest): Subscriber {
return client.put("/v1/subscribers/$id", request)
}
/**
* Delete a subscriber
*/
suspend fun delete(id: Long) {
client.delete("/v1/subscribers/$id")
}
/**
* Suspend a subscriber
*/
suspend fun suspend(id: Long): Subscriber {
return client.post("/v1/subscribers/$id/suspend", null)
}
/**
* Activate a suspended subscriber
*/
suspend fun activate(id: Long): Subscriber {
return client.post("/v1/subscribers/$id/activate", null)
}
}
@@ -0,0 +1,21 @@
package com.telecom
/**
* API for system management
*/
class SystemAPI(private val client: HTTPClient) {
/**
* Get system statistics
*/
suspend fun getStats(): SystemStats {
return client.get("/v1/system/stats")
}
/**
* Get system health status
*/
suspend fun getHealth(): HealthStatus {
return client.get("/v1/health")
}
}
@@ -1,330 +1,83 @@
package com.telecom
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.websocket.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import java.time.*
import java.time.format.DateTimeFormatter
/**
* Telecom Platform Kotlin SDK
*
* Provides coroutine-based access to the Telecom Platform API with full Kotlin serialization support.
*/
class TelecomSDK private constructor(
private val httpClient: HttpClient,
private val config: TelecomConfig,
private val webSocketClient: WebSocketClient
private val authProvider: AuthProvider,
private val httpClient: HTTPClient
) {
// API modules
val subscribers: SubscriberAPI
val usage: UsageAPI
val payments: PaymentAPI
val ratingPlans: RatingPlanAPI
val system: SystemAPI
val graphql: GraphQLAPI
companion object {
/**
* Create a new TelecomSDK instance
*/
fun create(config: TelecomConfig = TelecomConfig()): TelecomSDK {
val httpClient = HttpClient {
defaultRequest {
url(config.apiURL)
header("User-Agent", "Telecom-Kotlin-SDK/1.0.0")
config.apiKey?.let { header("Authorization", "Bearer $it") }
}
install(ContentNegotiation) {
json(Json {
ignoreUnknownKeys = true
isLenient = true
})
}
install(HttpTimeout) {
requestTimeoutMillis = config.timeout.toMillis()
}
install(Retry) {
maxRetries = config.maxRetries
retryOnExceptionIf { request, throwable ->
throwable is HttpRequestTimeoutException ||
throwable is ConnectTimeoutException
}
exponentialDelay()
}
HttpResponseValidator {
handleResponseExceptionWithRequest { exception, request ->
when (exception) {
is ClientRequestException -> {
when (exception.response.status) {
HttpStatusCode.Unauthorized -> throw TelecomException.AuthenticationError("Authentication failed")
HttpStatusCode.TooManyRequests -> throw TelecomException.RateLimitError("Rate limit exceeded")
in HttpStatusCode.BadRequest..HttpStatusCode.UnprocessableEntity -> {
val errorBody = exception.response.body<String>()
throw TelecomException.APIError("API error: $errorBody")
}
in HttpStatusCode.InternalServerError..HttpStatusCode.NetworkAuthenticationRequired -> {
throw TelecomException.ServerError("Server error: ${exception.response.status}")
}
}
}
}
}
}
}
val authProvider = AuthProvider(apiKey = config.apiKey, jwtSecret = config.jwtSecret)
val httpClient = HTTPClient(config = config, authProvider = authProvider)
val webSocketClient = WebSocketClient()
// Initialize API modules
val subscribers = SubscriberAPI(httpClient)
val usage = UsageAPI(httpClient)
val payments = PaymentAPI(httpClient)
val ratingPlans = RatingPlanAPI(httpClient)
val system = SystemAPI(httpClient)
val graphql = GraphQLAPI(httpClient)
return TelecomSDK(httpClient, config, webSocketClient)
return TelecomSDK(config, authProvider, httpClient, subscribers, usage, payments, ratingPlans, system, graphql)
}
}
// Subscriber Management
private constructor(
config: TelecomConfig,
authProvider: AuthProvider,
httpClient: HTTPClient,
subscribers: SubscriberAPI,
usage: UsageAPI,
payments: PaymentAPI,
ratingPlans: RatingPlanAPI,
system: SystemAPI,
graphql: GraphQLAPI
) {
this.config = config
this.authProvider = authProvider
this.httpClient = httpClient
this.subscribers = subscribers
this.usage = usage
this.payments = payments
this.ratingPlans = ratingPlans
this.system = system
this.graphql = graphql
}
// Authentication methods
/**
* Get subscriber by ID
* Generate a JWT token for authentication
*/
suspend fun getSubscriber(id: Long): Subscriber {
return httpClient.get("/v1/subscribers/$id").body()
fun generateJWTToken(userId: String, expiryHours: Int, additionalClaims: Map<String, Any> = emptyMap()): String {
return authProvider.generateJWTToken(userId, expiryHours, additionalClaims)
}
/**
* List subscribers with pagination
* Validate a JWT token
*/
suspend fun listSubscribers(
page: Int = 1,
pageSize: Int = 50,
status: SubscriberStatus? = null
): SubscriberList {
val params = mutableListOf(
"page" to page.toString(),
"page_size" to pageSize.toString()
)
status?.let { params.add("status" to it.value) }
return httpClient.get("/v1/subscribers") {
parameter(params)
}.body()
fun validateJWTToken(token: String): Map<String, Any> {
return authProvider.validateJWTToken(token)
}
/**
* Create a new subscriber
*/
suspend fun createSubscriber(request: CreateSubscriberRequest): Subscriber {
return httpClient.post("/v1/subscribers") {
setBody(request)
}.body()
}
/**
* Update an existing subscriber
*/
suspend fun updateSubscriber(id: Long, request: UpdateSubscriberRequest): Subscriber {
return httpClient.put("/v1/subscribers/$id") {
setBody(request)
}.body()
}
/**
* Suspend a subscriber
*/
suspend fun suspendSubscriber(id: Long): Subscriber {
return httpClient.post("/v1/subscribers/$id/suspend").body()
}
/**
* Activate a suspended subscriber
*/
suspend fun activateSubscriber(id: Long): Subscriber {
return httpClient.post("/v1/subscribers/$id/activate").body()
}
/**
* Terminate a subscriber
*/
suspend fun terminateSubscriber(id: Long): Boolean {
return httpClient.delete("/v1/subscribers/$id").body()
}
// Usage Management
/**
* Get usage statistics for a subscriber
*/
suspend fun getUsageStats(
subscriberId: Long,
startDate: LocalDateTime,
endDate: LocalDateTime
): UsageStats {
return httpClient.get("/v1/subscribers/$subscriberId/usage") {
parameter("start_date", startDate.format(DateTimeFormatter.ISO_DATE_TIME))
parameter("end_date", endDate.format(DateTimeFormatter.ISO_DATE_TIME))
}.body()
}
/**
* List usage events with filtering
*/
suspend fun listUsageEvents(
subscriberId: Long? = null,
usageType: UsageType? = null,
startDate: LocalDateTime? = null,
endDate: LocalDateTime? = null,
page: Int = 1,
pageSize: Int = 50
): UsageEventList {
val params = mutableListOf(
"page" to page.toString(),
"page_size" to pageSize.toString()
)
subscriberId?.let { params.add("subscriber_id" to it.toString()) }
usageType?.let { params.add("usage_type" to it.value) }
startDate?.let { params.add("start_date", it.format(DateTimeFormatter.ISO_DATE_TIME)) }
endDate?.let { params.add("end_date", it.format(DateTimeFormatter.ISO_DATE_TIME)) }
return httpClient.get("/v1/usage/events") {
parameter(params)
}.body()
}
/**
* Get real-time usage for a subscriber
*/
suspend fun getRealTimeUsage(subscriberId: Long): RealTimeUsage {
return httpClient.get("/v1/subscribers/$subscriberId/realtime").body()
}
// Payment Management
/**
* Create a payment transaction
*/
suspend fun createPaymentTransaction(request: CreatePaymentRequest): PaymentTransaction {
return httpClient.post("/v1/payments/transactions") {
setBody(request)
}.body()
}
/**
* Get payment transaction by ID
*/
suspend fun getPaymentTransaction(transactionId: String): PaymentTransaction {
return httpClient.get("/v1/payments/transactions/$transactionId").body()
}
/**
* List payment transactions with filtering
*/
suspend fun listPaymentTransactions(
subscriberId: Long? = null,
status: PaymentStatus? = null,
page: Int = 1,
pageSize: Int = 50
): PaymentTransactionList {
val params = mutableListOf(
"page" to page.toString(),
"page_size" to pageSize.toString()
)
subscriberId?.let { params.add("subscriber_id" to it.toString()) }
status?.let { params.add("status" to it.value) }
return httpClient.get("/v1/payments/transactions") {
parameter(params)
}.body()
}
// Rating Plans
/**
* List all available rating plans
*/
suspend fun listRatingPlans(): List<RatingPlan> {
return httpClient.get("/v1/rating-plans").body()
}
/**
* Get rating plan by ID
*/
suspend fun getRatingPlan(planId: String): RatingPlan {
return httpClient.get("/v1/rating-plans/$planId").body()
}
// System Management
/**
* Get system statistics
*/
suspend fun getSystemStats(): SystemStats {
return httpClient.get("/v1/system/stats").body()
}
/**
* Get system health status
*/
suspend fun getHealthStatus(): HealthStatus {
return httpClient.get("/v1/health").body()
}
// WebSocket Support
/**
* Connect to WebSocket for real-time updates
*/
fun connectWebSocket(
messageHandler: suspend (WebSocketMessage) -> Unit
): Flow<WebSocketMessage> = flow {
val wsUrl = config.apiURL.replace("http://", "ws://") + "/ws"
webSocketClient.webSocket(
urlString = wsUrl,
request = {
config.apiKey?.let { header("Authorization", "Bearer $it") }
}
) { session ->
session.incoming.consumeAsFlow().collect { frame ->
when (frame) {
is Frame.Text -> {
try {
val message = Json.decodeFromString<WebSocketMessage>(frame.readText())
emit(message)
messageHandler(message)
} catch (e: Exception) {
throw TelecomException.WebSocketError("Failed to parse WebSocket message: $e")
}
}
else -> {
// Handle other frame types if needed
}
}
}
}
}.catch { e ->
throw TelecomException.WebSocketError("WebSocket connection failed: $e")
}
// GraphQL Support
/**
* Execute a GraphQL query
*/
suspend fun executeGraphQLQuery(
query: String,
variables: Map<String, Any> = emptyMap()
): GraphQLResponse {
val request = GraphQLRequest(query, variables)
return httpClient.post("/graphql") {
setBody(request)
}.body()
}
// Cleanup
/**
* Close the SDK and cleanup resources
@@ -340,6 +93,8 @@ class TelecomSDK private constructor(
data class TelecomConfig(
val apiURL: String = "http://localhost:8000",
val apiKey: String? = null,
val timeout: Duration = Duration.ofSeconds(30),
val maxRetries: Int = 3
val jwtSecret: String? = null,
val timeout: java.time.Duration = java.time.Duration.ofSeconds(30),
val maxRetries: Int = 3,
val retryDelay: Long = 1 // in seconds
)
@@ -0,0 +1,54 @@
package com.telecom
import java.time.format.DateTimeFormatter
/**
* API for usage management
*/
class UsageAPI(private val client: HTTPClient) {
private val formatter = DateTimeFormatter.ISO_DATE_TIME
/**
* Get usage statistics for a subscriber
*/
suspend fun getStats(subscriberId: Long, startDate: java.time.LocalDateTime, endDate: java.time.LocalDateTime): UsageStats {
val params = mapOf(
"start_date" to formatter.format(startDate),
"end_date" to formatter.format(endDate)
)
return client.get("/v1/subscribers/$subscriberId/usage", params)
}
/**
* List usage events
*/
suspend fun listEvents(
subscriberId: Long? = null,
usageType: UsageType? = null,
startDate: java.time.LocalDateTime? = null,
endDate: java.time.LocalDateTime? = null,
page: Int = 1,
pageSize: Int = 50
): UsageEventList {
val params = mutableMapOf(
"page" to page.toString(),
"page_size" to pageSize.toString()
)
subscriberId?.let { params["subscriber_id"] = it.toString() }
usageType?.let { params["usage_type"] = it.toString() }
startDate?.let { params["start_date"] = formatter.format(it) }
endDate?.let { params["end_date"] = formatter.format(it) }
return client.get("/v1/usage/events", params)
}
/**
* Get real-time usage for a subscriber
*/
suspend fun getRealtime(subscriberId: Long): RealTimeUsage {
return client.get("/v1/subscribers/$subscriberId/realtime")
}
}