# SDK Documentation

This document provides comprehensive documentation for the official SDKs and client libraries available for provisioning.

## Available SDKs

Provisioning provides SDKs in multiple languages to facilitate integration:

### Official SDKs

- **Python SDK** (`provisioning-client`) - Full-featured Python client
- **JavaScript/TypeScript SDK** (`@provisioning/client`) - Node.js and browser support
- **Go SDK** (`go-provisioning-client`) - Go client library
- **Rust SDK** (`provisioning-rs`) - Native Rust integration

### Community SDKs

- **Java SDK** - Community-maintained Java client
- **C# SDK** - .NET client library
- **PHP SDK** - PHP client library

## Python SDK

### Installation

```bash
# Install from PyPI
pip install provisioning-client

# Or install development version
pip install git+https://github.com/provisioning-systems/python-client.git
```

### Quick Start

```python
from provisioning_client import ProvisioningClient
import asyncio

async def main():
    # Initialize client
    client = ProvisioningClient(
        base_url="http://localhost:9090",
        auth_url="http://localhost:8081",
        username="admin",
        password="your-password"
    )

    try:
        # Authenticate
        token = await client.authenticate()
        print(f"Authenticated with token: {token[:20]}...")

        # Create a server workflow
        task_id = client.create_server_workflow(
            infra="production",
            settings="prod-settings.ncl",
            wait=False
        )
        print(f"Server workflow created: {task_id}")

        # Wait for completion
        task = client.wait_for_task_completion(task_id, timeout=600)
        print(f"Task completed with status: {task.status}")

        if task.status == "Completed":
            print(f"Output: {task.output}")
        elif task.status == "Failed":
            print(f"Error: {task.error}")

    except Exception as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    asyncio.run(main())
```

### Advanced Usage

#### WebSocket Integration

```python
async def monitor_workflows():
    client = ProvisioningClient()
    await client.authenticate()

    # Set up event handlers
    async def on_task_update(event):
        print(f"Task {event['data']['task_id']} status: {event['data']['status']}")

    async def on_progress_update(event):
        print(f"Progress: {event['data']['progress']}% - {event['data']['current_step']}")

    client.on_event('TaskStatusChanged', on_task_update)
    client.on_event('WorkflowProgressUpdate', on_progress_update)

    # Connect to WebSocket
    await client.connect_websocket(['TaskStatusChanged', 'WorkflowProgressUpdate'])

    # Keep connection alive
    await asyncio.sleep(3600)  # Monitor for 1 hour
```

#### Batch Operations

```python
async def execute_batch_deployment():
    client = ProvisioningClient()
    await client.authenticate()

    batch_config = {
        "name": "production_deployment",
        "version": "1.0.0",
        "storage_backend": "surrealdb",
        "parallel_limit": 5,
        "rollback_enabled": True,
        "operations": [
            {
                "id": "servers",
                "type": "server_batch",
                "provider": "upcloud",
                "dependencies": [],
                "config": {
                    "server_configs": [
                        {"name": "web-01", "plan": "2xCPU-4 GB", "zone": "de-fra1"},
                        {"name": "web-02", "plan": "2xCPU-4 GB", "zone": "de-fra1"}
                    ]
                }
            },
            {
                "id": "kubernetes",
                "type": "taskserv_batch",
                "provider": "upcloud",
                "dependencies": ["servers"],
                "config": {
                    "taskservs": ["kubernetes", "cilium", "containerd"]
                }
            }
        ]
    }

    # Execute batch operation
    batch_result = await client.execute_batch_operation(batch_config)
    print(f"Batch operation started: {batch_result['batch_id']}")

    # Monitor progress
    while True:
        status = await client.get_batch_status(batch_result['batch_id'])
        print(f"Batch status: {status['status']} - {status.get('progress', 0)}%")

        if status['status'] in ['Completed', 'Failed', 'Cancelled']:
            break

        await asyncio.sleep(10)

    print(f"Batch operation finished: {status['status']}")
```

