refactor(api-server): move files SQL into db.documents domain methods
Replace raw queryAll calls in routes-files.ts with two new domain methods on db.documents: listDocuments (powers GET /api/companies/:companyId/documents) and getDocumentForUpdate (used by the PATCH document handler). The DocumentListRow / DocumentLookupRow shapes preserve the historical inline-SQL projections so HTTP responses remain identical.
This commit is contained in:
1 parent
7f097e3319
commit
79e57567b2
3 files changed
+74
-22
No files matched your search
@@ -2,19 +2,6 @@ import { z } from "zod";
|
||||
import { IMPORTANT_DOC_TYPE_SET } from "@shared/document-categories";
|
||||
import { requireParam, type AgentApiServer } from "./server-core";
|
||||
|
||||
type DocumentRow = {
|
||||
id: string; companyId: string; type: string; title: string; content: string;
|
||||
authorAgentId: string; reviewerAgentId: string | null; projectId: string | null;
|
||||
goalId: string | null; parentDocId: string | null; version: number; status: string;
|
||||
tagsJson: string; latestRevisionId: string | null; createdAt: string; updatedAt: string;
|
||||
};
|
||||
|
||||
type DocumentLookupRow = {
|
||||
id: string; companyId: string; type: string; title: string; content: string;
|
||||
authorAgentId: string; reviewerAgentId: string | null; projectId: string | null;
|
||||
goalId: string | null; version: number; status: string; tagsJson: string;
|
||||
};
|
||||
|
||||
const createDocumentSchema = z.object({
|
||||
type: z.string().optional(),
|
||||
title: z.string().optional(),
|
||||
@@ -171,10 +158,7 @@ export function registerFileRoutes(server: AgentApiServer) {
|
||||
server.json(res, 403, { error: "Company mismatch" });
|
||||
return;
|
||||
}
|
||||
const docs = db.queryAll<DocumentRow>(
|
||||
"select id, company_id as companyId, type, title, content, author_agent_id as authorAgentId, reviewer_agent_id as reviewerAgentId, project_id as projectId, goal_id as goalId, parent_doc_id as parentDocId, version, status, tags_json as tagsJson, latest_revision_id as latestRevisionId, created_at as createdAt, updated_at as updatedAt from documents where company_id = ? order by updated_at desc",
|
||||
token.companyId,
|
||||
).map((d) => ({ ...d, latestRevisionId: d.latestRevisionId ?? null }));
|
||||
const docs = db.listDocuments(token.companyId);
|
||||
server.json(res, 200, docs);
|
||||
});
|
||||
|
||||
@@ -188,11 +172,7 @@ export function registerFileRoutes(server: AgentApiServer) {
|
||||
server.json(res, 400, { error: "Missing documentId" });
|
||||
return;
|
||||
}
|
||||
const docRows = db.queryAll<DocumentLookupRow>(
|
||||
"select id, company_id as companyId, type, title, content, author_agent_id as authorAgentId, reviewer_agent_id as reviewerAgentId, project_id as projectId, goal_id as goalId, version, status, tags_json as tagsJson from documents where id = ? and company_id = ? limit 1",
|
||||
documentId, token.companyId,
|
||||
);
|
||||
const doc = docRows[0] ?? null;
|
||||
const doc = db.getDocumentForUpdate(documentId, token.companyId);
|
||||
if (!doc) {
|
||||
server.json(res, 404, { error: "Document not found in this company" });
|
||||
return;
|
||||
|
||||
@@ -83,6 +83,8 @@ export class AppDatabase extends AppDatabaseBase {
|
||||
|
||||
saveDocument(input: { id?: string | null; companyId: string; type: string; title: string; content?: string; authorAgentId?: string | null; reviewerAgentId?: string | null; projectId?: string | null; goalId?: string | null; parentDocId?: string | null; version?: number; status?: string; tagsJson?: string; sourceRunId?: string | null }) { return this.documentsMod.saveDocument(input); }
|
||||
listImportantDocsForReview(input: { companyId: string; types: readonly string[]; authorAgentId: string | null; taskId: string; limit?: number }) { return this.documentsMod.listImportantDocsForReview(input); }
|
||||
listDocuments(companyId: string) { return this.documentsMod.listDocuments(companyId); }
|
||||
getDocumentForUpdate(documentId: string, companyId: string) { return this.documentsMod.getDocumentForUpdate(documentId, companyId); }
|
||||
updateDocumentStatus(id: string, companyId: string, status: string) { this.documentsMod.updateDocumentStatus(id, companyId, status); }
|
||||
deleteDocument(id: string, companyId: string) { this.documentsMod.deleteDocument(id, companyId); }
|
||||
createDocumentRevision(input: { documentId: string; companyId: string; body: string; changeSummary?: string | null; createdByAgentId?: string | null; createdByUserId?: string | null }) { return this.documentsMod.createDocumentRevision(input); }
|
||||
|
||||
@@ -4,6 +4,49 @@ import type { AgentRecord } from "@shared/types";
|
||||
import type { ReadAll, SqliteRow, Transaction } from "./helpers";
|
||||
import { ensureId, nowIso, parseJson } from "./helpers";
|
||||
|
||||
/**
|
||||
* Document row shape returned by listDocuments — used by
|
||||
* GET /api/companies/:companyId/documents. Mirrors the historical inline
|
||||
* SQL projection so HTTP response bodies stay byte-stable for clients.
|
||||
*/
|
||||
export type DocumentListRow = {
|
||||
id: string;
|
||||
companyId: string;
|
||||
type: string;
|
||||
title: string;
|
||||
content: string;
|
||||
authorAgentId: string;
|
||||
reviewerAgentId: string | null;
|
||||
projectId: string | null;
|
||||
goalId: string | null;
|
||||
parentDocId: string | null;
|
||||
version: number;
|
||||
status: string;
|
||||
tagsJson: string;
|
||||
latestRevisionId: string | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Document lookup row used by the PATCH document handler — narrower
|
||||
* projection without parent_doc_id / latest_revision_id / timestamps.
|
||||
*/
|
||||
export type DocumentLookupRow = {
|
||||
id: string;
|
||||
companyId: string;
|
||||
type: string;
|
||||
title: string;
|
||||
content: string;
|
||||
authorAgentId: string;
|
||||
reviewerAgentId: string | null;
|
||||
projectId: string | null;
|
||||
goalId: string | null;
|
||||
version: number;
|
||||
status: string;
|
||||
tagsJson: string;
|
||||
};
|
||||
|
||||
export function buildDocumentMethods(
|
||||
getDb: () => DatabaseSync,
|
||||
readAll: ReadAll,
|
||||
@@ -31,6 +74,33 @@ export function buildDocumentMethods(
|
||||
return id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all documents for a company, ordered by updated_at desc.
|
||||
* Used by GET /api/companies/:companyId/documents.
|
||||
*/
|
||||
listDocuments(companyId: string): DocumentListRow[] {
|
||||
return readAll<DocumentListRow>(
|
||||
"select id, company_id as companyId, type, title, content, author_agent_id as authorAgentId, reviewer_agent_id as reviewerAgentId, project_id as projectId, goal_id as goalId, parent_doc_id as parentDocId, version, status, tags_json as tagsJson, latest_revision_id as latestRevisionId, created_at as createdAt, updated_at as updatedAt from documents where company_id = ? order by updated_at desc",
|
||||
companyId,
|
||||
).map((d) => ({ ...d, latestRevisionId: d.latestRevisionId ?? null }));
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the lookup row needed for PATCH /api/companies/:companyId/documents/:documentId.
|
||||
* Returns null when the document is missing or belongs to another company.
|
||||
*/
|
||||
getDocumentForUpdate(
|
||||
documentId: string,
|
||||
companyId: string,
|
||||
): DocumentLookupRow | null {
|
||||
const row = getDb()
|
||||
.prepare(
|
||||
"select id, company_id as companyId, type, title, content, author_agent_id as authorAgentId, reviewer_agent_id as reviewerAgentId, project_id as projectId, goal_id as goalId, version, status, tags_json as tagsJson from documents where id = ? and company_id = ? limit 1",
|
||||
)
|
||||
.get(documentId, companyId) as DocumentLookupRow | undefined;
|
||||
return row ?? null;
|
||||
},
|
||||
|
||||
updateDocumentStatus(id: string, companyId: string, status: string): void {
|
||||
getDb().prepare(
|
||||
"UPDATE documents SET status = ?, updated_at = ? WHERE id = ? AND company_id = ?",
|
||||
|
||||
Reference in new issue
Block a user