quickbase-js 0.1.0-beta.7 → 0.1.0-beta.9
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 +55 -0
- package/README.md +46 -130
- package/dist/esm/quickbase.d.ts +1292 -125
- package/dist/esm/quickbase.js +666 -190
- 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 +674 -189
- 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 +162 -103
- 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/open-api/utils/generateJsDoc.ts +96 -0
- package/package.json +33 -33
- package/src/RateLimitError.ts +12 -0
- package/src/ThrottleBucket.ts +41 -0
- package/src/authorizationStrategy.ts +246 -0
- package/src/generated-unified/QuickbaseClient.ts +809 -119
- package/src/invokeMethod.ts +242 -0
- package/src/quickbaseClient.ts +117 -248
- package/src/rateLimiter.ts +25 -0
- package/src/tokenCache.ts +10 -26
- 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/mockQuickbaseServer.ts +60 -0
- package/tests/vitest/qb/auth/retryUserToken401.test.ts +73 -0
- package/tests/vitest/qb/auth/ssoTokenIntegrationWithServer.test.ts +65 -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/getFieldUsage.test.ts +58 -150
- package/tests/vitest/qb/fields/updateField.test.ts +93 -0
- package/tests/vitest/qb/formulas/runFormula.test.ts +96 -0
- package/tests/vitest/qb/index.ts +1 -0
- package/tests/vitest/qb/tables/createRelationship.test.ts +102 -0
- package/tests/vitest/qb/tables/deleteRelationship.test.ts +143 -0
- package/tests/vitest/qb/tables/updateRelationship.test.ts +250 -0
- package/tests/vitest/unit/ThrottleBucket.test.ts +95 -0
- package/tests/vitest/unit/apps/copyApp.test.ts +133 -40
- package/tests/vitest/unit/apps/getAppEvents.test.ts +193 -0
- package/tests/vitest/unit/apps/updateApp.test.ts +412 -0
- package/tests/vitest/unit/auth/fetchTempToken401.test.ts +7 -26
- package/tests/vitest/unit/auth/getTempToken.test.ts +42 -18
- package/tests/vitest/unit/auth/retryTempToken401.test.ts +95 -0
- package/tests/vitest/unit/auth/retryTempTokenMaxRetries.test.ts +235 -0
- package/tests/vitest/unit/auth/retryUserToken401.test.ts +69 -0
- package/tests/vitest/unit/auth/ssoTokenRefresh.test.ts +325 -0
- package/tests/vitest/unit/fields/createField.test.ts +180 -120
- package/tests/vitest/unit/fields/getFieldUsage.test.ts +47 -14
- package/tests/vitest/unit/fields/updateField.test.ts +285 -0
- package/tests/vitest/unit/formulas/runFormula.test.ts +205 -0
- package/tests/vitest/unit/index.ts +2 -1
- package/tests/vitest/unit/inferHttpMethod.test.ts +131 -0
- package/tests/vitest/unit/rateLimit.test.ts +191 -0
- package/tests/vitest/unit/tables/createRelationship.test.ts +212 -0
- package/tests/vitest/unit/tables/deleteRelationship.test.ts +97 -0
- package/tests/vitest/unit/tables/updateRelationship.test.ts +307 -0
- package/tests/vitest/unit/tokenLifespan.test.ts +204 -0
- package/tools/log-gen.ts +20 -7
- package/tools/log-gen.yaml +3446 -1848
- package/tsconfig.json +5 -2
- package/vitest.config.ts +2 -2
- package/notes.txt +0 -158
- package/tests/vitest/unit/auth/retryOn401.test.ts +0 -116
- package/tests/vitest/unit/auth/two401s.test.ts +0 -86
- 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,13 +1,12 @@
|
|
|
1
|
-
// tests/vitest/unit/fields/createField.test.ts
|
|
2
1
|
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
3
2
|
import {
|
|
4
3
|
createClient,
|
|
5
4
|
mockFetch,
|
|
5
|
+
QB_TABLE_ID_1,
|
|
6
6
|
QB_REALM,
|
|
7
7
|
QB_USER_TOKEN,
|
|
8
|
-
QB_TABLE_ID_1,
|
|
9
8
|
} from "@tests/setup.ts";
|
|
10
|
-
import { CreateFieldRequest
|
|
9
|
+
import { CreateFieldRequest } from "@/generated/models";
|
|
11
10
|
|
|
12
11
|
describe("QuickbaseClient Unit - createField", () => {
|
|
13
12
|
let client: ReturnType<typeof createClient>;
|
|
@@ -22,52 +21,43 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
22
21
|
expect(typeof client.createField).toBe("function");
|
|
23
22
|
});
|
|
24
23
|
|
|
25
|
-
it("creates
|
|
24
|
+
it("creates field successfully with user token", async () => {
|
|
26
25
|
client = createClient(mockFetch, { debug: true });
|
|
27
26
|
|
|
28
27
|
const request: CreateFieldRequest = {
|
|
29
|
-
label: "
|
|
28
|
+
label: "NewField",
|
|
30
29
|
fieldType: "text",
|
|
31
|
-
fieldHelp: "A test field",
|
|
32
|
-
addToForms: true,
|
|
33
|
-
permissions: [
|
|
34
|
-
{ role: "Viewer", permissionType: "View", roleId: 10 },
|
|
35
|
-
{ role: "Administrator", permissionType: "Modify", roleId: 12 },
|
|
36
|
-
],
|
|
37
30
|
};
|
|
38
31
|
|
|
39
|
-
const mockResponse
|
|
40
|
-
id:
|
|
41
|
-
label: "
|
|
32
|
+
const mockResponse = {
|
|
33
|
+
id: 101,
|
|
34
|
+
label: "NewField",
|
|
42
35
|
fieldType: "text",
|
|
43
|
-
mode: "",
|
|
44
|
-
noWrap: false,
|
|
45
|
-
bold: false,
|
|
46
|
-
required: false,
|
|
47
36
|
appearsByDefault: true,
|
|
48
|
-
findEnabled: true,
|
|
49
|
-
unique: false,
|
|
50
|
-
doesDataCopy: false,
|
|
51
|
-
fieldHelp: "A test field",
|
|
52
37
|
audited: false,
|
|
38
|
+
bold: false,
|
|
39
|
+
doesDataCopy: false,
|
|
40
|
+
fieldHelp: "",
|
|
41
|
+
findEnabled: true,
|
|
42
|
+
mode: "",
|
|
43
|
+
noWrap: false,
|
|
44
|
+
permissions: [],
|
|
53
45
|
properties: {
|
|
54
|
-
primaryKey: false,
|
|
55
|
-
foreignKey: false,
|
|
56
|
-
numLines: 1,
|
|
57
|
-
maxLength: 0,
|
|
58
|
-
appendOnly: false,
|
|
59
46
|
allowHTML: false,
|
|
60
47
|
allowMentions: false,
|
|
61
|
-
sortAsGiven: false,
|
|
62
|
-
carryChoices: true,
|
|
63
48
|
allowNewChoices: false,
|
|
64
|
-
|
|
49
|
+
appendOnly: false,
|
|
50
|
+
carryChoices: true,
|
|
65
51
|
defaultValue: "",
|
|
52
|
+
foreignKey: false,
|
|
53
|
+
formula: "",
|
|
54
|
+
maxLength: 0,
|
|
55
|
+
numLines: 1,
|
|
56
|
+
primaryKey: false,
|
|
57
|
+
sortAsGiven: false,
|
|
66
58
|
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
{ permissionType: "Modify", role: "Administrator", roleId: 12 },
|
|
70
|
-
],
|
|
59
|
+
required: false,
|
|
60
|
+
unique: false,
|
|
71
61
|
};
|
|
72
62
|
|
|
73
63
|
mockFetch.mockResolvedValueOnce({
|
|
@@ -82,25 +72,22 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
82
72
|
});
|
|
83
73
|
|
|
84
74
|
expect(result).toEqual(mockResponse);
|
|
85
|
-
const callArgs = mockFetch.mock.calls[0];
|
|
86
|
-
console.log("Raw fetch call (user token):", callArgs); // Debug raw call
|
|
87
|
-
const receivedBody = JSON.parse(callArgs[1].body as string);
|
|
88
|
-
expect(receivedBody).toEqual(request); // Expect full body
|
|
89
75
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
90
76
|
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
91
|
-
|
|
77
|
+
{
|
|
92
78
|
method: "POST",
|
|
93
|
-
|
|
79
|
+
body: JSON.stringify(request),
|
|
80
|
+
headers: {
|
|
94
81
|
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
95
82
|
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
96
83
|
"Content-Type": "application/json",
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
}
|
|
84
|
+
},
|
|
85
|
+
credentials: "omit",
|
|
86
|
+
}
|
|
100
87
|
);
|
|
101
88
|
});
|
|
102
89
|
|
|
103
|
-
it("creates
|
|
90
|
+
it("creates field successfully with temp token", async () => {
|
|
104
91
|
client = createClient(mockFetch, { useTempTokens: true, debug: true });
|
|
105
92
|
|
|
106
93
|
const request: CreateFieldRequest = {
|
|
@@ -109,35 +96,35 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
109
96
|
addToForms: false,
|
|
110
97
|
};
|
|
111
98
|
|
|
112
|
-
const mockResponse
|
|
113
|
-
id:
|
|
99
|
+
const mockResponse = {
|
|
100
|
+
id: 102,
|
|
114
101
|
label: "TempField",
|
|
115
102
|
fieldType: "numeric",
|
|
116
|
-
|
|
117
|
-
|
|
103
|
+
appearsByDefault: false,
|
|
104
|
+
audited: false,
|
|
118
105
|
bold: false,
|
|
119
|
-
required: false,
|
|
120
|
-
appearsByDefault: true,
|
|
121
|
-
findEnabled: true,
|
|
122
|
-
unique: false,
|
|
123
106
|
doesDataCopy: false,
|
|
124
107
|
fieldHelp: "",
|
|
125
|
-
|
|
108
|
+
findEnabled: true,
|
|
109
|
+
mode: "",
|
|
110
|
+
noWrap: false,
|
|
111
|
+
permissions: [],
|
|
126
112
|
properties: {
|
|
127
|
-
primaryKey: false,
|
|
128
|
-
foreignKey: false,
|
|
129
|
-
numLines: 1,
|
|
130
|
-
maxLength: 0,
|
|
131
|
-
appendOnly: false,
|
|
132
113
|
allowHTML: false,
|
|
133
114
|
allowMentions: false,
|
|
134
|
-
sortAsGiven: false,
|
|
135
|
-
carryChoices: true,
|
|
136
115
|
allowNewChoices: false,
|
|
137
|
-
|
|
116
|
+
appendOnly: false,
|
|
117
|
+
carryChoices: true,
|
|
138
118
|
defaultValue: "",
|
|
119
|
+
foreignKey: false,
|
|
120
|
+
formula: "",
|
|
121
|
+
maxLength: 0,
|
|
122
|
+
numLines: 1,
|
|
123
|
+
primaryKey: false,
|
|
124
|
+
sortAsGiven: false,
|
|
139
125
|
},
|
|
140
|
-
|
|
126
|
+
required: false,
|
|
127
|
+
unique: false,
|
|
141
128
|
};
|
|
142
129
|
|
|
143
130
|
mockFetch
|
|
@@ -162,29 +149,21 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
162
149
|
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
163
150
|
1,
|
|
164
151
|
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
165
|
-
expect.
|
|
166
|
-
method: "GET",
|
|
167
|
-
headers: expect.objectContaining({
|
|
168
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
169
|
-
"Content-Type": "application/json",
|
|
170
|
-
}),
|
|
171
|
-
credentials: "include",
|
|
172
|
-
})
|
|
152
|
+
expect.any(Object)
|
|
173
153
|
);
|
|
174
|
-
const callArgs = mockFetch.mock.calls[1];
|
|
175
|
-
console.log("Raw fetch call (temp token):", callArgs); // Debug raw call
|
|
176
154
|
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
177
155
|
2,
|
|
178
156
|
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
179
|
-
|
|
157
|
+
{
|
|
180
158
|
method: "POST",
|
|
181
|
-
|
|
182
|
-
|
|
159
|
+
body: JSON.stringify(request),
|
|
160
|
+
headers: {
|
|
183
161
|
Authorization: "QB-TEMP-TOKEN temp_token",
|
|
184
162
|
"Content-Type": "application/json",
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
163
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
164
|
+
},
|
|
165
|
+
credentials: "omit",
|
|
166
|
+
}
|
|
188
167
|
);
|
|
189
168
|
});
|
|
190
169
|
|
|
@@ -196,35 +175,35 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
196
175
|
fieldType: "checkbox",
|
|
197
176
|
};
|
|
198
177
|
|
|
199
|
-
const mockResponse
|
|
178
|
+
const mockResponse = {
|
|
200
179
|
id: 102,
|
|
201
180
|
label: "RetryField",
|
|
202
181
|
fieldType: "checkbox",
|
|
203
|
-
mode: "",
|
|
204
|
-
noWrap: false,
|
|
205
|
-
bold: false,
|
|
206
|
-
required: false,
|
|
207
182
|
appearsByDefault: true,
|
|
208
|
-
|
|
209
|
-
|
|
183
|
+
audited: false,
|
|
184
|
+
bold: false,
|
|
210
185
|
doesDataCopy: false,
|
|
211
186
|
fieldHelp: "",
|
|
212
|
-
|
|
187
|
+
findEnabled: true,
|
|
188
|
+
mode: "",
|
|
189
|
+
noWrap: false,
|
|
190
|
+
permissions: [],
|
|
213
191
|
properties: {
|
|
214
|
-
primaryKey: false,
|
|
215
|
-
foreignKey: false,
|
|
216
|
-
numLines: 1,
|
|
217
|
-
maxLength: 0,
|
|
218
|
-
appendOnly: false,
|
|
219
192
|
allowHTML: false,
|
|
220
193
|
allowMentions: false,
|
|
221
|
-
sortAsGiven: false,
|
|
222
|
-
carryChoices: true,
|
|
223
194
|
allowNewChoices: false,
|
|
224
|
-
|
|
195
|
+
appendOnly: false,
|
|
196
|
+
carryChoices: true,
|
|
225
197
|
defaultValue: "",
|
|
198
|
+
foreignKey: false,
|
|
199
|
+
formula: "",
|
|
200
|
+
maxLength: 0,
|
|
201
|
+
numLines: 1,
|
|
202
|
+
primaryKey: false,
|
|
203
|
+
sortAsGiven: false,
|
|
226
204
|
},
|
|
227
|
-
|
|
205
|
+
required: false,
|
|
206
|
+
unique: false,
|
|
228
207
|
};
|
|
229
208
|
|
|
230
209
|
mockFetch
|
|
@@ -238,6 +217,7 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
238
217
|
ok: false,
|
|
239
218
|
status: 401,
|
|
240
219
|
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
220
|
+
text: () => Promise.resolve("Unauthorized"),
|
|
241
221
|
})
|
|
242
222
|
.mockResolvedValueOnce({
|
|
243
223
|
ok: true,
|
|
@@ -266,11 +246,16 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
266
246
|
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
267
247
|
2,
|
|
268
248
|
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
269
|
-
|
|
270
|
-
|
|
249
|
+
{
|
|
250
|
+
method: "POST",
|
|
251
|
+
body: JSON.stringify(request),
|
|
252
|
+
headers: {
|
|
271
253
|
Authorization: "QB-TEMP-TOKEN initial_token",
|
|
272
|
-
|
|
273
|
-
|
|
254
|
+
"Content-Type": "application/json",
|
|
255
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
256
|
+
},
|
|
257
|
+
credentials: "omit",
|
|
258
|
+
}
|
|
274
259
|
);
|
|
275
260
|
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
276
261
|
3,
|
|
@@ -280,18 +265,22 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
280
265
|
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
281
266
|
4,
|
|
282
267
|
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
283
|
-
|
|
284
|
-
|
|
268
|
+
{
|
|
269
|
+
method: "POST",
|
|
270
|
+
body: JSON.stringify(request),
|
|
271
|
+
headers: {
|
|
285
272
|
Authorization: "QB-TEMP-TOKEN new_token",
|
|
286
|
-
|
|
287
|
-
|
|
273
|
+
"Content-Type": "application/json",
|
|
274
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
275
|
+
},
|
|
276
|
+
credentials: "omit",
|
|
277
|
+
}
|
|
288
278
|
);
|
|
289
279
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
290
|
-
"Authorization error for createField, refreshing token:"
|
|
291
|
-
expect.any(String)
|
|
280
|
+
"Authorization error for createField (temp token), refreshing token:"
|
|
292
281
|
);
|
|
293
282
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
294
|
-
"Retrying
|
|
283
|
+
"[createField] Retrying with token: new_token..."
|
|
295
284
|
);
|
|
296
285
|
consoleSpy.mockRestore();
|
|
297
286
|
});
|
|
@@ -300,7 +289,7 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
300
289
|
client = createClient(mockFetch, { debug: true });
|
|
301
290
|
|
|
302
291
|
const request: CreateFieldRequest = {
|
|
303
|
-
label: "", // Invalid: label
|
|
292
|
+
label: "", // Invalid: empty label
|
|
304
293
|
fieldType: "text",
|
|
305
294
|
};
|
|
306
295
|
|
|
@@ -308,6 +297,7 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
308
297
|
ok: false,
|
|
309
298
|
status: 400,
|
|
310
299
|
json: () => Promise.resolve({ message: "Label is required" }),
|
|
300
|
+
text: () => Promise.resolve("Label is required"),
|
|
311
301
|
});
|
|
312
302
|
|
|
313
303
|
await expect(
|
|
@@ -316,15 +306,16 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
316
306
|
|
|
317
307
|
expect(mockFetch).toHaveBeenCalledWith(
|
|
318
308
|
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
319
|
-
|
|
309
|
+
{
|
|
320
310
|
method: "POST",
|
|
321
|
-
|
|
311
|
+
body: JSON.stringify(request),
|
|
312
|
+
headers: {
|
|
322
313
|
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
323
314
|
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
324
315
|
"Content-Type": "application/json",
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
}
|
|
316
|
+
},
|
|
317
|
+
credentials: "omit",
|
|
318
|
+
}
|
|
328
319
|
);
|
|
329
320
|
});
|
|
330
321
|
|
|
@@ -347,22 +338,49 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
347
338
|
ok: false,
|
|
348
339
|
status: 401,
|
|
349
340
|
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
341
|
+
text: () => Promise.resolve("Unauthorized"),
|
|
342
|
+
})
|
|
343
|
+
.mockResolvedValueOnce({
|
|
344
|
+
ok: true,
|
|
345
|
+
status: 200,
|
|
346
|
+
json: () => Promise.resolve({ temporaryAuthorization: "token_2" }),
|
|
350
347
|
})
|
|
351
348
|
.mockResolvedValueOnce({
|
|
352
349
|
ok: false,
|
|
353
350
|
status: 401,
|
|
354
|
-
json: () =>
|
|
355
|
-
|
|
351
|
+
json: () => Promise.resolve({ message: "Unauthorized again" }),
|
|
352
|
+
text: () => Promise.resolve("Unauthorized again"),
|
|
353
|
+
})
|
|
354
|
+
.mockResolvedValueOnce({
|
|
355
|
+
ok: true,
|
|
356
|
+
status: 200,
|
|
357
|
+
json: () => Promise.resolve({ temporaryAuthorization: "token_3" }),
|
|
358
|
+
})
|
|
359
|
+
.mockResolvedValueOnce({
|
|
360
|
+
ok: false,
|
|
361
|
+
status: 401,
|
|
362
|
+
json: () => Promise.resolve({ message: "Unauthorized again" }),
|
|
363
|
+
text: () => Promise.resolve("Unauthorized again"),
|
|
364
|
+
})
|
|
365
|
+
.mockResolvedValueOnce({
|
|
366
|
+
ok: true,
|
|
367
|
+
status: 200,
|
|
368
|
+
json: () => Promise.resolve({ temporaryAuthorization: "token_4" }),
|
|
369
|
+
})
|
|
370
|
+
.mockResolvedValueOnce({
|
|
371
|
+
ok: false,
|
|
372
|
+
status: 401,
|
|
373
|
+
json: () => Promise.resolve({ message: "Unauthorized again" }),
|
|
374
|
+
text: () => Promise.resolve("Unauthorized again"),
|
|
356
375
|
});
|
|
357
376
|
|
|
358
377
|
const consoleSpy = vi.spyOn(console, "log");
|
|
378
|
+
|
|
359
379
|
await expect(
|
|
360
380
|
client.createField({ tableId: QB_TABLE_ID_1, body: request })
|
|
361
|
-
).rejects.toThrow(
|
|
362
|
-
"API Error: Unauthorized in fetchTempToken (Status: 401)"
|
|
363
|
-
);
|
|
381
|
+
).rejects.toThrow("API Error: Unauthorized again (Status: 401)");
|
|
364
382
|
|
|
365
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
383
|
+
expect(mockFetch).toHaveBeenCalledTimes(8);
|
|
366
384
|
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
367
385
|
1,
|
|
368
386
|
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
@@ -372,6 +390,7 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
372
390
|
2,
|
|
373
391
|
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
374
392
|
expect.objectContaining({
|
|
393
|
+
method: "POST",
|
|
375
394
|
headers: expect.objectContaining({
|
|
376
395
|
Authorization: "QB-TEMP-TOKEN initial_token",
|
|
377
396
|
}),
|
|
@@ -382,10 +401,51 @@ describe("QuickbaseClient Unit - createField", () => {
|
|
|
382
401
|
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
383
402
|
expect.any(Object)
|
|
384
403
|
);
|
|
404
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
405
|
+
4,
|
|
406
|
+
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
407
|
+
expect.objectContaining({
|
|
408
|
+
method: "POST",
|
|
409
|
+
headers: expect.objectContaining({
|
|
410
|
+
Authorization: "QB-TEMP-TOKEN token_2",
|
|
411
|
+
}),
|
|
412
|
+
})
|
|
413
|
+
);
|
|
414
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
415
|
+
5,
|
|
416
|
+
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
417
|
+
expect.any(Object)
|
|
418
|
+
);
|
|
419
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
420
|
+
6,
|
|
421
|
+
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
422
|
+
expect.objectContaining({
|
|
423
|
+
method: "POST",
|
|
424
|
+
headers: expect.objectContaining({
|
|
425
|
+
Authorization: "QB-TEMP-TOKEN token_3",
|
|
426
|
+
}),
|
|
427
|
+
})
|
|
428
|
+
);
|
|
429
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
430
|
+
7,
|
|
431
|
+
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
432
|
+
expect.any(Object)
|
|
433
|
+
);
|
|
434
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
435
|
+
8,
|
|
436
|
+
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`,
|
|
437
|
+
expect.objectContaining({
|
|
438
|
+
method: "POST",
|
|
439
|
+
headers: expect.objectContaining({
|
|
440
|
+
Authorization: "QB-TEMP-TOKEN token_4",
|
|
441
|
+
}),
|
|
442
|
+
})
|
|
443
|
+
);
|
|
444
|
+
|
|
385
445
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
386
|
-
"Authorization error for createField, refreshing token:"
|
|
387
|
-
expect.any(String)
|
|
446
|
+
"Authorization error for createField (temp token), refreshing token:"
|
|
388
447
|
);
|
|
448
|
+
|
|
389
449
|
consoleSpy.mockRestore();
|
|
390
450
|
});
|
|
391
451
|
});
|
|
@@ -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,37 @@ 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 persistent 401 errors to exhaust retries
|
|
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
|
+
)
|
|
132
|
+
.mockResolvedValueOnce(
|
|
133
|
+
new Response(JSON.stringify({ message: "Unauthorized" }), {
|
|
134
|
+
status: 401,
|
|
135
|
+
statusText: "Unauthorized",
|
|
136
|
+
headers: { "Content-Type": "application/json" },
|
|
137
|
+
})
|
|
138
|
+
)
|
|
139
|
+
.mockResolvedValueOnce(
|
|
140
|
+
new Response(JSON.stringify({ message: "Unauthorized" }), {
|
|
141
|
+
status: 401,
|
|
142
|
+
statusText: "Unauthorized",
|
|
143
|
+
headers: { "Content-Type": "application/json" },
|
|
144
|
+
})
|
|
145
|
+
);
|
|
126
146
|
|
|
127
147
|
await expect(
|
|
128
148
|
client.getFieldUsage({
|
|
@@ -131,8 +151,21 @@ describe("QuickbaseClient Unit - getFieldUsage", () => {
|
|
|
131
151
|
})
|
|
132
152
|
).rejects.toThrow("API Error: Unauthorized (Status: 401)");
|
|
133
153
|
|
|
134
|
-
expect(mockFetch).toHaveBeenCalledTimes(
|
|
135
|
-
expect(mockFetch).
|
|
154
|
+
expect(mockFetch).toHaveBeenCalledTimes(4); // Initial + 3 retries
|
|
155
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
156
|
+
1,
|
|
157
|
+
`https://api.quickbase.com/v1/fields/usage/6?tableId=${QB_TABLE_ID_1}`,
|
|
158
|
+
expect.objectContaining({
|
|
159
|
+
method: "GET",
|
|
160
|
+
headers: expect.objectContaining({
|
|
161
|
+
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
162
|
+
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
163
|
+
"Content-Type": "application/json",
|
|
164
|
+
}),
|
|
165
|
+
})
|
|
166
|
+
);
|
|
167
|
+
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
168
|
+
4,
|
|
136
169
|
`https://api.quickbase.com/v1/fields/usage/6?tableId=${QB_TABLE_ID_1}`,
|
|
137
170
|
expect.objectContaining({
|
|
138
171
|
method: "GET",
|