diff --git a/.changeset/fix-webhook-response-types.md b/.changeset/fix-webhook-response-types.md new file mode 100644 index 00000000..a3126857 --- /dev/null +++ b/.changeset/fix-webhook-response-types.md @@ -0,0 +1,10 @@ +--- +"@proofkit/fmodata": minor +--- + +fix(fmodata): align webhook types with actual FM OData API response + +BREAKING: `WebhookListResponse`, `WebhookInfo`, and `WebhookAddResponse` property names changed to match what the API actually returns: +- `Status` → `status`, `WebHook` → `webhooks` +- `webHookID` → `webhookID`, `url` → `webhook` +- `webHookResult` → `webhookResult` diff --git a/apps/docs/content/docs/fmodata/webhooks.mdx b/apps/docs/content/docs/fmodata/webhooks.mdx index 1c5d19f8..5e308121 100644 --- a/apps/docs/content/docs/fmodata/webhooks.mdx +++ b/apps/docs/content/docs/fmodata/webhooks.mdx @@ -25,7 +25,7 @@ const result = await db.webhook.add({ }); // Access the created webhook ID -console.log(result.webHookResult.webHookID); +console.log(result.webhookResult.webhookID); ``` @@ -83,13 +83,13 @@ Get all webhooks configured for the database: ```typescript const result = await db.webhook.list(); -console.log(result.Status); // Status of the operation -console.log(result.WebHook); // Array of webhook configurations +console.log(result.status); // Status of the operation +console.log(result.webhooks); // Array of webhook configurations -result.WebHook.forEach((webhook) => { - console.log(`Webhook ${webhook.webHookID}:`); +result.webhooks.forEach((webhook) => { + console.log(`Webhook ${webhook.webhookID}:`); console.log(` Table: ${webhook.tableName}`); - console.log(` URL: ${webhook.url}`); + console.log(` URL: ${webhook.webhook}`); console.log(` Notify Schema Changes: ${webhook.notifySchemaChanges}`); console.log(` Select: ${webhook.select}`); console.log(` Filter: ${webhook.filter}`); @@ -104,9 +104,9 @@ Retrieve a specific webhook by ID: ```typescript const webhook = await db.webhook.get(1); -console.log(webhook.webHookID); +console.log(webhook.webhookID); console.log(webhook.tableName); -console.log(webhook.url); +console.log(webhook.webhook); console.log(webhook.headers); console.log(webhook.notifySchemaChanges); console.log(webhook.select); @@ -153,16 +153,16 @@ const addResult = await db.webhook.add({ notifySchemaChanges: false, }); -const webhookId = addResult.webHookResult.webHookID; +const webhookId = addResult.webhookResult.webhookID; console.log(`Created webhook with ID: ${webhookId}`); // List all webhooks const listResult = await db.webhook.list(); -console.log(`Total webhooks: ${listResult.WebHook.length}`); +console.log(`Total webhooks: ${listResult.webhooks.length}`); // Get the webhook we just created const webhook = await db.webhook.get(webhookId); -console.log(`Webhook URL: ${webhook.url}`); +console.log(`Webhook URL: ${webhook.webhook}`); // Manually invoke the webhook for specific records await db.webhook.invoke(webhookId, { rowIDs: [1, 2, 3] }); diff --git a/packages/fmodata/README.md b/packages/fmodata/README.md index e4dc178a..54d0bda0 100644 --- a/packages/fmodata/README.md +++ b/packages/fmodata/README.md @@ -794,7 +794,7 @@ const result = await db.webhook.add({ }); // Access the created webhook ID -console.log(result.webHookResult.webHookID); +console.log(result.webhookResult.webhookID); ``` ### Webhook Configuration Options @@ -855,13 +855,13 @@ Get all webhooks configured for the database: ```typescript const result = await db.webhook.list(); -console.log(result.Status); // Status of the operation -console.log(result.WebHook); // Array of webhook configurations +console.log(result.status); // Status of the operation +console.log(result.webhooks); // Array of webhook configurations -result.WebHook.forEach((webhook) => { - console.log(`Webhook ${webhook.webHookID}:`); +result.webhooks.forEach((webhook) => { + console.log(`Webhook ${webhook.webhookID}:`); console.log(` Table: ${webhook.tableName}`); - console.log(` URL: ${webhook.url}`); + console.log(` URL: ${webhook.webhook}`); console.log(` Notify Schema Changes: ${webhook.notifySchemaChanges}`); console.log(` Select: ${webhook.select}`); console.log(` Filter: ${webhook.filter}`); @@ -876,9 +876,9 @@ Retrieve a specific webhook by ID: ```typescript const webhook = await db.webhook.get(1); -console.log(webhook.webHookID); +console.log(webhook.webhookID); console.log(webhook.tableName); -console.log(webhook.url); +console.log(webhook.webhook); console.log(webhook.headers); console.log(webhook.notifySchemaChanges); console.log(webhook.select); @@ -925,16 +925,16 @@ const addResult = await db.webhook.add({ notifySchemaChanges: false, }); -const webhookId = addResult.webHookResult.webHookID; +const webhookId = addResult.webhookResult.webhookID; console.log(`Created webhook with ID: ${webhookId}`); // List all webhooks const listResult = await db.webhook.list(); -console.log(`Total webhooks: ${listResult.WebHook.length}`); +console.log(`Total webhooks: ${listResult.webhooks.length}`); // Get the webhook we just created const webhook = await db.webhook.get(webhookId); -console.log(`Webhook URL: ${webhook.url}`); +console.log(`Webhook URL: ${webhook.webhook}`); // Manually invoke the webhook for specific records await db.webhook.invoke(webhookId, { rowIDs: [1, 2, 3] }); diff --git a/packages/fmodata/scripts/capture-responses.ts b/packages/fmodata/scripts/capture-responses.ts index 1f883ab8..39c8932b 100644 --- a/packages/fmodata/scripts/capture-responses.ts +++ b/packages/fmodata/scripts/capture-responses.ts @@ -429,7 +429,7 @@ const queriesToCapture: { // Clone the response before extracting the data const cloned = response.clone(); - const newWebhookId = (await cloned.json()).webHookResult.webHookID; + const newWebhookId = (await cloned.json()).webhookResult.webhookID; await client(`/Webhook.Delete(${newWebhookId})`); return { url, method: "POST", response }; @@ -457,7 +457,7 @@ const queriesToCapture: { // Clone the response before extracting the data const cloned = response.clone(); - const newWebhookId = (await cloned.json()).webHookResult.webHookID; + const newWebhookId = (await cloned.json()).webhookResult.webhookID; await client(`/Webhook.Delete(${newWebhookId})`); return { url, method: "POST", response }; @@ -469,7 +469,7 @@ const queriesToCapture: { execute: async (client) => { const listResponse = await client("/Webhook.GetAll"); const listData = await listResponse.json(); - const webhookId = listData.WebHook?.[0]?.webHookID; + const webhookId = listData.webhooks?.[0]?.webhookID; if (!webhookId) { throw new Error("No webhook ID found"); } @@ -498,7 +498,7 @@ const queriesToCapture: { execute: async (client) => { const listResponse = await client("/Webhook.GetAll"); const listData = await listResponse.json(); - const webhookId = listData.WebHook?.[0]?.webHookID; + const webhookId = listData.webhooks?.[0]?.webhookID; if (!webhookId) { throw new Error("No webhook ID found"); } diff --git a/packages/fmodata/scripts/test-webhooks.ts b/packages/fmodata/scripts/test-webhooks.ts index 78923a31..f069276c 100644 --- a/packages/fmodata/scripts/test-webhooks.ts +++ b/packages/fmodata/scripts/test-webhooks.ts @@ -87,7 +87,9 @@ async function testWebhookMethods() { console.log("Result structure:"); console.log(JSON.stringify(listResult, null, 2)); console.log("\nTypeScript type should be:"); - console.log(" { Status: string; WebHook: Array<{ webHookID: number; tableName: string; url: string; ... }> }"); + console.log( + " { status: string; webhooks: Array<{ webhookID: number; tableName: string; webhook: string; ... }> }", + ); console.log("\n"); } catch (error: unknown) { console.log("❌ list() failed:", error instanceof Error ? error.message : String(error)); @@ -97,7 +99,7 @@ async function testWebhookMethods() { // Test 2: Add a webhook console.log("=== Test 2: Add Webhook ===\n"); - let webhookId: string | number | undefined; + let webhookId: number | undefined; try { const addResult = await db.webhook.add({ webhook: "https://example.com/webhook", @@ -110,15 +112,15 @@ async function testWebhookMethods() { console.log("Result structure:"); console.log(JSON.stringify(addResult, null, 2)); console.log("\nTypeScript type should be:"); - console.log(" { webHookResult: { webHookID: number } }"); + console.log(" { webhookResult: { webhookID: number } }"); // Try to extract webhook ID from nested structure if (typeof addResult === "object" && addResult !== null) { - const result = addResult as Record; - if ("webHookResult" in result) { - const webHookResult = result.webHookResult as Record; - if (webHookResult && "webHookID" in webHookResult) { - webhookId = webHookResult.webHookID as number; + const result = addResult as unknown as Record; + if ("webhookResult" in result) { + const webhookResult = result.webhookResult as Record; + if (webhookResult && "webhookID" in webhookResult) { + webhookId = webhookResult.webhookID as number; } } else if ("id" in result) { webhookId = result.id as number; @@ -148,7 +150,7 @@ async function testWebhookMethods() { console.log(JSON.stringify(getResult, null, 2)); console.log("\nTypeScript type should be:"); console.log( - " { webHookID: number; tableName: string; url: string; headers?: Record; notifySchemaChanges: boolean; select: string; filter: string; pendingOperations: unknown[] }", + " { webhookID: number; tableName: string; webhook: string; headers?: Record; notifySchemaChanges: boolean; select: string; filter: string; pendingOperations: unknown[] }", ); console.log("\n"); } catch (error: unknown) { @@ -227,8 +229,11 @@ async function testWebhookMethods() { } catch (error: unknown) { console.log("✅ get() failed as expected"); console.log("Error type:", error?.constructor?.name ?? typeof error); - console.log("Error message:", error.message); - console.log("Error:", JSON.stringify(error, Object.getOwnPropertyNames(error), 2)); + console.log("Error message:", error instanceof Error ? error.message : String(error)); + console.log( + "Error:", + JSON.stringify(error, error instanceof Error ? Object.getOwnPropertyNames(error) : undefined, 2), + ); console.log("\n"); } } catch (error: unknown) { diff --git a/packages/fmodata/scripts/verify-webhook-types.ts b/packages/fmodata/scripts/verify-webhook-types.ts new file mode 100644 index 00000000..ba9e777e --- /dev/null +++ b/packages/fmodata/scripts/verify-webhook-types.ts @@ -0,0 +1,251 @@ +/** + * Webhook API Response Verification Script + * + * This script makes RAW HTTP requests (no library wrappers) to verify the + * exact JSON structure returned by the FileMaker OData webhook endpoints. + * + * It tests: Webhook.GetAll, Webhook.Add, Webhook.Get, Webhook.Delete + * and compares the actual response keys against our TypeScript type definitions. + * + * Usage: + * bun run scripts/verify-webhook-types.ts + */ + +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { config } from "dotenv"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +config({ path: path.resolve(__dirname, "../.env.local") }); + +const serverUrl = process.env.FMODATA_SERVER_URL; +const apiKey = process.env.FMODATA_API_KEY; +const database = process.env.FMODATA_DATABASE; + +if (!(serverUrl && database && apiKey)) { + throw new Error("FMODATA_SERVER_URL, FMODATA_API_KEY, and FMODATA_DATABASE are required in .env.local"); +} + +const baseUrl = `${serverUrl.replace(/\/+$/, "")}/otto/fmi/odata/v4/${encodeURIComponent(database)}`; + +async function rawRequest( + endpoint: string, + method = "GET", + body?: unknown, +): Promise<{ status: number; json: unknown; raw: string }> { + const url = `${baseUrl}${endpoint}`; + const headers: Record = { + Authorization: `Bearer ${apiKey}`, + }; + if (body) { + headers["Content-Type"] = "application/json"; + } + + const res = await fetch(url, { + method, + headers, + body: body ? JSON.stringify(body) : undefined, + }); + + const raw = await res.text(); + let json: unknown; + try { + json = JSON.parse(raw); + } catch { + json = null; + } + return { status: res.status, json, raw }; +} + +interface TypeMismatch { + endpoint: string; + expected: string[]; + actual: string[]; + missing: string[]; + extra: string[]; +} + +const mismatches: TypeMismatch[] = []; + +function compareKeys(label: string, expectedKeys: string[], actual: Record) { + const actualKeys = Object.keys(actual).filter((k) => !k.startsWith("@")); // ignore @context etc. + const missing = expectedKeys.filter((k) => !actualKeys.includes(k)); + const extra = actualKeys.filter((k) => !expectedKeys.includes(k)); + + if (missing.length > 0 || extra.length > 0) { + mismatches.push({ + endpoint: label, + expected: expectedKeys, + actual: actualKeys, + missing, + extra, + }); + console.log(` MISMATCH for ${label}:`); + if (missing.length) { + console.log(` Missing from response: ${missing.join(", ")}`); + } + if (extra.length) { + console.log(` Extra in response: ${extra.join(", ")}`); + } + } else { + console.log(` OK: ${label} keys match`); + } +} + +async function main() { + console.log("Webhook API Response Verification"); + console.log("==================================\n"); + + // 1. Webhook.GetAll (list) + console.log("--- Webhook.GetAll ---"); + const listRes = await rawRequest("/Webhook.GetAll"); + console.log(` Status: ${listRes.status}`); + console.log(` Raw JSON:\n${JSON.stringify(listRes.json, null, 2)}\n`); + + if (listRes.json && typeof listRes.json === "object") { + const listData = listRes.json as Record; + + // Our type expects: { status: string; webhooks: WebhookInfo[] } + compareKeys("WebhookListResponse", ["status", "webhooks"], listData); + + // Check first webhook item structure + let webhooksKey: string | null = null; + if ("webhooks" in listData) { + webhooksKey = "webhooks"; + } else if ("WebHook" in listData) { + webhooksKey = "WebHook"; + } + if (webhooksKey) { + const webhooks = listData[webhooksKey] as Record[]; + if (webhooks && webhooks.length > 0) { + const first = webhooks[0] as Record; + compareKeys( + "WebhookInfo (from list)", + [ + "webhookID", + "tableName", + "webhook", + "headers", + "notifySchemaChanges", + "select", + "filter", + "pendingOperations", + ], + first, + ); + } + } + } + + // 2. Webhook.Add + console.log("\n--- Webhook.Add ---"); + const addRes = await rawRequest("/Webhook.Add", "POST", { + webhook: "https://example.com/verify-test-webhook", + tableName: "contacts", + headers: { "X-Verify-Test": "true" }, + notifySchemaChanges: false, + }); + console.log(` Status: ${addRes.status}`); + console.log(` Raw JSON:\n${JSON.stringify(addRes.json, null, 2)}\n`); + + let addedWebhookId: number | undefined; + + if (addRes.json && typeof addRes.json === "object") { + const addData = addRes.json as Record; + + // Our type expects: { webhookResult: { webhookID: number } } + compareKeys("WebhookAddResponse", ["webhookResult"], addData); + + // Check both possible keys + let resultKey: string | null = null; + if ("webhookResult" in addData) { + resultKey = "webhookResult"; + } else if ("webHookResult" in addData) { + resultKey = "webHookResult"; + } + if (resultKey) { + const result = addData[resultKey] as Record; + compareKeys("WebhookAddResponse inner", ["webhookID"], result); + // Try both key casings + addedWebhookId = (result.webhookID ?? result.webHookID) as number; + } + } + + // 3. Webhook.Get (using the ID from add) + if (addedWebhookId !== undefined) { + console.log(`\n--- Webhook.Get(${addedWebhookId}) ---`); + const getRes = await rawRequest(`/Webhook.Get(${addedWebhookId})`); + console.log(` Status: ${getRes.status}`); + console.log(` Raw JSON:\n${JSON.stringify(getRes.json, null, 2)}\n`); + + if (getRes.json && typeof getRes.json === "object") { + compareKeys( + "WebhookInfo (from get)", + [ + "webhookID", + "tableName", + "webhook", + "headers", + "notifySchemaChanges", + "select", + "filter", + "pendingOperations", + ], + getRes.json as Record, + ); + } + + // 4. Webhook.Delete + console.log(`\n--- Webhook.Delete(${addedWebhookId}) ---`); + const deleteRes = await rawRequest(`/Webhook.Delete(${addedWebhookId})`, "POST"); + console.log(` Status: ${deleteRes.status}`); + console.log(` Raw JSON:\n${JSON.stringify(deleteRes.json, null, 2)}\n`); + + if (deleteRes.json && typeof deleteRes.json === "object") { + compareKeys("WebhookDeleteResponse", ["webhookResult"], deleteRes.json as Record); + } + } else { + console.log("\n SKIP Webhook.Get and Webhook.Delete - no webhook ID from add"); + } + + // 5. Error case + console.log("\n--- Webhook.Get(99999) (not found) ---"); + const notFoundRes = await rawRequest("/Webhook.Get(99999)"); + console.log(` Status: ${notFoundRes.status}`); + console.log(` Raw JSON:\n${JSON.stringify(notFoundRes.json, null, 2)}\n`); + + // Summary + console.log("\n=================================="); + if (mismatches.length === 0) { + console.log("ALL TYPES MATCH the API response structure."); + } else { + console.log(`FOUND ${mismatches.length} TYPE MISMATCH(ES):\n`); + for (const m of mismatches) { + console.log(` ${m.endpoint}:`); + console.log(` Expected keys: ${m.expected.join(", ")}`); + console.log(` Actual keys: ${m.actual.join(", ")}`); + if (m.missing.length) { + console.log(` Missing: ${m.missing.join(", ")}`); + } + if (m.extra.length) { + console.log(` Extra: ${m.extra.join(", ")}`); + } + console.log(); + } + } + + return mismatches; +} + +main() + .then((mismatches) => { + if (mismatches.length > 0) { + process.exit(1); + } + }) + .catch((err) => { + console.error("Script failed:", err); + process.exit(1); + }); diff --git a/packages/fmodata/src/client/webhook-builder.ts b/packages/fmodata/src/client/webhook-builder.ts index a0ab52a7..d102543f 100644 --- a/packages/fmodata/src/client/webhook-builder.ts +++ b/packages/fmodata/src/client/webhook-builder.ts @@ -18,9 +18,9 @@ export interface Webhook { * Webhook information returned by the API */ export interface WebhookInfo { - webHookID: number; + webhookID: number; tableName: string; - url: string; + webhook: string; headers?: Record; notifySchemaChanges: boolean; select: string; @@ -32,16 +32,16 @@ export interface WebhookInfo { * Response from listing all webhooks */ export interface WebhookListResponse { - Status: string; - WebHook: WebhookInfo[]; + status: string; + webhooks: WebhookInfo[]; } /** * Response from adding a webhook */ export interface WebhookAddResponse { - webHookResult: { - webHookID: number; + webhookResult: { + webhookID: number; }; } @@ -71,7 +71,7 @@ export class WebhookManager { * tableName: contactsTable, * headers: { "X-Custom-Header": "value" }, * }); - * // result.webHookResult.webHookID contains the new webhook ID + * // result.webhookResult.webhookID contains the new webhook ID * ``` * @example * ```ts @@ -186,7 +186,7 @@ export class WebhookManager { * @example * ```ts * const webhook = await db.webhook.get(1); - * // webhook.webHookID, webhook.tableName, webhook.url, etc. + * // webhook.webhookID, webhook.tableName, webhook.webhook, etc. * ``` */ async get(webhookId: number, options?: ExecuteMethodOptions): Promise { @@ -208,8 +208,8 @@ export class WebhookManager { * @example * ```ts * const result = await db.webhook.list(); - * // result.Status contains the status - * // result.WebHook contains the array of webhooks + * // result.status contains the status + * // result.webhooks contains the array of webhooks * ``` */ async list(options?: ExecuteMethodOptions): Promise { diff --git a/packages/fmodata/tests/fixtures/responses.ts b/packages/fmodata/tests/fixtures/responses.ts index a9c57005..b4f74644 100644 --- a/packages/fmodata/tests/fixtures/responses.ts +++ b/packages/fmodata/tests/fixtures/responses.ts @@ -653,12 +653,12 @@ export const mockResponses = { }, response: { "@context": "https://api.example.com/fmi/odata/v4/fmdapi_test.fmp12/$metadata#WebHook Processor", - Status: "ACTIVE", - WebHook: [ + status: "ACTIVE", + webhooks: [ { - webHookID: 1, + webhookID: 1, tableName: "contacts", - url: "https://example.com/webhook", + webhook: "https://example.com/webhook", headers: { "X-Custom-Header": "test-value", }, @@ -668,9 +668,9 @@ export const mockResponses = { pendingOperations: [], }, { - webHookID: 2, + webhookID: 2, tableName: "contacts", - url: "https://example.com/webhook", + webhook: "https://example.com/webhook", headers: { "X-Custom-Header": "test-value", }, @@ -680,9 +680,9 @@ export const mockResponses = { pendingOperations: [], }, { - webHookID: 3, + webhookID: 3, tableName: "contacts", - url: "https://example.com/webhook", + webhook: "https://example.com/webhook", headers: { "X-Custom-Header": "test-value", }, @@ -692,9 +692,9 @@ export const mockResponses = { pendingOperations: [], }, { - webHookID: 6, + webhookID: 6, tableName: "contacts", - url: "https://example.com/webhook", + webhook: "https://example.com/webhook", headers: { "X-Custom-Header": "test-value", }, @@ -704,9 +704,9 @@ export const mockResponses = { pendingOperations: [], }, { - webHookID: 4, + webhookID: 4, tableName: "contacts", - url: "https://example.com/webhook", + webhook: "https://example.com/webhook", headers: { "X-Custom-Header": "test-value", }, @@ -716,9 +716,9 @@ export const mockResponses = { pendingOperations: [], }, { - webHookID: 7, + webhookID: 7, tableName: "contacts", - url: "https://example.com/webhook", + webhook: "https://example.com/webhook", headers: { "X-Custom-Header": "test-value", }, @@ -739,8 +739,8 @@ export const mockResponses = { "content-type": "application/json;charset=utf-8", }, response: { - webHookResult: { - webHookID: 5, + webhookResult: { + webhookID: 5, }, }, }, @@ -753,8 +753,8 @@ export const mockResponses = { "content-type": "application/json;charset=utf-8", }, response: { - webHookResult: { - webHookID: 8, + webhookResult: { + webhookID: 8, }, }, }, @@ -768,9 +768,9 @@ export const mockResponses = { }, response: { "@context": "https://api.example.com/fmi/odata/v4/fmdapi_test.fmp12/$metadata#WebHook", - webHookID: 1, + webhookID: 1, tableName: "contacts", - url: "https://example.com/webhook", + webhook: "https://example.com/webhook", headers: { "X-Custom-Header": "test-value", }, @@ -804,8 +804,8 @@ export const mockResponses = { "content-type": "application/json;charset=utf-8", }, response: { - webHookResult: { - webHookID: 1, + webhookResult: { + webhookID: 1, }, }, }, diff --git a/packages/fmodata/tests/webhooks.test.ts b/packages/fmodata/tests/webhooks.test.ts index 02d41d7d..f0967648 100644 --- a/packages/fmodata/tests/webhooks.test.ts +++ b/packages/fmodata/tests/webhooks.test.ts @@ -36,16 +36,16 @@ describe("WebhookManager", () => { }); expect(result).toBeDefined(); - expect(result.Status).toBe("ACTIVE"); - expect(Array.isArray(result.WebHook)).toBe(true); - expect(result.WebHook.length).toBeGreaterThan(0); + expect(result.status).toBe("ACTIVE"); + expect(Array.isArray(result.webhooks)).toBe(true); + expect(result.webhooks.length).toBeGreaterThan(0); // Extract expected data from mock response const mockData = mockResponses["webhook-list"].response; - const expectedWebhooks = mockData.WebHook; - expect(result.WebHook.length).toBe(expectedWebhooks.length); + const expectedWebhooks = mockData.webhooks; + expect(result.webhooks.length).toBe(expectedWebhooks.length); - const firstWebhook = result.WebHook[0]; + const firstWebhook = result.webhooks[0]; expect(firstWebhook).toBeDefined(); if (!firstWebhook) { throw new Error("Expected firstWebhook to be defined"); @@ -56,9 +56,9 @@ describe("WebhookManager", () => { if (!expectedFirstWebhook) { throw new Error("Expected first webhook in mock response"); } - expect(firstWebhook.webHookID).toBe(expectedFirstWebhook.webHookID); + expect(firstWebhook.webhookID).toBe(expectedFirstWebhook.webhookID); expect(firstWebhook.tableName).toBe("contacts"); - expect(firstWebhook.url).toBe("https://example.com/webhook"); + expect(firstWebhook.webhook).toBe("https://example.com/webhook"); expect(firstWebhook.headers).toEqual({ "X-Custom-Header": "test-value" }); expect(firstWebhook.notifySchemaChanges).toBe(false); expect(firstWebhook.select).toBe(""); @@ -73,12 +73,12 @@ describe("WebhookManager", () => { // Type check - result should be WebhookListResponse const typedResult: WebhookListResponse = result; - expect(typedResult.Status).toBe("ACTIVE"); + expect(typedResult.status).toBe("ACTIVE"); // Extract expected ID from mock response const mockData = mockResponses["webhook-list"].response; - const expectedFirstWebhookID = mockData.WebHook[0]?.webHookID; - expect(typedResult.WebHook[0]?.webHookID).toBe(expectedFirstWebhookID); + const expectedFirstWebhookID = mockData.webhooks[0]?.webhookID; + expect(typedResult.webhooks[0]?.webhookID).toBe(expectedFirstWebhookID); }); }); @@ -96,11 +96,11 @@ describe("WebhookManager", () => { ); expect(result).toBeDefined(); - expect(result.webHookResult).toBeDefined(); + expect(result.webhookResult).toBeDefined(); // Extract expected ID from mock response - const expectedWebhookID = mockResponses["webhook-add"].response.webHookResult.webHookID; - expect(result.webHookResult.webHookID).toBe(expectedWebhookID); + const expectedWebhookID = mockResponses["webhook-add"].response.webhookResult.webhookID; + expect(result.webhookResult.webhookID).toBe(expectedWebhookID); }); it("should extract table name from FMTable instance", async () => { @@ -115,13 +115,13 @@ describe("WebhookManager", () => { ); // Extract expected ID from mock response - const expectedWebhookID = mockResponses["webhook-add"].response.webHookResult.webHookID; - expect(result.webHookResult.webHookID).toBe(expectedWebhookID); + const expectedWebhookID = mockResponses["webhook-add"].response.webhookResult.webhookID; + expect(result.webhookResult.webhookID).toBe(expectedWebhookID); }); it("should support the same filter/select DX as the main query builder", async () => { let requestBody: string | null = null; - const _result = await db.webhook.add( + await db.webhook.add( { webhook: "https://example.com/webhook", tableName: contacts, @@ -170,8 +170,8 @@ describe("WebhookManager", () => { const typedResult: WebhookAddResponse = result; // Extract expected ID from mock response - const expectedWebhookID = mockResponses["webhook-add"].response.webHookResult.webHookID; - expect(typedResult.webHookResult.webHookID).toBe(expectedWebhookID); + const expectedWebhookID = mockResponses["webhook-add"].response.webhookResult.webhookID; + expect(typedResult.webhookResult.webhookID).toBe(expectedWebhookID); }); }); @@ -179,16 +179,16 @@ describe("WebhookManager", () => { it("should get a webhook by ID", async () => { // Extract webhook ID from mock response URL or response data const mockData = mockResponses["webhook-get"].response; - const webhookID = mockData.webHookID; + const webhookID = mockData.webhookID; const result = await db.webhook.get(webhookID, { fetchHandler: createMockFetch(mockResponses["webhook-get"]), }); expect(result).toBeDefined(); - expect(result.webHookID).toBe(webhookID); + expect(result.webhookID).toBe(webhookID); expect(result.tableName).toBe("contacts"); - expect(result.url).toBe("https://example.com/webhook"); + expect(result.webhook).toBe("https://example.com/webhook"); expect(result.headers).toEqual({ "X-Custom-Header": "test-value" }); expect(result.notifySchemaChanges).toBe(false); expect(result.select).toBe(""); @@ -207,7 +207,7 @@ describe("WebhookManager", () => { it("should have correct TypeScript types", async () => { // Extract webhook ID from mock response const mockData = mockResponses["webhook-get"].response; - const webhookID = mockData.webHookID; + const webhookID = mockData.webhookID; const result = await db.webhook.get(webhookID, { fetchHandler: createMockFetch(mockResponses["webhook-get"]), @@ -215,7 +215,7 @@ describe("WebhookManager", () => { // Type check - result should be WebhookInfo const typedResult: WebhookInfo = result; - expect(typedResult.webHookID).toBe(webhookID); + expect(typedResult.webhookID).toBe(webhookID); expect(typedResult.tableName).toBe("contacts"); }); }); @@ -223,7 +223,7 @@ describe("WebhookManager", () => { describe("remove()", () => { it("should remove a webhook successfully", async () => { // Extract webhook ID from mock response - const webhookID = mockResponses["webhook-delete"].response.webHookResult.webHookID; + const webhookID = mockResponses["webhook-delete"].response.webhookResult.webhookID; await expect( db.webhook.remove(webhookID, { @@ -234,7 +234,7 @@ describe("WebhookManager", () => { it("should return void on success", async () => { // Extract webhook ID from mock response - const webhookID = mockResponses["webhook-delete"].response.webHookResult.webHookID; + const webhookID = mockResponses["webhook-delete"].response.webhookResult.webhookID; const result = await db.webhook.remove(webhookID, { fetchHandler: createMockFetch(mockResponses["webhook-delete"]), @@ -272,8 +272,8 @@ describe("WebhookManager", () => { describe("integration", () => { it("should add, get, and remove a webhook in sequence", async () => { // Extract expected IDs from mock responses - const expectedAddID = mockResponses["webhook-add"].response.webHookResult.webHookID; - const expectedGetID = mockResponses["webhook-get"].response.webHookID; + const expectedAddID = mockResponses["webhook-add"].response.webhookResult.webhookID; + const expectedGetID = mockResponses["webhook-get"].response.webhookID; // Add webhook const addResult = await db.webhook.add( @@ -286,7 +286,7 @@ describe("WebhookManager", () => { }, ); - expect(addResult.webHookResult.webHookID).toBe(expectedAddID); + expect(addResult.webhookResult.webhookID).toBe(expectedAddID); // Get webhook - use the ID from the add result, but verify it matches expected get ID // Note: In a real scenario, the get would use the add result ID, but for mocking @@ -295,10 +295,10 @@ describe("WebhookManager", () => { fetchHandler: createMockFetch(mockResponses["webhook-get"]), }); - expect(getResult.webHookID).toBe(expectedGetID); + expect(getResult.webhookID).toBe(expectedGetID); // Remove webhook - use the ID from the delete mock response - const expectedDeleteID = mockResponses["webhook-delete"].response.webHookResult.webHookID; + const expectedDeleteID = mockResponses["webhook-delete"].response.webhookResult.webhookID; await expect( db.webhook.remove(expectedDeleteID, { fetchHandler: createMockFetch(mockResponses["webhook-delete"]),