#### Error Handling with Retries

```python
from provisioning_client.exceptions import (
    ProvisioningAPIError,
    AuthenticationError,
    ValidationError,
    RateLimitError
)
from tenacity import retry, stop_after_attempt, wait_exponential

class RobustProvisioningClient(ProvisioningClient):
    @retry(
        stop=stop_after_attempt(3),
        wait=wait_exponential(multiplier=1, min=4, max=10)
    )
    async def create_server_workflow_with_retry(self, **kwargs):
        try:
            return await self.create_server_workflow(**kwargs)
        except RateLimitError as e:
            print(f"Rate limited, retrying in {e.retry_after} seconds...")
            await asyncio.sleep(e.retry_after)
            raise
        except AuthenticationError:
            print("Authentication failed, re-authenticating...")
            await self.authenticate()
            raise
        except ValidationError as e:
            print(f"Validation error: {e}")
            # Don't retry validation errors
            raise
        except ProvisioningAPIError as e:
            print(f"API error: {e}")
            raise

# Usage
async def robust_workflow():
    client = RobustProvisioningClient()

    try:
        task_id = await client.create_server_workflow_with_retry(
            infra="production",
            settings="config.ncl"
        )
        print(f"Workflow created successfully: {task_id}")
    except Exception as e:
        print(f"Failed after retries: {e}")
```

### API Reference

#### ProvisioningClient Class

```python
class ProvisioningClient:
    def __init__(self,
                 base_url: str = "http://localhost:9090",
                 auth_url: str = "http://localhost:8081",
                 username: str = None,
                 password: str = None,
                 token: str = None):
        """Initialize the provisioning client"""

    async def authenticate(self) -> str:
        """Authenticate and get JWT token"""

    def create_server_workflow(self,
                             infra: str,
                             settings: str = "config.ncl",
                             check_mode: bool = False,
                             wait: bool = False) -> str:
        """Create a server provisioning workflow"""

    def create_taskserv_workflow(self,
                               operation: str,
                               taskserv: str,
                               infra: str,
                               settings: str = "config.ncl",
                               check_mode: bool = False,
                               wait: bool = False) -> str:
        """Create a task service workflow"""

    def get_task_status(self, task_id: str) -> WorkflowTask:
        """Get the status of a specific task"""

    def wait_for_task_completion(self,
                               task_id: str,
                               timeout: int = 300,
                               poll_interval: int = 5) -> WorkflowTask:
        """Wait for a task to complete"""

    async def connect_websocket(self, event_types: List[str] = None):
        """Connect to WebSocket for real-time updates"""

    def on_event(self, event_type: str, handler: Callable):
        """Register an event handler"""
```

## JavaScript/TypeScript SDK

### Installation

```bash
# npm
npm install @provisioning/client

# yarn
yarn add @provisioning/client

# pnpm
pnpm add @provisioning/client
```

### Quick Start

```typescript
import { ProvisioningClient } from '@provisioning/client';

async function main() {
  const client = new ProvisioningClient({
    baseUrl: 'http://localhost:9090',
    authUrl: 'http://localhost:8081',
    username: 'admin',
    password: 'your-password'
  });

  try {
    // Authenticate
    await client.authenticate();
    console.log('Authentication successful');

    // Create server workflow
    const taskId = await client.createServerWorkflow({
      infra: 'production',
      settings: 'prod-settings.ncl'
    });
    console.log(`Server workflow created: ${taskId}`);

    // Wait for completion
    const task = await client.waitForTaskCompletion(taskId);
    console.log(`Task completed with status: ${task.status}`);

  } catch (error) {
    console.error('Error:', error.message);
  }
}

main();
```

### React Integration

