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.
- package/CHANGELOG.md +16 -0
- package/dist/quickbase.d.ts +356 -6
- package/dist/quickbase.js +363 -20
- package/dist/quickbase.js.map +1 -1
- package/examples/react.js +42 -16
- package/examples/vanillajs.html +32 -8
- package/log-gen.yaml +146 -198
- package/package.json +4 -4
- package/src/code-generation/fix-spec-definitions.ts +99 -41
- package/src/code-generation/fix-spec-paths.ts +6 -19
- package/src/code-generation/generate-unified-interface.ts +1 -8
- package/src/code-generation/output/quickbase-fixed.json +142 -21
- package/src/generated-unified/QuickbaseClient.ts +3 -3
- package/tests/playwright/qb/auth/cacheSwitching.test.ts +2 -2
- package/tests/setup.ts +29 -9
- package/tests/vitest/qb/fields/getFields.test.ts +3 -5
- package/tests/vitest/qb/records/runQuery.test.ts +82 -0
- package/tests/vitest/unit/apps/getApp.test.ts +17 -12
- package/tests/vitest/unit/auth/fetchTempToken401.test.ts +29 -4
- package/tests/vitest/unit/auth/getTempToken.test.ts +30 -17
- package/tests/vitest/unit/auth/retryOn401.test.ts +39 -5
- package/tests/vitest/unit/fields/getFields.test.ts +14 -7
- package/tests/vitest/unit/records/deleteRecords.test.ts +28 -34
- package/tests/vitest/unit/records/runQuery.test.ts +102 -0
- package/tests/vitest/unit/records/upsert.test.ts +31 -41
- package/tests/vitest/unit/tables/createTable.test.ts +18 -10
- package/tests/vitest/unit/tables/deleteTable.test.ts +28 -19
- package/tests/vitest/unit/tables/getAppTables.test.ts +41 -28
- package/tests/vitest/unit/tables/getTable.test.ts +53 -32
- package/tests/vitest/unit/tables/updateTable.test.ts +17 -11
- package/vitest.config.ts +5 -0
|
@@ -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) => {
|
|
@@ -43,11 +48,31 @@ describe("QuickbaseClient - 401 with fetchTempToken 401", () => {
|
|
|
43
48
|
});
|
|
44
49
|
|
|
45
50
|
const consoleSpy = vi.spyOn(console, "log");
|
|
46
|
-
await expect(client.getFields({ tableId:
|
|
51
|
+
await expect(client.getFields({ tableId: QB_TABLE_ID_1 })).rejects.toThrow(
|
|
47
52
|
"API Error: Unauthorized in fetchTempToken (Status: 401)"
|
|
48
53
|
);
|
|
49
54
|
|
|
50
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
|
+
|
|
51
76
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
52
77
|
"Authorization error for getFields, refreshing token:",
|
|
53
78
|
expect.any(String)
|
|
@@ -1,7 +1,13 @@
|
|
|
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(() => {
|
|
@@ -18,7 +24,6 @@ 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({
|
|
@@ -27,14 +32,14 @@ describe("QuickbaseClient - getTempTokenDBID (Unit)", () => {
|
|
|
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,7 +48,6 @@ 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({
|
|
@@ -52,31 +56,40 @@ describe("QuickbaseClient - getTempTokenDBID (Unit)", () => {
|
|
|
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
|
-
const mockDbid = "mockDbid123";
|
|
67
|
-
|
|
68
70
|
mockFetch.mockResolvedValueOnce({
|
|
69
71
|
ok: false,
|
|
70
72
|
status: 401,
|
|
71
73
|
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
72
74
|
});
|
|
73
75
|
|
|
74
|
-
await expect(
|
|
75
|
-
(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
+
})
|
|
80
93
|
);
|
|
81
94
|
});
|
|
82
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,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
|
);
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
// tests/vitest/unit/records/deleteRecords.test.ts
|
|
2
|
-
import { describe, expect, test, beforeEach } from "vitest";
|
|
3
|
-
import {
|
|
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>;
|
|
4
13
|
|
|
5
|
-
describe("QuickbaseClient - deleteRecords (Unit)", () => {
|
|
6
14
|
beforeEach(() => {
|
|
7
|
-
mockFetch.mockClear();
|
|
15
|
+
mockFetch.mockClear();
|
|
16
|
+
qb = createClient(mockFetch, { debug: true }); // Use debug: true for consistency with other tests
|
|
8
17
|
});
|
|
9
18
|
|
|
10
|
-
test("
|
|
11
|
-
const qb = createClient(mockFetch, {
|
|
12
|
-
realm: "test-realm",
|
|
13
|
-
userToken: "test-token",
|
|
14
|
-
});
|
|
15
|
-
|
|
19
|
+
test("sends correct DELETE request and handles success", async () => {
|
|
16
20
|
mockFetch.mockResolvedValueOnce({
|
|
17
21
|
ok: true,
|
|
18
22
|
status: 200,
|
|
@@ -21,7 +25,7 @@ describe("QuickbaseClient - deleteRecords (Unit)", () => {
|
|
|
21
25
|
|
|
22
26
|
const result = await qb.deleteRecords({
|
|
23
27
|
body: {
|
|
24
|
-
from:
|
|
28
|
+
from: QB_TABLE_ID_1,
|
|
25
29
|
where: "{3.EX.'5'}", // Simulate deleting Record ID 5
|
|
26
30
|
},
|
|
27
31
|
});
|
|
@@ -31,22 +35,17 @@ describe("QuickbaseClient - deleteRecords (Unit)", () => {
|
|
|
31
35
|
expect.objectContaining({
|
|
32
36
|
method: "DELETE",
|
|
33
37
|
headers: expect.objectContaining({
|
|
34
|
-
"QB-Realm-Hostname":
|
|
35
|
-
Authorization:
|
|
38
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
39
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
36
40
|
"Content-Type": "application/json",
|
|
37
41
|
}),
|
|
38
|
-
body: JSON.stringify({ from:
|
|
42
|
+
body: JSON.stringify({ from: QB_TABLE_ID_1, where: "{3.EX.'5'}" }),
|
|
39
43
|
})
|
|
40
44
|
);
|
|
41
45
|
expect(result).toEqual({ numberDeleted: 1 });
|
|
42
46
|
});
|
|
43
47
|
|
|
44
|
-
test("
|
|
45
|
-
const qb = createClient(mockFetch, {
|
|
46
|
-
realm: "test-realm",
|
|
47
|
-
userToken: "test-token",
|
|
48
|
-
});
|
|
49
|
-
|
|
48
|
+
test("handles no records deleted", async () => {
|
|
50
49
|
mockFetch.mockResolvedValueOnce({
|
|
51
50
|
ok: true,
|
|
52
51
|
status: 200,
|
|
@@ -55,7 +54,7 @@ describe("QuickbaseClient - deleteRecords (Unit)", () => {
|
|
|
55
54
|
|
|
56
55
|
const result = await qb.deleteRecords({
|
|
57
56
|
body: {
|
|
58
|
-
from:
|
|
57
|
+
from: QB_TABLE_ID_1,
|
|
59
58
|
where: "{3.EX.'999999'}", // Non-existent Record ID
|
|
60
59
|
},
|
|
61
60
|
});
|
|
@@ -65,12 +64,12 @@ describe("QuickbaseClient - deleteRecords (Unit)", () => {
|
|
|
65
64
|
expect.objectContaining({
|
|
66
65
|
method: "DELETE",
|
|
67
66
|
headers: expect.objectContaining({
|
|
68
|
-
"QB-Realm-Hostname":
|
|
69
|
-
Authorization:
|
|
67
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
68
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
70
69
|
"Content-Type": "application/json",
|
|
71
70
|
}),
|
|
72
71
|
body: JSON.stringify({
|
|
73
|
-
from:
|
|
72
|
+
from: QB_TABLE_ID_1,
|
|
74
73
|
where: "{3.EX.'999999'}",
|
|
75
74
|
}),
|
|
76
75
|
})
|
|
@@ -78,12 +77,7 @@ describe("QuickbaseClient - deleteRecords (Unit)", () => {
|
|
|
78
77
|
expect(result).toEqual({ numberDeleted: 0 });
|
|
79
78
|
});
|
|
80
79
|
|
|
81
|
-
test("
|
|
82
|
-
const qb = createClient(mockFetch, {
|
|
83
|
-
realm: "test-realm",
|
|
84
|
-
userToken: "test-token",
|
|
85
|
-
});
|
|
86
|
-
|
|
80
|
+
test("throws error on API failure", async () => {
|
|
87
81
|
mockFetch.mockResolvedValueOnce({
|
|
88
82
|
ok: false,
|
|
89
83
|
status: 400,
|
|
@@ -93,7 +87,7 @@ describe("QuickbaseClient - deleteRecords (Unit)", () => {
|
|
|
93
87
|
await expect(() =>
|
|
94
88
|
qb.deleteRecords({
|
|
95
89
|
body: {
|
|
96
|
-
from:
|
|
90
|
+
from: QB_TABLE_ID_1,
|
|
97
91
|
where: "{3.EX.'invalid'}",
|
|
98
92
|
},
|
|
99
93
|
})
|
|
@@ -104,12 +98,12 @@ describe("QuickbaseClient - deleteRecords (Unit)", () => {
|
|
|
104
98
|
expect.objectContaining({
|
|
105
99
|
method: "DELETE",
|
|
106
100
|
headers: expect.objectContaining({
|
|
107
|
-
"QB-Realm-Hostname":
|
|
108
|
-
Authorization:
|
|
101
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
102
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
109
103
|
"Content-Type": "application/json",
|
|
110
104
|
}),
|
|
111
105
|
body: JSON.stringify({
|
|
112
|
-
from:
|
|
106
|
+
from: QB_TABLE_ID_1,
|
|
113
107
|
where: "{3.EX.'invalid'}",
|
|
114
108
|
}),
|
|
115
109
|
})
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// tests/vitest/unit/records/runQuery.test.ts
|
|
2
|
+
import { describe, test, expect, 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
|
+
import { RunQueryRequest, RunQueryResponse } from "@/generated";
|
|
11
|
+
|
|
12
|
+
describe("QuickbaseClient Unit - runQuery", () => {
|
|
13
|
+
let client: ReturnType<typeof createClient>;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
mockFetch.mockClear();
|
|
17
|
+
client = createClient(mockFetch, { debug: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("sends correct POST request and handles successful query response", async () => {
|
|
21
|
+
const queryRequest: RunQueryRequest = {
|
|
22
|
+
from: QB_TABLE_ID_1,
|
|
23
|
+
select: [3, 6, 7],
|
|
24
|
+
where: "{6.EX.'Task 1'}",
|
|
25
|
+
sortBy: [{ fieldId: 6, order: "ASC" }],
|
|
26
|
+
groupBy: [{ fieldId: 7, grouping: "equal-values" }],
|
|
27
|
+
options: { skip: 0, top: 100, compareWithAppLocalTime: false },
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const mockResponse: RunQueryResponse = {
|
|
31
|
+
data: [
|
|
32
|
+
{ "3": { value: 1 }, "6": { value: "Task 1" }, "7": { value: "High" } },
|
|
33
|
+
],
|
|
34
|
+
fields: [
|
|
35
|
+
{ id: 3, label: "Record ID#", type: "recordid" },
|
|
36
|
+
{ id: 6, label: "Task Name", type: "text" },
|
|
37
|
+
{ id: 7, label: "Priority", type: "text" },
|
|
38
|
+
],
|
|
39
|
+
metadata: {
|
|
40
|
+
numFields: 3,
|
|
41
|
+
numRecords: 1,
|
|
42
|
+
skip: 0,
|
|
43
|
+
top: 100,
|
|
44
|
+
totalRecords: 1,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
mockFetch.mockResolvedValueOnce({
|
|
49
|
+
ok: true,
|
|
50
|
+
status: 200,
|
|
51
|
+
json: () => Promise.resolve(mockResponse),
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const result = await client.runQuery({ body: queryRequest });
|
|
55
|
+
|
|
56
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
57
|
+
"https://api.quickbase.com/v1/records/query",
|
|
58
|
+
expect.objectContaining({
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: expect.objectContaining({
|
|
61
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
62
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
63
|
+
"Content-Type": "application/json",
|
|
64
|
+
}),
|
|
65
|
+
body: JSON.stringify(queryRequest),
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
expect(result).toEqual(mockResponse);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("handles API error with invalid query", async () => {
|
|
73
|
+
const invalidQueryRequest = {
|
|
74
|
+
from: QB_TABLE_ID_1,
|
|
75
|
+
select: [3],
|
|
76
|
+
where: "{999.EX.'Invalid'}", // Invalid field ID
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
mockFetch.mockResolvedValueOnce({
|
|
80
|
+
ok: false,
|
|
81
|
+
status: 400,
|
|
82
|
+
json: () => Promise.resolve({ message: "Invalid query" }),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
await expect(
|
|
86
|
+
client.runQuery({ body: invalidQueryRequest })
|
|
87
|
+
).rejects.toThrow("API Error: Invalid query (Status: 400)");
|
|
88
|
+
|
|
89
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
90
|
+
"https://api.quickbase.com/v1/records/query",
|
|
91
|
+
expect.objectContaining({
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers: expect.objectContaining({
|
|
94
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
95
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
96
|
+
"Content-Type": "application/json",
|
|
97
|
+
}),
|
|
98
|
+
body: JSON.stringify(invalidQueryRequest),
|
|
99
|
+
})
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
});
|