# Skybridge FaaS Client

A lightweight Go client library for interacting with the Skybridge Function-as-a-Service (FaaS) platform.

## Installation

```bash
go get github.com/RyanCopley/skybridge/faas-client
```

## Usage

### Basic Client Setup

```go
package main

import (
    "context"
    "log"
    
    faasclient "github.com/RyanCopley/skybridge/faas-client"
)

func main() {
    // Create a new client
    client := faasclient.NewClient(
        "http://localhost:8080", // FaaS API base URL
        faasclient.WithUserEmail("admin@example.com"), // Authentication
    )
    
    // Use the client...
}
```

### Authentication Options

```go
// Using user email header (for development)
client := faasclient.NewClient(baseURL, 
    faasclient.WithUserEmail("user@example.com"))

// Using custom auth headers
client := faasclient.NewClient(baseURL,
    faasclient.WithAuth(map[string]string{
        "Authorization": "Bearer " + token,
        "X-User-Email":  "user@example.com",
    }))

// Using custom HTTP client
httpClient := &http.Client{Timeout: 30 * time.Second}
client := faasclient.NewClient(baseURL,
    faasclient.WithHTTPClient(httpClient))
```

### Function Management

#### Creating a Function

```go
req := &faasclient.CreateFunctionRequest{
    Name:    "my-function",
    AppID:   "my-app",
    Runtime: faasclient.RuntimeNodeJS18,
    Image:   "node:18-alpine", // Optional, auto-selected if not provided
    Handler: "index.handler",
    Code:    "exports.handler = async (event) => { return 'Hello World'; }",
    Environment: map[string]string{
        "NODE_ENV": "production",
    },
    Timeout: faasclient.Duration(30 * time.Second),
    Memory:  512,
    Owner: faasclient.Owner{
        Type:  faasclient.OwnerTypeIndividual,
        Name:  "John Doe",
        Owner: "john@example.com",
    },
}

function, err := client.CreateFunction(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Printf("Created function: %s (ID: %s)", function.Name, function.ID)
```

#### Getting a Function

```go
function, err := client.GetFunction(context.Background(), functionID)
if err != nil {
    log.Fatal(err)
}

log.Printf("Function: %s, Runtime: %s", function.Name, function.Runtime)
```

#### Updating a Function

```go
newTimeout := faasclient.Duration(60 * time.Second)
req := &faasclient.UpdateFunctionRequest{
    Timeout: &newTimeout,
    Environment: map[string]string{
        "NODE_ENV": "development",
    },
}

function, err := client.UpdateFunction(context.Background(), functionID, req)
if err != nil {
    log.Fatal(err)
}
```

#### Listing Functions

```go
response, err := client.ListFunctions(context.Background(), "my-app", 50, 0)
if err != nil {
    log.Fatal(err)
}

for _, fn := range response.Functions {
    log.Printf("Function: %s (ID: %s)", fn.Name, fn.ID)
}
```

#### Deleting a Function

```go
err := client.DeleteFunction(context.Background(), functionID)
if err != nil {
    log.Fatal(err)
}
```

### Function Deployment

```go
// Deploy with default options
resp, err := client.DeployFunction(context.Background(), functionID, nil)
if err != nil {
    log.Fatal(err)
}

// Force deployment
req := &faasclient.DeployFunctionRequest{
    Force: true,
}
resp, err = client.DeployFunction(context.Background(), functionID, req)
if err != nil {
    log.Fatal(err)
}

log.Printf("Deployment status: %s", resp.Status)
```

### Function Execution

#### Synchronous Execution

```go
input := json.RawMessage(`{"name": "World"}`)
req := &faasclient.ExecuteFunctionRequest{
    FunctionID: functionID,
    Input:      input,
    Async:      false,
}

response, err := client.ExecuteFunction(context.Background(), req)
if err != nil {
    log.Fatal(err)
}

log.Printf("Result: %s", string(response.Output))
log.Printf("Duration: %v", response.Duration)
```

#### Asynchronous Execution

```go
input := json.RawMessage(`{"name": "World"}`)
response, err := client.InvokeFunction(context.Background(), functionID, input)
if err != nil {
    log.Fatal(err)
}

log.Printf("Execution ID: %s", response.ExecutionID)
log.Printf("Status: %s", response.Status)
```

### Execution Management

#### Getting Execution Details

```go
execution, err := client.GetExecution(context.Background(), executionID)
if err != nil {
    log.Fatal(err)
}

log.Printf("Status: %s", execution.Status)
log.Printf("Duration: %v", execution.Duration)
if execution.Error != "" {
    log.Printf("Error: %s", execution.Error)
}
```

#### Listing Executions

```go
// List all executions
response, err := client.ListExecutions(context.Background(), nil, 50, 0)
if err != nil {
    log.Fatal(err)
}

// List executions for a specific function
response, err = client.ListExecutions(context.Background(), &functionID, 50, 0)
if err != nil {
    log.Fatal(err)
}

for _, exec := range response.Executions {
    log.Printf("Execution: %s, Status: %s", exec.ID, exec.Status)
}
```

#### Canceling an Execution

```go
err := client.CancelExecution(context.Background(), executionID)
if err != nil {
    log.Fatal(err)
}
```

#### Getting Execution Logs

```go
logs, err := client.GetExecutionLogs(context.Background(), executionID)
if err != nil {
    log.Fatal(err)
}

for _, logLine := range logs.Logs {
    log.Printf("Log: %s", logLine)
}
```

#### Getting Running Executions

```go
response, err := client.GetRunningExecutions(context.Background())
if err != nil {
    log.Fatal(err)
}

log.Printf("Running executions: %d", response.Count)
for _, exec := range response.Executions {
    log.Printf("Running: %s (Function: %s)", exec.ID, exec.FunctionID)
}
```

## Types

The client provides comprehensive type definitions that match the FaaS API:

- `FunctionDefinition` - Complete function metadata
- `FunctionExecution` - Execution details and results
- `RuntimeType` - Supported runtimes (NodeJS18, Python39, Go120, Custom)
- `ExecutionStatus` - Execution states (Pending, Running, Completed, Failed, etc.)
- `Owner` - Ownership information
- Request/Response types for all operations

## Error Handling

The client provides detailed error messages that include HTTP status codes and response bodies:

```go
function, err := client.GetFunction(ctx, nonExistentID)
if err != nil {
    // Error will include status code and details
    log.Printf("Error: %v", err) // "get function failed with status 404: Function not found"
}
```

## Architecture Benefits

This client package is designed as a lightweight, standalone library that:

- **No heavy dependencies**: Only requires `google/uuid` 
- **Zero coupling**: Doesn't import the entire FaaS service
- **Modular**: Can be used by any service in your monolith
- **Type-safe**: Comprehensive Go types for all API operations
- **Flexible auth**: Supports multiple authentication methods