```tsx
import React, { useState, useEffect } from 'react';
import { ProvisioningClient } from '@provisioning/client';

interface Task {
  id: string;
  name: string;
  status: string;
  progress?: number;
}

const WorkflowDashboard: React.FC = () => {
  const [client] = useState(() => new ProvisioningClient({
    baseUrl: process.env.REACT_APP_API_URL,
    username: process.env.REACT_APP_USERNAME,
    password: process.env.REACT_APP_PASSWORD
  }));

  const [tasks, setTasks] = useState<Task[]>([]);
  const [connected, setConnected] = useState(false);

  useEffect(() => {
    const initClient = async () => {
      try {
        await client.authenticate();

        // Set up WebSocket event handlers
        client.on('TaskStatusChanged', (event: any) => {
          setTasks(prev => prev.map(task =>
            task.id === event.data.task_id
              ? { ...task, status: event.data.status, progress: event.data.progress }
              : task
          ));
        });

        client.on('websocketConnected', () => {
          setConnected(true);
        });

        client.on('websocketDisconnected', () => {
          setConnected(false);
        });

        // Connect WebSocket
        await client.connectWebSocket(['TaskStatusChanged', 'WorkflowProgressUpdate']);

        // Load initial tasks
        const initialTasks = await client.listTasks();
        setTasks(initialTasks);

      } catch (error) {
        console.error('Failed to initialize client:', error);
      }
    };

    initClient();

    return () => {
      client.disconnectWebSocket();
    };
  }, [client]);

  const createServerWorkflow = async () => {
    try {
      const taskId = await client.createServerWorkflow({
        infra: 'production',
        settings: 'config.ncl'
      });

      // Add to tasks list
      setTasks(prev => [...prev, {
        id: taskId,
        name: 'Server Creation',
        status: 'Pending'
      }]);

    } catch (error) {
      console.error('Failed to create workflow:', error);
    }
  };

  return (
    <div className="workflow-dashboard">
      <div className="header">
        <h1>Workflow Dashboard</h1>
        <div className={`connection-status ${connected ? 'connected' : 'disconnected'}`}>
          {connected ? '🟢 Connected' : '🔴 Disconnected'}
        </div>
      </div>

      <div className="controls">
        <button onClick={createServerWorkflow}>
          Create Server Workflow
        </button>
      </div>

      <div className="tasks">
        {tasks.map(task => (
          <div key={task.id} className="task-card">
            <h3>{task.name}</h3>
            <div className="task-status">
              <span className={`status ${task.status.toLowerCase()}`}>
                {task.status}
              </span>
              {task.progress && (
                <div className="progress-bar">
                  <div
                    className="progress-fill"
                    style={{ width: `${task.progress}%` }}
                  />
                  <span className="progress-text">{task.progress}%</span>
                </div>
              )}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default WorkflowDashboard;
```

### Node.js CLI Tool

