feat: initial scaffold of agent_runner_api service Go :8486
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type DodItem struct {
|
||||
ID string `json:"id"`
|
||||
RunID string `json:"run_id"`
|
||||
ItemKey string `json:"item_key"`
|
||||
Kind string `json:"kind"`
|
||||
Expected string `json:"expected"`
|
||||
Required bool `json:"required"`
|
||||
Status string `json:"status"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
|
||||
type DodEvidence struct {
|
||||
ID string `json:"id"`
|
||||
DodItemID string `json:"dod_item_id"`
|
||||
Kind string `json:"kind"`
|
||||
PayloadPath *string `json:"payload_path,omitempty"`
|
||||
PayloadURL *string `json:"payload_url,omitempty"`
|
||||
PayloadText *string `json:"payload_text,omitempty"`
|
||||
AttachedAt int64 `json:"attached_at"`
|
||||
ValidatedAt *int64 `json:"validated_at,omitempty"`
|
||||
ValidatedBy *string `json:"validated_by,omitempty"`
|
||||
}
|
||||
|
||||
func createDodItem(db *sql.DB, runID, key, kind, expected string, required bool) (DodItem, error) {
|
||||
id := "dod_" + uuid.New().String()[:12]
|
||||
now := time.Now().Unix()
|
||||
reqInt := 0
|
||||
if required {
|
||||
reqInt = 1
|
||||
}
|
||||
_, err := db.Exec(`INSERT INTO dod_items
|
||||
(id, run_id, item_key, kind, expected, required, status, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?)`,
|
||||
id, runID, key, kind, expected, reqInt, now)
|
||||
if err != nil {
|
||||
return DodItem{}, err
|
||||
}
|
||||
return DodItem{
|
||||
ID: id, RunID: runID, ItemKey: key, Kind: kind, Expected: expected,
|
||||
Required: required, Status: "pending", CreatedAt: now,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func listDodItems(db *sql.DB, runID string) ([]DodItem, error) {
|
||||
rows, err := db.Query(`SELECT id, run_id, item_key, kind, expected, required, status, created_at
|
||||
FROM dod_items WHERE run_id = ? ORDER BY created_at`, runID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := []DodItem{}
|
||||
for rows.Next() {
|
||||
var it DodItem
|
||||
var reqInt int
|
||||
if err := rows.Scan(&it.ID, &it.RunID, &it.ItemKey, &it.Kind, &it.Expected, &reqInt, &it.Status, &it.CreatedAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
it.Required = reqInt != 0
|
||||
out = append(out, it)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func attachEvidence(db *sql.DB, itemID, kind string, path, url, text *string) (DodEvidence, error) {
|
||||
id := "ev_" + uuid.New().String()[:12]
|
||||
now := time.Now().Unix()
|
||||
_, err := db.Exec(`INSERT INTO dod_evidence
|
||||
(id, dod_item_id, kind, payload_path, payload_url, payload_text, attached_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||
id, itemID, kind, nullStr(path), nullStr(url), nullStr(text), now)
|
||||
if err != nil {
|
||||
return DodEvidence{}, err
|
||||
}
|
||||
// Auto-bump item status to 'done' on first evidence
|
||||
_, _ = db.Exec(`UPDATE dod_items SET status = 'done' WHERE id = ? AND status = 'pending'`, itemID)
|
||||
return DodEvidence{
|
||||
ID: id, DodItemID: itemID, Kind: kind,
|
||||
PayloadPath: path, PayloadURL: url, PayloadText: text,
|
||||
AttachedAt: now,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func validateEvidence(db *sql.DB, evID, validatedBy string) error {
|
||||
now := time.Now().Unix()
|
||||
res, err := db.Exec(`UPDATE dod_evidence
|
||||
SET validated_at = ?, validated_by = ? WHERE id = ?`, now, validatedBy, evID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n, _ := res.RowsAffected()
|
||||
if n == 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
// Bump item status to validated
|
||||
_, _ = db.Exec(`UPDATE dod_items SET status = 'validated'
|
||||
WHERE id = (SELECT dod_item_id FROM dod_evidence WHERE id = ?)`, evID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func listEvidence(db *sql.DB, itemID string) ([]DodEvidence, error) {
|
||||
rows, err := db.Query(`SELECT id, dod_item_id, kind, payload_path, payload_url, payload_text,
|
||||
attached_at, validated_at, validated_by
|
||||
FROM dod_evidence WHERE dod_item_id = ? ORDER BY attached_at`, itemID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
out := []DodEvidence{}
|
||||
for rows.Next() {
|
||||
var ev DodEvidence
|
||||
var path, url, text, valBy sql.NullString
|
||||
var valAt sql.NullInt64
|
||||
if err := rows.Scan(&ev.ID, &ev.DodItemID, &ev.Kind, &path, &url, &text,
|
||||
&ev.AttachedAt, &valAt, &valBy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if path.Valid {
|
||||
s := path.String
|
||||
ev.PayloadPath = &s
|
||||
}
|
||||
if url.Valid {
|
||||
s := url.String
|
||||
ev.PayloadURL = &s
|
||||
}
|
||||
if text.Valid {
|
||||
s := text.String
|
||||
ev.PayloadText = &s
|
||||
}
|
||||
if valAt.Valid {
|
||||
v := valAt.Int64
|
||||
ev.ValidatedAt = &v
|
||||
}
|
||||
if valBy.Valid {
|
||||
s := valBy.String
|
||||
ev.ValidatedBy = &s
|
||||
}
|
||||
out = append(out, ev)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// dodGateOpen returns true when every required item has at least one validated evidence.
|
||||
func dodGateOpen(db *sql.DB, runID string) (bool, error) {
|
||||
row := db.QueryRow(`
|
||||
SELECT COUNT(*) FROM dod_items
|
||||
WHERE run_id = ? AND required = 1
|
||||
AND status != 'validated'`, runID)
|
||||
var n int
|
||||
if err := row.Scan(&n); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return n == 0, nil
|
||||
}
|
||||
|
||||
func nullStr(p *string) interface{} {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return *p
|
||||
}
|
||||
Reference in New Issue
Block a user