edge-connect-client/sdk/examples/deploy_app.go
Stephan Lo f3ac644813 feat: implement unified domain error handling system
Addresses Verbesserungspotential 2 (Error Handling uneinheitlich) by introducing
a comprehensive, structured error handling approach across all architectural layers.

## New Domain Error System
- Add ErrorCode enum with 15 semantic error types (NOT_FOUND, VALIDATION_FAILED, etc.)
- Implement DomainError struct with operation context, resource identifiers, and regions
- Create resource-specific error constructors (NewAppError, NewInstanceError, NewCloudletError)
- Add utility functions for error type checking (IsNotFoundError, IsValidationError, etc.)

## Service Layer Enhancements
- Replace generic fmt.Errorf with structured domain errors in all services
- Add comprehensive validation functions for App, AppInstance, and Cloudlet entities
- Implement business logic validation with meaningful error context
- Ensure consistent error semantics across app_service, instance_service, cloudlet_service

## Adapter Layer Updates
- Update EdgeConnect adapters to use domain errors instead of error constants
- Enhance CLI adapter with domain-specific error checking for better UX
- Fix SDK examples to use new IsNotFoundError() approach
- Maintain backward compatibility where possible

## Test Coverage
- Add comprehensive error_test.go with 100% coverage of new error system
- Update existing adapter tests to validate domain error types
- All tests passing with proper error type assertions

## Benefits
-  Consistent error handling across all architectural layers
-  Rich error context with operation, resource, and region information
-  Type-safe error checking with semantic error codes
-  Better user experience with domain-specific error messages
-  Maintainable centralized error definitions
-  Full hexagonal architecture compliance

Files modified: 12 files updated, 2 new files added
Tests: All passing (29+ test cases with enhanced error validation)
2025-10-08 16:52:36 +02:00

165 lines
5.3 KiB
Go

// ABOUTME: Example demonstrating EdgeXR SDK usage for app deployment workflow
// ABOUTME: Shows app creation, querying, and cleanup using the typed SDK APIs
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"time"
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/adapters/edgeconnect"
"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/domain"
)
func main() {
// Configure SDK client
baseURL := getEnvOrDefault("EDGEXR_BASE_URL", "https://hub.apps.edge.platform.mg3.mdb.osc.live")
// Support both token-based and username/password authentication
token := getEnvOrDefault("EDGEXR_TOKEN", "")
username := getEnvOrDefault("EDGEXR_USERNAME", "")
password := getEnvOrDefault("EDGEXR_PASSWORD", "")
var edgeClient *edgeconnect.Client
if token != "" {
// Use static token authentication
fmt.Println("🔐 Using Bearer token authentication")
edgeClient = edgeconnect.NewClient(baseURL,
edgeconnect.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
edgeconnect.WithAuthProvider(edgeconnect.NewStaticTokenProvider(token)),
edgeconnect.WithLogger(log.Default()),
)
} else if username != "" && password != "" {
// Use username/password authentication (matches existing client pattern)
fmt.Println("🔐 Using username/password authentication")
edgeClient = edgeconnect.NewClientWithCredentials(baseURL, username, password,
edgeconnect.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
edgeconnect.WithLogger(log.Default()),
)
} else {
log.Fatal("Authentication required: Set either EDGEXR_TOKEN or both EDGEXR_USERNAME and EDGEXR_PASSWORD")
}
ctx := context.Background()
// Example application to deploy
app := &edgeconnect.NewAppInput{
Region: "EU",
App: edgeconnect.App{
Key: edgeconnect.AppKey{
Organization: "edp2",
Name: "my-edge-app",
Version: "1.0.0",
},
Deployment: "docker",
ImageType: "ImageTypeDocker",
ImagePath: "https://registry-1.docker.io/library/nginx:latest",
DefaultFlavor: edgeconnect.Flavor{Name: "EU.small"},
ServerlessConfig: struct{}{},
AllowServerless: false,
},
}
// Demonstrate app lifecycle
if err := demonstrateAppLifecycle(ctx, edgeClient, app); err != nil {
log.Fatalf("App lifecycle demonstration failed: %v", err)
}
fmt.Println("✅ SDK example completed successfully!")
}
func demonstrateAppLifecycle(ctx context.Context, edgeClient *edgeconnect.Client, input *edgeconnect.NewAppInput) error {
appKey := input.App.Key
region := input.Region
var domainAppKey domain.AppKey
fmt.Printf("🚀 Demonstrating EdgeXR SDK with app: %s/%s v%s\n",
appKey.Organization, appKey.Name, appKey.Version)
// Step 1: Create the application
fmt.Println("\n1. Creating application...")
domainApp := &domain.App{
Key: domain.AppKey{
Organization: input.App.Key.Organization,
Name: input.App.Key.Name,
Version: input.App.Key.Version,
},
Deployment: input.App.Deployment,
ImageType: input.App.ImageType,
ImagePath: input.App.ImagePath,
DefaultFlavor: domain.Flavor{Name: input.App.DefaultFlavor.Name},
ServerlessConfig: input.App.ServerlessConfig,
AllowServerless: input.App.AllowServerless,
}
if err := edgeClient.CreateApp(ctx, input.Region, domainApp); err != nil {
return fmt.Errorf("failed to create app: %+v", err)
}
fmt.Printf("✅ App created: %s/%s v%s\n", appKey.Organization, appKey.Name, appKey.Version)
// Step 2: Query the application
fmt.Println("\n2. Querying application...")
domainAppKey = domain.AppKey{
Organization: appKey.Organization,
Name: appKey.Name,
Version: appKey.Version,
}
app, err := edgeClient.ShowApp(ctx, region, domainAppKey)
if err != nil {
return fmt.Errorf("failed to show app: %w", err)
}
fmt.Printf("✅ App found: %s/%s v%s (deployment: %s)\n",
app.Key.Organization, app.Key.Name, app.Key.Version, app.Deployment)
// Step 3: List applications in the organization
fmt.Println("\n3. Listing applications...")
filter := domain.AppKey{Organization: appKey.Organization}
apps, err := edgeClient.ShowApps(ctx, region, filter)
if err != nil {
return fmt.Errorf("failed to list apps: %w", err)
}
fmt.Printf("✅ Found %d applications in organization '%s'\n", len(apps), appKey.Organization)
// Step 4: Clean up - delete the application
fmt.Println("\n4. Cleaning up...")
domainAppKey = domain.AppKey{
Organization: appKey.Organization,
Name: appKey.Name,
Version: appKey.Version,
}
if err := edgeClient.DeleteApp(ctx, region, domainAppKey); err != nil {
return fmt.Errorf("failed to delete app: %w", err)
}
fmt.Printf("✅ App deleted: %s/%s v%s\n", appKey.Organization, appKey.Name, appKey.Version)
// Step 5: Verify deletion
fmt.Println("\n5. Verifying deletion...")
domainAppKey = domain.AppKey{
Organization: appKey.Organization,
Name: appKey.Name,
Version: appKey.Version,
}
_, err = edgeClient.ShowApp(ctx, region, domainAppKey)
if err != nil {
if domain.IsNotFoundError(err) {
fmt.Printf("✅ App successfully deleted (not found)\n")
} else {
return fmt.Errorf("unexpected error verifying deletion: %w", err)
}
} else {
return fmt.Errorf("app still exists after deletion")
}
return nil
}
func getEnvOrDefault(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}