quickbase-js 0.1.0-beta.7

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 (123) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/LICENSE +21 -0
  3. package/README.md +204 -0
  4. package/build-common.js +131 -0
  5. package/build-esm.js +71 -0
  6. package/build-umd.js +69 -0
  7. package/dist/esm/quickbase.d.ts +7594 -0
  8. package/dist/esm/quickbase.js +9752 -0
  9. package/dist/esm/quickbase.js.map +1 -0
  10. package/dist/esm/quickbase.min.js +2 -0
  11. package/dist/esm/quickbase.min.js.map +1 -0
  12. package/dist/umd/quickbase.umd.js +10502 -0
  13. package/dist/umd/quickbase.umd.js.map +1 -0
  14. package/dist/umd/quickbase.umd.min.js +2 -0
  15. package/dist/umd/quickbase.umd.min.js.map +1 -0
  16. package/examples/quicbase-jsCDN.html +36 -0
  17. package/examples/react.js +48 -0
  18. package/examples/vanillajs.html +47 -0
  19. package/notes.txt +158 -0
  20. package/open-api/fix-spec-main.ts +96 -0
  21. package/open-api/generate-unified-interface.ts +277 -0
  22. package/open-api/output/override-report.json +58 -0
  23. package/open-api/output/quickbase-fixed.json +10610 -0
  24. package/open-api/regenerate-client.ts +116 -0
  25. package/open-api/schema/enhance-general.ts +100 -0
  26. package/open-api/schema/fix-arrays.ts +69 -0
  27. package/open-api/schema/tags/apps.ts +90 -0
  28. package/open-api/schema/tags/fields.ts +156 -0
  29. package/open-api/schema/tags/groups.ts +51 -0
  30. package/open-api/schema/tags/index.ts +68 -0
  31. package/open-api/schema/tags/records.ts +115 -0
  32. package/open-api/schema/tags/tables.ts +211 -0
  33. package/open-api/schema/tags/users.ts +47 -0
  34. package/open-api/types/spec.ts +41 -0
  35. package/open-api/utils/infer-schema.ts +40 -0
  36. package/open-api/utils/naming.ts +10 -0
  37. package/package.json +91 -0
  38. package/playwright.config.ts +37 -0
  39. package/rollup.config.js +114 -0
  40. package/specs/QuickBase_RESTful_API_2025-03-04T06_22_39.725Z.json +46878 -0
  41. package/specs/split/apps.json +1315 -0
  42. package/specs/split/auth.json +191 -0
  43. package/specs/split/fields.json +3869 -0
  44. package/specs/split/records.json +754 -0
  45. package/specs/split/reports.json +619 -0
  46. package/specs/split/tables.json +1605 -0
  47. package/src/generated-unified/QuickbaseClient.ts +359 -0
  48. package/src/generated-unified/missing-types-report.json +3 -0
  49. package/src/index.ts +1 -0
  50. package/src/quickbaseClient.ts +402 -0
  51. package/src/tokenCache.ts +51 -0
  52. package/src/utils.ts +7 -0
  53. package/test-esm.js +1 -0
  54. package/test-results.json +2050 -0
  55. package/test-umd.js +1 -0
  56. package/test.ts +1 -0
  57. package/tests/playwright/qb/auth/cacheSwitching.test.ts +159 -0
  58. package/tests/playwright/qb/auth/fetchTempToken401.test.ts +219 -0
  59. package/tests/playwright/qb/auth/getAppWithTempToken.test.ts +162 -0
  60. package/tests/playwright/qb/auth/getAppWithUserToken.test.ts +153 -0
  61. package/tests/playwright/qb/auth/getTempToken.test.ts +152 -0
  62. package/tests/playwright/qb/auth/tokenPrefetch.test.ts +179 -0
  63. package/tests/playwright/qb/auth/tokenRenewal.test.ts +133 -0
  64. package/tests/setup.ts +51 -0
  65. package/tests/vitest/qb/apps/copyApp.test.ts +76 -0
  66. package/tests/vitest/qb/apps/createApp.test.ts +64 -0
  67. package/tests/vitest/qb/apps/deleteApp.test.ts +76 -0
  68. package/tests/vitest/qb/apps/getApp.test.ts +40 -0
  69. package/tests/vitest/qb/fields/createField.test.ts +78 -0
  70. package/tests/vitest/qb/fields/deleteFields.test.ts +89 -0
  71. package/tests/vitest/qb/fields/getField.test.ts +85 -0
  72. package/tests/vitest/qb/fields/getFieldUsage.test.ts +174 -0
  73. package/tests/vitest/qb/fields/getFields.test.ts +232 -0
  74. package/tests/vitest/qb/fields/getFieldsUsage.test.ts +77 -0
  75. package/tests/vitest/qb/index.ts +28 -0
  76. package/tests/vitest/qb/records/deleteRecords.test.ts +110 -0
  77. package/tests/vitest/qb/records/runQuery.test.ts +146 -0
  78. package/tests/vitest/qb/records/upsert.test.ts +102 -0
  79. package/tests/vitest/qb/tables/createTable.test.ts +55 -0
  80. package/tests/vitest/qb/tables/deleteTable.test.ts +50 -0
  81. package/tests/vitest/qb/tables/getAppTables.test.ts +46 -0
  82. package/tests/vitest/qb/tables/getRelationships.test.ts +80 -0
  83. package/tests/vitest/qb/tables/getTable.test.ts +26 -0
  84. package/tests/vitest/qb/tables/updateTable.test.ts +48 -0
  85. package/tests/vitest/unit/apps/copyApp.test.ts +488 -0
  86. package/tests/vitest/unit/apps/createApp.test.ts +186 -0
  87. package/tests/vitest/unit/apps/deleteApp.test.ts +216 -0
  88. package/tests/vitest/unit/apps/getApp.test.ts +128 -0
  89. package/tests/vitest/unit/auth/fetchTempToken401.test.ts +82 -0
  90. package/tests/vitest/unit/auth/getTempToken.test.ts +95 -0
  91. package/tests/vitest/unit/auth/retryOn401.test.ts +116 -0
  92. package/tests/vitest/unit/auth/two401s.test.ts +86 -0
  93. package/tests/vitest/unit/fields/createField.test.ts +391 -0
  94. package/tests/vitest/unit/fields/deleteFields.test.ts +141 -0
  95. package/tests/vitest/unit/fields/getField.test.ts +189 -0
  96. package/tests/vitest/unit/fields/getFieldUsage.test.ts +147 -0
  97. package/tests/vitest/unit/fields/getFields.test.ts +122 -0
  98. package/tests/vitest/unit/fields/getFieldsUsage.test.ts +160 -0
  99. package/tests/vitest/unit/index.ts +33 -0
  100. package/tests/vitest/unit/records/deleteRecords.test.ts +112 -0
  101. package/tests/vitest/unit/records/runQuery.test.ts +102 -0
  102. package/tests/vitest/unit/records/upsert.test.ts +234 -0
  103. package/tests/vitest/unit/tables/createTable.test.ts +70 -0
  104. package/tests/vitest/unit/tables/deleteTable.test.ts +77 -0
  105. package/tests/vitest/unit/tables/getAppTables.test.ts +85 -0
  106. package/tests/vitest/unit/tables/getRelationships.test.ts +259 -0
  107. package/tests/vitest/unit/tables/getTable.test.ts +233 -0
  108. package/tests/vitest/unit/tables/updateTable.test.ts +80 -0
  109. package/tests/vitest/unit/umd.test.ts +8 -0
  110. package/text.txt +104 -0
  111. package/tools/generate-types.ts +29 -0
  112. package/tools/log-gen.ts +231 -0
  113. package/tools/log-gen.yaml +3826 -0
  114. package/tools/openapi-jars/openapi-generator-cli-7.12.0.jar +0 -0
  115. package/tools/openapi-schema-validator.ts +38 -0
  116. package/tools/spec-analyzer.ts +163 -0
  117. package/tools/split-spec.ts +148 -0
  118. package/tools/test-import.ts +3 -0
  119. package/tools/validate-spec.ts +40 -0
  120. package/tsconfig.build.json +17 -0
  121. package/tsconfig.json +29 -0
  122. package/versionrc.json +9 -0
  123. package/vitest.config.ts +27 -0
