diff --git a/components/egnyte/actions/create-folder/create-folder.mjs b/components/egnyte/actions/create-folder/create-folder.mjs new file mode 100644 index 0000000000000..bce22fdf880bc --- /dev/null +++ b/components/egnyte/actions/create-folder/create-folder.mjs @@ -0,0 +1,28 @@ +import egnyte from "../../egnyte.app.mjs"; + +export default { + key: "egnyte-create-folder", + name: "Create Folder", + description: "Creates a new folder in your Egnyte workspace. [See the documentation](https://developers.egnyte.com/docs/File_System_Management_API_Documentation#Create-a-Folder)", + version: "0.0.1", + type: "action", + props: { + egnyte, + folderPath: { + type: "string", + label: "Folder Path", + description: "The full path to the new folder. Example: `/Shared/test`", + }, + }, + async run({ $ }) { + const folderPath = this.folderPath[0] === "/" + ? this.folderPath.slice(1) + : this.folderPath; + const response = await this.egnyte.createFolder({ + $, + folderPath, + }); + $.export("$summary", `Created folder "${this.folderPath}"`); + return response; + }, +}; diff --git a/components/egnyte/actions/upload-file/upload-file.mjs b/components/egnyte/actions/upload-file/upload-file.mjs new file mode 100644 index 0000000000000..09b9c1afab861 --- /dev/null +++ b/components/egnyte/actions/upload-file/upload-file.mjs @@ -0,0 +1,60 @@ +import egnyte from "../../egnyte.app.mjs"; +import FormData from "form-data"; +import fs from "fs"; +import path from "path"; +import mime from "mime"; + +export default { + key: "egnyte-upload-file", + name: "Upload File", + description: "Uploads a file to a specified folder in Egnyte. [See the documentation](https://developers.egnyte.com/docs/File_System_Management_API_Documentation#Upload-a-File)", + version: "0.0.1", + type: "action", + props: { + egnyte, + filePath: { + type: "string", + label: "File Path", + description: "The path to a file in the `/tmp` directory. [See the documentation on working with files](https://pipedream.com/docs/code/nodejs/working-with-files/#writing-a-file-to-tmp)", + }, + folderPath: { + type: "string", + label: "Folder Path", + description: "The full path to the folder where the file should be uploaded. Example: `/Shared/Documents", + }, + }, + async run({ $ }) { + const form = new FormData(); + + const filePath = this.filePath.includes("tmp/") + ? this.filePath + : `/tmp/${this.filePath}`; + + const filename = path.basename(filePath); + const contentType = mime.getType(filePath) || "application/octet-stream"; + + form.append("file", fs.createReadStream(filePath), { + filename, + contentType, + }); + + let folderPath = this.folderPath; + if (folderPath.startsWith("/")) { + folderPath = folderPath.slice(1); + } + if (folderPath.endsWith("/")) { + folderPath = folderPath.slice(0, -1); + } + + const response = await this.egnyte.uploadFile({ + $, + folderPath, + filename, + data: form, + headers: form.getHeaders(), + }); + + $.export("$summary", `Successfully uploaded file ${filename}`); + return response; + }, +}; diff --git a/components/egnyte/egnyte.app.mjs b/components/egnyte/egnyte.app.mjs index ca18e17720b3a..220b1f4c16e87 100644 --- a/components/egnyte/egnyte.app.mjs +++ b/components/egnyte/egnyte.app.mjs @@ -1,11 +1,59 @@ +import { axios } from "@pipedream/platform"; +import Bottleneck from "bottleneck"; +const limiter = new Bottleneck({ + minTime: 500, // 2 requests per second + maxConcurrent: 1, +}); +const axiosRateLimiter = limiter.wrap(axios); + export default { type: "app", app: "egnyte", - propDefinitions: {}, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return `https://${this.$auth.subdomain}.egnyte.com/pubapi/v1`; + }, + _makeRequest({ + $ = this, + path, + headers, + ...otherOpts + }) { + const config = { + url: `${this._baseUrl()}${path}`, + headers: { + ...headers, + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }, + ...otherOpts, + }; + return axiosRateLimiter($, config); + }, + getFolder({ + folderPath, ...opts + }) { + return this._makeRequest({ + path: `/fs/${folderPath}`, + ...opts, + }); + }, + createFolder({ folderPath }) { + return this._makeRequest({ + method: "POST", + path: `/fs/${folderPath}`, + data: { + action: "add_folder", + }, + }); + }, + uploadFile({ + folderPath, filename, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/fs-content/${folderPath}/${filename}`, + ...opts, + }); }, }, }; diff --git a/components/egnyte/package.json b/components/egnyte/package.json index 15b0d81a92aeb..d23f556e09fdd 100644 --- a/components/egnyte/package.json +++ b/components/egnyte/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/egnyte", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Egnyte Components", "main": "egnyte.app.mjs", "keywords": [ @@ -11,5 +11,12 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3", + "bottleneck": "^2.19.5", + "form-data": "^4.0.1", + "mime": "^4.0.6", + "path": "^0.12.7" } -} \ No newline at end of file +} diff --git a/components/egnyte/sources/common/base.mjs b/components/egnyte/sources/common/base.mjs new file mode 100644 index 0000000000000..115064ffd2441 --- /dev/null +++ b/components/egnyte/sources/common/base.mjs @@ -0,0 +1,80 @@ +import egnyte from "../../egnyte.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + egnyte, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + folderPath: { + type: "string", + label: "Folder Path", + description: "The folder path (example: `/Shared/Documents`) to watch for updates.", + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + getResourceType() { + throw new Error("getResourceType is not implemented"); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, + async run() { + const lastTs = this._getLastTs(); + let maxTs = lastTs; + const resourceType = this.getResourceType(); + + // Recursively process folder and subfolders + const processFolder = async (folderPath) => { + const results = await this.egnyte.getFolder({ + folderPath, + params: { + sort_by: "last_modified", + sort_direction: "descending", + }, + }); + + const items = results[resourceType]; + if (!items?.length) { + return; + } + const newItems = []; + + for (const item of items) { + const ts = item.uploaded; + if (ts >= lastTs) { + newItems.push(item); + maxTs = Math.max(ts, maxTs); + } + } + + const folders = results.folders; + if (folders?.length) { + for (const folder of folders) { + await processFolder(folder.path); + } + } + + newItems.reverse().forEach((item) => { + const meta = this.generateMeta(item); + this.$emit(item, meta); + }); + }; + + await processFolder(this.folderPath); + + this._setLastTs(maxTs); + }, +}; diff --git a/components/egnyte/sources/new-file-in-folder/new-file-in-folder.mjs b/components/egnyte/sources/new-file-in-folder/new-file-in-folder.mjs new file mode 100644 index 0000000000000..abb88b87f4f8d --- /dev/null +++ b/components/egnyte/sources/new-file-in-folder/new-file-in-folder.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "egnyte-new-file-in-folder", + name: "New File in Folder", + description: "Emit new event when a file is added within the specified folder in Egnyte. [See the documentation](https://developers.egnyte.com/docs/read/File_System_Management_API_Documentation#List-File-or-Folder)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceType() { + return "files"; + }, + generateMeta(file) { + return { + id: file.entry_id, + summary: `New file: ${file.name}`, + ts: file.uploaded, + }; + }, + }, +}; diff --git a/components/egnyte/sources/new-folder-added/new-folder-added.mjs b/components/egnyte/sources/new-folder-added/new-folder-added.mjs new file mode 100644 index 0000000000000..74b3f49a03afc --- /dev/null +++ b/components/egnyte/sources/new-folder-added/new-folder-added.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "egnyte-new-folder-added", + name: "New Folder", + description: "Emit new event when a folder is added within the specified folder in Egnyte. [See the documentation](https://developers.egnyte.com/docs/read/File_System_Management_API_Documentation#List-File-or-Folder).", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceType() { + return "folders"; + }, + generateMeta(folder) { + return { + id: folder.folder_id, + summary: `New folder: ${folder.name}`, + ts: folder.uploaded, + }; + }, + }, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5be7b5b9df1bd..2f4da7d29b196 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3181,7 +3181,23 @@ importers: components/efinder: {} - components/egnyte: {} + components/egnyte: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 + bottleneck: + specifier: ^2.19.5 + version: 2.19.5 + form-data: + specifier: ^4.0.1 + version: 4.0.1 + mime: + specifier: ^4.0.6 + version: 4.0.6 + path: + specifier: ^0.12.7 + version: 0.12.7 components/elastic_email: {} @@ -22365,6 +22381,11 @@ packages: engines: {node: '>=16'} hasBin: true + mime@4.0.6: + resolution: {integrity: sha512-4rGt7rvQHBbaSOF9POGkk1ocRP16Md1x36Xma8sz8h8/vfCUI2OtEIeCqe4Ofes853x4xDoPiFLIT47J5fI/7A==} + engines: {node: '>=16'} + hasBin: true + mimer@2.0.2: resolution: {integrity: sha512-izxvjsB7Ur5HrTbPu6VKTrzxSMBFBqyZQc6dWlZNQ4/wAvf886fD4lrjtFd8IQ8/WmZKdxKjUtqFFNaj3hQ52g==} engines: {node: '>= 12'} @@ -39665,6 +39686,8 @@ snapshots: mime@4.0.4: {} + mime@4.0.6: {} + mimer@2.0.2: {} mimic-fn@2.1.0: {}