Go SDK

Official SDK for Go 1.21+

Go ReferenceGitHub stars

Installation

go get github.com/qodryx/sdk-go

Requirements

  • Go 1.21 or higher

Quick Start

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/qodryx/sdk-go"
)

func main() {
    // Initialize the client
    client := qodryx.NewClient(os.Getenv("QODRYX_API_KEY"))

    ctx := context.Background()

    // List projects
    projects, err := client.Projects.List(ctx, nil)
    if err != nil {
        log.Fatal(err)
    }

    for _, project := range projects.Data {
        fmt.Printf("%s: %s\n", project.Name, project.ID)
    }

    // Trigger a security scan
    scan, err := client.Security.Scan(ctx, &qodryx.ScanOptions{
        ProjectID: "proj_abc123",
        Types:     []string{"sast", "secrets", "dependencies"},
    })
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Scan started: %s\n", scan.ID)
}

Configuration

package main

import (
    "net/http"
    "time"

    "github.com/qodryx/sdk-go"
)

func main() {
    // Basic configuration
    client := qodryx.NewClient(os.Getenv("QODRYX_API_KEY"))

    // With options
    client := qodryx.NewClient(
        os.Getenv("QODRYX_API_KEY"),
        qodryx.WithBaseURL("https://api.qodryx.com"),
        qodryx.WithTimeout(60*time.Second),
        qodryx.WithRetries(3),
        qodryx.WithHTTPClient(&http.Client{
            Timeout: 30 * time.Second,
        }),
        qodryx.WithHeaders(map[string]string{
            "X-Custom-Header": "value",
        }),
    )
}

API Reference

Projects

ctx := context.Background()

// List all projects
projects, err := client.Projects.List(ctx, &qodryx.ListOptions{
    Page:    1,
    PerPage: 20,
})

// Get a specific project
project, err := client.Projects.Get(ctx, "proj_abc123")

// Create a new project
newProject, err := client.Projects.Create(ctx, &qodryx.CreateProjectOptions{
    Name:        "my-app",
    Repository:  "github.com/user/my-app",
    Description: "My awesome app",
})

// Update a project
updated, err := client.Projects.Update(ctx, "proj_abc123", &qodryx.UpdateProjectOptions{
    Name: "new-name",
})

// Delete a project
err := client.Projects.Delete(ctx, "proj_abc123")

Workflows

ctx := context.Background()

// Create and trigger a workflow
workflow, err := client.Workflows.Create(ctx, &qodryx.CreateWorkflowOptions{
    ProjectID: "proj_abc123",
    Prompt:    "Add OAuth authentication with Google",
    Config: &qodryx.WorkflowConfig{
        AIProvider: "anthropic",
        AutoDeploy: false,
    },
})

// List workflows
workflows, err := client.Workflows.List(ctx, &qodryx.ListWorkflowsOptions{
    ProjectID: "proj_abc123",
    Status:    "running",
})

// Get workflow status
status, err := client.Workflows.Get(ctx, "wf_xyz789")

// Get workflow logs
logs, err := client.Workflows.Logs(ctx, "wf_xyz789", &qodryx.LogsOptions{
    Stage: "build",
})

// Cancel a workflow
err := client.Workflows.Cancel(ctx, "wf_xyz789")

// Wait for workflow completion
result, err := client.Workflows.WaitForCompletion(ctx, "wf_xyz789", &qodryx.WaitOptions{
    Timeout:      5 * time.Minute,
    PollInterval: 5 * time.Second,
})

Security

ctx := context.Background()

// Trigger a security scan
scan, err := client.Security.Scan(ctx, &qodryx.ScanOptions{
    ProjectID: "proj_abc123",
    Types:     []string{"sast", "secrets", "dependencies"},
    Branch:    "main",
})

// Get scan results
results, err := client.Security.Results(ctx, "scan_abc123")

// List all findings
findings, err := client.Security.Findings(ctx, &qodryx.FindingsOptions{
    ProjectID: "proj_abc123",
    Severity:  []string{"critical", "high"},
    Status:    "open",
})

