268 lines
8.3 KiB
Go
268 lines
8.3 KiB
Go
package postgres
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/google/uuid"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/RyanCopley/skybridge/faas/internal/domain"
|
|
"github.com/RyanCopley/skybridge/faas/internal/repository"
|
|
)
|
|
|
|
type functionRepository struct {
|
|
db *sql.DB
|
|
logger *zap.Logger
|
|
}
|
|
|
|
func NewFunctionRepository(db *sql.DB, logger *zap.Logger) repository.FunctionRepository {
|
|
return &functionRepository{
|
|
db: db,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
func (r *functionRepository) Create(ctx context.Context, function *domain.FunctionDefinition) (*domain.FunctionDefinition, error) {
|
|
envJSON, err := json.Marshal(function.Environment)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal environment: %w", err)
|
|
}
|
|
|
|
query := `
|
|
INSERT INTO functions (id, name, app_id, runtime, image, handler, code, environment, timeout, memory,
|
|
owner_type, owner_name, owner_owner, created_at, updated_at)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
|
RETURNING created_at, updated_at`
|
|
|
|
timeoutValue, _ := function.Timeout.Value()
|
|
err = r.db.QueryRowContext(ctx, query,
|
|
function.ID, function.Name, function.AppID, function.Runtime, function.Image,
|
|
function.Handler, function.Code, envJSON, timeoutValue,
|
|
function.Memory, function.Owner.Type, function.Owner.Name, function.Owner.Owner,
|
|
function.CreatedAt, function.UpdatedAt,
|
|
).Scan(&function.CreatedAt, &function.UpdatedAt)
|
|
|
|
if err != nil {
|
|
r.logger.Error("Failed to create function", zap.Error(err))
|
|
return nil, fmt.Errorf("failed to create function: %w", err)
|
|
}
|
|
|
|
return function, nil
|
|
}
|
|
|
|
func (r *functionRepository) GetByID(ctx context.Context, id uuid.UUID) (*domain.FunctionDefinition, error) {
|
|
query := `
|
|
SELECT id, name, app_id, runtime, image, handler, code, environment, timeout, memory,
|
|
owner_type, owner_name, owner_owner, created_at, updated_at
|
|
FROM functions WHERE id = $1`
|
|
|
|
function := &domain.FunctionDefinition{}
|
|
var envJSON []byte
|
|
|
|
err := r.db.QueryRowContext(ctx, query, id).Scan(
|
|
&function.ID, &function.Name, &function.AppID, &function.Runtime, &function.Image,
|
|
&function.Handler, &function.Code, &envJSON, &function.Timeout, &function.Memory,
|
|
&function.Owner.Type, &function.Owner.Name, &function.Owner.Owner,
|
|
&function.CreatedAt, &function.UpdatedAt,
|
|
)
|
|
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("function not found")
|
|
}
|
|
r.logger.Error("Failed to get function by ID", zap.String("id", id.String()), zap.Error(err))
|
|
return nil, fmt.Errorf("failed to get function: %w", err)
|
|
}
|
|
|
|
// Unmarshal environment
|
|
if err := json.Unmarshal(envJSON, &function.Environment); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal environment: %w", err)
|
|
}
|
|
|
|
return function, nil
|
|
}
|
|
|
|
func (r *functionRepository) GetByName(ctx context.Context, appID, name string) (*domain.FunctionDefinition, error) {
|
|
query := `
|
|
SELECT id, name, app_id, runtime, image, handler, code, environment, timeout, memory,
|
|
owner_type, owner_name, owner_owner, created_at, updated_at
|
|
FROM functions WHERE app_id = $1 AND name = $2`
|
|
|
|
function := &domain.FunctionDefinition{}
|
|
var envJSON []byte
|
|
|
|
err := r.db.QueryRowContext(ctx, query, appID, name).Scan(
|
|
&function.ID, &function.Name, &function.AppID, &function.Runtime, &function.Image,
|
|
&function.Handler, &function.Code, &envJSON, &function.Timeout, &function.Memory,
|
|
&function.Owner.Type, &function.Owner.Name, &function.Owner.Owner,
|
|
&function.CreatedAt, &function.UpdatedAt,
|
|
)
|
|
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, fmt.Errorf("function not found")
|
|
}
|
|
r.logger.Error("Failed to get function by name", zap.String("app_id", appID), zap.String("name", name), zap.Error(err))
|
|
return nil, fmt.Errorf("failed to get function: %w", err)
|
|
}
|
|
|
|
// Unmarshal environment
|
|
if err := json.Unmarshal(envJSON, &function.Environment); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal environment: %w", err)
|
|
}
|
|
|
|
return function, nil
|
|
}
|
|
|
|
func (r *functionRepository) Update(ctx context.Context, id uuid.UUID, updates *domain.UpdateFunctionRequest) (*domain.FunctionDefinition, error) {
|
|
// First get the current function
|
|
current, err := r.GetByID(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Apply updates
|
|
if updates.Name != nil {
|
|
current.Name = *updates.Name
|
|
}
|
|
if updates.Runtime != nil {
|
|
current.Runtime = *updates.Runtime
|
|
}
|
|
if updates.Image != nil {
|
|
current.Image = *updates.Image
|
|
}
|
|
if updates.Handler != nil {
|
|
current.Handler = *updates.Handler
|
|
}
|
|
if updates.Code != nil {
|
|
current.Code = *updates.Code
|
|
}
|
|
if updates.Environment != nil {
|
|
current.Environment = updates.Environment
|
|
}
|
|
if updates.Timeout != nil {
|
|
current.Timeout = *updates.Timeout
|
|
}
|
|
if updates.Memory != nil {
|
|
current.Memory = *updates.Memory
|
|
}
|
|
if updates.Owner != nil {
|
|
current.Owner = *updates.Owner
|
|
}
|
|
|
|
// Marshal environment
|
|
envJSON, err := json.Marshal(current.Environment)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal environment: %w", err)
|
|
}
|
|
|
|
query := `
|
|
UPDATE functions
|
|
SET name = $2, runtime = $3, image = $4, handler = $5, code = $6, environment = $7,
|
|
timeout = $8, memory = $9, owner_type = $10, owner_name = $11, owner_owner = $12,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = $1
|
|
RETURNING updated_at`
|
|
|
|
timeoutValue, _ := current.Timeout.Value()
|
|
err = r.db.QueryRowContext(ctx, query,
|
|
id, current.Name, current.Runtime, current.Image, current.Handler,
|
|
current.Code, envJSON, timeoutValue, current.Memory,
|
|
current.Owner.Type, current.Owner.Name, current.Owner.Owner,
|
|
).Scan(¤t.UpdatedAt)
|
|
|
|
if err != nil {
|
|
r.logger.Error("Failed to update function", zap.String("id", id.String()), zap.Error(err))
|
|
return nil, fmt.Errorf("failed to update function: %w", err)
|
|
}
|
|
|
|
return current, nil
|
|
}
|
|
|
|
func (r *functionRepository) Delete(ctx context.Context, id uuid.UUID) error {
|
|
query := `DELETE FROM functions WHERE id = $1`
|
|
|
|
result, err := r.db.ExecContext(ctx, query, id)
|
|
if err != nil {
|
|
r.logger.Error("Failed to delete function", zap.String("id", id.String()), zap.Error(err))
|
|
return fmt.Errorf("failed to delete function: %w", err)
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get affected rows: %w", err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return fmt.Errorf("function not found")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *functionRepository) List(ctx context.Context, appID string, limit, offset int) ([]*domain.FunctionDefinition, error) {
|
|
var query string
|
|
var args []interface{}
|
|
|
|
if appID != "" {
|
|
query = `
|
|
SELECT id, name, app_id, runtime, image, handler, code, environment, timeout, memory,
|
|
owner_type, owner_name, owner_owner, created_at, updated_at
|
|
FROM functions WHERE app_id = $1
|
|
ORDER BY created_at DESC LIMIT $2 OFFSET $3`
|
|
args = []interface{}{appID, limit, offset}
|
|
} else {
|
|
query = `
|
|
SELECT id, name, app_id, runtime, image, handler, code, environment, timeout, memory,
|
|
owner_type, owner_name, owner_owner, created_at, updated_at
|
|
FROM functions
|
|
ORDER BY created_at DESC LIMIT $1 OFFSET $2`
|
|
args = []interface{}{limit, offset}
|
|
}
|
|
|
|
rows, err := r.db.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
r.logger.Error("Failed to list functions", zap.Error(err))
|
|
return nil, fmt.Errorf("failed to list functions: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var functions []*domain.FunctionDefinition
|
|
for rows.Next() {
|
|
function := &domain.FunctionDefinition{}
|
|
var envJSON []byte
|
|
|
|
err := rows.Scan(
|
|
&function.ID, &function.Name, &function.AppID, &function.Runtime, &function.Image,
|
|
&function.Handler, &function.Code, &envJSON, &function.Timeout, &function.Memory,
|
|
&function.Owner.Type, &function.Owner.Name, &function.Owner.Owner,
|
|
&function.CreatedAt, &function.UpdatedAt,
|
|
)
|
|
|
|
if err != nil {
|
|
r.logger.Error("Failed to scan function", zap.Error(err))
|
|
return nil, fmt.Errorf("failed to scan function: %w", err)
|
|
}
|
|
|
|
// Unmarshal environment
|
|
if err := json.Unmarshal(envJSON, &function.Environment); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal environment: %w", err)
|
|
}
|
|
|
|
functions = append(functions, function)
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("failed to iterate functions: %w", err)
|
|
}
|
|
|
|
return functions, nil
|
|
}
|
|
|
|
func (r *functionRepository) GetByAppID(ctx context.Context, appID string) ([]*domain.FunctionDefinition, error) {
|
|
return r.List(ctx, appID, 1000, 0) // Get all functions for the app
|
|
}
|