Skip to content

apito-io/go-internal-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

13 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Go Apito SDK

Go Reference Go Report Card License: MIT

A comprehensive Go SDK for communicating with Apito GraphQL API endpoints. This SDK implements the InjectedDBOperationInterface and provides both type-safe and flexible interfaces for interacting with Apito's backend services.

πŸš€ Features

  • βœ… Complete SDK Implementation: Full implementation of InjectedDBOperationInterface
  • βœ… Type-Safe Operations: Generic typed methods for better development experience
  • βœ… GraphQL-Based: Native GraphQL communication with Apito backend
  • βœ… Authentication Ready: API key and tenant-based authentication
  • βœ… Context-Aware: Full context support with timeout and cancellation
  • βœ… Comprehensive Error Handling: Detailed error responses and GraphQL error support
  • βœ… Plugin-Ready: Perfect for HashiCorp Go plugins and microservices
  • βœ… Production Ready: Battle-tested in production environments

πŸ“¦ Installation

go get github.com/apito-io/go-internal-sdk

🎯 Quick Start

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    goapitosdk "github.com/apito-io/go-internal-sdk"
)

func main() {
    // Create a new client
    client := goapitosdk.NewClient(goapitosdk.Config{
        BaseURL: "https://api.apito.io/graphql",
        APIKey:  "your-api-key-here",
        Timeout: 30 * time.Second,
    })

    ctx := context.Background()

    // Create a new todo
    todoData := map[string]interface{}{
        "title":       "Learn Apito SDK",
        "description": "Complete the SDK tutorial",
        "status":      "todo",
        "priority":    "high",
    }

    request := &goapitosdk.CreateAndUpdateRequest{
        Model:   "todos",
        Payload: todoData,
    }

    todo, err := client.CreateNewResource(ctx, request)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Created todo: %s\n", todo.ID)
}

βš™οΈ Configuration

Basic Configuration

client := goapitosdk.NewClient(goapitosdk.Config{
    BaseURL: "https://api.apito.io/graphql",  // Your Apito GraphQL endpoint
    APIKey:  "your-api-key-here",             // X-APITO-KEY header value
    Timeout: 30 * time.Second,                // HTTP client timeout
})

Advanced Configuration

// Custom HTTP client with specific settings
customClient := &http.Client{
    Timeout: 60 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
    },
}

client := goapitosdk.NewClient(goapitosdk.Config{
    BaseURL:    "https://api.apito.io/graphql",
    APIKey:     "your-api-key-here",
    HTTPClient: customClient,
})

Context with Tenant ID

ctx := context.Background()
ctx = context.WithValue(ctx, "tenant_id", "your-tenant-id")

// All operations will now include the tenant ID
results, err := client.SearchResources(ctx, "users", filter, false)

πŸ“š Complete API Reference

πŸ” Authentication

Generate Tenant Token

Generate a new tenant token for multi-tenant operations:

tenantToken, err := client.GenerateTenantToken(ctx, "auth-token", "tenant-id")
if err != nil {
    log.Fatal(err)
}
fmt.Println("Generated token:", tenantToken)

πŸ“ Resource Management

Create New Resource

Untyped Creation:

request := &goapitosdk.CreateAndUpdateRequest{
    Model: "users",
    Payload: map[string]interface{}{
        "name":   "John Doe",
        "email":  "[email protected]",
        "active": true,
    },
    Connect: map[string]interface{}{
        "organization_id": "org-123",
    },
}

user, err := client.CreateNewResource(ctx, request)

Type-Safe Creation:

type User struct {
    ID     string `json:"id"`
    Name   string `json:"name"`
    Email  string `json:"email"`
    Active bool   `json:"active"`
}

typedUser, err := goapitosdk.CreateNewResourceTyped[User](client, ctx, request)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Created user: %s (%s)\n", typedUser.Data.Name, typedUser.Data.Email)

Update Resource

updateRequest := &goapitosdk.CreateAndUpdateRequest{
    ID:    "user-123",
    Model: "users",
    Payload: map[string]interface{}{
        "name": "Jane Doe Updated",
    },
    Connect: map[string]interface{}{
        "role_id": "role-456",
    },
    Disconnect: map[string]interface{}{
        "old_role_id": "role-123",
    },
    ForceUpdate: false,
}

updatedUser, err := client.UpdateResource(ctx, updateRequest)

Delete Resource

err := client.DeleteResource(ctx, "users", "user-123")
if err != nil {
    log.Fatal(err)
}

πŸ” Search & Retrieval

Search Resources

Basic Search:

filter := map[string]interface{}{
    "limit": 10,
    "page":  1,
    "where": map[string]interface{}{
        "status": "active",
        "role":   "admin",
    },
    "search": "[email protected]",
}

results, err := client.SearchResources(ctx, "users", filter, false)

Type-Safe Search:

