quickbase-js 0.1.0-beta.7 → 0.1.0-beta.8
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 +26 -0
- package/dist/esm/quickbase.d.ts +589 -125
- package/dist/esm/quickbase.js +484 -145
- package/dist/esm/quickbase.js.map +1 -1
- package/dist/esm/quickbase.min.js +1 -1
- package/dist/esm/quickbase.min.js.map +1 -1
- package/dist/umd/quickbase.umd.js +492 -144
- package/dist/umd/quickbase.umd.js.map +1 -1
- package/dist/umd/quickbase.umd.min.js +1 -1
- package/dist/umd/quickbase.umd.min.js.map +1 -1
- package/open-api/fix-spec-main.ts +69 -68
- package/open-api/generate-override-report.ts +214 -0
- package/open-api/generate-unified-interface.ts +73 -94
- package/open-api/output/override-report.html +11 -0
- package/open-api/output/override-report.json +14395 -58
- package/open-api/output/quickbase-fixed.json +583 -2
- package/open-api/schema/tags/fields.ts +331 -16
- package/open-api/schema/tags/index.ts +2 -4
- package/open-api/types/spec.ts +2 -1
- package/package.json +31 -33
- package/src/RateLimitError.ts +12 -0
- package/src/ThrottleBucket.ts +36 -0
- package/src/generated-unified/QuickbaseClient.ts +119 -119
- package/src/invokeMethod.ts +357 -0
- package/src/quickbaseClient.ts +51 -209
- package/tests/setup.ts +2 -0
- package/tests/vitest/qb/apps/getApp.test.ts +6 -2
- package/tests/vitest/qb/apps/getAppEvents.test.ts +67 -0
- package/tests/vitest/qb/apps/updateApp.test.ts +58 -0
- package/tests/vitest/qb/auth/retryUserToken401.test.ts +56 -0
- package/tests/vitest/qb/fields/createField.test.ts +1 -2
- package/tests/vitest/qb/fields/deleteFields.test.ts +10 -16
- package/tests/vitest/qb/fields/updateField.test.ts +93 -0
- package/tests/vitest/qb/index.ts +1 -0
- package/tests/vitest/qb/tables/updateRelationship.test.ts +277 -0
- package/tests/vitest/unit/apps/getAppEvents.test.ts +193 -0
- package/tests/vitest/unit/apps/updateApp.test.ts +381 -0
- package/tests/vitest/unit/auth/fetchTempToken401.test.ts +6 -26
- package/tests/vitest/unit/auth/{retryOn401.test.ts → retryTempToken401.test.ts} +3 -6
- package/tests/vitest/unit/auth/{two401s.test.ts → retryTempTokenTwo401.test.ts} +4 -9
- package/tests/vitest/unit/auth/retryUserToken401.test.ts +56 -0
- package/tests/vitest/unit/fields/createField.test.ts +8 -12
- package/tests/vitest/unit/fields/getFieldUsage.test.ts +33 -14
- package/tests/vitest/unit/fields/updateField.test.ts +280 -0
- package/tests/vitest/unit/rateLimit.test.ts +189 -0
- package/tests/vitest/unit/tables/updateRelationship.test.ts +305 -0
- package/tests/vitest/unit/tokenLifespan.test.ts +138 -0
- package/tools/log-gen.ts +8 -4
- package/tools/log-gen.yaml +3004 -1848
- package/tsconfig.json +5 -2
- package/vitest.config.ts +2 -2
- package/notes.txt +0 -158
- package/tools/generate-types.ts +0 -29
- package/tools/openapi-schema-validator.ts +0 -38
- package/tools/spec-analyzer.ts +0 -163
- package/tools/split-spec.ts +0 -148
- package/tools/test-import.ts +0 -3
- package/tools/validate-spec.ts +0 -40
- /package/tests/playwright/qb/auth/{tokenPrefetch.test.ts → tempTokenPrefetch.test.ts} +0 -0
- /package/tests/playwright/qb/auth/{tokenRenewal.test.ts → tempTokenRenewal.test.ts} +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
// tests/vitest/unit/auth/two401s.test.ts
|
|
2
1
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
3
2
|
import { createClient, mockFetch } from "@tests/setup.ts";
|
|
4
3
|
|
|
5
|
-
describe("QuickbaseClient - Two 401s in a Row", () => {
|
|
4
|
+
describe("QuickbaseClient - Two Temp Token 401s in a Row", () => {
|
|
6
5
|
let client: ReturnType<typeof createClient>;
|
|
7
6
|
|
|
8
7
|
beforeEach(() => {
|
|
@@ -28,7 +27,7 @@ describe("QuickbaseClient - Two 401s in a Row", () => {
|
|
|
28
27
|
return Promise.resolve({
|
|
29
28
|
ok: false,
|
|
30
29
|
status: 401,
|
|
31
|
-
|
|
30
|
+
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
32
31
|
});
|
|
33
32
|
}
|
|
34
33
|
if (url.includes("auth/temporary") && callCount === 3) {
|
|
@@ -70,15 +69,11 @@ describe("QuickbaseClient - Two 401s in a Row", () => {
|
|
|
70
69
|
]);
|
|
71
70
|
|
|
72
71
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
73
|
-
"Authorization error for getFields, refreshing token:",
|
|
72
|
+
"Authorization error for getFields (temp token), refreshing token:",
|
|
74
73
|
expect.any(String)
|
|
75
74
|
);
|
|
76
75
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
77
|
-
"Retrying getFields with
|
|
78
|
-
);
|
|
79
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
80
|
-
"Error response body for getFields:",
|
|
81
|
-
{ message: "Unauthorized again" }
|
|
76
|
+
"Retrying getFields with temp token"
|
|
82
77
|
);
|
|
83
78
|
|
|
84
79
|
consoleSpy.mockRestore();
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describe, it, vi, expect, beforeEach } from "vitest";
|
|
2
|
+
import { quickbase, QuickbaseClient } from "../../../../src/quickbaseClient";
|
|
3
|
+
import { ResponseError } from "../../../../src/generated/runtime";
|
|
4
|
+
|
|
5
|
+
describe("QuickbaseClient Integration - User Token Retry on 401", () => {
|
|
6
|
+
let client: QuickbaseClient;
|
|
7
|
+
const mockFetch = vi.fn();
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
const config = {
|
|
11
|
+
realm: process.env.QB_REALM || "builderprogram-dbradford6815",
|
|
12
|
+
userToken: "mock-user-token",
|
|
13
|
+
debug: true,
|
|
14
|
+
fetchApi: mockFetch,
|
|
15
|
+
};
|
|
16
|
+
console.log("[quickbaseTest] Config:", config);
|
|
17
|
+
client = quickbase(config);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("retries on transient 401 with user token and succeeds", async () => {
|
|
21
|
+
const consoleSpy = vi.spyOn(console, "log");
|
|
22
|
+
|
|
23
|
+
mockFetch
|
|
24
|
+
.mockImplementationOnce(() => {
|
|
25
|
+
const response = new Response(null, {
|
|
26
|
+
status: 401,
|
|
27
|
+
statusText: "Unauthorized",
|
|
28
|
+
});
|
|
29
|
+
console.log("[mockFetch] Throwing 401 ResponseError");
|
|
30
|
+
return Promise.reject(new ResponseError(response));
|
|
31
|
+
})
|
|
32
|
+
.mockResolvedValueOnce({
|
|
33
|
+
ok: true,
|
|
34
|
+
status: 200,
|
|
35
|
+
json: async () => [{ id: 1, label: "Mock Field", fieldType: "text" }],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const fields = await client.getFields({ tableId: "mock-table-id" });
|
|
39
|
+
|
|
40
|
+
expect(fields).toBeInstanceOf(Array);
|
|
41
|
+
expect(fields.length).toBeGreaterThan(0);
|
|
42
|
+
expect(fields[0]).toHaveProperty("id");
|
|
43
|
+
expect(fields[0]).toHaveProperty("label");
|
|
44
|
+
|
|
45
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
46
|
+
"Authorization error for getFields (user token), retrying with same token:",
|
|
47
|
+
"" // Matches empty ResponseError message from mock
|
|
48
|
+
);
|
|
49
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
50
|
+
"Retrying getFields with user token"
|
|
51
|
+
);
|
|
52
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
53
|
+
|
|
54
|
+
consoleSpy.mockRestore();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// tests/vitest/unit/fields/createField.test.ts
|
|
2
1
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
3
2
|
import {
|
|
4
3
|
createClient,
|
|
@@ -83,9 +82,9 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
83
82
|
|
|
84
83
|
expect(result).toEqual(mockResponse);
|
|
85
84
|
const callArgs = mockFetch.mock.calls[0];
|
|
86
|
-
console.log("Raw fetch call (user token):", callArgs);
|
|
85
|
+
console.log("Raw fetch call (user token):", callArgs);
|
|
87
86
|
const receivedBody = JSON.parse(callArgs[1].body as string);
|
|
88
|
-
expect(receivedBody).toEqual(request);
|
|
87
|
+
expect(receivedBody).toEqual(request);
|
|
89
88
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
90
89
|
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
91
90
|
expect.objectContaining({
|
|
@@ -172,7 +171,7 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
172
171
|
})
|
|
173
172
|
);
|
|
174
173
|
const callArgs = mockFetch.mock.calls[1];
|
|
175
|
-
console.log("Raw fetch call (temp token):", callArgs);
|
|
174
|
+
console.log("Raw fetch call (temp token):", callArgs);
|
|
176
175
|
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
177
176
|
2,
|
|
178
177
|
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
@@ -287,11 +286,11 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
287
286
|
})
|
|
288
287
|
);
|
|
289
288
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
290
|
-
"Authorization error for createField, refreshing token:",
|
|
289
|
+
"Authorization error for createField (temp token), refreshing token:",
|
|
291
290
|
expect.any(String)
|
|
292
291
|
);
|
|
293
292
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
294
|
-
"Retrying createField with
|
|
293
|
+
"Retrying createField with temp token"
|
|
295
294
|
);
|
|
296
295
|
consoleSpy.mockRestore();
|
|
297
296
|
});
|
|
@@ -351,16 +350,13 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
351
350
|
.mockResolvedValueOnce({
|
|
352
351
|
ok: false,
|
|
353
352
|
status: 401,
|
|
354
|
-
json: () =>
|
|
355
|
-
Promise.resolve({ message: "Unauthorized in fetchTempToken" }),
|
|
353
|
+
json: () => Promise.resolve({ message: "Temp token fetch failed" }),
|
|
356
354
|
});
|
|
357
355
|
|
|
358
356
|
const consoleSpy = vi.spyOn(console, "log");
|
|
359
357
|
await expect(
|
|
360
358
|
client.createField({ tableId: QB_TABLE_ID_1, body: request })
|
|
361
|
-
).rejects.toThrow(
|
|
362
|
-
"API Error: Unauthorized in fetchTempToken (Status: 401)"
|
|
363
|
-
);
|
|
359
|
+
).rejects.toThrow("API Error: Temp token fetch failed (Status: 401)");
|
|
364
360
|
|
|
365
361
|
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
366
362
|
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
@@ -383,7 +379,7 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
383
379
|
expect.any(Object)
|
|
384
380
|
);
|
|
385
381
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
386
|
-
"Authorization error for createField, refreshing token:",
|
|
382
|
+
"Authorization error for createField (temp token), refreshing token:",
|
|
387
383
|
expect.any(String)
|
|
388
384
|
);
|
|
389
385
|
consoleSpy.mockRestore();
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// tests/vitest/unit/fields/getFieldUsage.test.ts
|
|
2
1
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
3
2
|
import {
|
|
4
3
|
createClient,
|
|
@@ -27,7 +26,7 @@ describe("QuickbaseClient Unit - getFieldUsage", () => {
|
|
|
27
26
|
field: {
|
|
28
27
|
id: 6,
|
|
29
28
|
name: "name",
|
|
30
|
-
type: "text",
|
|
29
|
+
type: "text",
|
|
31
30
|
},
|
|
32
31
|
usage: {
|
|
33
32
|
actions: { count: 0 },
|
|
@@ -50,7 +49,6 @@ describe("QuickbaseClient Unit - getFieldUsage", () => {
|
|
|
50
49
|
},
|
|
51
50
|
};
|
|
52
51
|
|
|
53
|
-
// Mock the response as an array to match the updated API behavior
|
|
54
52
|
mockFetch.mockResolvedValueOnce(
|
|
55
53
|
new Response(JSON.stringify([mockResponse]), {
|
|
56
54
|
status: 200,
|
|
@@ -64,7 +62,6 @@ describe("QuickbaseClient Unit - getFieldUsage", () => {
|
|
|
64
62
|
tableId: QB_TABLE_ID_1,
|
|
65
63
|
});
|
|
66
64
|
|
|
67
|
-
// Expect an array and unwrap the first item
|
|
68
65
|
expect(result).toBeInstanceOf(Array);
|
|
69
66
|
expect(result).toHaveLength(1);
|
|
70
67
|
const fieldUsage = result[0];
|
|
@@ -115,14 +112,23 @@ describe("QuickbaseClient Unit - getFieldUsage", () => {
|
|
|
115
112
|
);
|
|
116
113
|
});
|
|
117
114
|
|
|
118
|
-
it("handles 401 error
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
115
|
+
it("handles persistent 401 error for field usage retrieval", async () => {
|
|
116
|
+
// Simulate a persistent 401 error (initial and retry attempts fail)
|
|
117
|
+
mockFetch
|
|
118
|
+
.mockResolvedValueOnce(
|
|
119
|
+
new Response(JSON.stringify({ message: "Unauthorized" }), {
|
|
120
|
+
status: 401,
|
|
121
|
+
statusText: "Unauthorized",
|
|
122
|
+
headers: { "Content-Type": "application/json" },
|
|
123
|
+
})
|
|
124
|
+
)
|
|
125
|
+
.mockResolvedValueOnce(
|
|
126
|
+
new Response(JSON.stringify({ message: "Unauthorized" }), {
|
|
127
|
+
status: 401,
|
|
128
|
+
statusText: "Unauthorized",
|
|
129
|
+
headers: { "Content-Type": "application/json" },
|
|
130
|
+
})
|
|
131
|
+
);
|
|
126
132
|
|
|
127
133
|
await expect(
|
|
128
134
|
client.getFieldUsage({
|
|
@@ -131,8 +137,21 @@ describe("QuickbaseClient Unit - getFieldUsage", () => {
|
|
|
131
137
|
})
|
|
132
138
|
).rejects.toThrow("API Error: Unauthorized (Status: 401)");
|
|
133
139
|
|
|
134
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
135
|
-
expect(mockFetch).
|
|
140
|
+
expect(mockFetch).toHaveBeenCalledTimes(2); // Initial call + 1 retry
|
|
141
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
142
|
+
1,
|
|
143
|
+
`https://api.quickbase.com/v1/fields/usage/6?tableId=${QB_TABLE_ID_1}`,
|
|
144
|
+
expect.objectContaining({
|
|
145
|
+
method: "GET",
|
|
146
|
+
headers: expect.objectContaining({
|
|
147
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
148
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
149
|
+
"Content-Type": "application/json",
|
|
150
|
+
}),
|
|
151
|
+
})
|
|
152
|
+
);
|
|
153
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
154
|
+
2,
|
|
136
155
|
`https://api.quickbase.com/v1/fields/usage/6?tableId=${QB_TABLE_ID_1}`,
|
|
137
156
|
expect.objectContaining({
|
|
138
157
|
method: "GET",
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
// tests/vitest/unit/fields/updateField.test.ts
|
|
2
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
3
|
+
import {
|
|
4
|
+
createClient,
|
|
5
|
+
mockFetch,
|
|
6
|
+
QB_APP_ID,
|
|
7
|
+
QB_REALM,
|
|
8
|
+
QB_USER_TOKEN,
|
|
9
|
+
QB_TABLE_ID_1,
|
|
10
|
+
} from "@tests/setup.ts";
|
|
11
|
+
import { UpdateFieldRequest, UpdateField200Response } from "@/generated/models";
|
|
12
|
+
|
|
13
|
+
describe("QuickbaseClient Unit - updateField", () => {
|
|
14
|
+
let client: ReturnType<typeof createClient>;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
mockFetch.mockClear();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("initializes and has updateField method", () => {
|
|
21
|
+
client = createClient(mockFetch, { debug: true });
|
|
22
|
+
expect(client).toBeDefined();
|
|
23
|
+
expect(typeof client.updateField).toBe("function");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("updates field successfully with user token", async () => {
|
|
27
|
+
client = createClient(mockFetch, {
|
|
28
|
+
realm: QB_REALM,
|
|
29
|
+
userToken: QB_USER_TOKEN,
|
|
30
|
+
debug: true,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const tableId = QB_TABLE_ID_1;
|
|
34
|
+
const fieldId = 123;
|
|
35
|
+
|
|
36
|
+
const request: UpdateFieldRequest = {
|
|
37
|
+
label: "Field1",
|
|
38
|
+
// fieldType omitted as it’s not allowed in updates
|
|
39
|
+
noWrap: false,
|
|
40
|
+
bold: false,
|
|
41
|
+
required: true,
|
|
42
|
+
appearsByDefault: false,
|
|
43
|
+
findEnabled: false,
|
|
44
|
+
unique: true,
|
|
45
|
+
fieldHelp: "field help",
|
|
46
|
+
addToForms: true,
|
|
47
|
+
properties: {
|
|
48
|
+
numLines: 1,
|
|
49
|
+
maxLength: 50,
|
|
50
|
+
appendOnly: true,
|
|
51
|
+
sortAsGiven: false,
|
|
52
|
+
allowMentions: false,
|
|
53
|
+
comments: "Updated field settings",
|
|
54
|
+
doesTotal: false,
|
|
55
|
+
defaultValue: "Initial text",
|
|
56
|
+
choices: ["Option1", "Option2"], // Still valid for properties
|
|
57
|
+
},
|
|
58
|
+
permissions: [
|
|
59
|
+
{ role: "Viewer", permissionType: "View", roleId: 10 },
|
|
60
|
+
{ role: "Participant", permissionType: "None", roleId: 11 },
|
|
61
|
+
{ role: "Administrator", permissionType: "Modify", roleId: 12 },
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const mockResponse: UpdateField200Response = {
|
|
66
|
+
id: fieldId,
|
|
67
|
+
label: "Field1",
|
|
68
|
+
fieldType: "text", // Returned by API, not updated
|
|
69
|
+
mode: "",
|
|
70
|
+
noWrap: false,
|
|
71
|
+
bold: false,
|
|
72
|
+
required: true,
|
|
73
|
+
appearsByDefault: false,
|
|
74
|
+
findEnabled: false,
|
|
75
|
+
unique: true,
|
|
76
|
+
doesDataCopy: false,
|
|
77
|
+
fieldHelp: "field help",
|
|
78
|
+
audited: false,
|
|
79
|
+
properties: {
|
|
80
|
+
numLines: 1,
|
|
81
|
+
maxLength: 50,
|
|
82
|
+
appendOnly: true,
|
|
83
|
+
sortAsGiven: false,
|
|
84
|
+
allowMentions: false,
|
|
85
|
+
comments: "Updated field settings",
|
|
86
|
+
doesTotal: false,
|
|
87
|
+
defaultValue: "Initial text",
|
|
88
|
+
choices: ["Option1", "Option2"],
|
|
89
|
+
allowHTML: false,
|
|
90
|
+
carryChoices: true,
|
|
91
|
+
allowNewChoices: false,
|
|
92
|
+
formula: "",
|
|
93
|
+
},
|
|
94
|
+
permissions: [
|
|
95
|
+
{ permissionType: "View", role: "Viewer", roleId: 10 },
|
|
96
|
+
{ permissionType: "None", role: "Participant", roleId: 11 },
|
|
97
|
+
{ permissionType: "Modify", role: "Administrator", roleId: 12 },
|
|
98
|
+
],
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
mockFetch.mockResolvedValueOnce({
|
|
102
|
+
ok: true,
|
|
103
|
+
status: 200,
|
|
104
|
+
json: () => Promise.resolve(mockResponse),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const result = await client.updateField({
|
|
108
|
+
fieldId,
|
|
109
|
+
tableId,
|
|
110
|
+
body: request,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
expect(result).toEqual(mockResponse);
|
|
114
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
115
|
+
`https://api.quickbase.com/v1/fields/${fieldId}?tableId=${tableId}`,
|
|
116
|
+
expect.objectContaining({
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: expect.objectContaining({
|
|
119
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
120
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
121
|
+
"Content-Type": "application/json",
|
|
122
|
+
}),
|
|
123
|
+
body: JSON.stringify(request),
|
|
124
|
+
credentials: "omit",
|
|
125
|
+
})
|
|
126
|
+
);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("updates field successfully with temp token", async () => {
|
|
130
|
+
client = createClient(mockFetch, {
|
|
131
|
+
realm: QB_REALM,
|
|
132
|
+
useTempTokens: true,
|
|
133
|
+
debug: true,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const tableId = QB_TABLE_ID_1;
|
|
137
|
+
const fieldId = 123;
|
|
138
|
+
|
|
139
|
+
const request: UpdateFieldRequest = {
|
|
140
|
+
label: "Field1",
|
|
141
|
+
// fieldType omitted as it’s not allowed in updates
|
|
142
|
+
required: true,
|
|
143
|
+
unique: true,
|
|
144
|
+
properties: {
|
|
145
|
+
numLines: 1,
|
|
146
|
+
maxLength: 0,
|
|
147
|
+
choices: ["Yes", "No"],
|
|
148
|
+
allowNewChoices: true,
|
|
149
|
+
defaultValue: "No",
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const mockResponse: UpdateField200Response = {
|
|
154
|
+
id: fieldId,
|
|
155
|
+
label: "Field1",
|
|
156
|
+
fieldType: "text-multiple-choice", // Returned by API, not updated
|
|
157
|
+
mode: "",
|
|
158
|
+
noWrap: false,
|
|
159
|
+
bold: false,
|
|
160
|
+
required: true,
|
|
161
|
+
appearsByDefault: false,
|
|
162
|
+
findEnabled: false,
|
|
163
|
+
unique: true,
|
|
164
|
+
doesDataCopy: false,
|
|
165
|
+
fieldHelp: "",
|
|
166
|
+
audited: false,
|
|
167
|
+
properties: {
|
|
168
|
+
numLines: 1,
|
|
169
|
+
maxLength: 0,
|
|
170
|
+
choices: ["Yes", "No"],
|
|
171
|
+
allowNewChoices: true,
|
|
172
|
+
defaultValue: "No",
|
|
173
|
+
appendOnly: false,
|
|
174
|
+
allowHTML: false,
|
|
175
|
+
allowMentions: false,
|
|
176
|
+
sortAsGiven: false,
|
|
177
|
+
carryChoices: true,
|
|
178
|
+
formula: "",
|
|
179
|
+
},
|
|
180
|
+
permissions: [
|
|
181
|
+
{ permissionType: "View", role: "Viewer", roleId: 10 },
|
|
182
|
+
{ permissionType: "None", role: "Participant", roleId: 11 },
|
|
183
|
+
{ permissionType: "Modify", role: "Administrator", roleId: 12 },
|
|
184
|
+
],
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
mockFetch
|
|
188
|
+
.mockResolvedValueOnce({
|
|
189
|
+
ok: true,
|
|
190
|
+
status: 200,
|
|
191
|
+
json: () => Promise.resolve({ temporaryAuthorization: "temp_token" }),
|
|
192
|
+
})
|
|
193
|
+
.mockResolvedValueOnce({
|
|
194
|
+
ok: true,
|
|
195
|
+
status: 200,
|
|
196
|
+
json: () => Promise.resolve(mockResponse),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const result = await client.updateField({
|
|
200
|
+
fieldId,
|
|
201
|
+
tableId,
|
|
202
|
+
body: request,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
expect(result).toEqual(mockResponse);
|
|
206
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
207
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
208
|
+
1,
|
|
209
|
+
`https://api.quickbase.com/v1/auth/temporary/${tableId}`,
|
|
210
|
+
expect.objectContaining({
|
|
211
|
+
method: "GET",
|
|
212
|
+
headers: expect.objectContaining({
|
|
213
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
214
|
+
"Content-Type": "application/json",
|
|
215
|
+
}),
|
|
216
|
+
credentials: "include",
|
|
217
|
+
})
|
|
218
|
+
);
|
|
219
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
220
|
+
2,
|
|
221
|
+
`https://api.quickbase.com/v1/fields/${fieldId}?tableId=${tableId}`,
|
|
222
|
+
expect.objectContaining({
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: expect.objectContaining({
|
|
225
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
226
|
+
Authorization: "QB-TEMP-TOKEN temp_token",
|
|
227
|
+
"Content-Type": "application/json",
|
|
228
|
+
}),
|
|
229
|
+
body: JSON.stringify(request),
|
|
230
|
+
credentials: "omit",
|
|
231
|
+
})
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("handles 400 error for invalid field update", async () => {
|
|
236
|
+
client = createClient(mockFetch, {
|
|
237
|
+
realm: QB_REALM,
|
|
238
|
+
userToken: QB_USER_TOKEN,
|
|
239
|
+
debug: true,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const tableId = QB_TABLE_ID_1;
|
|
243
|
+
const fieldId = 999;
|
|
244
|
+
|
|
245
|
+
const request: UpdateFieldRequest = {
|
|
246
|
+
label: "InvalidField",
|
|
247
|
+
// fieldType omitted as it’s not allowed in updates
|
|
248
|
+
required: true,
|
|
249
|
+
properties: {
|
|
250
|
+
numLines: 1,
|
|
251
|
+
decimalPlaces: 2,
|
|
252
|
+
currencySymbol: "$",
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
mockFetch.mockResolvedValueOnce({
|
|
257
|
+
ok: false,
|
|
258
|
+
status: 400,
|
|
259
|
+
json: () => Promise.resolve({ message: "Invalid field ID" }),
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
await expect(
|
|
263
|
+
client.updateField({ fieldId, tableId, body: request })
|
|
264
|
+
).rejects.toThrow("API Error: Invalid field ID (Status: 400)");
|
|
265
|
+
|
|
266
|
+
expect(mockFetch).toHaveBeenCalledWith(
|
|
267
|
+
`https://api.quickbase.com/v1/fields/${fieldId}?tableId=${tableId}`,
|
|
268
|
+
expect.objectContaining({
|
|
269
|
+
method: "POST",
|
|
270
|
+
headers: expect.objectContaining({
|
|
271
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
272
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
273
|
+
"Content-Type": "application/json",
|
|
274
|
+
}),
|
|
275
|
+
body: JSON.stringify(request),
|
|
276
|
+
credentials: "omit",
|
|
277
|
+
})
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
});
|