quickbase-js 1.1.0-beta.0 → 1.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/quickbase.d.ts +356 -6
  3. package/dist/quickbase.js +363 -20
  4. package/dist/quickbase.js.map +1 -1
  5. package/examples/react.js +42 -16
  6. package/examples/vanillajs.html +32 -8
  7. package/log-gen.yaml +146 -198
  8. package/package.json +4 -4
  9. package/src/code-generation/fix-spec-definitions.ts +99 -41
  10. package/src/code-generation/fix-spec-paths.ts +6 -19
  11. package/src/code-generation/generate-unified-interface.ts +1 -8
  12. package/src/code-generation/output/quickbase-fixed.json +142 -21
  13. package/src/generated-unified/QuickbaseClient.ts +3 -3
  14. package/tests/playwright/qb/auth/cacheSwitching.test.ts +2 -2
  15. package/tests/setup.ts +29 -9
  16. package/tests/vitest/qb/fields/getFields.test.ts +3 -5
  17. package/tests/vitest/qb/records/runQuery.test.ts +82 -0
  18. package/tests/vitest/unit/apps/getApp.test.ts +17 -12
  19. package/tests/vitest/unit/auth/fetchTempToken401.test.ts +29 -4
  20. package/tests/vitest/unit/auth/getTempToken.test.ts +30 -17
  21. package/tests/vitest/unit/auth/retryOn401.test.ts +39 -5
  22. package/tests/vitest/unit/fields/getFields.test.ts +14 -7
  23. package/tests/vitest/unit/records/deleteRecords.test.ts +28 -34
  24. package/tests/vitest/unit/records/runQuery.test.ts +102 -0
  25. package/tests/vitest/unit/records/upsert.test.ts +31 -41
  26. package/tests/vitest/unit/tables/createTable.test.ts +18 -10
  27. package/tests/vitest/unit/tables/deleteTable.test.ts +28 -19
  28. package/tests/vitest/unit/tables/getAppTables.test.ts +41 -28
  29. package/tests/vitest/unit/tables/getTable.test.ts +53 -32
  30. package/tests/vitest/unit/tables/updateTable.test.ts +17 -11
  31. package/vitest.config.ts +5 -0
@@ -79,10 +79,7 @@ export const definitions = {
79
79
  type: "string",
80
80
  description: "The unique identifier for this application.",
81
81
  },