```typescript
#!/usr/bin/env node

import { Command } from 'commander';
import { ProvisioningClient } from '@provisioning/client';
import chalk from 'chalk';
import ora from 'ora';

const program = new Command();

program
  .name('provisioning-cli')
  .description('CLI tool for provisioning')
  .version('1.0.0');

program
  .command('create-server')
  .description('Create a server workflow')
  .requiredOption('-i, --infra <infra>', 'Infrastructure target')
  .option('-s, --settings <settings>', 'Settings file', 'config.ncl')
  .option('-c, --check', 'Check mode only')
  .option('-w, --wait', 'Wait for completion')
  .action(async (options) => {
    const client = new ProvisioningClient({
      baseUrl: process.env.PROVISIONING_API_URL,
      username: process.env.PROVISIONING_USERNAME,
      password: process.env.PROVISIONING_PASSWORD
    });

    const spinner = ora('Authenticating...').start();

    try {
      await client.authenticate();
      spinner.text = 'Creating server workflow...';

      const taskId = await client.createServerWorkflow({
        infra: options.infra,
        settings: options.settings,
        check_mode: options.check,
        wait: false
      });

      spinner.succeed(`Server workflow created: ${chalk.green(taskId)}`);

      if (options.wait) {
        spinner.start('Waiting for completion...');

        // Set up progress updates
        client.on('TaskStatusChanged', (event: any) => {
          if (event.data.task_id === taskId) {
            spinner.text = `Status: ${event.data.status}`;
          }
        });

        client.on('WorkflowProgressUpdate', (event: any) => {
          if (event.data.workflow_id === taskId) {
            spinner.text = `${event.data.progress}% - ${event.data.current_step}`;
          }
        });

        await client.connectWebSocket(['TaskStatusChanged', 'WorkflowProgressUpdate']);

        const task = await client.waitForTaskCompletion(taskId);

        if (task.status === 'Completed') {
          spinner.succeed(chalk.green('Workflow completed successfully!'));
          if (task.output) {
            console.log(chalk.gray('Output:'), task.output);
          }
        } else {
          spinner.fail(chalk.red(`Workflow failed: ${task.error}`));
          process.exit(1);
        }
      }

    } catch (error) {
      spinner.fail(chalk.red(`Error: ${error.message}`));
      process.exit(1);
    }
  });

program
  .command('list-tasks')
  .description('List all tasks')
  .option('-s, --status <status>', 'Filter by status')
  .action(async (options) => {
    const client = new ProvisioningClient();

    try {
      await client.authenticate();
      const tasks = await client.listTasks(options.status);

      console.log(chalk.bold('Tasks:'));
      tasks.forEach(task => {
        const statusColor = task.status === 'Completed' ? 'green' :
                          task.status === 'Failed' ? 'red' :
                          task.status === 'Running' ? 'yellow' : 'gray';

        console.log(`  ${task.id} - ${task.name} [${chalk[statusColor](task.status)}]`);
      });

    } catch (error) {
      console.error(chalk.red(`Error: ${error.message}`));
      process.exit(1);
    }
  });

program
  .command('monitor')
  .description('Monitor workflows in real-time')
  .action(async () => {
    const client = new ProvisioningClient();

    try {
      await client.authenticate();

      console.log(chalk.bold('🔍 Monitoring workflows...'));
      console.log(chalk.gray('Press Ctrl+C to stop'));

      client.on('TaskStatusChanged', (event: any) => {
        const timestamp = new Date().toLocaleTimeString();
        const statusColor = event.data.status === 'Completed' ? 'green' :
                          event.data.status === 'Failed' ? 'red' :
                          event.data.status === 'Running' ? 'yellow' : 'gray';

        console.log(`[${chalk.gray(timestamp)}] Task ${event.data.task_id} → ${chalk[statusColor](event.data.status)}`);
      });

      client.on('WorkflowProgressUpdate', (event: any) => {
        const timestamp = new Date().toLocaleTimeString();
        console.log(`[${chalk.gray(timestamp)}] ${event.data.workflow_id}: ${event.data.progress}% - ${event.data.current_step}`);
      });

      await client.connectWebSocket(['TaskStatusChanged', 'WorkflowProgressUpdate']);

      // Keep the process running
      process.on('SIGINT', () => {
        console.log(chalk.yellow('\nStopping monitor...'));
        client.disconnectWebSocket();
        process.exit(0);
      });

      // Keep alive
      setInterval(() => {}, 1000);

    } catch (error) {
      console.error(chalk.red(`Error: ${error.message}`));
      process.exit(1);
    }
  });

program.parse();
```

### API Reference

