refactor: reuse gateway output helpers

This commit is contained in:
Peter Steinberger
2026-01-07 01:43:02 +00:00
parent b88c4e9d20
commit 15b7560a9b
5 changed files with 94 additions and 15 deletions

View File

@@ -36,6 +36,9 @@ const deps: CliDeps = {
describe("pollCommand", () => { describe("pollCommand", () => {
beforeEach(() => { beforeEach(() => {
callGatewayMock.mockReset(); callGatewayMock.mockReset();
runtime.log.mockReset();
runtime.error.mockReset();
runtime.exit.mockReset();
testConfig = {}; testConfig = {};
}); });
@@ -74,4 +77,34 @@ describe("pollCommand", () => {
| undefined; | undefined;
expect(args?.url).toBeUndefined(); expect(args?.url).toBeUndefined();
}); });
it("emits json output with gateway metadata", async () => {
callGatewayMock.mockResolvedValueOnce({ messageId: "p1", channelId: "C1" });
await pollCommand(
{
to: "channel:C1",
question: "hi?",
option: ["y", "n"],
provider: "discord",
json: true,
},
deps,
runtime,
);
const lastLog = runtime.log.mock.calls.at(-1)?.[0] as string | undefined;
expect(lastLog).toBeDefined();
const payload = JSON.parse(lastLog ?? "{}") as Record<string, unknown>;
expect(payload).toMatchObject({
provider: "discord",
via: "gateway",
to: "channel:C1",
messageId: "p1",
channelId: "C1",
mediaUrl: null,
question: "hi?",
options: ["y", "n"],
maxSelections: 1,
durationHours: null,
});
});
}); });

View File

@@ -1,6 +1,10 @@
import type { CliDeps } from "../cli/deps.js"; import type { CliDeps } from "../cli/deps.js";
import { callGateway, randomIdempotencyKey } from "../gateway/call.js"; import { callGateway, randomIdempotencyKey } from "../gateway/call.js";
import { success } from "../globals.js"; import { success } from "../globals.js";
import {
buildOutboundDeliveryJson,
formatGatewaySummary,
} from "../infra/outbound/format.js";
import { normalizePollInput, type PollInput } from "../polls.js"; import { normalizePollInput, type PollInput } from "../polls.js";
import type { RuntimeEnv } from "../runtime.js"; import type { RuntimeEnv } from "../runtime.js";
@@ -74,19 +78,24 @@ export async function pollCommand(
runtime.log( runtime.log(
success( success(
`✅ Poll sent via gateway (${provider}). Message ID: ${result.messageId ?? "unknown"}`, formatGatewaySummary({
action: "Poll sent",
provider,
messageId: result.messageId ?? null,
}),
), ),
); );
if (opts.json) { if (opts.json) {
runtime.log( runtime.log(
JSON.stringify( JSON.stringify(
{ {
provider, ...buildOutboundDeliveryJson({
via: "gateway", provider,
to: opts.to, via: "gateway",
toJid: result.toJid ?? null, to: opts.to,
channelId: result.channelId ?? null, result,
messageId: result.messageId, mediaUrl: null,
}),
question: normalized.question, question: normalized.question,
options: normalized.options, options: normalized.options,
maxSelections: normalized.maxSelections, maxSelections: normalized.maxSelections,

View File

@@ -5,6 +5,7 @@ import { success } from "../globals.js";
import { deliverOutboundPayloads } from "../infra/outbound/deliver.js"; import { deliverOutboundPayloads } from "../infra/outbound/deliver.js";
import { import {
buildOutboundDeliveryJson, buildOutboundDeliveryJson,
formatGatewaySummary,
formatOutboundDeliverySummary, formatOutboundDeliverySummary,
} from "../infra/outbound/format.js"; } from "../infra/outbound/format.js";
import { resolveOutboundTarget } from "../infra/outbound/targets.js"; import { resolveOutboundTarget } from "../infra/outbound/targets.js";
@@ -107,7 +108,7 @@ export async function sendCommand(
runtime.log( runtime.log(
success( success(
`✅ Sent via gateway. Message ID: ${result.messageId ?? "unknown"}`, formatGatewaySummary({ provider, messageId: result.messageId ?? null }),
), ),
); );
if (opts.json) { if (opts.json) {

View File

@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import { import {
buildOutboundDeliveryJson, buildOutboundDeliveryJson,
formatGatewaySummary,
formatOutboundDeliverySummary, formatOutboundDeliverySummary,
} from "./format.js"; } from "./format.js";
@@ -87,3 +88,21 @@ describe("buildOutboundDeliveryJson", () => {
}); });
}); });
}); });
describe("formatGatewaySummary", () => {
it("formats gateway summaries with provider", () => {
expect(
formatGatewaySummary({ provider: "whatsapp", messageId: "m1" }),
).toBe("✅ Sent via gateway (whatsapp). Message ID: m1");
});
it("supports custom actions", () => {
expect(
formatGatewaySummary({
action: "Poll sent",
provider: "discord",
messageId: "p1",
}),
).toBe("✅ Poll sent via gateway (discord). Message ID: p1");
});
});

View File

@@ -12,6 +12,14 @@ export type OutboundDeliveryJson = {
toJid?: string; toJid?: string;
}; };
type OutboundDeliveryMeta = {
messageId?: string;
chatId?: string;
channelId?: string;
timestamp?: number;
toJid?: string;
};
const resolveProviderLabel = (provider: string) => const resolveProviderLabel = (provider: string) =>
provider === "imessage" ? "iMessage" : provider; provider === "imessage" ? "iMessage" : provider;
@@ -34,7 +42,7 @@ export function formatOutboundDeliverySummary(
export function buildOutboundDeliveryJson(params: { export function buildOutboundDeliveryJson(params: {
provider: string; provider: string;
to: string; to: string;
result?: OutboundDeliveryResult; result?: OutboundDeliveryMeta | OutboundDeliveryResult;
via?: "direct" | "gateway"; via?: "direct" | "gateway";
mediaUrl?: string | null; mediaUrl?: string | null;
}): OutboundDeliveryJson { }): OutboundDeliveryJson {
@@ -48,12 +56,21 @@ export function buildOutboundDeliveryJson(params: {
mediaUrl: params.mediaUrl ?? null, mediaUrl: params.mediaUrl ?? null,
}; };
if (result && "chatId" in result) payload.chatId = result.chatId; if (result?.chatId !== undefined) payload.chatId = result.chatId;
if (result && "channelId" in result) payload.channelId = result.channelId; if (result?.channelId !== undefined) payload.channelId = result.channelId;
if (result && "timestamp" in result && result.timestamp !== undefined) { if (result?.timestamp !== undefined) payload.timestamp = result.timestamp;
payload.timestamp = result.timestamp; if (result?.toJid !== undefined) payload.toJid = result.toJid;
}
if (result && "toJid" in result) payload.toJid = result.toJid;
return payload; return payload;
} }
export function formatGatewaySummary(params: {
action?: string;
provider?: string;
messageId?: string | null;
}): string {
const action = params.action ?? "Sent";
const providerSuffix = params.provider ? ` (${params.provider})` : "";
const messageId = params.messageId ?? "unknown";
return `${action} via gateway${providerSuffix}. Message ID: ${messageId}`;
}