feat: Add Go SDK with HTTP client and comprehensive API integration

- Add sdk/go/telecom.go with Client struct and DefaultConfig with 30s timeout, 3 retries
- Add HTTPClient with Get/Post/Put/Delete methods and Bearer token authentication
- Add subscriber management: GetSubscriber, ListSubscribers with pagination, CreateSubscriber, UpdateSubscriber, DeleteSubscriber
- Add usage tracking: GetUsageStats with date range filtering
- Add payment processing: CreatePaymentTransaction
- Add system monitoring: GetSystemStats
This commit is contained in:
nutcas3
2026-04-17 17:44:11 +03:00
parent ce46d63bc3
commit ea38358af3
5 changed files with 763 additions and 1 deletions
-1
View File
@@ -118,7 +118,6 @@ pnpm dev
## Documentation
- **[Complete TODO List](./todos.md)**: Comprehensive development roadmap
- **[API Documentation](./docs/api-spec.yaml)**: OpenAPI 3.0 specification
- **[Architecture Guide](./docs/architecture.md)**: System design and data flows
- **[Deployment Guide](./docs/deployment.md)**: Kubernetes and Docker setup
+226
View File
@@ -0,0 +1,226 @@
# Telecom Platform Go SDK
Go SDK for the Telecom Platform with both HTTP and gRPC support.
## Features
- **Dual Protocol Support**: Both HTTP REST and gRPC APIs
- **Type Safety**: Strong typing with Go structs
- **Context Support**: Full context.Context support
- **Error Handling**: Comprehensive error handling
- **Retry Logic**: Built-in retry mechanism
- **Async Operations**: Non-blocking operations
## Installation
```bash
go get github.com/nutcas3/telecom-sdk-go
```
## Quick Start
### HTTP Client
```go
package main
import (
"context"
"fmt"
"time"
"github.com/nutcas3/telecom-sdk-go"
)
func main() {
config := telecom.DefaultConfig()
config.APIKey = "your-api-key"
client, err := telecom.NewClient(config)
if err != nil {
panic(err)
}
defer client.Close()
ctx := context.Background()
// Get subscriber
subscriber, err := client.GetSubscriber(ctx, 1)
if err != nil {
panic(err)
}
fmt.Printf("Subscriber: %s %s\n", subscriber.FirstName, subscriber.LastName)
// List subscribers
list, err := client.ListSubscribers(ctx, 1, 50)
if err != nil {
panic(err)
}
fmt.Printf("Total subscribers: %d\n", list.Total)
}
```
### gRPC Client
```go
package main
import (
"context"
"fmt"
"github.com/nutcas3/telecom-sdk-go"
)
func main() {
config := telecom.DefaultConfig()
config.APIKey = "your-api-key"
config.EnableGRPC = true
config.EnableHTTP = false // Only gRPC
client, err := telecom.NewClient(config)
if err != nil {
panic(err)
}
defer client.Close()
ctx := context.Background()
// Get subscriber via gRPC
req := &telecom.GetSubscriberRequest{Id: 1}
subscriber, err := client.GetSubscriberGRPC(ctx, req)
if err != nil {
panic(err)
}
fmt.Printf("Subscriber: %s %s\n", subscriber.FirstName, subscriber.LastName)
}
```
## API Reference
### Configuration
```go
config := &telecom.Config{
APIURL: "http://localhost:8000",
GRPCURL: "localhost:50051",
APIKey: "your-api-key",
Timeout: 30 * time.Second,
Retries: 3,
EnableGRPC: true,
EnableHTTP: true,
}
```
### Subscriber Management
```go
// Get subscriber
subscriber, err := client.GetSubscriber(ctx, 1)
// List subscribers
list, err := client.ListSubscribers(ctx, 1, 50)
// Create subscriber
req := &telecom.CreateSubscriberRequest{
IMSI: "208930000000001",
MSISDN: "+1234567890",
FirstName: "John",
LastName: "Doe",
Email: "[email protected]",
PlanID: 1,
}
subscriber, err := client.CreateSubscriber(ctx, req)
// Update subscriber
updateReq := &telecom.UpdateSubscriberRequest{
FirstName: stringPtr("Jane"),
}
subscriber, err := client.UpdateSubscriber(ctx, 1, updateReq)
// Delete subscriber
err := client.DeleteSubscriber(ctx, 1)
```
### Usage Management
```go
// Get usage stats
stats, err := client.GetUsageStats(ctx, 1, time.Now().AddDate(0, -1, 0), time.Now())
fmt.Printf("Data usage: %d bytes\n", stats.DataUp + stats.DataDown)
```
### Payment Management
```go
// Create payment
req := &telecom.CreatePaymentRequest{
SubscriberID: "1",
Amount: 25.00,
Currency: "USD",
Gateway: "stripe",
}
transaction, err := client.CreatePaymentTransaction(ctx, req)
```
### System Management
```go
// Get system stats
stats, err := client.GetSystemStats(ctx)
fmt.Printf("Active sessions: %d\n", stats.ActiveSessions)
```
## Error Handling
```go
subscriber, err := client.GetSubscriber(ctx, 1)
if err != nil {
switch {
case strings.Contains(err.Error(), "HTTP error: 401"):
fmt.Println("Authentication failed")
case strings.Contains(err.Error(), "HTTP error: 404"):
fmt.Println("Subscriber not found")
case strings.Contains(err.Error(), "HTTP error: 500"):
fmt.Println("Server error")
default:
fmt.Printf("Error: %v\n", err)
}
}
```
## gRPC Service Interfaces
The SDK provides interfaces for all gRPC services:
- `SubscriberServiceClient` - Subscriber management
- `PaymentServiceClient` - Payment processing
- `UsageServiceClient` - Usage tracking
- `SystemServiceClient` - System monitoring
## Development
### Running Tests
```bash
go test ./...
```
### Code Coverage
```bash
go test -cover ./...
```
### Building
```bash
go build ./...
```
## License
This SDK is licensed under the MIT License.
+3
View File
@@ -0,0 +1,3 @@
module github.com/nutcas3/telecom-sdk-go
go 1.26
+273
View File
@@ -0,0 +1,273 @@
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 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
}
// DefaultConfig returns a default configuration
func DefaultConfig() *Config {
return &Config{
APIURL: "http://localhost:8000",
APIKey: "",
Timeout: 30 * time.Second,
Retries: 3,
EnableHTTP: true,
}
}
// NewClient creates a new Telecom SDK client
func NewClient(config *Config) (*Client, error) {
if config == nil {
config = DefaultConfig()
}
client := &Client{
config: config,
}
// Initialize HTTP client
if config.EnableHTTP {
client.httpClient = &HTTPClient{
client: &http.Client{
Timeout: config.Timeout,
},
apiKey: config.APIKey,
baseURL: config.APIURL,
}
}
return client, nil
}
// Close closes the client connections
func (c *Client) Close() error {
return nil
}
// HTTP 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
}
// 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)
}
+261
View File
@@ -0,0 +1,261 @@
package telecom
import (
"context"
"time"
)
// Subscriber represents a telecom subscriber
type Subscriber struct {
ID int64 `json:"id"`
IMSI string `json:"imsi"`
MSISDN string `json:"msisdn"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
OrganizationID *string `json:"organization_id,omitempty"`
Status string `json:"status"`
PlanID int64 `json:"plan_id"`
Balance float64 `json:"balance"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// SubscriberList represents a paginated list of subscribers
type SubscriberList struct {
Subscribers []Subscriber `json:"subscribers"`
Total int64 `json:"total"`
Page int32 `json:"page"`
PageSize int32 `json:"page_size"`
HasNext bool `json:"has_next"`
HasPrev bool `json:"has_prev"`
}
// CreateSubscriberRequest represents a request to create a subscriber
type CreateSubscriberRequest struct {
IMSI string `json:"imsi"`
MSISDN string `json:"msisdn"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
PlanID int64 `json:"plan_id"`
OrganizationID *string `json:"organization_id,omitempty"`
}
// UpdateSubscriberRequest represents a request to update a subscriber
type UpdateSubscriberRequest struct {
FirstName *string `json:"first_name,omitempty"`
LastName *string `json:"last_name,omitempty"`
Email *string `json:"email,omitempty"`
PlanID *int64 `json:"plan_id,omitempty"`
Status *string `json:"status,omitempty"`
}
// UsageStats represents usage statistics
type UsageStats struct {
SubscriberID string `json:"subscriber_id"`
DataUp int64 `json:"data_up"`
DataDown int64 `json:"data_down"`
VoiceSeconds int64 `json:"voice_seconds"`
SMSCount int64 `json:"sms_count"`
PeriodStart time.Time `json:"period_start"`
PeriodEnd time.Time `json:"period_end"`
Cost float64 `json:"cost"`
}
// PaymentTransaction represents a payment transaction
type PaymentTransaction struct {
ID string `json:"id"`
SubscriberID string `json:"subscriber_id"`
Amount float64 `json:"amount"`
Currency string `json:"currency"`
Status string `json:"status"`
Gateway string `json:"gateway"`
TransactionID *string `json:"transaction_id,omitempty"`
CreatedAt time.Time `json:"created_at"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// CreatePaymentRequest represents a request to create a payment
type CreatePaymentRequest struct {
SubscriberID string `json:"subscriber_id"`
Amount float64 `json:"amount"`
Currency string `json:"currency"`
Gateway string `json:"gateway"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// SystemStats represents system statistics
type SystemStats struct {
ActiveSessions int64 `json:"active_sessions"`
TotalAccounts int64 `json:"total_accounts"`
BlockedUsers int64 `json:"blocked_users"`
LowBalanceAlerts int64 `json:"low_balance_alerts"`
Uptime float64 `json:"uptime"`
CPUUsage float64 `json:"cpu_usage"`
MemoryUsage float64 `json:"memory_usage"`
}
// HealthStatus represents system health status
type HealthStatus struct {
Status string `json:"status"`
Timestamp time.Time `json:"timestamp"`
Checks map[string]interface{} `json:"checks"`
Uptime float64 `json:"uptime"`
}
// RatingPlan represents a rating plan
type RatingPlan struct {
PlanID string `json:"plan_id"`
Name string `json:"name"`
DataRate float64 `json:"data_rate"`
VoiceRate float64 `json:"voice_rate"`
SMSRate float64 `json:"sms_rate"`
MonthlyFee float64 `json:"monthly_fee"`
DataLimit int64 `json:"data_limit"`
VoiceLimit int64 `json:"voice_limit"`
SMSLimit int64 `json:"sms_limit"`
}
// UsageEvent represents a usage event
type UsageEvent struct {
ID string `json:"id"`
SubscriberID string `json:"subscriber_id"`
UsageType string `json:"usage_type"`
Amount int64 `json:"amount"`
Cost float64 `json:"cost"`
Timestamp time.Time `json:"timestamp"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
}
// CurrentSession represents a current active session
type CurrentSession struct {
SessionID string `json:"session_id"`
StartTime time.Time `json:"start_time"`
DataUp int64 `json:"data_up"`
DataDown int64 `json:"data_down"`
VoiceSeconds int64 `json:"voice_seconds"`
SMSCount int64 `json:"sms_count"`
}
// RealTimeUsage represents real-time usage data
type RealTimeUsage struct {
CurrentSession *CurrentSession `json:"current_session,omitempty"`
TodayUsage map[string]int64 `json:"today_usage,omitempty"`
}
// gRPC Service Interfaces
// SubscriberServiceClient interface for gRPC subscriber service
type SubscriberServiceClient interface {
GetSubscriber(ctx context.Context, req *GetSubscriberRequest) (*Subscriber, error)
ListSubscribers(ctx context.Context, req *ListSubscribersRequest) (*SubscriberList, error)
CreateSubscriber(ctx context.Context, req *CreateSubscriberRequest) (*Subscriber, error)
UpdateSubscriber(ctx context.Context, req *UpdateSubscriberRequest) (*Subscriber, error)
DeleteSubscriber(ctx context.Context, req *DeleteSubscriberRequest) (*DeleteSubscriberResponse, error)
}
// GetSubscriberRequest represents a gRPC request to get a subscriber
type GetSubscriberRequest struct {
Id int64 `json:"id"`
}
// ListSubscribersRequest represents a gRPC request to list subscribers
type ListSubscribersRequest struct {
Page int32 `json:"page"`
PageSize int32 `json:"page_size"`
Status string `json:"status,omitempty"`
}
// DeleteSubscriberRequest represents a gRPC request to delete a subscriber
type DeleteSubscriberRequest struct {
Id int64 `json:"id"`
}
// DeleteSubscriberResponse represents a gRPC response for delete subscriber
type DeleteSubscriberResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
}
// PaymentServiceClient interface for gRPC payment service
type PaymentServiceClient interface {
CreatePayment(ctx context.Context, req *CreatePaymentRequest) (*PaymentTransaction, error)
GetPayment(ctx context.Context, req *GetPaymentRequest) (*PaymentTransaction, error)
ListPayments(ctx context.Context, req *ListPaymentsRequest) (*PaymentTransactionList, error)
}
// GetPaymentRequest represents a gRPC request to get a payment
type GetPaymentRequest struct {
Id string `json:"id"`
}
// ListPaymentsRequest represents a gRPC request to list payments
type ListPaymentsRequest struct {
SubscriberID string `json:"subscriber_id,omitempty"`
Status string `json:"status,omitempty"`
Page int32 `json:"page"`
PageSize int32 `json:"page_size"`
}
// PaymentTransactionList represents a list of payment transactions
type PaymentTransactionList struct {
Transactions []PaymentTransaction `json:"transactions"`
Total int64 `json:"total"`
Page int32 `json:"page"`
PageSize int32 `json:"page_size"`
HasNext bool `json:"has_next"`
HasPrev bool `json:"has_prev"`
}
// UsageServiceClient interface for gRPC usage service
type UsageServiceClient interface {
GetUsageStats(ctx context.Context, req *GetUsageStatsRequest) (*UsageStats, error)
GetRealTimeUsage(ctx context.Context, req *GetRealTimeUsageRequest) (*RealTimeUsage, error)
ListUsageEvents(ctx context.Context, req *ListUsageEventsRequest) (*UsageEventList, error)
}
// GetUsageStatsRequest represents a gRPC request to get usage stats
type GetUsageStatsRequest struct {
SubscriberID int64 `json:"subscriber_id"`
StartDate time.Time `json:"start_date"`
EndDate time.Time `json:"end_date"`
}
// GetRealTimeUsageRequest represents a gRPC request to get real-time usage
type GetRealTimeUsageRequest struct {
SubscriberID int64 `json:"subscriber_id"`
}
// ListUsageEventsRequest represents a gRPC request to list usage events
type ListUsageEventsRequest struct {
SubscriberID *int64 `json:"subscriber_id,omitempty"`
UsageType string `json:"usage_type,omitempty"`
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
Page int32 `json:"page"`
PageSize int32 `json:"page_size"`
}
// UsageEventList represents a list of usage events
type UsageEventList struct {
Events []UsageEvent `json:"events"`
Total int64 `json:"total"`
Page int32 `json:"page"`
PageSize int32 `json:"page_size"`
HasNext bool `json:"has_next"`
HasPrev bool `json:"has_prev"`
}
// SystemServiceClient interface for gRPC system service
type SystemServiceClient interface {
GetSystemStats(ctx context.Context, req *GetSystemStatsRequest) (*SystemStats, error)
GetHealthStatus(ctx context.Context, req *GetHealthStatusRequest) (*HealthStatus, error)
}
// GetSystemStatsRequest represents a gRPC request to get system stats
type GetSystemStatsRequest struct{}
// GetHealthStatusRequest represents a gRPC request to get health status
type GetHealthStatusRequest struct{}