```typescript
interface ProvisioningClientOptions {
  baseUrl?: string;
  authUrl?: string;
  username?: string;
  password?: string;
  token?: string;
}

class ProvisioningClient extends EventEmitter {
  constructor(options: ProvisioningClientOptions);

  async authenticate(): Promise<string>;

  async createServerWorkflow(config: {
    infra: string;
    settings?: string;
    check_mode?: boolean;
    wait?: boolean;
  }): Promise<string>;

  async createTaskservWorkflow(config: {
    operation: string;
    taskserv: string;
    infra: string;
    settings?: string;
    check_mode?: boolean;
    wait?: boolean;
  }): Promise<string>;

  async getTaskStatus(taskId: string): Promise<Task>;

  async listTasks(statusFilter?: string): Promise<Task[]>;

  async waitForTaskCompletion(
    taskId: string,
    timeout?: number,
    pollInterval?: number
  ): Promise<Task>;

  async connectWebSocket(eventTypes?: string[]): Promise<void>;

  disconnectWebSocket(): void;

  async executeBatchOperation(batchConfig: BatchConfig): Promise<any>;

  async getBatchStatus(batchId: string): Promise<any>;
}
```

## Go SDK

### Installation

```bash
go get github.com/provisioning-systems/go-client
```

### Quick Start

```go
package main

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

    "github.com/provisioning-systems/go-client"
)

func main() {
    // Initialize client
    client, err := provisioning.NewClient(&provisioning.Config{
        BaseURL:  "http://localhost:9090",
        AuthURL:  "http://localhost:8081",
        Username: "admin",
        Password: "your-password",
    })
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
    }

    ctx := context.Background()

    // Authenticate
    token, err := client.Authenticate(ctx)
    if err != nil {
        log.Fatalf("Authentication failed: %v", err)
    }
    fmt.Printf("Authenticated with token: %.20s...\n", token)

    // Create server workflow
    taskID, err := client.CreateServerWorkflow(ctx, &provisioning.CreateServerRequest{
        Infra:    "production",
        Settings: "prod-settings.ncl",
        Wait:     false,
    })
    if err != nil {
        log.Fatalf("Failed to create workflow: %v", err)
    }
    fmt.Printf("Server workflow created: %s\n", taskID)

    // Wait for completion
    task, err := client.WaitForTaskCompletion(ctx, taskID, 10*time.Minute)
    if err != nil {
        log.Fatalf("Failed to wait for completion: %v", err)
    }

    fmt.Printf("Task completed with status: %s\n", task.Status)
    if task.Status == "Completed" {
        fmt.Printf("Output: %s\n", task.Output)
    } else if task.Status == "Failed" {
        fmt.Printf("Error: %s\n", task.Error)
    }
}
```

### WebSocket Integration

```go
package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "os/signal"

    "github.com/provisioning-systems/go-client"
)

func main() {
    client, err := provisioning.NewClient(&provisioning.Config{
        BaseURL:  "http://localhost:9090",
        Username: "admin",
        Password: "password",
    })
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
    }

    ctx := context.Background()

    // Authenticate
    _, err = client.Authenticate(ctx)
    if err != nil {
        log.Fatalf("Authentication failed: %v", err)
    }

    // Set up WebSocket connection
    ws, err := client.ConnectWebSocket(ctx, []string{
        "TaskStatusChanged",
        "WorkflowProgressUpdate",
    })
    if err != nil {
        log.Fatalf("Failed to connect WebSocket: %v", err)
    }
    defer ws.Close()

    // Handle events
    go func() {
        for event := range ws.Events() {
            switch event.Type {
            case "TaskStatusChanged":
                fmt.Printf("Task %s status changed to: %s\n",
                    event.Data["task_id"], event.Data["status"])
            case "WorkflowProgressUpdate":
                fmt.Printf("Workflow progress: %v%% - %s\n",
                    event.Data["progress"], event.Data["current_step"])
            }
        }
    }()

    // Wait for interrupt
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    <-c

    fmt.Println("Shutting down...")
}
```

### HTTP Client with Retry Logic

