@unboundcx/sdk 4.9.3 → 4.10.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/index.js CHANGED
@@ -20,6 +20,7 @@ import { PortalsService } from './services/portals.js';
20
20
  import { SipEndpointsService } from './services/sipEndpoints.js';
21
21
  import { ExternalOAuthService } from './services/externalOAuth.js';
22
22
  import { GoogleCalendarService } from './services/googleCalendar.js';
23
+ import { DriveService } from './services/drive.js';
23
24
  import { EnrollService } from './services/enroll.js';
24
25
  import { PhoneNumbersService } from './services/phoneNumbers.js';
25
26
  import { RecordTypesService } from './services/recordTypes.js';
@@ -97,6 +98,7 @@ class UnboundSDK extends BaseSDK {
97
98
  this.sipEndpoints = new SipEndpointsService(this);
98
99
  this.externalOAuth = new ExternalOAuthService(this);
99
100
  this.googleCalendar = new GoogleCalendarService(this);
101
+ this.drive = new DriveService(this);
100
102
  this.enroll = new EnrollService(this);
101
103
  this.phoneNumbers = new PhoneNumbersService(this);
102
104
  this.recordTypes = new RecordTypesService(this);
@@ -278,6 +280,7 @@ export { PortalsService } from './services/portals.js';
278
280
  export { SipEndpointsService } from './services/sipEndpoints.js';
279
281
  export { ExternalOAuthService } from './services/externalOAuth.js';
280
282
  export { GoogleCalendarService } from './services/googleCalendar.js';
283
+ export { DriveService } from './services/drive.js';
281
284
  export { EnrollService } from './services/enroll.js';
282
285
  export {
283
286
  PhoneNumbersService,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "4.9.3",
3
+ "version": "4.10.1",
4
4
  "description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/services/chat.js CHANGED
@@ -825,7 +825,9 @@ export class ChatService {
825
825
 
826
826
  /**
827
827
  * Admin: get account-level chat settings.
828
- * @returns {Promise<Object>} `{allowReports, reportReasons}`
828
+ * @returns {Promise<Object>} `{allowReports, reportReasons, reportNotifications}`
829
+ * `reportNotifications[]` items are `{channelId, channelName, channelKind, reasons}` —
830
+ * `reasons` is `null` when the rule notifies for every reason.
829
831
  */
830
832
  async adminGetSettings() {
831
833
  return internalRequest(this.sdk, "/chat/admin/settings", "GET");
@@ -836,18 +838,26 @@ export class ChatService {
836
838
  * @param {Object} params
837
839
  * @param {boolean} params.allowReports
838
840
  * @param {string[]} [params.reportReasons]
839
- * @returns {Promise<Object>} `{allowReports, reportReasons}`
841
+ * @param {Object[]} [params.reportNotifications] - Channels to post a
842
+ * "message reported" card to. Each item: `{channelId: string, reasons:
843
+ * string[]|null}` — `reasons` null/empty means "notify for every
844
+ * reason"; channel must be a non-archived public/private channel.
845
+ * @returns {Promise<Object>} `{allowReports, reportReasons, reportNotifications}`
840
846
  */
841
- async adminPutSettings({ allowReports, reportReasons } = {}) {
847
+ async adminPutSettings({ allowReports, reportReasons, reportNotifications } = {}) {
842
848
  this.sdk.validateParams(
843
- { allowReports, reportReasons },
849
+ { allowReports, reportReasons, reportNotifications },
844
850
  {
845
851
  allowReports: { type: "boolean", required: true },
846
852
  reportReasons: { type: "array", required: false },
853
+ reportNotifications: { type: "array", required: false },
847
854
  },
848
855
  );
849
856
  const body = { allowReports };
850
857
  if (reportReasons !== undefined) body.reportReasons = reportReasons;
858
+ if (reportNotifications !== undefined) {
859
+ body.reportNotifications = reportNotifications;
860
+ }
851
861
  return internalRequest(this.sdk, "/chat/admin/settings", "PUT", {
852
862
  body,
853
863
  });
@@ -0,0 +1,234 @@
1
+ import { internalRequest } from '../base.js';
2
+ import { StorageService } from './storage.js';
3
+
4
+ export class DriveService {
5
+ constructor(sdk) {
6
+ this.sdk = sdk;
7
+ }
8
+
9
+ async status() {
10
+ const result = await internalRequest(this.sdk, '/drive/status', 'GET');
11
+ return result;
12
+ }
13
+
14
+ /**
15
+ * List Google Drive files and folders.
16
+ * @param {Object} [options]
17
+ * @param {string} [options.folderId]
18
+ * @param {string} [options.search]
19
+ * @param {number} [options.page]
20
+ * @param {number} [options.pageSize]
21
+ * @returns {Promise<Object>}
22
+ */
23
+ async listFiles({ folderId, search, page, pageSize } = {}) {
24
+ this.sdk.validateParams(
25
+ { folderId, search, page, pageSize },
26
+ {
27
+ folderId: { type: 'string', required: false },
28
+ search: { type: 'string', required: false },
29
+ page: { type: 'number', required: false },
30
+ pageSize: { type: 'number', required: false },
31
+ },
32
+ );
33
+
34
+ const query = {};
35
+ if (folderId !== undefined) query.folderId = folderId;
36
+ if (search !== undefined) query.search = search;
37
+ if (page !== undefined) query.page = page;
38
+ if (pageSize !== undefined) query.pageSize = pageSize;
39
+
40
+ const result = await internalRequest(this.sdk, '/drive/files', 'GET', {
41
+ query,
42
+ });
43
+ return result;
44
+ }
45
+
46
+ /**
47
+ * Create a Google Drive folder.
48
+ * @param {Object} options
49
+ * @param {string} options.name
50
+ * @param {string} [options.parentId]
51
+ * @returns {Promise<Object>}
52
+ */
53
+ async createFolder({ name, parentId } = {}) {
54
+ this.sdk.validateParams(
55
+ { name, parentId },
56
+ {
57
+ name: { type: 'string', required: true },
58
+ parentId: { type: 'string', required: false },
59
+ },
60
+ );
61
+
62
+ const body = { name };
63
+ if (parentId !== undefined) body.parentId = parentId;
64
+
65
+ const result = await internalRequest(this.sdk, '/drive/folders', 'POST', {
66
+ body,
67
+ });
68
+ return result;
69
+ }
70
+
71
+ /**
72
+ * Rename and/or move a Google Drive folder.
73
+ * @param {string} id
74
+ * @param {Object} [updates]
75
+ * @param {string} [updates.name]
76
+ * @param {string} [updates.parentId]
77
+ * @returns {Promise<Object>}
78
+ */
79
+ async updateFolder(id, { name, parentId } = {}) {
80
+ this.sdk.validateParams(
81
+ { id, name, parentId },
82
+ {
83
+ id: { type: 'string', required: true },
84
+ name: { type: 'string', required: false },
85
+ parentId: { type: 'string', required: false },
86
+ },
87
+ );
88
+
89
+ const body = {};
90
+ if (name !== undefined) body.name = name;
91
+ if (parentId !== undefined) body.parentId = parentId;
92
+
93
+ const result = await internalRequest(
94
+ this.sdk,
95
+ `/drive/folders/${id}`,
96
+ 'PATCH',
97
+ { body },
98
+ true,
99
+ );
100
+ return result;
101
+ }
102
+
103
+ async deleteFolder(id) {
104
+ this.sdk.validateParams(
105
+ { id },
106
+ {
107
+ id: { type: 'string', required: true },
108
+ },
109
+ );
110
+
111
+ const result = await internalRequest(
112
+ this.sdk,
113
+ `/drive/folders/${id}`,
114
+ 'DELETE',
115
+ );
116
+ return result;
117
+ }
118
+
119
+ /**
120
+ * Rename a Google Drive file.
121
+ * @param {string} id
122
+ * @param {Object} [updates]
123
+ * @param {string} [updates.name]
124
+ * @returns {Promise<Object>}
125
+ */
126
+ async updateFile(id, { name } = {}) {
127
+ this.sdk.validateParams(
128
+ { id, name },
129
+ {
130
+ id: { type: 'string', required: true },
131
+ name: { type: 'string', required: false },
132
+ },
133
+ );
134
+
135
+ const body = {};
136
+ if (name !== undefined) body.name = name;
137
+
138
+ const result = await internalRequest(
139
+ this.sdk,
140
+ `/drive/files/${id}`,
141
+ 'PATCH',
142
+ { body },
143
+ true,
144
+ );
145
+ return result;
146
+ }
147
+
148
+ /**
149
+ * Move Drive files into a folder.
150
+ * @param {Object} options
151
+ * @param {string[]} options.ids
152
+ * @param {string} [options.folderId]
153
+ * @returns {Promise<Object>}
154
+ */
155
+ async moveFiles({ ids, folderId } = {}) {
156
+ this.sdk.validateParams(
157
+ { ids, folderId },
158
+ {
159
+ ids: { type: 'array', required: true },
160
+ folderId: { type: 'string', required: false },
161
+ },
162
+ );
163
+
164
+ const body = { ids };
165
+ if (folderId !== undefined) body.folderId = folderId;
166
+
167
+ const result = await internalRequest(
168
+ this.sdk,
169
+ '/drive/files/move',
170
+ 'PATCH',
171
+ { body },
172
+ true,
173
+ );
174
+ return result;
175
+ }
176
+
177
+ async deleteFile(id) {
178
+ this.sdk.validateParams(
179
+ { id },
180
+ {
181
+ id: { type: 'string', required: true },
182
+ },
183
+ );
184
+
185
+ const result = await internalRequest(
186
+ this.sdk,
187
+ `/drive/files/${id}`,
188
+ 'DELETE',
189
+ );
190
+ return result;
191
+ }
192
+
193
+ /**
194
+ * Upload a file to Google Drive.
195
+ * @param {Object} config
196
+ * @param {Object} config.file - Buffer, File, or stream
197
+ * @param {string} [config.fileName]
198
+ * @param {string} [config.parentId]
199
+ * @param {Function} [config.onProgress]
200
+ * @returns {Promise<Object>}
201
+ */
202
+ async upload({ file, fileName, parentId, onProgress } = {}) {
203
+ this.sdk.validateParams(
204
+ { file, fileName, parentId },
205
+ {
206
+ file: { type: 'object', required: true },
207
+ fileName: { type: 'string', required: false },
208
+ parentId: { type: 'string', required: false },
209
+ },
210
+ );
211
+
212
+ const formFields = [];
213
+ if (parentId) formFields.push(['parentId', parentId]);
214
+
215
+ const storage = new StorageService(this.sdk);
216
+ return storage._performUpload(
217
+ file,
218
+ fileName,
219
+ formFields,
220
+ '/drive/upload',
221
+ 'POST',
222
+ onProgress,
223
+ );
224
+ }
225
+
226
+ async browserToken() {
227
+ const result = await internalRequest(
228
+ this.sdk,
229
+ '/drive/browserToken',
230
+ 'POST',
231
+ );
232
+ return result;
233
+ }
234
+ }
@@ -459,6 +459,7 @@ Response:
459
459
  * @param {string} [config.country='US'] - Country code for region selection
460
460
  * @param {string} [config.expireAfter] - Expiration time
461
461
  * @param {string} [config.relatedId] - Related object ID
462
+ * @param {string} [config.folderId] - Storage folder id
462
463
  * @param {boolean} [config.createAccessKey=false] - Generate an access key for the file
463
464
  * @param {number} [config.accessKeyExpiresIn] - Access key expiration in seconds
464
465
  * @param {string} [config.convertTo] - Convert uploaded file to this format before storing. Supported: 'pdf', 'tiff'. Input must be PDF, DOC, or DOCX.
@@ -479,6 +480,7 @@ Response:
479
480
  country = 'US',
480
481
  expireAfter,
481
482
  relatedId,
483
+ folderId,
482
484
  createAccessKey = false,
483
485
  accessKeyExpiresIn,
484
486
  convertTo,
@@ -496,6 +498,7 @@ Response:
496
498
  country,
497
499
  expireAfter,
498
500
  relatedId,
501
+ folderId,
499
502
  createAccessKey,
500
503
  accessKeyExpiresIn,
501
504
  convertTo,
@@ -510,6 +513,7 @@ Response:
510
513
  country: { type: 'string', required: false },
511
514
  expireAfter: { type: 'string', required: false },
512
515
  relatedId: { type: 'string', required: false },
516
+ folderId: { type: 'string', required: false },
513
517
  createAccessKey: { type: 'boolean', required: false },
514
518
  accessKeyExpiresIn: { type: 'number', required: false },
515
519
  convertTo: { type: 'string', required: false },
@@ -526,6 +530,7 @@ Response:
526
530
  if (country) formFields.push(['country', country]);
527
531
  if (expireAfter) formFields.push(['expireAfter', expireAfter]);
528
532
  if (relatedId) formFields.push(['relatedId', relatedId]);
533
+ if (folderId) formFields.push(['folderId', folderId]);
529
534
  if (createAccessKey !== undefined)
530
535
  formFields.push(['createAccessKey', createAccessKey.toString()]);
531
536
  if (accessKeyExpiresIn)
@@ -779,41 +784,84 @@ Response:
779
784
  return result;
780
785
  }
781
786
 
782
- async updateFileMetadata(storageId, metadata) {
787
+ /**
788
+ * Update file metadata (rename and/or move). Does not re-upload the file.
789
+ * @param {string} id - Storage file id
790
+ * @param {Object} [updates]
791
+ * @param {string} [updates.fileName]
792
+ * @param {string} [updates.folderId]
793
+ * @param {string} [updates.relatedId]
794
+ * @returns {Promise<Object>}
795
+ */
796
+ async updateFileMetadata(id, { fileName, folderId, relatedId } = {}) {
783
797
  this.sdk.validateParams(
784
- { storageId, metadata },
798
+ { id, fileName, folderId, relatedId },
785
799
  {
786
- storageId: { type: 'string', required: true },
787
- metadata: { type: 'object', required: true },
800
+ id: { type: 'string', required: true },
801
+ fileName: { type: 'string', required: false },
802
+ folderId: { type: 'string', required: false },
803
+ relatedId: { type: 'string', required: false },
788
804
  },
789
805
  );
790
806
 
791
- const params = {
792
- body: { metadata },
793
- };
807
+ const body = {};
808
+ if (fileName !== undefined) body.fileName = fileName;
809
+ if (folderId !== undefined) body.folderId = folderId;
810
+ if (relatedId !== undefined) body.relatedId = relatedId;
794
811
 
795
- const result = await internalRequest(this.sdk,
796
- `/storage/file/${storageId}/metadata`,
797
- 'PUT',
798
- params,
812
+ const result = await internalRequest(
813
+ this.sdk,
814
+ `/storage/files/${id}`,
815
+ 'PATCH',
816
+ { body },
817
+ true,
799
818
  );
800
819
  return result;
801
820
  }
802
821
 
822
+ /**
823
+ * List storage files.
824
+ * @param {Object} [options]
825
+ * @param {string} [options.relatedId]
826
+ * @param {string} [options.folderId]
827
+ * @param {string} [options.folder]
828
+ * @param {string} [options.classification]
829
+ * @param {string} [options.search]
830
+ * @param {string} [options.view]
831
+ * @param {number} [options.page]
832
+ * @param {number} [options.limit]
833
+ * @param {string} [options.sortBy]
834
+ * @param {string} [options.sortOrder]
835
+ * @param {string} [options.fileType]
836
+ * @param {boolean} [options.isPublic]
837
+ * @param {number} [options.offset] - Legacy; still forwarded if provided
838
+ * @param {string} [options.orderBy] - Legacy; still forwarded if provided
839
+ * @param {string} [options.orderDirection] - Legacy; still forwarded if provided
840
+ * @returns {Promise<Object>}
841
+ */
803
842
  async listFiles(options = {}) {
804
- const { classification, folder, limit, offset, orderBy, orderDirection } =
805
- options;
806
-
807
- // Validate optional parameters
808
843
  const validationSchema = {};
809
- if ('classification' in options)
810
- validationSchema.classification = { type: 'string' };
811
- if ('folder' in options) validationSchema.folder = { type: 'string' };
812
- if ('limit' in options) validationSchema.limit = { type: 'number' };
813
- if ('offset' in options) validationSchema.offset = { type: 'number' };
814
- if ('orderBy' in options) validationSchema.orderBy = { type: 'string' };
815
- if ('orderDirection' in options)
816
- validationSchema.orderDirection = { type: 'string' };
844
+ const optionalTypes = {
845
+ relatedId: 'string',
846
+ folderId: 'string',
847
+ folder: 'string',
848
+ classification: 'string',
849
+ search: 'string',
850
+ view: 'string',
851
+ page: 'number',
852
+ limit: 'number',
853
+ sortBy: 'string',
854
+ sortOrder: 'string',
855
+ fileType: 'string',
856
+ isPublic: 'boolean',
857
+ offset: 'number',
858
+ orderBy: 'string',
859
+ orderDirection: 'string',
860
+ };
861
+
862
+ for (const [key, type] of Object.entries(optionalTypes)) {
863
+ if (key in options) validationSchema[key] = { type };
864
+ }
817
865
 
818
866
  if (Object.keys(validationSchema).length > 0) {
819
867
  this.sdk.validateParams(options, validationSchema);
@@ -827,6 +875,152 @@ Response:
827
875
  return result;
828
876
  }
829
877
 
878
+ /**
879
+ * List storage folders for a related record.
880
+ * @param {Object} options
881
+ * @param {string} options.relatedId
882
+ * @param {string} [options.parentId]
883
+ * @param {string} [options.search]
884
+ * @returns {Promise<Object>}
885
+ */
886
+ async listFolders({ relatedId, parentId, search } = {}) {
887
+ this.sdk.validateParams(
888
+ { relatedId, parentId, search },
889
+ {
890
+ relatedId: { type: 'string', required: true },
891
+ parentId: { type: 'string', required: false },
892
+ search: { type: 'string', required: false },
893
+ },
894
+ );
895
+
896
+ const query = { relatedId };
897
+ if (parentId !== undefined) query.parentId = parentId;
898
+ if (search !== undefined) query.search = search;
899
+
900
+ const result = await internalRequest(this.sdk, '/storage/folders', 'GET', {
901
+ query,
902
+ });
903
+ return result;
904
+ }
905
+
906
+ /**
907
+ * Create a storage folder.
908
+ * @param {Object} options
909
+ * @param {string} options.relatedId
910
+ * @param {string} [options.parentId]
911
+ * @param {string} options.name
912
+ * @returns {Promise<Object>}
913
+ */
914
+ async createFolder({ relatedId, parentId, name } = {}) {
915
+ this.sdk.validateParams(
916
+ { relatedId, parentId, name },
917
+ {
918
+ relatedId: { type: 'string', required: true },
919
+ parentId: { type: 'string', required: false },
920
+ name: { type: 'string', required: true },
921
+ },
922
+ );
923
+
924
+ const body = { relatedId, name };
925
+ if (parentId !== undefined) body.parentId = parentId;
926
+
927
+ const result = await internalRequest(this.sdk, '/storage/folders', 'POST', {
928
+ body,
929
+ });
930
+ return result;
931
+ }
932
+
933
+ /**
934
+ * Rename and/or move a storage folder.
935
+ * @param {string} id
936
+ * @param {Object} [updates]
937
+ * @param {string} [updates.relatedId]
938
+ * @param {string} [updates.name]
939
+ * @param {string} [updates.parentId]
940
+ * @returns {Promise<Object>}
941
+ */
942
+ async updateFolder(id, { relatedId, name, parentId } = {}) {
943
+ this.sdk.validateParams(
944
+ { id, relatedId, name, parentId },
945
+ {
946
+ id: { type: 'string', required: true },
947
+ relatedId: { type: 'string', required: false },
948
+ name: { type: 'string', required: false },
949
+ parentId: { type: 'string', required: false },
950
+ },
951
+ );
952
+
953
+ const body = {};
954
+ if (relatedId !== undefined) body.relatedId = relatedId;
955
+ if (name !== undefined) body.name = name;
956
+ if (parentId !== undefined) body.parentId = parentId;
957
+
958
+ const result = await internalRequest(
959
+ this.sdk,
960
+ `/storage/folders/${id}`,
961
+ 'PATCH',
962
+ { body },
963
+ true,
964
+ );
965
+ return result;
966
+ }
967
+
968
+ /**
969
+ * Soft-delete a storage folder (cascades to children).
970
+ * @param {string} id
971
+ * @param {Object} options
972
+ * @param {string} options.relatedId
973
+ * @returns {Promise<Object>}
974
+ */
975
+ async deleteFolder(id, { relatedId } = {}) {
976
+ this.sdk.validateParams(
977
+ { id, relatedId },
978
+ {
979
+ id: { type: 'string', required: true },
980
+ relatedId: { type: 'string', required: true },
981
+ },
982
+ );
983
+
984
+ const result = await internalRequest(
985
+ this.sdk,
986
+ `/storage/folders/${id}`,
987
+ 'DELETE',
988
+ { query: { relatedId } },
989
+ );
990
+ return result;
991
+ }
992
+
993
+ /**
994
+ * Move files into a folder (DB folderId only).
995
+ * @param {Object} options
996
+ * @param {string[]} options.ids
997
+ * @param {string} [options.folderId]
998
+ * @param {string} options.relatedId
999
+ * @returns {Promise<Object>}
1000
+ */
1001
+ async moveFiles({ ids, folderId, relatedId } = {}) {
1002
+ this.sdk.validateParams(
1003
+ { ids, folderId, relatedId },
1004
+ {
1005
+ ids: { type: 'array', required: true },
1006
+ folderId: { type: 'string', required: false },
1007
+ relatedId: { type: 'string', required: true },
1008
+ },
1009
+ );
1010
+
1011
+ const body = { ids, relatedId };
1012
+ if (folderId !== undefined) body.folderId = folderId;
1013
+
1014
+ const result = await internalRequest(
1015
+ this.sdk,
1016
+ '/storage/files/move',
1017
+ 'PATCH',
1018
+ { body },
1019
+ true,
1020
+ );
1021
+ return result;
1022
+ }
1023
+
830
1024
  /**
831
1025
  * Generate an access key for an existing storage file
832
1026
  * @param {string} fileId - The storage file ID
@@ -39,6 +39,7 @@ async function testBasicSDKFunctionality() {
39
39
  'sipEndpoints',
40
40
  'externalOAuth',
41
41
  'googleCalendar',
42
+ 'drive',
42
43
  'enroll',
43
44
  ];
44
45
 
@@ -37,6 +37,7 @@ async function testPublicSDKCompleteness() {
37
37
  // Additional services found in analysis
38
38
  'externalOAuth',
39
39
  'googleCalendar',
40
+ 'drive',
40
41
  'enroll',
41
42
  'phoneNumbers',
42
43
  'recordTypes',
@@ -115,6 +115,7 @@ const services = [
115
115
  'sipEndpoints',
116
116
  'externalOAuth',
117
117
  'googleCalendar',
118
+ 'drive',
118
119
  'enroll',
119
120
  'phoneNumbers',
120
121
  'recordTypes',