typedResults, err := goapitosdk.SearchResourcesTyped[User](client, ctx, "users", filter, false)
if err != nil {
    log.Fatal(err)
}

for _, userDoc := range typedResults.Results {
    fmt.Printf("User: %s (%s)\n", userDoc.Data.Name, userDoc.Data.Email)
}

Advanced Filtering:

advancedFilter := map[string]interface{}{
    "limit":  20,
    "offset": 10,
    "where": map[string]interface{}{
        "created_at": map[string]interface{}{
            "$gte": "2024-01-01T00:00:00Z",
        },
        "status": map[string]interface{}{
            "$in": []string{"active", "pending"},
        },
    },
    "sort": map[string]interface{}{
        "created_at": -1, // Descending order
    },
}

results, err := client.SearchResources(ctx, "users", advancedFilter, false)

Get Single Resource

Untyped Retrieval:

user, err := client.GetSingleResource(ctx, "users", "user-123", false)
if err != nil {
    log.Fatal(err)
}

Type-Safe Retrieval:

typedUser, err := goapitosdk.GetSingleResourceTyped[User](client, ctx, "users", "user-123", false)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("User: %s\n", typedUser.Data.Name)

Get Related Documents

relationConnection := map[string]interface{}{
    "model": "todos",
    "filter": map[string]interface{}{
        "limit": 10,
        "where": map[string]interface{}{
            "status": "pending",
        },
    },
}

// Get todos related to a user
relatedTodos, err := client.GetRelationDocuments(ctx, "user-123", relationConnection)
if err != nil {
    log.Fatal(err)
}

// Type-safe version
typedTodos, err := goapitosdk.GetRelationDocumentsTyped[Todo](client, ctx, "user-123", relationConnection)

πŸ“Š Audit & Debug

Send Audit Log

auditData := goapitosdk.AuditData{
    Resource: "users",
    Action:   "create",
    Author: map[string]interface{}{
        "user_id": "admin-123",
        "name":    "Admin User",
    },
    Data: map[string]interface{}{
        "user_id": "user-456",
        "email":   "[email protected]",
    },
    Meta: map[string]interface{}{
        "ip_address": "192.168.1.1",
        "user_agent": "Apito-SDK/1.0",
        "timestamp":  time.Now().Format(time.RFC3339),
    },
}

err := client.SendAuditLog(ctx, auditData)
if err != nil {
    log.Fatal(err)
}

Debug Operations

debugData := map[string]interface{}{
    "operation": "user_creation",
    "duration":  "150ms",
    "success":   true,
}

result, err := client.Debug(ctx, "user_management", debugData)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Debug result: %+v\n", result)

🎯 Complete Todo Example

The SDK includes a comprehensive todo application example that demonstrates all features:

# Set environment variables
export APITO_BASE_URL="https://api.apito.io/graphql"
export APITO_API_KEY="your-api-key"
export APITO_TENANT_ID="your-tenant-id"  # Optional
export APITO_AUTH_TOKEN="your-auth-token"  # Optional for token generation

# Run the example
cd examples/basic
go run main.go

The example demonstrates:

  • πŸ” Authentication & tenant token generation
  • πŸ“ Creating resources (todos, users, categories)
  • πŸ” Searching with both typed and untyped methods
  • πŸ“„ Getting single resources
  • ✏️ Updating resources
  • πŸ”— Getting related documents
  • πŸ“Š Audit logging
  • πŸ› Debug functionality
  • πŸ—‘οΈ Resource cleanup

πŸ—οΈ Type System

Defining Custom Types

// Define your data structures
type Todo struct {
    ID          string    `json:"id"`
    Title       string    `json:"title"`
    Description string    `json:"description"`
    Status      string    `json:"status"`
    Priority    string    `json:"priority"`
    DueDate     time.Time `json:"due_date"`
    CreatedAt   time.Time `json:"created_at"`
    UpdatedAt   time.Time `json:"updated_at"`
}

type User struct {
    ID       string `json:"id"`
    Name     string `json:"name"`
    Email    string `json:"email"`
    Role     string `json:"role"`
    Active   bool   `json:"active"`
}

Type-Safe Operations

All operations have type-safe counterparts:

// Type-safe alternatives
GetSingleResourceTyped[T](client, ctx, model, id, singlePageData)
SearchResourcesTyped[T](client, ctx, model, filter, aggregate)
GetRelationDocumentsTyped[T](client, ctx, id, connection)
CreateNewResourceTyped[T](client, ctx, request)
UpdateResourceTyped[T](client, ctx, request)

πŸ”Œ Plugin Integration

HashiCorp Go Plugin Usage

// In your plugin
type MyPlugin struct {
    client goapitosdk.InjectedDBOperationInterface
}

func (p *MyPlugin) Initialize(client goapitosdk.InjectedDBOperationInterface) {
    p.client = client
}

