Weeks 1-7 of the template upgrade plan: - pkg/api: typed HTTPError with sentinels, Wrap/WrapMiddleware, Bind, health probes, OpenAPI schema/param builders - skeleton/packages: ui (design tokens, components), layout (DashboardShell), auth (AuthProvider, ProtectedRoute), api-client - skeleton/pkg: httperror, app/handler, app/bind, app/health, auth (JWT/API key middleware) - components/app-nextjs: Next.js 14 App Router template with dashboard, server actions, auth - cookbooks/feature-development.md with test and validation scripts - Handler tests for components, project management, and woodpecker webhook - 3 rounds of code review fixes applied Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
147 lines
3.4 KiB
Go
147 lines
3.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/orchard9/rdev/internal/domain"
|
|
)
|
|
|
|
// mockOperationRepo is a mock implementation of port.OperationRepository for testing.
|
|
type mockOperationRepo struct {
|
|
mu sync.Mutex
|
|
operations map[string]*domain.Operation
|
|
}
|
|
|
|
func newMockOperationRepo() *mockOperationRepo {
|
|
return &mockOperationRepo{
|
|
operations: make(map[string]*domain.Operation),
|
|
}
|
|
}
|
|
|
|
func (m *mockOperationRepo) Create(_ context.Context, op *domain.Operation) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.operations[op.ID] = op
|
|
return nil
|
|
}
|
|
|
|
func (m *mockOperationRepo) Update(_ context.Context, op *domain.Operation) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.operations[op.ID] = op
|
|
return nil
|
|
}
|
|
|
|
func (m *mockOperationRepo) Get(_ context.Context, id string) (*domain.Operation, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
op, ok := m.operations[id]
|
|
if !ok {
|
|
return nil, domain.ErrOperationNotFound
|
|
}
|
|
return op, nil
|
|
}
|
|
|
|
func (m *mockOperationRepo) GetByCommitSHA(_ context.Context, projectID, sha string) (*domain.Operation, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
for _, op := range m.operations {
|
|
if op.ProjectID == projectID && op.CommitSHA == sha {
|
|
return op, nil
|
|
}
|
|
}
|
|
return nil, domain.ErrOperationNotFound
|
|
}
|
|
|
|
func (m *mockOperationRepo) List(_ context.Context, filter domain.OperationFilters) ([]*domain.Operation, error) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
var result []*domain.Operation
|
|
for _, op := range m.operations {
|
|
if filter.ProjectID != "" && op.ProjectID != filter.ProjectID {
|
|
continue
|
|
}
|
|
result = append(result, op)
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (m *mockOperationRepo) AddStep(_ context.Context, operationID string, step domain.OperationStep) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
op, ok := m.operations[operationID]
|
|
if !ok {
|
|
return domain.ErrOperationNotFound
|
|
}
|
|
op.Steps = append(op.Steps, step)
|
|
return nil
|
|
}
|
|
|
|
func (m *mockOperationRepo) UpdateStep(_ context.Context, operationID string, step domain.OperationStep) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
op, ok := m.operations[operationID]
|
|
if !ok {
|
|
return domain.ErrOperationNotFound
|
|
}
|
|
for i, s := range op.Steps {
|
|
if s.Name == step.Name {
|
|
op.Steps[i] = step
|
|
return nil
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *mockOperationRepo) Complete(_ context.Context, operationID string, status domain.OperationStatus, output map[string]any, errMsg, errDetail string) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
op, ok := m.operations[operationID]
|
|
if !ok {
|
|
return domain.ErrOperationNotFound
|
|
}
|
|
op.Status = status
|
|
now := time.Now()
|
|
op.CompletedAt = &now
|
|
op.DurationMs = now.Sub(op.StartedAt).Milliseconds()
|
|
op.Output = output
|
|
op.Error = errMsg
|
|
op.ErrorDetail = errDetail
|
|
return nil
|
|
}
|
|
|
|
func (m *mockOperationRepo) SetCommitSHA(_ context.Context, operationID, sha string) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
op, ok := m.operations[operationID]
|
|
if !ok {
|
|
return domain.ErrOperationNotFound
|
|
}
|
|
op.CommitSHA = sha
|
|
return nil
|
|
}
|
|
|
|
func (m *mockOperationRepo) SetTriggeredBy(_ context.Context, operationID, parentID string) error {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
op, ok := m.operations[operationID]
|
|
if !ok {
|
|
return domain.ErrOperationNotFound
|
|
}
|
|
op.TriggeredBy = parentID
|
|
return nil
|
|
}
|
|
|
|
func (m *mockOperationRepo) DeleteOlderThan(_ context.Context, _ time.Time) (int64, error) {
|
|
return 0, nil
|
|
}
|
|
|
|
// count returns the number of operations stored.
|
|
func (m *mockOperationRepo) count() int {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
return len(m.operations)
|
|
}
|