quickbase-js 1.1.0-beta.3 → 1.1.0-beta.5
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 +31 -0
- package/README.md +115 -5
- package/build.js +175 -55
- package/dist/{quickbase.d.ts → esm/quickbase.d.ts} +1108 -349
- package/dist/{quickbase.js → esm/quickbase.js} +828 -263
- package/dist/esm/quickbase.js.map +1 -0
- package/dist/esm/quickbase.min.js +2 -0
- package/dist/esm/quickbase.min.js.map +1 -0
- package/dist/umd/quickbase.umd.d.ts +5341 -0
- package/dist/umd/quickbase.umd.js +7696 -0
- package/dist/umd/quickbase.umd.js.map +1 -0
- package/dist/umd/quickbase.umd.min.js +2 -0
- package/dist/umd/quickbase.umd.min.js.map +1 -0
- package/examples/react.js +12 -12
- package/examples/vanillajs.html +12 -12
- package/log-gen.yaml +536 -1915
- package/open-api/definitions/apps.ts +274 -0
- package/open-api/definitions/auth.ts +10 -0
- package/open-api/definitions/fields.ts +751 -0
- package/open-api/definitions/index.ts +18 -0
- package/open-api/definitions/records.ts +269 -0
- package/open-api/definitions/relationships.ts +90 -0
- package/open-api/definitions/reports.ts +10 -0
- package/open-api/definitions/tables.ts +112 -0
- package/{src/code-generation → open-api}/fix-spec-main.ts +3 -3
- package/{src/code-generation → open-api}/generate-unified-interface.ts +42 -17
- package/{src/code-generation → open-api}/output/quickbase-fixed.json +3244 -4586
- package/open-api/paths/apps.ts +100 -0
- package/open-api/paths/auth.ts +25 -0
- package/open-api/paths/fields.ts +207 -0
- package/open-api/paths/index.ts +17 -0
- package/open-api/paths/records.ts +68 -0
- package/open-api/paths/relationships.ts +32 -0
- package/open-api/paths/reports.ts +34 -0
- package/open-api/paths/tables.ts +162 -0
- package/{src/code-generation → open-api}/regenerate-client.ts +4 -4
- package/package.json +34 -25
- package/playwright-results.txt/.last-run.json +4 -0
- package/playwright.config.ts +1 -1
- package/rollup.config.mjs +114 -0
- package/src/generated-unified/QuickbaseClient.ts +7 -6
- package/test-artifacts/.last-run.json +4 -2
- package/test-esm.js +1 -0
- package/test-umd.js +1 -0
- package/test.ts +1 -0
- package/tests/playwright/qb/auth/umd.test.ts +10 -0
- package/tests/vitest/qb/fields/createField.test.ts +78 -0
- package/tests/vitest/qb/fields/deleteFields.test.ts +89 -0
- package/tests/vitest/qb/fields/getField.test.ts +85 -0
- package/tests/vitest/qb/fields/getFieldUsage.test.ts +174 -0
- package/tests/vitest/qb/fields/getFieldsUsage.test.ts +77 -0
- package/tests/vitest/qb/index.ts +25 -1
- package/tests/vitest/unit/fields/createField.test.ts +391 -0
- package/tests/vitest/unit/fields/deleteFields.test.ts +141 -0
- package/tests/vitest/unit/fields/getField.test.ts +189 -0
- package/tests/vitest/unit/fields/getFieldUsage.test.ts +147 -0
- package/tests/vitest/unit/fields/getFieldsUsage.test.ts +160 -0
- package/tests/vitest/unit/index.ts +30 -1
- package/tests/vitest/unit/umd.test.ts +8 -0
- package/{src → tools}/log-gen.ts +14 -10
- package/tools/openapi-jars/openapi-generator-cli-7.12.0.jar +0 -0
- package/tsconfig.build.json +17 -0
- package/tsconfig.json +5 -5
- package/dist/quickbase.js.map +0 -1
- package/rollup.config.js +0 -70
- package/src/code-generation/fix-spec-definitions.ts +0 -809
- package/src/code-generation/fix-spec-paths.ts +0 -439
- package/test-artifacts/test-results.json +0 -829
- /package/{src/specs → specs}/QuickBase_RESTful_API_2025-03-04T06_22_39.725Z.json +0 -0
package/log-gen.yaml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
project: quickbase-js
|
|
2
|
-
date: 3/
|
|
2
|
+
date: 3/12/2025
|
|
3
3
|
root: /home/drew/Projects/quickbase-js
|
|
4
4
|
goals:
|
|
5
5
|
- A library for interacting with the QuickBase RESTful API.
|
|
@@ -40,8 +40,8 @@ pipelineOverview:
|
|
|
40
40
|
- getTempTokenDBID() method is wrapped to enhance temporary token generation reuse interacting with a token cache.
|
|
41
41
|
- '#'
|
|
42
42
|
- 'npm run fix-spec: Generates quickbase-fixed.json from the latest QuickBase_RESTful*.json file.'
|
|
43
|
-
-
|
|
44
|
-
- 'npm run generate
|
|
43
|
+
- npm run regenerate:openapi Generates raw TypeScript-fetch files in src/generated/.
|
|
44
|
+
- 'npm run generate:unified: Generates a unified QuickbaseClient.ts interface in src/generated-unified/.'
|
|
45
45
|
tree:
|
|
46
46
|
name: .
|
|
47
47
|
type: directory
|
|
@@ -58,15 +58,148 @@ tree:
|
|
|
58
58
|
type: file
|
|
59
59
|
- name: build.js
|
|
60
60
|
type: file
|
|
61
|
+
contents: |-
|
|
62
|
+
import { execSync } from 'child_process';
|
|
63
|
+
import { rollup } from 'rollup';
|
|
64
|
+
import terser from '@rollup/plugin-terser';
|
|
65
|
+
import nodeResolve from '@rollup/plugin-node-resolve';
|
|
66
|
+
import commonjs from '@rollup/plugin-commonjs';
|
|
67
|
+
import dts from 'rollup-plugin-dts';
|
|
68
|
+
import { rimrafSync } from 'rimraf';
|
|
69
|
+
import { copyFileSync, readdirSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
70
|
+
import { join } from 'path';
|
|
71
|
+
|
|
72
|
+
// Clean the dist folder
|
|
73
|
+
rimrafSync('dist');
|
|
74
|
+
rimrafSync('dist/temp-src');
|
|
75
|
+
|
|
76
|
+
// Step 1: Copy runtime-relevant files from src/ to dist/temp-src and remove .ts extensions
|
|
77
|
+
const srcDir = join(process.cwd(), 'src');
|
|
78
|
+
const tempSrcDir = join(process.cwd(), 'dist/temp-src');
|
|
79
|
+
const runtimeDirs = ['generated', 'generated-unified'];
|
|
80
|
+
const runtimeFiles = ['index.ts', 'quickbaseClient.ts', 'tokenCache.ts'];
|
|
81
|
+
|
|
82
|
+
function copyAndRewriteTsExtensions(src, dest) {
|
|
83
|
+
console.log(`Copying from ${src} to ${dest}`);
|
|
84
|
+
mkdirSync(dest, { recursive: true });
|
|
85
|
+
const files = readdirSync(src, { withFileTypes: true });
|
|
86
|
+
for (const file of files) {
|
|
87
|
+
const srcPath = join(src, file.name);
|
|
88
|
+
const destPath = join(dest, file.name);
|
|
89
|
+
if (file.isDirectory()) {
|
|
90
|
+
if (runtimeDirs.includes(file.name) || runtimeDirs.some(dir => src.includes(dir))) {
|
|
91
|
+
copyAndRewriteTsExtensions(srcPath, destPath);
|
|
92
|
+
} else {
|
|
93
|
+
console.log(`Skipping non-runtime directory ${srcPath}`);
|
|
94
|
+
}
|
|
95
|
+
} else if (file.name.endsWith('.ts')) {
|
|
96
|
+
if (runtimeFiles.includes(file.name) || src.includes('generated') || src.includes('generated-unified')) {
|
|
97
|
+
console.log(`Processing ${srcPath} -> ${destPath}`);
|
|
98
|
+
let content = readFileSync(srcPath, 'utf8');
|
|
99
|
+
content = content.replace(/(from\s+["']\.[\w\/-]+)\.ts(["'])/g, '$1$2');
|
|
100
|
+
content = content.replace(/(export\s+\*\s+from\s+["']\.[\w\/-]+)\.ts(["'])/g, '$1$2');
|
|
101
|
+
writeFileSync(destPath, content, 'utf8');
|
|
102
|
+
} else {
|
|
103
|
+
console.log(`Skipping non-runtime file ${srcPath}`);
|
|
104
|
+
}
|
|
105
|
+
} else {
|
|
106
|
+
console.log(`Skipping non-.ts file ${srcPath}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
copyAndRewriteTsExtensions(srcDir, tempSrcDir);
|
|
111
|
+
|
|
112
|
+
// Step 2: Compile TypeScript to JavaScript with tsc
|
|
113
|
+
console.log('Running tsc...');
|
|
114
|
+
const tsFiles = readdirSync(tempSrcDir, { recursive: true })
|
|
115
|
+
.filter(file => file.endsWith('.ts'))
|
|
116
|
+
.map(file => join(tempSrcDir, file));
|
|
117
|
+
const tscCommand = `npx tsc ${tsFiles.join(' ')} --outDir ${join(process.cwd(), 'dist/temp')} --module ESNext --target ESNext --sourceMap --moduleResolution bundler --baseUrl ${process.cwd()}`;
|
|
118
|
+
console.log(`Command: ${tscCommand}`);
|
|
119
|
+
execSync(tscCommand, { stdio: 'inherit' });
|
|
120
|
+
|
|
121
|
+
// Step 3: Rollup configuration
|
|
122
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
123
|
+
const external = [
|
|
124
|
+
'node:http', 'node:https', 'node:zlib', 'node:stream', 'node:buffer',
|
|
125
|
+
'node:util', 'node:url', 'node:net', 'node:fs', 'node:path', 'node-fetch',
|
|
126
|
+
];
|
|
127
|
+
const globals = { 'node-fetch': 'fetch' };
|
|
128
|
+
|
|
129
|
+
// ESM Build
|
|
130
|
+
console.log(`Building ESM bundle (${isProd ? 'minified' : 'unminified'})...`);
|
|
131
|
+
const esmBundle = await rollup({
|
|
132
|
+
input: 'dist/temp/quickbaseClient.js',
|
|
133
|
+
external,
|
|
134
|
+
plugins: [
|
|
135
|
+
nodeResolve({ preferBuiltins: true }),
|
|
136
|
+
commonjs(),
|
|
137
|
+
isProd && terser(), // Only minify in prod
|
|
138
|
+
],
|
|
139
|
+
});
|
|
140
|
+
await esmBundle.write({
|
|
141
|
+
dir: 'dist/esm',
|
|
142
|
+
format: 'esm',
|
|
143
|
+
sourcemap: true,
|
|
144
|
+
entryFileNames: isProd ? 'quickbase.min.js' : 'quickbase.js',
|
|
145
|
+
});
|
|
146
|
+
await esmBundle.close();
|
|
147
|
+
|
|
148
|
+
// UMD Build
|
|
149
|
+
console.log(`Building UMD bundle (${isProd ? 'minified' : 'unminified'})...`);
|
|
150
|
+
const umdBundle = await rollup({
|
|
151
|
+
input: 'dist/temp/quickbaseClient.js',
|
|
152
|
+
external,
|
|
153
|
+
plugins: [
|
|
154
|
+
nodeResolve({ preferBuiltins: true, browser: true }),
|
|
155
|
+
commonjs(),
|
|
156
|
+
isProd && terser(), // Only minify in prod
|
|
157
|
+
],
|
|
158
|
+
});
|
|
159
|
+
await umdBundle.write({
|
|
160
|
+
file: join(process.cwd(), 'dist/umd', isProd ? 'quickbase.umd.min.js' : 'quickbase.umd.js'),
|
|
161
|
+
format: 'umd',
|
|
162
|
+
name: 'QuickbaseJS',
|
|
163
|
+
sourcemap: true,
|
|
164
|
+
globals,
|
|
165
|
+
});
|
|
166
|
+
await umdBundle.close();
|
|
167
|
+
|
|
168
|
+
// Declarations
|
|
169
|
+
console.log('Generating declarations...');
|
|
170
|
+
const dtsBundle = await rollup({
|
|
171
|
+
input: 'src/quickbaseClient.ts',
|
|
172
|
+
plugins: [dts({ tsconfig: './tsconfig.json', respectExternal: true })],
|
|
173
|
+
});
|
|
174
|
+
await dtsBundle.write({ file: 'dist/esm/quickbase.d.ts', format: 'esm' });
|
|
175
|
+
await dtsBundle.write({ file: 'dist/umd/quickbase.umd.d.ts', format: 'umd' });
|
|
176
|
+
await dtsBundle.close();
|
|
177
|
+
|
|
178
|
+
// Clean up
|
|
179
|
+
rimrafSync('dist/temp');
|
|
180
|
+
rimrafSync('dist/temp-src');
|
|
181
|
+
console.log('Build completed successfully');
|
|
61
182
|
- name: dist
|
|
62
183
|
type: directory
|
|
63
184
|
children:
|
|
64
|
-
- name:
|
|
65
|
-
type:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
185
|
+
- name: esm
|
|
186
|
+
type: directory
|
|
187
|
+
children:
|
|
188
|
+
- name: quickbase.d.ts
|
|
189
|
+
type: file
|
|
190
|
+
- name: quickbase.min.js
|
|
191
|
+
type: file
|
|
192
|
+
- name: quickbase.min.js.map
|
|
193
|
+
type: file
|
|
194
|
+
- name: umd
|
|
195
|
+
type: directory
|
|
196
|
+
children:
|
|
197
|
+
- name: quickbase.umd.d.ts
|
|
198
|
+
type: file
|
|
199
|
+
- name: quickbase.umd.min.js
|
|
200
|
+
type: file
|
|
201
|
+
- name: quickbase.umd.min.js.map
|
|
202
|
+
type: file
|
|
70
203
|
- name: examples
|
|
71
204
|
type: directory
|
|
72
205
|
children:
|
|
@@ -76,37 +209,167 @@ tree:
|
|
|
76
209
|
type: file
|
|
77
210
|
- name: log-gen.yaml
|
|
78
211
|
type: file
|
|
212
|
+
- name: open-api
|
|
213
|
+
type: directory
|
|
214
|
+
children:
|
|
215
|
+
- name: definitions
|
|
216
|
+
type: directory
|
|
217
|
+
children:
|
|
218
|
+
- name: apps.ts
|
|
219
|
+
type: file
|
|
220
|
+
- name: auth.ts
|
|
221
|
+
type: file
|
|
222
|
+
- name: fields.ts
|
|
223
|
+
type: file
|
|
224
|
+
- name: index.ts
|
|
225
|
+
type: file
|
|
226
|
+
- name: records.ts
|
|
227
|
+
type: file
|
|
228
|
+
- name: relationships.ts
|
|
229
|
+
type: file
|
|
230
|
+
- name: reports.ts
|
|
231
|
+
type: file
|
|
232
|
+
- name: tables.ts
|
|
233
|
+
type: file
|
|
234
|
+
- name: fix-spec-main.ts
|
|
235
|
+
type: file
|
|
236
|
+
- name: generate-unified-interface.ts
|
|
237
|
+
type: file
|
|
238
|
+
- name: output
|
|
239
|
+
type: directory
|
|
240
|
+
children:
|
|
241
|
+
- name: quickbase-fixed.json
|
|
242
|
+
type: file
|
|
243
|
+
- name: paths
|
|
244
|
+
type: directory
|
|
245
|
+
children:
|
|
246
|
+
- name: apps.ts
|
|
247
|
+
type: file
|
|
248
|
+
- name: auth.ts
|
|
249
|
+
type: file
|
|
250
|
+
- name: fields.ts
|
|
251
|
+
type: file
|
|
252
|
+
- name: index.ts
|
|
253
|
+
type: file
|
|
254
|
+
- name: records.ts
|
|
255
|
+
type: file
|
|
256
|
+
- name: relationships.ts
|
|
257
|
+
type: file
|
|
258
|
+
- name: reports.ts
|
|
259
|
+
type: file
|
|
260
|
+
- name: tables.ts
|
|
261
|
+
type: file
|
|
262
|
+
- name: regenerate-client.ts
|
|
263
|
+
type: file
|
|
79
264
|
- name: openapitools.json
|
|
80
265
|
type: file
|
|
81
266
|
- name: package-lock.json
|
|
82
267
|
type: file
|
|
83
268
|
- name: package.json
|
|
84
269
|
type: file
|
|
270
|
+
contents: |
|
|
271
|
+
{
|
|
272
|
+
"name": "quickbase-js",
|
|
273
|
+
"version": "1.1.0-beta.4",
|
|
274
|
+
"description": "QuickBase JavaScript client for frameworks and vanilla JS",
|
|
275
|
+
"type": "module",
|
|
276
|
+
"main": "dist/esm/quickbase.js",
|
|
277
|
+
"module": "dist/esm/quickbase.js",
|
|
278
|
+
"types": "dist/esm/quickbase.d.ts",
|
|
279
|
+
"publishConfig": {
|
|
280
|
+
"access": "public"
|
|
281
|
+
},
|
|
282
|
+
"exports": {
|
|
283
|
+
".": {
|
|
284
|
+
"import": {
|
|
285
|
+
"minified": "./dist/esm/quickbase.min.js",
|
|
286
|
+
"default": "./dist/esm/quickbase.js"
|
|
287
|
+
},
|
|
288
|
+
"require": {
|
|
289
|
+
"minified": "./dist/umd/quickbase.umd.min.js",
|
|
290
|
+
"default": "./dist/umd/quickbase.umd.js"
|
|
291
|
+
},
|
|
292
|
+
"types": "./dist/esm/quickbase.d.ts",
|
|
293
|
+
"umd:types": "./dist/umd/quickbase.umd.d.ts"
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
"scripts": {
|
|
297
|
+
"release": "standard-version",
|
|
298
|
+
"release:beta": "standard-version --prerelease beta",
|
|
299
|
+
"release:alpha": "standard-version --prerelease alpha",
|
|
300
|
+
"release:push": "npm run release:beta && git push --follow-tags origin main",
|
|
301
|
+
"release:publish": "npm run release:push && npm publish --tag beta",
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
"fix-spec": "node --loader ts-node/esm open-api/fix-spec-main.ts",
|
|
305
|
+
"regenerate:openapi": "rimraf src/generated && node --loader ts-node/esm open-api/regenerate-client.ts",
|
|
306
|
+
"regenerate:unified": "node --loader ts-node/esm open-api/generate-unified-interface.ts",
|
|
307
|
+
"regenerate:all": "npm run fix-spec && npm run regenerate:openapi && npm run regenerate:unified",
|
|
308
|
+
"log-gen": "node --loader ts-node/esm tools/log-gen.ts",
|
|
309
|
+
"build": "rimraf dist && node --loader ts-node/esm build.js",
|
|
310
|
+
"build:prod": "rimraf dist && NODE_ENV=production node --loader ts-node/esm build.js",
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
"build-old": "rimraf dist && rollup -c",
|
|
314
|
+
"build:prod-old": "rimraf dist && NODE_ENV=production rollup -c",
|
|
315
|
+
"test:all": "vitest run tests/vitest/**/**/*.test.ts --reporter=basic && playwright test tests/playwright/qb --config playwright.config.ts --reporter=dot --quiet",
|
|
316
|
+
"test:pw:qb:all": "playwright test tests/playwright/qb --config playwright.config.ts",
|
|
317
|
+
"test:vitest:all": "vitest run tests/vitest/**/**/*.test.ts",
|
|
318
|
+
"test:vitest:qb": "vitest run tests/vitest/qb/**/*.test.ts",
|
|
319
|
+
"test:vitest:unit": "vitest run tests/vitest/unit/**/*.test.ts",
|
|
320
|
+
"test:vitest:qb:apps": "vitest run tests/vitest/qb/apps/**/*.test.ts",
|
|
321
|
+
"test:vitest:qb:auth": "vitest run tests/vitest/qb/auth/**/*.test.ts",
|
|
322
|
+
"test:vitest:qb:fields": "vitest run tests/vitest/qb/fields/**/*.test.ts",
|
|
323
|
+
"test:vitest:qb:records": "vitest run tests/vitest/qb/records/**/*.test.ts",
|
|
324
|
+
"test:vitest:qb:tables": "vitest run tests/vitest/qb/tables/**/*.test.ts",
|
|
325
|
+
"test:vitest:unit:apps": "vitest run tests/vitest/unit/apps/**/*.test.ts",
|
|
326
|
+
"test:vitest:unit:auth": "vitest run tests/vitest/unit/auth/**/*.test.ts",
|
|
327
|
+
"test:vitest:unit:fields": "vitest run tests/vitest/unit/fields/**/*.test.ts",
|
|
328
|
+
"test:vitest:unit:records": "vitest run tests/vitest/unit/records/**/*.test.ts",
|
|
329
|
+
"test:vitest:unit:tables": "vitest run tests/vitest/unit/tables/**/*.test.ts",
|
|
330
|
+
"test:pw:qb:auth": "playwright test tests/playwright/qb/auth"
|
|
331
|
+
},
|
|
332
|
+
"dependencies": {
|
|
333
|
+
"typescript": "^5.8.2"
|
|
334
|
+
},
|
|
335
|
+
"devDependencies": {
|
|
336
|
+
"@openapitools/openapi-generator-cli": "^2.17.0",
|
|
337
|
+
"@playwright/test": "^1.51.0",
|
|
338
|
+
"@rollup/plugin-commonjs": "^28.0.3",
|
|
339
|
+
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
340
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
341
|
+
"@rollup/plugin-typescript": "^12.1.2",
|
|
342
|
+
"@types/js-yaml": "^4.0.9",
|
|
343
|
+
"@types/node": "^22.13.10",
|
|
344
|
+
"dotenv": "^16.4.7",
|
|
345
|
+
"glob": "^11.0.1",
|
|
346
|
+
"js-yaml": "^4.1.0",
|
|
347
|
+
"jsdom": "^26.0.0",
|
|
348
|
+
"node-fetch": "^3.3.2",
|
|
349
|
+
"openapi-types": "^12.1.3",
|
|
350
|
+
"rimraf": "^5.0.10",
|
|
351
|
+
"rollup": "^4.35.0",
|
|
352
|
+
"rollup-plugin-dts": "^6.1.1",
|
|
353
|
+
"standard-version": "^9.5.0",
|
|
354
|
+
"ts-morph": "^25.0.1",
|
|
355
|
+
"ts-node": "^10.9.2",
|
|
356
|
+
"tslib": "^2.8.1",
|
|
357
|
+
"undici-types": "^7.5.0",
|
|
358
|
+
"vitest": "^3.0.8"
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
- name: playwright-results.txt
|
|
362
|
+
type: directory
|
|
363
|
+
children:
|
|
364
|
+
- name: .last-run.json
|
|
365
|
+
type: file
|
|
85
366
|
- name: playwright.config.ts
|
|
86
367
|
type: file
|
|
87
|
-
- name: rollup.config.
|
|
368
|
+
- name: rollup.config.mjs
|
|
88
369
|
type: file
|
|
89
370
|
- name: src
|
|
90
371
|
type: directory
|
|
91
372
|
children:
|
|
92
|
-
- name: code-generation
|
|
93
|
-
type: directory
|
|
94
|
-
children:
|
|
95
|
-
- name: fix-spec-definitions.ts
|
|
96
|
-
type: file
|
|
97
|
-
- name: fix-spec-main.ts
|
|
98
|
-
type: file
|
|
99
|
-
- name: fix-spec-paths.ts
|
|
100
|
-
type: file
|
|
101
|
-
- name: generate-unified-interface.ts
|
|
102
|
-
type: file
|
|
103
|
-
- name: output
|
|
104
|
-
type: directory
|
|
105
|
-
children:
|
|
106
|
-
- name: quickbase-fixed.json
|
|
107
|
-
type: file
|
|
108
|
-
- name: regenerate-client.ts
|
|
109
|
-
type: file
|
|
110
373
|
- name: generated
|
|
111
374
|
type: directory
|
|
112
375
|
children:
|
|
@@ -191,6 +454,10 @@ tree:
|
|
|
191
454
|
type: file
|
|
192
455
|
- name: CreateAppRequestVariablesInner.ts
|
|
193
456
|
type: file
|
|
457
|
+
- name: CreateField200Response.ts
|
|
458
|
+
type: file
|
|
459
|
+
- name: CreateFieldRequest.ts
|
|
460
|
+
type: file
|
|
194
461
|
- name: CreateRelationship200Response.ts
|
|
195
462
|
type: file
|
|
196
463
|
- name: CreateRelationshipRequest.ts
|
|
@@ -199,6 +466,14 @@ tree:
|
|
|
199
466
|
type: file
|
|
200
467
|
- name: DeactivateUserToken200Response.ts
|
|
201
468
|
type: file
|
|
469
|
+
- name: DeleteApp200Response.ts
|
|
470
|
+
type: file
|
|
471
|
+
- name: DeleteAppRequest.ts
|
|
472
|
+
type: file
|
|
473
|
+
- name: DeleteFields200Response.ts
|
|
474
|
+
type: file
|
|
475
|
+
- name: DeleteFieldsRequest.ts
|
|
476
|
+
type: file
|
|
202
477
|
- name: DeleteFile200Response.ts
|
|
203
478
|
type: file
|
|
204
479
|
- name: DeleteRecords200Response.ts
|
|
@@ -221,9 +496,13 @@ tree:
|
|
|
221
496
|
type: file
|
|
222
497
|
- name: FieldProperties.ts
|
|
223
498
|
type: file
|
|
224
|
-
- name:
|
|
499
|
+
- name: FieldUsage.ts
|
|
225
500
|
type: file
|
|
226
|
-
- name:
|
|
501
|
+
- name: FieldUsageField.ts
|
|
502
|
+
type: file
|
|
503
|
+
- name: FieldUsageUsage.ts
|
|
504
|
+
type: file
|
|
505
|
+
- name: GenerateDocument200Response.ts
|
|
227
506
|
type: file
|
|
228
507
|
- name: GetRelationships200Response.ts
|
|
229
508
|
type: file
|
|
@@ -297,8 +576,6 @@ tree:
|
|
|
297
576
|
type: file
|
|
298
577
|
- name: UndenyUsers200Response.ts
|
|
299
578
|
type: file
|
|
300
|
-
- name: UpdateFieldRequest.ts
|
|
301
|
-
type: file
|
|
302
579
|
- name: UpdateRelationshipRequest.ts
|
|
303
580
|
type: file
|
|
304
581
|
- name: UpdateTableRequest.ts
|
|
@@ -315,6 +592,8 @@ tree:
|
|
|
315
592
|
type: file
|
|
316
593
|
- name: UpsertRequest.ts
|
|
317
594
|
type: file
|
|
595
|
+
- name: UsageCount.ts
|
|
596
|
+
type: file
|
|
318
597
|
- name: index.ts
|
|
319
598
|
type: file
|
|
320
599
|
- name: runtime.ts
|
|
@@ -325,12 +604,13 @@ tree:
|
|
|
325
604
|
- name: QuickbaseClient.ts
|
|
326
605
|
type: file
|
|
327
606
|
contents: |
|
|
328
|
-
// Generated on 2025-03-
|
|
329
|
-
import { App, CopyApp200Response, CopyAppRequest, CreateApp200Response, CreateAppRequest, DeleteRecords200Response, DeleteRecordsRequest, DeleteTableResponse, Field, GetRelationships200Response, GetTempTokenDBID200Response, ReportRunResponse, RunQueryRequest, RunQueryResponse, Table, UpdateTableRequest, Upsert200Response, Upsert207Response, UpsertRequest } from "../generated/models";
|
|
607
|
+
// Generated on 2025-03-12T19:20:50.952Z
|
|
608
|
+
import { App, CopyApp200Response, CopyAppRequest, CreateApp200Response, CreateAppRequest, CreateField200Response, CreateFieldRequest, DeleteApp200Response, DeleteAppRequest, DeleteFields200Response, DeleteFieldsRequest, DeleteRecords200Response, DeleteRecordsRequest, DeleteTableResponse, Field, FieldUsage, GetRelationships200Response, GetTempTokenDBID200Response, ReportRunResponse, RunQueryRequest, RunQueryResponse, Table, UpdateTableRequest, Upsert200Response, Upsert207Response, UpsertRequest } from "../generated/models";
|
|
330
609
|
|
|
331
610
|
export interface QuickbaseClient {
|
|
332
611
|
createApp: (params: { body: CreateAppRequest }) => Promise<CreateApp200Response>;
|
|
333
612
|
getApp: (params: { appId: string }) => Promise<App>;
|
|
613
|
+
deleteApp: (params: { appId: string; body: DeleteAppRequest }) => Promise<DeleteApp200Response>;
|
|
334
614
|
getAppEvents: (params: { appId: string }) => Promise<any[]>;
|
|
335
615
|
copyApp: (params: { appId: string; body: CopyAppRequest }) => Promise<CopyApp200Response>;
|
|
336
616
|
getAppTables: (params: { appId: string }) => Promise<Table[]>;
|
|
@@ -346,10 +626,11 @@ tree:
|
|
|
346
626
|
getReport: (params: { tableId: string; reportId: string }) => Promise<{ [key: string]: any }>;
|
|
347
627
|
runReport: (params: { reportId: string; generated: any }) => Promise<ReportRunResponse[]>;
|
|
348
628
|
getFields: (params: { tableId: string; includeFieldPerms?: boolean }) => Promise<Field[]>;
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
629
|
+
createField: (params: { tableId: string; body: CreateFieldRequest }) => Promise<CreateField200Response>;
|
|
630
|
+
deleteFields: (params: { tableId: string; body: DeleteFieldsRequest }) => Promise<DeleteFields200Response>;
|
|
631
|
+
getField: (params: { fieldId: number; tableId: string; includeFieldPerms?: boolean }) => Promise<Field>;
|
|
632
|
+
getFieldsUsage: (params: { tableId: string; skip?: number; top?: number }) => Promise<FieldUsage[]>;
|
|
633
|
+
getFieldUsage: (params: { fieldId: number; tableId: string }) => Promise<FieldUsage[]>;
|
|
353
634
|
runFormula: (params: { generated?: any }) => Promise<{ [key: string]: any }>;
|
|
354
635
|
upsert: (params: { body: UpsertRequest }) => Promise<Upsert200Response | Upsert207Response>;
|
|
355
636
|
deleteRecords: (params: { body: DeleteRecordsRequest }) => Promise<DeleteRecords200Response>;
|
|
@@ -387,13 +668,6 @@ tree:
|
|
|
387
668
|
}
|
|
388
669
|
- name: index.ts
|
|
389
670
|
type: file
|
|
390
|
-
- name: log-gen.ts
|
|
391
|
-
type: file
|
|
392
|
-
- name: openapi-jars
|
|
393
|
-
type: directory
|
|
394
|
-
children:
|
|
395
|
-
- name: openapi-generator-cli-7.12.0.jar
|
|
396
|
-
type: file
|
|
397
671
|
- name: quickbaseClient.ts
|
|
398
672
|
type: file
|
|
399
673
|
contents: |
|
|
@@ -478,15 +752,15 @@ tree:
|
|
|
478
752
|
|
|
479
753
|
const tokenCache = new TokenCache();
|
|
480
754
|
|
|
481
|
-
const
|
|
755
|
+
const baseHeaders: HTTPHeaders = {
|
|
482
756
|
"QB-Realm-Hostname": `${realm}.quickbase.com`,
|
|
483
757
|
"Content-Type": "application/json",
|
|
484
758
|
};
|
|
485
759
|
|
|
486
760
|
if (initialTempToken) {
|
|
487
|
-
|
|
761
|
+
baseHeaders["Authorization"] = `QB-TEMP-TOKEN ${initialTempToken}`;
|
|
488
762
|
} else if (userToken && !useTempTokens) {
|
|
489
|
-
|
|
763
|
+
baseHeaders["Authorization"] = `QB-USER-TOKEN ${userToken}`;
|
|
490
764
|
}
|
|
491
765
|
|
|
492
766
|
const defaultFetch: typeof fetch | undefined =
|
|
@@ -495,7 +769,7 @@ tree:
|
|
|
495
769
|
: undefined;
|
|
496
770
|
const configuration = new Configuration({
|
|
497
771
|
basePath: baseUrl,
|
|
498
|
-
headers: { ...
|
|
772
|
+
headers: { ...baseHeaders },
|
|
499
773
|
fetchApi: fetchApi || defaultFetch,
|
|
500
774
|
credentials: useTempTokens ? "include" : "omit",
|
|
501
775
|
});
|
|
@@ -561,7 +835,7 @@ tree:
|
|
|
561
835
|
`https://api.quickbase.com/v1/auth/temporary/${dbid}`,
|
|
562
836
|
{
|
|
563
837
|
method: "GET",
|
|
564
|
-
headers: { ...
|
|
838
|
+
headers: { ...baseHeaders },
|
|
565
839
|
credentials: "include",
|
|
566
840
|
}
|
|
567
841
|
);
|
|
@@ -593,23 +867,24 @@ tree:
|
|
|
593
867
|
return token;
|
|
594
868
|
};
|
|
595
869
|
|
|
596
|
-
|
|
870
|
+
async function invokeMethod<K extends keyof QuickbaseClient>(
|
|
597
871
|
methodName: K,
|
|
598
872
|
params: Parameters<QuickbaseClient[K]>[0] & Partial<TempTokenParams>,
|
|
599
873
|
retryCount: number = 0
|
|
600
|
-
): Promise<ReturnType<QuickbaseClient[K]>>
|
|
874
|
+
): Promise<ReturnType<QuickbaseClient[K]>> {
|
|
601
875
|
const methodInfo = methodMap[methodName];
|
|
602
876
|
if (!methodInfo) {
|
|
603
877
|
throw new Error(`Method ${methodName} not found`);
|
|
604
878
|
}
|
|
605
879
|
|
|
606
|
-
const
|
|
880
|
+
const requestParameters =
|
|
607
881
|
methodInfo.paramMap.length === 1 &&
|
|
608
882
|
methodInfo.paramMap[0] === "requestParameters"
|
|
609
|
-
?
|
|
610
|
-
:
|
|
883
|
+
? { requestParameters: params }
|
|
884
|
+
: params;
|
|
885
|
+
let requestOptions: RequestInit = {};
|
|
611
886
|
|
|
612
|
-
|
|
887
|
+
const selectedToken =
|
|
613
888
|
initialTempToken || (userToken && !useTempTokens ? userToken : undefined);
|
|
614
889
|
|
|
615
890
|
if (methodName === "getTempTokenDBID" && useTempTokens) {
|
|
@@ -622,43 +897,38 @@ tree:
|
|
|
622
897
|
}
|
|
623
898
|
}
|
|
624
899
|
|
|
625
|
-
|
|
900
|
+
let authorizationToken = selectedToken;
|
|
901
|
+
if (useTempTokens && !authorizationToken) {
|
|
626
902
|
const dbid = extractDbid(
|
|
627
903
|
params,
|
|
628
904
|
`No dbid found in params for ${methodName} to fetch temp token`
|
|
629
905
|
);
|
|
630
906
|
const cachedToken = tokenCache.get(dbid);
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
"Temporary tokens require a browser environment or a custom fetchApi with browser-like session support"
|
|
637
|
-
);
|
|
638
|
-
}
|
|
639
|
-
token = await fetchTempToken(dbid);
|
|
640
|
-
if (methodName === "getTempTokenDBID") {
|
|
641
|
-
return { temporaryAuthorization: token } as ReturnType<
|
|
642
|
-
QuickbaseClient[K]
|
|
643
|
-
>;
|
|
644
|
-
}
|
|
907
|
+
authorizationToken = cachedToken || (await fetchTempToken(dbid));
|
|
908
|
+
if (methodName === "getTempTokenDBID") {
|
|
909
|
+
return { temporaryAuthorization: authorizationToken } as ReturnType<
|
|
910
|
+
QuickbaseClient[K]
|
|
911
|
+
>;
|
|
645
912
|
}
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
913
|
+
requestOptions.headers = {
|
|
914
|
+
...baseHeaders,
|
|
915
|
+
Authorization: `QB-TEMP-TOKEN ${authorizationToken}`,
|
|
916
|
+
};
|
|
917
|
+
} else if (authorizationToken) {
|
|
918
|
+
requestOptions.headers = {
|
|
919
|
+
...baseHeaders,
|
|
920
|
+
Authorization: `QB-USER-TOKEN ${authorizationToken}`,
|
|
651
921
|
};
|
|
652
922
|
}
|
|
653
923
|
|
|
654
924
|
try {
|
|
655
|
-
|
|
656
|
-
return response;
|
|
925
|
+
return await methodInfo.method(requestParameters, requestOptions);
|
|
657
926
|
} catch (error) {
|
|
658
927
|
if (
|
|
659
928
|
error instanceof ResponseError &&
|
|
660
929
|
error.response.status === 401 &&
|
|
661
|
-
retryCount < 1
|
|
930
|
+
retryCount < 1 &&
|
|
931
|
+
useTempTokens
|
|
662
932
|
) {
|
|
663
933
|
if (debug) {
|
|
664
934
|
console.log(
|
|
@@ -670,12 +940,10 @@ tree:
|
|
|
670
940
|
params,
|
|
671
941
|
`No dbid to refresh token after authorization error`
|
|
672
942
|
);
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
Authorization: `QB-TEMP-TOKEN ${token}`,
|
|
678
|
-
},
|
|
943
|
+
authorizationToken = await fetchTempToken(dbid);
|
|
944
|
+
requestOptions.headers = {
|
|
945
|
+
...baseHeaders,
|
|
946
|
+
Authorization: `QB-TEMP-TOKEN ${authorizationToken}`,
|
|
679
947
|
};
|
|
680
948
|
if (debug) {
|
|
681
949
|
console.log(`Retrying ${methodName} with new token`);
|
|
@@ -699,7 +967,7 @@ tree:
|
|
|
699
967
|
}
|
|
700
968
|
throw error;
|
|
701
969
|
}
|
|
702
|
-
}
|
|
970
|
+
}
|
|
703
971
|
|
|
704
972
|
const proxy = new Proxy<QuickbaseClient>({} as QuickbaseClient, {
|
|
705
973
|
get: (_, prop: string): ((params: any) => Promise<any>) | undefined => {
|
|
@@ -716,15 +984,71 @@ tree:
|
|
|
716
984
|
}
|
|
717
985
|
- name: tokenCache.ts
|
|
718
986
|
type: file
|
|
987
|
+
contents: |
|
|
988
|
+
interface CachedToken {
|
|
989
|
+
token: string;
|
|
990
|
+
expiresAt: number; // Timestamp in milliseconds
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
export class TokenCache {
|
|
994
|
+
private cache: Map<string, CachedToken>;
|
|
995
|
+
private readonly lifespan: number; // Token lifespan in milliseconds
|
|
996
|
+
|
|
997
|
+
constructor(lifespan: number = 4 * 60 * 1000 + 50 * 1000) {
|
|
998
|
+
// Default 4:50
|
|
999
|
+
this.cache = new Map<string, CachedToken>();
|
|
1000
|
+
this.lifespan = lifespan;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
get(dbid: string): string | undefined {
|
|
1004
|
+
const entry = this.cache.get(dbid);
|
|
1005
|
+
const now = Date.now();
|
|
1006
|
+
if (entry && entry.expiresAt > now) {
|
|
1007
|
+
return entry.token;
|
|
1008
|
+
}
|
|
1009
|
+
return undefined; // Expired or not found
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
// New method to get full entry
|
|
1013
|
+
getEntry(dbid: string): CachedToken | undefined {
|
|
1014
|
+
const entry = this.cache.get(dbid);
|
|
1015
|
+
const now = Date.now();
|
|
1016
|
+
if (entry && entry.expiresAt > now) {
|
|
1017
|
+
return entry;
|
|
1018
|
+
}
|
|
1019
|
+
return undefined; // Expired or not found
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
set(dbid: string, token: string): void {
|
|
1023
|
+
const now = Date.now();
|
|
1024
|
+
this.cache.set(dbid, {
|
|
1025
|
+
token,
|
|
1026
|
+
expiresAt: now + this.lifespan,
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
dump(): [string, CachedToken][] {
|
|
1031
|
+
return Array.from(this.cache.entries());
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// New method to clear the cache
|
|
1035
|
+
clear(): void {
|
|
1036
|
+
this.cache.clear();
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
719
1039
|
- name: test-artifacts
|
|
720
1040
|
type: directory
|
|
721
1041
|
children:
|
|
722
1042
|
- name: .last-run.json
|
|
723
1043
|
type: file
|
|
724
|
-
|
|
725
|
-
|
|
1044
|
+
- name: test-esm.js
|
|
1045
|
+
type: file
|
|
726
1046
|
- name: test-results.json
|
|
727
1047
|
type: file
|
|
1048
|
+
- name: test-umd.js
|
|
1049
|
+
type: file
|
|
1050
|
+
- name: test.ts
|
|
1051
|
+
type: file
|
|
728
1052
|
- name: tests
|
|
729
1053
|
type: directory
|
|
730
1054
|
children:
|
|
@@ -749,6 +1073,8 @@ tree:
|
|
|
749
1073
|
type: file
|
|
750
1074
|
- name: tokenRenewal.test.ts
|
|
751
1075
|
type: file
|
|
1076
|
+
- name: umd.test.ts
|
|
1077
|
+
type: file
|
|
752
1078
|
- name: setup.ts
|
|
753
1079
|
type: file
|
|
754
1080
|
- name: vitest
|
|
@@ -760,13 +1086,29 @@ tree:
|
|
|
760
1086
|
- name: apps
|
|
761
1087
|
type: directory
|
|
762
1088
|
children:
|
|
1089
|
+
- name: copyApp.test.ts
|
|
1090
|
+
type: file
|
|
1091
|
+
- name: createApp.test.ts
|
|
1092
|
+
type: file
|
|
1093
|
+
- name: deleteApp.test.ts
|
|
1094
|
+
type: file
|
|
763
1095
|
- name: getApp.test.ts
|
|
764
1096
|
type: file
|
|
765
1097
|
- name: fields
|
|
766
1098
|
type: directory
|
|
767
1099
|
children:
|
|
1100
|
+
- name: createField.test.ts
|
|
1101
|
+
type: file
|
|
1102
|
+
- name: deleteFields.test.ts
|
|
1103
|
+
type: file
|
|
1104
|
+
- name: getField.test.ts
|
|
1105
|
+
type: file
|
|
1106
|
+
- name: getFieldUsage.test.ts
|
|
1107
|
+
type: file
|
|
768
1108
|
- name: getFields.test.ts
|
|
769
1109
|
type: file
|
|
1110
|
+
- name: getFieldsUsage.test.ts
|
|
1111
|
+
type: file
|
|
770
1112
|
- name: index.ts
|
|
771
1113
|
type: file
|
|
772
1114
|
- name: records
|
|
@@ -799,1882 +1141,161 @@ tree:
|
|
|
799
1141
|
- name: apps
|
|
800
1142
|
type: directory
|
|
801
1143
|
children:
|
|
1144
|
+
- name: copyApp.test.ts
|
|
1145
|
+
type: file
|
|
1146
|
+
- name: createApp.test.ts
|
|
1147
|
+
type: file
|
|
1148
|
+
- name: deleteApp.test.ts
|
|
1149
|
+
type: file
|
|
802
1150
|
- name: getApp.test.ts
|
|
803
1151
|
type: file
|
|
804
|
-
contents: |
|
|
805
|
-
// tests/vitest/unit/apps/getApp.test.ts
|
|
806
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
807
|
-
import {
|
|
808
|
-
createClient,
|
|
809
|
-
mockFetch,
|
|
810
|
-
QB_APP_ID,
|
|
811
|
-
QB_REALM,
|
|
812
|
-
QB_USER_TOKEN,
|
|
813
|
-
} from "@tests/setup.ts";
|
|
814
|
-
|
|
815
|
-
describe("QuickbaseClient Unit - getApp", () => {
|
|
816
|
-
let client: ReturnType<typeof createClient>;
|
|
817
|
-
|
|
818
|
-
beforeEach(() => {
|
|
819
|
-
mockFetch.mockClear();
|
|
820
|
-
client = createClient(mockFetch, { debug: true }); // Add debug: true for consistency
|
|
821
|
-
});
|
|
822
|
-
|
|
823
|
-
it("initializes without errors", () => {
|
|
824
|
-
expect(client).toBeDefined();
|
|
825
|
-
});
|
|
826
|
-
|
|
827
|
-
it("has getApp method", () => {
|
|
828
|
-
expect(typeof client.getApp).toBe("function");
|
|
829
|
-
});
|
|
830
|
-
|
|
831
|
-
it("calls getApp successfully", async () => {
|
|
832
|
-
mockFetch.mockImplementation((url: string, options: any) => {
|
|
833
|
-
console.log("Mock fetch:", url, options);
|
|
834
|
-
return Promise.resolve({
|
|
835
|
-
ok: true,
|
|
836
|
-
status: 200,
|
|
837
|
-
json: () =>
|
|
838
|
-
Promise.resolve({
|
|
839
|
-
id: QB_APP_ID,
|
|
840
|
-
name: "qb-copy",
|
|
841
|
-
created: "2025-02-13T18:22:33Z",
|
|
842
|
-
updated: "2025-03-04T04:25:51Z",
|
|
843
|
-
description: "",
|
|
844
|
-
timeZone: "(UTC-08:00) Pacific Time (US & Canada)",
|
|
845
|
-
dateFormat: "MM-DD-YYYY",
|
|
846
|
-
hasEveryoneOnTheInternet: false,
|
|
847
|
-
memoryInfo: { estMemory: 0, estMemoryInclDependentApps: 0 },
|
|
848
|
-
securityProperties: {
|
|
849
|
-
allowClone: false,
|
|
850
|
-
allowExport: true,
|
|
851
|
-
enableAppTokens: true,
|
|
852
|
-
hideFromPublic: false,
|
|
853
|
-
mustBeRealmApproved: false,
|
|
854
|
-
useIPFilter: false,
|
|
855
|
-
},
|
|
856
|
-
}),
|
|
857
|
-
} as Response);
|
|
858
|
-
});
|
|
859
|
-
|
|
860
|
-
const result = await client.getApp({ appId: QB_APP_ID });
|
|
861
|
-
expect(result).toEqual({
|
|
862
|
-
id: QB_APP_ID,
|
|
863
|
-
name: "qb-copy",
|
|
864
|
-
created: new Date("2025-02-13T18:22:33Z"),
|
|
865
|
-
updated: new Date("2025-03-04T04:25:51Z"),
|
|
866
|
-
description: "",
|
|
867
|
-
timeZone: "(UTC-08:00) Pacific Time (US & Canada)",
|
|
868
|
-
dateFormat: "MM-DD-YYYY",
|
|
869
|
-
hasEveryoneOnTheInternet: false,
|
|
870
|
-
memoryInfo: { estMemory: 0, estMemoryInclDependentApps: 0 },
|
|
871
|
-
securityProperties: {
|
|
872
|
-
allowClone: false,
|
|
873
|
-
allowExport: true,
|
|
874
|
-
enableAppTokens: true,
|
|
875
|
-
hideFromPublic: false,
|
|
876
|
-
mustBeRealmApproved: false,
|
|
877
|
-
useIPFilter: false,
|
|
878
|
-
},
|
|
879
|
-
});
|
|
880
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
881
|
-
`https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
|
|
882
|
-
expect.objectContaining({
|
|
883
|
-
headers: expect.objectContaining({
|
|
884
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
885
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
886
|
-
}),
|
|
887
|
-
})
|
|
888
|
-
);
|
|
889
|
-
});
|
|
890
|
-
});
|
|
891
1152
|
- name: auth
|
|
892
1153
|
type: directory
|
|
893
1154
|
children:
|
|
894
1155
|
- name: fetchTempToken401.test.ts
|
|
895
1156
|
type: file
|
|
896
|
-
contents: |
|
|
897
|
-
// tests/vitest/unit/auth/fetchTempToken401.test.ts
|
|
898
|
-
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
899
|
-
import {
|
|
900
|
-
createClient,
|
|
901
|
-
mockFetch,
|
|
902
|
-
QB_REALM,
|
|
903
|
-
QB_TABLE_ID_1,
|
|
904
|
-
} from "@tests/setup.ts";
|
|
905
|
-
|
|
906
|
-
describe("QuickbaseClient Unit - 401 with fetchTempToken 401", () => {
|
|
907
|
-
let client: ReturnType<typeof createClient>;
|
|
908
|
-
|
|
909
|
-
beforeEach(() => {
|
|
910
|
-
mockFetch.mockClear();
|
|
911
|
-
client = createClient(mockFetch, { useTempTokens: true, debug: true });
|
|
912
|
-
});
|
|
913
|
-
|
|
914
|
-
it("fails without infinite loop when fetchTempToken returns 401 after initial 401", async () => {
|
|
915
|
-
let callCount = 0;
|
|
916
|
-
|
|
917
|
-
mockFetch.mockImplementation((url) => {
|
|
918
|
-
callCount++;
|
|
919
|
-
console.log(`Mock fetch call ${callCount}: ${url}`);
|
|
920
|
-
if (url.includes("auth/temporary") && callCount === 1) {
|
|
921
|
-
return Promise.resolve({
|
|
922
|
-
ok: true,
|
|
923
|
-
status: 200,
|
|
924
|
-
json: () =>
|
|
925
|
-
Promise.resolve({ temporaryAuthorization: "initial_token" }),
|
|
926
|
-
});
|
|
927
|
-
}
|
|
928
|
-
if (url.includes("fields") && callCount === 2) {
|
|
929
|
-
return Promise.resolve({
|
|
930
|
-
ok: false,
|
|
931
|
-
status: 401,
|
|
932
|
-
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
933
|
-
});
|
|
934
|
-
}
|
|
935
|
-
if (url.includes("auth/temporary") && callCount === 3) {
|
|
936
|
-
return Promise.resolve({
|
|
937
|
-
ok: false,
|
|
938
|
-
status: 401,
|
|
939
|
-
json: () =>
|
|
940
|
-
Promise.resolve({ message: "Unauthorized in fetchTempToken" }),
|
|
941
|
-
});
|
|
942
|
-
}
|
|
943
|
-
return Promise.reject(new Error(`Unexpected fetch call: ${url}`));
|
|
944
|
-
});
|
|
945
|
-
|
|
946
|
-
const consoleSpy = vi.spyOn(console, "log");
|
|
947
|
-
await expect(client.getFields({ tableId: QB_TABLE_ID_1 })).rejects.toThrow(
|
|
948
|
-
"API Error: Unauthorized in fetchTempToken (Status: 401)"
|
|
949
|
-
);
|
|
950
|
-
|
|
951
|
-
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
952
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
953
|
-
1,
|
|
954
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
955
|
-
expect.any(Object)
|
|
956
|
-
);
|
|
957
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
958
|
-
2,
|
|
959
|
-
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`, // Removed &includeFieldPerms=false
|
|
960
|
-
expect.objectContaining({
|
|
961
|
-
headers: expect.objectContaining({
|
|
962
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
963
|
-
}),
|
|
964
|
-
})
|
|
965
|
-
);
|
|
966
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
967
|
-
3,
|
|
968
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
969
|
-
expect.any(Object)
|
|
970
|
-
);
|
|
971
|
-
|
|
972
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
973
|
-
"Authorization error for getFields, refreshing token:",
|
|
974
|
-
expect.any(String)
|
|
975
|
-
);
|
|
976
|
-
expect(consoleSpy).not.toHaveBeenCalledWith(
|
|
977
|
-
"Retrying getFields with new token"
|
|
978
|
-
);
|
|
979
|
-
consoleSpy.mockRestore();
|
|
980
|
-
});
|
|
981
|
-
});
|
|
982
1157
|
- name: getTempToken.test.ts
|
|
983
1158
|
type: file
|
|
984
|
-
contents: |
|
|
985
|
-
// tests/vitest/unit/auth/getTempTokenDBID.test.ts
|
|
986
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
987
|
-
import {
|
|
988
|
-
createClient,
|
|
989
|
-
mockFetch,
|
|
990
|
-
QB_REALM,
|
|
991
|
-
QB_TABLE_ID_1,
|
|
992
|
-
} from "@tests/setup.ts";
|
|
993
|
-
|
|
994
|
-
describe("QuickbaseClient Unit - getTempTokenDBID", () => {
|
|
995
|
-
let client: ReturnType<typeof createClient>;
|
|
996
|
-
|
|
997
|
-
beforeEach(() => {
|
|
998
|
-
mockFetch.mockClear();
|
|
999
|
-
client = createClient(mockFetch, { useTempTokens: true, debug: false });
|
|
1000
|
-
});
|
|
1001
|
-
|
|
1002
|
-
it("initializes without errors", () => {
|
|
1003
|
-
expect(client).toBeDefined();
|
|
1004
|
-
});
|
|
1005
|
-
|
|
1006
|
-
it("has getTempTokenDBID method", () => {
|
|
1007
|
-
expect(typeof client.getTempTokenDBID).toBe("function");
|
|
1008
|
-
});
|
|
1009
|
-
|
|
1010
|
-
it("fetches and caches temp token on first call", async () => {
|
|
1011
|
-
const mockToken = "b123xyz_temp_token";
|
|
1012
|
-
|
|
1013
|
-
mockFetch.mockResolvedValueOnce({
|
|
1014
|
-
ok: true,
|
|
1015
|
-
status: 200,
|
|
1016
|
-
json: () => Promise.resolve({ temporaryAuthorization: mockToken }),
|
|
1017
|
-
});
|
|
1018
|
-
|
|
1019
|
-
const result = await client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 });
|
|
1020
|
-
expect(result).toEqual({ temporaryAuthorization: mockToken });
|
|
1021
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1022
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
1023
|
-
expect.objectContaining({
|
|
1024
|
-
method: "GET",
|
|
1025
|
-
headers: expect.objectContaining({
|
|
1026
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1027
|
-
"Content-Type": "application/json",
|
|
1028
|
-
}),
|
|
1029
|
-
credentials: "include",
|
|
1030
|
-
})
|
|
1031
|
-
);
|
|
1032
|
-
});
|
|
1033
|
-
|
|
1034
|
-
it("reuses cached temp token on second call", async () => {
|
|
1035
|
-
const mockToken = "b123xyz_temp_token";
|
|
1036
|
-
|
|
1037
|
-
mockFetch.mockResolvedValueOnce({
|
|
1038
|
-
ok: true,
|
|
1039
|
-
status: 200,
|
|
1040
|
-
json: () => Promise.resolve({ temporaryAuthorization: mockToken }),
|
|
1041
|
-
});
|
|
1042
|
-
|
|
1043
|
-
const firstResult = await client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 });
|
|
1044
|
-
expect(firstResult).toEqual({ temporaryAuthorization: mockToken });
|
|
1045
|
-
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
1046
|
-
|
|
1047
|
-
mockFetch.mockClear();
|
|
1048
|
-
const secondResult = await client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 });
|
|
1049
|
-
expect(secondResult).toEqual({ temporaryAuthorization: mockToken });
|
|
1050
|
-
expect(mockFetch).not.toHaveBeenCalled();
|
|
1051
|
-
});
|
|
1052
|
-
|
|
1053
|
-
it("handles API error", async () => {
|
|
1054
|
-
mockFetch.mockResolvedValueOnce({
|
|
1055
|
-
ok: false,
|
|
1056
|
-
status: 401,
|
|
1057
|
-
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
1058
|
-
});
|
|
1059
|
-
|
|
1060
|
-
await expect(
|
|
1061
|
-
client.getTempTokenDBID({ dbid: QB_TABLE_ID_1 })
|
|
1062
|
-
).rejects.toSatisfy((error: Error) => {
|
|
1063
|
-
expect(error).toBeInstanceOf(Error);
|
|
1064
|
-
expect(error.message).toBe("API Error: Unauthorized (Status: 401)");
|
|
1065
|
-
return true;
|
|
1066
|
-
});
|
|
1067
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1068
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
1069
|
-
expect.objectContaining({
|
|
1070
|
-
method: "GET",
|
|
1071
|
-
headers: expect.objectContaining({
|
|
1072
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1073
|
-
"Content-Type": "application/json",
|
|
1074
|
-
}),
|
|
1075
|
-
credentials: "include",
|
|
1076
|
-
})
|
|
1077
|
-
);
|
|
1078
|
-
});
|
|
1079
|
-
});
|
|
1080
1159
|
- name: retryOn401.test.ts
|
|
1081
1160
|
type: file
|
|
1082
|
-
contents: |
|
|
1083
|
-
// tests/vitest/unit/auth/retryOn401.test.ts
|
|
1084
|
-
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
1085
|
-
import {
|
|
1086
|
-
createClient,
|
|
1087
|
-
mockFetch,
|
|
1088
|
-
QB_REALM,
|
|
1089
|
-
QB_TABLE_ID_1,
|
|
1090
|
-
} from "@tests/setup.ts";
|
|
1091
|
-
|
|
1092
|
-
describe("QuickbaseClient Unit - 401 Retry Creates New Token", () => {
|
|
1093
|
-
let client: ReturnType<typeof createClient>;
|
|
1094
|
-
|
|
1095
|
-
beforeEach(() => {
|
|
1096
|
-
mockFetch.mockClear();
|
|
1097
|
-
client = createClient(mockFetch, { useTempTokens: true, debug: true });
|
|
1098
|
-
});
|
|
1099
|
-
|
|
1100
|
-
it("creates a new token on 401 and retries successfully", async () => {
|
|
1101
|
-
const mockToken = "new_token_456";
|
|
1102
|
-
const mockFields = [{ id: 1, label: "Field1" }];
|
|
1103
|
-
let callCount = 0;
|
|
1104
|
-
|
|
1105
|
-
mockFetch.mockImplementation((url) => {
|
|
1106
|
-
callCount++;
|
|
1107
|
-
console.log(`Mock fetch call ${callCount}: ${url}`);
|
|
1108
|
-
if (url.includes("auth/temporary") && callCount === 1) {
|
|
1109
|
-
return Promise.resolve({
|
|
1110
|
-
ok: true,
|
|
1111
|
-
status: 200,
|
|
1112
|
-
json: () => Promise.resolve({ temporaryAuthorization: mockToken }),
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1115
|
-
if (url.includes("fields") && callCount === 2) {
|
|
1116
|
-
return Promise.resolve({
|
|
1117
|
-
ok: false,
|
|
1118
|
-
status: 401,
|
|
1119
|
-
text: () => Promise.resolve("Unauthorized"),
|
|
1120
|
-
});
|
|
1121
|
-
}
|
|
1122
|
-
if (url.includes("auth/temporary") && callCount === 3) {
|
|
1123
|
-
return Promise.resolve({
|
|
1124
|
-
ok: true,
|
|
1125
|
-
status: 200,
|
|
1126
|
-
json: () =>
|
|
1127
|
-
Promise.resolve({ temporaryAuthorization: mockToken + "_retry" }),
|
|
1128
|
-
});
|
|
1129
|
-
}
|
|
1130
|
-
if (url.includes("fields") && callCount === 4) {
|
|
1131
|
-
return Promise.resolve({
|
|
1132
|
-
ok: true,
|
|
1133
|
-
status: 200,
|
|
1134
|
-
json: () => Promise.resolve(mockFields),
|
|
1135
|
-
});
|
|
1136
|
-
}
|
|
1137
|
-
return Promise.reject(new Error(`Unexpected fetch call: ${url}`));
|
|
1138
|
-
});
|
|
1139
|
-
|
|
1140
|
-
const consoleSpy = vi.spyOn(console, "log");
|
|
1141
|
-
const result = await client.getFields({ tableId: QB_TABLE_ID_1 });
|
|
1142
|
-
|
|
1143
|
-
expect(result).toEqual(mockFields);
|
|
1144
|
-
expect(mockFetch).toHaveBeenCalledTimes(4);
|
|
1145
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
1146
|
-
1,
|
|
1147
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
1148
|
-
expect.any(Object)
|
|
1149
|
-
);
|
|
1150
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
1151
|
-
2,
|
|
1152
|
-
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`, // Removed &includeFieldPerms=false
|
|
1153
|
-
expect.objectContaining({
|
|
1154
|
-
headers: expect.objectContaining({
|
|
1155
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1156
|
-
}),
|
|
1157
|
-
})
|
|
1158
|
-
);
|
|
1159
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
1160
|
-
3,
|
|
1161
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
1162
|
-
expect.any(Object)
|
|
1163
|
-
);
|
|
1164
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
1165
|
-
4,
|
|
1166
|
-
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}`, // Removed &includeFieldPerms=false
|
|
1167
|
-
expect.objectContaining({
|
|
1168
|
-
headers: expect.objectContaining({
|
|
1169
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1170
|
-
}),
|
|
1171
|
-
})
|
|
1172
|
-
);
|
|
1173
|
-
|
|
1174
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
1175
|
-
"Authorization error for getFields, refreshing token:",
|
|
1176
|
-
expect.any(String)
|
|
1177
|
-
);
|
|
1178
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
1179
|
-
`Fetched and cached new token for dbid: ${QB_TABLE_ID_1}`,
|
|
1180
|
-
mockToken + "_retry",
|
|
1181
|
-
expect.any(String)
|
|
1182
|
-
);
|
|
1183
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
1184
|
-
"Retrying getFields with new token"
|
|
1185
|
-
);
|
|
1186
|
-
consoleSpy.mockRestore();
|
|
1187
|
-
});
|
|
1188
|
-
});
|
|
1189
1161
|
- name: two401s.test.ts
|
|
1190
1162
|
type: file
|
|
1191
|
-
contents: |
|
|
1192
|
-
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
1193
|
-
import { createClient, mockFetch } from "@tests/setup.ts";
|
|
1194
|
-
|
|
1195
|
-
describe("QuickbaseClient - Two 401s in a Row", () => {
|
|
1196
|
-
let client: ReturnType<typeof createClient>;
|
|
1197
|
-
|
|
1198
|
-
beforeEach(() => {
|
|
1199
|
-
mockFetch.mockClear();
|
|
1200
|
-
client = createClient(mockFetch, { useTempTokens: true, debug: true });
|
|
1201
|
-
});
|
|
1202
|
-
|
|
1203
|
-
it("logs an error after two 401s in a row", async () => {
|
|
1204
|
-
const mockDbid = "mockDbid123";
|
|
1205
|
-
const mockToken = "new_token_456";
|
|
1206
|
-
let callCount = 0;
|
|
1207
|
-
|
|
1208
|
-
mockFetch.mockImplementation((url) => {
|
|
1209
|
-
callCount++;
|
|
1210
|
-
console.log(`Mock fetch call ${callCount}: ${url}`);
|
|
1211
|
-
if (url.includes("auth/temporary") && callCount === 1) {
|
|
1212
|
-
return Promise.resolve({
|
|
1213
|
-
ok: true,
|
|
1214
|
-
status: 200,
|
|
1215
|
-
json: () => Promise.resolve({ temporaryAuthorization: mockToken }),
|
|
1216
|
-
});
|
|
1217
|
-
}
|
|
1218
|
-
if (url.includes("fields") && callCount === 2) {
|
|
1219
|
-
return Promise.resolve({
|
|
1220
|
-
ok: false,
|
|
1221
|
-
status: 401,
|
|
1222
|
-
text: () => Promise.resolve("Unauthorized"),
|
|
1223
|
-
});
|
|
1224
|
-
}
|
|
1225
|
-
if (url.includes("auth/temporary") && callCount === 3) {
|
|
1226
|
-
return Promise.resolve({
|
|
1227
|
-
ok: true,
|
|
1228
|
-
status: 200,
|
|
1229
|
-
json: () =>
|
|
1230
|
-
Promise.resolve({ temporaryAuthorization: mockToken + "_retry" }),
|
|
1231
|
-
});
|
|
1232
|
-
}
|
|
1233
|
-
if (url.includes("fields") && callCount === 4) {
|
|
1234
|
-
return Promise.resolve({
|
|
1235
|
-
ok: false,
|
|
1236
|
-
status: 401,
|
|
1237
|
-
json: () => Promise.resolve({ message: "Unauthorized again" }), // Changed to json()
|
|
1238
|
-
text: () => Promise.resolve("Unauthorized again"), // Keep text() for fallback
|
|
1239
|
-
});
|
|
1240
|
-
}
|
|
1241
|
-
return Promise.reject(new Error(`Unexpected fetch call: ${url}`));
|
|
1242
|
-
});
|
|
1243
|
-
|
|
1244
|
-
const consoleSpy = vi.spyOn(console, "log");
|
|
1245
|
-
await expect(client.getFields({ tableId: mockDbid })).rejects.toThrow(
|
|
1246
|
-
"API Error: Unauthorized again (Status: 401)"
|
|
1247
|
-
);
|
|
1248
|
-
|
|
1249
|
-
expect(mockFetch).toHaveBeenCalledTimes(4);
|
|
1250
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
1251
|
-
"Authorization error for getFields, refreshing token:",
|
|
1252
|
-
expect.any(String)
|
|
1253
|
-
);
|
|
1254
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
1255
|
-
"Fetched and cached new token for dbid: mockDbid123",
|
|
1256
|
-
mockToken + "_retry",
|
|
1257
|
-
expect.any(String)
|
|
1258
|
-
);
|
|
1259
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
1260
|
-
"Retrying getFields with new token"
|
|
1261
|
-
);
|
|
1262
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
1263
|
-
"Error response body for getFields:",
|
|
1264
|
-
{ message: "Unauthorized again" }
|
|
1265
|
-
);
|
|
1266
|
-
consoleSpy.mockRestore();
|
|
1267
|
-
});
|
|
1268
|
-
});
|
|
1269
1163
|
- name: fields
|
|
1270
1164
|
type: directory
|
|
1271
1165
|
children:
|
|
1166
|
+
- name: createField.test.ts
|
|
1167
|
+
type: file
|
|
1168
|
+
- name: deleteFields.test.ts
|
|
1169
|
+
type: file
|
|
1170
|
+
- name: getField.test.ts
|
|
1171
|
+
type: file
|
|
1172
|
+
- name: getFieldUsage.test.ts
|
|
1173
|
+
type: file
|
|
1272
1174
|
- name: getFields.test.ts
|
|
1273
1175
|
type: file
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
import { describe, it, expect } from "vitest";
|
|
1277
|
-
import {
|
|
1278
|
-
createClient,
|
|
1279
|
-
mockFetch,
|
|
1280
|
-
QB_REALM,
|
|
1281
|
-
QB_USER_TOKEN,
|
|
1282
|
-
QB_TABLE_ID_1,
|
|
1283
|
-
} from "@tests/setup.ts";
|
|
1284
|
-
|
|
1285
|
-
describe("QuickbaseClient Unit - getFields", () => {
|
|
1286
|
-
it("calls getFields successfully", async () => {
|
|
1287
|
-
mockFetch.mockImplementation((url: string, options: any) => {
|
|
1288
|
-
console.log("Mock fetch for getFields:", url, options);
|
|
1289
|
-
return Promise.resolve({
|
|
1290
|
-
ok: true,
|
|
1291
|
-
status: 200,
|
|
1292
|
-
json: () =>
|
|
1293
|
-
Promise.resolve([
|
|
1294
|
-
{
|
|
1295
|
-
id: 1,
|
|
1296
|
-
label: "Field1",
|
|
1297
|
-
fieldType: "text",
|
|
1298
|
-
noWrap: false,
|
|
1299
|
-
bold: false,
|
|
1300
|
-
required: false,
|
|
1301
|
-
appearsByDefault: false,
|
|
1302
|
-
findEnabled: false,
|
|
1303
|
-
unique: false,
|
|
1304
|
-
doesDataCopy: false,
|
|
1305
|
-
fieldHelp: "field help",
|
|
1306
|
-
audited: false,
|
|
1307
|
-
properties: {
|
|
1308
|
-
primaryKey: false,
|
|
1309
|
-
foreignKey: false,
|
|
1310
|
-
numLines: 1,
|
|
1311
|
-
maxLength: 0,
|
|
1312
|
-
appendOnly: false,
|
|
1313
|
-
allowHTML: false,
|
|
1314
|
-
allowMentions: false,
|
|
1315
|
-
sortAsGiven: false,
|
|
1316
|
-
carryChoices: true,
|
|
1317
|
-
allowNewChoices: false,
|
|
1318
|
-
formula: "",
|
|
1319
|
-
defaultValue: "",
|
|
1320
|
-
},
|
|
1321
|
-
permissions: [
|
|
1322
|
-
{ permissionType: "View", role: "Viewer", roleId: 10 },
|
|
1323
|
-
{ permissionType: "None", role: "Participant", roleId: 11 },
|
|
1324
|
-
{ permissionType: "Modify", role: "Administrator", roleId: 12 },
|
|
1325
|
-
],
|
|
1326
|
-
},
|
|
1327
|
-
]),
|
|
1328
|
-
} as Response);
|
|
1329
|
-
});
|
|
1330
|
-
|
|
1331
|
-
const client = createClient(mockFetch, { debug: true }); // Add debug: true for consistency
|
|
1332
|
-
const result = await client.getFields({
|
|
1333
|
-
tableId: QB_TABLE_ID_1,
|
|
1334
|
-
includeFieldPerms: true,
|
|
1335
|
-
});
|
|
1336
|
-
console.log("getFields response:", result);
|
|
1337
|
-
expect(result).toEqual(
|
|
1338
|
-
expect.arrayContaining([
|
|
1339
|
-
expect.objectContaining({
|
|
1340
|
-
id: 1,
|
|
1341
|
-
label: "Field1",
|
|
1342
|
-
fieldType: "text",
|
|
1343
|
-
noWrap: false,
|
|
1344
|
-
bold: false,
|
|
1345
|
-
required: false,
|
|
1346
|
-
appearsByDefault: false,
|
|
1347
|
-
findEnabled: false,
|
|
1348
|
-
unique: false,
|
|
1349
|
-
doesDataCopy: false,
|
|
1350
|
-
fieldHelp: "field help",
|
|
1351
|
-
audited: false,
|
|
1352
|
-
properties: expect.objectContaining({
|
|
1353
|
-
primaryKey: false,
|
|
1354
|
-
foreignKey: false,
|
|
1355
|
-
numLines: 1,
|
|
1356
|
-
maxLength: 0,
|
|
1357
|
-
appendOnly: false,
|
|
1358
|
-
allowHTML: false,
|
|
1359
|
-
allowMentions: false,
|
|
1360
|
-
sortAsGiven: false,
|
|
1361
|
-
carryChoices: true,
|
|
1362
|
-
allowNewChoices: false,
|
|
1363
|
-
formula: "",
|
|
1364
|
-
defaultValue: "",
|
|
1365
|
-
}),
|
|
1366
|
-
permissions: expect.arrayContaining([
|
|
1367
|
-
expect.objectContaining({
|
|
1368
|
-
permissionType: "View",
|
|
1369
|
-
role: "Viewer",
|
|
1370
|
-
roleId: 10,
|
|
1371
|
-
}),
|
|
1372
|
-
expect.objectContaining({
|
|
1373
|
-
permissionType: "None",
|
|
1374
|
-
role: "Participant",
|
|
1375
|
-
roleId: 11,
|
|
1376
|
-
}),
|
|
1377
|
-
expect.objectContaining({
|
|
1378
|
-
permissionType: "Modify",
|
|
1379
|
-
role: "Administrator",
|
|
1380
|
-
roleId: 12,
|
|
1381
|
-
}),
|
|
1382
|
-
]),
|
|
1383
|
-
}),
|
|
1384
|
-
])
|
|
1385
|
-
);
|
|
1386
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1387
|
-
`https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}&includeFieldPerms=true`,
|
|
1388
|
-
expect.objectContaining({
|
|
1389
|
-
headers: expect.objectContaining({
|
|
1390
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1391
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1392
|
-
}),
|
|
1393
|
-
})
|
|
1394
|
-
);
|
|
1395
|
-
});
|
|
1396
|
-
});
|
|
1176
|
+
- name: getFieldsUsage.test.ts
|
|
1177
|
+
type: file
|
|
1397
1178
|
- name: index.ts
|
|
1398
1179
|
type: file
|
|
1399
|
-
contents: |
|
|
1400
|
-
// tests/unit/index.ts
|
|
1401
|
-
export * from "./apps/getApp.test.ts";
|
|
1402
|
-
export * from "./fields/getFields.test.ts";
|
|
1403
|
-
export * from "./tables/getAppTables.test.ts";
|
|
1404
1180
|
- name: records
|
|
1405
1181
|
type: directory
|
|
1406
1182
|
children:
|
|
1407
1183
|
- name: deleteRecords.test.ts
|
|
1408
1184
|
type: file
|
|
1409
|
-
contents: |
|
|
1410
|
-
// tests/vitest/unit/records/deleteRecords.test.ts
|
|
1411
|
-
import { describe, expect, test, beforeEach } from "vitest";
|
|
1412
|
-
import {
|
|
1413
|
-
createClient,
|
|
1414
|
-
mockFetch,
|
|
1415
|
-
QB_REALM,
|
|
1416
|
-
QB_USER_TOKEN,
|
|
1417
|
-
QB_TABLE_ID_1,
|
|
1418
|
-
} from "@tests/setup.ts";
|
|
1419
|
-
|
|
1420
|
-
describe("QuickbaseClient Unit - deleteRecords", () => {
|
|
1421
|
-
let qb: ReturnType<typeof createClient>;
|
|
1422
|
-
|
|
1423
|
-
beforeEach(() => {
|
|
1424
|
-
mockFetch.mockClear();
|
|
1425
|
-
qb = createClient(mockFetch, { debug: true }); // Use debug: true for consistency with other tests
|
|
1426
|
-
});
|
|
1427
|
-
|
|
1428
|
-
test("sends correct DELETE request and handles success", async () => {
|
|
1429
|
-
mockFetch.mockResolvedValueOnce({
|
|
1430
|
-
ok: true,
|
|
1431
|
-
status: 200,
|
|
1432
|
-
json: () => Promise.resolve({ numberDeleted: 1 }),
|
|
1433
|
-
});
|
|
1434
|
-
|
|
1435
|
-
const result = await qb.deleteRecords({
|
|
1436
|
-
body: {
|
|
1437
|
-
from: QB_TABLE_ID_1,
|
|
1438
|
-
where: "{3.EX.'5'}", // Simulate deleting Record ID 5
|
|
1439
|
-
},
|
|
1440
|
-
});
|
|
1441
|
-
|
|
1442
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1443
|
-
"https://api.quickbase.com/v1/records",
|
|
1444
|
-
expect.objectContaining({
|
|
1445
|
-
method: "DELETE",
|
|
1446
|
-
headers: expect.objectContaining({
|
|
1447
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1448
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1449
|
-
"Content-Type": "application/json",
|
|
1450
|
-
}),
|
|
1451
|
-
body: JSON.stringify({ from: QB_TABLE_ID_1, where: "{3.EX.'5'}" }),
|
|
1452
|
-
})
|
|
1453
|
-
);
|
|
1454
|
-
expect(result).toEqual({ numberDeleted: 1 });
|
|
1455
|
-
});
|
|
1456
|
-
|
|
1457
|
-
test("handles no records deleted", async () => {
|
|
1458
|
-
mockFetch.mockResolvedValueOnce({
|
|
1459
|
-
ok: true,
|
|
1460
|
-
status: 200,
|
|
1461
|
-
json: () => Promise.resolve({ numberDeleted: 0 }),
|
|
1462
|
-
});
|
|
1463
|
-
|
|
1464
|
-
const result = await qb.deleteRecords({
|
|
1465
|
-
body: {
|
|
1466
|
-
from: QB_TABLE_ID_1,
|
|
1467
|
-
where: "{3.EX.'999999'}", // Non-existent Record ID
|
|
1468
|
-
},
|
|
1469
|
-
});
|
|
1470
|
-
|
|
1471
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1472
|
-
"https://api.quickbase.com/v1/records",
|
|
1473
|
-
expect.objectContaining({
|
|
1474
|
-
method: "DELETE",
|
|
1475
|
-
headers: expect.objectContaining({
|
|
1476
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1477
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1478
|
-
"Content-Type": "application/json",
|
|
1479
|
-
}),
|
|
1480
|
-
body: JSON.stringify({
|
|
1481
|
-
from: QB_TABLE_ID_1,
|
|
1482
|
-
where: "{3.EX.'999999'}",
|
|
1483
|
-
}),
|
|
1484
|
-
})
|
|
1485
|
-
);
|
|
1486
|
-
expect(result).toEqual({ numberDeleted: 0 });
|
|
1487
|
-
});
|
|
1488
|
-
|
|
1489
|
-
test("throws error on API failure", async () => {
|
|
1490
|
-
mockFetch.mockResolvedValueOnce({
|
|
1491
|
-
ok: false,
|
|
1492
|
-
status: 400,
|
|
1493
|
-
json: () => Promise.resolve({ message: "Invalid query" }),
|
|
1494
|
-
});
|
|
1495
|
-
|
|
1496
|
-
await expect(() =>
|
|
1497
|
-
qb.deleteRecords({
|
|
1498
|
-
body: {
|
|
1499
|
-
from: QB_TABLE_ID_1,
|
|
1500
|
-
where: "{3.EX.'invalid'}",
|
|
1501
|
-
},
|
|
1502
|
-
})
|
|
1503
|
-
).rejects.toThrow("API Error: Invalid query (Status: 400)");
|
|
1504
|
-
|
|
1505
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1506
|
-
"https://api.quickbase.com/v1/records",
|
|
1507
|
-
expect.objectContaining({
|
|
1508
|
-
method: "DELETE",
|
|
1509
|
-
headers: expect.objectContaining({
|
|
1510
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1511
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1512
|
-
"Content-Type": "application/json",
|
|
1513
|
-
}),
|
|
1514
|
-
body: JSON.stringify({
|
|
1515
|
-
from: QB_TABLE_ID_1,
|
|
1516
|
-
where: "{3.EX.'invalid'}",
|
|
1517
|
-
}),
|
|
1518
|
-
})
|
|
1519
|
-
);
|
|
1520
|
-
});
|
|
1521
|
-
});
|
|
1522
1185
|
- name: runQuery.test.ts
|
|
1523
1186
|
type: file
|
|
1524
|
-
contents: |
|
|
1525
|
-
// tests/vitest/unit/records/runQuery.test.ts
|
|
1526
|
-
import { describe, test, expect, beforeEach } from "vitest";
|
|
1527
|
-
import {
|
|
1528
|
-
createClient,
|
|
1529
|
-
mockFetch,
|
|
1530
|
-
QB_REALM,
|
|
1531
|
-
QB_USER_TOKEN,
|
|
1532
|
-
QB_TABLE_ID_1,
|
|
1533
|
-
} from "@tests/setup.ts";
|
|
1534
|
-
import { RunQueryRequest, RunQueryResponse } from "@/generated";
|
|
1535
|
-
|
|
1536
|
-
describe("QuickbaseClient Unit - runQuery", () => {
|
|
1537
|
-
let client: ReturnType<typeof createClient>;
|
|
1538
|
-
|
|
1539
|
-
beforeEach(() => {
|
|
1540
|
-
mockFetch.mockClear();
|
|
1541
|
-
client = createClient(mockFetch, { debug: true });
|
|
1542
|
-
});
|
|
1543
|
-
|
|
1544
|
-
test("sends correct POST request and handles successful query response", async () => {
|
|
1545
|
-
const queryRequest: RunQueryRequest = {
|
|
1546
|
-
from: QB_TABLE_ID_1,
|
|
1547
|
-
select: [3, 6, 7],
|
|
1548
|
-
where: "{6.EX.'Task 1'}",
|
|
1549
|
-
sortBy: [{ fieldId: 6, order: "ASC" }],
|
|
1550
|
-
groupBy: [{ fieldId: 7, grouping: "equal-values" }],
|
|
1551
|
-
options: { skip: 0, top: 100, compareWithAppLocalTime: false },
|
|
1552
|
-
};
|
|
1553
|
-
|
|
1554
|
-
const mockResponse: RunQueryResponse = {
|
|
1555
|
-
data: [
|
|
1556
|
-
{ "3": { value: 1 }, "6": { value: "Task 1" }, "7": { value: "High" } },
|
|
1557
|
-
],
|
|
1558
|
-
fields: [
|
|
1559
|
-
{ id: 3, label: "Record ID#", type: "recordid" },
|
|
1560
|
-
{ id: 6, label: "Task Name", type: "text" },
|
|
1561
|
-
{ id: 7, label: "Priority", type: "text" },
|
|
1562
|
-
],
|
|
1563
|
-
metadata: {
|
|
1564
|
-
numFields: 3,
|
|
1565
|
-
numRecords: 1,
|
|
1566
|
-
skip: 0,
|
|
1567
|
-
top: 100,
|
|
1568
|
-
totalRecords: 1,
|
|
1569
|
-
},
|
|
1570
|
-
};
|
|
1571
|
-
|
|
1572
|
-
mockFetch.mockResolvedValueOnce({
|
|
1573
|
-
ok: true,
|
|
1574
|
-
status: 200,
|
|
1575
|
-
json: () => Promise.resolve(mockResponse),
|
|
1576
|
-
});
|
|
1577
|
-
|
|
1578
|
-
const result = await client.runQuery({ body: queryRequest });
|
|
1579
|
-
|
|
1580
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1581
|
-
"https://api.quickbase.com/v1/records/query",
|
|
1582
|
-
expect.objectContaining({
|
|
1583
|
-
method: "POST",
|
|
1584
|
-
headers: expect.objectContaining({
|
|
1585
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1586
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1587
|
-
"Content-Type": "application/json",
|
|
1588
|
-
}),
|
|
1589
|
-
body: JSON.stringify(queryRequest),
|
|
1590
|
-
})
|
|
1591
|
-
);
|
|
1592
|
-
|
|
1593
|
-
expect(result).toEqual(mockResponse);
|
|
1594
|
-
});
|
|
1595
|
-
|
|
1596
|
-
test("handles API error with invalid query", async () => {
|
|
1597
|
-
const invalidQueryRequest = {
|
|
1598
|
-
from: QB_TABLE_ID_1,
|
|
1599
|
-
select: [3],
|
|
1600
|
-
where: "{999.EX.'Invalid'}", // Invalid field ID
|
|
1601
|
-
};
|
|
1602
|
-
|
|
1603
|
-
mockFetch.mockResolvedValueOnce({
|
|
1604
|
-
ok: false,
|
|
1605
|
-
status: 400,
|
|
1606
|
-
json: () => Promise.resolve({ message: "Invalid query" }),
|
|
1607
|
-
});
|
|
1608
|
-
|
|
1609
|
-
await expect(
|
|
1610
|
-
client.runQuery({ body: invalidQueryRequest })
|
|
1611
|
-
).rejects.toThrow("API Error: Invalid query (Status: 400)");
|
|
1612
|
-
|
|
1613
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1614
|
-
"https://api.quickbase.com/v1/records/query",
|
|
1615
|
-
expect.objectContaining({
|
|
1616
|
-
method: "POST",
|
|
1617
|
-
headers: expect.objectContaining({
|
|
1618
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1619
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1620
|
-
"Content-Type": "application/json",
|
|
1621
|
-
}),
|
|
1622
|
-
body: JSON.stringify(invalidQueryRequest),
|
|
1623
|
-
})
|
|
1624
|
-
);
|
|
1625
|
-
});
|
|
1626
|
-
});
|
|
1627
1187
|
- name: upsert.test.ts
|
|
1628
1188
|
type: file
|
|
1629
|
-
contents: |
|
|
1630
|
-
// tests/vitest/unit/records/upsert.test.ts
|
|
1631
|
-
import { describe, expect, test, beforeEach } from "vitest";
|
|
1632
|
-
import {
|
|
1633
|
-
createClient,
|
|
1634
|
-
mockFetch,
|
|
1635
|
-
QB_REALM,
|
|
1636
|
-
QB_USER_TOKEN,
|
|
1637
|
-
QB_TABLE_ID_1,
|
|
1638
|
-
} from "@tests/setup.ts";
|
|
1639
|
-
import { vi } from "vitest";
|
|
1640
|
-
|
|
1641
|
-
// Note: Type augmentation moved to src/types.d.ts
|
|
1642
|
-
describe("QuickbaseClient Unit - upsert", () => {
|
|
1643
|
-
beforeEach(() => {
|
|
1644
|
-
vi.resetModules(); // Reset module cache
|
|
1645
|
-
mockFetch.mockClear();
|
|
1646
|
-
});
|
|
1647
|
-
|
|
1648
|
-
test("sends correct POST request and handles successful upsert", async () => {
|
|
1649
|
-
const qb = createClient(mockFetch, { debug: true });
|
|
1650
|
-
|
|
1651
|
-
mockFetch.mockResolvedValueOnce({
|
|
1652
|
-
ok: true,
|
|
1653
|
-
status: 200,
|
|
1654
|
-
json: () =>
|
|
1655
|
-
Promise.resolve({
|
|
1656
|
-
data: [{ id: 1, "6": { value: "value1" } }],
|
|
1657
|
-
metadata: {
|
|
1658
|
-
createdRecordIds: [1],
|
|
1659
|
-
updatedRecordIds: [],
|
|
1660
|
-
unchangedRecordIds: [],
|
|
1661
|
-
totalNumberOfRecordsProcessed: 1,
|
|
1662
|
-
},
|
|
1663
|
-
}),
|
|
1664
|
-
});
|
|
1665
|
-
|
|
1666
|
-
console.log("[Test] Calling qb.upsert with:", {
|
|
1667
|
-
body: { to: QB_TABLE_ID_1, data: [{ "6": { value: "value1" } }] },
|
|
1668
|
-
});
|
|
1669
|
-
const result = await qb.upsert({
|
|
1670
|
-
body: { to: QB_TABLE_ID_1, data: [{ "6": { value: "value1" } }] },
|
|
1671
|
-
});
|
|
1672
|
-
|
|
1673
|
-
console.log("[Test] mockFetch calls:", mockFetch.mock.calls);
|
|
1674
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1675
|
-
"https://api.quickbase.com/v1/records",
|
|
1676
|
-
expect.objectContaining({
|
|
1677
|
-
method: "POST",
|
|
1678
|
-
headers: expect.objectContaining({
|
|
1679
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1680
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1681
|
-
"Content-Type": "application/json",
|
|
1682
|
-
}),
|
|
1683
|
-
body: JSON.stringify({
|
|
1684
|
-
to: QB_TABLE_ID_1,
|
|
1685
|
-
data: [{ "6": { value: "value1" } }],
|
|
1686
|
-
}),
|
|
1687
|
-
})
|
|
1688
|
-
);
|
|
1689
|
-
|
|
1690
|
-
expect(result).toEqual({
|
|
1691
|
-
data: [{ id: 1, "6": { value: "value1" } }],
|
|
1692
|
-
metadata: {
|
|
1693
|
-
createdRecordIds: [1],
|
|
1694
|
-
updatedRecordIds: [],
|
|
1695
|
-
unchangedRecordIds: [],
|
|
1696
|
-
totalNumberOfRecordsProcessed: 1,
|
|
1697
|
-
},
|
|
1698
|
-
});
|
|
1699
|
-
});
|
|
1700
|
-
|
|
1701
|
-
test("handles API error with invalid data", async () => {
|
|
1702
|
-
const qb = createClient(mockFetch, { debug: true });
|
|
1703
|
-
|
|
1704
|
-
mockFetch.mockResolvedValueOnce({
|
|
1705
|
-
ok: false,
|
|
1706
|
-
status: 400,
|
|
1707
|
-
json: () => Promise.resolve({ message: "Invalid data format" }),
|
|
1708
|
-
});
|
|
1709
|
-
|
|
1710
|
-
await expect(
|
|
1711
|
-
qb.upsert({
|
|
1712
|
-
body: { to: QB_TABLE_ID_1, data: [{ "999": { value: "invalid" } }] },
|
|
1713
|
-
})
|
|
1714
|
-
).rejects.toThrow("API Error: Invalid data format (Status: 400)");
|
|
1715
|
-
|
|
1716
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1717
|
-
"https://api.quickbase.com/v1/records",
|
|
1718
|
-
expect.objectContaining({
|
|
1719
|
-
method: "POST",
|
|
1720
|
-
headers: expect.objectContaining({
|
|
1721
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1722
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1723
|
-
"Content-Type": "application/json",
|
|
1724
|
-
}),
|
|
1725
|
-
body: JSON.stringify({
|
|
1726
|
-
to: QB_TABLE_ID_1,
|
|
1727
|
-
data: [{ "999": { value: "invalid" } }],
|
|
1728
|
-
}),
|
|
1729
|
-
})
|
|
1730
|
-
);
|
|
1731
|
-
});
|
|
1732
|
-
|
|
1733
|
-
test("handles upsert with temp token", async () => {
|
|
1734
|
-
const qb = createClient(mockFetch, { useTempTokens: true, debug: true });
|
|
1735
|
-
|
|
1736
|
-
mockFetch
|
|
1737
|
-
.mockResolvedValueOnce({
|
|
1738
|
-
ok: true,
|
|
1739
|
-
status: 200,
|
|
1740
|
-
json: () => Promise.resolve({ temporaryAuthorization: "temp-token" }),
|
|
1741
|
-
})
|
|
1742
|
-
.mockResolvedValueOnce({
|
|
1743
|
-
ok: true,
|
|
1744
|
-
status: 200,
|
|
1745
|
-
json: () =>
|
|
1746
|
-
Promise.resolve({
|
|
1747
|
-
data: [{ id: 2, "7": { value: "value2" } }],
|
|
1748
|
-
metadata: {
|
|
1749
|
-
createdRecordIds: [2],
|
|
1750
|
-
updatedRecordIds: [],
|
|
1751
|
-
unchangedRecordIds: [],
|
|
1752
|
-
totalNumberOfRecordsProcessed: 1,
|
|
1753
|
-
},
|
|
1754
|
-
}),
|
|
1755
|
-
});
|
|
1756
|
-
|
|
1757
|
-
const result = await qb.upsert({
|
|
1758
|
-
body: { to: QB_TABLE_ID_1, data: [{ "7": { value: "value2" } }] },
|
|
1759
|
-
dbid: QB_TABLE_ID_1, // Use QB_TABLE_ID_1 as dbid for temp token
|
|
1760
|
-
} as any); // Temporary type assertion until types.d.ts is applied
|
|
1761
|
-
|
|
1762
|
-
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
1763
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
1764
|
-
1,
|
|
1765
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
1766
|
-
expect.objectContaining({
|
|
1767
|
-
method: "GET",
|
|
1768
|
-
headers: expect.objectContaining({
|
|
1769
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1770
|
-
"Content-Type": "application/json",
|
|
1771
|
-
}),
|
|
1772
|
-
credentials: "include",
|
|
1773
|
-
})
|
|
1774
|
-
);
|
|
1775
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
1776
|
-
2,
|
|
1777
|
-
"https://api.quickbase.com/v1/records",
|
|
1778
|
-
expect.objectContaining({
|
|
1779
|
-
method: "POST",
|
|
1780
|
-
headers: expect.objectContaining({
|
|
1781
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1782
|
-
Authorization: "QB-TEMP-TOKEN temp-token",
|
|
1783
|
-
"Content-Type": "application/json",
|
|
1784
|
-
}),
|
|
1785
|
-
body: JSON.stringify({
|
|
1786
|
-
to: QB_TABLE_ID_1,
|
|
1787
|
-
data: [{ "7": { value: "value2" } }],
|
|
1788
|
-
}),
|
|
1789
|
-
})
|
|
1790
|
-
);
|
|
1791
|
-
|
|
1792
|
-
expect(result).toEqual({
|
|
1793
|
-
data: [{ id: 2, "7": { value: "value2" } }],
|
|
1794
|
-
metadata: {
|
|
1795
|
-
createdRecordIds: [2],
|
|
1796
|
-
updatedRecordIds: [],
|
|
1797
|
-
unchangedRecordIds: [],
|
|
1798
|
-
totalNumberOfRecordsProcessed: 1,
|
|
1799
|
-
},
|
|
1800
|
-
});
|
|
1801
|
-
});
|
|
1802
|
-
|
|
1803
|
-
test("sends correct POST request with data fields", async () => {
|
|
1804
|
-
const qb = createClient(mockFetch, { debug: true });
|
|
1805
|
-
|
|
1806
|
-
mockFetch.mockResolvedValueOnce({
|
|
1807
|
-
ok: true,
|
|
1808
|
-
status: 200,
|
|
1809
|
-
json: () =>
|
|
1810
|
-
Promise.resolve({
|
|
1811
|
-
data: [{ id: 1, "6": { value: "value1" } }],
|
|
1812
|
-
metadata: {
|
|
1813
|
-
createdRecordIds: [1],
|
|
1814
|
-
updatedRecordIds: [],
|
|
1815
|
-
unchangedRecordIds: [],
|
|
1816
|
-
totalNumberOfRecordsProcessed: 1,
|
|
1817
|
-
},
|
|
1818
|
-
}),
|
|
1819
|
-
});
|
|
1820
|
-
|
|
1821
|
-
const result = await qb.upsert({
|
|
1822
|
-
body: { to: QB_TABLE_ID_1, data: [{ "6": { value: "value1" } }] },
|
|
1823
|
-
});
|
|
1824
|
-
|
|
1825
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1826
|
-
"https://api.quickbase.com/v1/records",
|
|
1827
|
-
expect.objectContaining({
|
|
1828
|
-
method: "POST",
|
|
1829
|
-
headers: expect.objectContaining({
|
|
1830
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1831
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1832
|
-
"Content-Type": "application/json",
|
|
1833
|
-
}),
|
|
1834
|
-
body: JSON.stringify({
|
|
1835
|
-
to: QB_TABLE_ID_1,
|
|
1836
|
-
data: [{ "6": { value: "value1" } }],
|
|
1837
|
-
}),
|
|
1838
|
-
})
|
|
1839
|
-
);
|
|
1840
|
-
|
|
1841
|
-
if (result.data && result.data.length > 0) {
|
|
1842
|
-
expect((result.data[0]["6"] as { value: any }).value).toBe("value1");
|
|
1843
|
-
} else {
|
|
1844
|
-
throw new Error("Expected data array to have at least one element");
|
|
1845
|
-
}
|
|
1846
|
-
});
|
|
1847
|
-
});
|
|
1848
1189
|
- name: tables
|
|
1849
1190
|
type: directory
|
|
1850
1191
|
children:
|
|
1851
1192
|
- name: createTable.test.ts
|
|
1852
1193
|
type: file
|
|
1853
|
-
contents: |
|
|
1854
|
-
// tests/vitest/unit/records/createTable.test.ts
|
|
1855
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
1856
|
-
import {
|
|
1857
|
-
createClient,
|
|
1858
|
-
mockFetch,
|
|
1859
|
-
QB_APP_ID,
|
|
1860
|
-
QB_REALM,
|
|
1861
|
-
QB_USER_TOKEN,
|
|
1862
|
-
} from "@tests/setup.ts";
|
|
1863
|
-
|
|
1864
|
-
describe("QuickbaseClient Unit - createTable", () => {
|
|
1865
|
-
let client: ReturnType<typeof createClient>;
|
|
1866
|
-
|
|
1867
|
-
beforeEach(() => {
|
|
1868
|
-
mockFetch.mockClear();
|
|
1869
|
-
client = createClient(mockFetch, { debug: true });
|
|
1870
|
-
});
|
|
1871
|
-
|
|
1872
|
-
it("calls createTable successfully with user token", async () => {
|
|
1873
|
-
const mockBody = {
|
|
1874
|
-
name: "TestTable",
|
|
1875
|
-
description: "Unit test table",
|
|
1876
|
-
singleRecordName: "Test",
|
|
1877
|
-
pluralRecordName: "Tests",
|
|
1878
|
-
};
|
|
1879
|
-
const mockResponse = {
|
|
1880
|
-
id: "newly-created-table-id-1234567890", // Changed from "buya8h9iz"
|
|
1881
|
-
name: "TestTable",
|
|
1882
|
-
alias: "_DBID_TESTTABLE",
|
|
1883
|
-
description: "Unit test table",
|
|
1884
|
-
created: new Date("2025-03-10T21:00:00.000Z"),
|
|
1885
|
-
updated: new Date("2025-03-10T21:00:00.000Z"),
|
|
1886
|
-
nextRecordId: 1,
|
|
1887
|
-
nextFieldId: 6,
|
|
1888
|
-
defaultSortFieldId: 2,
|
|
1889
|
-
defaultSortOrder: "DESC",
|
|
1890
|
-
keyFieldId: 3,
|
|
1891
|
-
singleRecordName: "Test",
|
|
1892
|
-
pluralRecordName: "Tests",
|
|
1893
|
-
sizeLimit: "500 MB",
|
|
1894
|
-
spaceUsed: "0 KB",
|
|
1895
|
-
spaceRemaining: "500 MB",
|
|
1896
|
-
};
|
|
1897
|
-
|
|
1898
|
-
mockFetch.mockResolvedValueOnce({
|
|
1899
|
-
ok: true,
|
|
1900
|
-
status: 200,
|
|
1901
|
-
json: () => Promise.resolve(mockResponse),
|
|
1902
|
-
});
|
|
1903
|
-
|
|
1904
|
-
const response = await client.createTable({
|
|
1905
|
-
appId: QB_APP_ID,
|
|
1906
|
-
body: mockBody,
|
|
1907
|
-
});
|
|
1908
|
-
expect(response).toEqual(mockResponse);
|
|
1909
|
-
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
1910
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1911
|
-
`https://api.quickbase.com/v1/tables?appId=${QB_APP_ID}`,
|
|
1912
|
-
expect.objectContaining({
|
|
1913
|
-
method: "POST",
|
|
1914
|
-
headers: expect.objectContaining({
|
|
1915
|
-
"Content-Type": "application/json",
|
|
1916
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1917
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1918
|
-
}),
|
|
1919
|
-
body: JSON.stringify(mockBody),
|
|
1920
|
-
})
|
|
1921
|
-
);
|
|
1922
|
-
});
|
|
1923
|
-
});
|
|
1924
1194
|
- name: deleteTable.test.ts
|
|
1925
1195
|
type: file
|
|
1926
|
-
contents: |
|
|
1927
|
-
// tests/vitest/unit/records/deleteTable.test.ts
|
|
1928
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
1929
|
-
import {
|
|
1930
|
-
createClient,
|
|
1931
|
-
mockFetch,
|
|
1932
|
-
QB_APP_ID,
|
|
1933
|
-
QB_TABLE_ID_1,
|
|
1934
|
-
QB_REALM,
|
|
1935
|
-
QB_USER_TOKEN,
|
|
1936
|
-
} from "@tests/setup.ts";
|
|
1937
|
-
|
|
1938
|
-
describe("QuickbaseClient Unit - deleteTable", () => {
|
|
1939
|
-
let client: ReturnType<typeof createClient>;
|
|
1940
|
-
|
|
1941
|
-
beforeEach(() => {
|
|
1942
|
-
mockFetch.mockClear();
|
|
1943
|
-
client = createClient(mockFetch, { debug: true });
|
|
1944
|
-
});
|
|
1945
|
-
|
|
1946
|
-
it("calls deleteTable successfully with user token", async () => {
|
|
1947
|
-
const mockResponse = {
|
|
1948
|
-
deletedTableId: QB_TABLE_ID_1,
|
|
1949
|
-
};
|
|
1950
|
-
|
|
1951
|
-
mockFetch.mockResolvedValueOnce({
|
|
1952
|
-
ok: true,
|
|
1953
|
-
status: 200,
|
|
1954
|
-
json: () => Promise.resolve(mockResponse),
|
|
1955
|
-
});
|
|
1956
|
-
|
|
1957
|
-
const response = await client.deleteTable({
|
|
1958
|
-
tableId: QB_TABLE_ID_1,
|
|
1959
|
-
appId: QB_APP_ID,
|
|
1960
|
-
});
|
|
1961
|
-
expect(response).toEqual(mockResponse);
|
|
1962
|
-
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
1963
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1964
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_1}?appId=${QB_APP_ID}`,
|
|
1965
|
-
expect.objectContaining({
|
|
1966
|
-
method: "DELETE",
|
|
1967
|
-
headers: expect.objectContaining({
|
|
1968
|
-
"Content-Type": "application/json",
|
|
1969
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1970
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1971
|
-
}),
|
|
1972
|
-
})
|
|
1973
|
-
);
|
|
1974
|
-
});
|
|
1975
|
-
|
|
1976
|
-
it("handles 404 error for non-existent table", async () => {
|
|
1977
|
-
mockFetch.mockResolvedValueOnce({
|
|
1978
|
-
ok: false,
|
|
1979
|
-
status: 404,
|
|
1980
|
-
json: () =>
|
|
1981
|
-
Promise.resolve({
|
|
1982
|
-
message: "Invalid DBID",
|
|
1983
|
-
description: "Table not found in app.",
|
|
1984
|
-
}),
|
|
1985
|
-
});
|
|
1986
|
-
|
|
1987
|
-
await expect(
|
|
1988
|
-
client.deleteTable({ tableId: QB_TABLE_ID_1, appId: QB_APP_ID })
|
|
1989
|
-
).rejects.toThrow("API Error: Invalid DBID (Status: 404)");
|
|
1990
|
-
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
1991
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
1992
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_1}?appId=${QB_APP_ID}`,
|
|
1993
|
-
expect.objectContaining({
|
|
1994
|
-
method: "DELETE",
|
|
1995
|
-
headers: expect.objectContaining({
|
|
1996
|
-
"Content-Type": "application/json",
|
|
1997
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
1998
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
1999
|
-
}),
|
|
2000
|
-
})
|
|
2001
|
-
);
|
|
2002
|
-
});
|
|
2003
|
-
});
|
|
2004
1196
|
- name: getAppTables.test.ts
|
|
2005
1197
|
type: file
|
|
2006
|
-
contents: |
|
|
2007
|
-
// tests/vitest/unit/tables/getAppTables.test.ts
|
|
2008
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2009
|
-
import {
|
|
2010
|
-
createClient,
|
|
2011
|
-
mockFetch,
|
|
2012
|
-
QB_APP_ID,
|
|
2013
|
-
QB_REALM,
|
|
2014
|
-
QB_USER_TOKEN,
|
|
2015
|
-
} from "@tests/setup.ts";
|
|
2016
|
-
|
|
2017
|
-
describe("QuickbaseClient Unit - getAppTables", () => {
|
|
2018
|
-
let client: ReturnType<typeof createClient>;
|
|
2019
|
-
|
|
2020
|
-
beforeEach(() => {
|
|
2021
|
-
mockFetch.mockClear();
|
|
2022
|
-
client = createClient(mockFetch, { debug: true }); // Add debug: true for consistency
|
|
2023
|
-
});
|
|
2024
|
-
|
|
2025
|
-
it("calls getAppTables successfully", async () => {
|
|
2026
|
-
mockFetch.mockImplementation((url: string, options: any) => {
|
|
2027
|
-
console.log("Mock fetch for getAppTables:", url, options);
|
|
2028
|
-
return Promise.resolve({
|
|
2029
|
-
ok: true,
|
|
2030
|
-
status: 200,
|
|
2031
|
-
json: () =>
|
|
2032
|
-
Promise.resolve([
|
|
2033
|
-
{
|
|
2034
|
-
id: "buwai2zr4",
|
|
2035
|
-
name: "Root",
|
|
2036
|
-
alias: "_DBID_ROOT",
|
|
2037
|
-
description: "",
|
|
2038
|
-
created: new Date("2025-02-13T18:22:33.000Z"),
|
|
2039
|
-
updated: new Date("2025-02-13T18:22:34.000Z"),
|
|
2040
|
-
nextRecordId: 1,
|
|
2041
|
-
nextFieldId: 6,
|
|
2042
|
-
defaultSortFieldId: 2,
|
|
2043
|
-
defaultSortOrder: "DESC",
|
|
2044
|
-
keyFieldId: 3,
|
|
2045
|
-
singleRecordName: "Root",
|
|
2046
|
-
pluralRecordName: "Roots",
|
|
2047
|
-
sizeLimit: "500 MB",
|
|
2048
|
-
spaceUsed: "0 KB",
|
|
2049
|
-
spaceRemaining: "500 MB",
|
|
2050
|
-
},
|
|
2051
|
-
]),
|
|
2052
|
-
} as Response);
|
|
2053
|
-
});
|
|
2054
|
-
|
|
2055
|
-
const result = await client.getAppTables({ appId: QB_APP_ID });
|
|
2056
|
-
console.log("getAppTables response:", result);
|
|
2057
|
-
expect(result).toEqual(
|
|
2058
|
-
expect.arrayContaining([
|
|
2059
|
-
expect.objectContaining({
|
|
2060
|
-
id: "buwai2zr4",
|
|
2061
|
-
name: "Root",
|
|
2062
|
-
alias: "_DBID_ROOT",
|
|
2063
|
-
description: "",
|
|
2064
|
-
created: new Date("2025-02-13T18:22:33.000Z"),
|
|
2065
|
-
updated: new Date("2025-02-13T18:22:34.000Z"),
|
|
2066
|
-
nextRecordId: 1,
|
|
2067
|
-
nextFieldId: 6,
|
|
2068
|
-
defaultSortFieldId: 2,
|
|
2069
|
-
defaultSortOrder: "DESC",
|
|
2070
|
-
keyFieldId: 3,
|
|
2071
|
-
singleRecordName: "Root",
|
|
2072
|
-
pluralRecordName: "Roots",
|
|
2073
|
-
sizeLimit: "500 MB",
|
|
2074
|
-
spaceUsed: "0 KB",
|
|
2075
|
-
spaceRemaining: "500 MB",
|
|
2076
|
-
}),
|
|
2077
|
-
])
|
|
2078
|
-
);
|
|
2079
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2080
|
-
`https://api.quickbase.com/v1/tables?appId=${QB_APP_ID}`,
|
|
2081
|
-
expect.objectContaining({
|
|
2082
|
-
method: "GET",
|
|
2083
|
-
headers: expect.objectContaining({
|
|
2084
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
2085
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
2086
|
-
"Content-Type": "application/json",
|
|
2087
|
-
}),
|
|
2088
|
-
})
|
|
2089
|
-
);
|
|
2090
|
-
});
|
|
2091
|
-
});
|
|
2092
1198
|
- name: getRelationships.test.ts
|
|
2093
1199
|
type: file
|
|
2094
|
-
contents: |
|
|
2095
|
-
// tests/vitest/unit/relationships/getRelationships.test.ts
|
|
2096
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2097
|
-
import {
|
|
2098
|
-
createClient,
|
|
2099
|
-
mockFetch,
|
|
2100
|
-
QB_REALM,
|
|
2101
|
-
QB_USER_TOKEN,
|
|
2102
|
-
QB_TABLE_ID_1,
|
|
2103
|
-
QB_TABLE_ID_2,
|
|
2104
|
-
} from "@tests/setup.ts";
|
|
2105
|
-
import { GetRelationships200Response } from "/home/drew/Projects/quickbase-js/src/generated/models";
|
|
2106
|
-
|
|
2107
|
-
describe("QuickbaseClient Unit - getRelationships", () => {
|
|
2108
|
-
let client: ReturnType<typeof createClient>;
|
|
2109
|
-
|
|
2110
|
-
beforeEach(() => {
|
|
2111
|
-
mockFetch.mockClear();
|
|
2112
|
-
client = createClient(mockFetch, { debug: true }); // Consistent with output
|
|
2113
|
-
});
|
|
2114
|
-
|
|
2115
|
-
it("initializes without errors", () => {
|
|
2116
|
-
expect(client).toBeDefined();
|
|
2117
|
-
});
|
|
2118
|
-
|
|
2119
|
-
it("has getRelationships method", () => {
|
|
2120
|
-
expect(typeof client.getRelationships).toBe("function");
|
|
2121
|
-
});
|
|
2122
|
-
|
|
2123
|
-
it("sends correct GET request and handles successful response", async () => {
|
|
2124
|
-
const mockResponse: GetRelationships200Response = {
|
|
2125
|
-
metadata: {
|
|
2126
|
-
numRelationships: 1,
|
|
2127
|
-
skip: 0,
|
|
2128
|
-
totalRelationships: 1,
|
|
2129
|
-
},
|
|
2130
|
-
relationships: [
|
|
2131
|
-
{
|
|
2132
|
-
id: 6,
|
|
2133
|
-
parentTableId: QB_TABLE_ID_1, // Parent table
|
|
2134
|
-
childTableId: QB_TABLE_ID_2, // Child table
|
|
2135
|
-
foreignKeyField: {
|
|
2136
|
-
id: 6,
|
|
2137
|
-
label: "Related record",
|
|
2138
|
-
type: "numeric",
|
|
2139
|
-
},
|
|
2140
|
-
isCrossApp: false,
|
|
2141
|
-
lookupFields: [
|
|
2142
|
-
{
|
|
2143
|
-
id: 3,
|
|
2144
|
-
label: "Name",
|
|
2145
|
-
type: "text",
|
|
2146
|
-
},
|
|
2147
|
-
],
|
|
2148
|
-
summaryFields: [
|
|
2149
|
-
{
|
|
2150
|
-
id: 8,
|
|
2151
|
-
label: "my summary field",
|
|
2152
|
-
type: "numeric",
|
|
2153
|
-
},
|
|
2154
|
-
],
|
|
2155
|
-
},
|
|
2156
|
-
],
|
|
2157
|
-
};
|
|
2158
|
-
|
|
2159
|
-
mockFetch.mockResolvedValueOnce({
|
|
2160
|
-
ok: true,
|
|
2161
|
-
status: 200,
|
|
2162
|
-
json: () => Promise.resolve(mockResponse),
|
|
2163
|
-
});
|
|
2164
|
-
|
|
2165
|
-
const result = await client.getRelationships({
|
|
2166
|
-
tableId: QB_TABLE_ID_2,
|
|
2167
|
-
skip: 0,
|
|
2168
|
-
});
|
|
2169
|
-
|
|
2170
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2171
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_2}/relationships?skip=0`,
|
|
2172
|
-
expect.objectContaining({
|
|
2173
|
-
method: "GET",
|
|
2174
|
-
headers: expect.objectContaining({
|
|
2175
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
2176
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
2177
|
-
"Content-Type": "application/json",
|
|
2178
|
-
}),
|
|
2179
|
-
})
|
|
2180
|
-
);
|
|
2181
|
-
|
|
2182
|
-
expect(result).toEqual(mockResponse);
|
|
2183
|
-
expect(result.metadata.numRelationships).toBe(1);
|
|
2184
|
-
expect(result.relationships).toHaveLength(1);
|
|
2185
|
-
expect(result.relationships[0].id).toBe(6);
|
|
2186
|
-
expect(result.relationships[0].foreignKeyField.label).toBe(
|
|
2187
|
-
"Related record"
|
|
2188
|
-
);
|
|
2189
|
-
});
|
|
2190
|
-
|
|
2191
|
-
it("handles request without skip parameter", async () => {
|
|
2192
|
-
const mockResponse: GetRelationships200Response = {
|
|
2193
|
-
metadata: {
|
|
2194
|
-
numRelationships: 1,
|
|
2195
|
-
skip: 0,
|
|
2196
|
-
totalRelationships: 1,
|
|
2197
|
-
},
|
|
2198
|
-
relationships: [
|
|
2199
|
-
{
|
|
2200
|
-
id: 6,
|
|
2201
|
-
parentTableId: QB_TABLE_ID_1,
|
|
2202
|
-
childTableId: QB_TABLE_ID_2,
|
|
2203
|
-
foreignKeyField: {
|
|
2204
|
-
id: 6,
|
|
2205
|
-
label: "Related record",
|
|
2206
|
-
type: "numeric",
|
|
2207
|
-
},
|
|
2208
|
-
isCrossApp: false,
|
|
2209
|
-
lookupFields: [],
|
|
2210
|
-
summaryFields: [],
|
|
2211
|
-
},
|
|
2212
|
-
],
|
|
2213
|
-
};
|
|
2214
|
-
|
|
2215
|
-
mockFetch.mockResolvedValueOnce({
|
|
2216
|
-
ok: true,
|
|
2217
|
-
status: 200,
|
|
2218
|
-
json: () => Promise.resolve(mockResponse),
|
|
2219
|
-
});
|
|
2220
|
-
|
|
2221
|
-
const result = await client.getRelationships({
|
|
2222
|
-
tableId: QB_TABLE_ID_2, // Corrected from QB_TABLE_ID_1
|
|
2223
|
-
});
|
|
2224
|
-
|
|
2225
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2226
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_2}/relationships`,
|
|
2227
|
-
expect.objectContaining({
|
|
2228
|
-
method: "GET",
|
|
2229
|
-
headers: expect.objectContaining({
|
|
2230
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
2231
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
2232
|
-
"Content-Type": "application/json",
|
|
2233
|
-
}),
|
|
2234
|
-
})
|
|
2235
|
-
);
|
|
2236
|
-
|
|
2237
|
-
expect(result).toEqual(mockResponse);
|
|
2238
|
-
});
|
|
2239
|
-
|
|
2240
|
-
it("handles API error response", async () => {
|
|
2241
|
-
mockFetch.mockResolvedValueOnce({
|
|
2242
|
-
ok: false,
|
|
2243
|
-
status: 404,
|
|
2244
|
-
json: () => Promise.resolve({ message: "Table not found" }),
|
|
2245
|
-
});
|
|
2246
|
-
|
|
2247
|
-
await expect(
|
|
2248
|
-
client.getRelationships({
|
|
2249
|
-
tableId: QB_TABLE_ID_2, // Corrected from QB_TABLE_ID_1
|
|
2250
|
-
skip: 0,
|
|
2251
|
-
})
|
|
2252
|
-
).rejects.toThrow("API Error: Table not found (Status: 404)");
|
|
2253
|
-
|
|
2254
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2255
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_2}/relationships?skip=0`,
|
|
2256
|
-
expect.objectContaining({
|
|
2257
|
-
method: "GET",
|
|
2258
|
-
headers: expect.objectContaining({
|
|
2259
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
2260
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
2261
|
-
"Content-Type": "application/json",
|
|
2262
|
-
}),
|
|
2263
|
-
})
|
|
2264
|
-
);
|
|
2265
|
-
});
|
|
2266
|
-
|
|
2267
|
-
it("retries with temp token after 401", async () => {
|
|
2268
|
-
const mockResponse: GetRelationships200Response = {
|
|
2269
|
-
metadata: {
|
|
2270
|
-
numRelationships: 1,
|
|
2271
|
-
skip: 0,
|
|
2272
|
-
totalRelationships: 1,
|
|
2273
|
-
},
|
|
2274
|
-
relationships: [
|
|
2275
|
-
{
|
|
2276
|
-
id: 6,
|
|
2277
|
-
parentTableId: QB_TABLE_ID_1,
|
|
2278
|
-
childTableId: QB_TABLE_ID_2,
|
|
2279
|
-
foreignKeyField: {
|
|
2280
|
-
id: 6,
|
|
2281
|
-
label: "Related record",
|
|
2282
|
-
type: "numeric",
|
|
2283
|
-
},
|
|
2284
|
-
isCrossApp: false,
|
|
2285
|
-
},
|
|
2286
|
-
],
|
|
2287
|
-
};
|
|
2288
|
-
|
|
2289
|
-
mockFetch
|
|
2290
|
-
.mockResolvedValueOnce({
|
|
2291
|
-
ok: true,
|
|
2292
|
-
status: 200,
|
|
2293
|
-
json: () =>
|
|
2294
|
-
Promise.resolve({ temporaryAuthorization: "initial_token" }),
|
|
2295
|
-
})
|
|
2296
|
-
.mockResolvedValueOnce({
|
|
2297
|
-
ok: false,
|
|
2298
|
-
status: 401,
|
|
2299
|
-
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
2300
|
-
})
|
|
2301
|
-
.mockResolvedValueOnce({
|
|
2302
|
-
ok: true,
|
|
2303
|
-
status: 200,
|
|
2304
|
-
json: () => Promise.resolve({ temporaryAuthorization: "new_token" }),
|
|
2305
|
-
})
|
|
2306
|
-
.mockResolvedValueOnce({
|
|
2307
|
-
ok: true,
|
|
2308
|
-
status: 200,
|
|
2309
|
-
json: () => Promise.resolve(mockResponse),
|
|
2310
|
-
});
|
|
2311
|
-
|
|
2312
|
-
const tempClient = createClient(mockFetch, {
|
|
2313
|
-
useTempTokens: true,
|
|
2314
|
-
debug: true,
|
|
2315
|
-
});
|
|
2316
|
-
const result = await tempClient.getRelationships({
|
|
2317
|
-
tableId: QB_TABLE_ID_2,
|
|
2318
|
-
skip: 0,
|
|
2319
|
-
});
|
|
2320
|
-
|
|
2321
|
-
expect(mockFetch).toHaveBeenCalledTimes(4);
|
|
2322
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
2323
|
-
1,
|
|
2324
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_2}`,
|
|
2325
|
-
expect.any(Object)
|
|
2326
|
-
);
|
|
2327
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
2328
|
-
2,
|
|
2329
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_2}/relationships?skip=0`,
|
|
2330
|
-
expect.objectContaining({
|
|
2331
|
-
headers: expect.objectContaining({
|
|
2332
|
-
Authorization: "QB-TEMP-TOKEN initial_token",
|
|
2333
|
-
}),
|
|
2334
|
-
})
|
|
2335
|
-
);
|
|
2336
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
2337
|
-
3,
|
|
2338
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_2}`,
|
|
2339
|
-
expect.any(Object)
|
|
2340
|
-
);
|
|
2341
|
-
expect(mockFetch).toHaveBeenNthCalledWith(
|
|
2342
|
-
4,
|
|
2343
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_2}/relationships?skip=0`,
|
|
2344
|
-
expect.objectContaining({
|
|
2345
|
-
headers: expect.objectContaining({
|
|
2346
|
-
Authorization: "QB-TEMP-TOKEN new_token",
|
|
2347
|
-
}),
|
|
2348
|
-
})
|
|
2349
|
-
);
|
|
2350
|
-
|
|
2351
|
-
expect(result).toEqual(mockResponse);
|
|
2352
|
-
});
|
|
2353
|
-
});
|
|
2354
1200
|
- name: getTable.test.ts
|
|
2355
1201
|
type: file
|
|
2356
|
-
contents: |
|
|
2357
|
-
// tests/vitest/unit/tables/getTable.test.ts
|
|
2358
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2359
|
-
import {
|
|
2360
|
-
createClient,
|
|
2361
|
-
mockFetch,
|
|
2362
|
-
QB_APP_ID,
|
|
2363
|
-
QB_TABLE_ID_1,
|
|
2364
|
-
QB_REALM,
|
|
2365
|
-
QB_USER_TOKEN,
|
|
2366
|
-
} from "@tests/setup.ts";
|
|
2367
|
-
|
|
2368
|
-
describe("QuickbaseClient Unit - getTable", () => {
|
|
2369
|
-
let client: ReturnType<typeof createClient>;
|
|
2370
|
-
|
|
2371
|
-
beforeEach(() => {
|
|
2372
|
-
mockFetch.mockClear();
|
|
2373
|
-
});
|
|
2374
|
-
|
|
2375
|
-
it("calls getTable successfully with user token", async () => {
|
|
2376
|
-
client = createClient(mockFetch, { debug: true });
|
|
2377
|
-
|
|
2378
|
-
const mockResponse = {
|
|
2379
|
-
id: QB_TABLE_ID_1,
|
|
2380
|
-
name: "Root",
|
|
2381
|
-
alias: "_DBID_ROOT",
|
|
2382
|
-
description: "",
|
|
2383
|
-
created: new Date("2025-02-13T18:22:33.000Z"),
|
|
2384
|
-
updated: new Date("2025-02-13T18:22:34.000Z"),
|
|
2385
|
-
nextRecordId: 1,
|
|
2386
|
-
nextFieldId: 6,
|
|
2387
|
-
defaultSortFieldId: 2,
|
|
2388
|
-
defaultSortOrder: "DESC",
|
|
2389
|
-
keyFieldId: 3,
|
|
2390
|
-
singleRecordName: "Root",
|
|
2391
|
-
pluralRecordName: "Roots",
|
|
2392
|
-
sizeLimit: "500 MB",
|
|
2393
|
-
spaceUsed: "0 KB",
|
|
2394
|
-
spaceRemaining: "500 MB",
|
|
2395
|
-
};
|
|
2396
|
-
|
|
2397
|
-
mockFetch.mockResolvedValueOnce({
|
|
2398
|
-
ok: true,
|
|
2399
|
-
status: 200,
|
|
2400
|
-
json: () => Promise.resolve(mockResponse),
|
|
2401
|
-
});
|
|
2402
|
-
|
|
2403
|
-
const response = await client.getTable({
|
|
2404
|
-
tableId: QB_TABLE_ID_1,
|
|
2405
|
-
appId: QB_APP_ID,
|
|
2406
|
-
});
|
|
2407
|
-
expect(response).toEqual(mockResponse);
|
|
2408
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2409
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_1}?appId=${QB_APP_ID}`,
|
|
2410
|
-
expect.objectContaining({
|
|
2411
|
-
method: "GET",
|
|
2412
|
-
headers: expect.objectContaining({
|
|
2413
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
2414
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
2415
|
-
}),
|
|
2416
|
-
})
|
|
2417
|
-
);
|
|
2418
|
-
});
|
|
2419
|
-
|
|
2420
|
-
it("calls getTable successfully with temp token", async () => {
|
|
2421
|
-
client = createClient(mockFetch, { debug: true, useTempTokens: true });
|
|
2422
|
-
|
|
2423
|
-
const mockResponse = {
|
|
2424
|
-
id: QB_TABLE_ID_1,
|
|
2425
|
-
name: "Root",
|
|
2426
|
-
alias: "_DBID_ROOT",
|
|
2427
|
-
description: "",
|
|
2428
|
-
created: new Date("2025-02-13T18:22:33.000Z"),
|
|
2429
|
-
updated: new Date("2025-02-13T18:22:34.000Z"),
|
|
2430
|
-
nextRecordId: 1,
|
|
2431
|
-
nextFieldId: 6,
|
|
2432
|
-
defaultSortFieldId: 2,
|
|
2433
|
-
defaultSortOrder: "DESC",
|
|
2434
|
-
keyFieldId: 3,
|
|
2435
|
-
singleRecordName: "Root",
|
|
2436
|
-
pluralRecordName: "Roots",
|
|
2437
|
-
sizeLimit: "500 MB",
|
|
2438
|
-
spaceUsed: "0 KB",
|
|
2439
|
-
spaceRemaining: "500 MB",
|
|
2440
|
-
};
|
|
2441
|
-
|
|
2442
|
-
mockFetch
|
|
2443
|
-
.mockResolvedValueOnce({
|
|
2444
|
-
ok: true,
|
|
2445
|
-
status: 200,
|
|
2446
|
-
json: () => Promise.resolve({ temporaryAuthorization: "temp_token" }),
|
|
2447
|
-
})
|
|
2448
|
-
.mockResolvedValueOnce({
|
|
2449
|
-
ok: true,
|
|
2450
|
-
status: 200,
|
|
2451
|
-
json: () => Promise.resolve(mockResponse),
|
|
2452
|
-
});
|
|
2453
|
-
|
|
2454
|
-
const response = await client.getTable({
|
|
2455
|
-
tableId: QB_TABLE_ID_1,
|
|
2456
|
-
appId: QB_APP_ID,
|
|
2457
|
-
});
|
|
2458
|
-
expect(response).toEqual(mockResponse);
|
|
2459
|
-
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
2460
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2461
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
2462
|
-
expect.any(Object)
|
|
2463
|
-
);
|
|
2464
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2465
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_1}?appId=${QB_APP_ID}`,
|
|
2466
|
-
expect.objectContaining({
|
|
2467
|
-
headers: expect.objectContaining({
|
|
2468
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
2469
|
-
Authorization: "QB-TEMP-TOKEN temp_token",
|
|
2470
|
-
}),
|
|
2471
|
-
})
|
|
2472
|
-
);
|
|
2473
|
-
});
|
|
2474
|
-
|
|
2475
|
-
it("retries successfully after 401 with temp token", async () => {
|
|
2476
|
-
client = createClient(mockFetch, { debug: true, useTempTokens: true });
|
|
2477
|
-
|
|
2478
|
-
const mockResponse = {
|
|
2479
|
-
id: QB_TABLE_ID_1,
|
|
2480
|
-
name: "Root",
|
|
2481
|
-
alias: "_DBID_ROOT",
|
|
2482
|
-
description: "",
|
|
2483
|
-
created: new Date("2025-02-13T18:22:33.000Z"),
|
|
2484
|
-
updated: new Date("2025-02-13T18:22:34.000Z"),
|
|
2485
|
-
nextRecordId: 1,
|
|
2486
|
-
nextFieldId: 6,
|
|
2487
|
-
defaultSortFieldId: 2,
|
|
2488
|
-
defaultSortOrder: "DESC",
|
|
2489
|
-
keyFieldId: 3,
|
|
2490
|
-
singleRecordName: "Root",
|
|
2491
|
-
pluralRecordName: "Roots",
|
|
2492
|
-
sizeLimit: "500 MB",
|
|
2493
|
-
spaceUsed: "0 KB",
|
|
2494
|
-
spaceRemaining: "500 MB",
|
|
2495
|
-
};
|
|
2496
|
-
|
|
2497
|
-
mockFetch
|
|
2498
|
-
.mockResolvedValueOnce({
|
|
2499
|
-
ok: true,
|
|
2500
|
-
status: 200,
|
|
2501
|
-
json: () =>
|
|
2502
|
-
Promise.resolve({ temporaryAuthorization: "initial_token" }),
|
|
2503
|
-
})
|
|
2504
|
-
.mockResolvedValueOnce({
|
|
2505
|
-
ok: false,
|
|
2506
|
-
status: 401,
|
|
2507
|
-
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
2508
|
-
})
|
|
2509
|
-
.mockResolvedValueOnce({
|
|
2510
|
-
ok: true,
|
|
2511
|
-
status: 200,
|
|
2512
|
-
json: () => Promise.resolve({ temporaryAuthorization: "new_token" }),
|
|
2513
|
-
})
|
|
2514
|
-
.mockResolvedValueOnce({
|
|
2515
|
-
ok: true,
|
|
2516
|
-
status: 200,
|
|
2517
|
-
json: () => Promise.resolve(mockResponse),
|
|
2518
|
-
});
|
|
2519
|
-
|
|
2520
|
-
const response = await client.getTable({
|
|
2521
|
-
tableId: QB_TABLE_ID_1,
|
|
2522
|
-
appId: QB_APP_ID,
|
|
2523
|
-
});
|
|
2524
|
-
expect(response).toEqual(mockResponse);
|
|
2525
|
-
expect(mockFetch).toHaveBeenCalledTimes(4);
|
|
2526
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2527
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_1}?appId=${QB_APP_ID}`,
|
|
2528
|
-
expect.objectContaining({
|
|
2529
|
-
headers: expect.objectContaining({
|
|
2530
|
-
Authorization: "QB-TEMP-TOKEN new_token",
|
|
2531
|
-
}),
|
|
2532
|
-
})
|
|
2533
|
-
);
|
|
2534
|
-
});
|
|
2535
|
-
|
|
2536
|
-
it("handles 401 with failed temp token retry", async () => {
|
|
2537
|
-
client = createClient(mockFetch, { debug: true, useTempTokens: true });
|
|
2538
|
-
|
|
2539
|
-
mockFetch
|
|
2540
|
-
.mockResolvedValueOnce({
|
|
2541
|
-
ok: true,
|
|
2542
|
-
status: 200,
|
|
2543
|
-
json: () =>
|
|
2544
|
-
Promise.resolve({ temporaryAuthorization: "initial_token" }),
|
|
2545
|
-
})
|
|
2546
|
-
.mockResolvedValueOnce({
|
|
2547
|
-
ok: false,
|
|
2548
|
-
status: 401,
|
|
2549
|
-
json: () => Promise.resolve({ message: "Unauthorized" }),
|
|
2550
|
-
})
|
|
2551
|
-
.mockResolvedValueOnce({
|
|
2552
|
-
ok: false,
|
|
2553
|
-
status: 401,
|
|
2554
|
-
json: () =>
|
|
2555
|
-
Promise.resolve({ message: "Unauthorized in fetchTempToken" }),
|
|
2556
|
-
});
|
|
2557
|
-
|
|
2558
|
-
await expect(
|
|
2559
|
-
client.getTable({ tableId: QB_TABLE_ID_1, appId: QB_APP_ID })
|
|
2560
|
-
).rejects.toThrow(
|
|
2561
|
-
"API Error: Unauthorized in fetchTempToken (Status: 401)"
|
|
2562
|
-
);
|
|
2563
|
-
expect(mockFetch).toHaveBeenCalledTimes(3);
|
|
2564
|
-
});
|
|
2565
|
-
|
|
2566
|
-
it("handles 404 Not Found", async () => {
|
|
2567
|
-
client = createClient(mockFetch, { debug: true });
|
|
2568
|
-
|
|
2569
|
-
mockFetch.mockResolvedValueOnce({
|
|
2570
|
-
ok: false,
|
|
2571
|
-
status: 404,
|
|
2572
|
-
json: () => Promise.resolve({ message: "Table not found" }),
|
|
2573
|
-
});
|
|
2574
|
-
|
|
2575
|
-
await expect(
|
|
2576
|
-
client.getTable({ tableId: QB_TABLE_ID_1, appId: QB_APP_ID })
|
|
2577
|
-
).rejects.toThrow("API Error: Table not found (Status: 404)");
|
|
2578
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2579
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_1}?appId=${QB_APP_ID}`,
|
|
2580
|
-
expect.objectContaining({
|
|
2581
|
-
method: "GET",
|
|
2582
|
-
headers: expect.objectContaining({
|
|
2583
|
-
"QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
|
|
2584
|
-
Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
|
|
2585
|
-
}),
|
|
2586
|
-
})
|
|
2587
|
-
);
|
|
2588
|
-
});
|
|
2589
|
-
});
|
|
2590
1202
|
- name: updateTable.test.ts
|
|
2591
1203
|
type: file
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
import { describe, it, expect, beforeEach } from "vitest";
|
|
2595
|
-
import {
|
|
2596
|
-
createClient,
|
|
2597
|
-
mockFetch,
|
|
2598
|
-
QB_APP_ID,
|
|
2599
|
-
QB_TABLE_ID_1,
|
|
2600
|
-
} from "@tests/setup.ts";
|
|
2601
|
-
|
|
2602
|
-
describe("QuickbaseClient Unit - updateTable", () => {
|
|
2603
|
-
let client: ReturnType<typeof createClient>;
|
|
2604
|
-
|
|
2605
|
-
beforeEach(() => {
|
|
2606
|
-
mockFetch.mockClear();
|
|
2607
|
-
client = createClient(mockFetch, { debug: true });
|
|
2608
|
-
});
|
|
2609
|
-
|
|
2610
|
-
it("calls updateTable successfully with temp token", async () => {
|
|
2611
|
-
client = createClient(mockFetch, { debug: true, useTempTokens: true });
|
|
2612
|
-
|
|
2613
|
-
const mockBody = {
|
|
2614
|
-
name: "Updated Root",
|
|
2615
|
-
description: "Updated description",
|
|
2616
|
-
};
|
|
2617
|
-
const mockResponse = {
|
|
2618
|
-
id: QB_TABLE_ID_1,
|
|
2619
|
-
name: "Updated Root",
|
|
2620
|
-
alias: "_DBID_ROOT",
|
|
2621
|
-
description: "Updated description",
|
|
2622
|
-
created: new Date("2025-02-13T18:22:33.000Z"),
|
|
2623
|
-
updated: new Date("2025-03-10T12:00:00.000Z"),
|
|
2624
|
-
nextRecordId: 1,
|
|
2625
|
-
nextFieldId: 6,
|
|
2626
|
-
defaultSortFieldId: 2,
|
|
2627
|
-
defaultSortOrder: "DESC",
|
|
2628
|
-
keyFieldId: 3,
|
|
2629
|
-
singleRecordName: "Root",
|
|
2630
|
-
pluralRecordName: "Roots",
|
|
2631
|
-
sizeLimit: "500 MB",
|
|
2632
|
-
spaceUsed: "0 KB",
|
|
2633
|
-
spaceRemaining: "500 MB",
|
|
2634
|
-
};
|
|
2635
|
-
|
|
2636
|
-
mockFetch
|
|
2637
|
-
.mockResolvedValueOnce({
|
|
2638
|
-
ok: true,
|
|
2639
|
-
status: 200,
|
|
2640
|
-
json: () => Promise.resolve({ temporaryAuthorization: "temp_token" }),
|
|
2641
|
-
})
|
|
2642
|
-
.mockResolvedValueOnce({
|
|
2643
|
-
ok: true,
|
|
2644
|
-
status: 200,
|
|
2645
|
-
json: () => Promise.resolve(mockResponse),
|
|
2646
|
-
});
|
|
2647
|
-
|
|
2648
|
-
const response = await client.updateTable({
|
|
2649
|
-
tableId: QB_TABLE_ID_1,
|
|
2650
|
-
appId: QB_APP_ID,
|
|
2651
|
-
body: mockBody,
|
|
2652
|
-
});
|
|
2653
|
-
expect(response).toEqual(mockResponse);
|
|
2654
|
-
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
2655
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2656
|
-
`https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
|
|
2657
|
-
expect.any(Object)
|
|
2658
|
-
);
|
|
2659
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
2660
|
-
`https://api.quickbase.com/v1/tables/${QB_TABLE_ID_1}?appId=${QB_APP_ID}`,
|
|
2661
|
-
expect.objectContaining({
|
|
2662
|
-
method: "POST",
|
|
2663
|
-
headers: expect.objectContaining({
|
|
2664
|
-
"QB-Realm-Hostname": `${process.env.QB_REALM}.quickbase.com`,
|
|
2665
|
-
Authorization: "QB-TEMP-TOKEN temp_token",
|
|
2666
|
-
"Content-Type": "application/json",
|
|
2667
|
-
}),
|
|
2668
|
-
body: JSON.stringify(mockBody),
|
|
2669
|
-
})
|
|
2670
|
-
);
|
|
2671
|
-
});
|
|
2672
|
-
});
|
|
1204
|
+
- name: umd.test.ts
|
|
1205
|
+
type: file
|
|
2673
1206
|
- name: text.txt
|
|
2674
1207
|
type: file
|
|
1208
|
+
- name: tools
|
|
1209
|
+
type: directory
|
|
1210
|
+
children:
|
|
1211
|
+
- name: log-gen.ts
|
|
1212
|
+
type: file
|
|
1213
|
+
- name: openapi-jars
|
|
1214
|
+
type: directory
|
|
1215
|
+
children:
|
|
1216
|
+
- name: openapi-generator-cli-7.12.0.jar
|
|
1217
|
+
type: file
|
|
1218
|
+
- name: tsconfig.build.json
|
|
1219
|
+
type: file
|
|
1220
|
+
contents: |-
|
|
1221
|
+
{
|
|
1222
|
+
"extends": "./tsconfig.json",
|
|
1223
|
+
"compilerOptions": {
|
|
1224
|
+
"outDir": "dist/temp",
|
|
1225
|
+
"noEmit": false,
|
|
1226
|
+
"allowImportingTsExtensions": false,
|
|
1227
|
+
"declaration": false,
|
|
1228
|
+
"sourceMap": true
|
|
1229
|
+
},
|
|
1230
|
+
"include": ["dist/temp-src/**/*"],
|
|
1231
|
+
"exclude": [
|
|
1232
|
+
"node_modules",
|
|
1233
|
+
"dist",
|
|
1234
|
+
"tests/**/*",
|
|
1235
|
+
"src/**/*" // Exclude original src/ to avoid confusion
|
|
1236
|
+
]
|
|
1237
|
+
}
|
|
2675
1238
|
- name: tsconfig.json
|
|
2676
1239
|
type: file
|
|
1240
|
+
contents: |-
|
|
1241
|
+
{
|
|
1242
|
+
"compilerOptions": {
|
|
1243
|
+
"target": "ESNext",
|
|
1244
|
+
"module": "ESNext",
|
|
1245
|
+
"moduleResolution": "bundler",
|
|
1246
|
+
"strict": true,
|
|
1247
|
+
"lib": ["ES2015", "ESNext", "DOM"],
|
|
1248
|
+
"types": ["node", "playwright/test", "undici-types"],
|
|
1249
|
+
"esModuleInterop": true,
|
|
1250
|
+
"resolveJsonModule": true,
|
|
1251
|
+
"allowImportingTsExtensions": true,
|
|
1252
|
+
"noEmit": true,
|
|
1253
|
+
"outDir": "dist",
|
|
1254
|
+
"rootDir": ".",
|
|
1255
|
+
"declaration": true,
|
|
1256
|
+
"sourceMap": true,
|
|
1257
|
+
"baseUrl": ".",
|
|
1258
|
+
"paths": {
|
|
1259
|
+
"@/*": ["src/*"],
|
|
1260
|
+
"@tests/*": ["tests/*"]
|
|
1261
|
+
}
|
|
1262
|
+
},
|
|
1263
|
+
"include": ["src/**/*", "tests/**/*", "open-api/**/*", "tools/**/*"],
|
|
1264
|
+
"exclude": ["node_modules", "dist", "specs"],
|
|
1265
|
+
"ts-node": {
|
|
1266
|
+
"esm": true,
|
|
1267
|
+
"transpileOnly": true
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
2677
1270
|
- name: versionrc.json
|
|
2678
1271
|
type: file
|
|
2679
1272
|
- name: vitest.config.ts
|
|
2680
1273
|
type: file
|
|
1274
|
+
contents: |
|
|
1275
|
+
// vitest.config.ts
|
|
1276
|
+
import { defineConfig } from "vitest/config";
|
|
1277
|
+
import path from "path";
|
|
1278
|
+
import dotenv from "dotenv"; // Add this import
|
|
1279
|
+
|
|
1280
|
+
// Load .env variables before tests run
|
|
1281
|
+
dotenv.config({ path: path.resolve(__dirname, ".env") });
|
|
1282
|
+
|
|
1283
|
+
export default defineConfig({
|
|
1284
|
+
test: {
|
|
1285
|
+
environment: "jsdom",
|
|
1286
|
+
include: ["tests/vitest/**/**/*.test.ts"],
|
|
1287
|
+
setupFiles: ["tests/setup.ts"], // Relative path from root
|
|
1288
|
+
pool: "forks",
|
|
1289
|
+
poolOptions: {
|
|
1290
|
+
forks: {
|
|
1291
|
+
singleFork: true, // Run tests sequentially
|
|
1292
|
+
},
|
|
1293
|
+
},
|
|
1294
|
+
},
|
|
1295
|
+
resolve: {
|
|
1296
|
+
alias: {
|
|
1297
|
+
"@": path.resolve(__dirname, "./src"), // For src/ imports
|
|
1298
|
+
"@tests": path.resolve(__dirname, "./tests"), // For tests/ imports
|
|
1299
|
+
},
|
|
1300
|
+
},
|
|
1301
|
+
});
|