@@ -0,0 +1,189 @@
1
+ // tests/vitest/unit/fields/getField.test.ts
2
+ import { describe, it, expect, beforeEach } from "vitest";
3
+ import {
4
+ createClient,
5
+ mockFetch,
6
+ QB_REALM,
7
+ QB_USER_TOKEN,
8
+ QB_TABLE_ID_1,
9
+ } from "@tests/setup.ts";
10
+ import { Field } from "@/generated/models";
11
+
12
+ describe("QuickbaseClient Unit - getField", () => {
13
+ let client: ReturnType<typeof createClient>;
14
+
15
+ beforeEach(() => {
16
+ mockFetch.mockClear();
17
+ client = createClient(mockFetch, { debug: true });
18
+ });
19
+
20
+ it("initializes without errors and has getField method", () => {
21
+ expect(client).toBeDefined();
22
+ expect(typeof client.getField).toBe("function");
23
+ });
24
+
25
+ it("calls getField successfully with user token", async () => {
26
+ const mockResponse: Field = {
27
+ id: 123,
28
+ label: "Field1",
29
+ fieldType: "text",
30
+ mode: "",
31
+ noWrap: false,
32
+ bold: false,
33
+ required: false,
34
+ appearsByDefault: false,
35
+ findEnabled: false,
36
+ unique: false,
37
+ doesDataCopy: false,
38
+ fieldHelp: "field help",
39
+ audited: false,
40
+ properties: {
41
+ primaryKey: false,
42
+ foreignKey: false,
43
+ numLines: 1,
44
+ maxLength: 0,
45
+ appendOnly: false,
46
+ allowHTML: false,
47
+ allowMentions: false,
48
+ sortAsGiven: false,
49
+ carryChoices: true,
50
+ allowNewChoices: false,
51
+ formula: "",
52
+ defaultValue: "",
53
+ },
54
+ permissions: [
55
+ { permissionType: "View", role: "Viewer", roleId: 10 },
56
+ { permissionType: "None", role: "Participant", roleId: 11 },
57
+ { permissionType: "Modify", role: "Administrator", roleId: 12 },
58
+ ],
59
+ };
60
+
61
+ mockFetch.mockResolvedValueOnce({
62
+ ok: true,
63
+ status: 200,
64
+ json: () => Promise.resolve(mockResponse),
65
+ });
66
+
67
+ const result = await client.getField({
68
+ fieldId: 123,
69
+ tableId: QB_TABLE_ID_1,
70
+ includeFieldPerms: true,
71
+ });
72
+
73
+ expect(result).toEqual(mockResponse);
74
+ expect(mockFetch).toHaveBeenCalledWith(
75
+ `https://api.quickbase.com/v1/fields/123?tableId=${QB_TABLE_ID_1}&includeFieldPerms=true`,
76
+ expect.objectContaining({
77
+ method: "GET",
78
+ headers: expect.objectContaining({
79
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
80
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
81
+ "Content-Type": "application/json",
82
+ }),
83
+ })
84
+ );
85
+ });
86
+
87
+ it("calls getField successfully with temp token", async () => {
88
+ client = createClient(mockFetch, { useTempTokens: true, debug: true });
89
+
90
+ const mockResponse: Field = {
91
+ id: 123,
92
+ label: "Field1",
93
+ fieldType: "text",
94
+ mode: "",
95
+ noWrap: false,
96
+ bold: false,
97
+ required: false,
98
+ appearsByDefault: false,
99
+ findEnabled: false,
100
+ unique: false,
101
+ doesDataCopy: false,
102
+ fieldHelp: "field help",
103
+ audited: false,
104
+ properties: {
105
+ primaryKey: false,
106
+ foreignKey: false,
107
+ numLines: 1,
108
+ maxLength: 0,
109
+ appendOnly: false,
110
+ allowHTML: false,
111
+ allowMentions: false,
112
+ sortAsGiven: false,
113
+ carryChoices: true,
114
+ allowNewChoices: false,
115
+ formula: "",
116
+ defaultValue: "",
117
+ },
118
+ permissions: [],
119
+ };
120
+
121
+ mockFetch
122
+ .mockResolvedValueOnce({
123
+ ok: true,
124
+ status: 200,
125
+ json: () => Promise.resolve({ temporaryAuthorization: "temp_token" }),
126
+ })
127
+ .mockResolvedValueOnce({
128
+ ok: true,
129
+ status: 200,
130
+ json: () => Promise.resolve(mockResponse),
131
+ });
132
+
133
+ const result = await client.getField({
134
+ fieldId: 123,
135
+ tableId: QB_TABLE_ID_1,
136
+ });
137
+
138
+ expect(result).toEqual(mockResponse);
139
+ expect(mockFetch).toHaveBeenCalledTimes(2);
140
+ expect(mockFetch).toHaveBeenNthCalledWith(
141
+ 1,
142
+ `https://api.quickbase.com/v1/auth/temporary/${QB_TABLE_ID_1}`,
143
+ expect.objectContaining({
144
+ method: "GET",
145
+ headers: expect.objectContaining({
146
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
147
+ "Content-Type": "application/json",
148
+ }),
149
+ credentials: "include",
150
+ })
151
+ );
152
+ expect(mockFetch).toHaveBeenNthCalledWith(
153
+ 2,
154
+ `https://api.quickbase.com/v1/fields/123?tableId=${QB_TABLE_ID_1}`,
155
+ expect.objectContaining({
156
+ method: "GET",
157
+ headers: expect.objectContaining({
158
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
159
+ Authorization: "QB-TEMP-TOKEN temp_token",
160
+ "Content-Type": "application/json",
161
+ }),
162
+ })
163
+ );
164
+ });
165
+
166
+ it("handles 404 error for non-existent field", async () => {
167
+ mockFetch.mockResolvedValueOnce({
168
+ ok: false,
169
+ status: 404,
170
+ json: () => Promise.resolve({ message: "Field not found" }),
171
+ });
172
+
173
+ await expect(
174
+ client.getField({ fieldId: 999, tableId: QB_TABLE_ID_1 })
175
+ ).rejects.toThrow("API Error: Field not found (Status: 404)");
176
+
177
+ expect(mockFetch).toHaveBeenCalledWith(
178
+ `https://api.quickbase.com/v1/fields/999?tableId=${QB_TABLE_ID_1}`,
179
+ expect.objectContaining({
180
+ method: "GET",
181
+ headers: expect.objectContaining({
182
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
183
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
184
+ "Content-Type": "application/json",
185
+ }),
186
+ })
187
+ );
188
+ });
189
+ });
@@ -0,0 +1,147 @@
1
+ // tests/vitest/unit/fields/getFieldUsage.test.ts
2
+ import { describe, it, expect, beforeEach } from "vitest";
3
+ import {
4
+ createClient,
5
+ mockFetch,
6
+ QB_REALM,
7
+ QB_USER_TOKEN,
8
+ QB_TABLE_ID_1,
9
+ } from "@tests/setup.ts";
10
+ import { FieldUsage } from "@/generated/models";
11
+
12
+ describe("QuickbaseClient Unit - getFieldUsage", () => {
13
+ let client: ReturnType<typeof createClient>;
14
+
15
+ beforeEach(() => {
16
+ mockFetch.mockClear();
17
+ client = createClient(mockFetch, { realm: QB_REALM, debug: true });
18
+ });
19
+
20
+ it("initializes without errors and has getFieldUsage method", () => {
21
+ expect(client).toBeDefined();
22
+ expect(typeof client.getFieldUsage).toBe("function");
23
+ });
24
+
25
+ it("calls getFieldUsage successfully with user token", async () => {
26
+ const mockResponse: FieldUsage = {
27
+ field: {
28
+ id: 6,
29
+ name: "name",
30
+ type: "text", // Match QuickBase's lowercase response
31
+ },
32
+ usage: {
33
+ actions: { count: 0 },
34
+ appHomePages: { count: 0 },
35
+ dashboards: { count: 2 },
36
+ defaultReports: { count: 1 },
37
+ exactForms: { count: 0 },
38
+ fields: { count: 0 },
39
+ forms: { count: 1 },
40
+ notifications: { count: 0 },
41
+ personalReports: { count: 0 },
42
+ pipelines: { count: 1 },
43
+ relationships: { count: 0 },
44
+ reminders: { count: 0 },
45
+ reports: { count: 2 },
46
+ roles: { count: 2 },
47
+ tableImports: { count: 2 },
48
+ tableRules: { count: 1 },
49
+ webhooks: { count: 0 },
50
+ },
51
+ };
52
+
53
+ // Mock the response as an array to match the updated API behavior
54
+ mockFetch.mockResolvedValueOnce(
55
+ new Response(JSON.stringify([mockResponse]), {
56
+ status: 200,
57
+ statusText: "OK",
58
+ headers: { "Content-Type": "application/json" },
59
+ })
60
+ );
61
+
62
+ const result = await client.getFieldUsage({
63
+ fieldId: 6,
64
+ tableId: QB_TABLE_ID_1,
65
+ });
66
+
67
+ // Expect an array and unwrap the first item
68
+ expect(result).toBeInstanceOf(Array);
69
+ expect(result).toHaveLength(1);
70
+ const fieldUsage = result[0];
71
+
72
+ expect(fieldUsage).toEqual(mockResponse);
73
+ expect(mockFetch).toHaveBeenCalledWith(
74
+ `https://api.quickbase.com/v1/fields/usage/6?tableId=${QB_TABLE_ID_1}`,
75
+ expect.objectContaining({
76
+ method: "GET",
77
+ headers: expect.objectContaining({
78
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
79
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
80
+ "Content-Type": "application/json",
81
+ }),
82
+ })
83
+ );
84
+ expect(fieldUsage.field.id).toBe(6);
85
+ expect(fieldUsage.field.name).toBe("name");
86
+ expect(fieldUsage.usage.dashboards.count).toBe(2);
87
+ });
88
+
89
+ it("handles 404 error for non-existent field", async () => {
90
+ mockFetch.mockResolvedValueOnce(
91
+ new Response(JSON.stringify({ message: "Field not found" }), {
92
+ status: 404,
93
+ statusText: "Not Found",
94
+ headers: { "Content-Type": "application/json" },
95
+ })
96
+ );
97
+
98
+ await expect(
99
+ client.getFieldUsage({
100
+ fieldId: 999,
101
+ tableId: QB_TABLE_ID_1,
102
+ })
103
+ ).rejects.toThrow("API Error: Field not found (Status: 404)");
104
+
105
+ expect(mockFetch).toHaveBeenCalledWith(
106
+ `https://api.quickbase.com/v1/fields/usage/999?tableId=${QB_TABLE_ID_1}`,
107
+ expect.objectContaining({
108
+ method: "GET",
109
+ headers: expect.objectContaining({
110
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
111
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
112
+ "Content-Type": "application/json",
113
+ }),
114
+ })
115
+ );
116
+ });
117
+
118
+ it("handles 401 error and does not retry with user token", async () => {
119
+ mockFetch.mockResolvedValueOnce(
120
+ new Response(JSON.stringify({ message: "Unauthorized" }), {
121
+ status: 401,
122
+ statusText: "Unauthorized",
123
+ headers: { "Content-Type": "application/json" },
124
+ })
125
+ );
126
+
127
+ await expect(
128
+ client.getFieldUsage({
129
+ fieldId: 6,
130
+ tableId: QB_TABLE_ID_1,
131
+ })
132
+ ).rejects.toThrow("API Error: Unauthorized (Status: 401)");
133
+
134
+ expect(mockFetch).toHaveBeenCalledTimes(1); // No retry with user token
135
+ expect(mockFetch).toHaveBeenCalledWith(
136
+ `https://api.quickbase.com/v1/fields/usage/6?tableId=${QB_TABLE_ID_1}`,
137
+ expect.objectContaining({
138
+ method: "GET",
139
+ headers: expect.objectContaining({
140
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
141
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
142
+ "Content-Type": "application/json",
143
+ }),
144
+ })
145
+ );
146
+ });
147
+ });
@@ -0,0 +1,122 @@
1
+ // tests/vitest/unit/fields/getFields.test.ts
2
+ import { describe, it, expect } from "vitest";
3
+ import {
4
+ createClient,
5
+ mockFetch,
6
+ QB_REALM,
7
+ QB_USER_TOKEN,
8
+ QB_TABLE_ID_1,
9
+ } from "@tests/setup.ts";
10
+
11
+ describe("QuickbaseClient Unit - getFields", () => {
12
+ it("calls getFields successfully", async () => {
13
+ mockFetch.mockImplementation((url: string, options: any) => {
14
+ console.log("Mock fetch for getFields:", url, options);
15
+ return Promise.resolve({
16
+ ok: true,
17
+ status: 200,
18
+ json: () =>
19
+ Promise.resolve([
20
+ {
21
+ id: 1,
22
+ label: "Field1",
23
+ fieldType: "text",
24
+ noWrap: false,
25
+ bold: false,
26
+ required: false,
27
+ appearsByDefault: false,
28
+ findEnabled: false,
29
+ unique: false,
30
+ doesDataCopy: false,
31
+ fieldHelp: "field help",
32
+ audited: false,
33
+ properties: {
34
+ primaryKey: false,
35
+ foreignKey: false,
36
+ numLines: 1,
37
+ maxLength: 0,
38
+ appendOnly: false,
39
+ allowHTML: false,
40
+ allowMentions: false,
41
+ sortAsGiven: false,
42
+ carryChoices: true,
43
+ allowNewChoices: false,
44
+ formula: "",
45
+ defaultValue: "",
46
+ },
47
+ permissions: [
48
+ { permissionType: "View", role: "Viewer", roleId: 10 },
49
+ { permissionType: "None", role: "Participant", roleId: 11 },
50
+ { permissionType: "Modify", role: "Administrator", roleId: 12 },
51
+ ],
52
+ },
53
+ ]),
54
+ } as Response);
55
+ });
56
+
57
+ const client = createClient(mockFetch, { debug: true }); // Add debug: true for consistency
58
+ const result = await client.getFields({
59
+ tableId: QB_TABLE_ID_1,
60
+ includeFieldPerms: true,
61
+ });
62
+ console.log("getFields response:", result);
63
+ expect(result).toEqual(
64
+ expect.arrayContaining([
65
+ expect.objectContaining({
66
+ id: 1,
67
+ label: "Field1",
68
+ fieldType: "text",
69
+ noWrap: false,
70
+ bold: false,
71
+ required: false,
72
+ appearsByDefault: false,
73
+ findEnabled: false,
74
+ unique: false,
75
+ doesDataCopy: false,
76
+ fieldHelp: "field help",
77
+ audited: false,
78
+ properties: expect.objectContaining({
79
+ primaryKey: false,
80
+ foreignKey: false,
81
+ numLines: 1,
82
+ maxLength: 0,
83
+ appendOnly: false,
84
+ allowHTML: false,
85
+ allowMentions: false,
86
+ sortAsGiven: false,
87
+ carryChoices: true,
88
+ allowNewChoices: false,
89
+ formula: "",
90
+ defaultValue: "",
91
+ }),
92
+ permissions: expect.arrayContaining([
93
+ expect.objectContaining({
94
+ permissionType: "View",
95
+ role: "Viewer",
96
+ roleId: 10,
97
+ }),
98
+ expect.objectContaining({
99
+ permissionType: "None",
100
+ role: "Participant",
101
+ roleId: 11,
102
+ }),
103
+ expect.objectContaining({
104
+ permissionType: "Modify",
105
+ role: "Administrator",
106
+ roleId: 12,
107
+ }),
108
+ ]),
109
+ }),
110
+ ])
111
+ );
112
+ expect(mockFetch).toHaveBeenCalledWith(
113
+ `https://api.quickbase.com/v1/fields?tableId=${QB_TABLE_ID_1}&includeFieldPerms=true`,
114
+ expect.objectContaining({
115
+ headers: expect.objectContaining({
116
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
117
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
118
+ }),
119
+ })
120
+ );
121
+ });
122
+ });
@@ -0,0 +1,160 @@
1
+ // tests/vitest/unit/fields/getFieldsUsage.test.ts
2
+ import { describe, it, expect, beforeEach } from "vitest";
3
+ import {
4
+ createClient,
5
+ mockFetch,
6
+ QB_REALM,
7
+ QB_TABLE_ID_1,
8
+ QB_USER_TOKEN,
9
+ } from "@tests/setup.ts";
10
+ import { FieldUsage } from "@/generated/models";
11
+
12
+ describe("QuickbaseClient Unit - getFieldsUsage", () => {
13
+ let client: ReturnType<typeof createClient>;
14
+
15
+ beforeEach(() => {
16
+ mockFetch.mockClear();
17
+ client = createClient(mockFetch, { debug: true });
18
+ });
19
+
20
+ it("initializes without errors and has getFieldsUsage method", () => {
21
+ expect(client).toBeDefined();
22
+ expect(typeof client.getFieldsUsage).toBe("function");
23
+ });
24
+
25
+ it("calls getFieldsUsage successfully with user token", async () => {
26
+ const mockResponse: FieldUsage[] = [
27
+ {
28
+ field: {
29
+ id: 6,
30
+ name: "name",
31
+ type: "Text",
32
+ },
33
+ usage: {
34
+ actions: { count: 0 },
35
+ appHomePages: { count: 0 },
36
+ dashboards: { count: 2 },
37
+ defaultReports: { count: 1 },
38
+ exactForms: { count: 0 },
39
+ fields: { count: 0 },
40
+ forms: { count: 1 },
41
+ notifications: { count: 0 },
42
+ personalReports: { count: 0 },
43
+ pipelines: { count: 1 },
44
+ relationships: { count: 0 },
45
+ reminders: { count: 0 },
46
+ reports: { count: 2 },
47
+ roles: { count: 2 },
48
+ tableImports: { count: 3 },
49
+ tableRules: { count: 1 },
50
+ webhooks: { count: 0 },
51
+ },
52
+ },
53
+ ];
54
+
55
+ mockFetch.mockResolvedValueOnce({
56
+ ok: true,
57
+ status: 200,
58
+ json: () => Promise.resolve(mockResponse),
59
+ });
60
+
61
+ const result = await client.getFieldsUsage({
62
+ tableId: QB_TABLE_ID_1,
63
+ });
64
+
65
+ expect(result).toEqual(mockResponse);
66
+ expect(mockFetch).toHaveBeenCalledWith(
67
+ `https://api.quickbase.com/v1/fields/usage?tableId=${QB_TABLE_ID_1}`,
68
+ expect.objectContaining({
69
+ method: "GET",
70
+ headers: expect.objectContaining({
71
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
72
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
73
+ "Content-Type": "application/json",
74
+ }),
75
+ })
76
+ );
77
+ });
78
+
79
+ it("calls getFieldsUsage with skip and top parameters", async () => {
80
+ const mockResponse: FieldUsage[] = [
81
+ {
82
+ field: {
83
+ id: 7,
84
+ name: "priority",
85
+ type: "Text",
86
+ },
87
+ usage: {
88
+ actions: { count: 1 },
89
+ appHomePages: { count: 0 },
90
+ dashboards: { count: 0 },
91
+ defaultReports: { count: 0 },
92
+ exactForms: { count: 0 },
93
+ fields: { count: 1 },
94
+ forms: { count: 0 },
95
+ notifications: { count: 0 },
96
+ personalReports: { count: 0 },
97
+ pipelines: { count: 0 },
98
+ relationships: { count: 0 },
99
+ reminders: { count: 0 },
100
+ reports: { count: 1 },
101
+ roles: { count: 1 },
102
+ tableImports: { count: 0 },
103
+ tableRules: { count: 0 },
104
+ webhooks: { count: 0 },
105
+ },
106
+ },
107
+ ];
108
+
109
+ mockFetch.mockResolvedValueOnce({
110
+ ok: true,
111
+ status: 200,
112
+ json: () => Promise.resolve(mockResponse),
113
+ });
114
+
115
+ const result = await client.getFieldsUsage({
116
+ tableId: QB_TABLE_ID_1,
117
+ skip: 1,
118
+ top: 1,
119
+ });
120
+
121
+ expect(result).toEqual(mockResponse);
122
+ expect(mockFetch).toHaveBeenCalledWith(
123
+ `https://api.quickbase.com/v1/fields/usage?tableId=${QB_TABLE_ID_1}&skip=1&top=1`,
124
+ expect.objectContaining({
125
+ method: "GET",
126
+ headers: expect.objectContaining({
127
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
128
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
129
+ "Content-Type": "application/json",
130
+ }),
131
+ })
132
+ );
133
+ });
134
+
135
+ it("handles 404 error for non-existent table", async () => {
136
+ const nonExistentTableId = "nonexistent_dbid";
137
+
138
+ mockFetch.mockResolvedValueOnce({
139
+ ok: false,
140
+ status: 404,
141
+ json: () => Promise.resolve({ message: "Table not found" }),
142
+ });
143
+
144
+ await expect(
145
+ client.getFieldsUsage({ tableId: nonExistentTableId })
146
+ ).rejects.toThrow("API Error: Table not found (Status: 404)");
147
+
148
+ expect(mockFetch).toHaveBeenCalledWith(
149
+ `https://api.quickbase.com/v1/fields/usage?tableId=${nonExistentTableId}`,
150
+ expect.objectContaining({
151
+ method: "GET",
152
+ headers: expect.objectContaining({
153
+ "QB-Realm-Hostname": `${QB_REALM}.quickbase.com`,
154
+ Authorization: `QB-USER-TOKEN ${QB_USER_TOKEN}`,
155
+ "Content-Type": "application/json",
156
+ }),
157
+ })
158
+ );
159
+ });
160
+ });
@@ -0,0 +1,33 @@
1
+ // tests/vitest/unit/index.ts
2
+ // Apps
3
+ export * from "./apps/copyApp.test.ts";
4
+ export * from "./apps/createApp.test.ts";
5
+ export * from "./apps/deleteApp.test.ts";
6
+ export * from "./apps/getApp.test.ts";
7
+
8
+ // Auth
9
+ export * from "./auth/fetchTempToken401.test.ts";
10
+ export * from "./auth/getTempToken.test.ts";
11
+ export * from "./auth/retryOn401.test.ts";
12
+ export * from "./auth/two401s.test.ts";
13
+
14
+ // Fields
15
+ export * from "./fields/getFields.test.ts";
16
+ export * from "./fields/getField.test.ts";
17
+ export * from "./fields/createField.test.ts";
18
+ export * from "./fields/deleteFields.test.ts";
19
+ export * from "./fields/getFieldsUsage.test.ts";
20
+ export * from "./fields/getFieldUsage.test.ts";
21
+
22
+ // Records
23
+ export * from "./records/deleteRecords.test.ts";
24
+ export * from "./records/runQuery.test.ts";
25
+ export * from "./records/upsert.test.ts";
26
+
27
+ // Tables
28
+ export * from "./tables/createTable.test.ts";
29
+ export * from "./tables/deleteTable.test.ts";
30
+ export * from "./tables/getAppTables.test.ts";
31
+ export * from "./tables/getRelationships.test.ts";
32
+ export * from "./tables/getTable.test.ts";
33
+ export * from "./tables/updateTable.test.ts";