quickbase-js 0.1.0-beta.10

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.
Files changed (165) hide show
  1. package/CHANGELOG.md +211 -0
  2. package/LICENSE +21 -0
  3. package/README.md +179 -0
  4. package/api-examples.md +49 -0
  5. package/build-common.js +131 -0
  6. package/build-esm.js +71 -0
  7. package/build-umd.js +69 -0
  8. package/dist/esm/quickbase.d.ts +8081 -0
  9. package/dist/esm/quickbase.js +7928 -0
  10. package/dist/esm/quickbase.js.map +1 -0
  11. package/dist/esm/quickbase.min.js +2 -0
  12. package/dist/esm/quickbase.min.js.map +1 -0
  13. package/dist/umd/quickbase.umd.js +8504 -0
  14. package/dist/umd/quickbase.umd.js.map +1 -0
  15. package/dist/umd/quickbase.umd.min.js +2 -0
  16. package/dist/umd/quickbase.umd.min.js.map +1 -0
  17. package/docs/.vitepress/cache/deps/@theme_index.js +275 -0
  18. package/docs/.vitepress/cache/deps/@theme_index.js.map +7 -0
  19. package/docs/.vitepress/cache/deps/_metadata.json +40 -0
  20. package/docs/.vitepress/cache/deps/chunk-5A67QEFC.js +12542 -0
  21. package/docs/.vitepress/cache/deps/chunk-5A67QEFC.js.map +7 -0
  22. package/docs/.vitepress/cache/deps/chunk-7D4P4MMP.js +9719 -0
  23. package/docs/.vitepress/cache/deps/chunk-7D4P4MMP.js.map +7 -0
  24. package/docs/.vitepress/cache/deps/package.json +3 -0
  25. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4504 -0
  26. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  27. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +583 -0
  28. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  29. package/docs/.vitepress/cache/deps/vue.js +343 -0
  30. package/docs/.vitepress/cache/deps/vue.js.map +7 -0
  31. package/docs/.vitepress/config.mts +26 -0
  32. package/docs/api-examples.md +49 -0
  33. package/docs/index.md +59 -0
  34. package/docs/markdown-examples.md +85 -0
  35. package/docs-data/api-docs.json +4143 -0
  36. package/examples/quicbase-jsCDN.html +36 -0
  37. package/examples/react.js +48 -0
  38. package/examples/vanillajs.html +47 -0
  39. package/index.md +25 -0
  40. package/markdown-examples.md +85 -0
  41. package/open-api/fix-spec-main.ts +133 -0
  42. package/open-api/generate-unified-interface.ts +104 -0
  43. package/open-api/generateDocsJson.ts +24 -0
  44. package/open-api/output/quickbase-fixed.json +11191 -0
  45. package/open-api/post-process-generated.ts +226 -0
  46. package/open-api/regenerate-client.ts +117 -0
  47. package/open-api/schema/enhance-general.ts +111 -0
  48. package/open-api/schema/fix-arrays.ts +68 -0
  49. package/open-api/schema/tags/apps.ts +90 -0
  50. package/open-api/schema/tags/fields.ts +471 -0
  51. package/open-api/schema/tags/groups.ts +51 -0
  52. package/open-api/schema/tags/index.ts +66 -0
  53. package/open-api/schema/tags/records.ts +115 -0
  54. package/open-api/schema/tags/tables.ts +211 -0
  55. package/open-api/schema/tags/users.ts +47 -0
  56. package/open-api/types/spec.ts +42 -0
  57. package/open-api/utils/common.ts +29 -0
  58. package/open-api/utils/generateJsDoc.ts +74 -0
  59. package/open-api/utils/infer-schema.ts +40 -0
  60. package/open-api/utils/naming.ts +10 -0
  61. package/open-api/utils/sharedUtils.ts +352 -0
  62. package/package.json +96 -0
  63. package/playwright.config.ts +37 -0
  64. package/rollup.config.js +114 -0
  65. package/specs/QuickBase_RESTful_API_2025-03-04T06_22_39.725Z.json +46878 -0
  66. package/specs/split/apps.json +1315 -0
  67. package/specs/split/auth.json +191 -0
  68. package/specs/split/fields.json +3869 -0
  69. package/specs/split/records.json +754 -0
  70. package/specs/split/reports.json +619 -0
  71. package/specs/split/tables.json +1605 -0
  72. package/src/RateLimitError.ts +12 -0
  73. package/src/ThrottleBucket.ts +41 -0
  74. package/src/authorizationStrategy.ts +283 -0
  75. package/src/generated-unified/QuickbaseClient.ts +1135 -0
  76. package/src/generated-unified/missing-types-report.json +3 -0
  77. package/src/index.ts +1 -0
  78. package/src/invokeMethod.ts +245 -0
  79. package/src/quickbaseClient.ts +194 -0
  80. package/src/rateLimiter.ts +25 -0
  81. package/src/tokenCache.ts +35 -0
  82. package/src/utils.ts +50 -0
  83. package/tests/playwright/qb/auth/cacheSwitching.test.ts +159 -0
  84. package/tests/playwright/qb/auth/fetchTempToken401.test.ts +219 -0
  85. package/tests/playwright/qb/auth/getAppWithTempToken.test.ts +162 -0
  86. package/tests/playwright/qb/auth/getAppWithUserToken.test.ts +153 -0
  87. package/tests/playwright/qb/auth/getTempToken.test.ts +152 -0
  88. package/tests/playwright/qb/auth/tempTokenPrefetch.test.ts +179 -0
  89. package/tests/playwright/qb/auth/tempTokenRenewal.test.ts +133 -0
  90. package/tests/setup.ts +53 -0
  91. package/tests/vitest/qb/apps/copyApp.test.ts +76 -0
  92. package/tests/vitest/qb/apps/createApp.test.ts +64 -0
  93. package/tests/vitest/qb/apps/deleteApp.test.ts +76 -0
  94. package/tests/vitest/qb/apps/getApp.test.ts +44 -0
  95. package/tests/vitest/qb/apps/getAppEvents.test.ts +67 -0
  96. package/tests/vitest/qb/apps/updateApp.test.ts +58 -0
  97. package/tests/vitest/qb/auth/mockQuickbaseServer.ts +60 -0
  98. package/tests/vitest/qb/auth/retryUserToken401.test.ts +73 -0
  99. package/tests/vitest/qb/auth/ssoTokenIntegrationWithServer.test.ts +65 -0
  100. package/tests/vitest/qb/fields/createField.test.ts +77 -0
  101. package/tests/vitest/qb/fields/deleteFields.test.ts +83 -0
  102. package/tests/vitest/qb/fields/getField.test.ts +85 -0
  103. package/tests/vitest/qb/fields/getFieldUsage.test.ts +82 -0
  104. package/tests/vitest/qb/fields/getFields.test.ts +232 -0
  105. package/tests/vitest/qb/fields/getFieldsUsage.test.ts +77 -0
  106. package/tests/vitest/qb/fields/updateField.test.ts +93 -0
  107. package/tests/vitest/qb/formulas/runFormula.test.ts +96 -0
  108. package/tests/vitest/qb/index.ts +29 -0
  109. package/tests/vitest/qb/records/deleteRecords.test.ts +110 -0
  110. package/tests/vitest/qb/records/runQuery.test.ts +146 -0
  111. package/tests/vitest/qb/records/upsert.test.ts +102 -0
  112. package/tests/vitest/qb/tables/createRelationship.test.ts +102 -0
  113. package/tests/vitest/qb/tables/createTable.test.ts +55 -0
  114. package/tests/vitest/qb/tables/deleteRelationship.test.ts +143 -0
  115. package/tests/vitest/qb/tables/deleteTable.test.ts +50 -0
  116. package/tests/vitest/qb/tables/getAppTables.test.ts +46 -0
  117. package/tests/vitest/qb/tables/getRelationships.test.ts +80 -0
  118. package/tests/vitest/qb/tables/getTable.test.ts +26 -0
  119. package/tests/vitest/qb/tables/updateRelationship.test.ts +250 -0
  120. package/tests/vitest/qb/tables/updateTable.test.ts +48 -0
  121. package/tests/vitest/unit/ThrottleBucket.test.ts +95 -0
  122. package/tests/vitest/unit/apps/copyApp.test.ts +581 -0
  123. package/tests/vitest/unit/apps/createApp.test.ts +186 -0
  124. package/tests/vitest/unit/apps/deleteApp.test.ts +216 -0
  125. package/tests/vitest/unit/apps/getApp.test.ts +128 -0
  126. package/tests/vitest/unit/apps/getAppEvents.test.ts +193 -0
  127. package/tests/vitest/unit/apps/updateApp.test.ts +412 -0
  128. package/tests/vitest/unit/auth/fetchTempToken401.test.ts +63 -0
  129. package/tests/vitest/unit/auth/getTempToken.test.ts +119 -0
  130. package/tests/vitest/unit/auth/retryTempToken401.test.ts +95 -0
  131. package/tests/vitest/unit/auth/retryTempTokenMaxRetries.test.ts +206 -0
  132. package/tests/vitest/unit/auth/retryUserToken401.test.ts +69 -0
  133. package/tests/vitest/unit/auth/ssoTokenRefresh.test.ts +325 -0
  134. package/tests/vitest/unit/fields/createField.test.ts +451 -0
  135. package/tests/vitest/unit/fields/deleteFields.test.ts +141 -0
  136. package/tests/vitest/unit/fields/getField.test.ts +189 -0
  137. package/tests/vitest/unit/fields/getFieldUsage.test.ts +180 -0
  138. package/tests/vitest/unit/fields/getFields.test.ts +122 -0
  139. package/tests/vitest/unit/fields/getFieldsUsage.test.ts +160 -0
  140. package/tests/vitest/unit/fields/updateField.test.ts +285 -0
  141. package/tests/vitest/unit/formulas/runFormula.test.ts +205 -0
  142. package/tests/vitest/unit/index.ts +34 -0
  143. package/tests/vitest/unit/quickbaseClientHttpMethods.test.ts +139 -0
  144. package/tests/vitest/unit/rateLimit.test.ts +191 -0
  145. package/tests/vitest/unit/records/deleteRecords.test.ts +112 -0
  146. package/tests/vitest/unit/records/runQuery.test.ts +102 -0
  147. package/tests/vitest/unit/records/upsert.test.ts +234 -0
  148. package/tests/vitest/unit/tables/createRelationship.test.ts +212 -0
  149. package/tests/vitest/unit/tables/createTable.test.ts +70 -0
  150. package/tests/vitest/unit/tables/deleteRelationship.test.ts +97 -0
  151. package/tests/vitest/unit/tables/deleteTable.test.ts +77 -0
  152. package/tests/vitest/unit/tables/getAppTables.test.ts +85 -0
  153. package/tests/vitest/unit/tables/getRelationships.test.ts +259 -0
  154. package/tests/vitest/unit/tables/getTable.test.ts +233 -0
  155. package/tests/vitest/unit/tables/updateRelationship.test.ts +307 -0
  156. package/tests/vitest/unit/tables/updateTable.test.ts +80 -0
  157. package/tests/vitest/unit/tokenLifespan.test.ts +204 -0
  158. package/tests/vitest/unit/umd.test.ts +8 -0
  159. package/tools/log-gen.ts +251 -0
  160. package/tools/log-gen.yaml +4777 -0
  161. package/tools/openapi-jars/openapi-generator-cli-7.12.0.jar +0 -0
  162. package/tsconfig.build.json +17 -0
  163. package/tsconfig.json +32 -0
  164. package/versionrc.json +9 -0
  165. package/vitest.config.ts +27 -0
