fix(GRO-1003): address CI typecheck and lint failures on PR #379

Typecheck fixes:
- telnyx.ts:48 — coerce undefined to null for signature param
- inbound.ts/outbound.ts — add null guards on .returning() results
- schema.ts — add updatedAt to messages table
- package.json — add uuid and @types/uuid

Lint fixes:
- telnyx.ts — remove unused resolveBusinessIdByMessagingNumber import
- inbound.ts — remove unused messageDirectionEnum, messageStatusEnum imports
- inbound.ts — remove unused buildFindOrCreateConversationParams function
- inbound.test.ts — remove unused resolveBusinessIdByMessagingNumber import
- outbound.test.ts — remove unused mockEq/mockAnd variables
- outbound.test.ts — fix vi.mock path for sms.js (../../sms.js)
This commit is contained in:
2026-05-04 16:18:23 +00:00
committed by Flea Flicker [agent]
parent 1c4453ed45
commit 2b646d9e5d
7 changed files with 16 additions and 19 deletions
+2
View File
@@ -24,6 +24,7 @@
"nodemailer": "^6.9.16",
"stripe": "^22.0.0",
"telnyx": "^1.23.0",
"uuid": "^11.0.5",
"zod": "^4.3.6"
},
@@ -31,6 +32,7 @@
"@types/node": "^22.10.7",
"@types/node-cron": "^3.0.11",
"@types/nodemailer": "^6.4.17",
"@types/uuid": "^10.0.0",
"@vitest/coverage-v8": "^3.2.4",
"eslint": "^9.18.0",
"tsx": "^4.19.2",
+1 -2
View File
@@ -3,7 +3,6 @@ import { createHmac } from "crypto";
import {
handleMessageReceived,
handleMessageFinalized,
resolveBusinessIdByMessagingNumber,
TelnyxMessageReceivedPayload,
} from "../../services/messaging/inbound.js";
@@ -45,7 +44,7 @@ telnyxWebhooksRouter.post("/messaging", async (c) => {
return c.json({ error: "Could not read body" }, 400);
}
if (!validateTelnyxSignature(rawBody, signature)) {
if (!validateTelnyxSignature(rawBody, signature ?? null)) {
return c.json({ error: "Invalid signature" }, 401);
}
@@ -2,7 +2,6 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
import {
findOrCreateConversation,
upsertMessage,
resolveBusinessIdByMessagingNumber,
handleMessageReceived,
handleMessageFinalized,
TelnyxMessageReceivedPayload,
@@ -54,7 +53,7 @@ const makePayload = (
describe("signature validation via route", () => {
it("returns 401 when telnyx-signature header is missing", async () => {
const { telnyxWebhooksRouter } = await import("../../routes/webhooks/telnyx.js");
const { telnyxWebhooksRouter } = await import("../../../routes/webhooks/telnyx.js");
const payload = JSON.stringify(makePayload("message.received", "msg-123", "+1555111", "+1555222"));
const req = new Request("http://localhost/api/webhooks/telnyx/messaging", {
method: "POST",
@@ -67,7 +66,7 @@ describe("signature validation via route", () => {
it("returns 401 when signature does not match", async () => {
process.env.TELNYX_WEBHOOK_SECRET = "test-secret";
const { telnyxWebhooksRouter } = await import("../../routes/webhooks/telnyx.js");
const { telnyxWebhooksRouter } = await import("../../../routes/webhooks/telnyx.js");
const payload = JSON.stringify(makePayload("message.received", "msg-123", "+1555111", "+1555222"));
const req = new Request("http://localhost/api/webhooks/telnyx/messaging", {
method: "POST",
@@ -4,7 +4,7 @@ const mockSendSms = vi.fn();
const mockGetDb = vi.fn();
const mockUuidv4 = vi.fn();
vi.mock("../sms.js", () => ({
vi.mock("../../sms.js", () => ({
sendSms: mockSendSms,
}));
@@ -24,9 +24,6 @@ vi.mock("uuid", () => ({
const { sendMessage, MissingTenantPhoneNumberError } = await import("../outbound.ts");
const mockEq = (a: unknown, b: unknown) => [a, b];
const mockAnd = (...args: unknown[]) => args;
describe("sendMessage", () => {
beforeEach(() => {
vi.clearAllMocks();
+5 -10
View File
@@ -1,5 +1,4 @@
import { getDb, conversations, messages, businessSettings, eq, and, sql } from "@groombook/db";
import { messageDirectionEnum, messageStatusEnum } from "@groombook/db";
import { v4 as uuidv4 } from "uuid";
export interface TelnyxMessageReceivedPayload {
@@ -20,14 +19,6 @@ export interface TelnyxMessageReceivedPayload {
};
}
function buildFindOrCreateConversationParams(businessId: string, clientPhone: string, businessNumber: string) {
return {
businessId,
externalNumber: clientPhone,
businessNumber,
};
}
export async function findOrCreateConversation(
businessId: string,
clientPhone: string,
@@ -73,6 +64,8 @@ export async function findOrCreateConversation(
})
.returning({ id: conversations.id, clientId: conversations.clientId });
if (!created) throw new Error("Failed to create conversation");
return { id: created.id, clientId: created.clientId };
}
@@ -109,6 +102,8 @@ export async function upsertMessage(
})
.returning({ id: messages.id });
if (!inserted) throw new Error("Failed to insert message");
return { id: inserted.id, isNew: true };
}
@@ -184,4 +179,4 @@ export async function handleMessageFinalized(payload: TelnyxMessageReceivedPaylo
}
return { messageId: existing.id, newStatus };
}
}
@@ -66,6 +66,8 @@ async function findOrCreateConversation(
})
.returning({ id: conversations.id });
if (!created) throw new Error("Failed to create conversation");
return { id: created.id };
}
@@ -115,6 +117,8 @@ export async function sendMessage(opts: SendMessageOptions): Promise<SendMessage
})
.returning({ id: messages.id });
if (!queuedMessage) throw new Error("Failed to insert queued message");
try {
const result = await sendSms(to, body, mediaUrls);
+1
View File
@@ -477,6 +477,7 @@ export const messages = pgTable(
createdAt: timestamp("created_at").notNull().defaultNow(),
deliveredAt: timestamp("delivered_at"),
readByClientAt: timestamp("read_by_client_at"),
updatedAt: timestamp("updated_at").notNull().defaultNow(),
},
(t) => [
index("idx_messages_conversation_id_created_at").on(