@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,104 @@
1
+ // This source processes changes to any files in a user's Google Drive,
2
+ // implementing strategy enumerated in the Push Notifications API docs:
3
+ // https://developers.google.com/drive/api/v3/push and here:
4
+ // https://developers.google.com/drive/api/v3/manage-changes
5
+ //
6
+ // This source has two interfaces:
7
+ //
8
+ // 1) The HTTP requests tied to changes in the user's Google Drive
9
+ // 2) A timer that runs on regular intervals, renewing the notification channel as needed
10
+
11
+ import common from "../common-webhook.mjs";
12
+ import { GOOGLE_DRIVE_NOTIFICATION_CHANGE } from "../../constants.mjs";
13
+
14
+ export default {
15
+ ...common,
16
+ key: "google_drive-new-or-modified-comments",
17
+ name: "New or Modified Comments",
18
+ description:
19
+ "Emits a new event any time a file comment is added, modified, or deleted in your linked Google Drive",
20
+ version: "0.0.9",
21
+ type: "source",
22
+ // Dedupe events based on the "x-goog-message-number" header for the target channel:
23
+ // https://developers.google.com/drive/api/v3/push#making-watch-requests
24
+ dedupe: "unique",
25
+ hooks: {
26
+ ...common.hooks,
27
+ async activate() {
28
+ await common.hooks.activate.bind(this)();
29
+ this._setInitTime(Date.now());
30
+ },
31
+ async deactivate() {
32
+ await common.hooks.deactivate.bind(this)();
33
+ this._setInitTime(null);
34
+ },
35
+ },
36
+ methods: {
37
+ ...common.methods,
38
+ _getInitTime() {
39
+ return this.db.get("initTime");
40
+ },
41
+ _setInitTime(initTime) {
42
+ this.db.set("initTime", initTime);
43
+ },
44
+ _getLastCommentTimeForFile(fileId) {
45
+ return this.db.get(fileId) || this._getInitTime();
46
+ },
47
+ _updateLastCommentTimeForFile(fileId, commentTime) {
48
+ this.db.set(fileId, commentTime);
49
+ },
50
+ getUpdateTypes() {
51
+ return [
52
+ GOOGLE_DRIVE_NOTIFICATION_CHANGE,
53
+ ];
54
+ },
55
+ generateMeta(data, headers) {
56
+ const {
57
+ id: commentId,
58
+ content: summary,
59
+ modifiedTime: tsString,
60
+ } = data;
61
+ const { "x-goog-message-number": eventId } = headers;
62
+ return {
63
+ id: `${commentId}-${eventId}`,
64
+ summary,
65
+ ts: Date.parse(tsString),
66
+ };
67
+ },
68
+ async processChanges(changedFiles, headers) {
69
+ for (const file of changedFiles) {
70
+ const lastCommentTimeForFile = this._getLastCommentTimeForFile(file.id);
71
+ let maxModifiedTime = lastCommentTimeForFile;
72
+ const commentsStream = this.googleDrive.listComments(
73
+ file.id,
74
+ lastCommentTimeForFile,
75
+ );
76
+
77
+ for await (const comment of commentsStream) {
78
+ const commentTime = Date.parse(comment.modifiedTime);
79
+ if (commentTime <= lastCommentTimeForFile) {
80
+ continue;
81
+ }
82
+
83
+ const eventToEmit = {
84
+ comment,
85
+ file,
86
+ change: {
87
+ state: headers["x-goog-resource-state"],
88
+ resourceURI: headers["x-goog-resource-uri"],
89
+
90
+ // Additional details about the changes. Possible values: content,
91
+ // parents, children, permissions.
92
+ changed: headers["x-goog-changed"],
93
+ },
94
+ };
95
+ const meta = this.generateMeta(comment, headers);
96
+ this.$emit(eventToEmit, meta);
97
+
98
+ maxModifiedTime = Math.max(maxModifiedTime, commentTime);
99
+ this._updateLastCommentTimeForFile(file.id, maxModifiedTime);
100
+ }
101
+ }
102
+ },
103
+ },
104
+ };
@@ -0,0 +1,66 @@
1
+ // This source processes changes to any files in a user's Google Drive,
2
+ // implementing strategy enumerated in the Push Notifications API docs:
3
+ // https://developers.google.com/drive/api/v3/push and here:
4
+ // https://developers.google.com/drive/api/v3/manage-changes
5
+ //
6
+ // This source has two interfaces:
7
+ //
8
+ // 1) The HTTP requests tied to changes in the user's Google Drive
9
+ // 2) A timer that runs on regular intervals, renewing the notification channel as needed
10
+
11
+ import common from "../common-webhook.mjs";
12
+ import {
13
+ GOOGLE_DRIVE_NOTIFICATION_ADD,
14
+ GOOGLE_DRIVE_NOTIFICATION_CHANGE,
15
+ GOOGLE_DRIVE_NOTIFICATION_UPDATE,
16
+ } from "../../constants.mjs";
17
+
18
+ export default {
19
+ ...common,
20
+ key: "google_drive-new-or-modified-files",
21
+ name: "New or Modified Files",
22
+ description:
23
+ "Emits a new event any time any file in your linked Google Drive is added, modified, or deleted",
24
+ version: "0.0.20",
25
+ type: "source",
26
+ // Dedupe events based on the "x-goog-message-number" header for the target channel:
27
+ // https://developers.google.com/drive/api/v3/push#making-watch-requests
28
+ dedupe: "unique",
29
+ methods: {
30
+ ...common.methods,
31
+ getUpdateTypes() {
32
+ return [
33
+ GOOGLE_DRIVE_NOTIFICATION_ADD,
34
+ GOOGLE_DRIVE_NOTIFICATION_CHANGE,
35
+ GOOGLE_DRIVE_NOTIFICATION_UPDATE,
36
+ ];
37
+ },
38
+ generateMeta(data, headers) {
39
+ const {
40
+ id: fileId,
41
+ name: summary,
42
+ modifiedTime: tsString,
43
+ } = data;
44
+ const { "x-goog-message-number": eventId } = headers;
45
+ return {
46
+ id: `${fileId}-${eventId}`,
47
+ summary,
48
+ ts: Date.parse(tsString),
49
+ };
50
+ },
51
+ async processChanges(changedFiles, headers) {
52
+ for (const file of changedFiles) {
53
+ const eventToEmit = {
54
+ file,
55
+ change: {
56
+ state: headers["x-goog-resource-state"],
57
+ resourceURI: headers["x-goog-resource-uri"],
58
+ changed: headers["x-goog-changed"], // "Additional details about the changes. Possible values: content, parents, children, permissions"
59
+ },
60
+ };
61
+ const meta = this.generateMeta(file, headers);
62
+ this.$emit(eventToEmit, meta);
63
+ }
64
+ },
65
+ },
66
+ };
@@ -0,0 +1,86 @@
1
+ // This source processes changes to any files in a user's Google Drive,
2
+ // implementing strategy enumerated in the Push Notifications API docs:
3
+ // https://developers.google.com/drive/api/v3/push and here:
4
+ // https://developers.google.com/drive/api/v3/manage-changes
5
+ //
6
+ // This source has two interfaces:
7
+ //
8
+ // 1) The HTTP requests tied to changes in the user's Google Drive
9
+ // 2) A timer that runs on regular intervals, renewing the notification channel as needed
10
+
11
+ import common from "../common-webhook.mjs";
12
+ import {
13
+ GOOGLE_DRIVE_NOTIFICATION_ADD,
14
+ GOOGLE_DRIVE_NOTIFICATION_CHANGE,
15
+ GOOGLE_DRIVE_NOTIFICATION_UPDATE,
16
+ } from "../../constants.mjs";
17
+
18
+ export default {
19
+ ...common,
20
+ key: "google_drive-new-or-modified-folders",
21
+ name: "New or Modified Folders",
22
+ description:
23
+ "Emits a new event any time any folder in your linked Google Drive is added, modified, or deleted",
24
+ version: "0.0.9",
25
+ type: "source",
26
+ // Dedupe events based on the "x-goog-message-number" header for the target channel:
27
+ // https://developers.google.com/drive/api/v3/push#making-watch-requests
28
+ dedupe: "unique",
29
+ methods: {
30
+ ...common.methods,
31
+ _getLastModifiedTimeForFile(fileId) {
32
+ return this.db.get(fileId);
33
+ },
34
+ _setModifiedTimeForFile(fileId, modifiedTime) {
35
+ this.db.set(fileId, modifiedTime);
36
+ },
37
+ getUpdateTypes() {
38
+ return [
39
+ GOOGLE_DRIVE_NOTIFICATION_ADD,
40
+ GOOGLE_DRIVE_NOTIFICATION_CHANGE,
41
+ GOOGLE_DRIVE_NOTIFICATION_UPDATE,
42
+ ];
43
+ },
44
+ generateMeta(data, ts) {
45
+ const {
46
+ id: fileId,
47
+ name: summary,
48
+ } = data;
49
+ return {
50
+ id: `${fileId}-${ts}`,
51
+ summary,
52
+ ts,
53
+ };
54
+ },
55
+ async processChanges(changedFiles, headers) {
56
+ const files = changedFiles.filter(
57
+ // API docs that define Google Drive folders:
58
+ // https://developers.google.com/drive/api/v3/folder
59
+ (file) => file.mimeType === "application/vnd.google-apps.folder",
60
+ );
61
+
62
+ for (const file of files) {
63
+ // The changelog is updated each time a folder is opened. Check the
64
+ // folder's `modifiedTime` to see if the folder has been modified.
65
+ const fileInfo = await this.googleDrive.getFile(file.id);
66
+
67
+ const lastModifiedTimeForFile = this._getLastModifiedTimeForFile(file.id);
68
+ const modifiedTime = Date.parse(fileInfo.modifiedTime);
69
+ if (lastModifiedTimeForFile == modifiedTime) continue;
70
+
71
+ const eventToEmit = {
72
+ file,
73
+ change: {
74
+ state: headers["x-goog-resource-state"],
75
+ resourceURI: headers["x-goog-resource-uri"],
76
+ changed: headers["x-goog-changed"], // "Additional details about the changes. Possible values: content, parents, children, permissions"
77
+ },
78
+ };
79
+ const meta = this.generateMeta(file, modifiedTime);
80
+ this.$emit(eventToEmit, meta);
81
+
82
+ this._setModifiedTimeForFile(file.id, modifiedTime);
83
+ }
84
+ },
85
+ },
86
+ };
@@ -0,0 +1,68 @@
1
+ import googleDrive from "../../google_drive.app.mjs";
2
+
3
+ export default {
4
+ key: "google_drive-new-shared-drive",
5
+ name: "New Shared Drive",
6
+ description: "Emits a new event any time a shared drive is created.",
7
+ version: "0.0.9",
8
+ type: "source",
9
+ dedupe: "unique",
10
+ props: {
11
+ googleDrive,
12
+ db: "$.service.db",
13
+ timer: {
14
+ label: "Polling interval",
15
+ description: "Interval to poll the Google Drive API for new shared drives",
16
+ type: "$.interface.timer",
17
+ default: {
18
+ intervalSeconds: 60 * 15, // 30 minutes
19
+ },
20
+ },
21
+ },
22
+ hooks: {
23
+ async deploy() {
24
+ const { drives: initDrives } = await this.googleDrive.listDrivesInPage();
25
+ for (const drive of initDrives) {
26
+ const newDrive = await this.googleDrive.getDrive(drive.id);
27
+ const meta = this.generateMeta(newDrive);
28
+ this.$emit(newDrive, meta);
29
+ }
30
+
31
+ this._setKnownDrives(initDrives.map((drive) => drive.id));
32
+ },
33
+ },
34
+ methods: {
35
+ _getKnownDrives() {
36
+ return this.db.get("driveIds");
37
+ },
38
+ _setKnownDrives(driveIds) {
39
+ this.db.set("driveIds", Array.from(driveIds));
40
+ },
41
+ generateMeta(drive) {
42
+ const ts = new Date(drive.createdTime).getTime();
43
+ return {
44
+ id: drive.id,
45
+ summary: drive.name,
46
+ ts,
47
+ };
48
+ },
49
+ },
50
+ async run() {
51
+ const knownDrives = new Set(this._getKnownDrives());
52
+ const drivesStream = this.googleDrive.listDrives();
53
+ for await (const drive of drivesStream) {
54
+ if (knownDrives.has(drive.id)) {
55
+ // We've already seen this drive, so we skip it
56
+ continue;
57
+ }
58
+
59
+ knownDrives.add(drive.id);
60
+
61
+ const newDrive = await this.googleDrive.getDrive(drive.id);
62
+ const meta = this.generateMeta(newDrive);
63
+ this.$emit(newDrive, meta);
64
+ }
65
+
66
+ this._setKnownDrives(knownDrives);
67
+ },
68
+ };
package/utils.mjs ADDED
@@ -0,0 +1,267 @@
1
+ import fs from "fs";
2
+ import { axios } from "@pipedream/platform";
3
+ import {
4
+ MY_DRIVE_VALUE,
5
+ LEGACY_MY_DRIVE_VALUE,
6
+ MAX_FILE_OPTION_PATH_SEGMENTS,
7
+ } from "./constants.mjs";
8
+
9
+ /**
10
+ * Returns whether the specified drive ID corresponds to the authenticated
11
+ * user's My Drive or not
12
+ *
13
+ * @param {String} drive the ID value of a Google Drive
14
+ * @returns `true` only when the specified drive is the user's 'My Drive'
15
+ */
16
+ function isMyDrive(drive) {
17
+ return drive === MY_DRIVE_VALUE || drive === LEGACY_MY_DRIVE_VALUE;
18
+ }
19
+
20
+ /**
21
+ * Returns a valid Google Drive ID to be used in Google Drive API calls
22
+ *
23
+ * @param {String} drive the ID value of a Google Drive, as provided by the
24
+ * `drive` prop definition of this app
25
+ * @returns the proper Google Drive ID to be used in Google Drive API calls
26
+ */
27
+ function getDriveId(drive) {
28
+ return isMyDrive(drive)
29
+ ? null
30
+ : drive;
31
+ }
32
+
33
+ /**
34
+ * Gets an options object to be used in functions that use the
35
+ * [the `drive.drives.list` API](https://bit.ly/3AiWE1x).
36
+ *
37
+ * @param {String} drive the ID value of a Google Drive, as provided by the
38
+ * `drive` prop definition of this app
39
+ * @param {object} [baseOpts = {}] - an object containing extra/optional
40
+ * parameters to be fed to the GDrive API call, as defined in [the API
41
+ * docs](https://bit.ly/3AnQDR1)
42
+ *
43
+ * @returns an object containing the options
44
+ */
45
+ function getListFilesOpts(drive, baseOpts = {}) {
46
+ // Use default options (e.g., `corpora=drive`) for `files.list` if `drive` is
47
+ // empty or is "My Drive". Otherwise, use the "drive" corpus and include
48
+ // `supportsAllDrives` param.
49
+ const opts = (!drive || isMyDrive(drive))
50
+ ? baseOpts
51
+ : {
52
+ ...baseOpts,
53
+ corpora: "drive",
54
+ driveId: getDriveId(drive),
55
+ includeItemsFromAllDrives: true,
56
+ supportsAllDrives: true,
57
+ };
58
+ return opts;
59
+ }
60
+
61
+ /**
62
+ * Returns a file stream from a file URL or file path to be used in Google Drive
63
+ * API calls
64
+ *
65
+ * @param {Object} opts - an object containing options for getting a file stream
66
+ * @param {String} opts.fileUrl - the url of a file to download to a Readable
67
+ * stream
68
+ * @param {String} opts.filePath - the path to a file from which to create a
69
+ * Readable stream
70
+ * @returns {stream.Readable} a Readable stream from the file URL or file path
71
+ */
72
+ async function getFileStream({
73
+ $, fileUrl, filePath,
74
+ }) {
75
+ return fileUrl
76
+ ? (await axios($ ?? this, {
77
+ url: fileUrl,
78
+ method: "GET",
79
+ responseType: "stream",
80
+ }))
81
+ : fs.createReadStream(filePath);
82
+ }
83
+
84
+ function streamToBuffer(stream) {
85
+ return new Promise((resolve, reject) => {
86
+ const chunks = [];
87
+ stream.on("data", (chunk) => {
88
+ chunks.push(chunk);
89
+ }).on("end", () => {
90
+ resolve(Buffer.concat(chunks));
91
+ // eslint-disable-next-line newline-per-chained-call
92
+ }).on("error", (err) => {
93
+ reject(err);
94
+ });
95
+ });
96
+ }
97
+
98
+ function byteToMB(byte) {
99
+ return byte / 1024 / 1024;
100
+ }
101
+
102
+ /**
103
+ * Truncate an array of path segments from its base
104
+ *
105
+ * @param {String[]} pathArr - the array of path segments
106
+ * @returns the truncated array whose first element is "..." if truncated
107
+ */
108
+ function truncatePath(pathArr) {
109
+ if (pathArr.length <= MAX_FILE_OPTION_PATH_SEGMENTS) {
110
+ return pathArr;
111
+ }
112
+ return [
113
+ "...",
114
+ ...pathArr.slice(-1 * (MAX_FILE_OPTION_PATH_SEGMENTS - 1)),
115
+ ];
116
+ }
117
+
118
+ /**
119
+ * Builds an object mapping file IDs to arrays of file/folder ID path segments from the drive's root
120
+ * folder to each file, using the `file.parents` property and a list of folders in the drive
121
+ *
122
+ * @see
123
+ * {@link https://developers.google.com/drive/api/v3/reference/files Google Drive File Resource}
124
+ *
125
+ * @param {object[]} files - the array of files for which to build paths
126
+ * @param {object[]} folders - the array of folders in the drive
127
+ * @returns {Object.<string, string[]>} the object mapping file IDs to arrays of path segments
128
+ */
129
+ function buildFilePaths(files = [], folders = []) {
130
+ const folderIdToFolder = folders.reduce((acc, cur) => {
131
+ acc[cur.id] = cur;
132
+ return acc;
133
+ }, {});
134
+ const paths = {};
135
+ // Recursive function that returns an array of file `id`s representing the path to a file if
136
+ // requisite parent folders are available (in `file.parents`) to the requesting user, or an array
137
+ // containing the file ID otherwise
138
+ const pathToFile = (file) => {
139
+ if (!file) {
140
+ // unretrieved folder or root folder
141
+ return [];
142
+ }
143
+ if (paths[file.id] !== undefined) {
144
+ return paths[file.id];
145
+ }
146
+ if (!file.parents) {
147
+ // file belongs to a different drive and user does not have access to the parent
148
+ return [
149
+ file.id,
150
+ ];
151
+ }
152
+ let parentPath;
153
+ for (const parent of file.parents) {
154
+ parentPath = pathToFile(folderIdToFolder[parent]);
155
+ paths[parent] = parentPath;
156
+ if (parentPath?.[0]) {
157
+ break;
158
+ }
159
+ }
160
+ return [
161
+ ...parentPath,
162
+ file.id,
163
+ ];
164
+ };
165
+ files.forEach((file) => {
166
+ paths[file.id] = pathToFile(file);
167
+ });
168
+ return paths;
169
+ }
170
+
171
+ /**
172
+ * Builds an object mapping file IDs to arrays of file/folder name path segments from the drive's
173
+ * root folder to each file, using the `file.parents` property and a list of folders in the drive
174
+ *
175
+ * @param {object[]} files - the array of files for which to build paths
176
+ * @param {object[]} folders - the array of folders in the drive
177
+ * @returns {Object.<string, string[]>} the object mapping file IDs to arrays of path segments
178
+ */
179
+ function buildFileNamePaths(files = [], folders = []) {
180
+ const fileIdToFile = files.concat(folders).reduce((acc, cur) => {
181
+ acc[cur.id] = cur;
182
+ return acc;
183
+ }, {});
184
+ const fileIdToPath = buildFilePaths(files, folders);
185
+ return Object.fromEntries(Object.entries(fileIdToPath).map(([
186
+ id,
187
+ path,
188
+ ]) => ([
189
+ id,
190
+ path.filter((id) => fileIdToFile[id]?.name)
191
+ .map((id) => fileIdToFile[id]?.name),
192
+ ])));
193
+ }
194
+
195
+ /**
196
+ * Gets an object mapping file IDs to string paths from the drive's root folder to each file, if the
197
+ * file's `parents` are available to the requesting user
198
+ *
199
+ * @param {object[]} files - the array of files for which to get file paths
200
+ * @param {object[]} folders - the array of folders in the drive
201
+ * @returns {Object.<string, string>} the object mapping file IDs to file paths
202
+ */
203
+ function getFilePaths(files = [], folders = []) {
204
+ const fileIdToNamePath = buildFileNamePaths(files, folders);
205
+ return Object.fromEntries(Object.entries(fileIdToNamePath).map(([
206
+ id,
207
+ path,
208
+ ]) => ([
209
+ id,
210
+ truncatePath(path).join(" > "),
211
+ ])));
212
+ }
213
+
214
+ /**
215
+ * Return an object compose of non-empty string valued properties of `obj`
216
+ *
217
+ * @param {Object} obj - the source object
218
+ * @param {String[]} [fromKeys] - keys of properties in `obj` to omit if corresponding value is
219
+ * empty string, or all keys by default
220
+ * @returns the new object
221
+ */
222
+ function omitEmptyStringValues(obj, fromKeys) {
223
+ return Object.fromEntries(
224
+ // eslint-disable-next-line multiline-ternary,array-element-newline,array-bracket-newline
225
+ Object.entries(obj).filter(([ k, v ]) => {
226
+ return (fromKeys && !fromKeys.includes(k)) || v !== "";
227
+ }),
228
+ );
229
+ }
230
+
231
+ /**
232
+ * A utility function that accepts a string as an argument and reformats it in
233
+ * order to remove newline characters and consecutive spaces. Useful when
234
+ * dealing with very long templated strings that are split into multiple lines.
235
+ *
236
+ * @example
237
+ * // returns "This is a much cleaner string"
238
+ * toSingleLineString(`
239
+ * This is a much
240
+ * cleaner string
241
+ * `);
242
+ *
243
+ * @param {string} multiLineString the input string to reformat
244
+ * @returns a formatted string based on the content of the input argument,
245
+ * without newlines and multiple spaces
246
+ * Source: {@linkcode ../aws/sources/common/utils.mjs utils.mjs}.
247
+ */
248
+ function toSingleLineString(multiLineString) {
249
+ return multiLineString
250
+ .trim()
251
+ .replace(/\n/g, " ")
252
+ .replace(/\s{2,}/g, " ");
253
+ }
254
+
255
+ export {
256
+ MY_DRIVE_VALUE,
257
+ isMyDrive,
258
+ getDriveId,
259
+ getListFilesOpts,
260
+ getFileStream,
261
+ omitEmptyStringValues,
262
+ toSingleLineString,
263
+ buildFilePaths,
264
+ getFilePaths,
265
+ streamToBuffer,
266
+ byteToMB,
267
+ };