@@ -0,0 +1,325 @@
1
+ // tests/vitest/unit/auth/ssoTokenRefresh.test.ts
2
+
3
+ import { describe, it, expect, beforeEach, vi } from "vitest";
4
+ import { createClient, mockFetch, QB_REALM, QB_APP_ID } from "@tests/setup.ts";
5
+
6
+ describe("QuickbaseClient Unit - SsoTokenStrategy", () => {
7
+ let client: ReturnType<typeof createClient>;
8
+ const mockSamlToken = "mock_saml_token_base64url";
9
+ const mockSsoToken = "example_token_1NiIsImtpZCI6IjllciJ9w";
10
+
11
+ beforeEach(() => {
12
+ mockFetch.mockClear();
13
+ client = createClient(mockFetch, {
14
+ useSso: true,
15
+ samlToken: mockSamlToken,
16
+ realm: QB_REALM,
17
+ debug: true,
18
+ });
19
+ });
20
+
21
+ it("initializes without errors", () => {
22
+ expect(client).toBeDefined();
23
+ });
24
+
25
+ it("has exchangeSsoToken method", () => {
26
+ expect(typeof client.exchangeSsoToken).toBe("function");
27
+ });
28
+
29
+ it("fetches SSO token on first call", async () => {
30
+ const mockResponse = {
31
+ access_token: mockSsoToken,
32
+ issued_token_type: "urn:quickbase:params:oauth:token-type:temp_token",
33
+ token_type: "N_A",
34
+ };
35
+
36
+ mockFetch.mockResolvedValueOnce(
37
+ new Response(JSON.stringify(mockResponse), {
38
+ status: 200,
39
+ headers: { "Content-Type": "application/json" },
40
+ })
41
+ );
42
+
43
+ mockFetch.mockResolvedValueOnce(
44
+ new Response(JSON.stringify({ id: QB_APP_ID, name: "Test App" }), {
45
+ status: 200,
46
+ headers: { "Content-Type": "application/json" },
47
+ })
48
+ );
49
+
50
+ const result = await client.getApp({ appId: QB_APP_ID });
51
+
52
+ expect(mockFetch).toHaveBeenCalledTimes(2);
53
+ expect(mockFetch).toHaveBeenNthCalledWith(
54
+ 1,
55
+ "https://api.quickbase.com/v1/auth/oauth/token",
56
+ expect.objectContaining({
57
+ method: "POST",
58
+ headers: expect.objectContaining({
59
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
60
+ "Content-Type": "application/json",
61
+ }),
62
+ body: JSON.stringify({
63
+ grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
64
+ requested_token_type:
65
+ "urn:quickbase:params:oauth:token-type:temp_token",
66
+ subject_token: mockSamlToken,
67
+ subject_token_type: "urn:ietf:params:oauth:token-type:saml2",
68
+ }),
69
+ })
70
+ );
71
+ expect(mockFetch).toHaveBeenNthCalledWith(
72
+ 2,
73
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
74
+ expect.objectContaining({
75
+ headers: expect.objectContaining({
76
+ Authorization: `QB-TEMP-TOKEN ${mockSsoToken}`,
77
+ }),
78
+ })
79
+ );
80
+ expect(result).toEqual({ id: QB_APP_ID, name: "Test App" });
81
+ });
82
+
83
+ it("refreshes SSO token on 401 and retries", async () => {
84
+ const newToken = "new_token_abcdef123";
85
+ const appResponse = { id: QB_APP_ID, name: "Test App" };
86
+
87
+ mockFetch
88
+ .mockResolvedValueOnce(
89
+ new Response(
90
+ JSON.stringify({
91
+ access_token: mockSsoToken,
92
+ issued_token_type:
93
+ "urn:quickbase:params:oauth:token-type:temp_token",
94
+ token_type: "N_A",
95
+ }),
96
+ { status: 200, headers: { "Content-Type": "application/json" } }
97
+ )
98
+ )
99
+ .mockResolvedValueOnce(
100
+ new Response(JSON.stringify({ message: "Unauthorized" }), {
101
+ status: 401,
102
+ headers: { "Content-Type": "application/json" },
103
+ })
104
+ )
105
+ .mockResolvedValueOnce(
106
+ new Response(
107
+ JSON.stringify({
108
+ access_token: newToken,
109
+ issued_token_type:
110
+ "urn:quickbase:params:oauth:token-type:temp_token",
111
+ token_type: "N_A",
112
+ }),
113
+ { status: 200, headers: { "Content-Type": "application/json" } }
114
+ )
115
+ )
116
+ .mockResolvedValueOnce(
117
+ new Response(JSON.stringify(appResponse), {
118
+ status: 200,
119
+ headers: { "Content-Type": "application/json" },
120
+ })
121
+ );
122
+
123
+ const result = await client.getApp({ appId: QB_APP_ID });
124
+
125
+ expect(result).toEqual(appResponse);
126
+ expect(mockFetch).toHaveBeenCalledTimes(4);
127
+ expect(mockFetch).toHaveBeenNthCalledWith(
128
+ 1,
129
+ "https://api.quickbase.com/v1/auth/oauth/token",
130
+ expect.any(Object)
131
+ );
132
+ expect(mockFetch).toHaveBeenNthCalledWith(
133
+ 2,
134
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
135
+ expect.objectContaining({
136
+ headers: expect.objectContaining({
137
+ Authorization: `QB-TEMP-TOKEN ${mockSsoToken}`,
138
+ }),
139
+ })
140
+ );
141
+ expect(mockFetch).toHaveBeenNthCalledWith(
142
+ 3,
143
+ "https://api.quickbase.com/v1/auth/oauth/token",
144
+ expect.any(Object)
145
+ );
146
+ expect(mockFetch).toHaveBeenNthCalledWith(
147
+ 4,
148
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
149
+ expect.objectContaining({
150
+ headers: expect.objectContaining({
151
+ Authorization: `QB-TEMP-TOKEN ${newToken}`,
152
+ }),
153
+ })
154
+ );
155
+ });
156
+
157
+ it("handles API error on token exchange", async () => {
158
+ // Reset client to avoid interference from beforeEach
159
+ client = createClient(mockFetch, {
160
+ useSso: true,
161
+ samlToken: mockSamlToken,
162
+ realm: QB_REALM,
163
+ debug: true,
164
+ });
165
+
166
+ mockFetch
167
+ .mockResolvedValueOnce(
168
+ new Response(
169
+ JSON.stringify({
170
+ access_token: mockSsoToken,
171
+ issued_token_type:
172
+ "urn:quickbase:params:oauth:token-type:temp_token",
173
+ token_type: "N_A",
174
+ }),
175
+ { status: 200, headers: { "Content-Type": "application/json" } }
176
+ )
177
+ )
178
+ .mockResolvedValueOnce(
179
+ new Response(JSON.stringify({ message: "Invalid SAML token" }), {
180
+ status: 400,
181
+ headers: { "Content-Type": "application/json" },
182
+ })
183
+ );
184
+
185
+ await expect(
186
+ client.exchangeSsoToken({
187
+ grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
188
+ requested_token_type:
189
+ "urn:quickbase:params:oauth:token-type:temp_token",
190
+ subject_token: mockSamlToken,
191
+ subject_token_type: "urn:ietf:params:oauth:token-type:saml2",
192
+ })
193
+ ).rejects.toThrow("API Error: Invalid SAML token (Status: 400)");
194
+
195
+ expect(mockFetch).toHaveBeenCalledTimes(2);
196
+ expect(mockFetch).toHaveBeenNthCalledWith(
197
+ 1,
198
+ "https://api.quickbase.com/v1/auth/oauth/token",
199
+ expect.any(Object)
200
+ );
201
+ expect(mockFetch).toHaveBeenNthCalledWith(
202
+ 2,
203
+ "https://api.quickbase.com/v1/auth/oauth/token",
204
+ expect.any(Object)
205
+ );
206
+ });
207
+
208
+ it("exhausts retries on repeated 401s", async () => {
209
+ mockFetch
210
+ .mockResolvedValueOnce(
211
+ new Response(
212
+ JSON.stringify({
213
+ access_token: mockSsoToken,
214
+ issued_token_type:
215
+ "urn:quickbase:params:oauth:token-type:temp_token",
216
+ token_type: "N_A",
217
+ }),
218
+ { status: 200, headers: { "Content-Type": "application/json" } }
219
+ )
220
+ )
221
+ .mockResolvedValueOnce(
222
+ new Response(JSON.stringify({ message: "Unauthorized" }), {
223
+ status: 401,
224
+ headers: { "Content-Type": "application/json" },
225
+ })
226
+ )
227
+ .mockResolvedValueOnce(
228
+ new Response(
229
+ JSON.stringify({
230
+ access_token: "retry_token_1",
231
+ issued_token_type:
232
+ "urn:quickbase:params:oauth:token-type:temp_token",
233
+ token_type: "N_A",
234
+ }),
235
+ { status: 200, headers: { "Content-Type": "application/json" } }
236
+ )
237
+ )
238
+ .mockResolvedValueOnce(
239
+ new Response(JSON.stringify({ message: "Unauthorized" }), {
240
+ status: 401,
241
+ headers: { "Content-Type": "application/json" },
242
+ })
243
+ )
244
+ .mockResolvedValueOnce(
245
+ new Response(
246
+ JSON.stringify({
247
+ access_token: "retry_token_2",
248
+ issued_token_type:
249
+ "urn:quickbase:params:oauth:token-type:temp_token",
250
+ token_type: "N_A",
251
+ }),
252
+ { status: 200, headers: { "Content-Type": "application/json" } }
253
+ )
254
+ )
255
+ .mockResolvedValueOnce(
256
+ new Response(JSON.stringify({ message: "Unauthorized" }), {
257
+ status: 401,
258
+ headers: { "Content-Type": "application/json" },
259
+ })
260
+ )
261
+ .mockResolvedValueOnce(
262
+ new Response(
263
+ JSON.stringify({
264
+ access_token: "retry_token_3",
265
+ issued_token_type:
266
+ "urn:quickbase:params:oauth:token-type:temp_token",
267
+ token_type: "N_A",
268
+ }),
269
+ { status: 200, headers: { "Content-Type": "application/json" } }
270
+ )
271
+ )
272
+ .mockResolvedValueOnce(
273
+ new Response(JSON.stringify({ message: "Unauthorized" }), {
274
+ status: 401,
275
+ headers: { "Content-Type": "application/json" },
276
+ })
277
+ );
278
+
279
+ await expect(client.getApp({ appId: QB_APP_ID })).rejects.toThrow(
280
+ "API Error: Unauthorized (Status: 401)"
281
+ );
282
+
283
+ expect(mockFetch).toHaveBeenCalledTimes(8);
284
+ expect(mockFetch).toHaveBeenNthCalledWith(
285
+ 1,
286
+ "https://api.quickbase.com/v1/auth/oauth/token",
287
+ expect.any(Object)
288
+ );
289
+ expect(mockFetch).toHaveBeenNthCalledWith(
290
+ 2,
291
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
292
+ expect.any(Object)
293
+ );
294
+ expect(mockFetch).toHaveBeenNthCalledWith(
295
+ 3,
296
+ "https://api.quickbase.com/v1/auth/oauth/token",
297
+ expect.any(Object)
298
+ );
299
+ expect(mockFetch).toHaveBeenNthCalledWith(
300
+ 4,
301
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
302
+ expect.any(Object)
303
+ );
304
+ expect(mockFetch).toHaveBeenNthCalledWith(
305
+ 5,
306
+ "https://api.quickbase.com/v1/auth/oauth/token",
307
+ expect.any(Object)
308
+ );
309
+ expect(mockFetch).toHaveBeenNthCalledWith(
310
+ 6,
311
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
312
+ expect.any(Object)
313
+ );
314
+ expect(mockFetch).toHaveBeenNthCalledWith(
315
+ 7,
316
+ "https://api.quickbase.com/v1/auth/oauth/token",
317
+ expect.any(Object)
318
+ );
319
+ expect(mockFetch).toHaveBeenNthCalledWith(
320
+ 8,
321
+ `https://api.quickbase.com/v1/apps/${QB_APP_ID}`,
322
+ expect.any(Object)
323
+ );
324
+ });
325
+ });