@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.
- package/actions/add-file-sharing-preference/add-file-sharing-preference.mjs +83 -0
- package/actions/copy-file/copy-file.mjs +34 -0
- package/actions/create-file/create-file.mjs +242 -0
- package/actions/create-file-from-template/create-file-from-template.mjs +98 -0
- package/actions/create-file-from-text/create-file-from-text.mjs +67 -0
- package/actions/create-folder/create-folder.mjs +89 -0
- package/actions/create-shared-drive/create-shared-drive.mjs +25 -0
- package/actions/delete-file/delete-file.mjs +37 -0
- package/actions/delete-shared-drive/delete-shared-drive.mjs +30 -0
- package/actions/download-file/download-file.mjs +120 -0
- package/actions/find-file/find-file.mjs +35 -0
- package/actions/find-folder/find-folder.mjs +49 -0
- package/actions/get-folder-id-for-path/get-folder-id-for-path.mjs +62 -0
- package/actions/get-shared-drive/get-shared-drive.mjs +37 -0
- package/actions/google-mime-types.mjs +19 -0
- package/actions/google-workspace-export-formats.mjs +74 -0
- package/actions/language-codes.mjs +742 -0
- package/actions/move-file/move-file.mjs +52 -0
- package/actions/move-file-to-trash/move-file-to-trash.mjs +41 -0
- package/actions/replace-file/replace-file.mjs +134 -0
- package/actions/search-shared-drives/search-shared-drives.mjs +34 -0
- package/actions/update-file/update-file.mjs +164 -0
- package/actions/update-shared-drive/update-shared-drive.mjs +77 -0
- package/actions/upload-file/upload-file.mjs +120 -0
- package/constants.mjs +200 -0
- package/google_drive.app.mjs +1432 -0
- package/package.json +23 -20
- package/sources/changes-to-specific-files/changes-to-specific-files.mjs +226 -0
- package/sources/changes-to-specific-files-shared-drive/changes-to-specific-files-shared-drive.mjs +110 -0
- package/sources/common-webhook.mjs +201 -0
- package/sources/new-files-instant/new-files-instant.mjs +95 -0
- package/sources/new-or-modified-comments/new-or-modified-comments.mjs +104 -0
- package/sources/new-or-modified-files/new-or-modified-files.mjs +66 -0
- package/sources/new-or-modified-folders/new-or-modified-folders.mjs +86 -0
- package/sources/new-shared-drive/new-shared-drive.mjs +68 -0
- package/utils.mjs +267 -0
- package/google_drive.app.js +0 -212
- package/sources/changes-to-specific-files/changes-to-specific-files.js +0 -226
- 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
|
+
};
|