diff --git a/sdk/typescript/README.md b/sdk/typescript/README.md index 34c7dec..d6e8dea 100644 --- a/sdk/typescript/README.md +++ b/sdk/typescript/README.md @@ -2,6 +2,22 @@ A comprehensive TypeScript SDK for integrating with the Telecom Platform API. This SDK provides a clean, type-safe interface for managing subscribers, monitoring usage, handling payments, and receiving real-time updates. +## Architecture + +The SDK is organized into modular components: + +- `auth.ts` - Authentication provider handling API keys and JWT tokens +- `api/` - Directory containing API modules: + - `http-client.ts` - HTTP client with retry logic + - `subscribers.ts` - Subscriber management API + - `usage.ts` - Usage tracking API + - `payments.ts` - Payment processing API + - `rating-plans.ts` - Rating plan management API + - `system.ts` - System monitoring API + - `graphql.ts` - GraphQL query execution +- `websocket.ts` - WebSocket client for real-time updates +- `types.ts` - Type definitions and interfaces + ## Installation ```bash @@ -74,22 +90,17 @@ const sdk = TelecomSDK.initialize({ ```typescript // List subscribers -const subscribers = await sdk.subscribersService.listSubscribers({ +const subscribers = await sdk.subscribers.listSubscribers({ page: 1, pageSize: 20, status: 'active', - organizationId: 'org-123', - search: 'John Doe', }); // Get subscriber by ID -const subscriber = await sdk.subscribersService.getSubscriber(123); - -// Get subscriber by IMSI -const subscriber = await sdk.subscribersService.getSubscriberByImsi('123456789012345'); +const subscriber = await sdk.subscribers.getSubscriber(123); // Create new subscriber -const newSubscriber = await sdk.subscribersService.createSubscriber({ +const newSubscriber = await sdk.subscribers.createSubscriber({ msisdn: '+1234567890', firstName: 'John', lastName: 'Doe', @@ -98,70 +109,96 @@ const newSubscriber = await sdk.subscribersService.createSubscriber({ }); // Update subscriber -const updated = await sdk.subscribersService.updateSubscriber(123, { +const updated = await sdk.subscribers.updateSubscriber(123, { firstName: 'Jane', email: 'jane.doe@example.com', }); // Delete subscriber -await sdk.subscribersService.deleteSubscriber(123); +await sdk.subscribers.deleteSubscriber(123); // Suspend subscriber -await sdk.subscribersService.suspendSubscriber(123); +await sdk.subscribers.suspendSubscriber(123); // Activate subscriber -await sdk.subscribersService.activateSubscriber(123); - -// Terminate subscriber -await sdk.subscribersService.terminateSubscriber(123); +await sdk.subscribers.activateSubscriber(123); ``` -### Account Management +### Usage Management ```typescript -// Get subscriber account -const account = await sdk.subscribersService.getSubscriberAccount('123456789012345'); +// Get usage stats +const stats = await sdk.usage.getStats(1, '2024-01-01', '2024-01-31'); -// Top up balance -const updatedAccount = await sdk.subscribersService.topUpBalance('123456789012345', { - amount: 1000, // $10.00 in cents - paymentMethodId: 'pm_123', +// List usage events +const events = await sdk.usage.listEvents({ + subscriberId: 1, + usageType: 'data', + page: 1, + pageSize: 50, }); -// Get usage statistics -const stats = await sdk.subscribersService.getUsageStats('123456789012345', 'monthly'); - // Get real-time usage -const realtime = await sdk.subscribersService.getRealTimeUsage('123456789012345'); +const realtime = await sdk.usage.getRealTimeUsage(1); ``` -### eSIM Management +### Payment Management ```typescript -// Provision eSIM profile -const esim = await sdk.subscribersService.provisionESIM('123456789012345'); +// Create payment transaction +const transaction = await sdk.payments.createTransaction({ + subscriberId: 1, + amount: 25.00, + currency: 'USD', + gateway: 'stripe', + metadata: { description: 'Monthly plan' } +}); -// Activate eSIM profile -await sdk.subscribersService.activateESIM('123456789012345', esim.profileId); +// Get transaction +const transaction = await sdk.payments.getTransaction('txn-123'); -// Deactivate eSIM profile -await sdk.subscribersService.deactivateESIM('123456789012345', esim.profileId); - -// Get eSIM status -const status = await sdk.subscribersService.getESIMStatus('123456789012345'); +// List transactions +const transactions = await sdk.payments.listTransactions({ + subscriberId: 1, + status: 'completed', + page: 1, + pageSize: 50, +}); ``` -### Billing & Invoices +### Rating Plans ```typescript -// Get subscriber invoices -const invoices = await sdk.subscribersService.getInvoices('123456789012345'); +// List rating plans +const plans = await sdk.ratingPlans.list(); -// Get specific invoice -const invoice = await sdk.subscribersService.getInvoice('inv_123'); +// Get specific plan +const plan = await sdk.ratingPlans.get('plan-123'); +``` -// Download invoice PDF -const pdfBlob = await sdk.subscribersService.downloadInvoicePDF('inv_123'); +### System Management + +```typescript +// Get system stats +const stats = await sdk.system.getStats(); + +// Get health status +const health = await sdk.system.getHealth(); +``` + +### GraphQL + +```typescript +// Execute GraphQL query +const result = await sdk.graphql.execute(` + query GetSubscribers($first: Int) { + subscribers(first: $first) { + id + firstName + lastName + } + } +`, { first: 10 }); ``` ### Real-time Updates @@ -208,17 +245,13 @@ sdk.disconnectWebSocket(); ```typescript // Get system statistics -const stats = await sdk.getSystemStats(); +const stats = await sdk.system.getStats(); console.log('Active sessions:', stats.activeSessions); console.log('Total accounts:', stats.totalAccounts); // Get health status -const health = await sdk.getHealthStatus(); +const health = await sdk.system.getHealth(); console.log('Redis connected:', health.redisConnected); - -// Test connection -const test = await sdk.testConnection(); -console.log('API status:', test.status); ``` ## Error Handling @@ -227,7 +260,7 @@ The SDK provides comprehensive error handling with detailed error information: ```typescript try { - const subscriber = await sdk.subscribersService.getSubscriber(123); + const subscriber = await sdk.subscribers.getSubscriber(123); } catch (error) { console.error('Error:', error.code); console.error('Message:', error.message); @@ -263,7 +296,7 @@ The SDK is fully typed with TypeScript. All methods have proper type definitions import { Subscriber, SubscriberStatus, UsageType } from '@telecom-platform/sdk'; // Types are automatically inferred -const subscriber: Subscriber = await sdk.subscribersService.getSubscriber(123); +const subscriber: Subscriber = await sdk.subscribers.getSubscriber(123); const status: SubscriberStatus = subscriber.status; ``` diff --git a/sdk/typescript/src/auth.ts b/sdk/typescript/src/auth.ts index f3bbcdc..3b90d03 100644 --- a/sdk/typescript/src/auth.ts +++ b/sdk/typescript/src/auth.ts @@ -112,11 +112,14 @@ export class AuthProvider { return this.tokenExpiry > Math.floor(Date.now() / 1000); } - private base64UrlEncode(str: string): string { - return btoa(str) - .replace(/\+/g, '-') - .replace(/\//g, '_') - .replace(/=+$/, ''); + private base64UrlEncode(data: string | Uint8Array): string { + let binary: string; + if (typeof data === 'string') { + binary = btoa(data); + } else { + binary = btoa(String.fromCharCode(...data)); + } + return binary.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); } private base64UrlDecode(str: string): string { @@ -128,22 +131,132 @@ export class AuthProvider { } private sign(data: string, secret: string): string { - // Simple HMAC-SHA256 implementation - // In production, use a proper crypto library like crypto-js or Web Crypto API - const hmac = this.simpleHMACSHA256(data, secret); - return this.base64UrlEncode(hmac); + // HMAC-SHA256 implementation + // In production, use crypto-js: import CryptoJS from 'crypto-js'; + // const hash = CryptoJS.HmacSHA256(data, secret).toString(CryptoJS.enc.Base64); + // For SDK compatibility without external dependencies, using reference implementation + return this.syncHMACSHA256(data, secret); } - private simpleHMACSHA256(message: string, key: string): string { - // This is a simplified implementation for demonstration - // In production, use a proper crypto library - let hash = 0; - const str = message + key; - for (let i = 0; i < str.length; i++) { - const char = str.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash = hash & hash; + private syncHMACSHA256(message: string, key: string): string { + // HMAC-SHA256 implementation for environments without crypto support + // This is a reference implementation - in production use crypto-js or similar + const encoder = new TextEncoder(); + const keyBytes = encoder.encode(key); + const messageBytes = encoder.encode(message); + + // Pad key to 64 bytes (block size for SHA-256) + const block = 64; + const paddedKey = new Uint8Array(block); + paddedKey.set(keyBytes.slice(0, block)); + + // XOR with inner pad (0x36) and outer pad (0x5c) + const innerPad = new Uint8Array(block); + const outerPad = new Uint8Array(block); + for (let i = 0; i < block; i++) { + innerPad[i] = paddedKey[i] ^ 0x36; + outerPad[i] = paddedKey[i] ^ 0x5c; } - return Math.abs(hash).toString(16).padStart(64, '0'); + + // Inner hash: H((K ^ ipad) || message) + const innerCombined = new Uint8Array(block + messageBytes.length); + innerCombined.set(innerPad); + innerCombined.set(messageBytes, block); + const innerHash = this.sha256(innerCombined); + + // Outer hash: H((K ^ opad) || inner_hash) + const outerCombined = new Uint8Array(block + innerHash.length); + outerCombined.set(outerPad); + outerCombined.set(innerHash, block); + const result = this.sha256(outerCombined); + + return this.base64UrlEncode(result); } + + private sha256(data: Uint8Array): Uint8Array { + // SHA-256 implementation + // In production, use a proper crypto library + // This is a reference implementation + const h = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19]; + + // Pad data + const len = data.length * 8; + const k = Math.ceil((len + 65) / 512) * 512; + const padded = new Uint8Array(k / 8); + padded.set(data); + padded[data.length] = 0x80; + + const view = new DataView(padded.buffer); + view.setUint32(padded.length - 4, Math.floor(len / 0x100000000), true); + view.setUint32(padded.length - 8, len >>> 0, true); + + // Process blocks (simplified - in production use proper implementation) + for (let i = 0; i < padded.length; i += 64) { + const w = new Uint32Array(64); + for (let j = 0; j < 16; j++) { + w[j] = view.getUint32(i + j * 4, true); + } + + let [a, b, c, d, e, f, g, hVal] = h; + + for (let j = 0; j < 64; j++) { + if (j >= 16) { + w[j] = this.sigma1(w[j - 2]) + w[j - 7] + this.sigma0(w[j - 15]) + w[j - 16]; + } + const t1 = hVal + this.ep1(e) + this.ch(e, f, g) + this.k[j] + w[j]; + const t2 = this.ep0(a) + this.maj(a, b, c); + [hVal, g, f, e, d, c, b, a] = [g, f, e, d + t1, c, b, a, t1 + t2]; + } + + h[0] += a; h[1] += b; h[2] += c; h[3] += d; + h[4] += e; h[5] += f; h[6] += g; h[7] += hVal; + } + + const result = new Uint8Array(32); + const resultView = new DataView(result.buffer); + for (let i = 0; i < 8; i++) { + resultView.setUint32(i * 4, h[i], true); + } + return result; + } + + private rotr(x: number, n: number): number { + return (x >>> n) | (x << (32 - n)); + } + + private ep0(x: number): number { + return this.rotr(x, 2) ^ this.rotr(x, 13) ^ this.rotr(x, 22); + } + + private ep1(x: number): number { + return this.rotr(x, 6) ^ this.rotr(x, 11) ^ this.rotr(x, 25); + } + + private sigma0(x: number): number { + return this.rotr(x, 7) ^ this.rotr(x, 18) ^ (x >>> 3); + } + + private sigma1(x: number): number { + return this.rotr(x, 17) ^ this.rotr(x, 19) ^ (x >>> 10); + } + + private ch(x: number, y: number, z: number): number { + return (x & y) ^ (~x & z); + } + + private maj(x: number, y: number, z: number): number { + return (x & y) ^ (x & z) ^ (y & z); + } + + private k = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ]; }