mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 02:52:53 +00:00
docs: Add Go SDK README with comprehensive API documentation and implement modular architecture with authentication provider
- 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, PaymentsAPI, RatingPlansAPI
This commit is contained in:
+59
-13
@@ -1,9 +1,10 @@
|
||||
# Telecom Platform Go SDK
|
||||
|
||||
Go SDK for the Telecom Platform with both HTTP and gRPC support.
|
||||
Go SDK for the Telecom Platform with modular architecture for HTTP and gRPC support.
|
||||
|
||||
## Features
|
||||
|
||||
- **Modular Architecture**: Separate modules for authentication, HTTP client, and API services
|
||||
- **Dual Protocol Support**: Both HTTP REST and gRPC APIs
|
||||
- **Type Safety**: Strong typing with Go structs
|
||||
- **Context Support**: Full context.Context support
|
||||
@@ -11,6 +12,20 @@ Go SDK for the Telecom Platform with both HTTP and gRPC support.
|
||||
- **Retry Logic**: Built-in retry mechanism
|
||||
- **Async Operations**: Non-blocking operations
|
||||
|
||||
## Architecture
|
||||
|
||||
The SDK is organized into modular components:
|
||||
|
||||
- `auth.go` - Authentication provider handling API keys and JWT tokens
|
||||
- `client.go` - HTTP client with retry logic and authentication
|
||||
- `subscribers_api.go` - Subscriber management API
|
||||
- `usage_api.go` - Usage tracking API
|
||||
- `payments_api.go` - Payment processing API
|
||||
- `rating_plans_api.go` - Rating plan management API
|
||||
- `system_api.go` - System monitoring API
|
||||
- `graphql_api.go` - GraphQL query execution
|
||||
- `telecom.go` - Main client integrating all modules
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
@@ -45,7 +60,7 @@ func main() {
|
||||
ctx := context.Background()
|
||||
|
||||
// Get subscriber
|
||||
subscriber, err := client.GetSubscriber(ctx, 1)
|
||||
subscriber, err := client.Subscribers.Get(ctx, 1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -53,7 +68,7 @@ func main() {
|
||||
fmt.Printf("Subscriber: %s %s\n", subscriber.FirstName, subscriber.LastName)
|
||||
|
||||
// List subscribers
|
||||
list, err := client.ListSubscribers(ctx, 1, 50)
|
||||
list, err := client.Subscribers.List(ctx, 1, 50)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -119,10 +134,10 @@ config := &telecom.Config{
|
||||
|
||||
```go
|
||||
// Get subscriber
|
||||
subscriber, err := client.GetSubscriber(ctx, 1)
|
||||
subscriber, err := client.Subscribers.Get(ctx, 1)
|
||||
|
||||
// List subscribers
|
||||
list, err := client.ListSubscribers(ctx, 1, 50)
|
||||
list, err := client.Subscribers.List(ctx, 1, 50, "")
|
||||
|
||||
// Create subscriber
|
||||
req := &telecom.CreateSubscriberRequest{
|
||||
@@ -133,51 +148,82 @@ req := &telecom.CreateSubscriberRequest{
|
||||
Email: "[email protected]",
|
||||
PlanID: 1,
|
||||
}
|
||||
subscriber, err := client.CreateSubscriber(ctx, req)
|
||||
subscriber, err := client.Subscribers.Create(ctx, req)
|
||||
|
||||
// Update subscriber
|
||||
updateReq := &telecom.UpdateSubscriberRequest{
|
||||
FirstName: stringPtr("Jane"),
|
||||
}
|
||||
subscriber, err := client.UpdateSubscriber(ctx, 1, updateReq)
|
||||
subscriber, err := client.Subscribers.Update(ctx, 1, updateReq)
|
||||
|
||||
// Delete subscriber
|
||||
err := client.DeleteSubscriber(ctx, 1)
|
||||
err := client.Subscribers.Delete(ctx, 1)
|
||||
|
||||
// Suspend subscriber
|
||||
subscriber, err := client.Subscribers.Suspend(ctx, 1)
|
||||
|
||||
// Activate subscriber
|
||||
subscriber, err := client.Subscribers.Activate(ctx, 1)
|
||||
```
|
||||
|
||||
### Usage Management
|
||||
|
||||
```go
|
||||
// Get usage stats
|
||||
stats, err := client.GetUsageStats(ctx, 1, time.Now().AddDate(0, -1, 0), time.Now())
|
||||
stats, err := client.Usage.GetStats(ctx, 1, time.Now().AddDate(0, -1, 0), time.Now())
|
||||
fmt.Printf("Data usage: %d bytes\n", stats.DataUp + stats.DataDown)
|
||||
|
||||
// List usage events
|
||||
events, err := client.Usage.ListEvents(ctx, 1, "", 1, 50)
|
||||
|
||||
// Get real-time usage
|
||||
realtime, err := client.Usage.GetRealTime(ctx, 1)
|
||||
```
|
||||
|
||||
### Payment Management
|
||||
|
||||
```go
|
||||
// Create payment
|
||||
// Create payment transaction
|
||||
req := &telecom.CreatePaymentRequest{
|
||||
SubscriberID: "1",
|
||||
Amount: 25.00,
|
||||
Currency: "USD",
|
||||
Gateway: "stripe",
|
||||
}
|
||||
transaction, err := client.CreatePaymentTransaction(ctx, req)
|
||||
transaction, err := client.Payments.CreateTransaction(ctx, req)
|
||||
|
||||
// Get transaction
|
||||
transaction, err := client.Payments.GetTransaction(ctx, "txn-123")
|
||||
|
||||
// List transactions
|
||||
transactions, err := client.Payments.ListTransactions(ctx, 1, "", 1, 50)
|
||||
```
|
||||
|
||||
### Rating Plans
|
||||
|
||||
```go
|
||||
// List rating plans
|
||||
plans, err := client.RatingPlans.List(ctx)
|
||||
|
||||
// Get specific plan
|
||||
plan, err := client.RatingPlans.Get(ctx, "plan-123")
|
||||
```
|
||||
|
||||
### System Management
|
||||
|
||||
```go
|
||||
// Get system stats
|
||||
stats, err := client.GetSystemStats(ctx)
|
||||
stats, err := client.System.GetStats(ctx)
|
||||
fmt.Printf("Active sessions: %d\n", stats.ActiveSessions)
|
||||
|
||||
// Get health status
|
||||
health, err := client.System.GetHealth(ctx)
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```go
|
||||
subscriber, err := client.GetSubscriber(ctx, 1)
|
||||
subscriber, err := client.Subscribers.Get(ctx, 1)
|
||||
if err != nil {
|
||||
switch {
|
||||
case strings.Contains(err.Error(), "HTTP error: 401"):
|
||||
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
package telecom
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AuthProvider handles authentication for the Telecom SDK
|
||||
type AuthProvider struct {
|
||||
apiKey string
|
||||
jwtSecret string
|
||||
tokenCache string
|
||||
tokenExpiry time.Time
|
||||
}
|
||||
|
||||
// NewAuthProvider creates a new authentication provider
|
||||
func NewAuthProvider(apiKey, jwtSecret string) *AuthProvider {
|
||||
return &AuthProvider{
|
||||
apiKey: apiKey,
|
||||
jwtSecret: jwtSecret,
|
||||
}
|
||||
}
|
||||
|
||||
// GetHeaders returns authentication headers for API requests
|
||||
func (a *AuthProvider) GetHeaders() map[string]string {
|
||||
headers := map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "Telecom-Go-SDK/1.0.0",
|
||||
}
|
||||
|
||||
if a.apiKey != "" {
|
||||
headers["X-API-Key"] = a.apiKey
|
||||
}
|
||||
|
||||
if a.tokenCache != "" && a.isTokenValid() {
|
||||
headers["Authorization"] = "Bearer " + a.tokenCache
|
||||
}
|
||||
|
||||
return headers
|
||||
}
|
||||
|
||||
// GenerateJWTToken generates a JWT token for authentication
|
||||
func (a *AuthProvider) GenerateJWTToken(userID string, expiryHours int, additionalClaims map[string]interface{}) (string, error) {
|
||||
if a.jwtSecret == "" {
|
||||
return "", fmt.Errorf("JWT secret not configured")
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
claims := map[string]interface{}{
|
||||
"sub": userID,
|
||||
"exp": now + int64(expiryHours*3600),
|
||||
"iat": now,
|
||||
}
|
||||
|
||||
for k, v := range additionalClaims {
|
||||
claims[k] = v
|
||||
}
|
||||
|
||||
header := map[string]interface{}{
|
||||
"alg": "HS256",
|
||||
"typ": "JWT",
|
||||
}
|
||||
|
||||
encodedHeader := a.base64URLEncode(header)
|
||||
encodedPayload := a.base64URLEncode(claims)
|
||||
signature := a.sign(encodedHeader + "." + encodedPayload)
|
||||
|
||||
token := encodedHeader + "." + encodedPayload + "." + signature
|
||||
a.tokenCache = token
|
||||
a.tokenExpiry = time.Unix(claims["exp"].(int64), 0)
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
// ValidateJWTToken validates a JWT token
|
||||
func (a *AuthProvider) ValidateJWTToken(token string) (map[string]interface{}, error) {
|
||||
if a.jwtSecret == "" {
|
||||
return nil, fmt.Errorf("JWT secret not configured")
|
||||
}
|
||||
|
||||
parts := strings.Split(token, ".")
|
||||
if len(parts) != 3 {
|
||||
return nil, fmt.Errorf("invalid token format")
|
||||
}
|
||||
|
||||
encodedHeader, encodedPayload, signature := parts[0], parts[1], parts[2]
|
||||
expectedSignature := a.sign(encodedHeader + "." + encodedPayload)
|
||||
|
||||
if signature != expectedSignature {
|
||||
return nil, fmt.Errorf("invalid token signature")
|
||||
}
|
||||
|
||||
payloadBytes, err := a.base64URLDecode(encodedPayload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode payload: %w", err)
|
||||
}
|
||||
|
||||
var payload map[string]interface{}
|
||||
if err := json.Unmarshal(payloadBytes, &payload); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal payload: %w", err)
|
||||
}
|
||||
|
||||
if exp, ok := payload["exp"].(float64); ok {
|
||||
if float64(time.Now().Unix()) > exp {
|
||||
return nil, fmt.Errorf("token has expired")
|
||||
}
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// ClearTokenCache clears the cached JWT token
|
||||
func (a *AuthProvider) ClearTokenCache() {
|
||||
a.tokenCache = ""
|
||||
a.tokenExpiry = time.Time{}
|
||||
}
|
||||
|
||||
// isTokenValid checks if the cached token is still valid
|
||||
func (a *AuthProvider) isTokenValid() bool {
|
||||
if a.tokenCache == "" || a.tokenExpiry.IsZero() {
|
||||
return false
|
||||
}
|
||||
return time.Now().Before(a.tokenExpiry)
|
||||
}
|
||||
|
||||
// base64URLEncode encodes data to base64 URL-safe format
|
||||
func (a *AuthProvider) base64URLEncode(data interface{}) string {
|
||||
jsonBytes, _ := json.Marshal(data)
|
||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(jsonBytes), "=")
|
||||
}
|
||||
|
||||
// base64URLDecode decodes base64 URL-safe format
|
||||
func (a *AuthProvider) base64URLDecode(data string) ([]byte, error) {
|
||||
// Add padding if needed
|
||||
for len(data)%4 != 0 {
|
||||
data += "="
|
||||
}
|
||||
return base64.URLEncoding.DecodeString(data)
|
||||
}
|
||||
|
||||
// sign creates a HMAC-SHA256 signature
|
||||
func (a *AuthProvider) sign(data string) string {
|
||||
h := hmac.New(sha256.New, []byte(a.jwtSecret))
|
||||
h.Write([]byte(data))
|
||||
return a.base64URLEncode(h.Sum(nil))
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package telecom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HTTPClient handles HTTP requests with authentication
|
||||
type HTTPClient struct {
|
||||
client *http.Client
|
||||
authProvider *AuthProvider
|
||||
baseURL string
|
||||
timeout time.Duration
|
||||
maxRetries int
|
||||
retryDelay time.Duration
|
||||
enableLogging bool
|
||||
}
|
||||
|
||||
// NewHTTPClient creates a new HTTP client
|
||||
func NewHTTPClient(baseURL string, authProvider *AuthProvider, timeout time.Duration, maxRetries int, retryDelay time.Duration, enableLogging bool) *HTTPClient {
|
||||
return &HTTPClient{
|
||||
client: &http.Client{
|
||||
Timeout: timeout,
|
||||
},
|
||||
authProvider: authProvider,
|
||||
baseURL: baseURL,
|
||||
timeout: timeout,
|
||||
maxRetries: maxRetries,
|
||||
retryDelay: retryDelay,
|
||||
enableLogging: enableLogging,
|
||||
}
|
||||
}
|
||||
|
||||
// Get makes an HTTP GET request
|
||||
func (h *HTTPClient) Get(ctx context.Context, path string, result interface{}, params ...map[string]string) error {
|
||||
return h.request(ctx, "GET", path, nil, result, params...)
|
||||
}
|
||||
|
||||
// Post makes an HTTP POST request
|
||||
func (h *HTTPClient) Post(ctx context.Context, path string, body, result interface{}) error {
|
||||
return h.request(ctx, "POST", path, body, result)
|
||||
}
|
||||
|
||||
// Put makes an HTTP PUT request
|
||||
func (h *HTTPClient) Put(ctx context.Context, path string, body, result interface{}) error {
|
||||
return h.request(ctx, "PUT", path, body, result)
|
||||
}
|
||||
|
||||
// Delete makes an HTTP DELETE request
|
||||
func (h *HTTPClient) Delete(ctx context.Context, path string) error {
|
||||
return h.request(ctx, "DELETE", path, nil, nil)
|
||||
}
|
||||
|
||||
// request makes an HTTP request with retry logic
|
||||
func (h *HTTPClient) request(ctx context.Context, method, path string, body, result interface{}, params ...map[string]string) error {
|
||||
var lastErr error
|
||||
|
||||
for attempt := 0; attempt <= h.maxRetries; attempt++ {
|
||||
var bodyBytes []byte
|
||||
var err error
|
||||
|
||||
if body != nil {
|
||||
bodyBytes, err = json.Marshal(body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal body: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, h.baseURL+path, bytes.NewReader(bodyBytes))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
// Set headers
|
||||
headers := h.authProvider.GetHeaders()
|
||||
for k, v := range headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
// Add query parameters
|
||||
if len(params) > 0 {
|
||||
q := req.URL.Query()
|
||||
for k, v := range params[0] {
|
||||
q.Add(k, v)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
resp, err := h.client.Do(req)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
if attempt == h.maxRetries {
|
||||
return fmt.Errorf("request failed after %d retries: %w", h.maxRetries, err)
|
||||
}
|
||||
time.Sleep(h.retryDelay * time.Duration(1<<uint(attempt)))
|
||||
continue
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusUnauthorized {
|
||||
return fmt.Errorf("authentication failed")
|
||||
}
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
return fmt.Errorf("rate limit exceeded")
|
||||
}
|
||||
if resp.StatusCode >= 400 && resp.StatusCode < 500 {
|
||||
var errorResp map[string]interface{}
|
||||
json.NewDecoder(resp.Body).Decode(&errorResp)
|
||||
return fmt.Errorf("API error: %v", errorResp)
|
||||
}
|
||||
if resp.StatusCode >= 500 {
|
||||
return fmt.Errorf("server error: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
if err := json.NewDecoder(resp.Body).Decode(result); err != nil {
|
||||
return fmt.Errorf("failed to decode response: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return lastErr
|
||||
}
|
||||
|
||||
// Close closes the HTTP client
|
||||
func (h *HTTPClient) Close() error {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package telecom
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// GraphQLAPI handles GraphQL API calls
|
||||
type GraphQLAPI struct {
|
||||
client *HTTPClient
|
||||
}
|
||||
|
||||
// NewGraphQLAPI creates a new GraphQLAPI
|
||||
func NewGraphQLAPI(client *HTTPClient) *GraphQLAPI {
|
||||
return &GraphQLAPI{client: client}
|
||||
}
|
||||
|
||||
// Execute executes a GraphQL query
|
||||
func (g *GraphQLAPI) Execute(ctx context.Context, query string, variables map[string]interface{}) (map[string]interface{}, error) {
|
||||
data := map[string]interface{}{
|
||||
"query": query,
|
||||
}
|
||||
if variables != nil {
|
||||
data["variables"] = variables
|
||||
}
|
||||
|
||||
var result map[string]interface{}
|
||||
err := g.client.Post(ctx, "/graphql", data, &result)
|
||||
return result, err
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package telecom
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// PaymentAPI handles payment-related API calls
|
||||
type PaymentAPI struct {
|
||||
client *HTTPClient
|
||||
}
|
||||
|
||||
// NewPaymentAPI creates a new PaymentAPI
|
||||
func NewPaymentAPI(client *HTTPClient) *PaymentAPI {
|
||||
return &PaymentAPI{client: client}
|
||||
}
|
||||
|
||||
// CreateTransaction creates a new payment transaction
|
||||
func (p *PaymentAPI) CreateTransaction(ctx context.Context, req *CreatePaymentRequest) (*PaymentTransaction, error) {
|
||||
var transaction PaymentTransaction
|
||||
err := p.client.Post(ctx, "/v1/payments/transactions", req, &transaction)
|
||||
return &transaction, err
|
||||
}
|
||||
|
||||
// GetTransaction retrieves a payment transaction by ID
|
||||
func (p *PaymentAPI) GetTransaction(ctx context.Context, transactionID string) (*PaymentTransaction, error) {
|
||||
var transaction PaymentTransaction
|
||||
err := p.client.Get(ctx, fmt.Sprintf("/v1/payments/transactions/%s", transactionID), &transaction)
|
||||
return &transaction, err
|
||||
}
|
||||
|
||||
// ListTransactions retrieves a list of payment transactions
|
||||
func (p *PaymentAPI) ListTransactions(ctx context.Context, subscriberID int64, status string, page, pageSize int32) (*SubscriberList, error) {
|
||||
params := map[string]string{
|
||||
"page": fmt.Sprintf("%d", page),
|
||||
"page_size": fmt.Sprintf("%d", pageSize),
|
||||
}
|
||||
|
||||
if subscriberID > 0 {
|
||||
params["subscriber_id"] = fmt.Sprintf("%d", subscriberID)
|
||||
}
|
||||
if status != "" {
|
||||
params["status"] = status
|
||||
}
|
||||
|
||||
var list SubscriberList
|
||||
err := p.client.Get(ctx, "/v1/payments/transactions", &list, params)
|
||||
return &list, err
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package telecom
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// RatingPlanAPI handles rating plan-related API calls
|
||||
type RatingPlanAPI struct {
|
||||
client *HTTPClient
|
||||
}
|
||||
|
||||
// NewRatingPlanAPI creates a new RatingPlanAPI
|
||||
func NewRatingPlanAPI(client *HTTPClient) *RatingPlanAPI {
|
||||
return &RatingPlanAPI{client: client}
|
||||
}
|
||||
|
||||
// List retrieves all available rating plans
|
||||
func (r *RatingPlanAPI) List(ctx context.Context) ([]RatingPlan, error) {
|
||||
var plans []RatingPlan
|
||||
err := r.client.Get(ctx, "/v1/rating-plans", &plans)
|
||||
return plans, err
|
||||
}
|
||||
|
||||
// Get retrieves a rating plan by ID
|
||||
func (r *RatingPlanAPI) Get(ctx context.Context, planID string) (*RatingPlan, error) {
|
||||
var plan RatingPlan
|
||||
err := r.client.Get(ctx, fmt.Sprintf("/v1/rating-plans/%s", planID), &plan)
|
||||
return &plan, err
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package telecom
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// SubscriberAPI handles subscriber-related API calls
|
||||
type SubscriberAPI struct {
|
||||
client *HTTPClient
|
||||
}
|
||||
|
||||
// NewSubscriberAPI creates a new SubscriberAPI
|
||||
func NewSubscriberAPI(client *HTTPClient) *SubscriberAPI {
|
||||
return &SubscriberAPI{client: client}
|
||||
}
|
||||
|
||||
// Get retrieves a subscriber by ID
|
||||
func (s *SubscriberAPI) Get(ctx context.Context, id int64) (*Subscriber, error) {
|
||||
var subscriber Subscriber
|
||||
err := s.client.Get(ctx, fmt.Sprintf("/v1/subscribers/%d", id), &subscriber)
|
||||
return &subscriber, err
|
||||
}
|
||||
|
||||
// List retrieves a list of subscribers
|
||||
func (s *SubscriberAPI) List(ctx context.Context, page, pageSize int32, status string) (*SubscriberList, error) {
|
||||
params := map[string]string{
|
||||
"page": fmt.Sprintf("%d", page),
|
||||
"page_size": fmt.Sprintf("%d", pageSize),
|
||||
}
|
||||
if status != "" {
|
||||
params["status"] = status
|
||||
}
|
||||
|
||||
var list SubscriberList
|
||||
err := s.client.Get(ctx, "/v1/subscribers", &list, params)
|
||||
return &list, err
|
||||
}
|
||||
|
||||
// Create creates a new subscriber
|
||||
func (s *SubscriberAPI) Create(ctx context.Context, req *CreateSubscriberRequest) (*Subscriber, error) {
|
||||
var subscriber Subscriber
|
||||
err := s.client.Post(ctx, "/v1/subscribers", req, &subscriber)
|
||||
return &subscriber, err
|
||||
}
|
||||
|
||||
// Update updates an existing subscriber
|
||||
func (s *SubscriberAPI) Update(ctx context.Context, id int64, req *UpdateSubscriberRequest) (*Subscriber, error) {
|
||||
var subscriber Subscriber
|
||||
err := s.client.Put(ctx, fmt.Sprintf("/v1/subscribers/%d", id), req, &subscriber)
|
||||
return &subscriber, err
|
||||
}
|
||||
|
||||
// Delete deletes a subscriber
|
||||
func (s *SubscriberAPI) Delete(ctx context.Context, id int64) error {
|
||||
return s.client.Delete(ctx, fmt.Sprintf("/v1/subscribers/%d", id))
|
||||
}
|
||||
|
||||
// Suspend suspends a subscriber
|
||||
func (s *SubscriberAPI) Suspend(ctx context.Context, id int64) (*Subscriber, error) {
|
||||
var subscriber Subscriber
|
||||
err := s.client.Post(ctx, fmt.Sprintf("/v1/subscribers/%d/suspend", id), nil, &subscriber)
|
||||
return &subscriber, err
|
||||
}
|
||||
|
||||
// Activate activates a suspended subscriber
|
||||
func (s *SubscriberAPI) Activate(ctx context.Context, id int64) (*Subscriber, error) {
|
||||
var subscriber Subscriber
|
||||
err := s.client.Post(ctx, fmt.Sprintf("/v1/subscribers/%d/activate", id), nil, &subscriber)
|
||||
return &subscriber, err
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package telecom
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// SystemAPI handles system-related API calls
|
||||
type SystemAPI struct {
|
||||
client *HTTPClient
|
||||
}
|
||||
|
||||
// NewSystemAPI creates a new SystemAPI
|
||||
func NewSystemAPI(client *HTTPClient) *SystemAPI {
|
||||
return &SystemAPI{client: client}
|
||||
}
|
||||
|
||||
// GetStats retrieves system statistics
|
||||
func (s *SystemAPI) GetStats(ctx context.Context) (*SystemStats, error) {
|
||||
var stats SystemStats
|
||||
err := s.client.Get(ctx, "/v1/system/stats", &stats)
|
||||
return &stats, err
|
||||
}
|
||||
|
||||
// GetHealth retrieves system health status
|
||||
func (s *SystemAPI) GetHealth(ctx context.Context) (*HealthStatus, error) {
|
||||
var health HealthStatus
|
||||
err := s.client.Get(ctx, "/v1/health", &health)
|
||||
return &health, err
|
||||
}
|
||||
+59
-231
@@ -1,44 +1,45 @@
|
||||
package telecom
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Client represents the Telecom Platform SDK client
|
||||
type Client struct {
|
||||
config *Config
|
||||
httpClient *HTTPClient
|
||||
config *Config
|
||||
authProvider *AuthProvider
|
||||
httpClient *HTTPClient
|
||||
|
||||
// API modules
|
||||
Subscribers *SubscriberAPI
|
||||
Usage *UsageAPI
|
||||
Payments *PaymentAPI
|
||||
RatingPlans *RatingPlanAPI
|
||||
System *SystemAPI
|
||||
GraphQL *GraphQLAPI
|
||||
}
|
||||
|
||||
// Config holds the SDK configuration
|
||||
type Config struct {
|
||||
APIURL string
|
||||
APIKey string
|
||||
Timeout time.Duration
|
||||
Retries int
|
||||
EnableHTTP bool
|
||||
}
|
||||
|
||||
// HTTPClient handles HTTP requests
|
||||
type HTTPClient struct {
|
||||
client *http.Client
|
||||
apiKey string
|
||||
baseURL string
|
||||
APIURL string
|
||||
APIKey string
|
||||
JWTSecret string
|
||||
Timeout time.Duration
|
||||
MaxRetries int
|
||||
RetryDelay time.Duration
|
||||
EnableHTTP bool
|
||||
EnableLogging bool
|
||||
}
|
||||
|
||||
// DefaultConfig returns a default configuration
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
APIURL: "http://localhost:8000",
|
||||
APIKey: "",
|
||||
Timeout: 30 * time.Second,
|
||||
Retries: 3,
|
||||
EnableHTTP: true,
|
||||
APIURL: "http://localhost:8000",
|
||||
Timeout: 30 * time.Second,
|
||||
MaxRetries: 3,
|
||||
RetryDelay: 1 * time.Second,
|
||||
EnableHTTP: true,
|
||||
EnableLogging: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,226 +49,53 @@ func NewClient(config *Config) (*Client, error) {
|
||||
config = DefaultConfig()
|
||||
}
|
||||
|
||||
client := &Client{
|
||||
config: config,
|
||||
authProvider := NewAuthProvider(config.APIKey, config.JWTSecret)
|
||||
|
||||
var httpClient *HTTPClient
|
||||
if config.EnableHTTP {
|
||||
httpClient = NewHTTPClient(
|
||||
config.APIURL,
|
||||
authProvider,
|
||||
config.Timeout,
|
||||
config.MaxRetries,
|
||||
config.RetryDelay,
|
||||
config.EnableLogging,
|
||||
)
|
||||
}
|
||||
|
||||
// Initialize HTTP client
|
||||
if config.EnableHTTP {
|
||||
client.httpClient = &HTTPClient{
|
||||
client: &http.Client{
|
||||
Timeout: config.Timeout,
|
||||
},
|
||||
apiKey: config.APIKey,
|
||||
baseURL: config.APIURL,
|
||||
}
|
||||
client := &Client{
|
||||
config: config,
|
||||
authProvider: authProvider,
|
||||
httpClient: httpClient,
|
||||
}
|
||||
|
||||
// Initialize API modules
|
||||
client.Subscribers = NewSubscriberAPI(httpClient)
|
||||
client.Usage = NewUsageAPI(httpClient)
|
||||
client.Payments = NewPaymentAPI(httpClient)
|
||||
client.RatingPlans = NewRatingPlanAPI(httpClient)
|
||||
client.System = NewSystemAPI(httpClient)
|
||||
client.GraphQL = NewGraphQLAPI(httpClient)
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// Close closes the client connections
|
||||
func (c *Client) Close() error {
|
||||
if c.httpClient != nil {
|
||||
return c.httpClient.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HTTP Methods
|
||||
// Authentication methods
|
||||
|
||||
// GetSubscriber retrieves a subscriber by ID
|
||||
func (c *Client) GetSubscriber(ctx context.Context, id int64) (*Subscriber, error) {
|
||||
if !c.config.EnableHTTP {
|
||||
return nil, fmt.Errorf("HTTP client not enabled")
|
||||
}
|
||||
|
||||
var subscriber Subscriber
|
||||
err := c.httpClient.Get(ctx, fmt.Sprintf("/v1/subscribers/%d", id), &subscriber)
|
||||
return &subscriber, err
|
||||
// GenerateJWTToken generates a JWT token for authentication
|
||||
func (c *Client) GenerateJWTToken(userID string, expiryHours int, additionalClaims map[string]interface{}) (string, error) {
|
||||
return c.authProvider.GenerateJWTToken(userID, expiryHours, additionalClaims)
|
||||
}
|
||||
|
||||
// ListSubscribers retrieves a list of subscribers
|
||||
func (c *Client) ListSubscribers(ctx context.Context, page, pageSize int32) (*SubscriberList, error) {
|
||||
if !c.config.EnableHTTP {
|
||||
return nil, fmt.Errorf("HTTP client not enabled")
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"page": fmt.Sprintf("%d", page),
|
||||
"page_size": fmt.Sprintf("%d", pageSize),
|
||||
}
|
||||
|
||||
var list SubscriberList
|
||||
err := c.httpClient.Get(ctx, "/v1/subscribers", &list, params)
|
||||
return &list, err
|
||||
}
|
||||
|
||||
// CreateSubscriber creates a new subscriber
|
||||
func (c *Client) CreateSubscriber(ctx context.Context, req *CreateSubscriberRequest) (*Subscriber, error) {
|
||||
if !c.config.EnableHTTP {
|
||||
return nil, fmt.Errorf("HTTP client not enabled")
|
||||
}
|
||||
|
||||
var subscriber Subscriber
|
||||
err := c.httpClient.Post(ctx, "/v1/subscribers", req, &subscriber)
|
||||
return &subscriber, err
|
||||
}
|
||||
|
||||
// UpdateSubscriber updates an existing subscriber
|
||||
func (c *Client) UpdateSubscriber(ctx context.Context, id int64, req *UpdateSubscriberRequest) (*Subscriber, error) {
|
||||
if !c.config.EnableHTTP {
|
||||
return nil, fmt.Errorf("HTTP client not enabled")
|
||||
}
|
||||
|
||||
var subscriber Subscriber
|
||||
err := c.httpClient.Put(ctx, fmt.Sprintf("/v1/subscribers/%d", id), req, &subscriber)
|
||||
return &subscriber, err
|
||||
}
|
||||
|
||||
// DeleteSubscriber deletes a subscriber
|
||||
func (c *Client) DeleteSubscriber(ctx context.Context, id int64) error {
|
||||
if !c.config.EnableHTTP {
|
||||
return fmt.Errorf("HTTP client not enabled")
|
||||
}
|
||||
|
||||
return c.httpClient.Delete(ctx, fmt.Sprintf("/v1/subscribers/%d", id))
|
||||
}
|
||||
|
||||
// GetUsageStats retrieves usage statistics for a subscriber
|
||||
func (c *Client) GetUsageStats(ctx context.Context, subscriberID int64, startDate, endDate time.Time) (*UsageStats, error) {
|
||||
if !c.config.EnableHTTP {
|
||||
return nil, fmt.Errorf("HTTP client not enabled")
|
||||
}
|
||||
|
||||
params := map[string]string{
|
||||
"start_date": startDate.Format(time.RFC3339),
|
||||
"end_date": endDate.Format(time.RFC3339),
|
||||
}
|
||||
|
||||
var stats UsageStats
|
||||
err := c.httpClient.Get(ctx, fmt.Sprintf("/v1/subscribers/%d/usage", subscriberID), &stats, params)
|
||||
return &stats, err
|
||||
}
|
||||
|
||||
// CreatePaymentTransaction creates a new payment transaction
|
||||
func (c *Client) CreatePaymentTransaction(ctx context.Context, req *CreatePaymentRequest) (*PaymentTransaction, error) {
|
||||
if !c.config.EnableHTTP {
|
||||
return nil, fmt.Errorf("HTTP client not enabled")
|
||||
}
|
||||
|
||||
var transaction PaymentTransaction
|
||||
err := c.httpClient.Post(ctx, "/v1/payments/transactions", req, &transaction)
|
||||
return &transaction, err
|
||||
}
|
||||
|
||||
// GetSystemStats retrieves system statistics
|
||||
func (c *Client) GetSystemStats(ctx context.Context) (*SystemStats, error) {
|
||||
if !c.config.EnableHTTP {
|
||||
return nil, fmt.Errorf("HTTP client not enabled")
|
||||
}
|
||||
|
||||
var stats SystemStats
|
||||
err := c.httpClient.Get(ctx, "/v1/system/stats", &stats)
|
||||
return &stats, err
|
||||
}
|
||||
|
||||
// HTTPClient implementation
|
||||
|
||||
// Get makes an HTTP GET request
|
||||
func (h *HTTPClient) Get(ctx context.Context, path string, result interface{}, params ...map[string]string) error {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", h.baseURL+path, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if h.apiKey != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+h.apiKey)
|
||||
}
|
||||
|
||||
if len(params) > 0 {
|
||||
q := req.URL.Query()
|
||||
for k, v := range params[0] {
|
||||
q.Add(k, v)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
resp, err := h.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("HTTP error: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return json.NewDecoder(resp.Body).Decode(result)
|
||||
}
|
||||
|
||||
// Post makes an HTTP POST request
|
||||
func (h *HTTPClient) Post(ctx context.Context, path string, body, result interface{}) error {
|
||||
return h.doRequest(ctx, "POST", path, body, result)
|
||||
}
|
||||
|
||||
// Put makes an HTTP PUT request
|
||||
func (h *HTTPClient) Put(ctx context.Context, path string, body, result interface{}) error {
|
||||
return h.doRequest(ctx, "PUT", path, body, result)
|
||||
}
|
||||
|
||||
// Delete makes an HTTP DELETE request
|
||||
func (h *HTTPClient) Delete(ctx context.Context, path string) error {
|
||||
req, err := http.NewRequestWithContext(ctx, "DELETE", h.baseURL+path, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if h.apiKey != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+h.apiKey)
|
||||
}
|
||||
|
||||
resp, err := h.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusNoContent {
|
||||
return fmt.Errorf("HTTP error: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// doRequest makes an HTTP request with a body
|
||||
func (h *HTTPClient) doRequest(ctx context.Context, method, path string, body, result interface{}) error {
|
||||
var bodyBytes []byte
|
||||
var err error
|
||||
|
||||
if body != nil {
|
||||
bodyBytes, err = json.Marshal(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, method, h.baseURL+path, bytes.NewReader(bodyBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if h.apiKey != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+h.apiKey)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := h.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("HTTP error: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
return json.NewDecoder(resp.Body).Decode(result)
|
||||
// ValidateJWTToken validates a JWT token
|
||||
func (c *Client) ValidateJWTToken(token string) (map[string]interface{}, error) {
|
||||
return c.authProvider.ValidateJWTToken(token)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
package telecom
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UsageAPI handles usage-related API calls
|
||||
type UsageAPI struct {
|
||||
client *HTTPClient
|
||||
}
|
||||
|
||||
// NewUsageAPI creates a new UsageAPI
|
||||
func NewUsageAPI(client *HTTPClient) *UsageAPI {
|
||||
return &UsageAPI{client: client}
|
||||
}
|
||||
|
||||
// GetStats retrieves usage statistics for a subscriber
|
||||
func (u *UsageAPI) GetStats(ctx context.Context, subscriberID int64, startDate, endDate time.Time) (*UsageStats, error) {
|
||||
params := map[string]string{
|
||||
"start_date": startDate.Format(time.RFC3339),
|
||||
"end_date": endDate.Format(time.RFC3339),
|
||||
}
|
||||
|
||||
var stats UsageStats
|
||||
err := u.client.Get(ctx, fmt.Sprintf("/v1/subscribers/%d/usage", subscriberID), &stats, params)
|
||||
return &stats, err
|
||||
}
|
||||
|
||||
// ListEvents retrieves a list of usage events
|
||||
func (u *UsageAPI) ListEvents(ctx context.Context, subscriberID int64, usageType string, startDate, endDate time.Time, page, pageSize int32) (*UsageEventList, error) {
|
||||
params := map[string]string{
|
||||
"page": fmt.Sprintf("%d", page),
|
||||
"page_size": fmt.Sprintf("%d", pageSize),
|
||||
}
|
||||
|
||||
if subscriberID > 0 {
|
||||
params["subscriber_id"] = fmt.Sprintf("%d", subscriberID)
|
||||
}
|
||||
if usageType != "" {
|
||||
params["usage_type"] = usageType
|
||||
}
|
||||
if !startDate.IsZero() {
|
||||
params["start_date"] = startDate.Format(time.RFC3339)
|
||||
}
|
||||
if !endDate.IsZero() {
|
||||
params["end_date"] = endDate.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
var list UsageEventList
|
||||
err := u.client.Get(ctx, "/v1/usage/events", &list, params)
|
||||
return &list, err
|
||||
}
|
||||
|
||||
// GetRealtime retrieves real-time usage for a subscriber
|
||||
func (u *UsageAPI) GetRealtime(ctx context.Context, subscriberID int64) (*RealTimeUsage, error) {
|
||||
var usage RealTimeUsage
|
||||
err := u.client.Get(ctx, fmt.Sprintf("/v1/subscribers/%d/realtime", subscriberID), &usage)
|
||||
return &usage, err
|
||||
}
|
||||
@@ -4,6 +4,7 @@ Async Python SDK for the Telecom Platform with full type hints and modern async/
|
||||
|
||||
## Features
|
||||
|
||||
- **Modular Architecture**: Separate modules for authentication, API clients, and WebSocket
|
||||
- **Async/Await Support**: Full async/await support with aiohttp
|
||||
- **Type Safety**: Complete type hints with Pydantic models
|
||||
- **WebSocket Support**: Real-time updates via WebSocket
|
||||
@@ -32,6 +33,15 @@ pip install -r requirements.txt
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
The SDK is organized into modular components:
|
||||
|
||||
- `auth.py` - Authentication provider handling API keys and JWT tokens
|
||||
- `api.py` - Main API client with HTTP request handling and retry logic
|
||||
- `websocket.py` - WebSocket client for real-time updates
|
||||
- `types.py` - Type definitions and Pydantic models
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
|
||||
Reference in New Issue
Block a user