```go
package main

import (
    "context"
    "fmt"
    "time"

    "github.com/provisioning-systems/go-client"
    "github.com/cenkalti/backoff/v4"
)

type ResilientClient struct {
    *provisioning.Client
}

func NewResilientClient(config *provisioning.Config) (*ResilientClient, error) {
    client, err := provisioning.NewClient(config)
    if err != nil {
        return nil, err
    }

    return &ResilientClient{Client: client}, nil
}

func (c *ResilientClient) CreateServerWorkflowWithRetry(
    ctx context.Context,
    req *provisioning.CreateServerRequest,
) (string, error) {
    var taskID string

    operation := func() error {
        var err error
        taskID, err = c.CreateServerWorkflow(ctx, req)

        // Don't retry validation errors
        if provisioning.IsValidationError(err) {
            return backoff.Permanent(err)
        }

        return err
    }

    exponentialBackoff := backoff.NewExponentialBackOff()
    exponentialBackoff.MaxElapsedTime = 5 * time.Minute

    err := backoff.Retry(operation, exponentialBackoff)
    if err != nil {
        return "", fmt.Errorf("failed after retries: %w", err)
    }

    return taskID, nil
}

func main() {
    client, err := NewResilientClient(&provisioning.Config{
        BaseURL:  "http://localhost:9090",
        Username: "admin",
        Password: "password",
    })
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
    }

    ctx := context.Background()

    // Authenticate with retry
    _, err = client.Authenticate(ctx)
    if err != nil {
        log.Fatalf("Authentication failed: %v", err)
    }

    // Create workflow with retry
    taskID, err := client.CreateServerWorkflowWithRetry(ctx, &provisioning.CreateServerRequest{
        Infra:    "production",
        Settings: "config.ncl",
    })
    if err != nil {
        log.Fatalf("Failed to create workflow: %v", err)
    }

    fmt.Printf("Workflow created successfully: %s\n", taskID)
}
```

## Rust SDK

### Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
provisioning-rs = "2.0.0"
tokio = { version = "1.0", features = ["full"] }
```

### Quick Start

```rust
use provisioning_rs::{ProvisioningClient, Config, CreateServerRequest};
use tokio;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize client
    let config = Config {
        base_url: "http://localhost:9090".to_string(),
        auth_url: Some("http://localhost:8081".to_string()),
        username: Some("admin".to_string()),
        password: Some("your-password".to_string()),
        token: None,
    };

    let mut client = ProvisioningClient::new(config);

    // Authenticate
    let token = client.authenticate().await?;
    println!("Authenticated with token: {}...", &token[..20]);

    // Create server workflow
    let request = CreateServerRequest {
        infra: "production".to_string(),
        settings: Some("prod-settings.ncl".to_string()),
        check_mode: false,
        wait: false,
    };

    let task_id = client.create_server_workflow(request).await?;
    println!("Server workflow created: {}", task_id);

    // Wait for completion
    let task = client.wait_for_task_completion(&task_id, std::time::Duration::from_secs(600)).await?;

    println!("Task completed with status: {:?}", task.status);
    match task.status {
        TaskStatus::Completed => {
            if let Some(output) = task.output {
                println!("Output: {}", output);
            }
        },
        TaskStatus::Failed => {
            if let Some(error) = task.error {
                println!("Error: {}", error);
            }
        },
        _ => {}
    }

    Ok(())
}
```

### WebSocket Integration

```rust
use provisioning_rs::{ProvisioningClient, Config, WebSocketEvent};
use futures_util::StreamExt;
use tokio;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = Config {
        base_url: "http://localhost:9090".to_string(),
        username: Some("admin".to_string()),
        password: Some("password".to_string()),
        ..Default::default()
    };

    let mut client = ProvisioningClient::new(config);

    // Authenticate
    client.authenticate().await?;

    // Connect WebSocket
    let mut ws = client.connect_websocket(vec![
        "TaskStatusChanged".to_string(),
        "WorkflowProgressUpdate".to_string(),
    ]).await?;

    // Handle events
    tokio::spawn(async move {
        while let Some(event) = ws.next().await {
            match event {
                Ok(WebSocketEvent::TaskStatusChanged { data }) => {
                    println!("Task {} status changed to: {}", data.task_id, data.status);
                },
                Ok(WebSocketEvent::WorkflowProgressUpdate { data }) => {
                    println!("Workflow progress: {}% - {}", data.progress, data.current_step);
                },
                Ok(WebSocketEvent::SystemHealthUpdate { data }) => {
                    println!("System health: {}", data.overall_status);
                },
                Err(e) => {
                    eprintln!("WebSocket error: {}", e);
                    break;
                }
            }
        }
    });

    // Keep the main thread alive
    tokio::signal::ctrl_c().await?;
    println!("Shutting down...");

    Ok(())
}
```

### Batch Operations

```rust
use provisioning_rs::{BatchOperationRequest, BatchOperation};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut client = ProvisioningClient::new(config);
    client.authenticate().await?;

    // Define batch operation
    let batch_request = BatchOperationRequest {
        name: "production_deployment".to_string(),
        version: "1.0.0".to_string(),
        storage_backend: "surrealdb".to_string(),
        parallel_limit: 5,
        rollback_enabled: true,
        operations: vec![
            BatchOperation {
                id: "servers".to_string(),
                operation_type: "server_batch".to_string(),
                provider: "upcloud".to_string(),
                dependencies: vec![],
                config: serde_json::json!({
                    "server_configs": [
                        {"name": "web-01", "plan": "2xCPU-4 GB", "zone": "de-fra1"},
                        {"name": "web-02", "plan": "2xCPU-4 GB", "zone": "de-fra1"}
                    ]
                }),
            },
            BatchOperation {
                id: "kubernetes".to_string(),
                operation_type: "taskserv_batch".to_string(),
                provider: "upcloud".to_string(),
                dependencies: vec!["servers".to_string()],
                config: serde_json::json!({
                    "taskservs": ["kubernetes", "cilium", "containerd"]
                }),
            },
        ],
    };

    // Execute batch operation
    let batch_result = client.execute_batch_operation(batch_request).await?;
    println!("Batch operation started: {}", batch_result.batch_id);

    // Monitor progress
    loop {
        let status = client.get_batch_status(&batch_result.batch_id).await?;
        println!("Batch status: {} - {}%", status.status, status.progress.unwrap_or(0.0));

        match status.status.as_str() {
            "Completed" | "Failed" | "Cancelled" => break,
            _ => tokio::time::sleep(std::time::Duration::from_secs(10)).await,
        }
    }

    Ok(())
}
```

## Best Practices

### Authentication and Security

1. **Token Management**: Store tokens securely and implement automatic refresh
2. **Environment Variables**: Use environment variables for credentials
3. **HTTPS**: Always use HTTPS in production environments
4. **Token Expiration**: Handle token expiration gracefully

### Error Handling

1. **Specific Exceptions**: Handle specific error types appropriately
2. **Retry Logic**: Implement exponential backoff for transient failures
3. **Circuit Breakers**: Use circuit breakers for resilient integrations
4. **Logging**: Log errors with appropriate context

### Performance Optimization

1. **Connection Pooling**: Reuse HTTP connections
2. **Async Operations**: Use asynchronous operations where possible
3. **Batch Operations**: Group related operations for efficiency
4. **Caching**: Cache frequently accessed data appropriately

### WebSocket Connections

1. **Reconnection**: Implement automatic reconnection with backoff
2. **Event Filtering**: Subscribe only to needed event types
3. **Error Handling**: Handle WebSocket errors gracefully
4. **Resource Cleanup**: Properly close WebSocket connections

### Testing

1. **Unit Tests**: Test SDK functionality with mocked responses
2. **Integration Tests**: Test against real API endpoints
3. **Error Scenarios**: Test error handling paths
4. **Load Testing**: Validate performance under load

This comprehensive SDK documentation provides developers with everything needed to integrate with provisioning using their preferred programming language, complete with examples, best practices, and detailed API references.
