@pipedream/google_drive 1.2.0 → 1.3.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.
@@ -1,5 +1,6 @@
1
1
  import Mustaches from "google-docs-mustaches";
2
2
  import googleDrive from "../../google_drive.app.mjs";
3
+ import { parseObjectEntries } from "../../common/utils.mjs";
3
4
 
4
5
  const MODE_GOOGLE_DOC = "Google Doc";
5
6
  const MODE_PDF = "Pdf";
@@ -8,7 +9,7 @@ export default {
8
9
  key: "google_drive-create-file-from-template",
9
10
  name: "Create New File From Template",
10
11
  description: "Create a new Google Docs file from a template. Optionally include placeholders in the template document that will get replaced from this action. [See documentation](https://www.npmjs.com/package/google-docs-mustaches)",
11
- version: "0.1.16",
12
+ version: "0.1.17",
12
13
  annotations: {
13
14
  destructiveHint: true,
14
15
  openWorldHint: true,
@@ -17,10 +18,20 @@ export default {
17
18
  type: "action",
18
19
  props: {
19
20
  googleDrive,
21
+ drive: {
22
+ propDefinition: [
23
+ googleDrive,
24
+ "watchedDrive",
25
+ ],
26
+ optional: true,
27
+ },
20
28
  templateId: {
21
29
  propDefinition: [
22
30
  googleDrive,
23
31
  "fileId",
32
+ (c) => ({
33
+ drive: c.drive,
34
+ }),
24
35
  ],
25
36
  description:
26
37
  "Select the template document you'd like to use as the template, or use a custom expression to reference a document ID from a previous step. Template documents should contain placeholders in the format `{{xyz}}`.",
@@ -29,6 +40,9 @@ export default {
29
40
  propDefinition: [
30
41
  googleDrive,
31
42
  "folderId",
43
+ (c) => ({
44
+ drive: c.drive,
45
+ }),
32
46
  ],
33
47
  description:
34
48
  "Select the folder of the newly created Google Doc and/or PDF, or use a custom expression to reference a folder ID from a previous step.",
@@ -65,19 +79,38 @@ export default {
65
79
  mode: this.mode,
66
80
  };
67
81
 
82
+ const isSharedDrive = this.drive && this.drive !== "My Drive";
83
+
68
84
  const client = new Mustaches.default({
69
85
  token: () => this.googleDrive.$auth.oauth_access_token,
70
86
  });
71
87
 
88
+ // COPY THE TEMPLATE
89
+
90
+ const drive = this.googleDrive.drive();
91
+ const copiedTemplate = await drive.files.copy({
92
+ fileId: this.templateId,
93
+ requestBody: {
94
+ name: "template-copy",
95
+ parents: [
96
+ "root",
97
+ ],
98
+ },
99
+ supportsAllDrives: true,
100
+ });
101
+ const templateId = copiedTemplate.data.id;
102
+
72
103
  /* CREATE THE GOOGLE DOC */
73
104
 
74
105
  let googleDocId;
75
106
  try {
76
107
  googleDocId = await client.interpolate({
77
- source: this.templateId,
78
- destination: this.folderId,
108
+ source: templateId,
109
+ destination: !isSharedDrive
110
+ ? this.folderId
111
+ : undefined,
79
112
  name: this.name,
80
- data: this.replaceValues,
113
+ data: parseObjectEntries(this.replaceValues),
81
114
  });
82
115
  } catch (e) {
83
116
  const {
@@ -93,22 +126,56 @@ export default {
93
126
 
94
127
  /* CREATE THE PDF */
95
128
 
129
+ let pdfId;
96
130
  if (this.mode.includes(MODE_PDF)) {
97
- const pdfId = await client.export({
131
+ pdfId = await client.export({
98
132
  file: googleDocId,
99
133
  mimeType: "application/pdf",
100
134
  name: this.name,
101
- destination: this.folderId,
135
+ destination: !isSharedDrive
136
+ ? this.folderId
137
+ : undefined,
102
138
  });
103
139
  result["pdfId"] = pdfId;
104
140
  }
105
141
 
142
+ // MOVE FILE(S) TO SHARED DRIVE
143
+
144
+ if (isSharedDrive) {
145
+ if (this.mode.includes(MODE_GOOGLE_DOC)) {
146
+ const file = await this.googleDrive.getFile(googleDocId);
147
+ await this.googleDrive.updateFile(googleDocId, {
148
+ fields: "*",
149
+ removeParents: file.parents.join(","),
150
+ addParents: this.folderId || this.drive,
151
+ supportsAllDrives: true,
152
+ });
153
+ }
154
+
155
+ if (pdfId) {
156
+ const pdf = await this.googleDrive.getFile(pdfId);
157
+ await this.googleDrive.updateFile(pdfId, {
158
+ fields: "*",
159
+ removeParents: pdf.parents.join(","),
160
+ addParents: this.folderId || this.drive,
161
+ supportsAllDrives: true,
162
+ });
163
+ }
164
+ }
165
+
106
166
  /* REMOVE THE GOOGLE DOC */
107
167
 
108
168
  if (!this.mode.includes(MODE_GOOGLE_DOC)) {
109
169
  await this.googleDrive.deleteFile(googleDocId);
110
170
  }
111
171
 
172
+ // REMOVE THE COPIED TEMPLATE
173
+
174
+ await drive.files.delete({
175
+ fileId: templateId,
176
+ supportsAllDrives: true,
177
+ });
178
+
112
179
  $.export("$summary", "New file successfully created");
113
180
  return result;
114
181
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/google_drive",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Pipedream Google_drive Components",
5
5
  "main": "google_drive.app.mjs",
6
6
  "keywords": [
@@ -11,7 +11,7 @@
11
11
  "author": "Pipedream <support@pipedream.com> (https://pipedream.com/)",
12
12
  "dependencies": {
13
13
  "@googleapis/drive": "^2.3.0",
14
- "@pipedream/platform": "^3.1.0",
14
+ "@pipedream/platform": "^3.1.1",
15
15
  "cron-parser": "^4.9.0",
16
16
  "google-docs-mustaches": "^1.2.2",
17
17
  "got": "13.0.0",
@@ -0,0 +1,220 @@
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 sampleEmit from "./test-event.mjs";
5
+ import { GOOGLE_DRIVE_FOLDER_MIME_TYPE } from "../../common/constants.mjs";
6
+
7
+ export default {
8
+ key: "google_drive-new-or-modified-files-polling",
9
+ name: "New or Modified Files (Polling)",
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.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
+ files: {
32
+ type: "string[]",
33
+ label: "Files",
34
+ description: "The specific file(s) you want 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
+ optional: true,
36
+ default: [],
37
+ options({ prevContext }) {
38
+ const { nextPageToken } = prevContext;
39
+ return this.googleDrive.listFilesOptions(nextPageToken, this.getListFilesOpts());
40
+ },
41
+ },
42
+ folders: {
43
+ type: "string[]",
44
+ label: "Folders",
45
+ description: "The specific folder(s) to watch for changes. Leave blank to watch all folders in the Drive.",
46
+ 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
+ },
66
+ newFilesOnly: {
67
+ type: "boolean",
68
+ label: "New Files Only",
69
+ description: "If enabled, only emit events for newly created files (based on `createdTime`)",
70
+ default: false,
71
+ optional: true,
72
+ },
73
+ },
74
+ hooks: {
75
+ async deploy() {
76
+ // Get initial page token for change tracking
77
+ const driveId = this.getDriveId();
78
+ const startPageToken = await this.googleDrive.getPageToken(driveId);
79
+ this._setPageToken(startPageToken);
80
+
81
+ this._setLastRunTimestamp(Date.now());
82
+
83
+ // Emit sample events from the last 30 days
84
+ const daysAgo = new Date();
85
+ daysAgo.setDate(daysAgo.getDate() - 30);
86
+ const timeString = daysAgo.toISOString();
87
+
88
+ const args = this.getListFilesOpts({
89
+ q: `mimeType != "application/vnd.google-apps.folder" and modifiedTime > "${timeString}" and trashed = false`,
90
+ orderBy: "modifiedTime desc",
91
+ fields: "*",
92
+ pageSize: 5,
93
+ });
94
+
95
+ const { files } = await this.googleDrive.listFilesInPage(null, args);
96
+
97
+ for (const file of files) {
98
+ if (this.shouldProcess(file)) {
99
+ await this.emitFile(file);
100
+ }
101
+ }
102
+ },
103
+ },
104
+ methods: {
105
+ _getPageToken() {
106
+ return this.db.get("pageToken");
107
+ },
108
+ _setPageToken(pageToken) {
109
+ this.db.set("pageToken", pageToken);
110
+ },
111
+ _getLastRunTimestamp() {
112
+ return this.db.get("lastRunTimestamp");
113
+ },
114
+ _setLastRunTimestamp(timestamp) {
115
+ this.db.set("lastRunTimestamp", timestamp);
116
+ },
117
+ getDriveId(drive = this.drive) {
118
+ return this.googleDrive.getDriveId(drive);
119
+ },
120
+ getListFilesOpts(args = {}) {
121
+ return getListFilesOpts(this.drive, {
122
+ q: "mimeType != 'application/vnd.google-apps.folder' and trashed = false",
123
+ ...args,
124
+ });
125
+ },
126
+ shouldProcess(file, lastRunTimestamp) {
127
+ // Skip folders
128
+ if (file.mimeType === GOOGLE_DRIVE_FOLDER_MIME_TYPE) {
129
+ return false;
130
+ }
131
+
132
+ // Check if "new files only" mode is enabled
133
+ if (this.newFilesOnly) {
134
+ const fileCreatedTime = Date.parse(file.createdTime);
135
+ // Only process files created after the last run
136
+ if (fileCreatedTime <= lastRunTimestamp) {
137
+ return false;
138
+ }
139
+ }
140
+
141
+ // Check if specific files are being watched
142
+ if (this.files?.length > 0) {
143
+ if (!this.files.includes(file.id)) {
144
+ return false;
145
+ }
146
+ }
147
+
148
+ // Check if specific folders are being watched
149
+ if (this.folders?.length > 0) {
150
+ const watchedFolders = new Set(this.folders);
151
+ if (!file.parents || !file.parents.some((p) => watchedFolders.has(p))) {
152
+ return false;
153
+ }
154
+ }
155
+
156
+ return true;
157
+ },
158
+ generateMeta(file) {
159
+ const {
160
+ id: fileId,
161
+ name: summary,
162
+ modifiedTime: tsString,
163
+ } = file;
164
+ const ts = Date.parse(tsString);
165
+
166
+ return {
167
+ id: `${fileId}-${ts}`,
168
+ summary,
169
+ ts,
170
+ };
171
+ },
172
+ async emitFile(file) {
173
+ const meta = this.generateMeta(file);
174
+ this.$emit(file, meta);
175
+ },
176
+ },
177
+ async run() {
178
+ // Store the current run timestamp at the start
179
+ const currentRunTimestamp = Date.now();
180
+ const lastRunTimestamp = this._getLastRunTimestamp();
181
+
182
+ const pageToken = this._getPageToken();
183
+ const driveId = this.getDriveId();
184
+
185
+ const changedFilesStream = this.googleDrive.listChanges(pageToken, driveId);
186
+
187
+ for await (const changedFilesPage of changedFilesStream) {
188
+ console.log("Changed files page:", changedFilesPage);
189
+ const {
190
+ changedFiles,
191
+ nextPageToken,
192
+ } = changedFilesPage;
193
+
194
+ console.log(changedFiles.length
195
+ ? `Processing ${changedFiles.length} changed files`
196
+ : "No changed files since last run");
197
+
198
+ for (const file of changedFiles) {
199
+ // Get full file metadata including parents
200
+ const fullFile = await this.googleDrive.getFile(file.id, {
201
+ fields: "*",
202
+ });
203
+
204
+ if (!this.shouldProcess(fullFile, lastRunTimestamp)) {
205
+ console.log(`Skipping file ${fullFile.name || fullFile.id}`);
206
+ continue;
207
+ }
208
+
209
+ await this.emitFile(fullFile);
210
+ }
211
+
212
+ // Save the next page token after successfully processing
213
+ this._setPageToken(nextPageToken);
214
+ }
215
+
216
+ // Update the last run timestamp after processing all changes
217
+ this._setLastRunTimestamp(currentRunTimestamp);
218
+ },
219
+ sampleEmit,
220
+ };
@@ -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": "Pipedream 2213",
62
+ "starred": false,
63
+ "trashed": false,
64
+ "explicitlyTrashed": false,
65
+ "createdTime": "2023-02-06T15:13:33.023Z",
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
+ };