@pipedream/google_drive 0.3.2 → 0.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.
Files changed (39) hide show
  1. package/actions/add-file-sharing-preference/add-file-sharing-preference.mjs +83 -0
  2. package/actions/copy-file/copy-file.mjs +34 -0
  3. package/actions/create-file/create-file.mjs +242 -0
  4. package/actions/create-file-from-template/create-file-from-template.mjs +98 -0
  5. package/actions/create-file-from-text/create-file-from-text.mjs +67 -0
  6. package/actions/create-folder/create-folder.mjs +89 -0
  7. package/actions/create-shared-drive/create-shared-drive.mjs +25 -0
  8. package/actions/delete-file/delete-file.mjs +37 -0
  9. package/actions/delete-shared-drive/delete-shared-drive.mjs +30 -0
  10. package/actions/download-file/download-file.mjs +120 -0
  11. package/actions/find-file/find-file.mjs +35 -0
  12. package/actions/find-folder/find-folder.mjs +49 -0
  13. package/actions/get-folder-id-for-path/get-folder-id-for-path.mjs +62 -0
  14. package/actions/get-shared-drive/get-shared-drive.mjs +37 -0
  15. package/actions/google-mime-types.mjs +19 -0
  16. package/actions/google-workspace-export-formats.mjs +74 -0
  17. package/actions/language-codes.mjs +742 -0
  18. package/actions/move-file/move-file.mjs +52 -0
  19. package/actions/move-file-to-trash/move-file-to-trash.mjs +41 -0
  20. package/actions/replace-file/replace-file.mjs +134 -0
  21. package/actions/search-shared-drives/search-shared-drives.mjs +34 -0
  22. package/actions/update-file/update-file.mjs +164 -0
  23. package/actions/update-shared-drive/update-shared-drive.mjs +77 -0
  24. package/actions/upload-file/upload-file.mjs +120 -0
  25. package/constants.mjs +200 -0
  26. package/google_drive.app.mjs +1432 -0
  27. package/package.json +23 -20
  28. package/sources/changes-to-specific-files/changes-to-specific-files.mjs +226 -0
  29. package/sources/changes-to-specific-files-shared-drive/changes-to-specific-files-shared-drive.mjs +110 -0
  30. package/sources/common-webhook.mjs +201 -0
  31. package/sources/new-files-instant/new-files-instant.mjs +95 -0
  32. package/sources/new-or-modified-comments/new-or-modified-comments.mjs +104 -0
  33. package/sources/new-or-modified-files/new-or-modified-files.mjs +66 -0
  34. package/sources/new-or-modified-folders/new-or-modified-folders.mjs +86 -0
  35. package/sources/new-shared-drive/new-shared-drive.mjs +68 -0
  36. package/utils.mjs +267 -0
  37. package/google_drive.app.js +0 -212
  38. package/sources/changes-to-specific-files/changes-to-specific-files.js +0 -226
  39. package/sources/new-or-modified-files/new-or-modified-files.js +0 -213