82
- name: {
83
- type: "string",
84
- description: "The app name.",
85
- },
82
+ name: { type: "string", description: "The app name." },
86
83
  description: {
87
84
  type: "string",
88
85
  description: "The description for the app.",
@@ -197,10 +194,7 @@ export const definitions = {
197
194
  type: "string",
198
195
  description: "The unique identifier (dbid) of the table.",
199
196
  },
200
- name: {
201
- type: "string",
202
- description: "The name of the table.",
203
- },
197
+ name: { type: "string", description: "The name of the table." },
204
198
  alias: {
205
199
  type: "string",
206
200
  description: "The automatically-created table alias for the table.",
@@ -275,10 +269,7 @@ export const definitions = {
275
269
  UpdateTableRequest: {
276
270
  type: "object",
277
271
  properties: {
278
- name: {
279
- type: "string",
280
- description: "The new name for the table.",
281
- },
272
+ name: { type: "string", description: "The new name for the table." },
282
273
  singleRecordName: {
283
274
  type: "string",
284
275
  description:
@@ -301,10 +292,7 @@ export const definitions = {
301
292
  DeleteTableResponse: {
302
293
  type: "object",
303
294
  properties: {
304
- deletedTableId: {
305
- type: "string",
306
- description: "The deleted table id.",
307
- },
295
+ deletedTableId: { type: "string", description: "The deleted table id." },
308
296
  },
309
297
  },
310
298
  Record: {
@@ -358,9 +346,7 @@ export const definitions = {
358
346
  type: "object",
359
347
  additionalProperties: {
360
348
  type: "object",
361
- properties: {
362
- value: { type: "any" },
363
- },
349
+ properties: { value: { type: "any" } },
364
350
  },
365
351
  },
366
352
  description: "Array of upserted records with field IDs and values.",
@@ -401,9 +387,7 @@ export const definitions = {
401
387
  type: "object",
402
388
  additionalProperties: {
403
389
  type: "object",
404
- properties: {
405
- value: { type: "any" },
406
- },
390
+ properties: { value: { type: "any" } },
407
391
  },
408
392
  },
409
393
  description: "Array of successfully upserted records (may be empty).",
@@ -411,29 +395,15 @@ export const definitions = {
411
395
  metadata: {
412
396
  type: "object",
413
397
  properties: {
414
- createdRecordIds: {
415
- type: "array",
416
- items: { type: "integer" },
417
- },
418
- updatedRecordIds: {
419
- type: "array",
420
- items: { type: "integer" },
421
- },
422
- unchangedRecordIds: {
423
- type: "array",
424
- items: { type: "integer" },
425
- },
398
+ createdRecordIds: { type: "array", items: { type: "integer" } },
399
+ updatedRecordIds: { type: "array", items: { type: "integer" } },
400
+ unchangedRecordIds: { type: "array", items: { type: "integer" } },
426
401
  lineErrors: {
427
402
  type: "object",
428
- additionalProperties: {
429
- type: "array",
430
- items: { type: "string" },
431
- },
403
+ additionalProperties: { type: "array", items: { type: "string" } },
432
404
  description: "Errors by line number (1-based index).",
433
405
  },
434
- totalNumberOfRecordsProcessed: {
435
- type: "integer",
436
- },
406
+ totalNumberOfRecordsProcessed: { type: "integer" },
437
407
  },
438
408
  required: ["totalNumberOfRecordsProcessed"],
439
409
  },
@@ -482,4 +452,92 @@ export const definitions = {
482
452
  required: ["numberDeleted"],
483
453
  description: "Response body for successful deletion of records.",
484
454
  },
455
+ RunQueryRequest: {
456
+ type: "object",
457
+ required: ["from"],
458
+ properties: {
459
+ from: { type: "string", description: "Table ID (dbid)" },
460
+ select: {
461
+ type: "array",
462
+ items: { type: "integer" },
463
+ description: "Field IDs to return",
464
+ },
465
+ where: { type: "string", description: "Query string" },
466
+ sortBy: {
467
+ type: "array",
468
+ items: {
469
+ type: "object",
470
+ properties: {
471
+ fieldId: { type: "integer" },
472
+ order: { type: "string", enum: ["ASC", "DESC"] },
473
+ },
474
+ },
475
+ description: "Sort criteria",
476
+ },
477
+ groupBy: {
478
+ type: "array",
479
+ items: {
480
+ type: "object",
481
+ properties: {
482
+ fieldId: { type: "integer" },
483
+ grouping: { type: "string", enum: ["equal-values"] },
484
+ },
485
+ },
486
+ description: "Grouping criteria",
487
+ },
488
+ options: {
489
+ type: "object",
490
+ properties: {
491
+ skip: { type: "integer", description: "Number of records to skip" },
492
+ top: {
493
+ type: "integer",
494
+ description: "Max number of records to return",
495
+ },
496
+ compareWithAppLocalTime: {
497
+ type: "boolean",
498
+ description: "Compare times with app local time",
499
+ },
500
+ },
501
+ },
502
+ },
503
+ },
504
+ RunQueryResponse: {
505
+ type: "object",
506
+ properties: {
507
+ data: {
508
+ type: "array",
509
+ items: {
510
+ type: "object",
511
+ additionalProperties: {
512
+ type: "object",
513
+ properties: { value: { type: "any" } },
514
+ },
515
+ },
516
+ description: "Array of record data with field IDs as keys",
517
+ },
518
+ fields: {
519
+ type: "array",
520
+ items: {
521
+ type: "object",
522
+ properties: {
523
+ id: { type: "integer" },
524
+ label: { type: "string" },
525
+ type: { type: "string" },
526
+ },
527
+ },
528
+ description: "Field metadata",
529
+ },
530
+ metadata: {
531
+ type: "object",
532
+ properties: {
533
+ numFields: { type: "integer" },
534
+ numRecords: { type: "integer" },
535
+ skip: { type: "integer" },
536
+ top: { type: "integer" },
537
+ totalRecords: { type: "integer" },
538
+ },
539
+ description: "Query metadata",
540
+ },
541
+ },
542
+ },
485
543
  };
@@ -280,34 +280,21 @@ export const paths = {
280
280
  },
281
281
  "/records/query": {
282
282
  post: {
283
- operationId: "upsertRecords",
284
- summary: "Upsert records (alternative endpoint)",
283
+ operationId: "runQuery",
284
+ summary: "Query records in a table",
285
285
  tags: ["Records"],
286
286
  parameters: [
287
287
  {
288
- name: "generated",
288
+ name: "body",
289
289
  in: "body",
290
290
  required: true,
291
- schema: {
292
- type: "array",
293
- items: { $ref: "#/definitions/Record" },
294
- },
291
+ schema: { $ref: "#/definitions/RunQueryRequest" },
295
292
  },
296
293
  ],
297
294
  responses: {
298
295
  200: {
299
- description: "Success",
300
- schema: {
301
- type: "array",
302
- items: { $ref: "#/definitions/Upsert200Response" },
303
- },
304
- },
305
- 207: {
306
- description: "Multi-Status (partial success)",
307
- schema: {
308
- type: "array",
309
- items: { $ref: "#/definitions/Upsert207Response" },
310
- },
296
+ description: "Success - records returned",
297
+ schema: { $ref: "#/definitions/RunQueryResponse" },
311
298
  },
312
299
  },
313
300
  },
@@ -48,20 +48,15 @@ function generateInterface() {
48
48
  })
49
49
  .map((p) => {
50
50
  const param = p as OpenAPIV2.ParameterObject;
51
- let type = param.type
51
+ const type = param.type
52
52
  ? mapOpenApiTypeToTs(param.type)
53
53
  : param.schema
54
54
  ? mapRefToType(param.schema, modelImports)
55
55
  : "any";
56
- // Special handling for upsertRecords 'generated' parameter
57
- if (opId === "upsertRecords" && param.name === "generated") {
58
- type = "{ [key: string]: string }[]";
59
- }
60
56
  return `${param.name}${param.required ? "" : "?"}: ${type}`;
61
57
  })
62
58
  .join("; ");
63
59
 
64
- // Handle responses, ensuring distinct types for 200 and 207
65
60
  const successResponses = ["200", "207"]
66
61
  .map((code) => ({
67
62
  code,
@@ -73,7 +68,6 @@ function generateInterface() {
73
68
  const returnTypes = successResponses.map(({ response }) =>
74
69
  mapRefToType(response!.schema, modelImports)
75
70
  );
76
- // Deduplicate return types and join with " | ", default to "void" if empty
77
71
  const uniqueReturnTypes = [...new Set(returnTypes)];
78
72
  const returnType =
79
73
  uniqueReturnTypes.length > 0 ? uniqueReturnTypes.join(" | ") : "void";
@@ -84,7 +78,6 @@ function generateInterface() {
84
78
  }
85
79
  }
86
80
 
87
- // Generate import statements for all model types
88
81
  const importStatement =
89
82
  modelImports.size > 0
90
83
  ? `import { ${Array.from(modelImports)
@@ -4095,41 +4095,26 @@
4095
4095
  },
4096
4096
  "/records/query": {
4097
4097
  "post": {
4098
- "operationId": "upsertRecords",
4099
- "summary": "Upsert records (alternative endpoint)",
4098
+ "operationId": "runQuery",
4099
+ "summary": "Query records in a table",
4100
4100
  "tags": [
4101
4101
  "Records"
4102
4102
  ],
4103
4103
  "parameters": [
4104
4104
  {
4105
- "name": "generated",
4105
+ "name": "body",
4106
4106
  "in": "body",
4107
4107
  "required": true,
4108
4108
  "schema": {
4109
- "type": "array",
4110
- "items": {
4111
- "$ref": "#/definitions/Record"
4112
- }
4109
+ "$ref": "#/definitions/RunQueryRequest"
4113
4110
  }
4114
4111
  }
4115
4112
  ],
4116
4113
  "responses": {
4117
4114
  "200": {
4118
- "description": "Success",
4119
- "schema": {
4120
- "type": "array",
4121
- "items": {
4122
- "$ref": "#/definitions/Upsert200Response"
4123
- }
4124
- }
4125
- },
4126
- "207": {
4127
- "description": "Multi-Status (partial success)",
4115
+ "description": "Success - records returned",
4128
4116
  "schema": {
4129
- "type": "array",
4130
- "items": {
4131
- "$ref": "#/definitions/Upsert207Response"
4132
- }
4117
+ "$ref": "#/definitions/RunQueryResponse"
4133
4118
  }
4134
4119
  }
4135
4120
  }
@@ -7954,6 +7939,142 @@
7954
7939
  "numberDeleted"
7955
7940
  ],
7956
7941
  "description": "Response body for successful deletion of records."
7942
+ },
7943
+ "RunQueryRequest": {
7944
+ "type": "object",
7945
+ "required": [
7946
+ "from"
7947
+ ],
7948
+ "properties": {
7949
+ "from": {
7950
+ "type": "string",
7951
+ "description": "Table ID (dbid)"
7952
+ },
7953
+ "select": {
7954
+ "type": "array",
7955
+ "items": {
7956
+ "type": "integer"
7957
+ },
7958
+ "description": "Field IDs to return"
7959
+ },
7960
+ "where": {
7961
+ "type": "string",
7962
+ "description": "Query string"
7963
+ },
7964
+ "sortBy": {
7965
+ "type": "array",
7966
+ "items": {
7967
+ "type": "object",
7968
+ "properties": {
7969
+ "fieldId": {
7970
+ "type": "integer"
7971
+ },
7972
+ "order": {
7973
+ "type": "string",
7974
+ "enum": [
7975
+ "ASC",
7976
+ "DESC"
7977
+ ]
7978
+ }
7979
+ }
7980
+ },
7981
+ "description": "Sort criteria"
7982
+ },
7983
+ "groupBy": {
7984
+ "type": "array",
7985
+ "items": {
7986
+ "type": "object",
7987
+ "properties": {
7988
+ "fieldId": {
7989
+ "type": "integer"
7990
+ },
7991
+ "grouping": {
7992
+ "type": "string",
7993
+ "enum": [
7994
+ "equal-values"
7995
+ ]
7996
+ }
7997
+ }
7998
+ },
7999
+ "description": "Grouping criteria"
8000
+ },
8001
+ "options": {
8002
+ "type": "object",
8003
+ "properties": {
8004
+ "skip": {
8005
+ "type": "integer",
8006
+ "description": "Number of records to skip"
8007
+ },
8008
+ "top": {
8009
+ "type": "integer",
8010
+ "description": "Max number of records to return"
8011
+ },
8012
+ "compareWithAppLocalTime": {
8013
+ "type": "boolean",
8014
+ "description": "Compare times with app local time"
8015
+ }
8016
+ }
8017
+ }
8018
+ }
8019
+ },
8020
+ "RunQueryResponse": {
8021
+ "type": "object",
8022
+ "properties": {
8023
+ "data": {
8024
+ "type": "array",
8025
+ "items": {
8026
+ "type": "object",
8027
+ "additionalProperties": {
8028
+ "type": "object",
8029
+ "properties": {
8030
+ "value": {
8031
+ "type": "any"
8032
+ }
8033
+ }
8034
+ }
8035
+ },
8036
+ "description": "Array of record data with field IDs as keys"
8037
+ },
8038
+ "fields": {
8039
+ "type": "array",
8040
+ "items": {
8041
+ "type": "object",
8042
+ "properties": {
8043
+ "id": {
8044
+ "type": "integer"
8045
+ },
8046
+ "label": {
8047
+ "type": "string"
8048
+ },
8049
+ "type": {
8050
+ "type": "string"
8051
+ }
8052
+ }
8053
+ },
8054
+ "description": "Field metadata"
8055
+ },
8056
+ "metadata": {
8057
+ "type": "object",
8058
+ "properties": {
8059
+ "numFields": {
8060
+ "type": "integer"
8061
+ },
8062
+ "numRecords": {
8063
+ "type": "integer"
8064
+ },
8065
+ "skip": {
8066
+ "type": "integer"
8067
+ },
8068
+ "top": {
8069
+ "type": "integer"
8070
+ },
8071
+ "totalRecords": {
8072
+ "type": "integer"
8073
+ }
8074
+ },
8075
+ "description": "Query metadata"
8076
+ }
8077
+ }
7957
8078
  }
7958
8079
  }
7959
8080
  }
@@ -1,5 +1,5 @@
1
- // Generated on 2025-03-11T02:50:18.896Z
2
- import { App, CreateApp200Response, CreateAppRequest, DeleteRecords200Response, DeleteRecordsRequest, DeleteTableResponse, Field, GetTempTokenDBID200Response, Record, ReportRunResponse, Table, UpdateTableRequest, Upsert200Response, Upsert207Response, UpsertRequest } from "../generated/models";
1
+ // Generated on 2025-03-11T14:12:45.650Z
2
+ import { App, CreateApp200Response, CreateAppRequest, DeleteRecords200Response, DeleteRecordsRequest, DeleteTableResponse, Field, GetTempTokenDBID200Response, ReportRunResponse, RunQueryRequest, RunQueryResponse, Table, UpdateTableRequest, Upsert200Response, Upsert207Response, UpsertRequest } from "../generated/models";
3
3
 
4
4
  export interface QuickbaseClient {
5
5
  createApp: (params: { body: CreateAppRequest }) => Promise<CreateApp200Response>;
@@ -26,7 +26,7 @@ export interface QuickbaseClient {
26
26
  runFormula: (params: { generated?: any }) => Promise<{ [key: string]: any }>;
27
27
  upsert: (params: { body: UpsertRequest }) => Promise<Upsert200Response | Upsert207Response>;
28
28
  deleteRecords: (params: { body: DeleteRecordsRequest }) => Promise<DeleteRecords200Response>;
29
- upsertRecords: (params: { generated: { [key: string]: string }[] }) => Promise<Upsert200Response[] | Upsert207Response[]>;
29
+ runQuery: (params: { body: RunQueryRequest }) => Promise<RunQueryResponse>;
30
30
  getTempTokenDBID: (params: { dbid: string }) => Promise<GetTempTokenDBID200Response>;
31
31
  exchangeSsoToken: (params: { generated?: any }) => Promise<{ [key: string]: any }>;
32
32
  cloneUserToken: (params: { generated?: any }) => Promise<{ [key: string]: any }>;
@@ -67,13 +67,13 @@ test.describe("QuickbaseClient Integration - Cache Switching with Temp Tokens",
67
67
  console.log("Starting cacheSwitching test");
68
68
  const realm = process.env.QB_REALM;
69
69
  const appId = process.env.QB_APP_ID;
70
- const tableId = process.env.QB_TABLE_ID;
70
+ const tableId = process.env.QB_TABLE_ID_1;
71
71
  const username = process.env.QB_USERNAME;
72
72
  const password = process.env.QB_PASSWORD;
73
73
 
74
74
  if (!realm) throw new Error("QB_REALM is not defined in .env");
75
75
  if (!appId) throw new Error("QB_APP_ID is not defined in .env");
76
- if (!tableId) throw new Error("QB_TABLE_ID is not defined in .env");
76
+ if (!tableId) throw new Error("QB_TABLE_ID_1 is not defined in .env");
77
77
  if (!username) throw new Error("QB_USERNAME is not defined in .env");
78
78
  if (!password) throw new Error("QB_PASSWORD is not defined in .env");
79
79
 
package/tests/setup.ts CHANGED
@@ -2,26 +2,46 @@
2
2
  import { vi } from "vitest";
3
3
  import { quickbase } from "../src/quickbaseClient.ts";
4
4
  import type { QuickbaseConfig } from "../src/quickbaseClient.ts";
5
+ import dotenv from "dotenv";
6
+
7
+ // Load environment variables from .env file
8
+ dotenv.config({ path: "./.env" }); // Assumes .env is at project root
5
9
 
6
10
  export const mockFetch = vi.fn();
7
11
 
12
+ export const QB_REALM = process.env.QB_REALM || "test-realm";
13
+ export const QB_USER_TOKEN =
14
+ process.env.QB_USER_TOKEN || "user-token-1234567890";
15
+ export const QB_APP_ID = process.env.QB_APP_ID || "app-id-1234567890";
16
+ export const QB_TABLE_ID_1 = process.env.QB_TABLE_ID_1 || "table-id-1234567890";
17
+
18
+ // Mock env variables for consistency, using .env values if available
19
+ vi.stubEnv("QB_REALM", QB_REALM);
20
+ vi.stubEnv("QB_USER_TOKEN", QB_USER_TOKEN);
21
+ vi.stubEnv("QB_APP_ID", QB_APP_ID);
22
+ vi.stubEnv("QB_TABLE_ID_1", QB_TABLE_ID_1);
23
+
8
24
  export const createClient = (
9
25
  fetchApi?: any,
10
26
  config: Partial<QuickbaseConfig> = {}
11
27
  ) => {
28
+ // Use loaded env vars with fallbacks for safety
29
+ const realm = QB_REALM;
30
+ const userToken = QB_USER_TOKEN;
31
+
12
32
  const client = quickbase({
13
- realm: process.env.QB_REALM || "test-realm", // Match upsert.test.ts
14
- userToken: process.env.QB_USER_TOKEN || "test-token", // Match upsert.test.ts
33
+ realm,
34
+ userToken,
15
35
  debug: true,
16
36
  fetchApi,
37
+ ...config, // Allow overrides if provided
38
+ });
39
+ console.log("[createClient] Config:", {
40
+ realm,
41
+ userToken,
42
+ debug: true,
17
43
  ...config,
18
44
  });
19
- console.log("[createClient] Config:", config); // Debug config
20
- console.log("[createClient] Returning:", client); // Debug client
45
+ console.log("[createClient] Returning:", client);
21
46
  return client;
22
47
  };
23
-
24
- // Mock env variables for consistency
25
- vi.stubEnv("QB_REALM", "builderprogram-dbradford6815");
26
- vi.stubEnv("QB_USER_TOKEN", "b9f3pk_q4jd_0_b4qu5eebyvuix3xs57ysd7zn3");
27
- vi.stubEnv("QB_APP_ID", "buwai2zpe");
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { createClient } from "../../../setup.ts";
2
+ import { createClient, QB_TABLE_ID_1 } from "../../../setup.ts";
3
3
 
4
4
  describe("QuickbaseClient Integration - getFields", () => {
5
5
  const client = createClient();
@@ -7,8 +7,6 @@ describe("QuickbaseClient Integration - getFields", () => {
7
7
  it(
8
8
  "fetches real field data from QuickBase",
9
9
  async () => {
10
- const tableId = "buwai2z3s";
11
- if (!tableId) throw new Error("Table ID is not defined");
12
10
  if (!process.env.QB_REALM)
13
11
  throw new Error("QB_REALM is not defined in .env");
14
12
  if (!process.env.QB_USER_TOKEN)
@@ -17,10 +15,10 @@ describe("QuickbaseClient Integration - getFields", () => {
17
15
  console.log("Config used:", {
18
16
  realm: process.env.QB_REALM,
19
17
  userToken: process.env.QB_USER_TOKEN,
20
- tableId,
18
+ tableId: QB_TABLE_ID_1,
21
19
  });
22
20
  const result = await client.getFields({
23
- tableId,
21
+ tableId: QB_TABLE_ID_1,
24
22
  includeFieldPerms: true,
25
23
  });
26
24
  console.log("Real API response:", result);
@@ -0,0 +1,82 @@
1
+ // tests/vitest/qb/records/runQuery.test.ts
2
+ import { describe, it, expect } from "vitest";
3
+ import { createClient, QB_TABLE_ID_1 } from "../../../setup.ts"; // Import QB_TABLE_ID_1
4
+ import { RunQueryRequest, RunQueryResponse } from "@/generated/models";
5
+
6
+ describe("QuickbaseClient Integration - runQuery", () => {
7
+ let createdRecordId: number;
8
+
9
+ it(
10
+ "queries real records from QuickBase",
11
+ async () => {
12
+ const client = createClient();
13
+
14
+ // Upsert a test record to query later
15
+ const upsertResponse = await client.upsert({
16
+ body: {
17
+ to: QB_TABLE_ID_1,
18
+ data: [
19
+ {
20
+ "6": { value: "Test Task " + Date.now() }, // Assuming field 6 is a text field
21
+ },
22
+ ],
23
+ fieldsToReturn: [3, 6], // Record ID# and the text field
24
+ },
25
+ });
26
+ createdRecordId = upsertResponse.metadata.createdRecordIds[0];
27
+ console.log("Created test record with ID:", createdRecordId);
28
+
29
+ // Query the record
30
+ const queryRequest: RunQueryRequest = {
31
+ from: QB_TABLE_ID_1,
32
+ select: [3, 6], // Query Record ID# and the text field
33
+ where: `{3.EX.${createdRecordId}}`, // Filter by the created record
34
+ sortBy: [{ fieldId: 6, order: "ASC" }],
35
+ options: { skip: 0, top: 1 },
36
+ };
37
+
38
+ console.log("Config used:", {
39
+ realm: process.env.QB_REALM,
40
+ userToken: process.env.QB_USER_TOKEN,
41
+ tableId: QB_TABLE_ID_1,
42
+ });
43
+ console.log("Query request:", queryRequest);
44
+ const response: RunQueryResponse = await client.runQuery({
45
+ body: queryRequest,
46
+ });
47
+ console.log("Real API response:", response);
48
+
49
+ // Assertions
50
+ expect(response.data).toHaveLength(1);
51
+ expect(response.data[0]["3"].value).toBe(createdRecordId);
52
+ expect(response.data[0]["6"].value).toContain("Test Task");
53
+ expect(response.fields).toContainEqual({
54
+ id: 3,
55
+ label: expect.any(String),
56
+ type: expect.any(String),
57
+ });
58
+ expect(response.fields).toContainEqual({
59
+ id: 6,
60
+ label: expect.any(String),
61
+ type: expect.any(String),
62
+ });
63
+ expect(response.metadata).toMatchObject({
64
+ numFields: 2,
65
+ numRecords: 1,
66
+ skip: 0,
67
+ top: 1,
68
+ totalRecords: expect.any(Number),
69
+ });
70
+
71
+ // Cleanup: Delete the test record
72
+ await client.deleteRecords({
73
+ body: {
74
+ from: QB_TABLE_ID_1,
75
+ where: `{3.EX.${createdRecordId}}`, // Assuming field 3 is Record ID#
76
+ },
77
+ });
78
+ console.log("Cleaned up test record with ID:", createdRecordId);
79
+ },
80
+ { timeout: 10000 }
81
+ );
82
+ });