refactor(db): typed query helpers, shared test FormData helper
Replace raw db.execute tuple casts with queryRows/rowsFrom/execResult/ affectedRows helpers from lib/db, drop redundant mysql2 casts on typed query builders, and centralize per-test fakeForm into test/fake-form. Update db mocks in tests so helpers resolve against mocked execute.
This commit is contained in:
1 parent
2e25b39364
commit
8638e81444
116 files changed
+866
-897
No files matched your search
@@ -1,7 +1,7 @@
|
||||
import "server-only";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { db } from "@/lib/db";
|
||||
import { db, queryRows, rowsFrom } from "@/lib/db";
|
||||
import { logger } from "@/lib/logger";
|
||||
import { isDeliveryReference } from "./delivery-diagnostics";
|
||||
import {
|
||||
@@ -26,15 +26,14 @@ export async function runOperation<T>(
|
||||
await tx.execute(
|
||||
sql`INSERT INTO cms_operations (id,actor_id,kind,request_key,request_hash) VALUES (${randomUUID()},${input.actorId},${input.kind},${input.key},${hash}) ON DUPLICATE KEY UPDATE id=id`,
|
||||
);
|
||||
const [rows] = await tx.execute(
|
||||
sql`SELECT id,request_hash AS requestHash,result_json AS resultJson FROM cms_operations WHERE actor_id=${input.actorId} AND kind=${input.kind} AND request_key=${input.key} FOR UPDATE`,
|
||||
);
|
||||
const row = (
|
||||
rows as unknown as Array<{
|
||||
id: string;
|
||||
requestHash: string;
|
||||
resultJson: string | null;
|
||||
}>
|
||||
const row = rowsFrom<{
|
||||
id: string;
|
||||
requestHash: string;
|
||||
resultJson: string | null;
|
||||
}>(
|
||||
await tx.execute(
|
||||
sql`SELECT id,request_hash AS requestHash,result_json AS resultJson FROM cms_operations WHERE actor_id=${input.actorId} AND kind=${input.kind} AND request_key=${input.key} FOR UPDATE`,
|
||||
),
|
||||
)[0];
|
||||
if (!row) throw new Error("Operation unavailable");
|
||||
if (row.requestHash !== hash) throw new OperationConflict();
|
||||
@@ -61,16 +60,15 @@ export async function enqueueEffect(
|
||||
export const effectRepository = {
|
||||
async claim(): Promise<EffectClaim | null> {
|
||||
return db.transaction(async (tx) => {
|
||||
const [rows] = await tx.execute(
|
||||
sql`SELECT id,operation_id AS operationId,topic,attempts FROM cms_outbox WHERE (status='pending' AND available_at<=UTC_TIMESTAMP(3)) OR (status='running' AND lease_until<UTC_TIMESTAMP(3)) ORDER BY available_at,id LIMIT 1 FOR UPDATE`,
|
||||
);
|
||||
const row = (
|
||||
rows as unknown as Array<{
|
||||
id: string;
|
||||
operationId: string;
|
||||
topic: EffectTopic;
|
||||
attempts: number;
|
||||
}>
|
||||
const row = rowsFrom<{
|
||||
id: string;
|
||||
operationId: string;
|
||||
topic: EffectTopic;
|
||||
attempts: number;
|
||||
}>(
|
||||
await tx.execute(
|
||||
sql`SELECT id,operation_id AS operationId,topic,attempts FROM cms_outbox WHERE (status='pending' AND available_at<=UTC_TIMESTAMP(3)) OR (status='running' AND lease_until<UTC_TIMESTAMP(3)) ORDER BY available_at,id LIMIT 1 FOR UPDATE`,
|
||||
),
|
||||
)[0];
|
||||
if (!row) return null;
|
||||
const token = randomUUID();
|
||||
@@ -105,10 +103,7 @@ export const effectRepository = {
|
||||
export async function listEffects(id?: string) {
|
||||
if (id !== undefined && !isDeliveryReference(id)) return [];
|
||||
const filter = id === undefined ? sql`` : sql`WHERE e.id=${id}`;
|
||||
const [rows] = await db.execute(
|
||||
sql`SELECT e.id,e.operation_id AS operationId,e.topic,e.status,e.attempts,e.last_error AS lastError,e.created_at AS createdAt,e.available_at AS availableAt,o.actor_id AS actorId,o.kind,o.result_json AS resultJson FROM cms_outbox e JOIN cms_operations o ON o.id=e.operation_id ${filter} ORDER BY e.created_at DESC,e.id DESC LIMIT 100`,
|
||||
);
|
||||
return rows as unknown as Array<{
|
||||
return queryRows<{
|
||||
id: string;
|
||||
operationId: string;
|
||||
topic: EffectTopic;
|
||||
@@ -120,7 +115,9 @@ export async function listEffects(id?: string) {
|
||||
resultJson: string | null;
|
||||
actorId: number;
|
||||
kind: string;
|
||||
}>;
|
||||
}>(
|
||||
sql`SELECT e.id,e.operation_id AS operationId,e.topic,e.status,e.attempts,e.last_error AS lastError,e.created_at AS createdAt,e.available_at AS availableAt,o.actor_id AS actorId,o.kind,o.result_json AS resultJson FROM cms_outbox e JOIN cms_operations o ON o.id=e.operation_id ${filter} ORDER BY e.created_at DESC,e.id DESC LIMIT 100`,
|
||||
);
|
||||
}
|
||||
export async function retryEffect(id: string) {
|
||||
if (
|
||||
@@ -130,6 +127,6 @@ export async function retryEffect(id: string) {
|
||||
const [result] = await db.execute(
|
||||
sql`UPDATE cms_outbox SET status='pending',attempts=0,available_at=UTC_TIMESTAMP(3),last_error=NULL WHERE id=${id} AND status='failed'`,
|
||||
);
|
||||
if ((result as unknown as { affectedRows: number }).affectedRows !== 1)
|
||||
if (result.affectedRows !== 1)
|
||||
throw new Error("Delivery is no longer available for retry");
|
||||
}
|
||||
Reference in new issue
Block a user