func (p *MyPlugin) ProcessData(ctx context.Context) error {
    // Use the client for database operations
    results, err := p.client.SearchResources(ctx, "data", filter, false)
    if err != nil {
        return err
    }

    // Process results...
    return nil
}

Microservice Integration

// In your microservice
type UserService struct {
    apitoClient *goapitosdk.Client
}

func NewUserService(config goapitosdk.Config) *UserService {
    return &UserService{
        apitoClient: goapitosdk.NewClient(config),
    }
}

func (s *UserService) CreateUser(ctx context.Context, userData User) (*User, error) {
    request := &goapitosdk.CreateAndUpdateRequest{
        Model:   "users",
        Payload: structToMap(userData),
    }

    result, err := goapitosdk.CreateNewResourceTyped[User](s.apitoClient, ctx, request)
    if err != nil {
        return nil, err
    }

    return &result.Data, nil
}

πŸ”§ Error Handling

GraphQL Errors

results, err := client.SearchResources(ctx, "users", filter, false)
if err != nil {
    // Check if it's a GraphQL error
    if graphqlErr, ok := err.(*goapitosdk.GraphQLError); ok {
        fmt.Printf("GraphQL Error: %s\n", graphqlErr.Message)
        fmt.Printf("Path: %v\n", graphqlErr.Path)
        fmt.Printf("Extensions: %v\n", graphqlErr.Extensions)
    } else {
        // Handle other errors (HTTP, network, etc.)
        fmt.Printf("Error: %v\n", err)
    }
}

HTTP Errors

// Handle HTTP-level errors
client := goapitosdk.NewClient(goapitosdk.Config{
    BaseURL: "https://api.apito.io/graphql",
    APIKey:  "invalid-key",
    Timeout: 5 * time.Second,
})

_, err := client.SearchResources(ctx, "users", nil, false)
if err != nil {
    if strings.Contains(err.Error(), "HTTP error 401") {
        fmt.Println("Authentication failed - check your API key")
    } else if strings.Contains(err.Error(), "HTTP error 403") {
        fmt.Println("Authorization failed - check your permissions")
    }
}

πŸ§ͺ Testing

Mock Client

// For testing, you can implement the interface
type MockClient struct{}

func (m *MockClient) SearchResources(ctx context.Context, model string, filter map[string]interface{}, aggregate bool) (*goapitosdk.SearchResult, error) {
    // Return mock data
    return &goapitosdk.SearchResult{
        Results: []*shared.DefaultDocumentStructure{
            {ID: "test-1", Data: map[string]interface{}{"name": "Test User"}},
        },
        Count: 1,
    }, nil
}

// Use in tests
func TestUserService(t *testing.T) {
    service := &UserService{apitoClient: &MockClient{}}
    // Test your service...
}

πŸ“ˆ Performance Tips

Connection Pooling

// Configure HTTP client for better performance
client := &http.Client{
    Transport: &http.Transport{
        MaxIdleConns:          100,
        MaxIdleConnsPerHost:   10,
        IdleConnTimeout:       90 * time.Second,
        TLSHandshakeTimeout:   10 * time.Second,
        ExpectContinueTimeout: 1 * time.Second,
    },
    Timeout: 30 * time.Second,
}

apitoClient := goapitosdk.NewClient(goapitosdk.Config{
    BaseURL:    "https://api.apito.io/graphql",
    APIKey:     "your-api-key",
    HTTPClient: client,
})

Batch Operations

// Instead of multiple individual requests, batch them
var wg sync.WaitGroup
results := make(chan *goapitosdk.SearchResult, 10)

for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(page int) {
        defer wg.Done()
        filter := map[string]interface{}{"page": page, "limit": 100}
        result, err := client.SearchResources(ctx, "users", filter, false)
        if err == nil {
            results <- result
        }
    }(i)
}

go func() {
    wg.Wait()
    close(results)
}()

// Process results as they come in
for result := range results {
    // Process each batch...
}

πŸš€ Production Deployment

Environment Variables

# Required
APITO_BASE_URL=https://api.apito.io/graphql
APITO_API_KEY=your-production-api-key

# Optional
APITO_TENANT_ID=your-tenant-id
APITO_AUTH_TOKEN=your-auth-token
APITO_TIMEOUT=30s

Docker Configuration

FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -o app main.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/app .
CMD ["./app"]

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: apito-sdk-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: apito-sdk-app
  template:
    metadata:
      labels:
        app: apito-sdk-app
    spec:
      containers:
        - name: app
          image: your-app:latest
          env:
            - name: APITO_BASE_URL
              value: "https://api.apito.io/graphql"
            - name: APITO_API_KEY
              valueFrom:
                secretKeyRef:
                  name: apito-secrets
                  key: api-key

🀝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

git clone https://github.com/apito-io/go-internal-sdk.git
cd go-apito-sdk
go mod download
go test ./...

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ”— Links

πŸ†˜ Support

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages