quickbase-js 2.3.0 → 2.4.1
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/README.md +145 -1107
- package/dist/auth/ticket.d.ts.map +1 -1
- package/dist/auth/ticket.js +4 -3
- package/dist/auth/ticket.js.map +1 -1
- package/dist/client/request.d.ts +4 -0
- package/dist/client/request.d.ts.map +1 -1
- package/dist/client/request.js +15 -3
- package/dist/client/request.js.map +1 -1
- package/dist/generated/client.d.ts +2 -0
- package/dist/generated/client.d.ts.map +1 -1
- package/dist/generated/client.js +134 -113
- package/dist/generated/client.js.map +1 -1
- package/dist/generated/types.d.ts +2700 -2256
- package/dist/generated/types.d.ts.map +1 -1
- package/dist/generated/types.js +0 -2
- package/dist/generated/types.js.map +1 -1
- package/dist/quickbase.esm.js +3 -3
- package/dist/quickbase.esm.js.map +3 -3
- package/dist/quickbase.min.js +3 -3
- package/dist/quickbase.min.js.map +3 -3
- package/package.json +16 -13
- package/src/auth/index.ts +55 -0
- package/src/auth/sso-token.ts +143 -0
- package/src/auth/temp-token.ts +160 -0
- package/src/auth/ticket.ts +329 -0
- package/src/auth/token-cache.ts +97 -0
- package/src/auth/types.ts +50 -0
- package/src/auth/user-token.ts +38 -0
- package/src/cli/schema.ts +512 -0
- package/src/client/index.ts +36 -0
- package/src/client/pagination.ts +337 -0
- package/src/client/request.ts +544 -0
- package/src/client/retry.ts +169 -0
- package/src/client/throttle.ts +105 -0
- package/src/core/config.ts +160 -0
- package/src/core/dates.ts +63 -0
- package/src/core/errors.ts +349 -0
- package/src/core/index.ts +50 -0
- package/src/core/logger.ts +113 -0
- package/src/core/schema.ts +213 -0
- package/src/core/transform.ts +295 -0
- package/src/core/types.ts +337 -0
- package/src/generated/client.ts +1785 -0
- package/src/generated/operations.json +672 -0
- package/src/generated/types.ts +4884 -0
- package/src/index.ts +259 -0
- package/src/xml/codepages.ts +119 -0
- package/src/xml/dbvars.ts +75 -0
- package/src/xml/errors.ts +133 -0
- package/src/xml/fields.ts +121 -0
- package/src/xml/groups.ts +560 -0
- package/src/xml/html.ts +152 -0
- package/src/xml/index.ts +1196 -0
- package/src/xml/metadata.ts +111 -0
- package/src/xml/records.ts +301 -0
- package/src/xml/request.ts +238 -0
- package/src/xml/roles.ts +321 -0
- package/src/xml/schema.ts +436 -0
- package/src/xml/transform.ts +381 -0
- package/src/xml/types.ts +602 -0
- package/src/xml/users.ts +221 -0
- package/src/xml/webhooks.ts +260 -0
package/src/xml/index.ts
ADDED
|
@@ -0,0 +1,1196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XML API Client Module
|
|
3
|
+
*
|
|
4
|
+
* Provides access to legacy QuickBase XML API endpoints that have no
|
|
5
|
+
* JSON API equivalent (roles, groups, DBVars, code pages, etc.).
|
|
6
|
+
*
|
|
7
|
+
* XML-API: Remove this file when QuickBase discontinues the XML API.
|
|
8
|
+
* To find all XML API code, search for: XML-API
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { createClient } from 'quickbase-js';
|
|
13
|
+
* import { createXmlClient } from 'quickbase-js/xml';
|
|
14
|
+
*
|
|
15
|
+
* const qb = createClient({
|
|
16
|
+
* realm: 'myrealm',
|
|
17
|
+
* auth: { type: 'user-token', userToken: '...' },
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* const xml = createXmlClient(qb);
|
|
21
|
+
*
|
|
22
|
+
* // Get all roles in an app
|
|
23
|
+
* const roles = await xml.getRoleInfo('bqxyz123');
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
// Re-export types
|
|
28
|
+
export * from './types.js';
|
|
29
|
+
export { XmlError, XmlErrorCode, checkError, isUnauthorized, isNotFound, isInvalidTicket, isInvalidInput } from './errors.js';
|
|
30
|
+
|
|
31
|
+
// Import ReadOnlyError for consistent error handling
|
|
32
|
+
import { ReadOnlyError } from '../core/errors.js';
|
|
33
|
+
import type { ResolvedSchema } from '../core/types.js';
|
|
34
|
+
import { resolveTableAlias, resolveFieldAlias } from '../core/schema.js';
|
|
35
|
+
import { buildRequest, escapeXml, injectAppToken, injectUserToken } from './request.js';
|
|
36
|
+
|
|
37
|
+
// Import response transformation functions and types
|
|
38
|
+
import {
|
|
39
|
+
transformGetRecordInfoResult,
|
|
40
|
+
transformGetSchemaResult,
|
|
41
|
+
transformFieldAddChoicesResult,
|
|
42
|
+
transformFieldRemoveChoicesResult,
|
|
43
|
+
transformGrantedDBsResult,
|
|
44
|
+
transformGrantedDBsForGroupResult,
|
|
45
|
+
transformGetAppDTMInfoResult,
|
|
46
|
+
transformFindDBByNameResult,
|
|
47
|
+
type GetRecordInfoResultWithAlias,
|
|
48
|
+
type GetSchemaResultWithAlias,
|
|
49
|
+
type FieldAddChoicesResultWithAlias,
|
|
50
|
+
type FieldRemoveChoicesResultWithAlias,
|
|
51
|
+
type GrantedDBsResultWithAlias,
|
|
52
|
+
type GrantedDBsForGroupResultWithAlias,
|
|
53
|
+
type GetAppDTMInfoResultWithAlias,
|
|
54
|
+
type FindDBByNameResultWithAlias,
|
|
55
|
+
} from './transform.js';
|
|
56
|
+
|
|
57
|
+
// Re-export transformation types for consumers
|
|
58
|
+
export type {
|
|
59
|
+
RecordFieldWithAlias,
|
|
60
|
+
SchemaFieldWithAlias,
|
|
61
|
+
DatabaseInfoWithAlias,
|
|
62
|
+
SchemaChildTableWithAlias,
|
|
63
|
+
GetRecordInfoResultWithAlias,
|
|
64
|
+
GetSchemaResultWithAlias,
|
|
65
|
+
FieldAddChoicesResultWithAlias,
|
|
66
|
+
FieldRemoveChoicesResultWithAlias,
|
|
67
|
+
GrantedDBsResultWithAlias,
|
|
68
|
+
GrantedDBsForGroupResultWithAlias,
|
|
69
|
+
GetAppDTMInfoResultWithAlias,
|
|
70
|
+
FindDBByNameResultWithAlias,
|
|
71
|
+
} from './transform.js';
|
|
72
|
+
|
|
73
|
+
// Import types for XmlClient
|
|
74
|
+
import type { XmlCaller } from './types.js';
|
|
75
|
+
import type {
|
|
76
|
+
GetRoleInfoResult,
|
|
77
|
+
UserRolesResult,
|
|
78
|
+
GetUserRoleResult,
|
|
79
|
+
GetUsersInGroupResult,
|
|
80
|
+
CreateGroupResult,
|
|
81
|
+
GetGroupRoleResult,
|
|
82
|
+
ProvisionUserResult,
|
|
83
|
+
UserInfo,
|
|
84
|
+
DBInfo,
|
|
85
|
+
AddReplaceDBPageResult,
|
|
86
|
+
PageType,
|
|
87
|
+
DoQueryCountResult,
|
|
88
|
+
GrantedGroupsResult,
|
|
89
|
+
GetAncestorInfoResult,
|
|
90
|
+
ImportFromCSVResult,
|
|
91
|
+
CopyMasterDetailResult,
|
|
92
|
+
WebhooksCreateOptions,
|
|
93
|
+
WebhooksEditOptions,
|
|
94
|
+
} from './types.js';
|
|
95
|
+
|
|
96
|
+
// Import endpoint implementations
|
|
97
|
+
import {
|
|
98
|
+
getRoleInfo as getRoleInfoImpl,
|
|
99
|
+
userRoles as userRolesImpl,
|
|
100
|
+
getUserRole as getUserRoleImpl,
|
|
101
|
+
addUserToRole as addUserToRoleImpl,
|
|
102
|
+
removeUserFromRole as removeUserFromRoleImpl,
|
|
103
|
+
changeUserRole as changeUserRoleImpl,
|
|
104
|
+
} from './roles.js';
|
|
105
|
+
|
|
106
|
+
import {
|
|
107
|
+
createGroup as createGroupImpl,
|
|
108
|
+
deleteGroup as deleteGroupImpl,
|
|
109
|
+
copyGroup as copyGroupImpl,
|
|
110
|
+
changeGroupInfo as changeGroupInfoImpl,
|
|
111
|
+
getUsersInGroup as getUsersInGroupImpl,
|
|
112
|
+
addUserToGroup as addUserToGroupImpl,
|
|
113
|
+
removeUserFromGroup as removeUserFromGroupImpl,
|
|
114
|
+
getGroupRole as getGroupRoleImpl,
|
|
115
|
+
addGroupToRole as addGroupToRoleImpl,
|
|
116
|
+
removeGroupFromRole as removeGroupFromRoleImpl,
|
|
117
|
+
grantedGroups as grantedGroupsImpl,
|
|
118
|
+
grantedDBsForGroup as grantedDBsForGroupImpl,
|
|
119
|
+
addSubgroup as addSubgroupImpl,
|
|
120
|
+
removeSubgroup as removeSubgroupImpl,
|
|
121
|
+
} from './groups.js';
|
|
122
|
+
|
|
123
|
+
import {
|
|
124
|
+
getUserInfo as getUserInfoImpl,
|
|
125
|
+
provisionUser as provisionUserImpl,
|
|
126
|
+
sendInvitation as sendInvitationImpl,
|
|
127
|
+
changeManager as changeManagerImpl,
|
|
128
|
+
changeRecordOwner as changeRecordOwnerImpl,
|
|
129
|
+
} from './users.js';
|
|
130
|
+
|
|
131
|
+
import {
|
|
132
|
+
grantedDBs as grantedDBsImpl,
|
|
133
|
+
findDBByName as findDBByNameImpl,
|
|
134
|
+
getDBInfo as getDBInfoImpl,
|
|
135
|
+
getNumRecords as getNumRecordsImpl,
|
|
136
|
+
getSchema as getSchemaImpl,
|
|
137
|
+
} from './schema.js';
|
|
138
|
+
|
|
139
|
+
import {
|
|
140
|
+
getDBVar as getDBVarImpl,
|
|
141
|
+
setDBVar as setDBVarImpl,
|
|
142
|
+
} from './dbvars.js';
|
|
143
|
+
|
|
144
|
+
import {
|
|
145
|
+
getDBPage as getDBPageImpl,
|
|
146
|
+
addReplaceDBPage as addReplaceDBPageImpl,
|
|
147
|
+
} from './codepages.js';
|
|
148
|
+
|
|
149
|
+
import {
|
|
150
|
+
doQueryCount as doQueryCountImpl,
|
|
151
|
+
getRecordInfo as getRecordInfoImpl,
|
|
152
|
+
getRecordInfoByKey as getRecordInfoByKeyImpl,
|
|
153
|
+
importFromCSV as importFromCSVImpl,
|
|
154
|
+
runImport as runImportImpl,
|
|
155
|
+
copyMasterDetail as copyMasterDetailImpl,
|
|
156
|
+
} from './records.js';
|
|
157
|
+
|
|
158
|
+
import {
|
|
159
|
+
webhooksCreate as webhooksCreateImpl,
|
|
160
|
+
webhooksEdit as webhooksEditImpl,
|
|
161
|
+
webhooksDelete as webhooksDeleteImpl,
|
|
162
|
+
webhooksActivate as webhooksActivateImpl,
|
|
163
|
+
webhooksDeactivate as webhooksDeactivateImpl,
|
|
164
|
+
webhooksCopy as webhooksCopyImpl,
|
|
165
|
+
} from './webhooks.js';
|
|
166
|
+
|
|
167
|
+
import {
|
|
168
|
+
fieldAddChoices as fieldAddChoicesImpl,
|
|
169
|
+
fieldRemoveChoices as fieldRemoveChoicesImpl,
|
|
170
|
+
setKeyField as setKeyFieldImpl,
|
|
171
|
+
} from './fields.js';
|
|
172
|
+
|
|
173
|
+
import {
|
|
174
|
+
genAddRecordForm as genAddRecordFormImpl,
|
|
175
|
+
genResultsTable as genResultsTableImpl,
|
|
176
|
+
getRecordAsHTML as getRecordAsHTMLImpl,
|
|
177
|
+
} from './html.js';
|
|
178
|
+
|
|
179
|
+
import {
|
|
180
|
+
getAppDTMInfo as getAppDTMInfoImpl,
|
|
181
|
+
getAncestorInfo as getAncestorInfoImpl,
|
|
182
|
+
signOut as signOutImpl,
|
|
183
|
+
} from './metadata.js';
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Set of XML API actions that modify data.
|
|
187
|
+
* Used to enforce read-only mode.
|
|
188
|
+
*/
|
|
189
|
+
const xmlWriteActions = new Set([
|
|
190
|
+
// User/Role management
|
|
191
|
+
'API_AddUserToRole',
|
|
192
|
+
'API_RemoveUserFromRole',
|
|
193
|
+
'API_ChangeUserRole',
|
|
194
|
+
'API_ProvisionUser',
|
|
195
|
+
'API_SendInvitation',
|
|
196
|
+
'API_ChangeManager',
|
|
197
|
+
'API_ChangeRecordOwner',
|
|
198
|
+
|
|
199
|
+
// Group management
|
|
200
|
+
'API_CreateGroup',
|
|
201
|
+
'API_DeleteGroup',
|
|
202
|
+
'API_AddUserToGroup',
|
|
203
|
+
'API_RemoveUserFromGroup',
|
|
204
|
+
'API_AddGroupToRole',
|
|
205
|
+
'API_RemoveGroupFromRole',
|
|
206
|
+
'API_CopyGroup',
|
|
207
|
+
'API_ChangeGroupInfo',
|
|
208
|
+
'API_AddSubgroup',
|
|
209
|
+
'API_RemoveSubgroup',
|
|
210
|
+
|
|
211
|
+
// Variables
|
|
212
|
+
'API_SetDBVar',
|
|
213
|
+
|
|
214
|
+
// Code pages
|
|
215
|
+
'API_AddReplaceDBPage',
|
|
216
|
+
|
|
217
|
+
// Fields
|
|
218
|
+
'API_FieldAddChoices',
|
|
219
|
+
'API_FieldRemoveChoices',
|
|
220
|
+
'API_SetKeyField',
|
|
221
|
+
|
|
222
|
+
// Webhooks
|
|
223
|
+
'API_Webhooks_Create',
|
|
224
|
+
'API_Webhooks_Edit',
|
|
225
|
+
'API_Webhooks_Delete',
|
|
226
|
+
'API_Webhooks_Activate',
|
|
227
|
+
'API_Webhooks_Deactivate',
|
|
228
|
+
'API_Webhooks_Copy',
|
|
229
|
+
|
|
230
|
+
// Records/Import
|
|
231
|
+
'API_ImportFromCSV',
|
|
232
|
+
'API_RunImport',
|
|
233
|
+
'API_CopyMasterDetail',
|
|
234
|
+
'API_PurgeRecords',
|
|
235
|
+
'API_AddRecord',
|
|
236
|
+
'API_EditRecord',
|
|
237
|
+
'API_DeleteRecord',
|
|
238
|
+
|
|
239
|
+
// Auth (clears session state)
|
|
240
|
+
'API_SignOut',
|
|
241
|
+
]);
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Check if an XML API action modifies data.
|
|
245
|
+
*/
|
|
246
|
+
export function isXmlWriteAction(action: string): boolean {
|
|
247
|
+
return xmlWriteActions.has(action);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Options for creating an XML client
|
|
252
|
+
*/
|
|
253
|
+
export interface XmlClientOptions {
|
|
254
|
+
/** Block all write operations (read-only mode) */
|
|
255
|
+
readOnly?: boolean;
|
|
256
|
+
/** Schema for table/field alias resolution (typically inherited from main client) */
|
|
257
|
+
schema?: ResolvedSchema;
|
|
258
|
+
/** App token for apps that require application tokens (XML API only) */
|
|
259
|
+
appToken?: string;
|
|
260
|
+
/**
|
|
261
|
+
* Custom fetch function for direct HTTP requests (e.g., GetAppDTMInfo which
|
|
262
|
+
* bypasses doXml to avoid sending auth tokens). Defaults to globalThis.fetch.
|
|
263
|
+
*/
|
|
264
|
+
fetchApi?: typeof globalThis.fetch;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* XML API Client
|
|
269
|
+
*
|
|
270
|
+
* Provides methods for calling legacy QuickBase XML API endpoints.
|
|
271
|
+
* Create using the `createXmlClient()` function.
|
|
272
|
+
*/
|
|
273
|
+
export class XmlClient {
|
|
274
|
+
private readonly caller: XmlCaller;
|
|
275
|
+
private readonly readOnly: boolean;
|
|
276
|
+
private readonly schema?: ResolvedSchema;
|
|
277
|
+
private readonly fetchApi: typeof globalThis.fetch;
|
|
278
|
+
|
|
279
|
+
constructor(caller: XmlCaller, options?: XmlClientOptions) {
|
|
280
|
+
this.caller = caller;
|
|
281
|
+
this.readOnly = options?.readOnly ?? false;
|
|
282
|
+
this.schema = options?.schema;
|
|
283
|
+
this.fetchApi = options?.fetchApi ?? globalThis.fetch;
|
|
284
|
+
// Note: appToken is handled by the caller closure, not stored here
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Resolve a table alias to its ID using the configured schema.
|
|
289
|
+
* If no schema is configured or the input is already a table ID, returns unchanged.
|
|
290
|
+
*/
|
|
291
|
+
private resolveTable(tableRef: string): string {
|
|
292
|
+
return resolveTableAlias(this.schema, tableRef);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Resolve a field alias to its ID using the configured schema.
|
|
297
|
+
* If no schema is configured or the input is already a field ID, returns unchanged.
|
|
298
|
+
*/
|
|
299
|
+
private resolveField(tableId: string, fieldRef: string | number): number {
|
|
300
|
+
return resolveFieldAlias(this.schema, tableId, fieldRef);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Check if an action is allowed (not blocked by read-only mode).
|
|
305
|
+
* Throws ReadOnlyError if the action is blocked.
|
|
306
|
+
*/
|
|
307
|
+
private checkWriteAllowed(action: string): void {
|
|
308
|
+
if (this.readOnly && isXmlWriteAction(action)) {
|
|
309
|
+
throw new ReadOnlyError('POST', `/db/${this.caller.realm()}`, action);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Execute a raw XML API request.
|
|
315
|
+
* Enforces read-only mode if enabled.
|
|
316
|
+
* This is exposed for direct XML API calls not covered by typed methods.
|
|
317
|
+
*/
|
|
318
|
+
async executeXml(dbid: string, action: string, body: string): Promise<string> {
|
|
319
|
+
this.checkWriteAllowed(action);
|
|
320
|
+
return this.caller.doXml(dbid, action, body);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Get the realm name
|
|
325
|
+
*/
|
|
326
|
+
realm(): string {
|
|
327
|
+
return this.caller.realm();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ============================================================================
|
|
331
|
+
// Role Management
|
|
332
|
+
// ============================================================================
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Get all roles defined in an application.
|
|
336
|
+
* @see https://help.quickbase.com/docs/api-getroleinfo
|
|
337
|
+
*/
|
|
338
|
+
async getRoleInfo(appId: string): Promise<GetRoleInfoResult> {
|
|
339
|
+
return getRoleInfoImpl(this.caller, this.resolveTable(appId));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Get all users in an application and their role assignments.
|
|
344
|
+
* @see https://help.quickbase.com/docs/api-userroles
|
|
345
|
+
*/
|
|
346
|
+
async userRoles(appId: string): Promise<UserRolesResult> {
|
|
347
|
+
return userRolesImpl(this.caller, this.resolveTable(appId));
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Get roles for a specific user.
|
|
352
|
+
* @see https://help.quickbase.com/docs/api-getuserrole
|
|
353
|
+
*/
|
|
354
|
+
async getUserRole(
|
|
355
|
+
appId: string,
|
|
356
|
+
userId?: string,
|
|
357
|
+
includeGroups?: boolean
|
|
358
|
+
): Promise<GetUserRoleResult> {
|
|
359
|
+
return getUserRoleImpl(this.caller, this.resolveTable(appId), userId, includeGroups);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Assign a user to a role.
|
|
364
|
+
* @see https://help.quickbase.com/docs/api-addusertorole
|
|
365
|
+
*/
|
|
366
|
+
async addUserToRole(appId: string, userId: string, roleId: number): Promise<void> {
|
|
367
|
+
this.checkWriteAllowed('API_AddUserToRole');
|
|
368
|
+
return addUserToRoleImpl(this.caller, this.resolveTable(appId), userId, roleId);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Remove a user from a role.
|
|
373
|
+
* @see https://help.quickbase.com/docs/api-removeuserfromrole
|
|
374
|
+
*/
|
|
375
|
+
async removeUserFromRole(appId: string, userId: string, roleId: number): Promise<void> {
|
|
376
|
+
this.checkWriteAllowed('API_RemoveUserFromRole');
|
|
377
|
+
return removeUserFromRoleImpl(this.caller, this.resolveTable(appId), userId, roleId);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Change a user's role.
|
|
382
|
+
* @see https://help.quickbase.com/docs/api-changeuserrole
|
|
383
|
+
*/
|
|
384
|
+
async changeUserRole(
|
|
385
|
+
appId: string,
|
|
386
|
+
userId: string,
|
|
387
|
+
currentRoleId: number,
|
|
388
|
+
newRoleId?: number
|
|
389
|
+
): Promise<void> {
|
|
390
|
+
this.checkWriteAllowed('API_ChangeUserRole');
|
|
391
|
+
return changeUserRoleImpl(this.caller, this.resolveTable(appId), userId, currentRoleId, newRoleId);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ============================================================================
|
|
395
|
+
// Group Management
|
|
396
|
+
// ============================================================================
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Create a new group.
|
|
400
|
+
* @see https://help.quickbase.com/docs/api-creategroup
|
|
401
|
+
*/
|
|
402
|
+
async createGroup(
|
|
403
|
+
name: string,
|
|
404
|
+
description?: string,
|
|
405
|
+
accountId?: string
|
|
406
|
+
): Promise<CreateGroupResult> {
|
|
407
|
+
this.checkWriteAllowed('API_CreateGroup');
|
|
408
|
+
return createGroupImpl(this.caller, name, description, accountId);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Delete a group.
|
|
413
|
+
* @see https://help.quickbase.com/docs/api-deletegroup
|
|
414
|
+
*/
|
|
415
|
+
async deleteGroup(groupId: string): Promise<void> {
|
|
416
|
+
this.checkWriteAllowed('API_DeleteGroup');
|
|
417
|
+
return deleteGroupImpl(this.caller, groupId);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Copy a group.
|
|
422
|
+
* @see https://help.quickbase.com/docs/api-copygroup
|
|
423
|
+
*/
|
|
424
|
+
async copyGroup(
|
|
425
|
+
groupId: string,
|
|
426
|
+
name?: string,
|
|
427
|
+
description?: string,
|
|
428
|
+
accountId?: string
|
|
429
|
+
): Promise<CreateGroupResult> {
|
|
430
|
+
this.checkWriteAllowed('API_CopyGroup');
|
|
431
|
+
return copyGroupImpl(this.caller, groupId, name, description, accountId);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Change group information.
|
|
436
|
+
* @see https://help.quickbase.com/docs/api-changegroupinfo
|
|
437
|
+
*/
|
|
438
|
+
async changeGroupInfo(
|
|
439
|
+
groupId: string,
|
|
440
|
+
name?: string,
|
|
441
|
+
description?: string,
|
|
442
|
+
accountId?: string
|
|
443
|
+
): Promise<void> {
|
|
444
|
+
this.checkWriteAllowed('API_ChangeGroupInfo');
|
|
445
|
+
return changeGroupInfoImpl(this.caller, groupId, name, description, accountId);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Get users and managers in a group.
|
|
450
|
+
* @see https://help.quickbase.com/docs/api-getusersingroup
|
|
451
|
+
*/
|
|
452
|
+
async getUsersInGroup(groupId: string, includeManagers?: boolean): Promise<GetUsersInGroupResult> {
|
|
453
|
+
return getUsersInGroupImpl(this.caller, groupId, includeManagers);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Add a user to a group.
|
|
458
|
+
* @see https://help.quickbase.com/docs/api-addusertogroup
|
|
459
|
+
*/
|
|
460
|
+
async addUserToGroup(
|
|
461
|
+
groupId: string,
|
|
462
|
+
userId: string,
|
|
463
|
+
allowAdminAccess?: boolean
|
|
464
|
+
): Promise<void> {
|
|
465
|
+
this.checkWriteAllowed('API_AddUserToGroup');
|
|
466
|
+
return addUserToGroupImpl(this.caller, groupId, userId, allowAdminAccess);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Remove a user from a group.
|
|
471
|
+
* @see https://help.quickbase.com/docs/api-removeuserfromgroup
|
|
472
|
+
*/
|
|
473
|
+
async removeUserFromGroup(groupId: string, userId: string): Promise<void> {
|
|
474
|
+
this.checkWriteAllowed('API_RemoveUserFromGroup');
|
|
475
|
+
return removeUserFromGroupImpl(this.caller, groupId, userId);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Get roles assigned to a group in an app.
|
|
480
|
+
* @see https://help.quickbase.com/docs/api-getgrouprole
|
|
481
|
+
*/
|
|
482
|
+
async getGroupRole(appId: string, groupId: string): Promise<GetGroupRoleResult> {
|
|
483
|
+
return getGroupRoleImpl(this.caller, this.resolveTable(appId), groupId);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Assign a group to a role.
|
|
488
|
+
* @see https://help.quickbase.com/docs/api-addgrouptorole
|
|
489
|
+
*/
|
|
490
|
+
async addGroupToRole(appId: string, groupId: string, roleId: number): Promise<void> {
|
|
491
|
+
this.checkWriteAllowed('API_AddGroupToRole');
|
|
492
|
+
return addGroupToRoleImpl(this.caller, this.resolveTable(appId), groupId, roleId);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Remove a group from a role.
|
|
497
|
+
* @see https://help.quickbase.com/docs/api-removegroupfromrole
|
|
498
|
+
*/
|
|
499
|
+
async removeGroupFromRole(
|
|
500
|
+
appId: string,
|
|
501
|
+
groupId: string,
|
|
502
|
+
roleId: number,
|
|
503
|
+
allRoles?: boolean
|
|
504
|
+
): Promise<void> {
|
|
505
|
+
this.checkWriteAllowed('API_RemoveGroupFromRole');
|
|
506
|
+
return removeGroupFromRoleImpl(this.caller, this.resolveTable(appId), groupId, roleId, allRoles);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Get groups a user belongs to.
|
|
511
|
+
* @see https://help.quickbase.com/docs/api-grantedgroups
|
|
512
|
+
*/
|
|
513
|
+
async grantedGroups(userId?: string, adminOnly?: boolean): Promise<GrantedGroupsResult> {
|
|
514
|
+
return grantedGroupsImpl(this.caller, userId, adminOnly);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Get apps a group can access.
|
|
519
|
+
* @see https://help.quickbase.com/docs/api-granteddbsforgroup
|
|
520
|
+
*/
|
|
521
|
+
async grantedDBsForGroup(groupId: string): Promise<GrantedDBsForGroupResultWithAlias> {
|
|
522
|
+
const result = await grantedDBsForGroupImpl(this.caller, groupId);
|
|
523
|
+
return transformGrantedDBsForGroupResult(result, this.schema);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Add a subgroup to a group.
|
|
528
|
+
* @see https://help.quickbase.com/docs/api-addsubgroup
|
|
529
|
+
*/
|
|
530
|
+
async addSubgroup(groupId: string, subgroupId: string): Promise<void> {
|
|
531
|
+
this.checkWriteAllowed('API_AddSubgroup');
|
|
532
|
+
return addSubgroupImpl(this.caller, groupId, subgroupId);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Remove a subgroup from a group.
|
|
537
|
+
* @see https://help.quickbase.com/docs/api-removesubgroup
|
|
538
|
+
*/
|
|
539
|
+
async removeSubgroup(groupId: string, subgroupId: string): Promise<void> {
|
|
540
|
+
this.checkWriteAllowed('API_RemoveSubgroup');
|
|
541
|
+
return removeSubgroupImpl(this.caller, groupId, subgroupId);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
// ============================================================================
|
|
545
|
+
// User Management
|
|
546
|
+
// ============================================================================
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Get user information by email.
|
|
550
|
+
* @see https://help.quickbase.com/docs/api-getuserinfo
|
|
551
|
+
*/
|
|
552
|
+
async getUserInfo(email?: string): Promise<UserInfo> {
|
|
553
|
+
return getUserInfoImpl(this.caller, email);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Create a new unregistered user.
|
|
558
|
+
* @see https://help.quickbase.com/docs/api-provisionuser
|
|
559
|
+
*/
|
|
560
|
+
async provisionUser(
|
|
561
|
+
appId: string,
|
|
562
|
+
email: string,
|
|
563
|
+
firstName: string,
|
|
564
|
+
lastName: string,
|
|
565
|
+
roleId?: number
|
|
566
|
+
): Promise<ProvisionUserResult> {
|
|
567
|
+
this.checkWriteAllowed('API_ProvisionUser');
|
|
568
|
+
return provisionUserImpl(this.caller, this.resolveTable(appId), email, firstName, lastName, roleId);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Send invitation email to a user.
|
|
573
|
+
* @see https://help.quickbase.com/docs/api-sendinvitation
|
|
574
|
+
*/
|
|
575
|
+
async sendInvitation(appId: string, userId: string, userText?: string): Promise<void> {
|
|
576
|
+
this.checkWriteAllowed('API_SendInvitation');
|
|
577
|
+
return sendInvitationImpl(this.caller, this.resolveTable(appId), userId, userText);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Change the application manager.
|
|
582
|
+
* @see https://help.quickbase.com/docs/api-changemanager
|
|
583
|
+
*/
|
|
584
|
+
async changeManager(appId: string, newManagerEmail: string): Promise<void> {
|
|
585
|
+
this.checkWriteAllowed('API_ChangeManager');
|
|
586
|
+
return changeManagerImpl(this.caller, this.resolveTable(appId), newManagerEmail);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Change the owner of a record.
|
|
591
|
+
* @see https://help.quickbase.com/docs/api-changerecordowner
|
|
592
|
+
*/
|
|
593
|
+
async changeRecordOwner(
|
|
594
|
+
tableId: string,
|
|
595
|
+
recordId: number,
|
|
596
|
+
newOwner: string
|
|
597
|
+
): Promise<void> {
|
|
598
|
+
this.checkWriteAllowed('API_ChangeRecordOwner');
|
|
599
|
+
return changeRecordOwnerImpl(this.caller, this.resolveTable(tableId), recordId, newOwner);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// ============================================================================
|
|
603
|
+
// App Discovery
|
|
604
|
+
// ============================================================================
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* List all apps/tables the user can access.
|
|
608
|
+
* @see https://help.quickbase.com/docs/api-granteddbs
|
|
609
|
+
*/
|
|
610
|
+
async grantedDBs(options?: {
|
|
611
|
+
parentsOnly?: boolean;
|
|
612
|
+
excludeParents?: boolean;
|
|
613
|
+
adminOnly?: boolean;
|
|
614
|
+
includeAncestors?: boolean;
|
|
615
|
+
withEmbeddedTables?: boolean;
|
|
616
|
+
realmAppsOnly?: boolean;
|
|
617
|
+
}): Promise<GrantedDBsResultWithAlias> {
|
|
618
|
+
const result = await grantedDBsImpl(this.caller, options);
|
|
619
|
+
return transformGrantedDBsResult(result, this.schema);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Find an app by name.
|
|
624
|
+
* @see https://help.quickbase.com/docs/api-finddbbyname
|
|
625
|
+
*/
|
|
626
|
+
async findDBByName(name: string, parentsOnly?: boolean): Promise<FindDBByNameResultWithAlias> {
|
|
627
|
+
const result = await findDBByNameImpl(this.caller, name, parentsOnly);
|
|
628
|
+
return transformFindDBByNameResult(result, this.schema);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Get app/table metadata.
|
|
633
|
+
* @see https://help.quickbase.com/docs/api-getdbinfo
|
|
634
|
+
*/
|
|
635
|
+
async getDBInfo(dbid: string): Promise<DBInfo> {
|
|
636
|
+
return getDBInfoImpl(this.caller, this.resolveTable(dbid));
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Get total record count for a table.
|
|
641
|
+
* @see https://help.quickbase.com/docs/api-getnumrecords
|
|
642
|
+
*/
|
|
643
|
+
async getNumRecords(tableId: string): Promise<number> {
|
|
644
|
+
return getNumRecordsImpl(this.caller, this.resolveTable(tableId));
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// ============================================================================
|
|
648
|
+
// Schema Information
|
|
649
|
+
// ============================================================================
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Get comprehensive app/table metadata.
|
|
653
|
+
* @see https://help.quickbase.com/docs/api-getschema
|
|
654
|
+
*/
|
|
655
|
+
async getSchema(dbid: string): Promise<GetSchemaResultWithAlias> {
|
|
656
|
+
const resolvedDbid = this.resolveTable(dbid);
|
|
657
|
+
const result = await getSchemaImpl(this.caller, resolvedDbid);
|
|
658
|
+
return transformGetSchemaResult(result, this.schema, resolvedDbid);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// ============================================================================
|
|
662
|
+
// Application Variables (DBVars)
|
|
663
|
+
// ============================================================================
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Get an application variable value.
|
|
667
|
+
* @see https://help.quickbase.com/docs/api-getdbvar
|
|
668
|
+
*/
|
|
669
|
+
async getDBVar(appId: string, varName: string): Promise<string> {
|
|
670
|
+
return getDBVarImpl(this.caller, this.resolveTable(appId), varName);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Set an application variable value.
|
|
675
|
+
* @see https://help.quickbase.com/docs/api-setdbvar
|
|
676
|
+
*/
|
|
677
|
+
async setDBVar(appId: string, varName: string, value: string): Promise<void> {
|
|
678
|
+
this.checkWriteAllowed('API_SetDBVar');
|
|
679
|
+
return setDBVarImpl(this.caller, this.resolveTable(appId), varName, value);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// ============================================================================
|
|
683
|
+
// Code Pages
|
|
684
|
+
// ============================================================================
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Get stored code page content.
|
|
688
|
+
* @see https://help.quickbase.com/docs/api-getdbpage
|
|
689
|
+
*/
|
|
690
|
+
async getDBPage(appId: string, pageIdOrName: string | number): Promise<string> {
|
|
691
|
+
return getDBPageImpl(this.caller, this.resolveTable(appId), pageIdOrName);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Create or update a code page.
|
|
696
|
+
* @see https://help.quickbase.com/docs/api-addreplacedbpage
|
|
697
|
+
*/
|
|
698
|
+
async addReplaceDBPage(
|
|
699
|
+
appId: string,
|
|
700
|
+
pageName: string,
|
|
701
|
+
pageId: number | undefined,
|
|
702
|
+
pageType: PageType,
|
|
703
|
+
pageBody: string
|
|
704
|
+
): Promise<AddReplaceDBPageResult> {
|
|
705
|
+
this.checkWriteAllowed('API_AddReplaceDBPage');
|
|
706
|
+
return addReplaceDBPageImpl(this.caller, this.resolveTable(appId), pageName, pageId, pageType, pageBody);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// ============================================================================
|
|
710
|
+
// Field Management
|
|
711
|
+
// ============================================================================
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Add choices to a multiple-choice field.
|
|
715
|
+
* @see https://help.quickbase.com/docs/api-fieldaddchoices
|
|
716
|
+
*/
|
|
717
|
+
async fieldAddChoices(
|
|
718
|
+
tableId: string,
|
|
719
|
+
fieldId: number | string,
|
|
720
|
+
choices: string[]
|
|
721
|
+
): Promise<FieldAddChoicesResultWithAlias> {
|
|
722
|
+
this.checkWriteAllowed('API_FieldAddChoices');
|
|
723
|
+
const resolvedTableId = this.resolveTable(tableId);
|
|
724
|
+
const resolvedFieldId = this.resolveField(resolvedTableId, fieldId);
|
|
725
|
+
const result = await fieldAddChoicesImpl(this.caller, resolvedTableId, resolvedFieldId, choices);
|
|
726
|
+
return transformFieldAddChoicesResult(result, this.schema, resolvedTableId);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Remove choices from a multiple-choice field.
|
|
731
|
+
* @see https://help.quickbase.com/docs/api-fieldremovechoices
|
|
732
|
+
*/
|
|
733
|
+
async fieldRemoveChoices(
|
|
734
|
+
tableId: string,
|
|
735
|
+
fieldId: number | string,
|
|
736
|
+
choices: string[]
|
|
737
|
+
): Promise<FieldRemoveChoicesResultWithAlias> {
|
|
738
|
+
this.checkWriteAllowed('API_FieldRemoveChoices');
|
|
739
|
+
const resolvedTableId = this.resolveTable(tableId);
|
|
740
|
+
const resolvedFieldId = this.resolveField(resolvedTableId, fieldId);
|
|
741
|
+
const result = await fieldRemoveChoicesImpl(this.caller, resolvedTableId, resolvedFieldId, choices);
|
|
742
|
+
return transformFieldRemoveChoicesResult(result, this.schema, resolvedTableId);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* Set the key field for a table.
|
|
747
|
+
* @see https://help.quickbase.com/docs/api-setkeyfield
|
|
748
|
+
*/
|
|
749
|
+
async setKeyField(tableId: string, fieldId: number | string): Promise<void> {
|
|
750
|
+
this.checkWriteAllowed('API_SetKeyField');
|
|
751
|
+
const resolvedTableId = this.resolveTable(tableId);
|
|
752
|
+
const resolvedFieldId = this.resolveField(resolvedTableId, fieldId);
|
|
753
|
+
return setKeyFieldImpl(this.caller, resolvedTableId, resolvedFieldId);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
// ============================================================================
|
|
757
|
+
// Record Information
|
|
758
|
+
// ============================================================================
|
|
759
|
+
|
|
760
|
+
/**
|
|
761
|
+
* Get count of matching records without fetching data.
|
|
762
|
+
* @see https://help.quickbase.com/docs/api-doquerycount
|
|
763
|
+
*/
|
|
764
|
+
async doQueryCount(tableId: string, query?: string): Promise<DoQueryCountResult> {
|
|
765
|
+
return doQueryCountImpl(this.caller, this.resolveTable(tableId), query);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* Get a record with field metadata.
|
|
770
|
+
* @see https://help.quickbase.com/docs/api-getrecordinfo
|
|
771
|
+
*/
|
|
772
|
+
async getRecordInfo(tableId: string, recordId: number): Promise<GetRecordInfoResultWithAlias> {
|
|
773
|
+
const resolvedTableId = this.resolveTable(tableId);
|
|
774
|
+
const result = await getRecordInfoImpl(this.caller, resolvedTableId, recordId);
|
|
775
|
+
return transformGetRecordInfoResult(result, this.schema, resolvedTableId);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/**
|
|
779
|
+
* Get a record by key field value.
|
|
780
|
+
* @see https://help.quickbase.com/docs/api-getrecordinfo
|
|
781
|
+
*/
|
|
782
|
+
async getRecordInfoByKey(tableId: string, keyValue: string): Promise<GetRecordInfoResultWithAlias> {
|
|
783
|
+
const resolvedTableId = this.resolveTable(tableId);
|
|
784
|
+
const result = await getRecordInfoByKeyImpl(this.caller, resolvedTableId, keyValue);
|
|
785
|
+
return transformGetRecordInfoResult(result, this.schema, resolvedTableId);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Bulk import/update records from CSV data.
|
|
790
|
+
* @see https://help.quickbase.com/docs/api-importfromcsv
|
|
791
|
+
*/
|
|
792
|
+
async importFromCSV(
|
|
793
|
+
tableId: string,
|
|
794
|
+
options: {
|
|
795
|
+
recordsCsv: string;
|
|
796
|
+
clist?: (number | string)[];
|
|
797
|
+
skipFirst?: boolean;
|
|
798
|
+
mergeFieldId?: number | string;
|
|
799
|
+
decimalPercent?: boolean;
|
|
800
|
+
}
|
|
801
|
+
): Promise<ImportFromCSVResult> {
|
|
802
|
+
this.checkWriteAllowed('API_ImportFromCSV');
|
|
803
|
+
const resolvedTableId = this.resolveTable(tableId);
|
|
804
|
+
// Resolve field aliases in clist and mergeFieldId
|
|
805
|
+
const resolvedOptions = {
|
|
806
|
+
...options,
|
|
807
|
+
clist: options.clist?.map(f => this.resolveField(resolvedTableId, f)),
|
|
808
|
+
mergeFieldId: options.mergeFieldId !== undefined
|
|
809
|
+
? this.resolveField(resolvedTableId, options.mergeFieldId)
|
|
810
|
+
: undefined,
|
|
811
|
+
};
|
|
812
|
+
return importFromCSVImpl(this.caller, resolvedTableId, resolvedOptions);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Execute a saved import definition.
|
|
817
|
+
* @see https://help.quickbase.com/docs/api-runimport
|
|
818
|
+
*/
|
|
819
|
+
async runImport(tableId: string, importId: number): Promise<ImportFromCSVResult> {
|
|
820
|
+
this.checkWriteAllowed('API_RunImport');
|
|
821
|
+
return runImportImpl(this.caller, this.resolveTable(tableId), importId);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Copy a master record with its detail records.
|
|
826
|
+
* @see https://help.quickbase.com/docs/api-copymasterdetail
|
|
827
|
+
*/
|
|
828
|
+
async copyMasterDetail(
|
|
829
|
+
tableId: string,
|
|
830
|
+
options: {
|
|
831
|
+
rid: number;
|
|
832
|
+
copyfid?: number | string;
|
|
833
|
+
recurse?: boolean;
|
|
834
|
+
relfids?: (number | string)[];
|
|
835
|
+
}
|
|
836
|
+
): Promise<CopyMasterDetailResult> {
|
|
837
|
+
this.checkWriteAllowed('API_CopyMasterDetail');
|
|
838
|
+
const resolvedTableId = this.resolveTable(tableId);
|
|
839
|
+
// Resolve field aliases in copyfid and relfids
|
|
840
|
+
const resolvedOptions = {
|
|
841
|
+
...options,
|
|
842
|
+
copyfid: options.copyfid !== undefined
|
|
843
|
+
? this.resolveField(resolvedTableId, options.copyfid)
|
|
844
|
+
: undefined,
|
|
845
|
+
relfids: options.relfids?.map(f => this.resolveField(resolvedTableId, f)),
|
|
846
|
+
};
|
|
847
|
+
return copyMasterDetailImpl(this.caller, resolvedTableId, resolvedOptions);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
// ============================================================================
|
|
851
|
+
// App Metadata
|
|
852
|
+
// ============================================================================
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Get modification timestamps (fast, no auth required).
|
|
856
|
+
*
|
|
857
|
+
* This method bypasses the normal doXml transport and makes a direct
|
|
858
|
+
* unauthenticated HTTP request. The QuickBase docs state "No ticket or
|
|
859
|
+
* app token is required" for this call, and sending a user token causes
|
|
860
|
+
* rejection ("User Tokens cannot be used to call this API").
|
|
861
|
+
*
|
|
862
|
+
* @see https://help.quickbase.com/docs/api-getappdtminfo
|
|
863
|
+
*/
|
|
864
|
+
async getAppDTMInfo(appId: string): Promise<GetAppDTMInfoResultWithAlias> {
|
|
865
|
+
const resolvedId = this.resolveTable(appId);
|
|
866
|
+
const inner = `<dbid>${escapeXml(resolvedId)}</dbid>`;
|
|
867
|
+
const body = buildRequest(inner);
|
|
868
|
+
const url = `https://${this.caller.realm()}.quickbase.com/db/main`;
|
|
869
|
+
|
|
870
|
+
// Direct unauthenticated fetch — no user token, no app token
|
|
871
|
+
const response = await this.fetchApi(url, {
|
|
872
|
+
method: 'POST',
|
|
873
|
+
headers: {
|
|
874
|
+
'Content-Type': 'application/xml',
|
|
875
|
+
'QUICKBASE-ACTION': 'API_GetAppDTMInfo',
|
|
876
|
+
},
|
|
877
|
+
body,
|
|
878
|
+
credentials: 'omit',
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
const xml = await response.text();
|
|
882
|
+
|
|
883
|
+
// Reuse getAppDTMInfoImpl with a passthrough caller that returns pre-fetched XML
|
|
884
|
+
const result = await getAppDTMInfoImpl(
|
|
885
|
+
{ realm: this.caller.realm, doXml: async () => xml },
|
|
886
|
+
resolvedId
|
|
887
|
+
);
|
|
888
|
+
|
|
889
|
+
return transformGetAppDTMInfoResult(result, this.schema);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/**
|
|
893
|
+
* Get app copy/template lineage info.
|
|
894
|
+
* @see https://help.quickbase.com/docs/api-getancestorinfo
|
|
895
|
+
*/
|
|
896
|
+
async getAncestorInfo(appId: string): Promise<GetAncestorInfoResult> {
|
|
897
|
+
return getAncestorInfoImpl(this.caller, this.resolveTable(appId));
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// ============================================================================
|
|
901
|
+
// Webhooks
|
|
902
|
+
// ============================================================================
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* Create a webhook.
|
|
906
|
+
* @see https://help.quickbase.com/docs/api-webhooks-create
|
|
907
|
+
*/
|
|
908
|
+
async webhooksCreate(tableId: string, options: WebhooksCreateOptions): Promise<void> {
|
|
909
|
+
this.checkWriteAllowed('API_Webhooks_Create');
|
|
910
|
+
return webhooksCreateImpl(this.caller, this.resolveTable(tableId), options);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Edit a webhook.
|
|
915
|
+
* @see https://help.quickbase.com/docs/api-webhooks-edit
|
|
916
|
+
*/
|
|
917
|
+
async webhooksEdit(
|
|
918
|
+
tableId: string,
|
|
919
|
+
actionId: string,
|
|
920
|
+
options: WebhooksEditOptions
|
|
921
|
+
): Promise<void> {
|
|
922
|
+
this.checkWriteAllowed('API_Webhooks_Edit');
|
|
923
|
+
return webhooksEditImpl(this.caller, this.resolveTable(tableId), actionId, options);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/**
|
|
927
|
+
* Delete webhooks.
|
|
928
|
+
* @see https://help.quickbase.com/docs/api-webhooks-delete
|
|
929
|
+
*/
|
|
930
|
+
async webhooksDelete(tableId: string, actionIds: string[]): Promise<{ numChanged: number }> {
|
|
931
|
+
this.checkWriteAllowed('API_Webhooks_Delete');
|
|
932
|
+
return webhooksDeleteImpl(this.caller, this.resolveTable(tableId), actionIds);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/**
|
|
936
|
+
* Activate webhooks.
|
|
937
|
+
* @see https://help.quickbase.com/docs/api-webhooks-activate
|
|
938
|
+
*/
|
|
939
|
+
async webhooksActivate(tableId: string, actionIds: string[]): Promise<{ numChanged: number }> {
|
|
940
|
+
this.checkWriteAllowed('API_Webhooks_Activate');
|
|
941
|
+
return webhooksActivateImpl(this.caller, this.resolveTable(tableId), actionIds);
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Deactivate webhooks.
|
|
946
|
+
* @see https://help.quickbase.com/docs/api-webhooks-deactivate
|
|
947
|
+
*/
|
|
948
|
+
async webhooksDeactivate(tableId: string, actionIds: string[]): Promise<{ numChanged: number }> {
|
|
949
|
+
this.checkWriteAllowed('API_Webhooks_Deactivate');
|
|
950
|
+
return webhooksDeactivateImpl(this.caller, this.resolveTable(tableId), actionIds);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* Copy a webhook.
|
|
955
|
+
* @see https://help.quickbase.com/docs/api-webhooks-copy
|
|
956
|
+
*/
|
|
957
|
+
async webhooksCopy(tableId: string, actionId: string): Promise<{ actionId: string }> {
|
|
958
|
+
this.checkWriteAllowed('API_Webhooks_Copy');
|
|
959
|
+
return webhooksCopyImpl(this.caller, this.resolveTable(tableId), actionId);
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
// ============================================================================
|
|
963
|
+
// HTML Generation
|
|
964
|
+
// ============================================================================
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* Generate HTML form for adding a record.
|
|
968
|
+
* @see https://help.quickbase.com/docs/api-genaddrecordform
|
|
969
|
+
*/
|
|
970
|
+
async genAddRecordForm(
|
|
971
|
+
tableId: string,
|
|
972
|
+
fields?: Array<{ id: number | string; value: string }>
|
|
973
|
+
): Promise<string> {
|
|
974
|
+
const resolvedTableId = this.resolveTable(tableId);
|
|
975
|
+
const resolvedFields = fields?.map(f => ({
|
|
976
|
+
id: this.resolveField(resolvedTableId, f.id),
|
|
977
|
+
value: f.value,
|
|
978
|
+
}));
|
|
979
|
+
return genAddRecordFormImpl(this.caller, resolvedTableId, resolvedFields);
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Generate HTML/JS/CSV table of query results.
|
|
984
|
+
* @see https://help.quickbase.com/docs/api-genresultstable
|
|
985
|
+
*/
|
|
986
|
+
async genResultsTable(
|
|
987
|
+
tableId: string,
|
|
988
|
+
options?: {
|
|
989
|
+
query?: string;
|
|
990
|
+
clist?: (number | string)[];
|
|
991
|
+
slist?: (number | string)[];
|
|
992
|
+
options?: string;
|
|
993
|
+
format?: 'structured' | 'csv';
|
|
994
|
+
}
|
|
995
|
+
): Promise<string> {
|
|
996
|
+
const resolvedTableId = this.resolveTable(tableId);
|
|
997
|
+
const resolvedOptions = options ? {
|
|
998
|
+
...options,
|
|
999
|
+
clist: options.clist?.map(f => this.resolveField(resolvedTableId, f)),
|
|
1000
|
+
slist: options.slist?.map(f => this.resolveField(resolvedTableId, f)),
|
|
1001
|
+
} : undefined;
|
|
1002
|
+
return genResultsTableImpl(this.caller, resolvedTableId, resolvedOptions);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
* Get a record rendered as HTML.
|
|
1007
|
+
* @see https://help.quickbase.com/docs/api-getrecordashtml
|
|
1008
|
+
*/
|
|
1009
|
+
async getRecordAsHTML(
|
|
1010
|
+
tableId: string,
|
|
1011
|
+
options: {
|
|
1012
|
+
rid?: number;
|
|
1013
|
+
key?: string;
|
|
1014
|
+
jht?: boolean;
|
|
1015
|
+
dfid?: number | string;
|
|
1016
|
+
}
|
|
1017
|
+
): Promise<string> {
|
|
1018
|
+
const resolvedTableId = this.resolveTable(tableId);
|
|
1019
|
+
const resolvedOptions = {
|
|
1020
|
+
...options,
|
|
1021
|
+
dfid: options.dfid !== undefined
|
|
1022
|
+
? this.resolveField(resolvedTableId, options.dfid)
|
|
1023
|
+
: undefined,
|
|
1024
|
+
};
|
|
1025
|
+
return getRecordAsHTMLImpl(this.caller, resolvedTableId, resolvedOptions);
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// ============================================================================
|
|
1029
|
+
// Authentication
|
|
1030
|
+
// ============================================================================
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Clear ticket cookie (browser-focused).
|
|
1034
|
+
* @see https://help.quickbase.com/docs/api-signout
|
|
1035
|
+
*/
|
|
1036
|
+
async signOut(): Promise<void> {
|
|
1037
|
+
return signOutImpl(this.caller);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Interface for objects that can execute XML API requests.
|
|
1043
|
+
* Used to adapt the main QuickBase client for XML API calls.
|
|
1044
|
+
*/
|
|
1045
|
+
export interface XmlExecutor {
|
|
1046
|
+
/**
|
|
1047
|
+
* Execute an XML API request
|
|
1048
|
+
* @param dbid - The database ID
|
|
1049
|
+
* @param action - The QUICKBASE-ACTION header value
|
|
1050
|
+
* @param body - The XML request body
|
|
1051
|
+
* @returns The raw XML response body
|
|
1052
|
+
*/
|
|
1053
|
+
executeXml(dbid: string, action: string, body: string): Promise<string>;
|
|
1054
|
+
|
|
1055
|
+
/**
|
|
1056
|
+
* Get the realm name
|
|
1057
|
+
*/
|
|
1058
|
+
realm: string;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
/**
|
|
1062
|
+
* Adapter to create an XmlCaller from a QuickbaseClient.
|
|
1063
|
+
* This bridges the main client to the XML API client.
|
|
1064
|
+
*/
|
|
1065
|
+
interface QuickbaseClientLike {
|
|
1066
|
+
getConfig(): {
|
|
1067
|
+
realm: string;
|
|
1068
|
+
fetchApi: typeof fetch;
|
|
1069
|
+
timeout: number;
|
|
1070
|
+
readOnly?: boolean;
|
|
1071
|
+
schema?: ResolvedSchema;
|
|
1072
|
+
appToken?: string;
|
|
1073
|
+
};
|
|
1074
|
+
/**
|
|
1075
|
+
* Get the auth strategy for XML requests.
|
|
1076
|
+
* This is exposed via getAuthStrategy() method added to the client.
|
|
1077
|
+
*/
|
|
1078
|
+
getAuthStrategy?: () => {
|
|
1079
|
+
getToken(dbid?: string): Promise<string>;
|
|
1080
|
+
getAuthorizationHeader(token: string): string;
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* Create an XML API client from a QuickBase client.
|
|
1086
|
+
*
|
|
1087
|
+
* @param client - The main QuickBase client
|
|
1088
|
+
* @param options - Options for the XML client
|
|
1089
|
+
* @returns XmlClient instance
|
|
1090
|
+
*
|
|
1091
|
+
* @example
|
|
1092
|
+
* ```typescript
|
|
1093
|
+
* const qb = createClient({ realm: 'myrealm', auth: {...} });
|
|
1094
|
+
* const xml = createXmlClient(qb);
|
|
1095
|
+
*
|
|
1096
|
+
* const roles = await xml.getRoleInfo('bqxyz123');
|
|
1097
|
+
* ```
|
|
1098
|
+
*/
|
|
1099
|
+
export function createXmlClient(
|
|
1100
|
+
client: QuickbaseClientLike,
|
|
1101
|
+
options?: XmlClientOptions
|
|
1102
|
+
): XmlClient {
|
|
1103
|
+
const config = client.getConfig();
|
|
1104
|
+
|
|
1105
|
+
// Get auth strategy if available
|
|
1106
|
+
const getAuth = client.getAuthStrategy;
|
|
1107
|
+
if (!getAuth) {
|
|
1108
|
+
throw new Error(
|
|
1109
|
+
'XML client requires access to auth strategy. ' +
|
|
1110
|
+
'Make sure you are using a compatible version of the QuickBase client.'
|
|
1111
|
+
);
|
|
1112
|
+
}
|
|
1113
|
+
const auth = getAuth();
|
|
1114
|
+
|
|
1115
|
+
// Resolve app token: explicit option > config
|
|
1116
|
+
const appToken = options?.appToken ?? config.appToken;
|
|
1117
|
+
|
|
1118
|
+
// Create an XmlCaller adapter that makes XML requests
|
|
1119
|
+
const caller: XmlCaller = {
|
|
1120
|
+
realm: () => config.realm,
|
|
1121
|
+
doXml: async (dbid: string, action: string, body: string): Promise<string> => {
|
|
1122
|
+
const url = `https://${config.realm}.quickbase.com/db/${encodeURIComponent(dbid)}`;
|
|
1123
|
+
|
|
1124
|
+
// Inject app token if configured
|
|
1125
|
+
let finalBody = appToken ? injectAppToken(body, appToken) : body;
|
|
1126
|
+
|
|
1127
|
+
// Get auth token for this dbid
|
|
1128
|
+
const token = await auth.getToken(dbid);
|
|
1129
|
+
const authHeader = auth.getAuthorizationHeader(token);
|
|
1130
|
+
|
|
1131
|
+
// Build headers - XML API requires tokens in body, not Authorization header
|
|
1132
|
+
// Check if using user token auth (QB-USER-TOKEN prefix)
|
|
1133
|
+
const headers: Record<string, string> = {
|
|
1134
|
+
'Content-Type': 'application/xml',
|
|
1135
|
+
'QUICKBASE-ACTION': action,
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
if (authHeader.startsWith('QB-USER-TOKEN ')) {
|
|
1139
|
+
// User token auth: inject token into XML body instead of header
|
|
1140
|
+
// The XML API requires <usertoken> element, not Authorization header
|
|
1141
|
+
// See: https://help.quickbase.com/docs/api-getdbpage
|
|
1142
|
+
finalBody = injectUserToken(finalBody, token);
|
|
1143
|
+
} else {
|
|
1144
|
+
// Other auth types (temp token, SSO): fall back to header auth
|
|
1145
|
+
// Note: These may not work with XML API endpoints that require body auth
|
|
1146
|
+
headers['Authorization'] = authHeader;
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1149
|
+
// Create abort controller for timeout
|
|
1150
|
+
const controller = new AbortController();
|
|
1151
|
+
const timeoutId = setTimeout(() => controller.abort(), config.timeout);
|
|
1152
|
+
|
|
1153
|
+
try {
|
|
1154
|
+
const response = await config.fetchApi(url, {
|
|
1155
|
+
method: 'POST',
|
|
1156
|
+
headers,
|
|
1157
|
+
body: finalBody,
|
|
1158
|
+
signal: controller.signal,
|
|
1159
|
+
credentials: 'omit',
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
const text = await response.text();
|
|
1163
|
+
|
|
1164
|
+
// Check for HTTP-level errors (429, 5xx)
|
|
1165
|
+
if (response.status === 429) {
|
|
1166
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
1167
|
+
throw new Error(
|
|
1168
|
+
`Rate limited (429). Retry after: ${retryAfter ?? 'unknown'} seconds`
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if (response.status >= 500) {
|
|
1173
|
+
throw new Error(`Server error: ${response.status} ${response.statusText}`);
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
return text;
|
|
1177
|
+
} catch (error) {
|
|
1178
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
1179
|
+
throw new Error(`Request timeout after ${config.timeout}ms`);
|
|
1180
|
+
}
|
|
1181
|
+
throw error;
|
|
1182
|
+
} finally {
|
|
1183
|
+
clearTimeout(timeoutId);
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
// Inherit readOnly and schema from main client if not explicitly set in options
|
|
1189
|
+
const resolvedOptions: XmlClientOptions = {
|
|
1190
|
+
readOnly: options?.readOnly ?? config.readOnly ?? false,
|
|
1191
|
+
schema: options?.schema ?? config.schema,
|
|
1192
|
+
fetchApi: options?.fetchApi ?? config.fetchApi,
|
|
1193
|
+
};
|
|
1194
|
+
|
|
1195
|
+
return new XmlClient(caller, resolvedOptions);
|
|
1196
|
+
}
|