refactor(api-server): move approvals SQL into db.approvals domain methods

Replace the ad-hoc getApproval helper and the filterable list query
in routes-approvals.ts with db.approvals methods. Add findApprovalById
(non-throwing lookup returning null) and listApprovalsForApi (accepts
requestedByAgentId / relatedTaskId / statuses filters and builds the
prepared statement inside the domain layer).
This commit is contained in:
ZacharyZhang-NY committed 2026-04-16 22:38:05 -04:00
1 parent 28296b3d5f
commit 4342cf3e2f
3 files changed
+26 -30

No files matched your search

+11 -30
View File
@@ -1,10 +1,8 @@
import { z } from "zod";
import log from "electron-log/main.js";
import type { SQLInputValue } from "node:sqlite";
import { approvalInputSchema } from "@shared/contracts";
import { resolveApprovalDecision, shouldAutoApproveApproval } from "../approval-policy";
import { requireParam, type AgentApiServer } from "./server-core";
import type { AppDatabase } from "../database";
const approvalListQuerySchema = z.object({
requestedByAgentId: z.string().uuid().optional(),
@@ -12,21 +10,6 @@ const approvalListQuerySchema = z.object({
status: z.string().optional(),
});
type ApprovalRow = {
id: string; companyId: string; relatedTaskId: string | null; relatedAgentId: string | null;
requestedByAgentId: string | null; type: string; payloadSummary: string | null;
impactSummary: string | null; payloadJson: string | null; decisionNote: string | null;
state: string; createdAt: string; updatedAt: string;
};
function getApproval(db: AppDatabase, approvalId: string): ApprovalRow | null {
const rows = db.queryAll<ApprovalRow>(
"select id, company_id as companyId, related_task_id as relatedTaskId, related_agent_id as relatedAgentId, requested_by_agent_id as requestedByAgentId, type, payload_summary as payloadSummary, impact_summary as impactSummary, payload_json as payloadJson, decision_note as decisionNote, state, created_at as createdAt, updated_at as updatedAt from approvals where id = ? limit 1",
approvalId,
);
return rows[0] ?? null;
}
export function registerApprovalRoutes(server: AgentApiServer) {
const db = server.db;
const callbacks = server.callbacks;
@@ -128,18 +111,16 @@ export function registerApprovalRoutes(server: AgentApiServer) {
return;
}
const statuses = parsedQuery.data.status?.split(",").filter(Boolean) ?? null;
let sql = "select id, company_id as companyId, related_task_id as relatedTaskId, related_agent_id as relatedAgentId, requested_by_agent_id as requestedByAgentId, type, payload_summary as payloadSummary, impact_summary as impactSummary, payload_json as payloadJson, decision_note as decisionNote, state, created_at as createdAt, updated_at as updatedAt from approvals where company_id = ?";
const sqlArgs: unknown[] = [companyId];
if (parsedQuery.data.requestedByAgentId) { sql += " and requested_by_agent_id = ?"; sqlArgs.push(parsedQuery.data.requestedByAgentId); }
if (parsedQuery.data.relatedTaskId) { sql += " and related_task_id = ?"; sqlArgs.push(parsedQuery.data.relatedTaskId); }
if (statuses && statuses.length > 0) { sql += ` and state in (${statuses.map(() => "?").join(", ")})`; sqlArgs.push(...statuses); }
sql += " order by created_at desc";
const approvals = db.queryAll<ApprovalRow>(sql, ...(sqlArgs as SQLInputValue[]));
const approvals = db.listApprovalsForApi(companyId, {
requestedByAgentId: parsedQuery.data.requestedByAgentId,
relatedTaskId: parsedQuery.data.relatedTaskId,
statuses,
});
server.json(res, 200, approvals);
});
server.addRoute("GET", "/api/approvals/:approvalId", (_req, res, params, token) => {
const approval = getApproval(db, requireParam(params, "approvalId"));
const approval = db.findApprovalById(requireParam(params, "approvalId"));
if (!approval || approval.companyId !== token.companyId) {
server.json(res, 404, { error: "Approval not found" });
return;
@@ -153,7 +134,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
server.addRoute("GET", "/api/approvals/:approvalId/comments", (_req, res, params, token) => {
const approvalId = requireParam(params, "approvalId");
const approval = getApproval(db, approvalId);
const approval = db.findApprovalById(approvalId);
if (!approval) { server.json(res, 404, { error: "Approval not found" }); return; }
if (approval.companyId !== token.companyId) { server.json(res, 403, { error: "Company mismatch" }); return; }
const comments = db.listApprovalComments(approvalId, approval.companyId);
@@ -161,7 +142,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
});
server.addRoute("POST", "/api/approvals/:approvalId/comments", (_req, res, params, token, body) => {
const approval = getApproval(db, requireParam(params, "approvalId"));
const approval = db.findApprovalById(requireParam(params, "approvalId"));
if (!approval) { server.json(res, 404, { error: "Approval not found" }); return; }
if (approval.companyId !== token.companyId) { server.json(res, 403, { error: "Company mismatch" }); return; }
const data = body as Record<string, unknown>;
@@ -185,7 +166,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
});
server.addRoute("POST", "/api/approvals/:approvalId/resubmit", (_req, res, params, token) => {
const approval = getApproval(db, requireParam(params, "approvalId"));
const approval = db.findApprovalById(requireParam(params, "approvalId"));
if (!approval) { server.json(res, 404, { error: "Approval not found" }); return; }
if (approval.companyId !== token.companyId) { server.json(res, 403, { error: "Company mismatch" }); return; }
if (approval.state !== "revision_requested") {
@@ -203,7 +184,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
server.addRoute("GET", "/api/approvals/:approvalId/tasks", (_req, res, params, token) => {
const approvalId = requireParam(params, "approvalId");
const approval = getApproval(db, approvalId);
const approval = db.findApprovalById(approvalId);
if (!approval) { server.json(res, 404, { error: "Approval not found" }); return; }
if (approval.companyId !== token.companyId) { server.json(res, 403, { error: "Company mismatch" }); return; }
const taskIds = db.listTasksForApproval(approvalId);
@@ -212,7 +193,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
server.addRoute("POST", "/api/approvals/:approvalId/tasks", (_req, res, params, token, body) => {
const approvalId = requireParam(params, "approvalId");
const approval = getApproval(db, approvalId);
const approval = db.findApprovalById(approvalId);
if (!approval) { server.json(res, 404, { error: "Approval not found" }); return; }
if (approval.companyId !== token.companyId) { server.json(res, 403, { error: "Company mismatch" }); return; }
const data = body as Record<string, unknown>;
+1
View File
@@ -291,6 +291,7 @@ export class AppDatabaseBase {
requestApproval(input: { companyId: string; relatedTaskId?: string | null; requestedByAgentId?: string | null; relatedAgentId?: string | null; type: string; payloadSummary: string; impactSummary: string; payloadJson?: string | null }) { return this.approvalsMod.requestApproval(input); }
decideApproval(input: { companyId?: string | null; approvalId: string; state: string; decisionNote: string }) { return this.approvalsMod.decideApproval(input); }
getApprovalRecord(id: string) { return this.approvalsMod.getApprovalRecord(id); }
findApprovalById(id: string) { return this.approvalsMod.findApprovalById(id); }
deleteApproval(id: string, companyId: string) { this.approvalsMod.deleteApproval(id, companyId); }
getBlockingApproval(companyId: string, agentId: string, taskId: string | null) { return this.approvalsMod.getBlockingApproval(companyId, agentId, taskId); }
resubmitApproval(approvalId: string, companyId: string) { this.approvalsMod.resubmitApproval(approvalId, companyId); }
+14
View File
@@ -23,6 +23,20 @@ export function buildApprovalMethods(
return row;
},
/**
* Non-throwing lookup by id. Returns null when the approval does not
* exist. Used by approval routes that need to branch on 404 rather
* than catching an exception.
*/
findApprovalById(id: string): (ApprovalRecord & { payloadJson: string | null; relatedAgentId: string | null }) | null {
const row = getDb()
.prepare(
"select id, company_id as companyId, related_task_id as relatedTaskId, requested_by_agent_id as requestedByAgentId, related_agent_id as relatedAgentId, type, payload_summary as payloadSummary, impact_summary as impactSummary, payload_json as payloadJson, decision_note as decisionNote, state, created_at as createdAt, updated_at as updatedAt from approvals where id = ?",
)
.get(id) as (ApprovalRecord & { payloadJson: string | null; relatedAgentId: string | null }) | undefined;
return row ?? null;
},
requestApproval(input: {
companyId: string;
relatedTaskId?: string | null;