@pipedream/google_drive 1.3.1 → 1.4.0
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/package.json +1 -1
- package/sources/new-files-instant-polling/new-files-instant-polling.mjs +190 -0
- package/sources/new-files-instant-polling/test-event.mjs +99 -0
- package/sources/new-or-modified-comments-polling/new-or-modified-comments-polling.mjs +112 -0
- package/sources/new-or-modified-comments-polling/test-event.mjs +35 -0
- package/sources/new-or-modified-files-polling/new-or-modified-files-polling.mjs +19 -24
- package/sources/new-or-modified-folders-polling/new-or-modified-folders-polling.mjs +224 -0
- package/sources/new-or-modified-folders-polling/test-event.mjs +51 -0
- package/sources/new-spreadsheet-polling/new-spreadsheet-polling.mjs +188 -0
- package/sources/new-spreadsheet-polling/test-event.mjs +111 -0
package/package.json
CHANGED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
|
|
2
|
+
import googleDrive from "../../google_drive.app.mjs";
|
|
3
|
+
import { getListFilesOpts } from "../../common/utils.mjs";
|
|
4
|
+
import { GOOGLE_DRIVE_FOLDER_MIME_TYPE } from "../../common/constants.mjs";
|
|
5
|
+
import sampleEmit from "./test-event.mjs";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
key: "google_drive-new-files-instant-polling",
|
|
9
|
+
name: "New Files (Polling)",
|
|
10
|
+
description: "Emit new event when a new file is added in your linked Google Drive",
|
|
11
|
+
version: "0.0.1",
|
|
12
|
+
type: "source",
|
|
13
|
+
dedupe: "unique",
|
|
14
|
+
props: {
|
|
15
|
+
googleDrive,
|
|
16
|
+
db: "$.service.db",
|
|
17
|
+
timer: {
|
|
18
|
+
type: "$.interface.timer",
|
|
19
|
+
default: {
|
|
20
|
+
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
drive: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
googleDrive,
|
|
26
|
+
"watchedDrive",
|
|
27
|
+
],
|
|
28
|
+
description: "Defaults to My Drive. To select a [Shared Drive](https://support.google.com/a/users/answer/9310351) instead, select it from this list.",
|
|
29
|
+
optional: false,
|
|
30
|
+
},
|
|
31
|
+
folders: {
|
|
32
|
+
propDefinition: [
|
|
33
|
+
googleDrive,
|
|
34
|
+
"folderId",
|
|
35
|
+
({ drive }) => ({
|
|
36
|
+
drive,
|
|
37
|
+
baseOpts: {
|
|
38
|
+
q: `mimeType = '${GOOGLE_DRIVE_FOLDER_MIME_TYPE}' and trashed = false`,
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
],
|
|
42
|
+
type: "string[]",
|
|
43
|
+
label: "Folders",
|
|
44
|
+
description: "The specific folder(s) to watch for new files. Leave blank to watch all files in the Drive.",
|
|
45
|
+
optional: true,
|
|
46
|
+
default: [],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
hooks: {
|
|
50
|
+
async deploy() {
|
|
51
|
+
// Get initial page token for change tracking
|
|
52
|
+
const driveId = this.getDriveId();
|
|
53
|
+
const startPageToken = await this.googleDrive.getPageToken(driveId);
|
|
54
|
+
this._setPageToken(startPageToken);
|
|
55
|
+
|
|
56
|
+
this._setLastRunTimestamp(Date.now());
|
|
57
|
+
|
|
58
|
+
// Emit sample files from the last 30 days
|
|
59
|
+
const daysAgo = new Date();
|
|
60
|
+
daysAgo.setDate(daysAgo.getDate() - 30);
|
|
61
|
+
const timeString = daysAgo.toISOString();
|
|
62
|
+
|
|
63
|
+
const args = this.getListFilesOpts({
|
|
64
|
+
q: `mimeType != "application/vnd.google-apps.folder" and createdTime > "${timeString}" and trashed = false`,
|
|
65
|
+
orderBy: "createdTime desc",
|
|
66
|
+
fields: "*",
|
|
67
|
+
pageSize: 5,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const { files } = await this.googleDrive.listFilesInPage(null, args);
|
|
71
|
+
|
|
72
|
+
for (const file of files) {
|
|
73
|
+
if (this.shouldProcess(file)) {
|
|
74
|
+
await this.emitFile(file);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
methods: {
|
|
80
|
+
_getPageToken() {
|
|
81
|
+
return this.db.get("pageToken");
|
|
82
|
+
},
|
|
83
|
+
_setPageToken(pageToken) {
|
|
84
|
+
this.db.set("pageToken", pageToken);
|
|
85
|
+
},
|
|
86
|
+
_getLastRunTimestamp() {
|
|
87
|
+
return this.db.get("lastRunTimestamp");
|
|
88
|
+
},
|
|
89
|
+
_setLastRunTimestamp(timestamp) {
|
|
90
|
+
this.db.set("lastRunTimestamp", timestamp);
|
|
91
|
+
},
|
|
92
|
+
getDriveId(drive = this.drive) {
|
|
93
|
+
return this.googleDrive.getDriveId(drive);
|
|
94
|
+
},
|
|
95
|
+
getListFilesOpts(args = {}) {
|
|
96
|
+
return getListFilesOpts(this.drive, {
|
|
97
|
+
q: "mimeType != 'application/vnd.google-apps.folder' and trashed = false",
|
|
98
|
+
...args,
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
shouldProcess(file) {
|
|
102
|
+
// Skip folders
|
|
103
|
+
if (file.mimeType === GOOGLE_DRIVE_FOLDER_MIME_TYPE) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check if specific folders are being watched
|
|
108
|
+
if (this.folders?.length > 0) {
|
|
109
|
+
const watchedFolders = new Set(this.folders);
|
|
110
|
+
if (!file.parents || !file.parents.some((p) => watchedFolders.has(p))) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return true;
|
|
116
|
+
},
|
|
117
|
+
generateMeta(file) {
|
|
118
|
+
const {
|
|
119
|
+
id: fileId,
|
|
120
|
+
name: summary,
|
|
121
|
+
createdTime: tsString,
|
|
122
|
+
} = file;
|
|
123
|
+
const ts = Date.parse(tsString);
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
id: `${fileId}-${ts}`,
|
|
127
|
+
summary,
|
|
128
|
+
ts,
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
async emitFile(file) {
|
|
132
|
+
const meta = this.generateMeta(file);
|
|
133
|
+
this.$emit(file, meta);
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
async run() {
|
|
137
|
+
const currentRunTimestamp = Date.now();
|
|
138
|
+
const lastRunTimestamp = this._getLastRunTimestamp();
|
|
139
|
+
|
|
140
|
+
const pageToken = this._getPageToken();
|
|
141
|
+
const driveId = this.getDriveId();
|
|
142
|
+
|
|
143
|
+
const changedFilesStream = this.googleDrive.listChanges(pageToken, driveId);
|
|
144
|
+
|
|
145
|
+
for await (const changedFilesPage of changedFilesStream) {
|
|
146
|
+
console.log("Changed files page:", changedFilesPage);
|
|
147
|
+
const {
|
|
148
|
+
changedFiles,
|
|
149
|
+
nextPageToken,
|
|
150
|
+
} = changedFilesPage;
|
|
151
|
+
|
|
152
|
+
console.log(changedFiles.length
|
|
153
|
+
? `Processing ${changedFiles.length} changed files`
|
|
154
|
+
: "No changed files since last run");
|
|
155
|
+
|
|
156
|
+
for (const file of changedFiles) {
|
|
157
|
+
// Skip folders
|
|
158
|
+
if (file.mimeType === GOOGLE_DRIVE_FOLDER_MIME_TYPE) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Get full file metadata including parents
|
|
163
|
+
const fullFile = await this.googleDrive.getFile(file.id, {
|
|
164
|
+
fields: "*",
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Check if it's a new file (created after last run)
|
|
168
|
+
const fileCreatedTime = Date.parse(fullFile.createdTime);
|
|
169
|
+
if (fileCreatedTime <= lastRunTimestamp) {
|
|
170
|
+
console.log(`Skipping existing file ${fullFile.name || fullFile.id}`);
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!this.shouldProcess(fullFile)) {
|
|
175
|
+
console.log(`Skipping file ${fullFile.name || fullFile.id}`);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await this.emitFile(fullFile);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Save the next page token after successfully processing
|
|
183
|
+
this._setPageToken(nextPageToken);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Update the last run timestamp after processing all changes
|
|
187
|
+
this._setLastRunTimestamp(currentRunTimestamp);
|
|
188
|
+
},
|
|
189
|
+
sampleEmit,
|
|
190
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
"kind": "drive#file",
|
|
3
|
+
"copyRequiresWriterPermission": false,
|
|
4
|
+
"writersCanShare": true,
|
|
5
|
+
"viewedByMe": true,
|
|
6
|
+
"mimeType": "text/plain",
|
|
7
|
+
"parents": [
|
|
8
|
+
"0ANs73yKKVA"
|
|
9
|
+
],
|
|
10
|
+
"iconLink": "https://drive-thirdparty.googleusercontent.com/16/type/text/plain",
|
|
11
|
+
"shared": false,
|
|
12
|
+
"lastModifyingUser": {
|
|
13
|
+
"displayName": "John Doe",
|
|
14
|
+
"kind": "drive#user",
|
|
15
|
+
"me": true,
|
|
16
|
+
"permissionId": "077423361841532483",
|
|
17
|
+
"emailAddress": "john@doe.com",
|
|
18
|
+
"photoLink": "https://lh3.googleusercontent.com/a/default-user=s64"
|
|
19
|
+
},
|
|
20
|
+
"owners": [
|
|
21
|
+
{
|
|
22
|
+
"displayName": "John Doe",
|
|
23
|
+
"kind": "drive#user",
|
|
24
|
+
"me": true,
|
|
25
|
+
"permissionId": "07742336189483",
|
|
26
|
+
"emailAddress": "john@doe.com",
|
|
27
|
+
"photoLink": "https://lh3.googleusercontent.com/a/default-user=s64"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"webViewLink": "https://drive.google.com/file/d/1LmXTIQ0wqKP7T3-l9r127Maw4Pt2BViTOo/view?usp=drivesdk",
|
|
31
|
+
"size": "1024",
|
|
32
|
+
"viewersCanCopyContent": true,
|
|
33
|
+
"permissions": [
|
|
34
|
+
{
|
|
35
|
+
"id": "07742336184153259483",
|
|
36
|
+
"displayName": "John Doe",
|
|
37
|
+
"type": "user",
|
|
38
|
+
"kind": "drive#permission",
|
|
39
|
+
"photoLink": "https://lh3.googleusercontent.com/a/default-user=s64",
|
|
40
|
+
"emailAddress": "john@doe.com",
|
|
41
|
+
"role": "owner",
|
|
42
|
+
"deleted": false,
|
|
43
|
+
"pendingOwner": false
|
|
44
|
+
}
|
|
45
|
+
],
|
|
46
|
+
"spaces": [
|
|
47
|
+
"drive"
|
|
48
|
+
],
|
|
49
|
+
"id": "1LmXTIQ0wqKP7T3-l9r127Maw4Pt2BViTOo",
|
|
50
|
+
"name": "New File.txt",
|
|
51
|
+
"starred": false,
|
|
52
|
+
"trashed": false,
|
|
53
|
+
"explicitlyTrashed": false,
|
|
54
|
+
"createdTime": "2023-06-28T12:52:54.570Z",
|
|
55
|
+
"modifiedTime": "2023-06-28T12:52:54.570Z",
|
|
56
|
+
"modifiedByMeTime": "2023-06-28T12:52:54.570Z",
|
|
57
|
+
"viewedByMeTime": "2023-06-29T06:30:58.441Z",
|
|
58
|
+
"quotaBytesUsed": "1024",
|
|
59
|
+
"version": "1",
|
|
60
|
+
"ownedByMe": true,
|
|
61
|
+
"isAppAuthorized": false,
|
|
62
|
+
"capabilities": {
|
|
63
|
+
"canChangeViewersCanCopyContent": true,
|
|
64
|
+
"canEdit": true,
|
|
65
|
+
"canCopy": true,
|
|
66
|
+
"canComment": true,
|
|
67
|
+
"canAddChildren": false,
|
|
68
|
+
"canDelete": true,
|
|
69
|
+
"canDownload": true,
|
|
70
|
+
"canListChildren": false,
|
|
71
|
+
"canRemoveChildren": false,
|
|
72
|
+
"canRename": true,
|
|
73
|
+
"canTrash": true,
|
|
74
|
+
"canReadRevisions": true,
|
|
75
|
+
"canChangeCopyRequiresWriterPermission": true,
|
|
76
|
+
"canMoveItemIntoTeamDrive": true,
|
|
77
|
+
"canUntrash": true,
|
|
78
|
+
"canModifyContent": true,
|
|
79
|
+
"canMoveItemOutOfDrive": true,
|
|
80
|
+
"canAddMyDriveParent": false,
|
|
81
|
+
"canRemoveMyDriveParent": true,
|
|
82
|
+
"canMoveItemWithinDrive": true,
|
|
83
|
+
"canShare": true,
|
|
84
|
+
"canMoveChildrenWithinDrive": false,
|
|
85
|
+
"canModifyContentRestriction": true,
|
|
86
|
+
"canChangeSecurityUpdateEnabled": false,
|
|
87
|
+
"canAcceptOwnership": false,
|
|
88
|
+
"canReadLabels": true,
|
|
89
|
+
"canModifyLabels": true
|
|
90
|
+
},
|
|
91
|
+
"modifiedByMe": true,
|
|
92
|
+
"permissionIds": [
|
|
93
|
+
"07742336184153259483"
|
|
94
|
+
],
|
|
95
|
+
"linkShareMetadata": {
|
|
96
|
+
"securityUpdateEligible": false,
|
|
97
|
+
"securityUpdateEnabled": true
|
|
98
|
+
}
|
|
99
|
+
};
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
|
|
2
|
+
import googleDrive from "../../google_drive.app.mjs";
|
|
3
|
+
import sampleEmit from "./test-event.mjs";
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
key: "google_drive-new-or-modified-comments-polling",
|
|
7
|
+
name: "New or Modified Comments (Polling)",
|
|
8
|
+
description: "Emit new event when a comment is created or modified in the selected file",
|
|
9
|
+
version: "0.0.1",
|
|
10
|
+
type: "source",
|
|
11
|
+
dedupe: "unique",
|
|
12
|
+
props: {
|
|
13
|
+
googleDrive,
|
|
14
|
+
db: "$.service.db",
|
|
15
|
+
timer: {
|
|
16
|
+
type: "$.interface.timer",
|
|
17
|
+
default: {
|
|
18
|
+
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
drive: {
|
|
22
|
+
propDefinition: [
|
|
23
|
+
googleDrive,
|
|
24
|
+
"watchedDrive",
|
|
25
|
+
],
|
|
26
|
+
description: "Defaults to My Drive. To select a [Shared Drive](https://support.google.com/a/users/answer/9310351) instead, select it from this list.",
|
|
27
|
+
optional: false,
|
|
28
|
+
},
|
|
29
|
+
fileId: {
|
|
30
|
+
propDefinition: [
|
|
31
|
+
googleDrive,
|
|
32
|
+
"fileId",
|
|
33
|
+
({ drive }) => ({
|
|
34
|
+
drive,
|
|
35
|
+
}),
|
|
36
|
+
],
|
|
37
|
+
description: "The file to watch for comments",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
hooks: {
|
|
41
|
+
async deploy() {
|
|
42
|
+
// Set the init time to now
|
|
43
|
+
this._setInitTime(Date.now());
|
|
44
|
+
|
|
45
|
+
// Emit sample comments from the file
|
|
46
|
+
const commentsStream = this.googleDrive.listComments(this.fileId, null);
|
|
47
|
+
let count = 0;
|
|
48
|
+
|
|
49
|
+
for await (const comment of commentsStream) {
|
|
50
|
+
if (count >= 5) break; // Limit to 5 sample comments
|
|
51
|
+
await this.emitComment(comment);
|
|
52
|
+
count++;
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
methods: {
|
|
57
|
+
_getInitTime() {
|
|
58
|
+
return this.db.get("initTime");
|
|
59
|
+
},
|
|
60
|
+
_setInitTime(initTime) {
|
|
61
|
+
this.db.set("initTime", initTime);
|
|
62
|
+
},
|
|
63
|
+
_getLastCommentTime() {
|
|
64
|
+
return this.db.get("lastCommentTime") || this._getInitTime();
|
|
65
|
+
},
|
|
66
|
+
_setLastCommentTime(commentTime) {
|
|
67
|
+
this.db.set("lastCommentTime", commentTime);
|
|
68
|
+
},
|
|
69
|
+
generateMeta(comment) {
|
|
70
|
+
const {
|
|
71
|
+
id: commentId,
|
|
72
|
+
content: summary,
|
|
73
|
+
modifiedTime: tsString,
|
|
74
|
+
} = comment;
|
|
75
|
+
const ts = Date.parse(tsString);
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
id: `${commentId}-${ts}`,
|
|
79
|
+
summary,
|
|
80
|
+
ts,
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
async emitComment(comment) {
|
|
84
|
+
const meta = this.generateMeta(comment);
|
|
85
|
+
this.$emit(comment, meta);
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
async run() {
|
|
89
|
+
const lastCommentTime = this._getLastCommentTime();
|
|
90
|
+
let maxCommentTime = lastCommentTime;
|
|
91
|
+
|
|
92
|
+
const commentsStream = this.googleDrive.listComments(
|
|
93
|
+
this.fileId,
|
|
94
|
+
lastCommentTime,
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
for await (const comment of commentsStream) {
|
|
98
|
+
const commentTime = Date.parse(comment.modifiedTime);
|
|
99
|
+
if (commentTime <= lastCommentTime) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
await this.emitComment(comment);
|
|
104
|
+
|
|
105
|
+
maxCommentTime = Math.max(maxCommentTime, commentTime);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Update the last comment time after processing all comments
|
|
109
|
+
this._setLastCommentTime(maxCommentTime);
|
|
110
|
+
},
|
|
111
|
+
sampleEmit,
|
|
112
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
"kind": "drive#comment",
|
|
3
|
+
"id": "AAAAAvqIXEU",
|
|
4
|
+
"createdTime": "2023-02-06T15:13:33.023Z",
|
|
5
|
+
"modifiedTime": "2023-06-28T12:52:54.570Z",
|
|
6
|
+
"author": {
|
|
7
|
+
"displayName": "John Doe",
|
|
8
|
+
"kind": "drive#user",
|
|
9
|
+
"me": true,
|
|
10
|
+
"permissionId": "077423361841532483",
|
|
11
|
+
"emailAddress": "john@doe.com",
|
|
12
|
+
"photoLink": "https://lh3.googleusercontent.com/a/default-user=s64"
|
|
13
|
+
},
|
|
14
|
+
"htmlContent": "<a href=\"mailto:john@doe.com\">John Doe</a> commented:",
|
|
15
|
+
"content": "This is a sample comment",
|
|
16
|
+
"deleted": false,
|
|
17
|
+
"replies": [
|
|
18
|
+
{
|
|
19
|
+
"kind": "drive#reply",
|
|
20
|
+
"id": "AAAAAvqIXEU/replies/AEnB2UqIXEU",
|
|
21
|
+
"createdTime": "2023-02-06T15:13:33.023Z",
|
|
22
|
+
"modifiedTime": "2023-06-28T12:52:54.570Z",
|
|
23
|
+
"author": {
|
|
24
|
+
"displayName": "Jane Doe",
|
|
25
|
+
"kind": "drive#user",
|
|
26
|
+
"permissionId": "077423361841532483",
|
|
27
|
+
"emailAddress": "jane@doe.com",
|
|
28
|
+
"photoLink": "https://lh3.googleusercontent.com/a/default-user=s64"
|
|
29
|
+
},
|
|
30
|
+
"htmlContent": "<a href=\"mailto:jane@doe.com\">Jane Doe</a> replied:",
|
|
31
|
+
"content": "This is a reply to the comment",
|
|
32
|
+
"deleted": false
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
};
|
|
@@ -8,7 +8,7 @@ export default {
|
|
|
8
8
|
key: "google_drive-new-or-modified-files-polling",
|
|
9
9
|
name: "New or Modified Files (Polling)",
|
|
10
10
|
description: "Emit new event when a file in the selected Drive is created, modified or trashed. [See the documentation](https://developers.google.com/drive/api/v3/reference/changes/list)",
|
|
11
|
-
version: "0.0.
|
|
11
|
+
version: "0.0.2",
|
|
12
12
|
type: "source",
|
|
13
13
|
dedupe: "unique",
|
|
14
14
|
props: {
|
|
@@ -29,39 +29,34 @@ export default {
|
|
|
29
29
|
optional: false,
|
|
30
30
|
},
|
|
31
31
|
files: {
|
|
32
|
+
propDefinition: [
|
|
33
|
+
googleDrive,
|
|
34
|
+
"fileId",
|
|
35
|
+
({ drive }) => ({
|
|
36
|
+
drive,
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
32
39
|
type: "string[]",
|
|
33
40
|
label: "Files",
|
|
34
|
-
description: "The specific file(s)
|
|
41
|
+
description: "The specific file(s) to watch for changes. Leave blank to watch all files in the Drive. Note that events will only be emitted for files directly in these folders (not in their subfolders, unless also selected here)",
|
|
35
42
|
optional: true,
|
|
36
43
|
default: [],
|
|
37
|
-
options({ prevContext }) {
|
|
38
|
-
const { nextPageToken } = prevContext;
|
|
39
|
-
return this.googleDrive.listFilesOptions(nextPageToken, this.getListFilesOpts());
|
|
40
|
-
},
|
|
41
44
|
},
|
|
42
45
|
folders: {
|
|
46
|
+
propDefinition: [
|
|
47
|
+
googleDrive,
|
|
48
|
+
"folderId",
|
|
49
|
+
({ drive }) => ({
|
|
50
|
+
drive,
|
|
51
|
+
baseOpts: {
|
|
52
|
+
q: `mimeType = '${GOOGLE_DRIVE_FOLDER_MIME_TYPE}' and trashed = false`,
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
],
|
|
43
56
|
type: "string[]",
|
|
44
57
|
label: "Folders",
|
|
45
58
|
description: "The specific folder(s) to watch for changes. Leave blank to watch all folders in the Drive.",
|
|
46
59
|
optional: true,
|
|
47
|
-
default: [],
|
|
48
|
-
options({ prevContext }) {
|
|
49
|
-
const { nextPageToken } = prevContext;
|
|
50
|
-
const baseOpts = {
|
|
51
|
-
q: "mimeType = 'application/vnd.google-apps.folder' and trashed = false",
|
|
52
|
-
};
|
|
53
|
-
const isMyDrive = this.googleDrive.isMyDrive(this.drive);
|
|
54
|
-
const opts = isMyDrive
|
|
55
|
-
? baseOpts
|
|
56
|
-
: {
|
|
57
|
-
...baseOpts,
|
|
58
|
-
corpora: "drive",
|
|
59
|
-
driveId: this.getDriveId(),
|
|
60
|
-
includeItemsFromAllDrives: true,
|
|
61
|
-
supportsAllDrives: true,
|
|
62
|
-
};
|
|
63
|
-
return this.googleDrive.listFilesOptions(nextPageToken, opts);
|
|
64
|
-
},
|
|
65
60
|
},
|
|
66
61
|
newFilesOnly: {
|
|
67
62
|
type: "boolean",
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
|
|
2
|
+
import googleDrive from "../../google_drive.app.mjs";
|
|
3
|
+
import { getListFilesOpts } from "../../common/utils.mjs";
|
|
4
|
+
import { GOOGLE_DRIVE_FOLDER_MIME_TYPE } from "../../common/constants.mjs";
|
|
5
|
+
import sampleEmit from "./test-event.mjs";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
key: "google_drive-new-or-modified-folders-polling",
|
|
9
|
+
name: "New or Modified Folders (Polling)",
|
|
10
|
+
description: "Emit new event when a folder is created or modified in the selected Drive",
|
|
11
|
+
version: "0.0.1",
|
|
12
|
+
type: "source",
|
|
13
|
+
dedupe: "unique",
|
|
14
|
+
props: {
|
|
15
|
+
googleDrive,
|
|
16
|
+
db: "$.service.db",
|
|
17
|
+
timer: {
|
|
18
|
+
type: "$.interface.timer",
|
|
19
|
+
default: {
|
|
20
|
+
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
drive: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
googleDrive,
|
|
26
|
+
"watchedDrive",
|
|
27
|
+
],
|
|
28
|
+
description: "Defaults to My Drive. To select a [Shared Drive](https://support.google.com/a/users/answer/9310351) instead, select it from this list.",
|
|
29
|
+
optional: false,
|
|
30
|
+
},
|
|
31
|
+
folderId: {
|
|
32
|
+
propDefinition: [
|
|
33
|
+
googleDrive,
|
|
34
|
+
"folderId",
|
|
35
|
+
({ drive }) => ({
|
|
36
|
+
drive,
|
|
37
|
+
}),
|
|
38
|
+
],
|
|
39
|
+
label: "Parent Folder",
|
|
40
|
+
description: "The ID of the parent folder which contains the folders. If not specified, it will watch all folders from the drive's top-level folder.",
|
|
41
|
+
optional: true,
|
|
42
|
+
},
|
|
43
|
+
includeSubfolders: {
|
|
44
|
+
type: "boolean",
|
|
45
|
+
label: "Include Subfolders",
|
|
46
|
+
description: "Whether to include subfolders of the parent folder in the changes.",
|
|
47
|
+
optional: true,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
hooks: {
|
|
51
|
+
async deploy() {
|
|
52
|
+
// Get initial page token for change tracking
|
|
53
|
+
const driveId = this.getDriveId();
|
|
54
|
+
const startPageToken = await this.googleDrive.getPageToken(driveId);
|
|
55
|
+
this._setPageToken(startPageToken);
|
|
56
|
+
|
|
57
|
+
this._setLastRunTimestamp(Date.now());
|
|
58
|
+
|
|
59
|
+
// Emit sample folders from the last 30 days
|
|
60
|
+
const daysAgo = new Date();
|
|
61
|
+
daysAgo.setDate(daysAgo.getDate() - 30);
|
|
62
|
+
const timeString = daysAgo.toISOString();
|
|
63
|
+
|
|
64
|
+
const args = this.getListFilesOpts({
|
|
65
|
+
q: `mimeType = "${GOOGLE_DRIVE_FOLDER_MIME_TYPE}" and modifiedTime > "${timeString}" and trashed = false`,
|
|
66
|
+
fields: "*",
|
|
67
|
+
pageSize: 5,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const { files } = await this.googleDrive.listFilesInPage(null, args);
|
|
71
|
+
|
|
72
|
+
for (const file of files) {
|
|
73
|
+
if (await this.shouldProcess(file)) {
|
|
74
|
+
await this.emitFolder(file);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
methods: {
|
|
80
|
+
_getPageToken() {
|
|
81
|
+
return this.db.get("pageToken");
|
|
82
|
+
},
|
|
83
|
+
_setPageToken(pageToken) {
|
|
84
|
+
this.db.set("pageToken", pageToken);
|
|
85
|
+
},
|
|
86
|
+
_getLastRunTimestamp() {
|
|
87
|
+
return this.db.get("lastRunTimestamp");
|
|
88
|
+
},
|
|
89
|
+
_setLastRunTimestamp(timestamp) {
|
|
90
|
+
this.db.set("lastRunTimestamp", timestamp);
|
|
91
|
+
},
|
|
92
|
+
_getLastModifiedTimeForFile(fileId) {
|
|
93
|
+
return this.db.get(fileId);
|
|
94
|
+
},
|
|
95
|
+
_setModifiedTimeForFile(fileId, modifiedTime) {
|
|
96
|
+
this.db.set(fileId, modifiedTime);
|
|
97
|
+
},
|
|
98
|
+
getDriveId(drive = this.drive) {
|
|
99
|
+
return this.googleDrive.getDriveId(drive);
|
|
100
|
+
},
|
|
101
|
+
getListFilesOpts(args = {}) {
|
|
102
|
+
return getListFilesOpts(this.drive, {
|
|
103
|
+
...args,
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
async getAllParents(folderId) {
|
|
107
|
+
const allParents = [];
|
|
108
|
+
let currentId = folderId;
|
|
109
|
+
|
|
110
|
+
while (currentId) {
|
|
111
|
+
const folder = await this.googleDrive.getFile(currentId, {
|
|
112
|
+
fields: "parents",
|
|
113
|
+
});
|
|
114
|
+
const parents = folder.parents;
|
|
115
|
+
|
|
116
|
+
if (parents && parents.length > 0) {
|
|
117
|
+
allParents.push(parents[0]);
|
|
118
|
+
}
|
|
119
|
+
currentId = parents?.[0];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return allParents;
|
|
123
|
+
},
|
|
124
|
+
async shouldProcess(file) {
|
|
125
|
+
// Skip if not a folder
|
|
126
|
+
if (file.mimeType !== GOOGLE_DRIVE_FOLDER_MIME_TYPE) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// If no parent folder specified, process all folders
|
|
131
|
+
if (!this.folderId) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const root = await this.googleDrive.getFile(this.drive === "My Drive"
|
|
136
|
+
? "root"
|
|
137
|
+
: this.drive);
|
|
138
|
+
|
|
139
|
+
const allParents = [];
|
|
140
|
+
if (this.includeSubfolders) {
|
|
141
|
+
allParents.push(...(await this.getAllParents(file.id)));
|
|
142
|
+
} else if (file.parents) {
|
|
143
|
+
allParents.push(file.parents[0]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return allParents.includes(this.folderId || root.id);
|
|
147
|
+
},
|
|
148
|
+
generateMeta(file) {
|
|
149
|
+
const {
|
|
150
|
+
id: fileId,
|
|
151
|
+
name: summary,
|
|
152
|
+
modifiedTime: tsString,
|
|
153
|
+
} = file;
|
|
154
|
+
const ts = Date.parse(tsString);
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
id: `${fileId}-${ts}`,
|
|
158
|
+
summary,
|
|
159
|
+
ts,
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
async emitFolder(file) {
|
|
163
|
+
const meta = this.generateMeta(file);
|
|
164
|
+
this.$emit(file, meta);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
async run() {
|
|
168
|
+
const currentRunTimestamp = Date.now();
|
|
169
|
+
|
|
170
|
+
const pageToken = this._getPageToken();
|
|
171
|
+
const driveId = this.getDriveId();
|
|
172
|
+
|
|
173
|
+
const changedFilesStream = this.googleDrive.listChanges(pageToken, driveId);
|
|
174
|
+
|
|
175
|
+
for await (const changedFilesPage of changedFilesStream) {
|
|
176
|
+
console.log("Changed files page:", changedFilesPage);
|
|
177
|
+
const {
|
|
178
|
+
changedFiles,
|
|
179
|
+
nextPageToken,
|
|
180
|
+
} = changedFilesPage;
|
|
181
|
+
|
|
182
|
+
console.log(changedFiles.length
|
|
183
|
+
? `Processing ${changedFiles.length} changed files`
|
|
184
|
+
: "No changed files since last run");
|
|
185
|
+
|
|
186
|
+
for (const file of changedFiles) {
|
|
187
|
+
// Skip if not a folder
|
|
188
|
+
if (file.mimeType !== GOOGLE_DRIVE_FOLDER_MIME_TYPE) {
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Get full file metadata
|
|
193
|
+
const fullFile = await this.googleDrive.getFile(file.id, {
|
|
194
|
+
fields: "*",
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const modifiedTime = Date.parse(fullFile.modifiedTime);
|
|
198
|
+
const lastModifiedTimeForFile = this._getLastModifiedTimeForFile(fullFile.id);
|
|
199
|
+
|
|
200
|
+
// Skip if not modified since last check
|
|
201
|
+
if (lastModifiedTimeForFile === modifiedTime) {
|
|
202
|
+
console.log(`Skipping unmodified folder ${fullFile.name || fullFile.id}`);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (!await this.shouldProcess(fullFile)) {
|
|
207
|
+
console.log(`Skipping folder ${fullFile.name || fullFile.id}`);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
await this.emitFolder(fullFile);
|
|
212
|
+
|
|
213
|
+
this._setModifiedTimeForFile(fullFile.id, modifiedTime);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Save the next page token after successfully processing
|
|
217
|
+
this._setPageToken(nextPageToken);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Update the last run timestamp after processing all changes
|
|
221
|
+
this._setLastRunTimestamp(currentRunTimestamp);
|
|
222
|
+
},
|
|
223
|
+
sampleEmit,
|
|
224
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
"kind": "drive#file",
|
|
3
|
+
"id": "1F1Q9K7L8N2P3R4S5T6U7V8W9X0Y1Z2A3",
|
|
4
|
+
"name": "New Folder",
|
|
5
|
+
"mimeType": "application/vnd.google-apps.folder",
|
|
6
|
+
"parents": [
|
|
7
|
+
"0ANs73yKKVA"
|
|
8
|
+
],
|
|
9
|
+
"createdTime": "2023-06-28T12:52:54.570Z",
|
|
10
|
+
"modifiedTime": "2023-06-28T12:52:54.570Z",
|
|
11
|
+
"modifiedByMeTime": "2023-06-28T12:52:54.570Z",
|
|
12
|
+
"viewedByMeTime": "2023-06-29T06:30:58.441Z",
|
|
13
|
+
"shared": false,
|
|
14
|
+
"ownedByMe": true,
|
|
15
|
+
"trashed": false,
|
|
16
|
+
"explicitlyTrashed": false,
|
|
17
|
+
"owners": [
|
|
18
|
+
{
|
|
19
|
+
"displayName": "John Doe",
|
|
20
|
+
"kind": "drive#user",
|
|
21
|
+
"me": true,
|
|
22
|
+
"permissionId": "07742336189483",
|
|
23
|
+
"emailAddress": "john@doe.com",
|
|
24
|
+
"photoLink": "https://lh3.googleusercontent.com/a/default-user=s64"
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"lastModifyingUser": {
|
|
28
|
+
"displayName": "John Doe",
|
|
29
|
+
"kind": "drive#user",
|
|
30
|
+
"me": true,
|
|
31
|
+
"permissionId": "077423361841532483",
|
|
32
|
+
"emailAddress": "john@doe.com",
|
|
33
|
+
"photoLink": "https://lh3.googleusercontent.com/a/default-user=s64"
|
|
34
|
+
},
|
|
35
|
+
"webViewLink": "https://drive.google.com/drive/folders/1F1Q9K7L8N2P3R4S5T6U7V8W9X0Y1Z2A3?usp=drivesdk",
|
|
36
|
+
"version": "1",
|
|
37
|
+
"capabilities": {
|
|
38
|
+
"canCopy": true,
|
|
39
|
+
"canDelete": true,
|
|
40
|
+
"canRename": true,
|
|
41
|
+
"canTrash": true,
|
|
42
|
+
"canUntrash": true,
|
|
43
|
+
"canShare": true,
|
|
44
|
+
"canListChildren": true,
|
|
45
|
+
"canAddChildren": true,
|
|
46
|
+
"canRemoveChildren": true
|
|
47
|
+
},
|
|
48
|
+
"spaces": [
|
|
49
|
+
"drive"
|
|
50
|
+
]
|
|
51
|
+
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform";
|
|
2
|
+
import googleDrive from "../../google_drive.app.mjs";
|
|
3
|
+
import { getListFilesOpts } from "../../common/utils.mjs";
|
|
4
|
+
import { GOOGLE_DRIVE_FOLDER_MIME_TYPE } from "../../common/constants.mjs";
|
|
5
|
+
import sampleEmit from "./test-event.mjs";
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
key: "google_drive-new-spreadsheet-polling",
|
|
9
|
+
name: "New Spreadsheet (Polling)",
|
|
10
|
+
description: "Emit new event when a new spreadsheet is created in a drive.",
|
|
11
|
+
version: "0.0.1",
|
|
12
|
+
type: "source",
|
|
13
|
+
dedupe: "unique",
|
|
14
|
+
props: {
|
|
15
|
+
googleDrive,
|
|
16
|
+
db: "$.service.db",
|
|
17
|
+
timer: {
|
|
18
|
+
type: "$.interface.timer",
|
|
19
|
+
default: {
|
|
20
|
+
intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
drive: {
|
|
24
|
+
propDefinition: [
|
|
25
|
+
googleDrive,
|
|
26
|
+
"watchedDrive",
|
|
27
|
+
],
|
|
28
|
+
description: "Defaults to My Drive. To select a [Shared Drive](https://support.google.com/a/users/answer/9310351) instead, select it from this list.",
|
|
29
|
+
optional: false,
|
|
30
|
+
},
|
|
31
|
+
folders: {
|
|
32
|
+
propDefinition: [
|
|
33
|
+
googleDrive,
|
|
34
|
+
"folderId",
|
|
35
|
+
({ drive }) => ({
|
|
36
|
+
drive,
|
|
37
|
+
baseOpts: {
|
|
38
|
+
q: `mimeType = '${GOOGLE_DRIVE_FOLDER_MIME_TYPE}' and trashed = false`,
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
],
|
|
42
|
+
type: "string[]",
|
|
43
|
+
label: "Folders",
|
|
44
|
+
description: "The specific folder(s) to watch for new spreadsheets. Leave blank to watch all folders in the Drive.",
|
|
45
|
+
optional: true,
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
hooks: {
|
|
49
|
+
async deploy() {
|
|
50
|
+
// Get initial page token for change tracking
|
|
51
|
+
const driveId = this.getDriveId();
|
|
52
|
+
const startPageToken = await this.googleDrive.getPageToken(driveId);
|
|
53
|
+
this._setPageToken(startPageToken);
|
|
54
|
+
|
|
55
|
+
this._setLastRunTimestamp(Date.now());
|
|
56
|
+
|
|
57
|
+
// Emit sample spreadsheets from the last 30 days
|
|
58
|
+
const daysAgo = new Date();
|
|
59
|
+
daysAgo.setDate(daysAgo.getDate() - 30);
|
|
60
|
+
const timeString = daysAgo.toISOString();
|
|
61
|
+
|
|
62
|
+
const args = this.getListFilesOpts({
|
|
63
|
+
q: `mimeType = "application/vnd.google-apps.spreadsheet" and createdTime > "${timeString}" and trashed = false`,
|
|
64
|
+
orderBy: "createdTime desc",
|
|
65
|
+
fields: "*",
|
|
66
|
+
pageSize: 5,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const { files } = await this.googleDrive.listFilesInPage(null, args);
|
|
70
|
+
|
|
71
|
+
for (const file of files) {
|
|
72
|
+
if (this.shouldProcess(file)) {
|
|
73
|
+
await this.emitSpreadsheet(file);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
methods: {
|
|
79
|
+
_getPageToken() {
|
|
80
|
+
return this.db.get("pageToken");
|
|
81
|
+
},
|
|
82
|
+
_setPageToken(pageToken) {
|
|
83
|
+
this.db.set("pageToken", pageToken);
|
|
84
|
+
},
|
|
85
|
+
_getLastRunTimestamp() {
|
|
86
|
+
return this.db.get("lastRunTimestamp");
|
|
87
|
+
},
|
|
88
|
+
_setLastRunTimestamp(timestamp) {
|
|
89
|
+
this.db.set("lastRunTimestamp", timestamp);
|
|
90
|
+
},
|
|
91
|
+
getDriveId(drive = this.drive) {
|
|
92
|
+
return this.googleDrive.getDriveId(drive);
|
|
93
|
+
},
|
|
94
|
+
getListFilesOpts(args = {}) {
|
|
95
|
+
return getListFilesOpts(this.drive, {
|
|
96
|
+
...args,
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
shouldProcess(file) {
|
|
100
|
+
// Check if it's a spreadsheet
|
|
101
|
+
if (!file.mimeType || !file.mimeType.includes("spreadsheet")) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check if specific folders are being watched
|
|
106
|
+
if (this.folders?.length > 0) {
|
|
107
|
+
const watchedFolders = new Set(this.folders);
|
|
108
|
+
if (!file.parents || !file.parents.some((p) => watchedFolders.has(p))) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return true;
|
|
114
|
+
},
|
|
115
|
+
generateMeta(file) {
|
|
116
|
+
const {
|
|
117
|
+
id: fileId,
|
|
118
|
+
name: summary,
|
|
119
|
+
createdTime: tsString,
|
|
120
|
+
} = file;
|
|
121
|
+
const ts = Date.parse(tsString);
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
id: `${fileId}-${ts}`,
|
|
125
|
+
summary,
|
|
126
|
+
ts,
|
|
127
|
+
};
|
|
128
|
+
},
|
|
129
|
+
async emitSpreadsheet(file) {
|
|
130
|
+
const meta = this.generateMeta(file);
|
|
131
|
+
this.$emit(file, meta);
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
async run() {
|
|
135
|
+
const currentRunTimestamp = Date.now();
|
|
136
|
+
const lastRunTimestamp = this._getLastRunTimestamp();
|
|
137
|
+
|
|
138
|
+
const pageToken = this._getPageToken();
|
|
139
|
+
const driveId = this.getDriveId();
|
|
140
|
+
|
|
141
|
+
const changedFilesStream = this.googleDrive.listChanges(pageToken, driveId);
|
|
142
|
+
|
|
143
|
+
for await (const changedFilesPage of changedFilesStream) {
|
|
144
|
+
console.log("Changed files page:", changedFilesPage);
|
|
145
|
+
const {
|
|
146
|
+
changedFiles,
|
|
147
|
+
nextPageToken,
|
|
148
|
+
} = changedFilesPage;
|
|
149
|
+
|
|
150
|
+
console.log(changedFiles.length
|
|
151
|
+
? `Processing ${changedFiles.length} changed files`
|
|
152
|
+
: "No changed files since last run");
|
|
153
|
+
|
|
154
|
+
for (const file of changedFiles) {
|
|
155
|
+
// Skip if not a spreadsheet
|
|
156
|
+
if (!file.mimeType || !file.mimeType.includes("spreadsheet")) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Get full file metadata including parents
|
|
161
|
+
const fullFile = await this.googleDrive.getFile(file.id, {
|
|
162
|
+
fields: "*",
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Check if it's a new spreadsheet (created after last run)
|
|
166
|
+
const fileCreatedTime = Date.parse(fullFile.createdTime);
|
|
167
|
+
if (fileCreatedTime <= lastRunTimestamp) {
|
|
168
|
+
console.log(`Skipping existing spreadsheet ${fullFile.name || fullFile.id}`);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!this.shouldProcess(fullFile)) {
|
|
173
|
+
console.log(`Skipping spreadsheet ${fullFile.name || fullFile.id}`);
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
await this.emitSpreadsheet(fullFile);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Save the next page token after successfully processing
|
|
181
|
+
this._setPageToken(nextPageToken);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Update the last run timestamp after processing all changes
|
|
185
|
+
this._setLastRunTimestamp(currentRunTimestamp);
|
|
186
|
+
},
|
|
187
|
+
sampleEmit,
|
|
188
|
+
};
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
"kind": "drive#file",
|
|
3
|
+
"copyRequiresWriterPermission": false,
|
|
4
|
+
"writersCanShare": true,
|
|
5
|
+
"viewedByMe": true,
|
|
6
|
+
"mimeType": "application/vnd.google-apps.spreadsheet",
|
|
7
|
+
"exportLinks": {
|
|
8
|
+
"application/x-vnd.oasis.opendocument.spreadsheet": "https://docs.google.com/spreadsheets/export?id=1LmXTIQ0wqKP7T3-l9r127MaTXn3pVbTmw4&exportFormat=ods",
|
|
9
|
+
"text/tab-separated-values": "https://docs.google.com/spreadsheets/export?id=1LmXTIQ0wqKP7T3-l9r127MaTXnViTOo&exportFormat=tsv",
|
|
10
|
+
"application/pdf": "https://docs.google.com/spreadsheets/export?id=1LmXTIQ0wqKP7T3-l9r127MaBViTOo&exportFormat=pdf",
|
|
11
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "https://docs.google.com/spreadsheets/export?id=1LmXTIQ0wqKP7T3-l9r127MaTXn3pVbOo&exportFormat=xlsx",
|
|
12
|
+
"text/csv": "https://docs.google.com/spreadsheets/export?id=1LmXTIQ0wqKP7T3-l9r127MaTXn3pVbBViTOo&exportFormat=csv",
|
|
13
|
+
"application/zip": "https://docs.google.com/spreadsheets/export?id=1LmXTIQ0wqKP7T3-VbTmw4Pt2BViTOo&exportFormat=zip",
|
|
14
|
+
"application/vnd.oasis.opendocument.spreadsheet": "https://docs.google.com/spreadsheets/export?id=1LmXTIQ0wqKP7T3-l9r127MaTXn3pVbViTOo&exportFormat=ods"
|
|
15
|
+
},
|
|
16
|
+
"parents": [
|
|
17
|
+
"0ANs73yKKVA"
|
|
18
|
+
],
|
|
19
|
+
"thumbnailLink": "https://docs.google.com/feeds/vt?gd=true&id=1LmXTIQ0wqKP7T3-l9r127MaTXn3pVbTmw4&v=12&s=AMedNnoAAAAAZKWjrEqscucFpYCyRCJqnd0wtBiDtXYh&sz=s220",
|
|
20
|
+
"iconLink": "https://drive-thirdparty.googleusercontent.com/16/type/application/vnd.google-apps.spreadsheet",
|
|
21
|
+
"shared": false,
|
|
22
|
+
"lastModifyingUser": {
|
|
23
|
+
"displayName": "John Doe",
|
|
24
|
+
"kind": "drive#user",
|
|
25
|
+
"me": true,
|
|
26
|
+
"permissionId": "077423361841532483",
|
|
27
|
+
"emailAddress": "john@doe.com",
|
|
28
|
+
"photoLink": "https://lh3.googleusercontent.com/a/default-user=s64"
|
|
29
|
+
},
|
|
30
|
+
"owners": [
|
|
31
|
+
{
|
|
32
|
+
"displayName": "John Doe",
|
|
33
|
+
"kind": "drive#user",
|
|
34
|
+
"me": true,
|
|
35
|
+
"permissionId": "07742336189483",
|
|
36
|
+
"emailAddress": "john@doe.com",
|
|
37
|
+
"photoLink": "https://lh3.googleusercontent.com/a/default-user=s64"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"webViewLink": "https://docs.google.com/spreadsheets/d/1LmXTIQ0wqKP7T3-l9r127MaTXn3pVbTmwTOo/edit?usp=drivesdk",
|
|
41
|
+
"size": "1024",
|
|
42
|
+
"viewersCanCopyContent": true,
|
|
43
|
+
"permissions": [
|
|
44
|
+
{
|
|
45
|
+
"id": "07742336184153259483",
|
|
46
|
+
"displayName": "John Doe",
|
|
47
|
+
"type": "user",
|
|
48
|
+
"kind": "drive#permission",
|
|
49
|
+
"photoLink": "https://lh3.googleusercontent.com/a/default-user=s64",
|
|
50
|
+
"emailAddress": "john@doe.com",
|
|
51
|
+
"role": "owner",
|
|
52
|
+
"deleted": false,
|
|
53
|
+
"pendingOwner": false
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"hasThumbnail": true,
|
|
57
|
+
"spaces": [
|
|
58
|
+
"drive"
|
|
59
|
+
],
|
|
60
|
+
"id": "1LmXTIQ0wqKP7T3-l9r127Maw4Pt2BViTOo",
|
|
61
|
+
"name": "New Spreadsheet",
|
|
62
|
+
"starred": false,
|
|
63
|
+
"trashed": false,
|
|
64
|
+
"explicitlyTrashed": false,
|
|
65
|
+
"createdTime": "2023-06-28T12:52:54.570Z",
|
|
66
|
+
"modifiedTime": "2023-06-28T12:52:54.570Z",
|
|
67
|
+
"modifiedByMeTime": "2023-06-28T12:52:54.570Z",
|
|
68
|
+
"viewedByMeTime": "2023-06-29T06:30:58.441Z",
|
|
69
|
+
"quotaBytesUsed": "1024",
|
|
70
|
+
"version": "53",
|
|
71
|
+
"ownedByMe": true,
|
|
72
|
+
"isAppAuthorized": false,
|
|
73
|
+
"capabilities": {
|
|
74
|
+
"canChangeViewersCanCopyContent": true,
|
|
75
|
+
"canEdit": true,
|
|
76
|
+
"canCopy": true,
|
|
77
|
+
"canComment": true,
|
|
78
|
+
"canAddChildren": false,
|
|
79
|
+
"canDelete": true,
|
|
80
|
+
"canDownload": true,
|
|
81
|
+
"canListChildren": false,
|
|
82
|
+
"canRemoveChildren": false,
|
|
83
|
+
"canRename": true,
|
|
84
|
+
"canTrash": true,
|
|
85
|
+
"canReadRevisions": true,
|
|
86
|
+
"canChangeCopyRequiresWriterPermission": true,
|
|
87
|
+
"canMoveItemIntoTeamDrive": true,
|
|
88
|
+
"canUntrash": true,
|
|
89
|
+
"canModifyContent": true,
|
|
90
|
+
"canMoveItemOutOfDrive": true,
|
|
91
|
+
"canAddMyDriveParent": false,
|
|
92
|
+
"canRemoveMyDriveParent": true,
|
|
93
|
+
"canMoveItemWithinDrive": true,
|
|
94
|
+
"canShare": true,
|
|
95
|
+
"canMoveChildrenWithinDrive": false,
|
|
96
|
+
"canModifyContentRestriction": true,
|
|
97
|
+
"canChangeSecurityUpdateEnabled": false,
|
|
98
|
+
"canAcceptOwnership": false,
|
|
99
|
+
"canReadLabels": true,
|
|
100
|
+
"canModifyLabels": true
|
|
101
|
+
},
|
|
102
|
+
"thumbnailVersion": "12",
|
|
103
|
+
"modifiedByMe": true,
|
|
104
|
+
"permissionIds": [
|
|
105
|
+
"07742336184153259483"
|
|
106
|
+
],
|
|
107
|
+
"linkShareMetadata": {
|
|
108
|
+
"securityUpdateEligible": false,
|
|
109
|
+
"securityUpdateEnabled": true
|
|
110
|
+
}
|
|
111
|
+
};
|