Go SDK
The Unizo Go SDK provides a robust and type-safe way to interact with the Unizo API from Go applications. It supports Go 1.18+ and follows Go best practices and idioms.
Installation
Go Modules (Recommended)
go get github.com/unizo/go-sdk
Import in your code
import "github.com/unizo/go-sdk/unizo"
Basic Setup
package main
import (
"context"
"log"
"github.com/unizo/go-sdk/unizo"
)
func main() {
// Initialize the client
client := unizo.NewClient(&unizo.Config{
APIKey: "your-api-key",
Environment: unizo.EnvironmentProduction, // or unizo.EnvironmentSandbox
})
}
Configuration Options
import (
"time"
"log"
"github.com/unizo/go-sdk/unizo"
)
client := unizo.NewClient(&unizo.Config{
APIKey: "your-api-key",
Environment: unizo.EnvironmentProduction,
BaseURL: "https://api.unizo.com",
Timeout: 30 * time.Second,
UserAgent: "MyApp/1.0.0",
RetryConfig: &unizo.RetryConfig{
Attempts: 3,
InitialDelay: 1 * time.Second,
MaxDelay: 10 * time.Second,
BackoffFactor: 2.0,
},
Debug: false,
Logger: log.Default(),
})
Environment Variables
import (
"os"
"github.com/unizo/go-sdk/unizo"
)
client := unizo.NewClient(&unizo.Config{
APIKey: os.Getenv("UNIZO_API_KEY"),
Environment: unizo.ParseEnvironment(os.Getenv("UNIZO_ENVIRONMENT")),
})
Authentication
API Key Authentication
// Set during initialization
client := unizo.NewClient(&unizo.Config{
APIKey: "your-api-key",
})
// Or set later
client.SetAPIKey("your-api-key")
// Using context for temporary authentication
ctx := unizo.WithAPIKey(context.Background(), "temp-api-key")
identity, err := client.Identity.Get(ctx)
Bearer Token Authentication
// For user-specific operations
client := unizo.NewClient(&unizo.Config{
BearerToken: "user-access-token",
})
// Or set dynamically
client.SetBearerToken("user-access-token")
// Using context
ctx := unizo.WithBearerToken(context.Background(), "user-access-token")
Usage Examples
Identity Management
package main
import (
"context"
"fmt"
"log"
"github.com/unizo/go-sdk/unizo"
)
func main() {
client := unizo.NewClient(&unizo.Config{
APIKey: "your-api-key",
})
ctx := context.Background()
// Get user identity
identity, err := client.Identity.Get(ctx)
if err != nil {
log.Fatalf("Failed to get identity: %v", err)
}
fmt.Printf("User ID: %s\n", identity.ID)
fmt.Printf("Email: %s\n", identity.Email)
// Update user profile
updatedProfile, err := client.Identity.Update(ctx, &unizo.UpdateIdentityRequest{
Name: unizo.String("John Doe"),
Email: unizo.String("john@example.com"),
Metadata: map[string]interface{}{
"department": "Engineering",
"role": "Senior Developer",
},
})
if err != nil {
log.Fatalf("Failed to update profile: %v", err)
}
fmt.Printf("Profile updated: %s\n", updatedProfile.Name)
}
Source Code Management
// List repositories
repositories, err := client.SourceCode.Repositories.List(ctx, &unizo.ListRepositoriesRequest{
Page: unizo.Int(1),
Limit: unizo.Int(50),
Provider: unizo.String("github"),
})
if err != nil {
log.Fatalf("Failed to list repositories: %v", err)
}
for _, repo := range repositories.Data {
fmt.Printf("Repository: %s (%s)\n", repo.Name, repo.URL)
}
// Get repository details
repo, err := client.SourceCode.Repositories.Get(ctx, "repo-id")
if err != nil {
log.Fatalf("Failed to get repository: %v", err)
}
fmt.Printf("Repository stats: %+v\n", repo.Stats)
// Create webhook
webhook, err := client.SourceCode.Webhooks.Create(ctx, &unizo.CreateWebhookRequest{
RepositoryID: "repo-id",
URL: "https://your-app.com/webhooks/source-code",
Events: []unizo.WebhookEvent{unizo.WebhookEventPush, unizo.WebhookEventPullRequest},
Secret: unizo.String("webhook-secret"),
})
if err != nil {
log.Fatalf("Failed to create webhook: %v", err)
}
fmt.Printf("Webhook created: %s\n", webhook.ID)
Ticketing System
import (
"time"
)
// Create a ticket
ticket, err := client.Ticketing.Tickets.Create(ctx, &unizo.CreateTicketRequest{
Title: "Bug Report: Login Issues",
Description: "Users are experiencing login failures after the latest update",
Priority: unizo.TicketPriorityHigh,
Assignee: unizo.String("user-id"),
Tags: []string{"bug", "login", "urgent"},
DueDate: unizo.Time(time.Now().Add(72 * time.Hour)), // 3 days from now
})
if err != nil {
log.Fatalf("Failed to create ticket: %v", err)
}
fmt.Printf("Created ticket: %s\n", ticket.ID)
// List tickets with filtering
tickets, err := client.Ticketing.Tickets.List(ctx, &unizo.ListTicketsRequest{
Status: unizo.TicketStatus(unizo.TicketStatusOpen),
Priority: unizo.TicketPriority(unizo.TicketPriorityHigh),
Assignee: unizo.String("current-user"),
Page: unizo.Int(1),
Limit: unizo.Int(25),
CreatedAfter: unizo.Time(time.Now().Add(-7 * 24 * time.Hour)), // 1 week ago
})
if err != nil {
log.Fatalf("Failed to list tickets: %v", err)
}
fmt.Printf("Found %d tickets\n", tickets.Total)
// Update ticket
updatedTicket, err := client.Ticketing.Tickets.Update(ctx, ticket.ID, &unizo.UpdateTicketRequest{
Status: unizo.TicketStatus(unizo.TicketStatusInProgress),
Comments: []unizo.Comment{{
Text: "Started investigating the issue",
Author: "developer-id",
Timestamp: time.Now(),
}},
})
if err != nil {
log.Fatalf("Failed to update ticket: %v", err)
}
Communications
// Send email with template
emailResult, err := client.Communications.Email.Send(ctx, &unizo.SendEmailRequest{
To: []string{"user@example.com"},
Subject: "Welcome to Unizo",
Template: unizo.String("welcome-template"),
Variables: map[string]interface{}{
"user_name": "John Doe",
"activation_link": "https://app.unizo.com/activate/token",
},
Attachments: []unizo.Attachment{{
Filename: "welcome_guide.pdf",
Content: []byte("PDF content here"),
ContentType: "application/pdf",
}},
})
if err != nil {
log.Fatalf("Failed to send email: %v", err)
}
fmt.Printf("Email sent: %s\n", emailResult.MessageID)
// Send plain text email
plainEmail, err := client.Communications.Email.Send(ctx, &unizo.SendEmailRequest{
To: []string{"user@example.com"},
Subject: "System Notification",
Text: unizo.String("Your account has been activated successfully."),
})
if err != nil {
log.Fatalf("Failed to send plain email: %v", err)
}
// Send SMS
smsResult, err := client.Communications.SMS.Send(ctx, &unizo.SendSMSRequest{
To: "+1234567890",
Message: "Your verification code is: 123456",
SenderID: unizo.String("UNIZO"),
})
if err != nil {
log.Fatalf("Failed to send SMS: %v", err)
}
fmt.Printf("SMS sent: %s\n", smsResult.MessageID)
Incident Management
// Create incident
incident, err := client.Incidents.Create(ctx, &unizo.CreateIncidentRequest{
Title: "API Service Degradation",
Description: "Response times increased by 200% across all endpoints",
Severity: unizo.IncidentSeverityMajor,
Status: unizo.IncidentStatusInvestigating,
Services: []string{"api-gateway", "database", "cache"},
AffectedUsers: unizo.Int(1500),
})
if err != nil {
log.Fatalf("Failed to create incident: %v", err)
}
fmt.Printf("Created incident: %s\n", incident.ID)
// List active incidents
activeIncidents, err := client.Incidents.List(ctx, &unizo.ListIncidentsRequest{
Status: []unizo.IncidentStatus{unizo.IncidentStatusOpen, unizo.IncidentStatusInvestigating},
Severity: []unizo.IncidentSeverity{unizo.IncidentSeverityCritical, unizo.IncidentSeverityMajor},
})
if err != nil {
log.Fatalf("Failed to list incidents: %v", err)
}
// Update incident with timeline
err = client.Incidents.Update(ctx, incident.ID, &unizo.UpdateIncidentRequest{
Status: unizo.IncidentStatus(unizo.IncidentStatusResolved),
Resolution: unizo.String("Scaled up database instances and optimized queries"),
Timeline: []unizo.TimelineEvent{{
Timestamp: time.Now(),
Event: "Issue resolved",
Description: "Database performance restored to normal levels",
}},
})
if err != nil {
log.Fatalf("Failed to update incident: %v", err)
}
Error Handling
Error Types
import (
"errors"
"github.com/unizo/go-sdk/unizo"
)
identity, err := client.Identity.Get(ctx)
if err != nil {
var validationErr *unizo.ValidationError
var authErr *unizo.AuthenticationError
var rateLimitErr *unizo.RateLimitError
var notFoundErr *unizo.NotFoundError
var serverErr *unizo.ServerError
switch {
case errors.As(err, &validationErr):
fmt.Printf("Validation error: %s\n", validationErr.Message)
for field, fieldErr := range validationErr.FieldErrors {
fmt.Printf("%s: %s\n", field, fieldErr)
}
case errors.As(err, &authErr):
fmt.Printf("Authentication failed: %s\n", authErr.Message)
// Handle re-authentication
case errors.As(err, &rateLimitErr):
fmt.Printf("Rate limit exceeded. Retry after: %d seconds\n", rateLimitErr.RetryAfter)
// Implement backoff strategy
case errors.As(err, ¬FoundErr):
fmt.Printf("Resource not found: %s\n", notFoundErr.Message)
case errors.As(err, &serverErr):
fmt.Printf("Server error: %s\n", serverErr.Message)
// Log for debugging
default:
if unizoErr, ok := err.(*unizo.Error); ok {
fmt.Printf("Unizo API error: %s\n", unizoErr.Message)
fmt.Printf("Status code: %d\n", unizoErr.StatusCode)
fmt.Printf("Error code: %s\n", unizoErr.Code)
} else {
fmt.Printf("Unknown error: %v\n", err)
}
}
}
Retry Logic
import (
"context"
"math/rand"
"time"
)
func withRetry(ctx context.Context, maxAttempts int, baseDelay time.Duration, operation func() error) error {
for attempt := 1; attempt <= maxAttempts; attempt++ {
err := operation()
if err == nil {
return nil
}
var rateLimitErr *unizo.RateLimitError
if errors.As(err, &rateLimitErr) && attempt < maxAttempts {
// Exponential backoff with jitter
delay := baseDelay * time.Duration(1<<(attempt-1))
retryAfter := time.Duration(rateLimitErr.RetryAfter) * time.Second
actualDelay := time.Duration(float64(max(delay, retryAfter)) * (0.5 + 0.5*rand.Float64()))
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(actualDelay):
continue
}
}
if attempt == maxAttempts {
return err
}
}
return nil
}
func max(a, b time.Duration) time.Duration {
if a > b {
return a
}
return b
}
// Usage
err := withRetry(ctx, 3, 1*time.Second, func() error {
_, err := client.Identity.Get(ctx)
return err
})
if err != nil {
log.Fatalf("Failed after retries: %v", err)
}
Circuit Breaker Pattern
import (
"sync"
"time"
)
type CircuitBreaker struct {
failureThreshold int
recoveryTimeout time.Duration
failureCount int
lastFailureTime time.Time
state string
mutex sync.RWMutex
}
func NewCircuitBreaker(failureThreshold int, recoveryTimeout time.Duration) *CircuitBreaker {
return &CircuitBreaker{
failureThreshold: failureThreshold,
recoveryTimeout: recoveryTimeout,
state: "closed",
}
}
func (cb *CircuitBreaker) Call(operation func() error) error {
cb.mutex.RLock()
state := cb.state
cb.mutex.RUnlock()
switch state {
case "open":
cb.mutex.RLock()
canTry := time.Since(cb.lastFailureTime) > cb.recoveryTimeout
cb.mutex.RUnlock()
if canTry {
cb.mutex.Lock()
cb.state = "half-open"
cb.mutex.Unlock()
return cb.attemptCall(operation)
} else {
return errors.New("circuit breaker is open")
}
case "half-open", "closed":
return cb.attemptCall(operation)
}
return nil
}
func (cb *CircuitBreaker) attemptCall(operation func() error) error {
err := operation()
cb.mutex.Lock()
defer cb.mutex.Unlock()
if err != nil {
cb.recordFailure()
return err
}
cb.resetFailureCount()
return nil
}
func (cb *CircuitBreaker) recordFailure() {
cb.failureCount++
cb.lastFailureTime = time.Now()
if cb.failureCount >= cb.failureThreshold {
cb.state = "open"
}
}
func (cb *CircuitBreaker) resetFailureCount() {
cb.failureCount = 0
cb.state = "closed"
}
// Usage
circuitBreaker := NewCircuitBreaker(3, 30*time.Second)
err := circuitBreaker.Call(func() error {
_, err := client.Identity.Get(ctx)
return err
})
if err != nil {
fmt.Printf("Service temporarily unavailable: %v\n", err)
}
Concurrency and Goroutines
import (
"sync"
)
// Concurrent API calls
func fetchUserData(ctx context.Context, client *unizo.Client) (*UserData, error) {
var wg sync.WaitGroup
var mu sync.Mutex
var errs []error
userData := &UserData{}
// Fetch identity
wg.Add(1)
go func() {
defer wg.Done()
identity, err := client.Identity.Get(ctx)
if err != nil {
mu.Lock()
errs = append(errs, err)
mu.Unlock()
return
}
mu.Lock()
userData.Identity = identity
mu.Unlock()
}()
// Fetch tickets
wg.Add(1)
go func() {
defer wg.Done()
tickets, err := client.Ticketing.Tickets.List(ctx, &unizo.ListTicketsRequest{
Status: unizo.TicketStatus(unizo.TicketStatusOpen),
})
if err != nil {
mu.Lock()
errs = append(errs, err)
mu.Unlock()
return
}
mu.Lock()
userData.Tickets = tickets.Data
mu.Unlock()
}()
wg.Wait()
if len(errs) > 0 {
return nil, errs[0] // Return first error
}
return userData, nil
}
type UserData struct {
Identity *unizo.Identity
Tickets []unizo.Ticket
}
Worker Pool Pattern
import (
"context"
"sync"
)
type TicketProcessor struct {
client *unizo.Client
numWorkers int
}
func NewTicketProcessor(client *unizo.Client, numWorkers int) *TicketProcessor {
return &TicketProcessor{
client: client,
numWorkers: numWorkers,
}
}
func (tp *TicketProcessor) ProcessTickets(ctx context.Context, ticketRequests []unizo.CreateTicketRequest) ([]unizo.Ticket, error) {
jobs := make(chan unizo.CreateTicketRequest, len(ticketRequests))
results := make(chan unizo.Ticket, len(ticketRequests))
errors := make(chan error, len(ticketRequests))
// Start workers
var wg sync.WaitGroup
for i := 0; i < tp.numWorkers; i++ {
wg.Add(1)
go tp.worker(ctx, &wg, jobs, results, errors)
}
// Send jobs
for _, req := range ticketRequests {
jobs <- req
}
close(jobs)
// Wait for workers to finish
go func() {
wg.Wait()
close(results)
close(errors)
}()
// Collect results
var tickets []unizo.Ticket
var errs []error
for i := 0; i < len(ticketRequests); i++ {
select {
case ticket := <-results:
tickets = append(tickets, ticket)
case err := <-errors:
errs = append(errs, err)
case <-ctx.Done():
return nil, ctx.Err()
}
}
if len(errs) > 0 {
return tickets, errs[0] // Return first error
}
return tickets, nil
}
func (tp *TicketProcessor) worker(ctx context.Context, wg *sync.WaitGroup, jobs <-chan unizo.CreateTicketRequest, results chan<- unizo.Ticket, errors chan<- error) {
defer wg.Done()
for req := range jobs {
ticket, err := tp.client.Ticketing.Tickets.Create(ctx, &req)
if err != nil {
errors <- err
} else {
results <- *ticket
}
}
}
Webhook Handling
HTTP Server Example
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func webhookHandler(w http.ResponseWriter, r *http.Request) {
signature := r.Header.Get("X-Unizo-Signature")
body, err := io.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read request body", http.StatusBadRequest)
return
}
if !validateWebhookSignature(body, signature, os.Getenv("WEBHOOK_SECRET")) {
http.Error(w, "Invalid signature", http.StatusUnauthorized)
return
}
var event map[string]interface{}
if err := json.Unmarshal(body, &event); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
eventType, ok := event["type"].(string)
if !ok {
http.Error(w, "Missing event type", http.StatusBadRequest)
return
}
switch eventType {
case "ticket.created":
handleTicketCreated(event["data"])
case "incident.updated":
handleIncidentUpdated(event["data"])
default:
fmt.Printf("Unknown event type: %s\n", eventType)
}
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "OK")
}
func validateWebhookSignature(payload []byte, signature, secret string) bool {
expectedSignature := "sha256=" + computeHMAC(payload, secret)
return hmac.Equal([]byte(signature), []byte(expectedSignature))
}
func computeHMAC(payload []byte, secret string) string {
h := hmac.New(sha256.New, []byte(secret))
h.Write(payload)
return hex.EncodeToString(h.Sum(nil))
}
func handleTicketCreated(data interface{}) {
fmt.Printf("New ticket created: %+v\n", data)
// Process ticket creation
}
func handleIncidentUpdated(data interface{}) {
fmt.Printf("Incident updated: %+v\n", data)
// Process incident update
}
func main() {
http.HandleFunc("/webhooks/unizo", webhookHandler)
fmt.Println("Webhook server listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Gin Framework Example
import (
"net/http"
"github.com/gin-gonic/gin"
)
func setupWebhookRoutes(r *gin.Engine) {
webhooks := r.Group("/webhooks")
{
webhooks.POST("/unizo", handleUnizoWebhook)
}
}
func handleUnizoWebhook(c *gin.Context) {
signature := c.GetHeader("X-Unizo-Signature")
body, err := c.GetRawData()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to read request body"})
return
}
if !validateWebhookSignature(body, signature, os.Getenv("WEBHOOK_SECRET")) {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid signature"})
return
}
var event map[string]interface{}
if err := c.ShouldBindJSON(&event); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid JSON"})
return
}
eventType := event["type"].(string)
switch eventType {
case "ticket.created":
go handleTicketCreatedAsync(event["data"])
case "incident.updated":
go handleIncidentUpdatedAsync(event["data"])
}
c.JSON(http.StatusOK, gin.H{"status": "ok"})
}
func handleTicketCreatedAsync(data interface{}) {
// Process ticket creation asynchronously
}
func handleIncidentUpdatedAsync(data interface{}) {
// Process incident update asynchronously
}
Testing
Unit Testing
package main
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/unizo/go-sdk/unizo"
)
// Mock client for testing
type MockUnizoClient struct {
mock.Mock
}
func (m *MockUnizoClient) GetIdentity(ctx context.Context) (*unizo.Identity, error) {
args := m.Called(ctx)
return args.Get(0).(*unizo.Identity), args.Error(1)
}
func TestGetUserIdentity(t *testing.T) {
// Setup
mockClient := new(MockUnizoClient)
expectedIdentity := &unizo.Identity{
ID: "user-123",
Email: "test@example.com",
Name: "Test User",
}
mockClient.On("GetIdentity", mock.Anything).Return(expectedIdentity, nil)
// Test
ctx := context.Background()
identity, err := mockClient.GetIdentity(ctx)
// Assert
assert.NoError(t, err)
assert.Equal(t, "user-123", identity.ID)
assert.Equal(t, "test@example.com", identity.Email)
mockClient.AssertExpectations(t)
}
Integration Testing
package main
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/unizo/go-sdk/unizo"
)
func TestUnizoClientIntegration(t *testing.T) {
// Setup mock server
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/v1/identity" && r.Method == "GET" {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"id":"user-123","email":"test@example.com","name":"Test User"}`))
}
}))
defer server.Close()
// Setup client with mock server
client := unizo.NewClient(&unizo.Config{
APIKey: "test-api-key",
BaseURL: server.URL,
})
// Test
ctx := context.Background()
identity, err := client.Identity.Get(ctx)
// Assert
assert.NoError(t, err)
assert.Equal(t, "user-123", identity.ID)
assert.Equal(t, "test@example.com", identity.Email)
}
func TestCreateTicket(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/v1/ticketing/tickets" && r.Method == "POST" {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"id":"ticket-123","title":"Test Ticket","status":"open"}`))
}
}))
defer server.Close()
client := unizo.NewClient(&unizo.Config{
APIKey: "test-api-key",
BaseURL: server.URL,
})
ctx := context.Background()
ticket, err := client.Ticketing.Tickets.Create(ctx, &unizo.CreateTicketRequest{
Title: "Test Ticket",
Description: "This is a test ticket",
})
assert.NoError(t, err)
assert.Equal(t, "ticket-123", ticket.ID)
assert.Equal(t, "open", string(ticket.Status))
}
Benchmark Testing
func BenchmarkGetIdentity(b *testing.B) {
client := unizo.NewClient(&unizo.Config{
APIKey: "test-api-key",
})
ctx := context.Background()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := client.Identity.Get(ctx)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkConcurrentRequests(b *testing.B) {
client := unizo.NewClient(&unizo.Config{
APIKey: "test-api-key",
})
ctx := context.Background()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, err := client.Identity.Get(ctx)
if err != nil {
b.Fatal(err)
}
}
})
}
Performance Optimization
Connection Pooling
import (
"net/http"
"time"
)
// Custom HTTP client with connection pooling
httpClient := &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
MaxIdleConns: 100,
MaxIdleConnsPerHost: 20,
IdleConnTimeout: 90 * time.Second,
},
}
client := unizo.NewClient(&unizo.Config{
APIKey: "your-api-key",
HTTPClient: httpClient,
})
Caching
import (
"sync"
"time"
)
type Cache struct {
data map[string]CacheEntry
mu sync.RWMutex
}
type CacheEntry struct {
Value interface{}
ExpiresAt time.Time
}
func NewCache() *Cache {
return &Cache{
data: make(map[string]CacheEntry),
}
}
func (c *Cache) Get(key string) (interface{}, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
entry, exists := c.data[key]
if !exists || time.Now().After(entry.ExpiresAt) {
return nil, false
}
return entry.Value, true
}
func (c *Cache) Set(key string, value interface{}, ttl time.Duration) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = CacheEntry{
Value: value,
ExpiresAt: time.Now().Add(ttl),
}
}
// Cached client wrapper
type CachedUnizoClient struct {
client *unizo.Client
cache *Cache
}
func NewCachedUnizoClient(client *unizo.Client) *CachedUnizoClient {
return &CachedUnizoClient{
client: client,
cache: NewCache(),
}
}
func (c *CachedUnizoClient) GetIdentity(ctx context.Context) (*unizo.Identity, error) {
cacheKey := "identity"
if cached, found := c.cache.Get(cacheKey); found {
return cached.(*unizo.Identity), nil
}
identity, err := c.client.Identity.Get(ctx)
if err != nil {
return nil, err
}
c.cache.Set(cacheKey, identity, 5*time.Minute)
return identity, nil
}
Batch Operations
// Batch ticket creation
func createTicketsBatch(ctx context.Context, client *unizo.Client, requests []unizo.CreateTicketRequest) ([]unizo.Ticket, error) {
const batchSize = 10
var allTickets []unizo.Ticket
for i := 0; i < len(requests); i += batchSize {
end := i + batchSize
if end > len(requests) {
end = len(requests)
}
batch := requests[i:end]
tickets, err := client.Ticketing.Tickets.CreateBatch(ctx, batch)
if err != nil {
return allTickets, err
}
allTickets = append(allTickets, tickets...)
}
return allTickets, nil
}
Logging and Monitoring
import (
"log/slog"
"os"
)
// Setup structured logging
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
}))
client := unizo.NewClient(&unizo.Config{
APIKey: "your-api-key",
Logger: logger,
Debug: true,
})
// Custom logging middleware
type LoggingMiddleware struct {
logger *slog.Logger
}
func NewLoggingMiddleware(logger *slog.Logger) *LoggingMiddleware {
return &LoggingMiddleware{logger: logger}
}
func (m *LoggingMiddleware) RoundTrip(req *http.Request) (*http.Response, error) {
start := time.Now()
m.logger.Info("Request started",
"method", req.Method,
"url", req.URL.String(),
)
resp, err := http.DefaultTransport.RoundTrip(req)
duration := time.Since(start)
if err != nil {
m.logger.Error("Request failed",
"method", req.Method,
"url", req.URL.String(),
"duration", duration,
"error", err,
)
} else {
m.logger.Info("Request completed",
"method", req.Method,
"url", req.URL.String(),
"status", resp.StatusCode,
"duration", duration,
)
}
return resp, err
}
// Use middleware
httpClient := &http.Client{
Transport: NewLoggingMiddleware(logger),
}
client := unizo.NewClient(&unizo.Config{
APIKey: "your-api-key",
HTTPClient: httpClient,
})
Resources
Support
- Create issues on GitHub
- Email: sdk-support@unizo.com
- Discord: Unizo Developers
Requirements
- Go 1.18+
- Go modules enabled
- Standard library packages (net/http, encoding/json, etc.)
- github.com/stretchr/testify (for testing, optional)