Files
sap-legacy-webhook-emulator/src/dispatcher/evn1-repository.ts
T

93 lines
3.6 KiB
TypeScript
Raw Normal View History

2026-09-16 08:30:45 -05:00
import type { ServiceLayerClient } from '../service-layer-client.ts';
import { generateCode } from '../lib/codes.ts';
import { hhmmss } from '../lib/timestamps.ts';
import { userTableEntity } from '../lib/service-layer-paths.ts';
const ENTITY = userTableEntity('EVN1');
export interface Evn1Row {
code: string;
eventId: string;
webhookId: string;
status: string;
retry: number;
}
function toRow(raw: Record<string, unknown>): Evn1Row {
return {
code: raw.Code as string,
eventId: raw.U_EventID as string,
webhookId: raw.U_WebhookID as string,
status: raw.U_Status as string,
retry: (raw.U_Retry as number) ?? 0,
};
}
export async function findExisting(client: ServiceLayerClient, eventId: string, webhookId: string): Promise<Evn1Row | null> {
const filter = encodeURIComponent(`U_EventID eq '${eventId}' and U_WebhookID eq '${webhookId}'`);
const res = await client.get(`/${ENTITY}?$filter=${filter}`);
const rows = (res.data?.value as Record<string, unknown>[]) ?? [];
return rows.length > 0 ? toRow(rows[0]) : null;
}
export async function create(client: ServiceLayerClient, eventId: string, webhookId: string): Promise<void> {
await client.post(`/${ENTITY}`, {
Code: generateCode(),
Name: `${eventId}/${webhookId}`,
U_EventID: eventId,
U_WebhookID: webhookId,
U_Status: 'F',
U_Retry: 0,
});
}
export async function listPending(client: ServiceLayerClient): Promise<Evn1Row[]> {
const rows = await client.getAll(`/${ENTITY}?$filter=${encodeURIComponent("U_Status eq 'F'")}`);
return rows.map(toRow);
}
export async function listForEvent(client: ServiceLayerClient, eventId: string): Promise<Evn1Row[]> {
const rows = await client.getAll(`/${ENTITY}?$filter=${encodeURIComponent(`U_EventID eq '${eventId}'`)}`);
return rows.map(toRow);
}
export async function markDelivered(client: ServiceLayerClient, code: string, returnCode: number): Promise<void> {
const now = new Date();
await client.patch(`/${ENTITY}('${code}')`, {
U_Status: 'D',
U_ReturnCode: returnCode,
U_LastSentDT: now.toISOString(),
U_LastSentTM: hhmmss(now),
});
}
export async function markFailedAttempt(client: ServiceLayerClient, row: Evn1Row, returnCode: number): Promise<void> {
const now = new Date();
await client.patch(`/${ENTITY}('${row.code}')`, {
U_Retry: row.retry + 1,
U_ReturnCode: returnCode,
U_LastSentDT: now.toISOString(),
U_LastSentTM: hhmmss(now),
});
}
// Pone en 0 el contador de reintentos de las entregas pendientes de una
// suscripción. Se usa al reactivarla (State X -> A): sin esto, las filas
// siguen con Retry >= RETRY_LIMIT y la suscripción vuelve a X en el
// siguiente tick sin intentar enviar nada.
export async function resetRetries(client: ServiceLayerClient, webhookId: string): Promise<number> {
const filter = encodeURIComponent(`U_WebhookID eq '${webhookId}' and U_Status eq 'F'`);
const rows = (await client.getAll(`/${ENTITY}?$filter=${filter}`)).map(toRow).filter((r) => r.retry > 0);
for (const row of rows) await client.patch(`/${ENTITY}('${row.code}')`, { U_Retry: 0 });
return rows.length;
}
// Entregas fallidas/pendientes (Status='F') de una suscripción.
export async function listPendingForWebhook(client: ServiceLayerClient, webhookId: string): Promise<Evn1Row[]> {
const filter = encodeURIComponent(`U_WebhookID eq '${webhookId}' and U_Status eq 'F'`);
return (await client.getAll(`/${ENTITY}?$filter=${filter}`)).map(toRow);
}
export async function deleteRow(client: ServiceLayerClient, code: string): Promise<void> {
await client.delete(`/${ENTITY}('${code}')`);
}