210 lines
5.6 KiB
Go
210 lines
5.6 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
|
|
"inbox/internal/domain/lane"
|
|
)
|
|
|
|
func (s *Store) CreateLane(ctx context.Context, value lane.Record) (lane.Record, error) {
|
|
if err := value.Validate(); err != nil {
|
|
return lane.Record{}, err
|
|
}
|
|
if value.ID == "" {
|
|
id, err := s.newID("lane")
|
|
if err != nil {
|
|
return lane.Record{}, err
|
|
}
|
|
value.ID = id
|
|
}
|
|
now := s.now()
|
|
value.CreatedAt = coalesceString(value.CreatedAt, now)
|
|
value.UpdatedAt = now
|
|
|
|
if _, err := s.db.ExecContext(ctx, `
|
|
INSERT INTO lanes(
|
|
id, workspace_id, topic_id, name, slug, purpose, status, base_branch, branch_name, head_commit, worktree_path,
|
|
container_name, runtime_endpoint, created_by_role_name, result_summary_markdown, error_message,
|
|
created_at, updated_at, started_at, completed_at
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
`,
|
|
value.ID,
|
|
value.WorkspaceID,
|
|
value.TopicID,
|
|
value.Name,
|
|
value.Slug,
|
|
value.Purpose,
|
|
string(value.Status),
|
|
value.BaseBranch,
|
|
value.BranchName,
|
|
value.HeadCommit,
|
|
value.WorktreePath,
|
|
value.ContainerName,
|
|
value.RuntimeEndpoint,
|
|
value.CreatedByRoleName,
|
|
value.ResultSummaryMarkdown,
|
|
value.ErrorMessage,
|
|
value.CreatedAt,
|
|
value.UpdatedAt,
|
|
nullableString(value.StartedAt),
|
|
nullableString(value.CompletedAt),
|
|
); err != nil {
|
|
return lane.Record{}, fmt.Errorf("create lane: %w", err)
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
func (s *Store) GetLane(ctx context.Context, laneID string) (lane.Record, error) {
|
|
row := s.db.QueryRowContext(ctx, `
|
|
SELECT id, workspace_id, topic_id, name, slug, purpose, status, base_branch, branch_name, head_commit, worktree_path,
|
|
container_name, runtime_endpoint, created_by_role_name, result_summary_markdown, error_message,
|
|
created_at, updated_at, started_at, completed_at
|
|
FROM lanes
|
|
WHERE id = ?
|
|
`, laneID)
|
|
return scanLane(row)
|
|
}
|
|
|
|
func (s *Store) ListLanesByTopic(ctx context.Context, topicID string) ([]lane.Record, error) {
|
|
rows, err := s.db.QueryContext(ctx, `
|
|
SELECT id, workspace_id, topic_id, name, slug, purpose, status, base_branch, branch_name, head_commit, worktree_path,
|
|
container_name, runtime_endpoint, created_by_role_name, result_summary_markdown, error_message,
|
|
created_at, updated_at, started_at, completed_at
|
|
FROM lanes
|
|
WHERE topic_id = ?
|
|
ORDER BY created_at, id
|
|
`, topicID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list lanes by topic: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []lane.Record
|
|
for rows.Next() {
|
|
item, err := scanLane(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, item)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("iterate lanes by topic: %w", err)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *Store) ListLanesByWorkspace(ctx context.Context, workspaceID string) ([]lane.Record, error) {
|
|
rows, err := s.db.QueryContext(ctx, `
|
|
SELECT id, workspace_id, topic_id, name, slug, purpose, status, base_branch, branch_name, head_commit, worktree_path,
|
|
container_name, runtime_endpoint, created_by_role_name, result_summary_markdown, error_message,
|
|
created_at, updated_at, started_at, completed_at
|
|
FROM lanes
|
|
WHERE workspace_id = ?
|
|
ORDER BY created_at, id
|
|
`, workspaceID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("list lanes by workspace: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var out []lane.Record
|
|
for rows.Next() {
|
|
item, err := scanLane(rows)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, item)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("iterate lanes by workspace: %w", err)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *Store) UpdateLane(ctx context.Context, value lane.Record) (lane.Record, error) {
|
|
if value.ID == "" {
|
|
return lane.Record{}, fmt.Errorf("lane id is required")
|
|
}
|
|
before, err := s.GetLane(ctx, value.ID)
|
|
if err != nil {
|
|
return lane.Record{}, err
|
|
}
|
|
value.CreatedAt = before.CreatedAt
|
|
value.UpdatedAt = s.now()
|
|
if err := value.Validate(); err != nil {
|
|
return lane.Record{}, err
|
|
}
|
|
|
|
if _, err := s.db.ExecContext(ctx, `
|
|
UPDATE lanes
|
|
SET workspace_id = ?, topic_id = ?, name = ?, slug = ?, purpose = ?, status = ?, base_branch = ?, branch_name = ?, head_commit = ?,
|
|
worktree_path = ?, container_name = ?, runtime_endpoint = ?, created_by_role_name = ?,
|
|
result_summary_markdown = ?, error_message = ?, updated_at = ?, started_at = ?, completed_at = ?
|
|
WHERE id = ?
|
|
`,
|
|
value.WorkspaceID,
|
|
value.TopicID,
|
|
value.Name,
|
|
value.Slug,
|
|
value.Purpose,
|
|
string(value.Status),
|
|
value.BaseBranch,
|
|
value.BranchName,
|
|
value.HeadCommit,
|
|
value.WorktreePath,
|
|
value.ContainerName,
|
|
value.RuntimeEndpoint,
|
|
value.CreatedByRoleName,
|
|
value.ResultSummaryMarkdown,
|
|
value.ErrorMessage,
|
|
value.UpdatedAt,
|
|
nullableString(value.StartedAt),
|
|
nullableString(value.CompletedAt),
|
|
value.ID,
|
|
); err != nil {
|
|
return lane.Record{}, fmt.Errorf("update lane: %w", err)
|
|
}
|
|
return s.GetLane(ctx, value.ID)
|
|
}
|
|
|
|
func scanLane(s scanner) (lane.Record, error) {
|
|
var item lane.Record
|
|
var status string
|
|
var startedAt sql.NullString
|
|
var completedAt sql.NullString
|
|
if err := s.Scan(
|
|
&item.ID,
|
|
&item.WorkspaceID,
|
|
&item.TopicID,
|
|
&item.Name,
|
|
&item.Slug,
|
|
&item.Purpose,
|
|
&status,
|
|
&item.BaseBranch,
|
|
&item.BranchName,
|
|
&item.HeadCommit,
|
|
&item.WorktreePath,
|
|
&item.ContainerName,
|
|
&item.RuntimeEndpoint,
|
|
&item.CreatedByRoleName,
|
|
&item.ResultSummaryMarkdown,
|
|
&item.ErrorMessage,
|
|
&item.CreatedAt,
|
|
&item.UpdatedAt,
|
|
&startedAt,
|
|
&completedAt,
|
|
); err != nil {
|
|
return lane.Record{}, err
|
|
}
|
|
item.Status = lane.Status(status)
|
|
if startedAt.Valid {
|
|
item.StartedAt = startedAt.String
|
|
}
|
|
if completedAt.Valid {
|
|
item.CompletedAt = completedAt.String
|
|
}
|
|
return item, nil
|
|
}
|