// Get specific finding
finding, err := client.Security.Finding(ctx, "finding_abc123")

// Auto-remediate a finding
remediation, err := client.Security.Remediate(ctx, "finding_abc123")

// Ignore a finding
err := client.Security.Ignore(ctx, "finding_abc123", &qodryx.IgnoreOptions{
    Reason: "False positive - test file",
})

Deployments

ctx := context.Background()

// Trigger a deployment
deployment, err := client.Deployments.Create(ctx, &qodryx.CreateDeploymentOptions{
    ProjectID:   "proj_abc123",
    Environment: "production",
    Provider:    "vercel",
    Branch:      "main",
})

// Get deployment status
status, err := client.Deployments.Get(ctx, "deploy_abc123")

// List deployments
deployments, err := client.Deployments.List(ctx, &qodryx.ListDeploymentsOptions{
    ProjectID:   "proj_abc123",
    Environment: "production",
})

// Rollback a deployment
err := client.Deployments.Rollback(ctx, "deploy_abc123")

// Get deployment logs
logs, err := client.Deployments.Logs(ctx, "deploy_abc123")

Tests

ctx := context.Background()

// Generate tests for a file
tests, err := client.Tests.Generate(ctx, &qodryx.GenerateTestsOptions{
    ProjectID: "proj_abc123",
    FilePath:  "pkg/auth/login.go",
    Framework: "testing",
})

// Run tests
results, err := client.Tests.Run(ctx, &qodryx.RunTestsOptions{
    ProjectID: "proj_abc123",
    Coverage:  true,
})

// Get test coverage
coverage, err := client.Tests.Coverage(ctx, "proj_abc123")

Error Handling

import (
    "errors"
    "github.com/qodryx/sdk-go"
)

ctx := context.Background()

scan, err := client.Security.Scan(ctx, &qodryx.ScanOptions{
    ProjectID: "proj_abc123",
})

if err != nil {
    var apiErr *qodryx.APIError
    if errors.As(err, &apiErr) {
        switch apiErr.Code {
        case qodryx.ErrAuthentication:
            log.Println("Invalid API key")
        case qodryx.ErrRateLimit:
            log.Printf("Rate limit exceeded, retry after %d seconds", apiErr.RetryAfter)
        case qodryx.ErrNotFound:
            log.Println("Project not found")
        case qodryx.ErrValidation:
            log.Printf("Validation error: %v", apiErr.Errors)
        default:
            log.Printf("API error: %s - %s", apiErr.Code, apiErr.Message)
        }
        return
    }
    log.Fatal(err)
}

Context and Cancellation

import (
    "context"
    "time"
)

// With timeout
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

projects, err := client.Projects.List(ctx, nil)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        log.Println("Request timed out")
        return
    }
    log.Fatal(err)
}

// With cancellation
ctx, cancel := context.WithCancel(context.Background())

go func() {
    // Cancel after some condition
    time.Sleep(10 * time.Second)
    cancel()
}()

result, err := client.Workflows.WaitForCompletion(ctx, "wf_xyz789", nil)
if errors.Is(err, context.Canceled) {
    log.Println("Operation was cancelled")
}

Pagination

ctx := context.Background()

// Manual pagination
page := 1
for {
    projects, err := client.Projects.List(ctx, &qodryx.ListOptions{
        Page:    page,
        PerPage: 50,
    })
    if err != nil {
        log.Fatal(err)
    }

    for _, project := range projects.Data {
        fmt.Println(project.Name)
    }

    if page >= projects.Meta.TotalPages {
        break
    }
    page++
}

// Using iterator
iter := client.Projects.ListAll(ctx)
for iter.Next() {
    project := iter.Current()
    fmt.Println(project.Name)
}
if err := iter.Err(); err != nil {
    log.Fatal(err)
}

Webhooks

package main

import (
    "io"
    "log"
    "net/http"

    "github.com/qodryx/sdk-go/webhook"
)