@@ -0,0 +1,1432 @@
1
+ import axios from "axios";
2
+ import drive from "@googleapis/drive";
3
+ import { v4 as uuid } from "uuid";
4
+ import isoLanguages from "./actions/language-codes.mjs";
5
+ import mimeDb from "mime-db";
6
+ const mimeTypes = Object.keys(mimeDb);
7
+
8
+ import {
9
+ GOOGLE_DRIVE_UPDATE_TYPES,
10
+ MY_DRIVE_VALUE,
11
+ WEBHOOK_SUBSCRIPTION_EXPIRATION_TIME_MILLISECONDS,
12
+ GOOGLE_DRIVE_FOLDER_MIME_TYPE,
13
+ GOOGLE_DRIVE_ROLES,
14
+ GOOGLE_DRIVE_GRANTEE_TYPES,
15
+ GOOGLE_DRIVE_GRANTEE_ANYONE,
16
+ GOOGLE_DRIVE_ROLE_READER,
17
+ GOOGLE_DRIVE_UPLOAD_TYPES,
18
+ } from "./constants.mjs";
19
+ import googleMimeTypes from "./actions/google-mime-types.mjs";
20
+
21
+ import {
22
+ isMyDrive,
23
+ getDriveId,
24
+ getListFilesOpts,
25
+ omitEmptyStringValues,
26
+ toSingleLineString,
27
+ getFilePaths,
28
+ } from "./utils.mjs";
29
+
30
+ export default {
31
+ type: "app",
32
+ app: "google_drive",
33
+ propDefinitions: {
34
+ watchedDrive: {
35
+ type: "string",
36
+ label: "Drive",
37
+ description: "Defaults to `My Drive`. To select a [Shared Drive](https://support.google.com/a/users/answer/9310351) instead, select it from this list.",
38
+ optional: true,
39
+ default: MY_DRIVE_VALUE,
40
+ async options({ prevContext }) {
41
+ const { nextPageToken } = prevContext;
42
+ return this._listDriveOptions(nextPageToken);
43
+ },
44
+ },
45
+ folderId: {
46
+ type: "string",
47
+ label: "Folder",
48
+ description: "The folder in the drive",
49
+ async options({
50
+ prevContext,
51
+ drive,
52
+ baseOpts = {
53
+ q: `mimeType = '${GOOGLE_DRIVE_FOLDER_MIME_TYPE}'`,
54
+ },
55
+ }) {
56
+ const { nextPageToken } = prevContext;
57
+ return this.listDriveFilesOptions(drive, nextPageToken, baseOpts);
58
+ },
59
+ },
60
+ fileId: {
61
+ type: "string",
62
+ label: "File",
63
+ description: "The file in the drive",
64
+ async options({
65
+ prevContext,
66
+ drive,
67
+ baseOpts = {
68
+ q: `mimeType != '${GOOGLE_DRIVE_FOLDER_MIME_TYPE}'`,
69
+ },
70
+ }) {
71
+ const { nextPageToken } = prevContext;
72
+ return this.listDriveFilesOptions(drive, nextPageToken, baseOpts);
73
+ },
74
+ },
75
+ fileOrFolderId: {
76
+ type: "string",
77
+ label: "File or Folder",
78
+ description: "The file or folder in the drive",
79
+ async options({
80
+ prevContext, drive, baseOpts = {},
81
+ }) {
82
+ const { nextPageToken } = prevContext;
83
+ return this.listDriveFilesOptions(drive, nextPageToken, baseOpts);
84
+ },
85
+ },
86
+ fileParents: {
87
+ type: "string[]",
88
+ label: "File Parents",
89
+ description: "The folder IDs of the file's parents",
90
+ optional: true,
91
+ async options({ fileId }) {
92
+ if (!fileId) {
93
+ return [];
94
+ }
95
+ let file;
96
+ try {
97
+ file = await this.getFile(fileId, {
98
+ fields: "parents",
99
+ });
100
+ } catch (err) {
101
+ return [];
102
+ }
103
+ let parentFolders = await Promise.all(
104
+ file.parents.map((parentId) => this.getFile(parentId, {
105
+ fields: "id,name",
106
+ })),
107
+ );
108
+ return parentFolders.map(({
109
+ id, name,
110
+ }) => ({
111
+ value: id,
112
+ label: name,
113
+ }));
114
+ },
115
+ },
116
+ updateTypes: {
117
+ type: "string[]",
118
+ label: "Types of updates",
119
+ description: `The types of updates you want to watch for on these files.
120
+ [See Google's docs]
121
+ (https://developers.google.com/drive/api/v3/push#understanding-drive-api-notification-events).`,
122
+ default: GOOGLE_DRIVE_UPDATE_TYPES,
123
+ options: GOOGLE_DRIVE_UPDATE_TYPES,
124
+ },
125
+ watchForPropertiesChanges: {
126
+ type: "boolean",
127
+ label: "Watch for changes to file properties",
128
+ description: `Watch for changes to [file properties](https://developers.google.com/drive/api/v3/properties)
129
+ in addition to changes to content. **Defaults to \`false\`, watching for only changes to content**.`,
130
+ optional: true,
131
+ default: false,
132
+ },
133
+ fileUrl: {
134
+ type: "string",
135
+ label: "File URL",
136
+ description: toSingleLineString(`
137
+ The URL of the file you want to upload to Google Drive. Must specify either **File URL**
138
+ or **File Path**.
139
+ `),
140
+ optional: true,
141
+ },
142
+ filePath: {
143
+ type: "string",
144
+ label: "File Path",
145
+ description: toSingleLineString(`
146
+ The path to the file saved to the [\`/tmp\`
147
+ directory](https://pipedream.com/docs/workflows/steps/code/nodejs/working-with-files/#the-tmp-directory)
148
+ (e.g. \`/tmp/myFile.csv\`). Must specify either **File URL** or **File Path**.
149
+ `),
150
+ optional: true,
151
+ },
152
+ fileName: {
153
+ type: "string",
154
+ label: "Name",
155
+ description: "The name of the file (e.g., `/myFile.csv`)",
156
+ optional: true,
157
+ },
158
+ fileNameSearchTerm: {
159
+ type: "string",
160
+ label: "Search Name",
161
+ description: "Enter the name of a file to search for.",
162
+ optional: true,
163
+ },
164
+ mimeType: {
165
+ type: "string",
166
+ label: "Mime Type",
167
+ description: toSingleLineString(`
168
+ The MIME type of the file (e.g., \`image/jpeg\`). Google Drive will attempt to automatically
169
+ detect an appropriate value from uploaded content if no value is provided. The value cannot
170
+ be changed unless a new revision is uploaded. If a file is created with a [Google Doc MIME
171
+ type](https://developers.google.com/drive/api/v3/mime-types), the uploaded content will be
172
+ imported if possible.
173
+ `),
174
+ optional: true,
175
+ async options({ page = 0 }) {
176
+ const allTypes = googleMimeTypes.concat(mimeTypes);
177
+ const start = page * 500;
178
+ const end = start + 500;
179
+ return allTypes.slice(start, end);
180
+ },
181
+ },
182
+ uploadType: {
183
+ type: "string",
184
+ label: "Upload Type",
185
+ description: `The type of upload request to the /upload URI. If you are uploading data
186
+ (using an /upload URI), this field is required. If you are creating a metadata-only file,
187
+ this field is not required.
188
+ media - Simple upload. Upload the media only, without any metadata.
189
+ multipart - Multipart upload. Upload both the media and its metadata, in a single request.
190
+ resumable - Resumable upload. Upload the file in a resumable fashion, using a series of
191
+ at least two requests where the first request includes the metadata.`,
192
+ options: GOOGLE_DRIVE_UPLOAD_TYPES,
193
+ },
194
+ useDomainAdminAccess: {
195
+ type: "boolean",
196
+ label: "Use Domain Admin Access",
197
+ description: "Issue the request as a domain administrator",
198
+ optional: true,
199
+ default: false,
200
+ },
201
+ role: {
202
+ type: "string",
203
+ label: "Role",
204
+ description: "The role granted by this permission",
205
+ optional: true,
206
+ default: GOOGLE_DRIVE_ROLE_READER,
207
+ options: GOOGLE_DRIVE_ROLES,
208
+ },
209
+ type: {
210
+ type: "string",
211
+ label: "Type",
212
+ description:
213
+ "The type of the grantee. If **Type** is `user` or `group`, you must provide an **Email Address** for the user or group. When **Type** is `domain`, you must provide a `Domain`. Sharing with a domain is only valid for G Suite users.",
214
+ optional: true,
215
+ default: GOOGLE_DRIVE_GRANTEE_ANYONE,
216
+ options: GOOGLE_DRIVE_GRANTEE_TYPES,
217
+ },
218
+ domain: {
219
+ type: "string",
220
+ label: "Domain",
221
+ description:
222
+ "The domain of the G Suite organization to which this permission refers if **Type** is `domain` (e.g., `yourcomapany.com`)",
223
+ optional: true,
224
+ },
225
+ emailAddress: {
226
+ type: "string",
227
+ label: "Email Address",
228
+ description:
229
+ "The email address of the user or group to which this permission refers if **Type** is `user` or `group`",
230
+ optional: true,
231
+ },
232
+ ocrLanguage: {
233
+ type: "string",
234
+ label: "OCR Language",
235
+ description:
236
+ "A language hint for OCR processing during image import (ISO 639-1 code)",
237
+ optional: true,
238
+ options: isoLanguages,
239
+ },
240
+ useContentAsIndexableText: {
241
+ type: "boolean",
242
+ label: "Use Content As Indexable Text",
243
+ description:
244
+ "Whether to use the uploaded content as indexable text",
245
+ optional: true,
246
+ },
247
+ keepRevisionForever: {
248
+ type: "boolean",
249
+ label: "Keep Revision Forever",
250
+ description: toSingleLineString(`
251
+ Whether to set the 'keepForever' field in the new head revision. This is only applicable
252
+ to files with binary content in Google Drive. Only 200 revisions for the file can be kept
253
+ forever. If the limit is reached, try deleting pinned revisions.
254
+ `),
255
+ optional: true,
256
+ },
257
+ },
258
+ methods: {
259
+ // Static methods
260
+ isMyDrive,
261
+ getDriveId,
262
+ getFilePaths,
263
+
264
+ // Returns a drive object authenticated with the user's access token
265
+ drive() {
266
+ const auth = new drive.auth.OAuth2();
267
+ auth.setCredentials({
268
+ access_token: this.$auth.oauth_access_token,
269
+ });
270
+ return drive.drive({
271
+ version: "v3",
272
+ auth,
273
+ });
274
+ },
275
+ // Google's push notifications provide a URL to the resource that changed,
276
+ // which we can use to fetch the file's metadata. So we use axios here
277
+ // (vs. the Node client) to get that.
278
+ async getFileMetadata(url) {
279
+ return (
280
+ await axios({
281
+ method: "GET",
282
+ headers: {
283
+ Authorization: `Bearer ${this.$auth.oauth_access_token}`,
284
+ },
285
+ url,
286
+ })
287
+ ).data;
288
+ },
289
+ /**
290
+ * This method yields a list of changes that occurred to files in a
291
+ * particular Google Drive. It is a wrapper around [the `drive.changes.list`
292
+ * API](https://bit.ly/2SGb5M2) but defined as a generator to enable lazy
293
+ * loading of multiple pages.
294
+ *
295
+ * @typedef {object} ChangesPage
296
+ * @property {object[]} changedFiles - the list of file changes, as [defined
297
+ * by the API](https://bit.ly/3h7WeUa). This list filters out any result
298
+ * that is not a proper object.
299
+ * @property {string} nextPageToken - the page token [returned by the last API
300
+ * call](https://bit.ly/3h7WeUa). **Note that this generator keeps track of
301
+ * this token internally, and the purpose of this value is to provide a way
302
+ * for consumers of this method to handle checkpoints in case of an
303
+ * unexpected halt.**
304
+ *
305
+ * @param {string} [pageToken] - the token for continuing a previous list
306
+ * request on the next page. This must be a token that was previously
307
+ * returned by this same method.
308
+ * @param {string} [driveId] - the shared drive from which changes are
309
+ * returned
310
+ * @yields
311
+ * @type {ChangesPage}
312
+ */
313
+ async *listChanges(pageToken, driveId) {
314
+ const drive = this.drive();
315
+ let changeRequest = {
316
+ pageToken,
317
+ pageSize: 1000,
318
+ };
319
+
320
+ // As with many of the methods for Google Drive, we must
321
+ // pass a request of a different shape when we're requesting
322
+ // changes for My Drive (null driveId) vs. a shared drive
323
+ if (driveId) {
324
+ changeRequest = {
325
+ ...changeRequest,
326
+ driveId,
327
+ includeItemsFromAllDrives: true,
328
+ supportsAllDrives: true,
329
+ };
330
+ }
331
+
332
+ while (true) {
333
+ const { data } = await drive.changes.list(changeRequest);
334
+ const {
335
+ changes = [],
336
+ newStartPageToken,
337
+ nextPageToken,
338
+ } = data;
339
+
340
+ // Some changes do not include an associated file object. Return only
341
+ // those that do
342
+ const changedFiles = changes
343
+ .map((change) => change.file)
344
+ .filter((f) => typeof f === "object");
345
+
346
+ yield {
347
+ changedFiles,
348
+ nextPageToken: nextPageToken || newStartPageToken,
349
+ };
350
+
351
+ if (newStartPageToken) {
352
+ // The 'newStartPageToken' field is only returned as part of the last
353
+ // page from the API response: https://bit.ly/3jBEvWV
354
+ break;
355
+ }
356
+
357
+ changeRequest.pageToken = nextPageToken;
358
+ }
359
+ },
360
+ async getPageToken(driveId) {
361
+ const drive = this.drive();
362
+ const request = driveId
363
+ ? {
364
+ driveId,
365
+ supportsAllDrives: true,
366
+ }
367
+ : {};
368
+ const { data } = await drive.changes.getStartPageToken(request);
369
+ return data.startPageToken;
370
+ },
371
+ checkHeaders(headers, subscription, channelID) {
372
+ if (
373
+ !headers["x-goog-resource-state"] ||
374
+ !headers["x-goog-resource-id"] ||
375
+ !headers["x-goog-resource-uri"] ||
376
+ !headers["x-goog-message-number"]
377
+ ) {
378
+ console.log("Request missing necessary headers: ", headers);
379
+ return false;
380
+ }
381
+
382
+ const incomingChannelID = headers["x-goog-channel-id"];
383
+ if (incomingChannelID !== channelID) {
384
+ console.log(
385
+ `Channel ID of ${incomingChannelID} not equal to deployed component channel of ${channelID}`,
386
+ );
387
+ return false;
388
+ }
389
+
390
+ if (headers["x-goog-resource-id"] !== subscription.resourceId) {
391
+ console.log(
392
+ `Resource ID of ${subscription.resourceId} not currently being tracked. Exiting`,
393
+ );
394
+ return false;
395
+ }
396
+ return true;
397
+ },
398
+
399
+ /**
400
+ * A utility method around [the `drive.drives.list`
401
+ * API](https://bit.ly/3AiWE1x) but scoped to a specific page of the API
402
+ * response
403
+ *
404
+ * @typedef {object} DriveListPage - an object representing a page that
405
+ * lists GDrive drives, as defined by [the API](https://bit.ly/3jwxbvy)
406
+ *
407
+ * @param {string} [pageToken] - the page token for the next page of shared
408
+ * drives
409
+ * @param {number} [pageSize=10] - the number of records to retrieve as part
410
+ * of the page
411
+ *
412
+ * @returns
413
+ * @type {DriveListPage}
414
+ */
415
+ async listDrivesInPage(pageToken, pageSize = 10) {
416
+ const drive = this.drive();
417
+ const { data } = await drive.drives.list({
418
+ pageSize,
419
+ pageToken,
420
+ });
421
+ return data;
422
+ },
423
+ /**
424
+ * This method yields the visible GDrive drives of the authenticated
425
+ * account. It is a wrapper around [the `drive.drives.list`
426
+ * API](https://bit.ly/3AiWE1x) but defined as a generator to enable lazy
427
+ * loading of multiple pages.
428
+ *
429
+ * @typedef {object} Drive - an object representing a GDrive drive, as
430
+ * defined by [the API](https://bit.ly/3ycifGY)
431
+ *
432
+ * @yields
433
+ * @type {Drive}
434
+ */
435
+ async *listDrives() {
436
+ let pageToken;
437
+
438
+ while (true) {
439
+ const {
440
+ drives = [],
441
+ nextPageToken,
442
+ } = await this.listDrivesInPage(
443
+ pageToken,
444
+ );
445
+
446
+ for (const drive in drives) {
447
+ yield drive;
448
+ }
449
+
450
+ if (!nextPageToken) {
451
+ // The 'nextPageToken' field is only returned when there's still
452
+ // comments to be retrieved (i.e. when the end of the list has not
453
+ // been reached yet): https://bit.ly/3jwxbvy
454
+ break;
455
+ }
456
+
457
+ pageToken = nextPageToken;
458
+ }
459
+ },
460
+ async _listDriveOptions(pageToken) {
461
+ const {
462
+ drives,
463
+ nextPageToken,
464
+ } = await this.listDrivesInPage(pageToken);
465
+
466
+ // "My Drive" isn't returned from the list of drives, so we add it to the
467
+ // list and assign it a static ID that we can refer to when we need. We
468
+ // only do this during the first page of options (i.e. when `pageToken` is
469
+ // undefined).
470
+ const options =
471
+ pageToken !== undefined
472
+ ? []
473
+ : [
474
+ {
475
+ label: "My Drive",
476
+ value: MY_DRIVE_VALUE,
477
+ },
478
+ ];
479
+ for (const d of drives) {
480
+ options.push({
481
+ label: d.name,
482
+ value: d.id,
483
+ });
484
+ }
485
+ return {
486
+ options,
487
+ context: {
488
+ nextPageToken,
489
+ },
490
+ };
491
+ },
492
+ /**
493
+ * A utility method around [the `drive.files.list`
494
+ * API](https://bit.ly/366CFVN) but scoped to a specific page of the API
495
+ * response
496
+ *
497
+ * @typedef {object} FileListPage - an object representing a page that lists
498
+ * GDrive files, as defined by [the API](https://bit.ly/3xdbAwc)
499
+ *
500
+ * @param {string} [pageToken] - the page token for the next page of shared
501
+ * drives
502
+ * @param {object} [extraOpts = {}] - an object containing extra/optional
503
+ * parameters to be fed to the GDrive API call, as defined in [the API
504
+ * docs](https://bit.ly/3AnQDR1)
505
+ *
506
+ * @returns
507
+ * @type {FileListPage}
508
+ */
509
+ async listFilesInPage(pageToken, extraOpts = {}) {
510
+ const drive = this.drive();
511
+ const { data } = await drive.files.list({
512
+ pageToken,
513
+ ...extraOpts,
514
+ });
515
+ return data;
516
+ },
517
+ /**
518
+ * A utility method around [the `drive.files.list`
519
+ * API](https://bit.ly/366CFVN) but scoped to a specific page of the API
520
+ * response, and intended to be used as a way for prop definitions to return
521
+ * a list of options.
522
+ *
523
+ * @param {string} [pageToken] - the page token for the next page of shared
524
+ * drives
525
+ * @param {object} [extraOpts = {}] - an object containing extra/optional
526
+ * parameters to be fed to the GDrive API call, as defined in [the API
527
+ * docs](https://bit.ly/3AnQDR1)
528
+ *
529
+ * @returns a list of prop options
530
+ */
531
+ async listFilesOptions(pageToken, extraOpts = {}) {
532
+ const {
533
+ files,
534
+ nextPageToken,
535
+ } = await this.listFilesInPage(
536
+ pageToken,
537
+ extraOpts,
538
+ );
539
+ const options = files.map((file) => ({
540
+ label: file.name,
541
+ value: file.id,
542
+ }));
543
+ return {
544
+ options,
545
+ context: {
546
+ nextPageToken,
547
+ },
548
+ };
549
+ },
550
+ /**
551
+ * Method returns a list of folder options
552
+ *
553
+ * @param {string} [pageToken] - the page token for the next page of shared
554
+ * drives
555
+ * @param {object} [opts = {}] - an object containing extra/optional
556
+ * parameters to be fed to the GDrive API call, as defined in [the API
557
+ * docs](https://bit.ly/3AnQDR1)
558
+ *
559
+ * @returns a list of prop options
560
+ */
561
+
562
+ async listFolderOptions(pageToken, opts = {}) {
563
+ return await this.listFilesOptions(pageToken, {
564
+ ...opts,
565
+ q: "mimeType = 'application/vnd.google-apps.folder'",
566
+ });
567
+ },
568
+ /**
569
+ * Gets a list of prop options for a GDrive file in a particular GDrive
570
+ * drive, if provided
571
+ *
572
+ * @param {String} [drive] the ID value of a Google Drive, as provided by the
573
+ * `drive` prop definition of this app, to which the file belongs
574
+ * @param {string} [pageToken] - the page token for the next page of files
575
+ * @param {object} [extraOpts = {}] - an object containing extra/optional
576
+ * parameters to be fed to the GDrive API call, as defined in [the API
577
+ * docs](https://bit.ly/3AnQDR1)
578
+ *
579
+ * @returns a list of prop options
580
+ */
581
+ async listDriveFilesOptions(drive, pageToken = null, extraOpts = {}) {
582
+ const opts = {
583
+ ...getListFilesOpts(drive, extraOpts),
584
+ fields: "nextPageToken,files(id,name,parents)",
585
+ };
586
+ // Fetch folders to use to build file paths. If num folders in a drive exceeds 1000, file
587
+ // paths may be incomplete.
588
+ const foldersPromise = this.listFilesInPage(null, {
589
+ ...opts,
590
+ pageSize: 1000, // Max pageSize
591
+ q: `mimeType = '${GOOGLE_DRIVE_FOLDER_MIME_TYPE}'`,
592
+ });
593
+ const filesPromise = this.listFilesInPage(
594
+ pageToken,
595
+ opts,
596
+ );
597
+ const [
598
+ { files: folders },
599
+ {
600
+ files, nextPageToken,
601
+ },
602
+ ] = await Promise.all([
603
+ foldersPromise,
604
+ filesPromise,
605
+ ]);
606
+ const filePaths = this.getFilePaths(files, folders);
607
+ const options = files.map((file) => ({
608
+ label: filePaths[file.id],
609
+ value: file.id,
610
+ }));
611
+ return {
612
+ options,
613
+ context: {
614
+ nextPageToken,
615
+ },
616
+ };
617
+ },
618
+ /**
619
+ * This method yields comments made to a particular GDrive file. It is a
620
+ * wrapper around [the `drive.comments.list` API](https://bit.ly/2UjYajv)
621
+ * but defined as a generator to enable lazy loading of multiple pages.
622
+ *
623
+ * @typedef {object} Comment - an object representing a comment in a GDrive
624
+ * file, as defined by [the API](https://bit.ly/3htAd12)
625
+ *
626
+ * @yields
627
+ * @type {Comment}
628
+ */
629
+ async *listComments(fileId, startModifiedTime = null) {
630
+ const drive = this.drive();
631
+ const opts = {
632
+ fileId,
633
+ fields: "*",
634
+ pageSize: 100,
635
+ };
636
+
637
+ if (startModifiedTime !== null) {
638
+ opts.startModifiedTime = new Date(startModifiedTime).toISOString();
639
+ }
640
+
641
+ while (true) {
642
+ const { data } = await drive.comments.list(opts);
643
+ const {
644
+ comments = [],
645
+ nextPageToken,
646
+ } = data;
647
+
648
+ for (const comment of comments) {
649
+ yield comment;
650
+ }
651
+
652
+ if (!nextPageToken) {
653
+ // The 'nextPageToken' field is only returned when there's still
654
+ // comments to be retrieved (i.e. when the end of the list has not
655
+ // been reached yet): https://bit.ly/3w9ru9m
656
+ break;
657
+ }
658
+
659
+ opts.pageToken = nextPageToken;
660
+ }
661
+ },
662
+ _makeWatchRequestBody(id, address) {
663
+ const expiration =
664
+ Date.now() + WEBHOOK_SUBSCRIPTION_EXPIRATION_TIME_MILLISECONDS;
665
+ return {
666
+ id, // the component-specific channel ID, a UUID
667
+ type: "web_hook",
668
+ address, // the component-specific HTTP endpoint
669
+ expiration,
670
+ };
671
+ },
672
+ async watchDrive(id, address, pageToken, driveId) {
673
+ const drive = this.drive();
674
+ const requestBody = this._makeWatchRequestBody(id, address);
675
+ let watchRequest = {
676
+ pageToken,
677
+ requestBody,
678
+ };
679
+
680
+ // Google expects an entirely different object to be passed
681
+ // when you make a watch request for My Drive vs. a shared drive
682
+ // "My Drive" doesn't have a driveId, so if this method is called
683
+ // without a driveId, we make a watch request for My Drive
684
+ if (driveId) {
685
+ watchRequest = {
686
+ ...watchRequest,
687
+ driveId,
688
+ includeItemsFromAllDrives: true,
689
+ supportsAllDrives: true,
690
+ };
691
+ }
692
+
693
+ // When watching for changes to an entire account, we must pass a pageToken,
694
+ // which points to the moment in time we want to start watching for changes:
695
+ // https://developers.google.com/drive/api/v3/manage-changes
696
+ const {
697
+ expiration,
698
+ resourceId,
699
+ } = (
700
+ await drive.changes.watch(watchRequest)
701
+ ).data;
702
+ console.log(`Watch request for drive successful, expiry: ${expiration}`);
703
+ return {
704
+ expiration: parseInt(expiration),
705
+ resourceId,
706
+ };
707
+ },
708
+ async watchFile(id, address, fileId) {
709
+ const drive = this.drive();
710
+ const requestBody = this._makeWatchRequestBody(id, address);
711
+ const {
712
+ expiration,
713
+ resourceId,
714
+ } = (
715
+ await drive.files.watch({
716
+ fileId,
717
+ requestBody,
718
+ supportsAllDrives: true,
719
+ })
720
+ ).data;
721
+ console.log(
722
+ `Watch request for file ${fileId} successful, expiry: ${expiration}`,
723
+ );
724
+ return {
725
+ expiration: parseInt(expiration),
726
+ resourceId,
727
+ };
728
+ },
729
+ async stopNotifications(id, resourceId) {
730
+ // id = channelID
731
+ // See https://github.com/googleapis/google-api-nodejs-client/issues/627
732
+ const drive = this.drive();
733
+
734
+ // If for some reason the channel doesn't exist, this throws an error
735
+ // It's OK for this to fail in those cases, since we'll renew the channel
736
+ // immediately after trying to stop it if we still want notifications,
737
+ // so we squash the error, log it, and move on.
738
+ try {
739
+ await drive.channels.stop({
740
+ resource: {
741
+ id,
742
+ resourceId,
743
+ },
744
+ });
745
+ console.log(`Stopped push notifications on channel ${id}`);
746
+ } catch (err) {
747
+ console.error(
748
+ `Failed to stop channel ${id} for resource ${resourceId}: ${err}`,
749
+ );
750
+ }
751
+ },
752
+ /**
753
+ * Get a file in a drive
754
+ *
755
+ * @param {string} fileId - the ID value of the file to get
756
+ * @param {object} [params={}] - an object representing parameters used to
757
+ * get a file
758
+ * @param {string} [params.fields="*"] - the paths of the fields to include
759
+ * in the response
760
+ * @param {string} [params.alt] - if set to `media`, then the response
761
+ * includes the file contents in the response body
762
+ * @param {...*} [params.extraParams] - extra/optional parameters to be fed
763
+ * to the GDrive API call, as defined in [the API
764
+ * docs](https://bit.ly/3i5ctkS)
765
+ * @returns the file
766
+ */
767
+ async getFile(fileId, params = {}) {
768
+ const {
769
+ fields = "*",
770
+ alt,
771
+ ...extraParams
772
+ } = params;
773
+ const drive = this.drive();
774
+ return (
775
+ await drive.files.get({
776
+ fileId,
777
+ fields,
778
+ alt,
779
+ supportsAllDrives: true,
780
+ ...extraParams,
781
+ }, (alt === "media")
782
+ ? {
783
+ responseType: "stream",
784
+ }
785
+ : undefined)
786
+ ).data;
787
+ },
788
+ async getDrive(driveId) {
789
+ const drive = this.drive();
790
+ return (
791
+ await drive.drives.get({
792
+ driveId,
793
+ })
794
+ ).data;
795
+ },
796
+ async activateHook(channelID, url, drive) {
797
+ const startPageToken = await this.getPageToken(drive);
798
+ const {
799
+ expiration,
800
+ resourceId,
801
+ } = await this.watchDrive(
802
+ channelID,
803
+ url,
804
+ startPageToken,
805
+ drive,
806
+ );
807
+ return {
808
+ startPageToken,
809
+ expiration,
810
+ resourceId,
811
+ };
812
+ },
813
+ async activateFileHook(channelID, url, fileId) {
814
+ channelID = channelID || uuid();
815
+
816
+ const {
817
+ expiration,
818
+ resourceId,
819
+ } = await this.watchFile(
820
+ channelID,
821
+ url,
822
+ fileId,
823
+ );
824
+
825
+ return {
826
+ expiration,
827
+ resourceId,
828
+ channelID,
829
+ };
830
+ },
831
+ async deactivateHook(channelID, resourceId) {
832
+ if (!channelID) {
833
+ console.log(
834
+ "Channel not found, cannot stop notifications for non-existent channel",
835
+ );
836
+ return;
837
+ }
838
+
839
+ if (!resourceId) {
840
+ console.log(
841
+ "No resource ID found, cannot stop notifications for non-existent resource",
842
+ );
843
+ return;
844
+ }
845
+
846
+ await this.stopNotifications(channelID, resourceId);
847
+ },
848
+ async renewSubscription(drive, subscription, url, channelID, pageToken) {
849
+ const newChannelID = channelID || uuid();
850
+ const driveId = this.getDriveId(drive);
851
+ const newPageToken = pageToken || (await this.getPageToken(driveId));
852
+
853
+ const {
854
+ expiration,
855
+ resourceId,
856
+ } = await this.checkResubscription(
857
+ subscription,
858
+ newChannelID,
859
+ newPageToken,
860
+ url,
861
+ drive,
862
+ );
863
+
864
+ return {
865
+ newChannelID,
866
+ newPageToken,
867
+ expiration,
868
+ resourceId,
869
+ };
870
+ },
871
+ async checkResubscription(
872
+ subscription,
873
+ channelID,
874
+ pageToken,
875
+ endpoint,
876
+ drive,
877
+ ) {
878
+ const driveId = this.getDriveId(drive);
879
+ if (subscription && subscription.resourceId) {
880
+ console.log(
881
+ `Notifications for resource ${subscription.resourceId} are expiring at ${subscription.expiration}.
882
+ Stopping existing sub`,
883
+ );
884
+ await this.stopNotifications(channelID, subscription.resourceId);
885
+ }
886
+
887
+ const {
888
+ expiration,
889
+ resourceId,
890
+ } = await this.watchDrive(
891
+ channelID,
892
+ endpoint,
893
+ pageToken,
894
+ driveId,
895
+ );
896
+ return {
897
+ expiration,
898
+ resourceId,
899
+ };
900
+ },
901
+ async renewFileSubscription(subscription, url, channelID, fileId, nextRunTimestamp) {
902
+ if (nextRunTimestamp && subscription?.expiration < nextRunTimestamp) {
903
+ return subscription;
904
+ }
905
+
906
+ const newChannelID = channelID || uuid();
907
+
908
+ if (subscription?.resourceId) {
909
+ console.log(
910
+ `Notifications for resource ${subscription.resourceId} are expiring at ${subscription.expiration}. Renewing`,
911
+ );
912
+ await this.stopNotifications(
913
+ channelID,
914
+ subscription.resourceId,
915
+ );
916
+ }
917
+ const {
918
+ expiration,
919
+ resourceId,
920
+ } = await this.watchFile(
921
+ channelID,
922
+ url,
923
+ fileId,
924
+ );
925
+
926
+ return {
927
+ newChannelID,
928
+ expiration,
929
+ resourceId,
930
+ };
931
+ },
932
+ /**
933
+ * Get a filtered list of folders
934
+ *
935
+ * @param {object} [opts={}] - an object representing configuration options
936
+ * used to filter the folders that are listed
937
+ * @param {string} [opts.drive] - the ID value of a Google Drive, as
938
+ * provided by the `drive` prop definition of this app
939
+ * @param {string} [opts.name] - the name of the folder to find
940
+ * @param {string} [opts.parentId] - the ID of the parent folder of the
941
+ * folder to find, used to filter the listed folders
942
+ * @param {boolean} [opts.excludeTrashed=true] - `true` if folders in the
943
+ * trash should be excluded
944
+ * @returns the list of folders
945
+ */
946
+ async findFolder(opts = {}) {
947
+ const {
948
+ drive: driveProp,
949
+ name,
950
+ parentId,
951
+ excludeTrashed = true,
952
+ } = opts;
953
+ const drive = this.drive();
954
+ let q = `mimeType = '${GOOGLE_DRIVE_FOLDER_MIME_TYPE}'`;
955
+ if (name) {
956
+ q += ` and name = '${name}'`;
957
+ }
958
+ if (parentId) {
959
+ q += ` and '${parentId}' in parents`;
960
+ }
961
+ if (excludeTrashed) {
962
+ q += " and trashed != true";
963
+ }
964
+ const listOpts = getListFilesOpts(driveProp, {
965
+ q,
966
+ });
967
+ return (await drive.files.list(listOpts)).data.files;
968
+ },
969
+ /**
970
+ * Create a file in a drive
971
+ *
972
+ * @param {object} [opts={}] - an object representing configuration options
973
+ * used to create a file
974
+ * @param {stream.Readable} [opts.file] - a file stream to create the file
975
+ * with
976
+ * @param {string} [opts.mimeType] - the MIME type of the file to create
977
+ * @param {string} [opts.name] - the name of the file to create
978
+ * @param {string} [opts.parentId] - the ID value of the parent folder to
979
+ * create the file in
980
+ * @param {string} [opts.driveId] - the ID value of a Google Drive to create
981
+ * the file in if `parentId` is undefined
982
+ * @param {string} [opts.fields] - the paths of the fields to include in the
983
+ * response
984
+ * @param {string} [opts.supportsAllDrives=true] - whether the requesting
985
+ * application supports both My Drives and shared drives
986
+ * @param {object} [opts.requestBody] - extra/optional properties to be used
987
+ * in the request body of the GDrive API, as defined in [the API
988
+ * docs](https://bit.ly/3kuvbnq)
989
+ * @param {...*} [opts.extraParams] - extra/optional parameters to be fed to
990
+ * the GDrive API call, as defined in [the API docs](https://bit.ly/2VY0MVg)
991
+ * @returns the created file
992
+ */
993
+ async createFile(opts = {}) {
994
+ const {
995
+ file,
996
+ mimeType,
997
+ name,
998
+ parentId,
999
+ driveId,
1000
+ fields,
1001
+ supportsAllDrives = true,
1002
+ requestBody,
1003
+ uploadType,
1004
+ ...extraParams
1005
+ } = opts;
1006
+ const drive = this.drive();
1007
+ const parent = parentId ?? driveId;
1008
+ return (
1009
+ await drive.files.create({
1010
+ fields,
1011
+ supportsAllDrives,
1012
+ media: file
1013
+ ? {
1014
+ mimeType,
1015
+ body: file,
1016
+ uploadType,
1017
+ }
1018
+ : undefined,
1019
+ requestBody: {
1020
+ name,
1021
+ mimeType,
1022
+ parents: parent
1023
+ ? [
1024
+ parent,
1025
+ ]
1026
+ : undefined,
1027
+ ...extraParams.resource,
1028
+ ...requestBody,
1029
+ },
1030
+ ...extraParams,
1031
+ })
1032
+ ).data;
1033
+ },
1034
+ /**
1035
+ * Create a folder in a drive
1036
+ *
1037
+ * @param {object} [opts={}] - an object representing configuration options
1038
+ * used to create a folder
1039
+ * @param {string} [opts.name] - the name of the folder to create
1040
+ * @param {string} [opts.parentId] - the ID value of the parent folder to
1041
+ * create the folder in
1042
+ * @param {string} [opts.fields="*"] - the paths of the fields to include in
1043
+ * the response
1044
+ * @param {...*} [opts.extraParams] - extra/optional parameters to be fed to
1045
+ * the GDrive API call, as defined in [the API docs](https://bit.ly/2VY0MVg)
1046
+ * @returns the created folder
1047
+ */
1048
+ async createFolder(opts = {}) {
1049
+ const {
1050
+ name,
1051
+ parentId,
1052
+ fields = "*",
1053
+ ...extraParams
1054
+ } = opts;
1055
+ return await this.createFile({
1056
+ name,
1057
+ parentId,
1058
+ fields,
1059
+ mimeType: `${GOOGLE_DRIVE_FOLDER_MIME_TYPE}`,
1060
+ ...extraParams,
1061
+ });
1062
+ },
1063
+ /**
1064
+ * Update a file's media content
1065
+ *
1066
+ * @param {string} fileId - the ID value of the file to update
1067
+ * @param {stream.Readable} [fileStream] - a file stream used to update the
1068
+ * content of the file
1069
+ * @param {object} [opts={}] - an object representing configuration options
1070
+ * used to update a file
1071
+ * @param {string} [opts.mimeType] - the MIME type of the file
1072
+ * used to update the file content
1073
+ * @param {string} [opts.supportsAllDrives=true] - whether the requesting
1074
+ * application supports both My Drives and shared drives
1075
+ * @param {...*} [opts.extraParams] - extra/optional parameters to be fed to
1076
+ * the GDrive API call, as defined in [the API docs](https://bit.ly/3lNg9Zw)
1077
+ * @returns the updated file
1078
+ */
1079
+ async updateFileMedia(fileId, fileStream, opts = {}) {
1080
+ const {
1081
+ mimeType,
1082
+ supportsAllDrives = true,
1083
+ uploadType,
1084
+ ...extraParams
1085
+ } = opts;
1086
+ const drive = this.drive();
1087
+ return (
1088
+ await drive.files.update({
1089
+ fileId,
1090
+ supportsAllDrives,
1091
+ media: {
1092
+ mimeType,
1093
+ body: fileStream,
1094
+ uploadType,
1095
+ },
1096
+ ...extraParams,
1097
+ })
1098
+ ).data;
1099
+ },
1100
+ /**
1101
+ * Update a file's metadata
1102
+ *
1103
+ * @param {string} fileId - the ID value of the file to update
1104
+ * @param {object} [opts={}] - an object representing configuration options
1105
+ * used to update a file
1106
+ * @param {string} [opts.name] - the updated name of the file
1107
+ * @param {string} [opts.mimeType] - the updated MIME type of the file
1108
+ * @param {string} [opts.fields] - the paths of the fields to include in
1109
+ * the response
1110
+ * @param {string} [opts.removeParents] - a comma-separated list of parent
1111
+ * folder IDs to add to the file's parents
1112
+ * @param {string} [opts.addParents] - a comma-separated list of parent
1113
+ * folder IDs to add to the file's parents
1114
+ * @param {string} [opts.supportsAllDrives=true] - whether the requesting
1115
+ * application supports both My Drives and shared drives
1116
+ * @param {object} [opts.requestBody] - extra/optional properties to be used
1117
+ * in the request body of the GDrive API, as defined in
1118
+ * [the API docs](https://bit.ly/3nTMi4n)
1119
+ * @param {...*} [opts.extraParams] - extra/optional parameters to be fed to
1120
+ * the GDrive API call, as defined in [the API docs](https://bit.ly/3lNg9Zw)
1121
+ * @returns the updated file
1122
+ */
1123
+ async updateFile(fileId, opts = {}) {
1124
+ const {
1125
+ name,
1126
+ mimeType,
1127
+ fields,
1128
+ removeParents,
1129
+ addParents,
1130
+ supportsAllDrives = true,
1131
+ requestBody,
1132
+ uploadType,
1133
+ ...extraParams
1134
+ } = opts;
1135
+ const drive = this.drive();
1136
+ return (
1137
+ await drive.files.update({
1138
+ fileId,
1139
+ uploadType,
1140
+ removeParents,
1141
+ addParents,
1142
+ fields,
1143
+ supportsAllDrives,
1144
+ requestBody: {
1145
+ name,
1146
+ mimeType,
1147
+ ...requestBody,
1148
+ },
1149
+ ...extraParams,
1150
+ })
1151
+ ).data;
1152
+ },
1153
+ /**
1154
+ * Copy a file
1155
+ *
1156
+ * @param {string} fileId - the ID value of the file to copy
1157
+ * @param {object} [opts={}] - an object representing configuration options
1158
+ * used to copy a file
1159
+ * @param {string} [opts.fields="*"] - the paths of the fields to include in
1160
+ * the response
1161
+ * @param {string} [opts.supportsAllDrives=true] - whether the requesting
1162
+ * application supports both My Drives and shared drives
1163
+ * @param {...*} [opts.extraParams] - extra/optional parameters to be fed to
1164
+ * the GDrive API call, as defined in [the API docs](https://bit.ly/3kq5eFO)
1165
+ * @returns the copy of the file
1166
+ */
1167
+ async copyFile(fileId, opts = {}) {
1168
+ const {
1169
+ fields = "*",
1170
+ supportsAllDrives = true,
1171
+ ...extraParams
1172
+ } = opts;
1173
+ const drive = this.drive();
1174
+ return (
1175
+ await drive.files.copy({
1176
+ fileId,
1177
+ fields,
1178
+ supportsAllDrives,
1179
+ ...extraParams,
1180
+ })
1181
+ ).data;
1182
+ },
1183
+ /**
1184
+ * Delete a file
1185
+ *
1186
+ * @param {string} fileId - the ID value of the file to delete
1187
+ * @param {object} [params={}] - an object representing parameters used to
1188
+ * delete a file
1189
+ * @param {string} [params.supportsAllDrives=true] - whether the requesting
1190
+ * application supports both My Drives and shared drives
1191
+ * @param {...*} [params.extraParams] - extra/optional parameters to be fed
1192
+ * to the GDrive API call, as defined in [the API
1193
+ * docs](https://bit.ly/3MjRkB7)
1194
+ * @returns {void}
1195
+ */
1196
+ async deleteFile(fileId, params = {}) {
1197
+ const {
1198
+ supportsAllDrives = true,
1199
+ ...extraParams
1200
+ } = params;
1201
+ const drive = this.drive();
1202
+ return (
1203
+ await drive.files.delete({
1204
+ fileId,
1205
+ supportsAllDrives,
1206
+ ...extraParams,
1207
+ })
1208
+ ).data;
1209
+ },
1210
+ /**
1211
+ * Download a Google Workspace document using the
1212
+ * [files.export](https://bit.ly/2Zkrxo8) method
1213
+ *
1214
+ * @param {string} fileId - the ID value of the file to download
1215
+ * @param {object} [params={}] - an object representing parameters used to
1216
+ * download a Workspace file
1217
+ * @param {string} [params.mimeType] - the MIME type to which to export the
1218
+ * document
1219
+ * @param {...*} [params.extraParams] - extra/optional parameters to be fed to
1220
+ * the GDrive API call, as defined in [the API docs](https://bit.ly/3o6rRRF)
1221
+ * @returns the file download
1222
+ */
1223
+ async downloadWorkspaceFile(fileId, params = {}) {
1224
+ const {
1225
+ mimeType,
1226
+ ...extraParams
1227
+ } = params;
1228
+ const drive = this.drive();
1229
+ return (
1230
+ await drive.files.export({
1231
+ fileId,
1232
+ mimeType,
1233
+ ...extraParams,
1234
+ }, {
1235
+ responseType: "stream",
1236
+ })
1237
+ ).data;
1238
+ },
1239
+ /**
1240
+ * Create a permission for a file
1241
+ *
1242
+ * @param {string} fileId - the ID value of the file for which to create a
1243
+ * Permission
1244
+ * @param {object} [opts={}] - an object representing configuration options
1245
+ * used to create a permission
1246
+ * @param {string} [opts.role="reader"] - the role granted by this
1247
+ * permission. Currently, one of `owner`,`organizer`,`fileOrganizer`,
1248
+ * `writer`,`commenter`, `reader`.
1249
+ * @param {string} [opts.type] - the type of the grantee. Valid values are:
1250
+ * `user`,`group`,`domain`,`anyone`.
1251
+ * @param {string} [opts.domain] - the domain to which this permission
1252
+ * refers
1253
+ * @param {string} [opts.emailAddress] - the email address of the user or
1254
+ * group to which this permission refers
1255
+ * @param {string} [opts.supportsAllDrives=true] - whether the requesting
1256
+ * application supports both My Drives and shared drives
1257
+ * @returns the created Permission
1258
+ */
1259
+ async createPermission(fileId, opts = {}) {
1260
+ const {
1261
+ role = "reader",
1262
+ type,
1263
+ domain,
1264
+ emailAddress,
1265
+ supportsAllDrives = true,
1266
+ } = opts;
1267
+ const drive = this.drive();
1268
+ return (
1269
+ await drive.permissions.create({
1270
+ fileId,
1271
+ supportsAllDrives,
1272
+ requestBody: omitEmptyStringValues({
1273
+ role,
1274
+ type,
1275
+ domain,
1276
+ emailAddress,
1277
+ }),
1278
+ })
1279
+ ).data;
1280
+ },
1281
+ /**
1282
+ * Get a shared drive
1283
+ *
1284
+ * @param {string} driveId - the ID value of the drive
1285
+ * @param {object} [opts={}] - an object representing configuration options
1286
+ * used to get a shared drive
1287
+ * @param {boolean} [opts.useDomainAccess] - if the request should issued a
1288
+ * domain administrator, granted if the requester an administrator of the
1289
+ * domain to which the shared drive belongs
1290
+ * @returns the shared drive
1291
+ */
1292
+ async getSharedDrive(driveId, opts = {}) {
1293
+ const { useDomainAdminAccess } = opts;
1294
+ const drive = this.drive();
1295
+ return (
1296
+ await drive.drives.get({
1297
+ driveId,
1298
+ useDomainAdminAccess,
1299
+ })
1300
+ ).data;
1301
+ },
1302
+ /**
1303
+ * Search for drives according to the list parameters
1304
+ *
1305
+ * @param {object} [opts={}] - an object representing configuration options
1306
+ * used to search for drives
1307
+ * @param {string} [opts.q] - query string for searching shared drives. See
1308
+ * the ["Search for shared drives"](https://bit.ly/2XJ1oik) guide for
1309
+ * supported syntax.
1310
+ * @param {boolean} [opts.useDomainAccess] - if the request should issued a
1311
+ * domain administrator, granted if the requester an administrator of the
1312
+ * domain to which the shared drive belongs
1313
+ * @param {...*} [opts.extraParams] - extra/optional parameters to be fed to
1314
+ * the GDrive API call, as defined in [the API docs](https://bit.ly/3CCf4e3)
1315
+ * the response
1316
+ * @returns a list of drives
1317
+ */
1318
+ async searchDrives(opts = {}) {
1319
+ const {
1320
+ q,
1321
+ useDomainAdminAccess,
1322
+ ...extraParams
1323
+ } = opts;
1324
+ const drive = this.drive();
1325
+ return (
1326
+ await drive.drives.list({
1327
+ q,
1328
+ useDomainAdminAccess,
1329
+ ...extraParams,
1330
+ })
1331
+ ).data;
1332
+ },
1333
+ /**
1334
+ * Create a drive
1335
+ *
1336
+ * @param {object} [opts={}] - an object representing configuration options
1337
+ * used to create a drive
1338
+ * @param {string} [opts.name] - the name of the drive to create
1339
+ * @param {object} [opts.requestBody] - extra/optional properties to be used
1340
+ * in the request body of the GDrive API, as defined in
1341
+ * [the API docs](https://bit.ly/2ZiyIgJ)
1342
+ * @returns the created drive
1343
+ */
1344
+ async createDrive(opts = {}) {
1345
+ const {
1346
+ name,
1347
+ requestBody,
1348
+ } = opts;
1349
+ const drive = this.drive();
1350
+ return (
1351
+ await drive.drives.create({
1352
+ requestId: uuid(),
1353
+ requestBody: {
1354
+ name,
1355
+ ...requestBody,
1356
+ },
1357
+ })
1358
+ ).data;
1359
+ },
1360
+ /**
1361
+ * Update a shared drive
1362
+ *
1363
+ * @param {string} driveId - the ID value of the drive
1364
+ * @param {object} [opts={}] - an object representing configuration options
1365
+ * used to update a shared drive
1366
+ * @param {boolean} [opts.useDomainAccess] - if the request should issued a
1367
+ * domain administrator, granted if the requester an administrator of the
1368
+ * domain to which the shared drive belongs
1369
+ * @param {object} [opts.requestBody] - extra/optional properties to be used
1370
+ * in the request body of the GDrive API, as defined in
1371
+ * [the API docs](https://bit.ly/3AxJP3c)
1372
+ * @returns the updated shared drive
1373
+ */
1374
+ async updateSharedDrive(driveId, opts = {}) {
1375
+ const {
1376
+ useDomainAdminAccess,
1377
+ requestBody,
1378
+ } = opts;
1379
+ const drive = this.drive();
1380
+ return (
1381
+ await drive.drives.update({
1382
+ driveId,
1383
+ useDomainAdminAccess,
1384
+ requestBody: {
1385
+ ...requestBody,
1386
+ },
1387
+ })
1388
+ ).data;
1389
+ },
1390
+ /**
1391
+ * Delete a shared drive
1392
+ *
1393
+ * @param {string} driveId - the ID value of the drive to delete
1394
+ * @returns {void}
1395
+ */
1396
+ async deleteSharedDrive(driveId) {
1397
+ const drive = this.drive();
1398
+ return (
1399
+ await drive.drives.delete({
1400
+ driveId,
1401
+ })
1402
+ ).data;
1403
+ },
1404
+ /**
1405
+ * Gets information about the user, the user's Drive, and system capabilities. It is a wrapper
1406
+ * around the [the `about.get`
1407
+ * API]{@link https://developers.google.com/drive/api/v3/reference/about/get}.
1408
+ *
1409
+ * @param {string} [fields="*"] - the paths of the fields to include in the response
1410
+ * @returns an About resource
1411
+ */
1412
+ async getAbout(fields = "*") {
1413
+ const drive = this.drive();
1414
+ return (
1415
+ await drive.about.get({
1416
+ fields,
1417
+ })
1418
+ ).data;
1419
+ },
1420
+ /**
1421
+ * Get a list of all supported export formats supported by the system for this user
1422
+ *
1423
+ * @see
1424
+ * {@link https://bit.ly/3HRbUqd Google Workspace documents and corresponding export MIME types}
1425
+ *
1426
+ * @returns a list of supported export formats for each Google Workspace format
1427
+ */
1428
+ async getExportFormats() {
1429
+ return (await this.getAbout("exportFormats")).exportFormats;
1430
+ },
1431
+ },
1432
+ };