@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
package/package.json CHANGED
@@ -1,21 +1,24 @@
1
1
  {
2
- "name": "@pipedream/google_drive",
3
- "version": "0.3.2",
4
- "description": "Pipedream Google_drive Components",
5
- "main": "google_drive.app.js",
6
- "keywords": [
7
- "pipedream",
8
- "google_drive"
9
- ],
10
- "homepage": "https://pipedream.com/apps/google_drive",
11
- "author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
12
- "license": "MIT",
13
- "dependencies": {
14
- "axios": "^0.21.1",
15
- "googleapis": "^67.0.0"
16
- },
17
- "gitHead": "bf0e1cb3ad6da248c6125f9b512c59bfe35bf1fa",
18
- "publishConfig": {
19
- "access": "public"
20
- }
21
- }
2
+ "name": "@pipedream/google_drive",
3
+ "version": "0.4.1",
4
+ "description": "Pipedream Google_drive Components",
5
+ "main": "google_drive.app.mjs",
6
+ "keywords": [
7
+ "pipedream",
8
+ "google_drive"
9
+ ],
10
+ "homepage": "https://pipedream.com/apps/google_drive",
11
+ "author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
12
+ "license": "MIT",
13
+ "dependencies": {
14
+ "@googleapis/drive": "^2.3.0",
15
+ "@pipedream/platform": "^0.9.0",
16
+ "axios": "^0.21.1",
17
+ "mime-db": "^1.51.0",
18
+ "uuid": "^8.3.2"
19
+ },
20
+ "gitHead": "e12480b94cc03bed4808ebc6b13e7fdb3a1ba535",
21
+ "publishConfig": {
22
+ "access": "public"
23
+ }
24
+ }
@@ -0,0 +1,226 @@
1
+ import cronParser from "cron-parser";
2
+ import includes from "lodash/includes.js";
3
+ import { v4 as uuid } from "uuid";
4
+
5
+ import { MY_DRIVE_VALUE } from "../../constants.mjs";
6
+
7
+ import changesToSpecificFiles from "../changes-to-specific-files-shared-drive/changes-to-specific-files-shared-drive.mjs";
8
+
9
+ /**
10
+ * This source uses the Google Drive API's
11
+ * {@link https://developers.google.com/drive/api/v3/reference/files/watch files: watch}
12
+ * endpoint to subscribe to changes to specific files in the user's drive.
13
+ */
14
+ export default {
15
+ ...changesToSpecificFiles,
16
+ key: "google_drive-changes-to-specific-files",
17
+ name: "Changes to Specific Files",
18
+ description: "Watches for changes to specific files, emitting an event any time a change is made to one of those files. To watch for changes to [shared drive](https://support.google.com/a/users/answer/9310351) files, use the **Changes to Specific Files (Shared Drive)** source instead.",
19
+ version: "0.0.19",
20
+ type: "source",
21
+ // Dedupe events based on the "x-goog-message-number" header for the target channel:
22
+ // https://developers.google.com/drive/api/v3/push#making-watch-requests
23
+ dedupe: "unique",
24
+ props: {
25
+ ...changesToSpecificFiles.props,
26
+ drive: {
27
+ type: "string",
28
+ label: "Drive",
29
+ description: "Defaults to `My Drive`. To use a [Shared Drive](https://support.google.com/a/users/answer/9310351), use the **Changes to Specific Files (Shared Drive)** source instead.",
30
+ optional: true,
31
+ default: MY_DRIVE_VALUE,
32
+ },
33
+ updateTypes: {
34
+ propDefinition: [
35
+ changesToSpecificFiles.props.googleDrive,
36
+ "updateTypes",
37
+ ],
38
+ },
39
+ },
40
+ hooks: {
41
+ ...changesToSpecificFiles.hooks,
42
+ async activate() {
43
+ // Called when a component is created or updated. Handles all the logic
44
+ // for starting and stopping watch notifications tied to the desired
45
+ // files.
46
+
47
+ // You can pass the same channel ID in watch requests for multiple files, so
48
+ // our channel ID is fixed for this component to simplify the state we have to
49
+ // keep track of.
50
+ const channelID = this._getChannelID() || uuid();
51
+
52
+ // Subscriptions are keyed on Google's resourceID, "an opaque value that
53
+ // identifies the watched resource". This value is included in request
54
+ // headers, allowing us to look up the watched resource.
55
+ let subscriptions = this._getSubscriptions() || {};
56
+
57
+ const files = this.files;
58
+ for (const fileID of files) {
59
+ const {
60
+ expiration,
61
+ resourceId,
62
+ } = await this.googleDrive.activateFileHook(
63
+ channelID,
64
+ this.http.endpoint,
65
+ fileID,
66
+ );
67
+ // The fileID must be kept with the subscription metadata so we can
68
+ // renew the watch request for this specific file when it expires.
69
+ subscriptions[resourceId] = {
70
+ expiration,
71
+ fileID,
72
+ };
73
+ }
74
+
75
+ // Save metadata on the subscription so we can stop / renew later
76
+ this._setSubscriptions(subscriptions);
77
+ this._setChannelID(channelID);
78
+ },
79
+ async deactivate() {
80
+ const channelID = this._getChannelID();
81
+ if (!channelID) {
82
+ console.log(
83
+ "Channel not found, cannot stop notifications for non-existent channel",
84
+ );
85
+ return;
86
+ }
87
+
88
+ const subscriptions = this._getSubscriptions() || {};
89
+ for (const resourceId of Object.keys(subscriptions)) {
90
+ await this.googleDrive.stopNotifications(channelID, resourceId);
91
+ }
92
+
93
+ // Reset DB state
94
+ this._setSubscriptions({});
95
+ this._setChannelID(null);
96
+ },
97
+ },
98
+ methods: {
99
+ ...changesToSpecificFiles.methods,
100
+ _getSubscriptions() {
101
+ return this.db.get("subscriptions") || {};
102
+ },
103
+ _setSubscriptions(subscriptions) {
104
+ this.db.set("subscriptions", subscriptions);
105
+ },
106
+ _getNextTimerEventTimestamp(event) {
107
+ if (event.cron) {
108
+ return cronParser
109
+ .parseExpression(event.cron)
110
+ .next()
111
+ .getTime();
112
+ }
113
+ if (event.interval_seconds) {
114
+ return Date.now() + event.interval_seconds * 1000;
115
+ }
116
+ },
117
+ async renewFileSubscriptions(event) {
118
+ // Assume subscription & channelID may all be undefined at
119
+ // this point Handle their absence appropriately.
120
+ const subscriptions = this._getSubscriptions() || {};
121
+ const channelID = this._getChannelID() || uuid();
122
+
123
+ const nextRunTimestamp = this._getNextTimerEventTimestamp(event);
124
+
125
+ for (const [
126
+ currentResourceId,
127
+ metadata,
128
+ ] of Object.entries(subscriptions)) {
129
+ const { fileID } = metadata;
130
+
131
+ const subscription = {
132
+ ...metadata,
133
+ resourceId: currentResourceId,
134
+ };
135
+ const {
136
+ expiration,
137
+ resourceId,
138
+ } = await this.googleDrive.renewFileSubscription(
139
+ subscription,
140
+ this.http.endpoint,
141
+ channelID,
142
+ fileID,
143
+ nextRunTimestamp,
144
+ );
145
+ subscriptions[resourceId] = {
146
+ expiration,
147
+ fileID,
148
+ };
149
+ }
150
+ this._setSubscriptions(subscriptions);
151
+ this._setChannelID(channelID);
152
+ },
153
+ },
154
+ async run(event) {
155
+ // This function is polymorphic: it can be triggered as a cron job, to make sure we renew
156
+ // watch requests for specific files, or via HTTP request (the change payloads from Google)
157
+
158
+ // Component was invoked by timer
159
+ if (event.timestamp) {
160
+ return this.renewFileSubscriptions(event);
161
+ }
162
+
163
+ const channelID = this._getChannelID();
164
+ let subscriptions = this._getSubscriptions() || {};
165
+
166
+ const { headers } = event;
167
+
168
+ if (
169
+ !headers["x-goog-resource-state"] ||
170
+ !headers["x-goog-resource-id"] ||
171
+ !headers["x-goog-resource-uri"] ||
172
+ !headers["x-goog-message-number"]
173
+ ) {
174
+ console.log("Request missing necessary headers: ", headers);
175
+ return;
176
+ }
177
+
178
+ const incomingChannelID = headers["x-goog-channel-id"];
179
+ if (incomingChannelID !== channelID) {
180
+ console.log(
181
+ `Channel ID of ${incomingChannelID} not equal to deployed component channel of ${channelID}`,
182
+ );
183
+ return;
184
+ }
185
+
186
+ if (subscriptions[headers["x-goog-resource-id"]] === undefined) {
187
+ console.log(
188
+ `Resource ID of ${headers["x-goog-resource-id"]} not currently being tracked. Exiting`,
189
+ );
190
+ return;
191
+ }
192
+
193
+ if (!includes(this.updateTypes, headers["x-goog-resource-state"])) {
194
+ console.log(
195
+ `Update type ${headers["x-goog-resource-state"]} not in list of updates to watch: `,
196
+ this.updateTypes,
197
+ );
198
+ return;
199
+ }
200
+
201
+ // We observed false positives where a single change to a document would trigger two changes:
202
+ // one to "properties" and another to "content,properties". But changes to properties
203
+ // alone are legitimate, most users just won't want this source to emit in those cases.
204
+ // If x-goog-changed is _only_ set to "properties", only move on if the user set the prop
205
+ if (
206
+ !this.watchForPropertiesChanges &&
207
+ headers["x-goog-changed"] === "properties"
208
+ ) {
209
+ console.log(
210
+ "Change to properties only, which this component is set to ignore. Exiting",
211
+ );
212
+ return;
213
+ }
214
+
215
+ const file = await this.googleDrive.getFileMetadata(
216
+ headers["x-goog-resource-uri"],
217
+ );
218
+
219
+ if (!file || !Object.keys(file).length) {
220
+ console.log("No file metadata returned, nothing to emit");
221
+ return;
222
+ }
223
+
224
+ this.processChange(file, headers);
225
+ },
226
+ };
@@ -0,0 +1,110 @@
1
+ // This source processes changes to specific 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 .
4
+ //
5
+ // This source has two interfaces:
6
+ //
7
+ // 1) The HTTP requests tied to changes in files in the user's Google Drive
8
+ // 2) A timer that runs on regular intervals, renewing the notification channel as needed
9
+
10
+ import common from "../common-webhook.mjs";
11
+
12
+ import {
13
+ GOOGLE_DRIVE_NOTIFICATION_CHANGE,
14
+ GOOGLE_DRIVE_NOTIFICATION_ADD,
15
+ GOOGLE_DRIVE_NOTIFICATION_UPDATE,
16
+ } from "../../constants.mjs";
17
+
18
+ /**
19
+ * This source uses the Google Drive API's
20
+ * {@link https://developers.google.com/drive/api/v3/reference/changes/watch changes: watch}
21
+ * endpoint to subscribe to changes to the user's drive or a shard drive.
22
+ */
23
+ export default {
24
+ ...common,
25
+ key: "google_drive-changes-to-specific-files-shared-drive",
26
+ name: "Changes to Specific Files (Shared Drive)",
27
+ description: "Watches for changes to specific files in a shared drive, emitting an event any time a change is made to one of those files",
28
+ version: "0.0.1",
29
+ type: "source",
30
+ // Dedupe events based on the "x-goog-message-number" header for the target channel:
31
+ // https://developers.google.com/drive/api/v3/push#making-watch-requests
32
+ dedupe: "unique",
33
+ props: {
34
+ ...common.props,
35
+ files: {
36
+ type: "string[]",
37
+ label: "Files",
38
+ description: "The files you want to watch for changes.",
39
+ optional: true,
40
+ default: [],
41
+ options({ prevContext }) {
42
+ const { nextPageToken } = prevContext;
43
+ const baseOpts = {};
44
+ const opts = this.isMyDrive()
45
+ ? baseOpts
46
+ : {
47
+ ...baseOpts,
48
+ corpora: "drive",
49
+ driveId: this.getDriveId(),
50
+ includeItemsFromAllDrives: true,
51
+ supportsAllDrives: true,
52
+ };
53
+ return this.googleDrive.listFilesOptions(nextPageToken, opts);
54
+ },
55
+ },
56
+ },
57
+ methods: {
58
+ ...common.methods,
59
+ getUpdateTypes() {
60
+ return [
61
+ GOOGLE_DRIVE_NOTIFICATION_ADD,
62
+ GOOGLE_DRIVE_NOTIFICATION_CHANGE,
63
+ GOOGLE_DRIVE_NOTIFICATION_UPDATE,
64
+ ];
65
+ },
66
+ generateMeta(data, headers) {
67
+ const {
68
+ id: fileId,
69
+ name: fileName,
70
+ modifiedTime: tsString,
71
+ } = data;
72
+ const {
73
+ "x-goog-message-number": eventId,
74
+ "x-goog-resource-state": resourceState,
75
+ } = headers;
76
+
77
+ return {
78
+ id: `${fileId}-${eventId}`,
79
+ summary: `${resourceState.toUpperCase()} - ${
80
+ fileName || "Untitled"
81
+ }`,
82
+ ts: Date.parse(tsString),
83
+ };
84
+ },
85
+ isFileRelevant(file) {
86
+ return this.files.includes(file.id);
87
+ },
88
+ async processChange(file, headers) {
89
+ const eventToEmit = {
90
+ file,
91
+ change: {
92
+ state: headers["x-goog-resource-state"],
93
+ resourceURI: headers["x-goog-resource-uri"],
94
+ changed: headers["x-goog-changed"], // "Additional details about the changes. Possible values: content, parents, children, permissions"
95
+ },
96
+ };
97
+ const meta = this.generateMeta(file, headers);
98
+ this.$emit(eventToEmit, meta);
99
+ },
100
+ async processChanges(changedFiles, headers) {
101
+ for (const file of changedFiles) {
102
+ if (!this.isFileRelevant(file)) {
103
+ console.log(`Skipping event for irrelevant file ${file.id}`);
104
+ continue;
105
+ }
106
+ this.processChange(file, headers);
107
+ }
108
+ },
109
+ },
110
+ };
@@ -0,0 +1,201 @@
1
+ import includes from "lodash/includes.js";
2
+ import { v4 as uuid } from "uuid";
3
+
4
+ import googleDrive from "../google_drive.app.mjs";
5
+ import { WEBHOOK_SUBSCRIPTION_RENEWAL_SECONDS } from "../constants.mjs";
6
+
7
+ export default {
8
+ props: {
9
+ googleDrive,
10
+ db: "$.service.db",
11
+ http: "$.interface.http",
12
+ drive: {
13
+ propDefinition: [
14
+ googleDrive,
15
+ "watchedDrive",
16
+ ],
17
+ description: "Defaults to My Drive. To select a [Shared Drive](https://support.google.com/a/users/answer/9310351) instead, select it from this list.",
18
+ optional: false,
19
+ },
20
+ watchForPropertiesChanges: {
21
+ propDefinition: [
22
+ googleDrive,
23
+ "watchForPropertiesChanges",
24
+ ],
25
+ },
26
+ timer: {
27
+ label: "Push notification renewal schedule",
28
+ description:
29
+ "The Google Drive API requires occasional renewal of push notification subscriptions. **This runs in the background, so you should not need to modify this schedule**.",
30
+ type: "$.interface.timer",
31
+ static: {
32
+ intervalSeconds: WEBHOOK_SUBSCRIPTION_RENEWAL_SECONDS,
33
+ },
34
+ },
35
+ },
36
+ hooks: {
37
+ async activate() {
38
+ // Called when a component is created or updated. Handles all the logic
39
+ // for starting and stopping watch notifications tied to the desired
40
+ // files.
41
+ const channelID = uuid();
42
+ const {
43
+ startPageToken,
44
+ expiration,
45
+ resourceId,
46
+ } = await this.googleDrive.activateHook(
47
+ channelID,
48
+ this.http.endpoint,
49
+ this.getDriveId(),
50
+ );
51
+
52
+ // We use and increment the pageToken as new changes arrive, in run()
53
+ this._setPageToken(startPageToken);
54
+
55
+ // Save metadata on the subscription so we can stop / renew later
56
+ // Subscriptions are tied to Google's resourceID, "an opaque value that
57
+ // identifies the watched resource". This value is included in request headers
58
+ this._setSubscription({
59
+ resourceId,
60
+ expiration,
61
+ });
62
+ this._setChannelID(channelID);
63
+ },
64
+ async deactivate() {
65
+ const channelID = this._getChannelID();
66
+ const { resourceId } = this._getSubscription();
67
+ await this.googleDrive.deactivateHook(channelID, resourceId);
68
+
69
+ this._setSubscription(null);
70
+ this._setChannelID(null);
71
+ this._setPageToken(null);
72
+ },
73
+ },
74
+ methods: {
75
+ _getSubscription() {
76
+ return this.db.get("subscription");
77
+ },
78
+ _setSubscription(subscription) {
79
+ this.db.set("subscription", subscription);
80
+ },
81
+ _getChannelID() {
82
+ return this.db.get("channelID");
83
+ },
84
+ _setChannelID(channelID) {
85
+ this.db.set("channelID", channelID);
86
+ },
87
+ _getPageToken() {
88
+ return this.db.get("pageToken");
89
+ },
90
+ _setPageToken(pageToken) {
91
+ this.db.set("pageToken", pageToken);
92
+ },
93
+ isMyDrive(drive = this.drive) {
94
+ return googleDrive.methods.isMyDrive(drive);
95
+ },
96
+ getDriveId(drive = this.drive) {
97
+ return googleDrive.methods.getDriveId(drive);
98
+ },
99
+ /**
100
+ * This method returns the types of updates/events from Google Drive that
101
+ * the event source should listen to. This base implementation returns an
102
+ * empty list, which means that any event source that extends this module
103
+ * and that does not refine this implementation will essentially ignore
104
+ * every incoming event from Google Drive.
105
+ *
106
+ * @returns
107
+ * @type {UpdateType[]}
108
+ */
109
+ getUpdateTypes() {
110
+ return [];
111
+ },
112
+ /**
113
+ * This method is responsible for processing a list of changed files
114
+ * according to the event source's purpose. As an abstract method, it must
115
+ * be implemented by every event source that extends this module.
116
+ *
117
+ * @param {object[]} [changedFiles] - the list of file changes, as [defined
118
+ * by the API](https://bit.ly/3h7WeUa)
119
+ * @param {object} [headers] - an object containing the request headers of
120
+ * the webhook call made by Google Drive
121
+ */
122
+ processChanges() {
123
+ throw new Error("processChanges is not implemented");
124
+ },
125
+ },
126
+ async run(event) {
127
+ // This function is polymorphic: it can be triggered as a cron job, to make
128
+ // sure we renew watch requests for specific files, or via HTTP request (the
129
+ // change payloads from Google)
130
+ const subscription = this._getSubscription();
131
+ const channelID = this._getChannelID();
132
+ const pageToken = this._getPageToken();
133
+
134
+ // Component was invoked by timer
135
+ if (event.timestamp) {
136
+ const {
137
+ newChannelID,
138
+ newPageToken,
139
+ expiration,
140
+ resourceId,
141
+ } = await this.googleDrive.renewSubscription(
142
+ this.drive,
143
+ subscription,
144
+ this.http.endpoint,
145
+ channelID,
146
+ pageToken,
147
+ );
148
+
149
+ this._setSubscription({
150
+ expiration,
151
+ resourceId,
152
+ });
153
+ this._setChannelID(newChannelID);
154
+ this._setPageToken(newPageToken);
155
+ return;
156
+ }
157
+
158
+ const { headers } = event;
159
+ if (!this.googleDrive.checkHeaders(headers, subscription, channelID)) {
160
+ return;
161
+ }
162
+
163
+ if (!includes(this.getUpdateTypes(), headers["x-goog-resource-state"])) {
164
+ console.log(
165
+ `Update type ${headers["x-goog-resource-state"]} not in list of updates to watch: `,
166
+ this.getUpdateTypes(),
167
+ );
168
+ return;
169
+ }
170
+
171
+ // We observed false positives where a single change to a document would trigger two changes:
172
+ // one to "properties" and another to "content,properties". But changes to properties
173
+ // alone are legitimate, most users just won't want this source to emit in those cases.
174
+ // If x-goog-changed is _only_ set to "properties", only move on if the user set the prop
175
+ if (
176
+ !this.watchForPropertiesChanges &&
177
+ headers["x-goog-changed"] === "properties"
178
+ ) {
179
+ console.log(
180
+ "Change to properties only, which this component is set to ignore. Exiting",
181
+ );
182
+ return;
183
+ }
184
+
185
+ const driveId = this.getDriveId();
186
+ const changedFilesStream = this.googleDrive.listChanges(pageToken, driveId);
187
+ for await (const changedFilesPage of changedFilesStream) {
188
+ const {
189
+ changedFiles,
190
+ nextPageToken,
191
+ } = changedFilesPage;
192
+
193
+ // Process all the changed files retrieved from the current page
194
+ await this.processChanges(changedFiles, headers);
195
+
196
+ // After successfully processing the changed files, we store the page
197
+ // token of the next page
198
+ this._setPageToken(nextPageToken);
199
+ }
200
+ },
201
+ };
@@ -0,0 +1,95 @@
1
+ import common from "../common-webhook.mjs";
2
+ import {
3
+ GOOGLE_DRIVE_NOTIFICATION_ADD,
4
+ GOOGLE_DRIVE_NOTIFICATION_CHANGE,
5
+ } from "../../constants.mjs";
6
+
7
+ export default {
8
+ ...common,
9
+ key: "google_drive-new-files-instant",
10
+ name: "New Files (Instant)",
11
+ description: "Emit new event any time a new file is added in your linked Google Drive",
12
+ version: "0.0.15",
13
+ type: "source",
14
+ dedupe: "unique",
15
+ props: {
16
+ ...common.props,
17
+ folders: {
18
+ type: "string[]",
19
+ label: "Folders",
20
+ description:
21
+ "(Optional) The folders you want to watch for changes. Leave blank to watch for any new file in the Drive.",
22
+ optional: true,
23
+ default: [],
24
+ options({ prevContext }) {
25
+ const { nextPageToken } = prevContext;
26
+ const baseOpts = {
27
+ q: "mimeType = 'application/vnd.google-apps.folder'",
28
+ };
29
+ const opts = this.isMyDrive()
30
+ ? baseOpts
31
+ : {
32
+ ...baseOpts,
33
+ corpora: "drive",
34
+ driveId: this.getDriveId(),
35
+ includeItemsFromAllDrives: true,
36
+ supportsAllDrives: true,
37
+ };
38
+ return this.googleDrive.listFilesOptions(nextPageToken, opts);
39
+ },
40
+ },
41
+ },
42
+ hooks: {
43
+ ...common.hooks,
44
+ async activate() {
45
+ await common.hooks.activate.bind(this)();
46
+ this._setLastFileCreatedTime(Date.now());
47
+ },
48
+ },
49
+ methods: {
50
+ ...common.methods,
51
+ _getLastFileCreatedTime() {
52
+ return this.db.get("lastFileCreatedTime");
53
+ },
54
+ _setLastFileCreatedTime(lastFileCreatedTime) {
55
+ this.db.set("lastFileCreatedTime", lastFileCreatedTime);
56
+ },
57
+ shouldProcess(file) {
58
+ const watchedFolders = new Set(this.folders);
59
+ return (
60
+ watchedFolders.size == 0 ||
61
+ (file.parents && file.parents.some((p) => watchedFolders.has(p)))
62
+ );
63
+ },
64
+ getUpdateTypes() {
65
+ return [
66
+ GOOGLE_DRIVE_NOTIFICATION_ADD,
67
+ GOOGLE_DRIVE_NOTIFICATION_CHANGE,
68
+ ];
69
+ },
70
+ async processChanges(changedFiles) {
71
+ const lastFileCreatedTime = this._getLastFileCreatedTime();
72
+ let maxCreatedTime = lastFileCreatedTime;
73
+
74
+ for (const file of changedFiles) {
75
+ const fileInfo = await this.googleDrive.getFile(file.id);
76
+ const createdTime = Date.parse(fileInfo.createdTime);
77
+ if (
78
+ !this.shouldProcess(fileInfo) ||
79
+ createdTime < lastFileCreatedTime
80
+ ) {
81
+ continue;
82
+ }
83
+
84
+ this.$emit(fileInfo, {
85
+ summary: `New File: ${fileInfo.name}`,
86
+ id: file.id,
87
+ ts: createdTime,
88
+ });
89
+
90
+ maxCreatedTime = Math.max(createdTime, maxCreatedTime);
91
+ this._setLastFileCreatedTime(maxCreatedTime);
92
+ }
93
+ },
94
+ },
95
+ };