func main() {
    secret := os.Getenv("WEBHOOK_SECRET")

    http.HandleFunc("/webhooks/qodryx", func(w http.ResponseWriter, r *http.Request) {
        signature := r.Header.Get("X-Qodryx-Signature")
        
        body, err := io.ReadAll(r.Body)
        if err != nil {
            http.Error(w, "Bad request", http.StatusBadRequest)
            return
        }

        // Verify signature
        if !webhook.Verify(body, signature, secret) {
            http.Error(w, "Invalid signature", http.StatusUnauthorized)
            return
        }

        // Parse event
        event, err := webhook.Parse(body)
        if err != nil {
            http.Error(w, "Invalid payload", http.StatusBadRequest)
            return
        }

        switch event.Type {
        case webhook.WorkflowCompleted:
            data := event.Data.(*webhook.WorkflowCompletedData)
            log.Printf("Workflow completed: %s", data.WorkflowID)
        
        case webhook.SecurityFindingCreated:
            data := event.Data.(*webhook.SecurityFindingData)
            log.Printf("New vulnerability: %s", data.Severity)
        }

        w.WriteHeader(http.StatusOK)
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}

Concurrency

import (
    "context"
    "sync"

    "golang.org/x/sync/errgroup"
)

ctx := context.Background()

// Scan multiple projects concurrently
projectIDs := []string{"proj_1", "proj_2", "proj_3"}

g, ctx := errgroup.WithContext(ctx)
results := make([]*qodryx.ScanResult, len(projectIDs))
var mu sync.Mutex

for i, id := range projectIDs {
    i, id := i, id // Capture loop variables
    g.Go(func() error {
        scan, err := client.Security.Scan(ctx, &qodryx.ScanOptions{
            ProjectID: id,
        })
        if err != nil {
            return err
        }

        mu.Lock()
        results[i] = scan
        mu.Unlock()
        return nil
    })
}

if err := g.Wait(); err != nil {
    log.Fatal(err)
}

for _, result := range results {
    fmt.Printf("Scan started: %s\n", result.ID)
}

Complete Example

package main

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

    "github.com/qodryx/sdk-go"
)

func runSecurityCheck(projectID string) error {
    client := qodryx.NewClient(os.Getenv("QODRYX_API_KEY"))
    ctx := context.Background()

    fmt.Println("Starting security scan...")

    // Trigger scan
    scan, err := client.Security.Scan(ctx, &qodryx.ScanOptions{
        ProjectID: projectID,
        Types:     []string{"sast", "secrets", "dependencies"},
    })
    if err != nil {
        return fmt.Errorf("failed to start scan: %w", err)
    }
    fmt.Printf("Scan started: %s\n", scan.ID)

    // Wait for completion
    for {
        results, err := client.Security.Results(ctx, scan.ID)
        if err != nil {
            return fmt.Errorf("failed to get results: %w", err)
        }

        switch results.Status {
        case "completed":
            fmt.Println("Scan completed!")

            // Check for critical findings
            var critical []qodryx.Finding
            for _, f := range results.Findings {
                if f.Severity == "critical" {
                    critical = append(critical, f)
                }
            }

            if len(critical) > 0 {
                fmt.Printf("❌ Found %d critical vulnerabilities:\n", len(critical))
                for _, f := range critical {
                    fmt.Printf("  - %s (%s:%d)\n", f.Title, f.File, f.Line)
                }
                return fmt.Errorf("critical vulnerabilities found")
            }

            fmt.Println("✅ No critical vulnerabilities found")
            return nil

        case "failed":
            return fmt.Errorf("scan failed")
        }

        fmt.Printf("Scan in progress... (%s)\n", results.Status)
        time.Sleep(5 * time.Second)
    }
}

func main() {
    projectID := os.Getenv("QODRYX_PROJECT")
    if len(os.Args) > 1 {
        projectID = os.Args[1]
    }

    if projectID == "" {
        log.Fatal("Usage: go run main.go <project_id>")
    }

    if err := runSecurityCheck(projectID); err != nil {
        log.Fatal(err)
    }
}

Next Steps