quickbase-js 1.0.1-beta.0 → 1.1.0-beta.0
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 +24 -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/log-gen.yaml +3565 -0
- package/package.json +11 -7
- package/rollup.config.js +2 -2
- package/src/code-generation/fix-spec-definitions.ts +417 -68
- package/src/code-generation/fix-spec-paths.ts +207 -2
- package/src/code-generation/output/quickbase-fixed.json +646 -1122
- package/src/generated-unified/QuickbaseClient.ts +9 -8
- 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 +2 -2
- 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 +16 -8
- package/tests/vitest/qb/apps/getApp.test.ts +37 -43
- package/tests/vitest/qb/records/deleteRecords.test.ts +78 -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 +1 -1
- package/tests/vitest/unit/auth/fetchTempToken401.test.ts +4 -3
- package/tests/vitest/unit/auth/getTempToken.test.ts +8 -9
- package/tests/vitest/unit/auth/retryOn401.test.ts +1 -1
- package/tests/vitest/unit/auth/two401s.test.ts +1 -1
- package/tests/vitest/unit/fields/getFields.test.ts +1 -1
- package/tests/vitest/unit/records/deleteRecords.test.ts +118 -0
- package/tests/vitest/unit/records/upsert.test.ts +228 -0
- package/tests/vitest/unit/tables/createTable.test.ts +62 -0
- package/tests/vitest/unit/tables/deleteTable.test.ts +68 -0
- package/tests/vitest/unit/tables/getAppTables.test.ts +1 -1
- package/tests/vitest/unit/tables/getTable.test.ts +212 -0
- package/tests/vitest/unit/tables/updateTable.test.ts +74 -0
- package/tsconfig.json +3 -2
- package/vitest.config.ts +3 -2
- package/dist/quickbaseClient.js.map +0 -1
- package/logGen.yaml +0 -1820
- package/src/utils.ts +0 -5
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
+
import { createClient, mockFetch } from "@tests/setup.ts";
|
|
3
|
+
|
|
4
|
+
describe("QuickbaseClient - getTable (Unit)", () => {
|
|
5
|
+
let client: ReturnType<typeof createClient>;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
mockFetch.mockClear();
|
|
9
|
+
client = createClient(mockFetch, { debug: true });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("calls getTable successfully with user token", async () => {
|
|
13
|
+
const mockAppId = "buwai2zpe";
|
|
14
|
+
const mockTableId = "buwai2zr4";
|
|
15
|
+
const mockResponse = {
|
|
16
|
+
id: mockTableId,
|
|
17
|
+
name: "Root",
|
|
18
|
+
alias: "_DBID_ROOT",
|
|
19
|
+
description: "",
|
|
20
|
+
created: new Date("2025-02-13T18:22:33.000Z"),
|
|
21
|
+
updated: new Date("2025-02-13T18:22:34.000Z"),
|
|
22
|
+
nextRecordId: 1,
|
|
23
|
+
nextFieldId: 6,
|
|
24
|
+
defaultSortFieldId: 2,
|
|
25
|
+
defaultSortOrder: "DESC",
|
|
26
|
+
keyFieldId: 3,
|
|
27
|
+
singleRecordName: "Root",
|
|
28
|
+
pluralRecordName: "Roots",
|
|
29
|
+
sizeLimit: "500 MB",
|
|
30
|
+
spaceUsed: "0 KB",
|
|
31
|
+
spaceRemaining: "500 MB",
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
mockFetch.mockResolvedValueOnce({
|
|
35
|
+
ok: true,
|
|
36
|
+
status: 200,
|
|
37
|
+
json: () => Promise.resolve(mockResponse),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const response = await client.getTable({
|
|
41
|
+
tableId: mockTableId,
|
|
42
|
+
appId: mockAppId,
|
|
43
|
+
});
|
|
44
|
+
expect(response).toEqual(mockResponse);
|
|
45
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
46
|
+
`https://api.quickbase.com/v1/tables/${mockTableId}?appId=${mockAppId}`,
|
|
47
|
+
expect.objectContaining({
|
|
48
|
+
method: "GET",
|
|
49
|
+
headers: expect.objectContaining({
|
|
50
|
+
Authorization:
|
|
51
|
+
"QB-USER-TOKEN b9f3pk_q4jd_0_b4qu5eebyvuix3xs57ysd7zn3",
|
|
52
|
+
}),
|
|
53
|
+
})
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("calls getTable successfully with temp token", async () => {
|
|
58
|
+
client = createClient(mockFetch, { debug: true, useTempTokens: true });
|
|
59
|
+
const mockAppId = "buwai2zpe";
|
|
60
|
+
const mockTableId = "buwai2zr4";
|
|
61
|
+
const mockResponse = {
|
|
62
|
+
id: mockTableId,
|
|
63
|
+
name: "Root",
|
|
64
|
+
alias: "_DBID_ROOT",
|
|
65
|
+
description: "",
|
|
66
|
+
created: new Date("2025-02-13T18:22:33.000Z"),
|
|
67
|
+
updated: new Date("2025-02-13T18:22:34.000Z"),
|
|
68
|
+
nextRecordId: 1,
|
|
69
|
+
nextFieldId: 6,
|
|
70
|
+
defaultSortFieldId: 2,
|
|
71
|
+
defaultSortOrder: "DESC",
|
|
72
|
+
keyFieldId: 3,
|
|
73
|
+
singleRecordName: "Root",
|
|
74
|
+
pluralRecordName: "Roots",
|
|
75
|
+
sizeLimit: "500 MB",
|
|
76
|
+
spaceUsed: "0 KB",
|
|
77
|
+
spaceRemaining: "500 MB",
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
mockFetch
|
|
81
|
+
.mockResolvedValueOnce({
|
|
82
|
+
ok: true,
|
|
83
|
+
status: 200,
|
|
84
|
+
json: () => Promise.resolve({ temporaryAuthorization: "temp_token" }),
|
|
85
|
+
})
|
|
86
|
+
.mockResolvedValueOnce({
|
|
87
|
+
ok: true,
|
|
88
|
+
status: 200,
|
|
89
|
+
json: () => Promise.resolve(mockResponse),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const response = await client.getTable({
|
|
93
|
+
tableId: mockTableId,
|
|
94
|
+
appId: mockAppId,
|
|
95
|
+
});
|
|
96
|
+
expect(response).toEqual(mockResponse);
|
|
97
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
98
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
99
|
+
`https://api.quickbase.com/v1/auth/temporary/${mockTableId}`,
|
|
100
|
+
expect.any(Object)
|
|
101
|
+
);
|
|
102
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
103
|
+
`https://api.quickbase.com/v1/tables/${mockTableId}?appId=${mockAppId}`,
|
|
104
|
+
expect.objectContaining({
|
|
105
|
+
headers: expect.objectContaining({
|
|
106
|
+
Authorization: "QB-TEMP-TOKEN temp_token",
|
|
107
|
+
}),
|
|
108
|
+
})
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("retries successfully after 401 with temp token", async () => {
|
|
113
|
+
client = createClient(mockFetch, { debug: true, useTempTokens: true });
|
|
114
|
+
const mockAppId = "buwai2zpe";
|
|
115
|
+
const mockTableId = "buwai2zr4";
|
|
116
|
+
const mockResponse = {
|
|
117
|
+
id: mockTableId,
|
|
118
|
+
name: "Root",
|
|
119
|
+
alias: "_DBID_ROOT",
|
|
120
|
+
description: "",
|
|
121
|
+
created: new Date("2025-02-13T18:22:33.000Z"),
|
|
122
|
+
updated: new Date("2025-02-13T18:22:34.000Z"),
|
|
123
|
+
nextRecordId: 1,
|
|
124
|
+
nextFieldId: 6,
|
|
125
|
+
defaultSortFieldId: 2,
|
|
126
|
+
defaultSortOrder: "DESC",
|
|
127
|
+
keyFieldId: 3,
|
|
128
|
+
singleRecordName: "Root",
|
|
129
|
+
pluralRecordName: "Roots",
|
|
130
|
+
sizeLimit: "500 MB",
|
|
131
|
+
spaceUsed: "0 KB",
|
|
132
|
+
spaceRemaining: "500 MB",
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
mockFetch
|
|
136
|
+
.mockResolvedValueOnce({
|
|
137
|
+
ok: true,
|
|
138
|
+
status: 200,
|
|
139
|
+
json: () =>
|
|
140
|
+
Promise.resolve({ temporaryAuthorization: "initial_token" }),
|
|
141
|
+
})
|
|
142
|
+
.mockResolvedValueOnce({
|
|
143
|
+
ok: false,
|
|
144
|
+
status: 401,
|
|
145
|
+
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
146
|
+
})
|
|
147
|
+
.mockResolvedValueOnce({
|
|
148
|
+
ok: true,
|
|
149
|
+
status: 200,
|
|
150
|
+
json: () => Promise.resolve({ temporaryAuthorization: "new_token" }),
|
|
151
|
+
})
|
|
152
|
+
.mockResolvedValueOnce({
|
|
153
|
+
ok: true,
|
|
154
|
+
status: 200,
|
|
155
|
+
json: () => Promise.resolve(mockResponse),
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const response = await client.getTable({
|
|
159
|
+
tableId: mockTableId,
|
|
160
|
+
appId: mockAppId,
|
|
161
|
+
});
|
|
162
|
+
expect(response).toEqual(mockResponse);
|
|
163
|
+
expect(mockFetch).toHaveBeenCalledTimes(4); // Initial token, 401, retry token, success
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("handles 401 with failed temp token retry", async () => {
|
|
167
|
+
client = createClient(mockFetch, { debug: true, useTempTokens: true });
|
|
168
|
+
const mockAppId = "buwai2zpe";
|
|
169
|
+
const mockTableId = "buwai2zr4";
|
|
170
|
+
|
|
171
|
+
mockFetch
|
|
172
|
+
.mockResolvedValueOnce({
|
|
173
|
+
ok: true,
|
|
174
|
+
status: 200,
|
|
175
|
+
json: () =>
|
|
176
|
+
Promise.resolve({ temporaryAuthorization: "initial_token" }),
|
|
177
|
+
})
|
|
178
|
+
.mockResolvedValueOnce({
|
|
179
|
+
ok: false,
|
|
180
|
+
status: 401,
|
|
181
|
+
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
182
|
+
})
|
|
183
|
+
.mockResolvedValueOnce({
|
|
184
|
+
ok: false,
|
|
185
|
+
status: 401,
|
|
186
|
+
json: () =>
|
|
187
|
+
Promise.resolve({ message: "Unauthorized in fetchTempToken" }),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await expect(
|
|
191
|
+
client.getTable({ tableId: mockTableId, appId: mockAppId })
|
|
192
|
+
).rejects.toThrow(
|
|
193
|
+
"API Error: Unauthorized in fetchTempToken (Status: 401)"
|
|
194
|
+
);
|
|
195
|
+
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("handles 404 Not Found", async () => {
|
|
199
|
+
const mockAppId = "buwai2zpe";
|
|
200
|
+
const mockTableId = "invalidTableId";
|
|
201
|
+
|
|
202
|
+
mockFetch.mockResolvedValueOnce({
|
|
203
|
+
ok: false,
|
|
204
|
+
status: 404,
|
|
205
|
+
json: () => Promise.resolve({ message: "Table not found" }),
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await expect(
|
|
209
|
+
client.getTable({ tableId: mockTableId, appId: mockAppId })
|
|
210
|
+
).rejects.toThrow("API Error: Table not found (Status: 404)");
|
|
211
|
+
});
|
|
212
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
+
import { createClient, mockFetch } from "@tests/setup.ts";
|
|
3
|
+
|
|
4
|
+
describe("QuickbaseClient - updateTable (Unit)", () => {
|
|
5
|
+
let client: ReturnType<typeof createClient>;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
mockFetch.mockClear();
|
|
9
|
+
client = createClient(mockFetch, { debug: true });
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("calls updateTable successfully with temp token", async () => {
|
|
13
|
+
client = createClient(mockFetch, { debug: true, useTempTokens: true });
|
|
14
|
+
const mockAppId = "buwai2zpe";
|
|
15
|
+
const mockTableId = "buwai2zr4";
|
|
16
|
+
const mockBody = {
|
|
17
|
+
name: "Updated Root",
|
|
18
|
+
description: "Updated description",
|
|
19
|
+
};
|
|
20
|
+
const mockResponse = {
|
|
21
|
+
id: mockTableId,
|
|
22
|
+
name: "Updated Root",
|
|
23
|
+
alias: "_DBID_ROOT",
|
|
24
|
+
description: "Updated description",
|
|
25
|
+
created: new Date("2025-02-13T18:22:33.000Z"),
|
|
26
|
+
updated: new Date("2025-03-10T12:00:00.000Z"),
|
|
27
|
+
nextRecordId: 1,
|
|
28
|
+
nextFieldId: 6,
|
|
29
|
+
defaultSortFieldId: 2,
|
|
30
|
+
defaultSortOrder: "DESC",
|
|
31
|
+
keyFieldId: 3,
|
|
32
|
+
singleRecordName: "Root",
|
|
33
|
+
pluralRecordName: "Roots",
|
|
34
|
+
sizeLimit: "500 MB",
|
|
35
|
+
spaceUsed: "0 KB",
|
|
36
|
+
spaceRemaining: "500 MB",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
mockFetch
|
|
40
|
+
.mockResolvedValueOnce({
|
|
41
|
+
ok: true,
|
|
42
|
+
status: 200,
|
|
43
|
+
json: () => Promise.resolve({ temporaryAuthorization: "temp_token" }),
|
|
44
|
+
})
|
|
45
|
+
.mockResolvedValueOnce({
|
|
46
|
+
ok: true,
|
|
47
|
+
status: 200,
|
|
48
|
+
json: () => Promise.resolve(mockResponse),
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const response = await client.updateTable({
|
|
52
|
+
tableId: mockTableId,
|
|
53
|
+
appId: mockAppId,
|
|
54
|
+
body: mockBody,
|
|
55
|
+
});
|
|
56
|
+
expect(response).toEqual(mockResponse);
|
|
57
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
58
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
59
|
+
`https://api.quickbase.com/v1/auth/temporary/${mockTableId}`,
|
|
60
|
+
expect.any(Object)
|
|
61
|
+
);
|
|
62
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
63
|
+
`https://api.quickbase.com/v1/tables/${mockTableId}?appId=${mockAppId}`,
|
|
64
|
+
expect.objectContaining({
|
|
65
|
+
method: "POST", // Updated to match QuickBase API spec
|
|
66
|
+
headers: expect.objectContaining({
|
|
67
|
+
Authorization: "QB-TEMP-TOKEN temp_token",
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
}),
|
|
70
|
+
body: JSON.stringify(mockBody),
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
});
|
package/tsconfig.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"module": "ESNext",
|
|
5
5
|
"moduleResolution": "bundler",
|
|
6
6
|
"strict": true,
|
|
7
|
-
"lib": ["ES2015", "ESNext", "DOM"],
|
|
7
|
+
"lib": ["ES2015", "ESNext", "DOM"],
|
|
8
8
|
"types": ["node", "playwright/test"],
|
|
9
9
|
"esModuleInterop": true,
|
|
10
10
|
"resolveJsonModule": true,
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
"sourceMap": true,
|
|
17
17
|
"baseUrl": ".",
|
|
18
18
|
"paths": {
|
|
19
|
-
"@/*": ["
|
|
19
|
+
"@/*": ["src/*"], // Matches Vite's @ -> src/
|
|
20
|
+
"@tests/*": ["tests/*"] // Matches Vite's @tests -> tests/
|
|
20
21
|
}
|
|
21
22
|
},
|
|
22
23
|
"include": ["src/**/*", "tests/**/*"],
|
package/vitest.config.ts
CHANGED
|
@@ -5,7 +5,7 @@ export default defineConfig({
|
|
|
5
5
|
test: {
|
|
6
6
|
environment: "jsdom",
|
|
7
7
|
include: ["tests/vitest/**/**/*.test.ts"],
|
|
8
|
-
setupFiles: ["tests/setup.ts"],
|
|
8
|
+
setupFiles: ["tests/setup.ts"], // Relative path from root
|
|
9
9
|
pool: "forks",
|
|
10
10
|
poolOptions: {
|
|
11
11
|
forks: {
|
|
@@ -15,7 +15,8 @@ export default defineConfig({
|
|
|
15
15
|
},
|
|
16
16
|
resolve: {
|
|
17
17
|
alias: {
|
|
18
|
-
"@": path.resolve(__dirname, "./"),
|
|
18
|
+
"@": path.resolve(__dirname, "./src"), // For src/ imports
|
|
19
|
+
"@tests": path.resolve(__dirname, "./tests"), // For tests/ imports
|
|
19
20
|
},
|
|
20
21
|
},
|
|
21
22
|
});
|