quickbase-js 1.0.1-beta.0 → 1.1.0-beta.1
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.
- package/CHANGELOG.md +38 -0
- package/dist/{quickbaseClient.d.ts → quickbase.d.ts} +344 -339
- package/dist/{quickbaseClient.js → quickbase.js} +350 -266
- package/dist/quickbase.js.map +1 -0
- package/examples/react.js +42 -16
- package/examples/vanillajs.html +32 -8
- package/log-gen.yaml +3513 -0
- package/package.json +13 -9
- package/rollup.config.js +2 -2
- package/src/code-generation/fix-spec-definitions.ts +473 -66
- package/src/code-generation/fix-spec-paths.ts +210 -18
- package/src/code-generation/generate-unified-interface.ts +1 -8
- package/src/code-generation/output/quickbase-fixed.json +783 -1138
- package/src/generated-unified/QuickbaseClient.ts +10 -9
- package/src/log-gen.ts +82 -35
- package/src/quickbaseClient.ts +39 -42
- package/test-artifacts/test-results.json +173 -1145
- package/tests/playwright/qb/auth/cacheSwitching.test.ts +4 -4
- package/tests/playwright/qb/auth/fetchTempToken401.test.ts +2 -2
- package/tests/playwright/qb/auth/getAppWithUserToken.test.ts +2 -2
- package/tests/playwright/qb/auth/getTempToken.test.ts +2 -2
- package/tests/playwright/qb/auth/tokenPrefetch.test.ts +2 -2
- package/tests/playwright/qb/auth/tokenRenewal.test.ts +2 -2
- package/tests/setup.ts +37 -9
- package/tests/vitest/qb/apps/getApp.test.ts +37 -43
- package/tests/vitest/qb/fields/getFields.test.ts +3 -5
- package/tests/vitest/qb/records/deleteRecords.test.ts +78 -0
- package/tests/vitest/qb/records/runQuery.test.ts +82 -0
- package/tests/vitest/qb/records/upsert.test.ts +72 -0
- package/tests/vitest/qb/tables/createTable.test.ts +55 -0
- package/tests/vitest/qb/tables/deleteTable.test.ts +50 -0
- package/tests/vitest/qb/tables/getAppTables.test.ts +43 -64
- package/tests/vitest/qb/tables/getTable.test.ts +26 -0
- package/tests/vitest/qb/tables/updateTable.test.ts +48 -0
- package/tests/vitest/unit/apps/getApp.test.ts +17 -12
- package/tests/vitest/unit/auth/fetchTempToken401.test.ts +32 -6
- package/tests/vitest/unit/auth/getTempToken.test.ts +36 -24
- package/tests/vitest/unit/auth/retryOn401.test.ts +39 -5
- package/tests/vitest/unit/auth/two401s.test.ts +1 -1
- package/tests/vitest/unit/fields/getFields.test.ts +14 -7
- package/tests/vitest/unit/records/deleteRecords.test.ts +112 -0
- package/tests/vitest/unit/records/runQuery.test.ts +102 -0
- package/tests/vitest/unit/records/upsert.test.ts +218 -0
- package/tests/vitest/unit/tables/createTable.test.ts +70 -0
- package/tests/vitest/unit/tables/deleteTable.test.ts +77 -0
- package/tests/vitest/unit/tables/getAppTables.test.ts +41 -28
- package/tests/vitest/unit/tables/getTable.test.ts +233 -0
- package/tests/vitest/unit/tables/updateTable.test.ts +80 -0
- package/tsconfig.json +3 -2
- package/vitest.config.ts +8 -2
- package/dist/quickbaseClient.js.map +0 -1
- package/logGen.yaml +0 -1820
- package/src/utils.ts +0 -5
|
@@ -1,67 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { test, expect } from "vitest";
|
|
2
|
+
import { quickbase } from "../../../../src/quickbaseClient";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
test(
|
|
5
|
+
"QuickbaseClient Integration - getAppTables > fetches real table data from QuickBase",
|
|
6
|
+
{ timeout: 10000 },
|
|
7
|
+
async () => {
|
|
8
|
+
const config = {
|
|
9
|
+
realm: process.env.QB_REALM || "",
|
|
10
|
+
userToken: process.env.QB_USER_TOKEN || "",
|
|
11
|
+
debug: true,
|
|
12
|
+
};
|
|
13
|
+
const client = quickbase(config);
|
|
14
|
+
const tablesAppId = "buwai2zpe";
|
|
6
15
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const tablesAppId = process.env.QB_APP_ID;
|
|
11
|
-
if (!tablesAppId) throw new Error("QB_APP_ID is not defined in .env");
|
|
12
|
-
if (!process.env.QB_REALM)
|
|
13
|
-
throw new Error("QB_REALM is not defined in .env");
|
|
14
|
-
if (!process.env.QB_USER_TOKEN)
|
|
15
|
-
throw new Error("QB_USER_TOKEN is not defined in .env");
|
|
16
|
+
console.log("Config used:", config);
|
|
17
|
+
const result = await client.getAppTables({ appId: tablesAppId });
|
|
18
|
+
console.log("Real API response:", result);
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
expect.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
expect.objectContaining({
|
|
45
|
-
alias: "_DBID_ROLES",
|
|
46
|
-
created: new Date("2025-02-13T18:22:33Z"),
|
|
47
|
-
defaultSortFieldId: 2,
|
|
48
|
-
defaultSortOrder: "DESC",
|
|
49
|
-
description: "",
|
|
50
|
-
id: "buwai2z3s",
|
|
51
|
-
keyFieldId: 3,
|
|
52
|
-
name: "Roles",
|
|
53
|
-
nextFieldId: 6,
|
|
54
|
-
nextRecordId: 1,
|
|
55
|
-
pluralRecordName: "Roles",
|
|
56
|
-
singleRecordName: "Role",
|
|
57
|
-
sizeLimit: "500 MB",
|
|
58
|
-
spaceRemaining: "500 MB",
|
|
59
|
-
spaceUsed: "0 KB",
|
|
60
|
-
updated: new Date("2025-02-13T18:22:34Z"),
|
|
61
|
-
}),
|
|
62
|
-
])
|
|
63
|
-
);
|
|
64
|
-
},
|
|
65
|
-
{ timeout: 10000 }
|
|
66
|
-
);
|
|
67
|
-
});
|
|
20
|
+
expect(result).toBeDefined();
|
|
21
|
+
expect(result).toBeInstanceOf(Array);
|
|
22
|
+
expect(result.length).toBeGreaterThan(0);
|
|
23
|
+
expect(result).toEqual(
|
|
24
|
+
expect.arrayContaining([
|
|
25
|
+
expect.objectContaining({
|
|
26
|
+
id: "buwai2zr4",
|
|
27
|
+
alias: "_DBID_ROOT",
|
|
28
|
+
name: expect.any(String), // Allow dynamic name due to updateTable
|
|
29
|
+
description: expect.any(String), // Allow dynamic description
|
|
30
|
+
created: expect.any(Date),
|
|
31
|
+
updated: expect.any(Date), // Allow varying timestamps
|
|
32
|
+
nextRecordId: expect.any(Number),
|
|
33
|
+
nextFieldId: expect.any(Number),
|
|
34
|
+
defaultSortFieldId: expect.any(Number),
|
|
35
|
+
defaultSortOrder: "DESC",
|
|
36
|
+
keyFieldId: expect.any(Number),
|
|
37
|
+
singleRecordName: "Root",
|
|
38
|
+
pluralRecordName: "Roots",
|
|
39
|
+
sizeLimit: "500 MB",
|
|
40
|
+
spaceUsed: expect.any(String),
|
|
41
|
+
spaceRemaining: "500 MB",
|
|
42
|
+
}),
|
|
43
|
+
])
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { test, expect } from "vitest";
|
|
2
|
+
import { quickbase } from "../../../../src/quickbaseClient";
|
|
3
|
+
|
|
4
|
+
test(
|
|
5
|
+
"QuickbaseClient Integration - getTable > fetches real table data from QuickBase",
|
|
6
|
+
{ timeout: 10000 },
|
|
7
|
+
async () => {
|
|
8
|
+
const config = {
|
|
9
|
+
realm: process.env.QB_REALM || "",
|
|
10
|
+
userToken: process.env.QB_USER_TOKEN || "",
|
|
11
|
+
debug: true,
|
|
12
|
+
};
|
|
13
|
+
const client = quickbase(config);
|
|
14
|
+
const tableId = "buwai2zr4";
|
|
15
|
+
const appId = "buwai2zpe";
|
|
16
|
+
|
|
17
|
+
console.log("Config used:", config);
|
|
18
|
+
const response = await client.getTable({ tableId, appId });
|
|
19
|
+
expect(response).toBeDefined();
|
|
20
|
+
expect(response.id).toBe(tableId);
|
|
21
|
+
expect(response.name).toBe("Root"); // Expect original name
|
|
22
|
+
expect(response.alias).toBe("_DBID_ROOT");
|
|
23
|
+
expect(response.created).toBeInstanceOf(Date);
|
|
24
|
+
console.log("Real API response:", response);
|
|
25
|
+
}
|
|
26
|
+
);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { test, expect } from "vitest";
|
|
2
|
+
import { quickbase } from "../../../../src/quickbaseClient";
|
|
3
|
+
|
|
4
|
+
test(
|
|
5
|
+
"QuickbaseClient Integration - updateTable > updates real table data in QuickBase",
|
|
6
|
+
{ timeout: 10000 },
|
|
7
|
+
async () => {
|
|
8
|
+
const config = {
|
|
9
|
+
realm: process.env.QB_REALM || "",
|
|
10
|
+
userToken: process.env.QB_USER_TOKEN || "",
|
|
11
|
+
debug: true,
|
|
12
|
+
};
|
|
13
|
+
const client = quickbase(config);
|
|
14
|
+
const appId = "buwai2zpe";
|
|
15
|
+
const tableId = "buwai2zr4";
|
|
16
|
+
const updatedBody = {
|
|
17
|
+
name: "Root Updated " + Date.now(),
|
|
18
|
+
description: "Integration test update",
|
|
19
|
+
};
|
|
20
|
+
const originalBody = {
|
|
21
|
+
name: "Root",
|
|
22
|
+
description: "",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
console.log("Config used:", config);
|
|
26
|
+
// Update the table
|
|
27
|
+
const response = await client.updateTable({
|
|
28
|
+
tableId,
|
|
29
|
+
appId,
|
|
30
|
+
body: updatedBody,
|
|
31
|
+
});
|
|
32
|
+
expect(response).toBeDefined();
|
|
33
|
+
expect(response.id).toBe(tableId);
|
|
34
|
+
expect(response.name).toBe(updatedBody.name);
|
|
35
|
+
expect(response.description).toBe(updatedBody.description);
|
|
36
|
+
console.log("Real API response:", response);
|
|
37
|
+
|
|
38
|
+
// Cleanup: Revert the table to original state
|
|
39
|
+
const cleanupResponse = await client.updateTable({
|
|
40
|
+
tableId,
|
|
41
|
+
appId,
|
|
42
|
+
body: originalBody,
|
|
43
|
+
});
|
|
44
|
+
expect(cleanupResponse.name).toBe(originalBody.name);
|
|
45
|
+
expect(cleanupResponse.description).toBe(originalBody.description);
|
|
46
|
+
console.log("Cleanup API response:", cleanupResponse);
|
|
47
|
+
}
|
|
48
|
+
);
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
// tests/vitest/unit/apps/getApp.test.ts
|
|
1
2
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createClient,
|
|
5
|
+
mockFetch,
|
|
6
|
+
QB_APP_ID,
|
|
7
|
+
QB_REALM,
|
|
8
|
+
QB_USER_TOKEN,
|
|
9
|
+
} from "@tests/setup.ts";
|
|
3
10
|
|
|
4
|
-
describe("QuickbaseClient - getApp
|
|
5
|
-
|
|
11
|
+
describe("QuickbaseClient Unit - getApp", () => {
|
|
12
|
+
let client: ReturnType<typeof createClient>;
|
|
6
13
|
|
|
7
14
|
beforeEach(() => {
|
|
8
15
|
mockFetch.mockClear();
|
|
16
|
+
client = createClient(mockFetch, { debug: true }); // Add debug: true for consistency
|
|
9
17
|
});
|
|
10
18
|
|
|
11
19
|
it("initializes without errors", () => {
|
|
@@ -24,7 +32,7 @@ describe("QuickbaseClient - getApp (Unit)", () => {
|
|
|
24
32
|
status: 200,
|
|
25
33
|
json: () =>
|
|
26
34
|
Promise.resolve({
|
|
27
|
-
id:
|
|
35
|
+
id: QB_APP_ID,
|
|
28
36
|
name: "qb-copy",
|
|
29
37
|
created: "2025-02-13T18:22:33Z",
|
|
30
38
|
updated: "2025-03-04T04:25:51Z",
|
|
@@ -45,12 +53,9 @@ describe("QuickbaseClient - getApp (Unit)", () => {
|
|
|
45
53
|
} as Response);
|
|
46
54
|
});
|
|
47
55
|
|
|
48
|
-
const
|
|
49
|
-
if (!getAppId) throw new Error("QB_APP_ID is not defined in .env");
|
|
50
|
-
console.log("Test appId:", getAppId);
|
|
51
|
-
const result = await client.getApp({ appId: getAppId });
|
|
56
|
+
const result = await client.getApp({ appId: QB_APP_ID });
|
|
52
57
|
expect(result).toEqual({
|
|
53
|
-
id:
|
|
58
|
+
id: QB_APP_ID,
|
|
54
59
|
name: "qb-copy",
|
|
55
60
|
created: new Date("2025-02-13T18:22:33Z"),
|
|
56
61
|
updated: new Date("2025-03-04T04:25:51Z"),
|
|
@@ -69,11 +74,11 @@ describe("QuickbaseClient - getApp (Unit)", () => {
|
|
|
69
74
|
},
|
|
70
75
|
});
|
|
71
76
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
72
|
-
`https://api.quickbase.com/v1/apps/${
|
|
77
|
+
`https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
|
|
73
78
|
expect.objectContaining({
|
|
74
79
|
headers: expect.objectContaining({
|
|
75
|
-
|
|
76
|
-
|
|
80
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
81
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
77
82
|
}),
|
|
78
83
|
})
|
|
79
84
|
);
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
// tests/vitest/unit/auth/fetchTempToken401.test.ts
|
|
1
2
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createClient,
|
|
5
|
+
mockFetch,
|
|
6
|
+
QB_REALM,
|
|
7
|
+
QB_TABLE_ID_1,
|
|
8
|
+
} from "@tests/setup.ts";
|
|
3
9
|
|
|
4
|
-
describe("QuickbaseClient - 401 with fetchTempToken 401", () => {
|
|
10
|
+
describe("QuickbaseClient Unit - 401 with fetchTempToken 401", () => {
|
|
5
11
|
let client: ReturnType<typeof createClient>;
|
|
6
12
|
|
|
7
13
|
beforeEach(() => {
|
|
@@ -10,7 +16,6 @@ describe("QuickbaseClient - 401 with fetchTempToken 401", () => {
|
|
|
10
16
|
});
|
|
11
17
|
|
|
12
18
|
it("fails without infinite loop when fetchTempToken returns 401 after initial 401", async () => {
|
|
13
|
-
const mockDbid = "mockDbid123";
|
|
14
19
|
let callCount = 0;
|
|
15
20
|
|
|
16
21
|
mockFetch.mockImplementation((url) => {
|
|
@@ -28,25 +33,46 @@ describe("QuickbaseClient - 401 with fetchTempToken 401", () => {
|
|
|
28
33
|
return Promise.resolve({
|
|
29
34
|
ok: false,
|
|
30
35
|
status: 401,
|
|
31
|
-
|
|
36
|
+
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
32
37
|
});
|
|
33
38
|
}
|
|
34
39
|
if (url.includes("auth/temporary") && callCount === 3) {
|
|
35
40
|
return Promise.resolve({
|
|
36
41
|
ok: false,
|
|
37
42
|
status: 401,
|
|
38
|
-
|
|
43
|
+
json: () =>
|
|
44
|
+
Promise.resolve({ message: "Unauthorized in fetchTempToken" }),
|
|
39
45
|
});
|
|
40
46
|
}
|
|
41
47
|
return Promise.reject(new Error(`Unexpected fetch call: ${url}`));
|
|
42
48
|
});
|
|
43
49
|
|
|
44
50
|
const consoleSpy = vi.spyOn(console, "log");
|
|
45
|
-
await expect(client.getFields({ tableId:
|
|
51
|
+
await expect(client.getFields({ tableId: QB_TABLE_ID_1 })).rejects.toThrow(
|
|
46
52
|
"API Error: Unauthorized in fetchTempToken (Status: 401)"
|
|
47
53
|
);
|
|
48
54
|
|
|
49
55
|
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
56
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
57
|
+
1,
|
|
58
|
+
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
59
|
+
expect.any(Object)
|
|
60
|
+
);
|
|
61
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
62
|
+
2,
|
|
63
|
+
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`, // Removed &includeFieldPerms=false
|
|
64
|
+
expect.objectContaining({
|
|
65
|
+
headers: expect.objectContaining({
|
|
66
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
67
|
+
}),
|
|
68
|
+
})
|
|
69
|
+
);
|
|
70
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
71
|
+
3,
|
|
72
|
+
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
73
|
+
expect.any(Object)
|
|
74
|
+
);
|
|
75
|
+
|
|
50
76
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
51
77
|
"Authorization error for getFields, refreshing token:",
|
|
52
78
|
expect.any(String)
|
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
// tests/vitest/unit/auth/getTempTokenDBID.test.ts
|
|
1
2
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createClient,
|
|
5
|
+
mockFetch,
|
|
6
|
+
QB_REALM,
|
|
7
|
+
QB_TABLE_ID_1,
|
|
8
|
+
} from "@tests/setup.ts";
|
|
3
9
|
|
|
4
|
-
describe("QuickbaseClient - getTempTokenDBID
|
|
10
|
+
describe("QuickbaseClient Unit - getTempTokenDBID", () => {
|
|
5
11
|
let client: ReturnType<typeof createClient>;
|
|
6
12
|
|
|
7
13
|
beforeEach(() => {
|
|
8
14
|
mockFetch.mockClear();
|
|
9
|
-
client = createClient(mockFetch, { useTempTokens: true, debug: false });
|
|
15
|
+
client = createClient(mockFetch, { useTempTokens: true, debug: false });
|
|
10
16
|
});
|
|
11
17
|
|
|
12
18
|
it("initializes without errors", () => {
|
|
@@ -18,23 +24,22 @@ describe("QuickbaseClient - getTempTokenDBID (Unit)", () => {
|
|
|
18
24
|
});
|
|
19
25
|
|
|
20
26
|
it("fetches and caches temp token on first call", async () => {
|
|
21
|
-
const mockDbid = "mockDbid123";
|
|
22
27
|
const mockToken = "b123xyz_temp_token";
|
|
23
28
|
|
|
24
29
|
mockFetch.mockResolvedValueOnce({
|
|
25
30
|
ok: true,
|
|
26
31
|
status: 200,
|
|
27
32
|
json: () => Promise.resolve({ temporaryAuthorization: mockToken }),
|
|
28
|
-
}
|
|
33
|
+
});
|
|
29
34
|
|
|
30
|
-
const result = await client.getTempTokenDBID({ dbid:
|
|
35
|
+
const result = await client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 });
|
|
31
36
|
expect(result).toEqual({ temporaryAuthorization: mockToken });
|
|
32
37
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
33
|
-
`https://api.quickbase.com/v1/auth/temporary/${
|
|
38
|
+
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
34
39
|
expect.objectContaining({
|
|
35
40
|
method: "GET",
|
|
36
41
|
headers: expect.objectContaining({
|
|
37
|
-
"QB-Realm-Hostname": `${
|
|
42
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
38
43
|
"Content-Type": "application/json",
|
|
39
44
|
}),
|
|
40
45
|
credentials: "include",
|
|
@@ -43,41 +48,48 @@ describe("QuickbaseClient - getTempTokenDBID (Unit)", () => {
|
|
|
43
48
|
});
|
|
44
49
|
|
|
45
50
|
it("reuses cached temp token on second call", async () => {
|
|
46
|
-
const mockDbid = "mockDbid123";
|
|
47
51
|
const mockToken = "b123xyz_temp_token";
|
|
48
52
|
|
|
49
53
|
mockFetch.mockResolvedValueOnce({
|
|
50
54
|
ok: true,
|
|
51
55
|
status: 200,
|
|
52
56
|
json: () => Promise.resolve({ temporaryAuthorization: mockToken }),
|
|
53
|
-
}
|
|
57
|
+
});
|
|
54
58
|
|
|
55
|
-
const firstResult = await client.getTempTokenDBID({ dbid:
|
|
59
|
+
const firstResult = await client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 });
|
|
56
60
|
expect(firstResult).toEqual({ temporaryAuthorization: mockToken });
|
|
57
61
|
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
58
62
|
|
|
59
63
|
mockFetch.mockClear();
|
|
60
|
-
const secondResult = await client.getTempTokenDBID({ dbid:
|
|
64
|
+
const secondResult = await client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 });
|
|
61
65
|
expect(secondResult).toEqual({ temporaryAuthorization: mockToken });
|
|
62
66
|
expect(mockFetch).not.toHaveBeenCalled();
|
|
63
67
|
});
|
|
64
68
|
|
|
65
69
|
it("handles API error", async () => {
|
|
66
|
-
|
|
67
|
-
const errorResponse = {
|
|
70
|
+
mockFetch.mockResolvedValueOnce({
|
|
68
71
|
ok: false,
|
|
69
72
|
status: 401,
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
mockFetch.mockResolvedValueOnce(errorResponse);
|
|
73
|
+
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
74
|
+
});
|
|
74
75
|
|
|
75
|
-
await expect(
|
|
76
|
-
(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
await expect(
|
|
77
|
+
client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 })
|
|
78
|
+
).rejects.toSatisfy((error: Error) => {
|
|
79
|
+
expect(error).toBeInstanceOf(Error);
|
|
80
|
+
expect(error.message).toBe("API Error: Unauthorized (Status: 401)");
|
|
81
|
+
return true;
|
|
82
|
+
});
|
|
83
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
84
|
+
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
85
|
+
expect.objectContaining({
|
|
86
|
+
method: "GET",
|
|
87
|
+
headers: expect.objectContaining({
|
|
88
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
89
|
+
"Content-Type": "application/json",
|
|
90
|
+
}),
|
|
91
|
+
credentials: "include",
|
|
92
|
+
})
|
|
81
93
|
);
|
|
82
94
|
});
|
|
83
95
|
});
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
// tests/vitest/unit/auth/retryOn401.test.ts
|
|
1
2
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createClient,
|
|
5
|
+
mockFetch,
|
|
6
|
+
QB_REALM,
|
|
7
|
+
QB_TABLE_ID_1,
|
|
8
|
+
} from "@tests/setup.ts";
|
|
3
9
|
|
|
4
|
-
describe("QuickbaseClient - 401 Retry Creates New Token", () => {
|
|
10
|
+
describe("QuickbaseClient Unit - 401 Retry Creates New Token", () => {
|
|
5
11
|
let client: ReturnType<typeof createClient>;
|
|
6
12
|
|
|
7
13
|
beforeEach(() => {
|
|
@@ -10,7 +16,6 @@ describe("QuickbaseClient - 401 Retry Creates New Token", () => {
|
|
|
10
16
|
});
|
|
11
17
|
|
|
12
18
|
it("creates a new token on 401 and retries successfully", async () => {
|
|
13
|
-
const mockDbid = "mockDbid123";
|
|
14
19
|
const mockToken = "new_token_456";
|
|
15
20
|
const mockFields = [{ id: 1, label: "Field1" }];
|
|
16
21
|
let callCount = 0;
|
|
@@ -51,16 +56,45 @@ describe("QuickbaseClient - 401 Retry Creates New Token", () => {
|
|
|
51
56
|
});
|
|
52
57
|
|
|
53
58
|
const consoleSpy = vi.spyOn(console, "log");
|
|
54
|
-
const result = await client.getFields({ tableId:
|
|
59
|
+
const result = await client.getFields({ tableId: QB_TABLE_ID_1 });
|
|
55
60
|
|
|
56
61
|
expect(result).toEqual(mockFields);
|
|
57
62
|
expect(mockFetch).toHaveBeenCalledTimes(4);
|
|
63
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
64
|
+
1,
|
|
65
|
+
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
66
|
+
expect.any(Object)
|
|
67
|
+
);
|
|
68
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
69
|
+
2,
|
|
70
|
+
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`, // Removed &includeFieldPerms=false
|
|
71
|
+
expect.objectContaining({
|
|
72
|
+
headers: expect.objectContaining({
|
|
73
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
74
|
+
}),
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
78
|
+
3,
|
|
79
|
+
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
80
|
+
expect.any(Object)
|
|
81
|
+
);
|
|
82
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
83
|
+
4,
|
|
84
|
+
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`, // Removed &includeFieldPerms=false
|
|
85
|
+
expect.objectContaining({
|
|
86
|
+
headers: expect.objectContaining({
|
|
87
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
88
|
+
}),
|
|
89
|
+
})
|
|
90
|
+
);
|
|
91
|
+
|
|
58
92
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
59
93
|
"Authorization error for getFields, refreshing token:",
|
|
60
94
|
expect.any(String)
|
|
61
95
|
);
|
|
62
96
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
63
|
-
|
|
97
|
+
`Fetched and cached new token for dbid: ${QB_TABLE_ID_1}`,
|
|
64
98
|
mockToken + "_retry",
|
|
65
99
|
expect.any(String)
|
|
66
100
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
-
import { createClient, mockFetch } from "
|
|
2
|
+
import { createClient, mockFetch } from "@tests/setup.ts";
|
|
3
3
|
|
|
4
4
|
describe("QuickbaseClient - Two 401s in a Row", () => {
|
|
5
5
|
let client: ReturnType<typeof createClient>;
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
// tests/vitest/unit/fields/getFields.test.ts
|
|
1
2
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createClient,
|
|
5
|
+
mockFetch,
|
|
6
|
+
QB_REALM,
|
|
7
|
+
QB_USER_TOKEN,
|
|
8
|
+
QB_TABLE_ID_1,
|
|
9
|
+
} from "@tests/setup.ts";
|
|
3
10
|
|
|
4
|
-
describe("QuickbaseClient - getFields
|
|
11
|
+
describe("QuickbaseClient Unit - getFields", () => {
|
|
5
12
|
it("calls getFields successfully", async () => {
|
|
6
13
|
mockFetch.mockImplementation((url: string, options: any) => {
|
|
7
14
|
console.log("Mock fetch for getFields:", url, options);
|
|
@@ -47,9 +54,9 @@ describe("QuickbaseClient - getFields (Unit)", () => {
|
|
|
47
54
|
} as Response);
|
|
48
55
|
});
|
|
49
56
|
|
|
50
|
-
const client = createClient(mockFetch);
|
|
57
|
+
const client = createClient(mockFetch, { debug: true }); // Add debug: true for consistency
|
|
51
58
|
const result = await client.getFields({
|
|
52
|
-
tableId:
|
|
59
|
+
tableId: QB_TABLE_ID_1,
|
|
53
60
|
includeFieldPerms: true,
|
|
54
61
|
});
|
|
55
62
|
console.log("getFields response:", result);
|
|
@@ -103,11 +110,11 @@ describe("QuickbaseClient - getFields (Unit)", () => {
|
|
|
103
110
|
])
|
|
104
111
|
);
|
|
105
112
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
106
|
-
`https://api.quickbase.com/v1/fields?tableId
|
|
113
|
+
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}&includeFieldPerms=true`,
|
|
107
114
|
expect.objectContaining({
|
|
108
115
|
headers: expect.objectContaining({
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
117
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
111
118
|
}),
|
|
112
119
|
})
|
|
113
120
|
);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// tests/vitest/unit/records/deleteRecords.test.ts
|
|
2
|
+
import { describe, expect, test, beforeEach } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
createClient,
|
|
5
|
+
mockFetch,
|
|
6
|
+
QB_REALM,
|
|
7
|
+
QB_USER_TOKEN,
|
|
8
|
+
QB_TABLE_ID_1,
|
|
9
|
+
} from "@tests/setup.ts";
|
|
10
|
+
|
|
11
|
+
describe("QuickbaseClient Unit - deleteRecords", () => {
|
|
12
|
+
let qb: ReturnType<typeof createClient>;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
mockFetch.mockClear();
|
|
16
|
+
qb = createClient(mockFetch, { debug: true }); // Use debug: true for consistency with other tests
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("sends correct DELETE request and handles success", async () => {
|
|
20
|
+
mockFetch.mockResolvedValueOnce({
|
|
21
|
+
ok: true,
|
|
22
|
+
status: 200,
|
|
23
|
+
json: () => Promise.resolve({ numberDeleted: 1 }),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const result = await qb.deleteRecords({
|
|
27
|
+
body: {
|
|
28
|
+
from: QB_TABLE_ID_1,
|
|
29
|
+
where: "{3.EX.'5'}", // Simulate deleting Record ID 5
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
34
|
+
"https://api.quickbase.com/v1/records",
|
|
35
|
+
expect.objectContaining({
|
|
36
|
+
method: "DELETE",
|
|
37
|
+
headers: expect.objectContaining({
|
|
38
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
39
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
}),
|
|
42
|
+
body: JSON.stringify({ from: QB_TABLE_ID_1, where: "{3.EX.'5'}" }),
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
expect(result).toEqual({ numberDeleted: 1 });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("handles no records deleted", async () => {
|
|
49
|
+
mockFetch.mockResolvedValueOnce({
|
|
50
|
+
ok: true,
|
|
51
|
+
status: 200,
|
|
52
|
+
json: () => Promise.resolve({ numberDeleted: 0 }),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const result = await qb.deleteRecords({
|
|
56
|
+
body: {
|
|
57
|
+
from: QB_TABLE_ID_1,
|
|
58
|
+
where: "{3.EX.'999999'}", // Non-existent Record ID
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
63
|
+
"https://api.quickbase.com/v1/records",
|
|
64
|
+
expect.objectContaining({
|
|
65
|
+
method: "DELETE",
|
|
66
|
+
headers: expect.objectContaining({
|
|
67
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
68
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
69
|
+
"Content-Type": "application/json",
|
|
70
|
+
}),
|
|
71
|
+
body: JSON.stringify({
|
|
72
|
+
from: QB_TABLE_ID_1,
|
|
73
|
+
where: "{3.EX.'999999'}",
|
|
74
|
+
}),
|
|
75
|
+
})
|
|
76
|
+
);
|
|
77
|
+
expect(result).toEqual({ numberDeleted: 0 });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("throws error on API failure", async () => {
|
|
81
|
+
mockFetch.mockResolvedValueOnce({
|
|
82
|
+
ok: false,
|
|
83
|
+
status: 400,
|
|
84
|
+
json: () => Promise.resolve({ message: "Invalid query" }),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
await expect(() =>
|
|
88
|
+
qb.deleteRecords({
|
|
89
|
+
body: {
|
|
90
|
+
from: QB_TABLE_ID_1,
|
|
91
|
+
where: "{3.EX.'invalid'}",
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
).rejects.toThrow("API Error: Invalid query (Status: 400)");
|
|
95
|
+
|
|
96
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
97
|
+
"https://api.quickbase.com/v1/records",
|
|
98
|
+
expect.objectContaining({
|
|
99
|
+
method: "DELETE",
|
|
100
|
+
headers: expect.objectContaining({
|
|
101
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
102
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
103
|
+
"Content-Type": "application/json",
|
|
104
|
+
}),
|
|
105
|
+
body: JSON.stringify({
|
|
106
|
+
from: QB_TABLE_ID_1,
|
|
107
|
+
where: "{3.EX.'invalid'}",
|
|
108
|
+
}),
|
|
109
|
+
})
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
});
|