Files
kandev/apps/backend/internal/task/repository/memory.go
T
jcfs 20f5f43fb3 feat: complete acp-go-sdk integration with task state updates
- Use acp-go-sdk for all ACP communication in agentctl
- Mark agent as completed when Prompt() returns successfully
- Update task state to COMPLETED when agent completes
- Update task state to FAILED when agent fails and retries exhausted
- Enhanced E2E test to verify agent actually creates files
- E2E test now validates full flow: task creation -> agent work -> file creation -> task completion
2026-01-10 16:46:35 +00:00

272 lines
6.1 KiB
Go

package repository
import (
"context"
"fmt"
"sync"
"time"
"github.com/google/uuid"
"github.com/kandev/kandev/internal/task/models"
v1 "github.com/kandev/kandev/pkg/api/v1"
)
// MemoryRepository provides in-memory task storage operations
type MemoryRepository struct {
tasks map[string]*models.Task
boards map[string]*models.Board
columns map[string]*models.Column
mu sync.RWMutex
}
// Ensure MemoryRepository implements Repository interface
var _ Repository = (*MemoryRepository)(nil)
// NewMemoryRepository creates a new in-memory task repository
func NewMemoryRepository() *MemoryRepository {
return &MemoryRepository{
tasks: make(map[string]*models.Task),
boards: make(map[string]*models.Board),
columns: make(map[string]*models.Column),
}
}
// Close is a no-op for in-memory repository
func (r *MemoryRepository) Close() error {
return nil
}
// Task operations
// CreateTask creates a new task
func (r *MemoryRepository) CreateTask(ctx context.Context, task *models.Task) error {
r.mu.Lock()
defer r.mu.Unlock()
if task.ID == "" {
task.ID = uuid.New().String()
}
now := time.Now().UTC()
task.CreatedAt = now
task.UpdatedAt = now
r.tasks[task.ID] = task
return nil
}
// GetTask retrieves a task by ID
func (r *MemoryRepository) GetTask(ctx context.Context, id string) (*models.Task, error) {
r.mu.RLock()
defer r.mu.RUnlock()
task, ok := r.tasks[id]
if !ok {
return nil, fmt.Errorf("task not found: %s", id)
}
return task, nil
}
// UpdateTask updates an existing task
func (r *MemoryRepository) UpdateTask(ctx context.Context, task *models.Task) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.tasks[task.ID]; !ok {
return fmt.Errorf("task not found: %s", task.ID)
}
task.UpdatedAt = time.Now().UTC()
r.tasks[task.ID] = task
return nil
}
// DeleteTask deletes a task by ID
func (r *MemoryRepository) DeleteTask(ctx context.Context, id string) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.tasks[id]; !ok {
return fmt.Errorf("task not found: %s", id)
}
delete(r.tasks, id)
return nil
}
// ListTasks returns all tasks for a board
func (r *MemoryRepository) ListTasks(ctx context.Context, boardID string) ([]*models.Task, error) {
r.mu.RLock()
defer r.mu.RUnlock()
var result []*models.Task
for _, task := range r.tasks {
if task.BoardID == boardID {
result = append(result, task)
}
}
return result, nil
}
// ListTasksByColumn returns all tasks in a column
func (r *MemoryRepository) ListTasksByColumn(ctx context.Context, columnID string) ([]*models.Task, error) {
r.mu.RLock()
defer r.mu.RUnlock()
var result []*models.Task
for _, task := range r.tasks {
if task.ColumnID == columnID {
result = append(result, task)
}
}
return result, nil
}
// UpdateTaskState updates the state of a task
func (r *MemoryRepository) UpdateTaskState(ctx context.Context, id string, state v1.TaskState) error {
r.mu.Lock()
defer r.mu.Unlock()
task, ok := r.tasks[id]
if !ok {
return fmt.Errorf("task not found: %s", id)
}
task.State = state
task.UpdatedAt = time.Now().UTC()
return nil
}
// Board operations
// CreateBoard creates a new board
func (r *MemoryRepository) CreateBoard(ctx context.Context, board *models.Board) error {
r.mu.Lock()
defer r.mu.Unlock()
if board.ID == "" {
board.ID = uuid.New().String()
}
now := time.Now().UTC()
board.CreatedAt = now
board.UpdatedAt = now
r.boards[board.ID] = board
return nil
}
// GetBoard retrieves a board by ID
func (r *MemoryRepository) GetBoard(ctx context.Context, id string) (*models.Board, error) {
r.mu.RLock()
defer r.mu.RUnlock()
board, ok := r.boards[id]
if !ok {
return nil, fmt.Errorf("board not found: %s", id)
}
return board, nil
}
// UpdateBoard updates an existing board
func (r *MemoryRepository) UpdateBoard(ctx context.Context, board *models.Board) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.boards[board.ID]; !ok {
return fmt.Errorf("board not found: %s", board.ID)
}
board.UpdatedAt = time.Now().UTC()
r.boards[board.ID] = board
return nil
}
// DeleteBoard deletes a board by ID
func (r *MemoryRepository) DeleteBoard(ctx context.Context, id string) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.boards[id]; !ok {
return fmt.Errorf("board not found: %s", id)
}
delete(r.boards, id)
return nil
}
// ListBoards returns all boards
func (r *MemoryRepository) ListBoards(ctx context.Context) ([]*models.Board, error) {
r.mu.RLock()
defer r.mu.RUnlock()
result := make([]*models.Board, 0, len(r.boards))
for _, board := range r.boards {
result = append(result, board)
}
return result, nil
}
// Column operations
// CreateColumn creates a new column
func (r *MemoryRepository) CreateColumn(ctx context.Context, column *models.Column) error {
r.mu.Lock()
defer r.mu.Unlock()
if column.ID == "" {
column.ID = uuid.New().String()
}
now := time.Now().UTC()
column.CreatedAt = now
column.UpdatedAt = now
r.columns[column.ID] = column
return nil
}
// GetColumn retrieves a column by ID
func (r *MemoryRepository) GetColumn(ctx context.Context, id string) (*models.Column, error) {
r.mu.RLock()
defer r.mu.RUnlock()
column, ok := r.columns[id]
if !ok {
return nil, fmt.Errorf("column not found: %s", id)
}
return column, nil
}
// UpdateColumn updates an existing column
func (r *MemoryRepository) UpdateColumn(ctx context.Context, column *models.Column) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.columns[column.ID]; !ok {
return fmt.Errorf("column not found: %s", column.ID)
}
column.UpdatedAt = time.Now().UTC()
r.columns[column.ID] = column
return nil
}
// DeleteColumn deletes a column by ID
func (r *MemoryRepository) DeleteColumn(ctx context.Context, id string) error {
r.mu.Lock()
defer r.mu.Unlock()
if _, ok := r.columns[id]; !ok {
return fmt.Errorf("column not found: %s", id)
}
delete(r.columns, id)
return nil
}
// ListColumns returns all columns for a board
func (r *MemoryRepository) ListColumns(ctx context.Context, boardID string) ([]*models.Column, error) {
r.mu.RLock()
defer r.mu.RUnlock()
var result []*models.Column
for _, column := range r.columns {
if column.BoardID == boardID {
result = append(result, column)
}
}
return result, nil
}