From d1df610b5c8c9f728e7bcc4cd9dd8c5320c377b7 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Mon, 20 Jan 2025 12:41:48 -0500 Subject: [PATCH 1/8] autodesk init --- .../actions/create-project/create-project.mjs | 36 +++ .../update-file-metadata.mjs | 30 ++ .../actions/upload-file/upload-file.mjs | 51 ++++ components/autodesk/autodesk.app.mjs | 281 +++++++++++++++++- components/autodesk/package.json | 2 +- .../new-file-instant/new-file-instant.mjs | 109 +++++++ .../new-project-instant.mjs | 112 +++++++ .../new-version-instant.mjs | 132 ++++++++ 8 files changed, 750 insertions(+), 3 deletions(-) create mode 100644 components/autodesk/actions/create-project/create-project.mjs create mode 100644 components/autodesk/actions/update-file-metadata/update-file-metadata.mjs create mode 100644 components/autodesk/actions/upload-file/upload-file.mjs create mode 100644 components/autodesk/sources/new-file-instant/new-file-instant.mjs create mode 100644 components/autodesk/sources/new-project-instant/new-project-instant.mjs create mode 100644 components/autodesk/sources/new-version-instant/new-version-instant.mjs diff --git a/components/autodesk/actions/create-project/create-project.mjs b/components/autodesk/actions/create-project/create-project.mjs new file mode 100644 index 0000000000000..0106b6e7dbaa9 --- /dev/null +++ b/components/autodesk/actions/create-project/create-project.mjs @@ -0,0 +1,36 @@ +import autodesk from "../../autodesk.app.mjs"; + +export default { + key: "autodesk-create-project", + name: "Create Project", + description: "Creates a new project in Autodesk. [See the documentation](https://aps.autodesk.com/developer/documentation)", + version: "0.0.{{ts}}", + type: "action", + props: { + autodesk, + projectName: { + propDefinition: [ + autodesk, + "projectName", + ], + }, + projectDescription: { + propDefinition: [ + autodesk, + "projectDescription", + ], + }, + targetWorkspaceId: { + propDefinition: [ + autodesk, + "targetWorkspaceId", + ], + optional: true, + }, + }, + async run({ $ }) { + const project = await this.autodesk.createProject(); + $.export("$summary", `Created project ${project.name} (ID: ${project.id})`); + return project; + }, +}; diff --git a/components/autodesk/actions/update-file-metadata/update-file-metadata.mjs b/components/autodesk/actions/update-file-metadata/update-file-metadata.mjs new file mode 100644 index 0000000000000..47dc247fca3ec --- /dev/null +++ b/components/autodesk/actions/update-file-metadata/update-file-metadata.mjs @@ -0,0 +1,30 @@ +import autodesk from "../../autodesk.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "autodesk-update-file-metadata", + name: "Update File Metadata", + description: "Updates metadata for an existing file in Autodesk. [See the documentation](https://aps.autodesk.com/developer/documentation)", + version: "0.0.{{ts}}", + type: "action", + props: { + autodesk, + fileId: { + propDefinition: [ + autodesk, + "fileId", + ], + }, + metadata: { + propDefinition: [ + autodesk, + "metadata", + ], + }, + }, + async run({ $ }) { + const response = await this.autodesk.updateMetadata(); + $.export("$summary", `Updated metadata for file ${this.fileId}`); + return response; + }, +}; diff --git a/components/autodesk/actions/upload-file/upload-file.mjs b/components/autodesk/actions/upload-file/upload-file.mjs new file mode 100644 index 0000000000000..9e023bcdb5f4d --- /dev/null +++ b/components/autodesk/actions/upload-file/upload-file.mjs @@ -0,0 +1,51 @@ +import autodesk from "../../autodesk.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "autodesk-upload-file", + name: "Upload File to Autodesk Project or Folder", + description: "Uploads a new file to a specified project or folder in Autodesk. [See the documentation]().", + version: "0.0.{{ts}}", + type: "action", + props: { + autodesk, + projectId: { + propDefinition: [ + autodesk, + "projectId", + ], + }, + fileName: { + type: "string", + label: "File Name", + description: "The name of the file to upload", + }, + fileContent: { + type: "string", + label: "File Content", + description: "The content of the file to upload", + }, + folderId: { + propDefinition: [ + autodesk, + "folderId", + ], + optional: true, + }, + }, + async run({ $ }) { + this.autodesk.projectId = this.projectId; + this.autodesk.fileName = this.fileName; + this.autodesk.fileContent = this.fileContent; + if (this.folderId) { + this.autodesk.folderId = this.folderId; + } + + const response = await this.autodesk.uploadFile(); + + $.export("$summary", `Uploaded file "${this.fileName}" to project "${this.projectId}"${this.folderId + ? ` and folder "${this.folderId}"` + : ""}.`); + return response; + }, +}; diff --git a/components/autodesk/autodesk.app.mjs b/components/autodesk/autodesk.app.mjs index 67dc994768ffb..697acfe554275 100644 --- a/components/autodesk/autodesk.app.mjs +++ b/components/autodesk/autodesk.app.mjs @@ -1,11 +1,288 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "autodesk", - propDefinitions: {}, + version: "0.0.{{ts}}", + propDefinitions: { + workspaceId: { + type: "string", + label: "Workspace", + description: "The workspace to monitor for new projects", + async options() { + const workspaces = await this.listWorkspaces(); + return workspaces.map((ws) => ({ + label: ws.name, + value: ws.id, + })); + }, + }, + accountId: { + type: "string", + label: "Account", + description: "The account to monitor for new projects", + async options() { + const accounts = await this.listAccounts(); + return accounts.map((acc) => ({ + label: acc.name, + value: acc.id, + })); + }, + }, + projectId: { + type: "string", + label: "Project", + description: "The project to monitor or manage", + async options({ + workspaceId, accountId, + }) { + const projects = await this.listProjects({ + workspaceId, + accountId, + }); + return projects.map((p) => ({ + label: p.name, + value: p.id, + })); + }, + }, + folderId: { + type: "string", + label: "Folder", + description: "The folder to monitor for new file uploads", + async options({ projectId }) { + const folders = await this.listFolders({ + projectId, + }); + return folders.map((f) => ({ + label: f.name, + value: f.id, + })); + }, + }, + fileId: { + type: "string", + label: "File", + description: "The file to track versions or update metadata", + async options({ + projectId, folderId, + }) { + const files = await this.listFiles({ + projectId, + folderId, + }); + return files.map((f) => ({ + label: f.name, + value: f.id, + })); + }, + }, + projectName: { + type: "string", + label: "Project Name", + description: "The name of the new project", + }, + projectDescription: { + type: "string", + label: "Project Description", + description: "The description of the new project", + optional: true, + }, + targetWorkspaceId: { + type: "string", + label: "Target Workspace", + description: "The workspace to associate the new project with", + async options() { + const workspaces = await this.listWorkspaces(); + return workspaces.map((ws) => ({ + label: ws.name, + value: ws.id, + })); + }, + optional: true, + }, + fileName: { + type: "string", + label: "File Name", + description: "The name of the file to upload", + }, + fileContent: { + type: "string", + label: "File Content", + description: "The content of the file to upload", + }, + metadata: { + type: "string[]", + label: "Metadata", + description: "Key-value pairs for the file metadata to update (as JSON strings)", + }, + }, methods: { - // this.$auth contains connected account data authKeys() { console.log(Object.keys(this.$auth)); }, + _baseUrl() { + return "https://developer.api.autodesk.com"; // Replace with the actual base URL + }, + async _makeRequest(opts = {}) { + const { + $ = this, method = "GET", path = "/", headers, ...otherOpts + } = opts; + return axios($, { + method, + url: this._baseUrl() + path, + headers: { + ...headers, + "Authorization": `Bearer ${this.$auth.access_token}`, // Adjust based on actual auth scheme + "Content-Type": "application/json", + }, + ...otherOpts, + }); + }, + async listWorkspaces(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/project/v1/workspaces", + ...opts, + }); + }, + async listAccounts(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/project/v1/accounts", + ...opts, + }); + }, + async listProjects(opts = {}) { + const { + workspaceId, accountId, + } = opts; + const queryParams = {}; + if (workspaceId) queryParams.workspace_id = workspaceId; + if (accountId) queryParams.account_id = accountId; + return this._makeRequest({ + method: "GET", + path: "/project/v1/projects", + params: queryParams, + ...opts, + }); + }, + async listFolders(opts = {}) { + const { projectId } = opts; + return this._makeRequest({ + method: "GET", + path: `/data/v1/projects/${projectId}/folders`, + ...opts, + }); + }, + async listFiles(opts = {}) { + const { + projectId, folderId, + } = opts; + const path = folderId + ? `/data/v1/projects/${projectId}/folders/${folderId}/files` + : `/data/v1/projects/${projectId}/files`; + return this._makeRequest({ + method: "GET", + path, + ...opts, + }); + }, + async createProject(opts = {}) { + const { + projectName, projectDescription, targetWorkspaceId, + } = this; + const data = { + name: projectName, + description: projectDescription, + workspace_id: targetWorkspaceId, + }; + return this._makeRequest({ + method: "POST", + path: "/project/v1/projects", + data, + ...opts, + }); + }, + async uploadFile(opts = {}) { + const { + projectId, folderId, fileName, fileContent, + } = this; + const path = folderId + ? `/data/v1/projects/${projectId}/folders/${folderId}/files` + : `/data/v1/projects/${projectId}/files`; + const data = { + name: fileName, + content: fileContent, + }; + return this._makeRequest({ + method: "POST", + path, + data, + ...opts, + }); + }, + async updateMetadata(opts = {}) { + const { + fileId, metadata, + } = this; + const metadataObj = metadata.reduce((acc, item) => { + const parsed = JSON.parse(item); + return { + ...acc, + ...parsed, + }; + }, {}); + return this._makeRequest({ + method: "PATCH", + path: `/data/v1/files/${fileId}/metadata`, + data: metadataObj, + ...opts, + }); + }, + async createWebhook(opts = {}) { + const { + eventType, targetUrl, projectId, folderId, fileId, + } = opts; + const data = { + event_type: eventType, + callback_url: targetUrl, + project_id: projectId, + folder_id: folderId, + file_id: fileId, + }; + return this._makeRequest({ + method: "POST", + path: "/webhooks/v1/hooks", + data, + ...opts, + }); + }, + async deleteWebhook(opts = {}) { + const { webhookId } = opts; + return this._makeRequest({ + method: "DELETE", + path: `/webhooks/v1/hooks/${webhookId}`, + ...opts, + }); + }, + async paginate(fn, ...opts) { + let results = []; + let hasMore = true; + let page = 1; + while (hasMore) { + const response = await fn({ + ...opts, + page, + }); + if (!response || response.length === 0) { + hasMore = false; + } else { + results = results.concat(response); + page += 1; + } + } + return results; + }, }, }; diff --git a/components/autodesk/package.json b/components/autodesk/package.json index 777962c7a9c3c..42004e447211d 100644 --- a/components/autodesk/package.json +++ b/components/autodesk/package.json @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/autodesk/sources/new-file-instant/new-file-instant.mjs b/components/autodesk/sources/new-file-instant/new-file-instant.mjs new file mode 100644 index 0000000000000..fdf23e39bc1ff --- /dev/null +++ b/components/autodesk/sources/new-file-instant/new-file-instant.mjs @@ -0,0 +1,109 @@ +import autodesk from "../../autodesk.app.mjs"; +import crypto from "crypto"; +import { axios } from "@pipedream/platform"; + +export default { + key: "autodesk-new-file-instant", + name: "New File Uploaded", + description: "Emit a new event when a file is uploaded to a specified project or folder in Autodesk. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + autodesk: { + type: "app", + app: "autodesk", + }, + projectId: { + propDefinition: [ + autodesk, + "projectId", + ], + }, + folderId: { + propDefinition: [ + autodesk, + "folderId", + ], + optional: true, + }, + db: "$.service.db", + http: { + type: "$.interface.http", + customResponse: true, + }, + webhookSecret: { + type: "string", + label: "Webhook Secret", + description: "Secret used to validate incoming webhook signatures", + }, + }, + hooks: { + async deploy() { + const params = { + projectId: this.projectId, + }; + if (this.folderId) { + params.folderId = this.folderId; + } + const files = await this.autodesk.paginate(this.autodesk.listFiles, params); + const recentFiles = files.slice(-50).reverse(); + for (const file of recentFiles) { + this.$emit(file, { + id: file.id || file.fileId || `${file.created_at}_${file.id}`, + summary: `New file uploaded: ${file.name}`, + ts: file.created_at + ? Date.parse(file.created_at) + : Date.now(), + }); + } + }, + async activate() { + const targetUrl = this.http.endpoint; + const eventType = "file.uploaded"; + const webhook = await this.autodesk.createWebhook({ + eventType, + targetUrl, + projectId: this.projectId, + folderId: this.folderId, + }); + await this.db.set("webhookId", webhook.id); + }, + async deactivate() { + const webhookId = await this.db.get("webhookId"); + if (webhookId) { + await this.autodesk.deleteWebhook({ + webhookId, + }); + await this.db.delete("webhookId"); + } + }, + }, + async run(event) { + const signature = event.headers["x-autodesk-signature"] || event.headers["X-Autodesk-Signature"]; + const computedSignature = crypto + .createHmac("sha256", this.webhookSecret) + .update(event.rawBody) + .digest("base64"); + if (signature !== computedSignature) { + this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + + const file = event.body; + this.$emit(file, { + id: file.id || file.fileId || `${file.created_at}_${file.id}`, + summary: `New file uploaded: ${file.name}`, + ts: file.created_at + ? Date.parse(file.created_at) + : Date.now(), + }); + this.http.respond({ + status: 200, + body: "OK", + }); + }, +}; diff --git a/components/autodesk/sources/new-project-instant/new-project-instant.mjs b/components/autodesk/sources/new-project-instant/new-project-instant.mjs new file mode 100644 index 0000000000000..71c97db928db4 --- /dev/null +++ b/components/autodesk/sources/new-project-instant/new-project-instant.mjs @@ -0,0 +1,112 @@ +import autodesk from "../../autodesk.app.mjs"; +import { axios } from "@pipedream/platform"; +import crypto from "crypto"; + +export default { + key: "autodesk-new-project-instant", + name: "New Project Created", + description: "Emit new event when a new project is created in Autodesk. [See the documentation](https://aps.autodesk.com/developer/documentation)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + autodesk: { + type: "app", + app: "autodesk", + }, + workspaceId: { + propDefinition: [ + autodesk, + "workspaceId", + ], + }, + accountId: { + propDefinition: [ + autodesk, + "accountId", + ], + }, + db: "$.service.db", + http: { + type: "$.interface.http", + customResponse: true, + }, + }, + hooks: { + async activate() { + if (!this.workspaceId && !this.accountId) { + throw new Error("You must provide either a workspace or account to monitor."); + } + const webhook = await this.autodesk.createWebhook({ + eventType: "project.created", + targetUrl: this.http.endpoint, + workspaceId: this.workspaceId, + accountId: this.accountId, + }); + await this.db.set("webhookId", webhook.id); + }, + async deactivate() { + const webhookId = await this.db.get("webhookId"); + if (webhookId) { + await this.autodesk.deleteWebhook({ + webhookId, + }); + await this.db.set("webhookId", null); + } + }, + async deploy() { + const projects = await this.autodesk.paginate(this.autodesk.listProjects, { + workspaceId: this.workspaceId, + accountId: this.accountId, + }); + const last50Projects = projects.slice(-50); + + // Emit from oldest to newest + for (const project of last50Projects) { + const id = project.id || project.ts; + const summary = `New project created: ${project.name}`; + const ts = project.createdAt + ? Date.parse(project.createdAt) + : Date.now(); + + this.$emit(project, { + id, + summary, + ts, + }); + } + }, + }, + async run(event) { + const signature = event.headers["X-Signature"]; + const rawBody = event.body; + const secret = this.autodesk.$auth.webhookSecret; + + const computedSignature = crypto + .createHmac("sha256", secret) + .update(rawBody) + .digest("hex"); + + if (computedSignature !== signature) { + this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + + const project = event.body; + + const id = project.id || Date.now(); + const summary = `New project created: ${project.name}`; + const ts = project.createdAt + ? Date.parse(project.createdAt) + : Date.now(); + + this.$emit(project, { + id, + summary, + ts, + }); + }, +}; diff --git a/components/autodesk/sources/new-version-instant/new-version-instant.mjs b/components/autodesk/sources/new-version-instant/new-version-instant.mjs new file mode 100644 index 0000000000000..1b61c07dbdede --- /dev/null +++ b/components/autodesk/sources/new-version-instant/new-version-instant.mjs @@ -0,0 +1,132 @@ +import autodesk from "../../autodesk.app.mjs"; +import crypto from "crypto"; +import { axios } from "@pipedream/platform"; + +export default { + key: "autodesk-new-version-instant", + name: "New File Version Created", + description: "Emit new event when a new version of a file is created in Autodesk. [See the documentation]()", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + autodesk: { + type: "app", + app: "autodesk", + }, + db: "$.service.db", + http: { + type: "$.interface.http", + customResponse: true, + }, + projectId: { + propDefinition: [ + autodesk, + "projectId", + ], + optional: true, + }, + fileId: { + propDefinition: [ + autodesk, + "fileId", + ], + optional: true, + }, + }, + methods: { + async _createWebhook() { + const eventType = "file_version_created"; + const targetUrl = this.http.endpoint; + const { + projectId, fileId, + } = this; + + const webhookOpts = { + eventType, + targetUrl, + projectId: projectId || undefined, + fileId: fileId || undefined, + }; + + const response = await this.autodesk.createWebhook(webhookOpts); + return response.id; + }, + async _deleteWebhook(webhookId) { + await this.autodesk.deleteWebhook({ + webhookId, + }); + }, + async _fetchHistoricalEvents() { + const { + projectId, fileId, + } = this; + let versions = []; + + if (fileId) { + const fileVersions = await this.autodesk._makeRequest({ + method: "GET", + path: `/data/v1/files/${fileId}/versions`, + }); + versions = fileVersions.slice(-50); + } else if (projectId) { + const files = await this.autodesk.listFiles({ + projectId, + }); + for (const file of files) { + const fileVersions = await this.autodesk._makeRequest({ + method: "GET", + path: `/data/v1/files/${file.id}/versions`, + }); + versions = versions.concat(fileVersions.slice(-50)); + } + versions = versions.slice(-50); + } + + for (const version of versions) { + this.$emit(version, { + id: version.id, + summary: `New version created for file ${version.fileName}`, + ts: Date.parse(version.createdAt) || Date.now(), + }); + } + }, + }, + hooks: { + async activate() { + const webhookId = await this._createWebhook(); + await this.db.set("webhookId", webhookId); + }, + async deactivate() { + const webhookId = await this.db.get("webhookId"); + if (webhookId) { + await this._deleteWebhook(webhookId); + await this.db.delete("webhookId"); + } + }, + async deploy() { + await this._fetchHistoricalEvents(); + }, + }, + async run(event) { + const secret = this.autodesk.$auth.secret; + const signature = event.headers["X-Signature"]; + const computedSignature = crypto + .createHmac("sha256", secret) + .update(event.body) + .digest("hex"); + if (computedSignature !== signature) { + await this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + const versionData = JSON.parse(event.body); + this.$emit(versionData, { + id: versionData.id || versionData.ts, + summary: `New version created for file ${versionData.fileName || "unknown"}`, + ts: Date.parse(versionData.timestamp) || Date.now(), + }); + }, +}; From 9e158758a3278a1f73f6b464e3ff624b6f79df83 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Tue, 21 Jan 2025 13:45:00 -0500 Subject: [PATCH 2/8] new components --- .../actions/create-folder/create-folder.mjs | 86 ++++ .../actions/create-project/create-project.mjs | 36 -- .../update-file-metadata.mjs | 61 ++- .../actions/upload-file/upload-file.mjs | 119 ++++-- components/autodesk/autodesk.app.mjs | 367 +++++++++--------- components/autodesk/package.json | 5 +- .../autodesk/sources/common/base-polling.mjs | 51 +++ .../autodesk/sources/common/base-webhook.mjs | 95 +++++ .../new-file-instant/new-file-instant.mjs | 109 ------ .../new-folder-instant/new-folder-instant.mjs | 26 ++ .../sources/new-folder-instant/test-event.mjs | 37 ++ .../new-project-created.mjs | 40 ++ .../new-project-created/test-event.mjs | 54 +++ .../new-project-instant.mjs | 112 ------ .../new-version-instant.mjs | 138 +------ .../new-version-instant/test-event.mjs | 62 +++ 16 files changed, 790 insertions(+), 608 deletions(-) create mode 100644 components/autodesk/actions/create-folder/create-folder.mjs delete mode 100644 components/autodesk/actions/create-project/create-project.mjs create mode 100644 components/autodesk/sources/common/base-polling.mjs create mode 100644 components/autodesk/sources/common/base-webhook.mjs delete mode 100644 components/autodesk/sources/new-file-instant/new-file-instant.mjs create mode 100644 components/autodesk/sources/new-folder-instant/new-folder-instant.mjs create mode 100644 components/autodesk/sources/new-folder-instant/test-event.mjs create mode 100644 components/autodesk/sources/new-project-created/new-project-created.mjs create mode 100644 components/autodesk/sources/new-project-created/test-event.mjs delete mode 100644 components/autodesk/sources/new-project-instant/new-project-instant.mjs create mode 100644 components/autodesk/sources/new-version-instant/test-event.mjs diff --git a/components/autodesk/actions/create-folder/create-folder.mjs b/components/autodesk/actions/create-folder/create-folder.mjs new file mode 100644 index 0000000000000..39dda4e98f0c3 --- /dev/null +++ b/components/autodesk/actions/create-folder/create-folder.mjs @@ -0,0 +1,86 @@ +import autodesk from "../../autodesk.app.mjs"; + +export default { + key: "autodesk-create-folder", + name: "Create Folder", + description: "Creates a new folder in a project in Autodesk. [See the documentation](https://aps.autodesk.com/en/docs/data/v2/reference/http/projects-project_id-folders-POST/)", + version: "0.0.1", + type: "action", + props: { + autodesk, + hubId: { + propDefinition: [ + autodesk, + "hubId", + ], + }, + projectId: { + propDefinition: [ + autodesk, + "projectId", + (c) => ({ + hubId: c.hubId, + }), + ], + }, + name: { + type: "string", + label: "Name", + description: "The name of the new folder", + }, + parent: { + propDefinition: [ + autodesk, + "folderId", + (c) => ({ + hubId: c.hubId, + projectId: c.projectId, + }), + ], + label: "Parent Folder ID", + description: "The identifier of the parent folder", + }, + type: { + type: "string", + label: "Extension Type", + description: "The type of folder extension. For BIM 360 Docs folders, use `folders:autodesk.bim360:Folder`. For all other services, use `folders:autodesk.core:Folder`.", + options: [ + "folders:autodesk.core:Folder", + "folders:autodesk.bim360:Folder", + ], + default: "folders:autodesk.core:Folder", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.autodesk.createFolder({ + $, + projectId: this.projectId, + data: { + jsonapi: { + version: "1.0", + }, + data: { + type: "folders", + attributes: { + name: this.name, + extension: { + type: this.type, + version: "1.0", + }, + }, + relationships: { + parent: { + data: { + type: "folders", + id: this.parent, + }, + }, + }, + }, + }, + }); + $.export("$summary", `Successfully created new folder: ${this.name}`); + return response; + }, +}; diff --git a/components/autodesk/actions/create-project/create-project.mjs b/components/autodesk/actions/create-project/create-project.mjs deleted file mode 100644 index 0106b6e7dbaa9..0000000000000 --- a/components/autodesk/actions/create-project/create-project.mjs +++ /dev/null @@ -1,36 +0,0 @@ -import autodesk from "../../autodesk.app.mjs"; - -export default { - key: "autodesk-create-project", - name: "Create Project", - description: "Creates a new project in Autodesk. [See the documentation](https://aps.autodesk.com/developer/documentation)", - version: "0.0.{{ts}}", - type: "action", - props: { - autodesk, - projectName: { - propDefinition: [ - autodesk, - "projectName", - ], - }, - projectDescription: { - propDefinition: [ - autodesk, - "projectDescription", - ], - }, - targetWorkspaceId: { - propDefinition: [ - autodesk, - "targetWorkspaceId", - ], - optional: true, - }, - }, - async run({ $ }) { - const project = await this.autodesk.createProject(); - $.export("$summary", `Created project ${project.name} (ID: ${project.id})`); - return project; - }, -}; diff --git a/components/autodesk/actions/update-file-metadata/update-file-metadata.mjs b/components/autodesk/actions/update-file-metadata/update-file-metadata.mjs index 47dc247fca3ec..8c62f47f68ed7 100644 --- a/components/autodesk/actions/update-file-metadata/update-file-metadata.mjs +++ b/components/autodesk/actions/update-file-metadata/update-file-metadata.mjs @@ -1,29 +1,74 @@ import autodesk from "../../autodesk.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "autodesk-update-file-metadata", name: "Update File Metadata", - description: "Updates metadata for an existing file in Autodesk. [See the documentation](https://aps.autodesk.com/developer/documentation)", - version: "0.0.{{ts}}", + description: "Updates metadata for an existing file in Autodesk. [See the documentation](https://aps.autodesk.com/en/docs/data/v2/reference/http/projects-project_id-items-item_id-PATCH/)", + version: "0.0.1", type: "action", props: { autodesk, - fileId: { + hubId: { propDefinition: [ autodesk, - "fileId", + "hubId", ], }, - metadata: { + projectId: { + propDefinition: [ + autodesk, + "projectId", + (c) => ({ + hubId: c.hubId, + }), + ], + }, + parent: { propDefinition: [ autodesk, - "metadata", + "folderId", + (c) => ({ + hubId: c.hubId, + projectId: c.projectId, + }), ], + label: "Parent Folder ID", + description: "The identifier of the file's parent folder", + }, + fileId: { + propDefinition: [ + autodesk, + "fileId", + (c) => ({ + projectId: c.projectId, + folderId: c.parent, + }), + ], + }, + metadata: { + type: "string", + label: "Metadata", + description: "An object containing key-value pairs of the attributes to update. Example: `{ \"displayName\": \"newFileName.jpg\"}`", }, }, async run({ $ }) { - const response = await this.autodesk.updateMetadata(); + const response = await this.autodesk.updateMetadata({ + $, + projectId: this.projectId, + fileId: this.fileId, + data: { + jsonapi: { + version: "1.0", + }, + data: { + type: "items", + id: this.fileId, + attributes: typeof this.metadata === "string" + ? JSON.parse(this.metadata) + : this.metadata, + }, + }, + }); $.export("$summary", `Updated metadata for file ${this.fileId}`); return response; }, diff --git a/components/autodesk/actions/upload-file/upload-file.mjs b/components/autodesk/actions/upload-file/upload-file.mjs index 9e023bcdb5f4d..1dbc06722dae8 100644 --- a/components/autodesk/actions/upload-file/upload-file.mjs +++ b/components/autodesk/actions/upload-file/upload-file.mjs @@ -1,18 +1,37 @@ import autodesk from "../../autodesk.app.mjs"; -import { axios } from "@pipedream/platform"; +import fs from "fs"; export default { key: "autodesk-upload-file", - name: "Upload File to Autodesk Project or Folder", - description: "Uploads a new file to a specified project or folder in Autodesk. [See the documentation]().", - version: "0.0.{{ts}}", + name: "Upload File", + description: "Uploads a new file to a specified folder in Autodesk. [See the documentation](https://aps.autodesk.com/en/docs/data/v2/tutorials/upload-file/).", + version: "0.0.1", type: "action", props: { autodesk, + hubId: { + propDefinition: [ + autodesk, + "hubId", + ], + }, projectId: { propDefinition: [ autodesk, "projectId", + (c) => ({ + hubId: c.hubId, + }), + ], + }, + folderId: { + propDefinition: [ + autodesk, + "folderId", + (c) => ({ + hubId: c.hubId, + projectId: c.projectId, + }), ], }, fileName: { @@ -20,32 +39,80 @@ export default { label: "File Name", description: "The name of the file to upload", }, - fileContent: { + filePath: { type: "string", - label: "File Content", - description: "The content of the file to upload", - }, - folderId: { - propDefinition: [ - autodesk, - "folderId", - ], - optional: true, + 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)", }, }, async run({ $ }) { - this.autodesk.projectId = this.projectId; - this.autodesk.fileName = this.fileName; - this.autodesk.fileContent = this.fileContent; - if (this.folderId) { - this.autodesk.folderId = this.folderId; - } - - const response = await this.autodesk.uploadFile(); - - $.export("$summary", `Uploaded file "${this.fileName}" to project "${this.projectId}"${this.folderId - ? ` and folder "${this.folderId}"` - : ""}.`); + // Create storage location + const { data } = await this.autodesk.createStorageLocation({ + $, + projectId: this.projectId, + data: { + jsonapi: { + version: "1.0", + }, + data: { + type: "objects", + attributes: { + name: this.fileName, + }, + relationships: { + target: { + data: { + type: "folders", + id: this.folderId, + }, + }, + }, + }, + }, + }); + + const objectId = data.id; + const [ + bucketKey, + objectKey, + ] = objectId.match(/^urn:adsk\.objects:os\.object:([^/]+)\/(.+)$/); + + // Generate signed URL + const { + urls, uploadKey, + } = await this.autodesk.generateSignedUrl({ + $, + bucketKey, + objectKey, + }); + + const signedUrl = urls[0]; + + // Upload to signed URL + const fileStream = fs.createReadStream(this.filePath.includes("tmp/") + ? this.filePath + : `/tmp/${this.filePath}`); + + await this.autodesk._makeRequest({ + $, + url: signedUrl, + data: fileStream, + headers: { + "Content-Type": "application/octet-stream", + }, + }); + + // Complete the upload + const response = await this.autodesk.completeUpload({ + $, + bucketKey, + objectKey, + data: { + uploadKey, + }, + }); + + $.export("$summary", `Successfully uploaded file ${this.fileName}`); return response; }, }; diff --git a/components/autodesk/autodesk.app.mjs b/components/autodesk/autodesk.app.mjs index 697acfe554275..61aa39f82c8b4 100644 --- a/components/autodesk/autodesk.app.mjs +++ b/components/autodesk/autodesk.app.mjs @@ -3,286 +3,265 @@ import { axios } from "@pipedream/platform"; export default { type: "app", app: "autodesk", - version: "0.0.{{ts}}", propDefinitions: { - workspaceId: { + hubId: { type: "string", - label: "Workspace", - description: "The workspace to monitor for new projects", + label: "Hub ID", + description: "The identifier of a hub", async options() { - const workspaces = await this.listWorkspaces(); - return workspaces.map((ws) => ({ - label: ws.name, - value: ws.id, - })); - }, - }, - accountId: { - type: "string", - label: "Account", - description: "The account to monitor for new projects", - async options() { - const accounts = await this.listAccounts(); - return accounts.map((acc) => ({ - label: acc.name, - value: acc.id, - })); + const { data } = await this.listHubs(); + return data?.map(({ + id, attributes, + }) => ({ + label: attributes.name, + value: id, + })) || []; }, }, projectId: { type: "string", label: "Project", - description: "The project to monitor or manage", + description: "The identifier of a project", async options({ - workspaceId, accountId, + hubId, page, }) { - const projects = await this.listProjects({ - workspaceId, - accountId, + if (!hubId) { + return []; + } + const { data } = await this.listProjects({ + hubId, + params: { + "page[number]": page, + }, }); - return projects.map((p) => ({ - label: p.name, - value: p.id, - })); + return data?.map(({ + id, attributes, + }) => ({ + label: attributes.name, + value: id, + })) || []; }, }, folderId: { type: "string", - label: "Folder", - description: "The folder to monitor for new file uploads", - async options({ projectId }) { - const folders = await this.listFolders({ + label: "Folder ID", + description: "The identifier of a folder", + async options({ + hubId, projectId, + }) { + if (!hubId || !projectId) { + return []; + } + const rootFolderId = await this.getProjectTopFolderId({ + hubId, projectId, }); - return folders.map((f) => ({ - label: f.name, - value: f.id, - })); + const options = [ + { + label: "Root Folder", + value: rootFolderId, + }, + ]; + + const fetchFoldersRecursively = async (folderId) => { + const { data } = await this.getFolderContent({ + projectId, + folderId, + params: { + "filter[type]": "folders", + }, + }); + const folders = data?.map(({ + id, attributes, + }) => ({ + label: attributes.name, + value: id, + })) || []; + + options.push(...folders); + + for (const folder of folders) { + await fetchFoldersRecursively(folder.value); + } + }; + + await fetchFoldersRecursively(rootFolderId); + + return options; }, }, fileId: { type: "string", label: "File", - description: "The file to track versions or update metadata", + description: "The identifier of a file", async options({ projectId, folderId, }) { - const files = await this.listFiles({ + if (!projectId || !folderId) { + return []; + } + const { data } = await this.getFolderContent({ projectId, folderId, + params: { + "filter[type]": "items", + }, }); - return files.map((f) => ({ - label: f.name, - value: f.id, - })); + return data?.map(({ + id, attributes, + }) => ({ + label: attributes.displayName, + value: id, + })) || []; }, }, - projectName: { - type: "string", - label: "Project Name", - description: "The name of the new project", - }, - projectDescription: { - type: "string", - label: "Project Description", - description: "The description of the new project", - optional: true, - }, - targetWorkspaceId: { - type: "string", - label: "Target Workspace", - description: "The workspace to associate the new project with", - async options() { - const workspaces = await this.listWorkspaces(); - return workspaces.map((ws) => ({ - label: ws.name, - value: ws.id, - })); - }, - optional: true, - }, - fileName: { - type: "string", - label: "File Name", - description: "The name of the file to upload", - }, - fileContent: { - type: "string", - label: "File Content", - description: "The content of the file to upload", - }, - metadata: { - type: "string[]", - label: "Metadata", - description: "Key-value pairs for the file metadata to update (as JSON strings)", - }, }, methods: { - authKeys() { - console.log(Object.keys(this.$auth)); - }, _baseUrl() { - return "https://developer.api.autodesk.com"; // Replace with the actual base URL + return "https://developer.api.autodesk.com"; }, - async _makeRequest(opts = {}) { - const { - $ = this, method = "GET", path = "/", headers, ...otherOpts - } = opts; + _makeRequest({ + $ = this, + path, + headers, + ...otherOpts + }) { return axios($, { - method, - url: this._baseUrl() + path, + url: `${this._baseUrl()}${path}`, headers: { - ...headers, - "Authorization": `Bearer ${this.$auth.access_token}`, // Adjust based on actual auth scheme + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, "Content-Type": "application/json", + ...headers, }, ...otherOpts, }); }, - async listWorkspaces(opts = {}) { + createWebhook({ + system, event, ...opts + }) { return this._makeRequest({ - method: "GET", - path: "/project/v1/workspaces", + method: "POST", + path: `/webhooks/v1/systems/${system}/events/${event}/hooks`, ...opts, }); }, - async listAccounts(opts = {}) { + deleteWebhook({ + system, event, hookId, + }) { return this._makeRequest({ - method: "GET", - path: "/project/v1/accounts", - ...opts, + method: "DELETE", + path: `/webhooks/v1/systems/${system}/events/${event}/hooks/${hookId}`, }); }, - async listProjects(opts = {}) { - const { - workspaceId, accountId, - } = opts; - const queryParams = {}; - if (workspaceId) queryParams.workspace_id = workspaceId; - if (accountId) queryParams.account_id = accountId; + listHubs(opts = {}) { return this._makeRequest({ - method: "GET", - path: "/project/v1/projects", - params: queryParams, + path: "/project/v1/hubs", ...opts, }); }, - async listFolders(opts = {}) { - const { projectId } = opts; + listProjects({ + hubId, ...opts + }) { return this._makeRequest({ - method: "GET", - path: `/data/v1/projects/${projectId}/folders`, + path: `/project/v1/hubs/${hubId}/projects`, ...opts, }); }, - async listFiles(opts = {}) { - const { - projectId, folderId, - } = opts; - const path = folderId - ? `/data/v1/projects/${projectId}/folders/${folderId}/files` - : `/data/v1/projects/${projectId}/files`; - return this._makeRequest({ - method: "GET", - path, + async getProjectTopFolderId({ + hubId, projectId, ...opts + }) { + const { data } = await this._makeRequest({ + path: `/project/v1/hubs/${hubId}/projects/${projectId}/topFolders`, ...opts, }); + return data[0].id; }, - async createProject(opts = {}) { - const { - projectName, projectDescription, targetWorkspaceId, - } = this; - const data = { - name: projectName, - description: projectDescription, - workspace_id: targetWorkspaceId, - }; + getFolderContent({ + projectId, folderId, ...opts + }) { return this._makeRequest({ - method: "POST", - path: "/project/v1/projects", - data, + path: `/data/v1/projects/${projectId}/folders/${folderId}/contents`, ...opts, }); }, - async uploadFile(opts = {}) { - const { - projectId, folderId, fileName, fileContent, - } = this; - const path = folderId - ? `/data/v1/projects/${projectId}/folders/${folderId}/files` - : `/data/v1/projects/${projectId}/files`; - const data = { - name: fileName, - content: fileContent, - }; + createFolder({ + projectId, ...opts + }) { return this._makeRequest({ method: "POST", - path, - data, + path: `/data/v1/projects/${projectId}/folders`, + headers: { + "Content-Type": "application/vnd.api+json", + }, ...opts, }); }, - async updateMetadata(opts = {}) { - const { - fileId, metadata, - } = this; - const metadataObj = metadata.reduce((acc, item) => { - const parsed = JSON.parse(item); - return { - ...acc, - ...parsed, - }; - }, {}); + updateMetadata({ + projectId, fileId, ...opts + }) { return this._makeRequest({ method: "PATCH", - path: `/data/v1/files/${fileId}/metadata`, - data: metadataObj, + path: `/data/v1/projects/${projectId}/items/${fileId}`, + headers: { + "Content-Type": "application/vnd.api+json", + }, ...opts, }); }, - async createWebhook(opts = {}) { - const { - eventType, targetUrl, projectId, folderId, fileId, - } = opts; - const data = { - event_type: eventType, - callback_url: targetUrl, - project_id: projectId, - folder_id: folderId, - file_id: fileId, - }; + createStorageLocation({ + projectId, ...opts + }) { return this._makeRequest({ method: "POST", - path: "/webhooks/v1/hooks", - data, + path: `/data/v1/projects/${projectId}/storage`, + headers: { + "Content-Type": "application/vnd.api+json", + }, ...opts, }); }, - async deleteWebhook(opts = {}) { - const { webhookId } = opts; + generateSignedUrl({ + bucketKey, objectKey, ...opts + }) { return this._makeRequest({ - method: "DELETE", - path: `/webhooks/v1/hooks/${webhookId}`, + path: `/oss/v2/buckets/${bucketKey}/objects/${objectKey}/signeds3upload`, + ...opts, + }); + }, + completeUpload({ + bucketKey, objectKey, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/oss/v2/buckets/${bucketKey}/objects/${objectKey}/signeds3upload`, ...opts, }); }, - async paginate(fn, ...opts) { - let results = []; + async *paginate({ + fn, + args, + max, + }) { let hasMore = true; - let page = 1; + let count = 0; + args = { + ...args, + params: { + ...args?.params, + "page[number]": 0, + "page[limit]": 200, + }, + }; while (hasMore) { - const response = await fn({ - ...opts, - page, - }); - if (!response || response.length === 0) { - hasMore = false; - } else { - results = results.concat(response); - page += 1; + const { data } = await fn(args); + for (const item of data) { + yield item; + if (max && ++count >= max) { + return; + } + hasMore = data?.length === args.params["page[limit]"]; } } - return results; }, }, }; diff --git a/components/autodesk/package.json b/components/autodesk/package.json index 42004e447211d..edab34b5a08b0 100644 --- a/components/autodesk/package.json +++ b/components/autodesk/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/autodesk", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Autodesk Components", "main": "autodesk.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/autodesk/sources/common/base-polling.mjs b/components/autodesk/sources/common/base-polling.mjs new file mode 100644 index 0000000000000..ca28c59971ddd --- /dev/null +++ b/components/autodesk/sources/common/base-polling.mjs @@ -0,0 +1,51 @@ +import autodesk from "../../autodesk.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + autodesk, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + async processEvent(max) { + const items = this.autodesk.paginate({ + fn: this.getFn(), + args: this.getArgs(), + max, + }); + + const results = []; + for await (const item of items) { + results.push(item); + } + + results.forEach((result) => { + const meta = this.generateMeta(result); + this.$emit(result, meta); + }); + }, + getFn() { + throw new Error("getFn is not implemented"); + }, + getArgs() { + return {}; + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, + hooks: { + async deploy() { + await this.processEvent(25); + }, + }, + async run() { + await this.processEvent(); + }, +}; diff --git a/components/autodesk/sources/common/base-webhook.mjs b/components/autodesk/sources/common/base-webhook.mjs new file mode 100644 index 0000000000000..dd2cb1423ef53 --- /dev/null +++ b/components/autodesk/sources/common/base-webhook.mjs @@ -0,0 +1,95 @@ +import autodesk from "../../autodesk.app.mjs"; + +export default { + props: { + autodesk, + db: "$.service.db", + http: { + type: "$.interface.http", + customResponse: true, + }, + hubId: { + propDefinition: [ + autodesk, + "hubId", + ], + }, + projectId: { + propDefinition: [ + autodesk, + "projectId", + (c) => ({ + hubId: c.hubId, + }), + ], + }, + folderId: { + propDefinition: [ + autodesk, + "folderId", + (c) => ({ + hubId: c.hubId, + projectId: c.projectId, + }), + ], + }, + }, + hooks: { + async activate() { + const { headers: { location } } = await this.autodesk.createWebhook({ + system: "data", + event: this.getEvent(), + data: { + callbackUrl: this.http.endpoint, + scope: { + folder: this.folderId, + }, + hubId: this.hubId, + projectId: this.projectId, + autoReactivateHook: true, + }, + returnFullResponse: true, + }); + if (!location) { + throw new Error("Could not create webhook"); + } + const hookId = location.split("/").pop(); + this._setHookId(hookId); + }, + async deactivate() { + const hookId = this._getHookId(); + if (hookId) { + await this.autodesk.deleteWebhook({ + hookId, + }); + } + }, + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + getEvent() { + throw new Error("getEvent is not implemented"); + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, + async run(event) { + this.http.respond({ + status: 200, + }); + + const { body: { payload } } = event; + if (!payload) { + return; + } + + const meta = this.generateMeta(payload); + this.$emit(payload, meta); + }, +}; diff --git a/components/autodesk/sources/new-file-instant/new-file-instant.mjs b/components/autodesk/sources/new-file-instant/new-file-instant.mjs deleted file mode 100644 index fdf23e39bc1ff..0000000000000 --- a/components/autodesk/sources/new-file-instant/new-file-instant.mjs +++ /dev/null @@ -1,109 +0,0 @@ -import autodesk from "../../autodesk.app.mjs"; -import crypto from "crypto"; -import { axios } from "@pipedream/platform"; - -export default { - key: "autodesk-new-file-instant", - name: "New File Uploaded", - description: "Emit a new event when a file is uploaded to a specified project or folder in Autodesk. [See the documentation]()", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - autodesk: { - type: "app", - app: "autodesk", - }, - projectId: { - propDefinition: [ - autodesk, - "projectId", - ], - }, - folderId: { - propDefinition: [ - autodesk, - "folderId", - ], - optional: true, - }, - db: "$.service.db", - http: { - type: "$.interface.http", - customResponse: true, - }, - webhookSecret: { - type: "string", - label: "Webhook Secret", - description: "Secret used to validate incoming webhook signatures", - }, - }, - hooks: { - async deploy() { - const params = { - projectId: this.projectId, - }; - if (this.folderId) { - params.folderId = this.folderId; - } - const files = await this.autodesk.paginate(this.autodesk.listFiles, params); - const recentFiles = files.slice(-50).reverse(); - for (const file of recentFiles) { - this.$emit(file, { - id: file.id || file.fileId || `${file.created_at}_${file.id}`, - summary: `New file uploaded: ${file.name}`, - ts: file.created_at - ? Date.parse(file.created_at) - : Date.now(), - }); - } - }, - async activate() { - const targetUrl = this.http.endpoint; - const eventType = "file.uploaded"; - const webhook = await this.autodesk.createWebhook({ - eventType, - targetUrl, - projectId: this.projectId, - folderId: this.folderId, - }); - await this.db.set("webhookId", webhook.id); - }, - async deactivate() { - const webhookId = await this.db.get("webhookId"); - if (webhookId) { - await this.autodesk.deleteWebhook({ - webhookId, - }); - await this.db.delete("webhookId"); - } - }, - }, - async run(event) { - const signature = event.headers["x-autodesk-signature"] || event.headers["X-Autodesk-Signature"]; - const computedSignature = crypto - .createHmac("sha256", this.webhookSecret) - .update(event.rawBody) - .digest("base64"); - if (signature !== computedSignature) { - this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - - const file = event.body; - this.$emit(file, { - id: file.id || file.fileId || `${file.created_at}_${file.id}`, - summary: `New file uploaded: ${file.name}`, - ts: file.created_at - ? Date.parse(file.created_at) - : Date.now(), - }); - this.http.respond({ - status: 200, - body: "OK", - }); - }, -}; diff --git a/components/autodesk/sources/new-folder-instant/new-folder-instant.mjs b/components/autodesk/sources/new-folder-instant/new-folder-instant.mjs new file mode 100644 index 0000000000000..288c8459908cd --- /dev/null +++ b/components/autodesk/sources/new-folder-instant/new-folder-instant.mjs @@ -0,0 +1,26 @@ +import common from "../common/base-webhook.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "autodesk-new-folder-instant", + name: "New Folder Created (Instant)", + description: "Emit new event when a folder is added to a specified folder in Autodesk. [See the documentation](https://aps.autodesk.com/en/docs/webhooks/v1/reference/http/webhooks/systems-system-events-event-hooks-POST/)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEvent() { + return "dm.folder.added"; + }, + generateMeta(payload) { + return { + id: payload.source, + summary: `New Folder Created: ${payload.name}`, + ts: Date.parse(payload.createdTime), + }; + }, + }, + sampleEmit, +}; diff --git a/components/autodesk/sources/new-folder-instant/test-event.mjs b/components/autodesk/sources/new-folder-instant/test-event.mjs new file mode 100644 index 0000000000000..1af723c45d371 --- /dev/null +++ b/components/autodesk/sources/new-folder-instant/test-event.mjs @@ -0,0 +1,37 @@ +export default { + "modifiedTime": "2025-01-20T21:24:41+0000", + "creator": "7QNJ9NNCKNKRJ8ZU", + "hidden": false, + "folderAggregatePath": "/tenant-57405840/group-470016059/folder/subfolder", + "indexable": false, + "source": "urn:adsk.wipprod:fs.folder:co.NDQS1d9xSZWYNKPIXn1WBQ", + "user_info": { + "id": "7QNJ9NNCKNKRJ8ZU" + }, + "name": "subfolder", + "context": { + "operation": "PostFolders" + }, + "createdTime": "2025-01-20T21:24:41+0000", + "modifiedBy": "7QNJ9NNCKNKRJ8ZU", + "parentFolderUrn": "urn:adsk.wipprod:fs.folder:co.g9HYzAN-QbKWxdHTtLDfog", + "ancestors": [ + { + "name": "pipedream", + "urn": "urn:adsk.wipprod:fs.folder:co.jAXxcF7IQQSUHIhZ1CPHGA" + }, + { + "name": "Default Project", + "urn": "urn:adsk.wipprod:fs.folder:co.6Nb9sJXVRNKRVbgmZyS4oQ" + }, + { + "name": "folder", + "urn": "urn:adsk.wipprod:fs.folder:co.g9HYzAN-QbKWxdHTtLDfog" + } + ], + "project": "470016059", + "tenant": "57405840", + "custom-metadata": { + "fileName": "subfolder" + } +} \ No newline at end of file diff --git a/components/autodesk/sources/new-project-created/new-project-created.mjs b/components/autodesk/sources/new-project-created/new-project-created.mjs new file mode 100644 index 0000000000000..2451451d4b8c2 --- /dev/null +++ b/components/autodesk/sources/new-project-created/new-project-created.mjs @@ -0,0 +1,40 @@ +import common from "../common/base-polling.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "autodesk-new-project-created", + name: "New Project Created", + description: "Emit new event when a new project is created in Autodesk. [See the documentation](https://aps.autodesk.com/en/docs/data/v2/reference/http/hubs-hub_id-projects-GET/)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + hubId: { + propDefinition: [ + common.props.autodesk, + "hubId", + ], + }, + }, + methods: { + ...common.methods, + getFn() { + return this.autodesk.listProjects; + }, + getArgs() { + return { + hubId: this.hubId, + }; + }, + generateMeta(project) { + return { + id: project.id, + summary: `New Project: ${project.attributes.name}`, + ts: Date.now(), + }; + }, + }, + sampleEmit, +}; diff --git a/components/autodesk/sources/new-project-created/test-event.mjs b/components/autodesk/sources/new-project-created/test-event.mjs new file mode 100644 index 0000000000000..1f8ca0b083c01 --- /dev/null +++ b/components/autodesk/sources/new-project-created/test-event.mjs @@ -0,0 +1,54 @@ +export default { + "type": "projects", + "id": "a.YnVzaW5lc3M6cGlwZWRyZWFtI0QyMDI1MDEyMDg2Mjg5MDYwNg", + "attributes": { + "name": "Default Project", + "scopes": [ + "global" + ], + "extension": { + "type": "projects:autodesk.core:Project", + "version": "1.0", + "schema": { + "href": "https://developer.api.autodesk.com/schema/v1/versions/projects:autodesk.core:Project-1.0" + }, + "data": {} + } + }, + "links": { + "self": { + "href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVzaW5lc3M6cGlwZWRyZWFt/projects/a.YnVzaW5lc3M6cGlwZWRyZWFtI0QyMDI1MDEyMDg2Mjg5MDYwNg" + } + }, + "relationships": { + "hub": { + "data": { + "type": "hubs", + "id": "a.YnVzaW5lc3M6cGlwZWRyZWFt" + }, + "links": { + "related": { + "href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVzaW5lc3M6cGlwZWRyZWFt" + } + } + }, + "rootFolder": { + "data": { + "type": "folders", + "id": "urn:adsk.wipprod:fs.folder:co.6Nb9sJXVRNKRVbgmZyS4oQ" + }, + "meta": { + "link": { + "href": "https://developer.api.autodesk.com/data/v1/projects/a.YnVzaW5lc3M6cGlwZWRyZWFtI0QyMDI1MDEyMDg2Mjg5MDYwNg/folders/urn:adsk.wipprod:fs.folder:co.6Nb9sJXVRNKRVbgmZyS4oQ" + } + } + }, + "topFolders": { + "links": { + "related": { + "href": "https://developer.api.autodesk.com/project/v1/hubs/a.YnVzaW5lc3M6cGlwZWRyZWFt/projects/a.YnVzaW5lc3M6cGlwZWRyZWFtI0QyMDI1MDEyMDg2Mjg5MDYwNg/topFolders" + } + } + } + } +} \ No newline at end of file diff --git a/components/autodesk/sources/new-project-instant/new-project-instant.mjs b/components/autodesk/sources/new-project-instant/new-project-instant.mjs deleted file mode 100644 index 71c97db928db4..0000000000000 --- a/components/autodesk/sources/new-project-instant/new-project-instant.mjs +++ /dev/null @@ -1,112 +0,0 @@ -import autodesk from "../../autodesk.app.mjs"; -import { axios } from "@pipedream/platform"; -import crypto from "crypto"; - -export default { - key: "autodesk-new-project-instant", - name: "New Project Created", - description: "Emit new event when a new project is created in Autodesk. [See the documentation](https://aps.autodesk.com/developer/documentation)", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - autodesk: { - type: "app", - app: "autodesk", - }, - workspaceId: { - propDefinition: [ - autodesk, - "workspaceId", - ], - }, - accountId: { - propDefinition: [ - autodesk, - "accountId", - ], - }, - db: "$.service.db", - http: { - type: "$.interface.http", - customResponse: true, - }, - }, - hooks: { - async activate() { - if (!this.workspaceId && !this.accountId) { - throw new Error("You must provide either a workspace or account to monitor."); - } - const webhook = await this.autodesk.createWebhook({ - eventType: "project.created", - targetUrl: this.http.endpoint, - workspaceId: this.workspaceId, - accountId: this.accountId, - }); - await this.db.set("webhookId", webhook.id); - }, - async deactivate() { - const webhookId = await this.db.get("webhookId"); - if (webhookId) { - await this.autodesk.deleteWebhook({ - webhookId, - }); - await this.db.set("webhookId", null); - } - }, - async deploy() { - const projects = await this.autodesk.paginate(this.autodesk.listProjects, { - workspaceId: this.workspaceId, - accountId: this.accountId, - }); - const last50Projects = projects.slice(-50); - - // Emit from oldest to newest - for (const project of last50Projects) { - const id = project.id || project.ts; - const summary = `New project created: ${project.name}`; - const ts = project.createdAt - ? Date.parse(project.createdAt) - : Date.now(); - - this.$emit(project, { - id, - summary, - ts, - }); - } - }, - }, - async run(event) { - const signature = event.headers["X-Signature"]; - const rawBody = event.body; - const secret = this.autodesk.$auth.webhookSecret; - - const computedSignature = crypto - .createHmac("sha256", secret) - .update(rawBody) - .digest("hex"); - - if (computedSignature !== signature) { - this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - - const project = event.body; - - const id = project.id || Date.now(); - const summary = `New project created: ${project.name}`; - const ts = project.createdAt - ? Date.parse(project.createdAt) - : Date.now(); - - this.$emit(project, { - id, - summary, - ts, - }); - }, -}; diff --git a/components/autodesk/sources/new-version-instant/new-version-instant.mjs b/components/autodesk/sources/new-version-instant/new-version-instant.mjs index 1b61c07dbdede..fcfd2c64807ce 100644 --- a/components/autodesk/sources/new-version-instant/new-version-instant.mjs +++ b/components/autodesk/sources/new-version-instant/new-version-instant.mjs @@ -1,132 +1,26 @@ -import autodesk from "../../autodesk.app.mjs"; -import crypto from "crypto"; -import { axios } from "@pipedream/platform"; +import common from "../common/base-webhook.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "autodesk-new-version-instant", - name: "New File Version Created", - description: "Emit new event when a new version of a file is created in Autodesk. [See the documentation]()", - version: "0.0.{{ts}}", + name: "New File Version Created (Instant)", + description: "Emit new event when a new version of a file is created in Autodesk. [See the documentation](https://aps.autodesk.com/en/docs/webhooks/v1/reference/http/webhooks/systems-system-events-event-hooks-POST/)", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - autodesk: { - type: "app", - app: "autodesk", - }, - db: "$.service.db", - http: { - type: "$.interface.http", - customResponse: true, - }, - projectId: { - propDefinition: [ - autodesk, - "projectId", - ], - optional: true, - }, - fileId: { - propDefinition: [ - autodesk, - "fileId", - ], - optional: true, - }, - }, methods: { - async _createWebhook() { - const eventType = "file_version_created"; - const targetUrl = this.http.endpoint; - const { - projectId, fileId, - } = this; - - const webhookOpts = { - eventType, - targetUrl, - projectId: projectId || undefined, - fileId: fileId || undefined, + ...common.methods, + getEvent() { + return "dm.version.added"; + }, + generateMeta(payload) { + return { + id: payload.source, + summary: `New File Version for File: ${payload.name}`, + ts: Date.parse(payload.createdTime), }; - - const response = await this.autodesk.createWebhook(webhookOpts); - return response.id; - }, - async _deleteWebhook(webhookId) { - await this.autodesk.deleteWebhook({ - webhookId, - }); }, - async _fetchHistoricalEvents() { - const { - projectId, fileId, - } = this; - let versions = []; - - if (fileId) { - const fileVersions = await this.autodesk._makeRequest({ - method: "GET", - path: `/data/v1/files/${fileId}/versions`, - }); - versions = fileVersions.slice(-50); - } else if (projectId) { - const files = await this.autodesk.listFiles({ - projectId, - }); - for (const file of files) { - const fileVersions = await this.autodesk._makeRequest({ - method: "GET", - path: `/data/v1/files/${file.id}/versions`, - }); - versions = versions.concat(fileVersions.slice(-50)); - } - versions = versions.slice(-50); - } - - for (const version of versions) { - this.$emit(version, { - id: version.id, - summary: `New version created for file ${version.fileName}`, - ts: Date.parse(version.createdAt) || Date.now(), - }); - } - }, - }, - hooks: { - async activate() { - const webhookId = await this._createWebhook(); - await this.db.set("webhookId", webhookId); - }, - async deactivate() { - const webhookId = await this.db.get("webhookId"); - if (webhookId) { - await this._deleteWebhook(webhookId); - await this.db.delete("webhookId"); - } - }, - async deploy() { - await this._fetchHistoricalEvents(); - }, - }, - async run(event) { - const secret = this.autodesk.$auth.secret; - const signature = event.headers["X-Signature"]; - const computedSignature = crypto - .createHmac("sha256", secret) - .update(event.body) - .digest("hex"); - if (computedSignature !== signature) { - await this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - const versionData = JSON.parse(event.body); - this.$emit(versionData, { - id: versionData.id || versionData.ts, - summary: `New version created for file ${versionData.fileName || "unknown"}`, - ts: Date.parse(versionData.timestamp) || Date.now(), - }); }, + sampleEmit, }; diff --git a/components/autodesk/sources/new-version-instant/test-event.mjs b/components/autodesk/sources/new-version-instant/test-event.mjs new file mode 100644 index 0000000000000..106b0776d727a --- /dev/null +++ b/components/autodesk/sources/new-version-instant/test-event.mjs @@ -0,0 +1,62 @@ +export default { + "ext": "jpg", + "modifiedTime": "2025-01-20T21:33:05+0000", + "creator": "7QNJ9NNCKNKRJ8ZU", + "lineageUrn": "urn:adsk.wipprod:dm.lineage:ghXmW_6LQNKrcMoezksabw", + "sizeInBytes": 463713, + "hidden": false, + "indexable": true, + "source": "urn:adsk.wipprod:fs.file:vf.ghXmW_6LQNKrcMoezksabw?version=1", + "mimeType": "application/image", + "version": "1", + "user_info": { + "id": "7QNJ9NNCKNKRJ8ZU" + }, + "name": "file.jpg", + "context": { + "lineage": { + "reserved": false, + "reservedUserName": null, + "reservedUserId": null, + "reservedTime": null, + "unreservedUserName": null, + "unreservedUserId": null, + "unreservedTime": null, + "createUserId": "7QNJ9NNCKNKRJ8ZU", + "createTime": "2025-01-20T21:33:05+0000", + "createUserName": "", + "lastModifiedUserId": "7QNJ9NNCKNKRJ8ZU", + "lastModifiedTime": "2025-01-20T21:33:05+0000", + "lastModifiedUserName": "" + }, + "operation": "PostVersionedFiles" + }, + "createdTime": "2025-01-20T21:33:05+0000", + "modifiedBy": "7QNJ9NNCKNKRJ8ZU", + "state": "CONTENT_AVAILABLE", + "parentFolderUrn": "urn:adsk.wipprod:fs.folder:co.NDQS1d9xSZWYNKPIXn1WBQ", + "ancestors": [ + { + "name": "pipedream", + "urn": "urn:adsk.wipprod:fs.folder:co.jAXxcF7IQQSUHIhZ1CPHGA" + }, + { + "name": "Default Project", + "urn": "urn:adsk.wipprod:fs.folder:co.6Nb9sJXVRNKRVbgmZyS4oQ" + }, + { + "name": "folder", + "urn": "urn:adsk.wipprod:fs.folder:co.g9HYzAN-QbKWxdHTtLDfog" + }, + { + "name": "subfolder", + "urn": "urn:adsk.wipprod:fs.folder:co.NDQS1d9xSZWYNKPIXn1WBQ" + } + ], + "project": "470016059", + "tenant": "57405840", + "custom-metadata": { + "lineageTitle": "file.jpg", + "fileName": "file.jpg" + } +} \ No newline at end of file From f83ba4bb485fcb613f86a374fbd8ac8eb172bb6e Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Tue, 21 Jan 2025 13:49:45 -0500 Subject: [PATCH 3/8] pnpm-lock.yaml --- pnpm-lock.yaml | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3686908afa83..76d7438c44506 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -566,8 +566,7 @@ importers: components/alphamoon: {} - components/alteryx_analytics_cloud: - specifiers: {} + components/alteryx_analytics_cloud: {} components/altiria: dependencies: @@ -702,8 +701,7 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/apaleo: - specifiers: {} + components/apaleo: {} components/apex_27: {} @@ -868,7 +866,11 @@ importers: components/autobound: {} - components/autodesk: {} + components/autodesk: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/autoklose: {} @@ -4895,8 +4897,7 @@ importers: components/homerun: {} - components/honeyhive: - specifiers: {} + components/honeyhive: {} components/hookdeck: dependencies: @@ -7017,8 +7018,7 @@ importers: specifier: ^3.0.1 version: 3.0.3 - components/nmbrs: - specifiers: {} + components/nmbrs: {} components/nocodb: dependencies: @@ -11453,8 +11453,7 @@ importers: components/vero: {} - components/verticalresponse: - specifiers: {} + components/verticalresponse: {} components/vestaboard: dependencies: @@ -11841,8 +11840,7 @@ importers: components/wolfram_alpha: {} - components/wolfram_alpha_api: - specifiers: {} + components/wolfram_alpha_api: {} components/wonderchat: {} From c2256f334abbdcbebadf266382afcd27e01a9422 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Tue, 21 Jan 2025 14:11:51 -0500 Subject: [PATCH 4/8] updates --- components/autodesk/autodesk.app.mjs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/components/autodesk/autodesk.app.mjs b/components/autodesk/autodesk.app.mjs index 61aa39f82c8b4..1556e7c697dbe 100644 --- a/components/autodesk/autodesk.app.mjs +++ b/components/autodesk/autodesk.app.mjs @@ -63,7 +63,10 @@ export default { }, ]; - const fetchFoldersRecursively = async (folderId) => { + const fetchFoldersRecursively = async (folderId, depth = 0, maxDepth = 10) => { + if (depth > maxDepth) { + return; + } const { data } = await this.getFolderContent({ projectId, folderId, @@ -81,7 +84,7 @@ export default { options.push(...folders); for (const folder of folders) { - await fetchFoldersRecursively(folder.value); + await fetchFoldersRecursively(folder.value, depth + 1, maxDepth); } }; @@ -260,6 +263,7 @@ export default { return; } hasMore = data?.length === args.params["page[limit]"]; + args.params["page[number]"] += 1; } } }, From 4a774d646d412be05ef9985ea283e8a4e72f4a53 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Thu, 23 Jan 2025 13:54:38 -0500 Subject: [PATCH 5/8] wip --- .../actions/upload-file/upload-file.mjs | 85 +++++++++++++++++-- components/autodesk/autodesk.app.mjs | 12 +++ components/autodesk/package.json | 3 +- 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/components/autodesk/actions/upload-file/upload-file.mjs b/components/autodesk/actions/upload-file/upload-file.mjs index 1dbc06722dae8..26940dca347c4 100644 --- a/components/autodesk/actions/upload-file/upload-file.mjs +++ b/components/autodesk/actions/upload-file/upload-file.mjs @@ -1,5 +1,7 @@ import autodesk from "../../autodesk.app.mjs"; +import { axios } from "@pipedream/platform"; import fs from "fs"; +import FormData from "form-data"; export default { key: "autodesk-upload-file", @@ -44,6 +46,17 @@ export default { 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)", }, + type: { + type: "string", + label: "Extension Type", + description: "The type of file extension. For BIM 360 Docs folders, use `items:autodesk.bim360:File`. For all other services, use `items:autodesk.core:File`.", + options: [ + "items:autodesk.core:File", + "items:autodesk.bim360:File", + ], + default: "items:autodesk.core:File", + optional: true, + }, }, async run({ $ }) { // Create storage location @@ -75,7 +88,7 @@ export default { const [ bucketKey, objectKey, - ] = objectId.match(/^urn:adsk\.objects:os\.object:([^/]+)\/(.+)$/); + ] = objectId.split("os.object:")[1]?.split("/") || []; // Generate signed URL const { @@ -89,21 +102,25 @@ export default { const signedUrl = urls[0]; // Upload to signed URL - const fileStream = fs.createReadStream(this.filePath.includes("tmp/") + const form = new FormData(); + form.append("file", fs.createReadStream(this.filePath.includes("tmp/") ? this.filePath - : `/tmp/${this.filePath}`); + : `/tmp/${this.filePath}`)); - await this.autodesk._makeRequest({ - $, + await axios($, { url: signedUrl, - data: fileStream, + data: form, + method: "PUT", headers: { + ...form.getHeaders(), "Content-Type": "application/octet-stream", }, + maxContentLength: Infinity, + maxBodyLength: Infinity, }); // Complete the upload - const response = await this.autodesk.completeUpload({ + await this.autodesk.completeUpload({ $, bucketKey, objectKey, @@ -112,6 +129,60 @@ export default { }, }); + // Create version 1.0 of uploaded file + const response = await this.autodesk.createFirstVersionOfFile({ + $, + projectId: this.projectId, + data: { + jsonapi: { + version: "1.0", + }, + data: { + type: "items", + attributes: { + displayName: this.fileName, + extension: { + type: this.type, + version: "1.0", + }, + }, + relationships: { + tip: { + data: { + type: "versions", + id: "1", + }, + }, + parent: { + data: { + type: "folders", + id: this.folderId, + }, + }, + }, + }, + included: { + type: "versions", + id: "1", + attributes: { + name: this.fileName, + extension: { + type: this.type.replace("items", "versions"), + version: "1.0", + }, + }, + relationships: { + storage: { + data: { + type: "objects", + id: objectId, + }, + }, + }, + }, + }, + }); + $.export("$summary", `Successfully uploaded file ${this.fileName}`); return response; }, diff --git a/components/autodesk/autodesk.app.mjs b/components/autodesk/autodesk.app.mjs index 1556e7c697dbe..ccf8aba8e0fe6 100644 --- a/components/autodesk/autodesk.app.mjs +++ b/components/autodesk/autodesk.app.mjs @@ -240,6 +240,18 @@ export default { ...opts, }); }, + createFirstVersionOfFile({ + projectId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/data/v1/projects/${projectId}/items`, + headers: { + "Content-Type": "application/vnd.api+json", + }, + ...opts, + }); + }, async *paginate({ fn, args, diff --git a/components/autodesk/package.json b/components/autodesk/package.json index edab34b5a08b0..297acc15a3c3c 100644 --- a/components/autodesk/package.json +++ b/components/autodesk/package.json @@ -13,6 +13,7 @@ "access": "public" }, "dependencies": { - "@pipedream/platform": "^3.0.3" + "@pipedream/platform": "^3.0.3", + "form-data": "^4.0.1" } } From fc0edd0438367e0a505841e2fe3f7497c3f6ecb1 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Thu, 23 Jan 2025 14:44:56 -0500 Subject: [PATCH 6/8] updates --- .../update-file-metadata.mjs | 75 ------------------- .../actions/upload-file/upload-file.mjs | 44 +++++------ components/autodesk/autodesk.app.mjs | 37 --------- components/autodesk/package.json | 3 +- 4 files changed, 24 insertions(+), 135 deletions(-) delete mode 100644 components/autodesk/actions/update-file-metadata/update-file-metadata.mjs diff --git a/components/autodesk/actions/update-file-metadata/update-file-metadata.mjs b/components/autodesk/actions/update-file-metadata/update-file-metadata.mjs deleted file mode 100644 index 8c62f47f68ed7..0000000000000 --- a/components/autodesk/actions/update-file-metadata/update-file-metadata.mjs +++ /dev/null @@ -1,75 +0,0 @@ -import autodesk from "../../autodesk.app.mjs"; - -export default { - key: "autodesk-update-file-metadata", - name: "Update File Metadata", - description: "Updates metadata for an existing file in Autodesk. [See the documentation](https://aps.autodesk.com/en/docs/data/v2/reference/http/projects-project_id-items-item_id-PATCH/)", - version: "0.0.1", - type: "action", - props: { - autodesk, - hubId: { - propDefinition: [ - autodesk, - "hubId", - ], - }, - projectId: { - propDefinition: [ - autodesk, - "projectId", - (c) => ({ - hubId: c.hubId, - }), - ], - }, - parent: { - propDefinition: [ - autodesk, - "folderId", - (c) => ({ - hubId: c.hubId, - projectId: c.projectId, - }), - ], - label: "Parent Folder ID", - description: "The identifier of the file's parent folder", - }, - fileId: { - propDefinition: [ - autodesk, - "fileId", - (c) => ({ - projectId: c.projectId, - folderId: c.parent, - }), - ], - }, - metadata: { - type: "string", - label: "Metadata", - description: "An object containing key-value pairs of the attributes to update. Example: `{ \"displayName\": \"newFileName.jpg\"}`", - }, - }, - async run({ $ }) { - const response = await this.autodesk.updateMetadata({ - $, - projectId: this.projectId, - fileId: this.fileId, - data: { - jsonapi: { - version: "1.0", - }, - data: { - type: "items", - id: this.fileId, - attributes: typeof this.metadata === "string" - ? JSON.parse(this.metadata) - : this.metadata, - }, - }, - }); - $.export("$summary", `Updated metadata for file ${this.fileId}`); - return response; - }, -}; diff --git a/components/autodesk/actions/upload-file/upload-file.mjs b/components/autodesk/actions/upload-file/upload-file.mjs index 26940dca347c4..8cd8b24be8021 100644 --- a/components/autodesk/actions/upload-file/upload-file.mjs +++ b/components/autodesk/actions/upload-file/upload-file.mjs @@ -1,7 +1,6 @@ import autodesk from "../../autodesk.app.mjs"; import { axios } from "@pipedream/platform"; import fs from "fs"; -import FormData from "form-data"; export default { key: "autodesk-upload-file", @@ -102,18 +101,19 @@ export default { const signedUrl = urls[0]; // Upload to signed URL - const form = new FormData(); - form.append("file", fs.createReadStream(this.filePath.includes("tmp/") + const filePath = this.filePath.includes("tmp/") ? this.filePath - : `/tmp/${this.filePath}`)); + : `/tmp/${this.filePath}`; + const fileStream = fs.createReadStream(filePath); + const fileSize = fs.statSync(filePath).size; await axios($, { url: signedUrl, - data: form, + data: fileStream, method: "PUT", headers: { - ...form.getHeaders(), "Content-Type": "application/octet-stream", + "Content-Length": fileSize, }, maxContentLength: Infinity, maxBodyLength: Infinity, @@ -161,25 +161,27 @@ export default { }, }, }, - included: { - type: "versions", - id: "1", - attributes: { - name: this.fileName, - extension: { - type: this.type.replace("items", "versions"), - version: "1.0", + included: [ + { + type: "versions", + id: "1", + attributes: { + name: this.fileName, + extension: { + type: this.type.replace("items", "versions"), + version: "1.0", + }, }, - }, - relationships: { - storage: { - data: { - type: "objects", - id: objectId, + relationships: { + storage: { + data: { + type: "objects", + id: objectId, + }, }, }, }, - }, + ], }, }); diff --git a/components/autodesk/autodesk.app.mjs b/components/autodesk/autodesk.app.mjs index ccf8aba8e0fe6..8ea681124d772 100644 --- a/components/autodesk/autodesk.app.mjs +++ b/components/autodesk/autodesk.app.mjs @@ -93,31 +93,6 @@ export default { return options; }, }, - fileId: { - type: "string", - label: "File", - description: "The identifier of a file", - async options({ - projectId, folderId, - }) { - if (!projectId || !folderId) { - return []; - } - const { data } = await this.getFolderContent({ - projectId, - folderId, - params: { - "filter[type]": "items", - }, - }); - return data?.map(({ - id, attributes, - }) => ({ - label: attributes.displayName, - value: id, - })) || []; - }, - }, }, methods: { _baseUrl() { @@ -199,18 +174,6 @@ export default { ...opts, }); }, - updateMetadata({ - projectId, fileId, ...opts - }) { - return this._makeRequest({ - method: "PATCH", - path: `/data/v1/projects/${projectId}/items/${fileId}`, - headers: { - "Content-Type": "application/vnd.api+json", - }, - ...opts, - }); - }, createStorageLocation({ projectId, ...opts }) { diff --git a/components/autodesk/package.json b/components/autodesk/package.json index 297acc15a3c3c..edab34b5a08b0 100644 --- a/components/autodesk/package.json +++ b/components/autodesk/package.json @@ -13,7 +13,6 @@ "access": "public" }, "dependencies": { - "@pipedream/platform": "^3.0.3", - "form-data": "^4.0.1" + "@pipedream/platform": "^3.0.3" } } From 1e074b5efafd5b7c8483a4c4359945b987f412db Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Thu, 23 Jan 2025 14:55:37 -0500 Subject: [PATCH 7/8] fix pagination --- components/autodesk/autodesk.app.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/autodesk/autodesk.app.mjs b/components/autodesk/autodesk.app.mjs index 8ea681124d772..8c9d9f84b2ef2 100644 --- a/components/autodesk/autodesk.app.mjs +++ b/components/autodesk/autodesk.app.mjs @@ -237,9 +237,9 @@ export default { if (max && ++count >= max) { return; } - hasMore = data?.length === args.params["page[limit]"]; - args.params["page[number]"] += 1; } + hasMore = data?.length === args.params["page[limit]"]; + args.params["page[number]"] += 1; } }, }, From 611c97ef82a05d337c6173227cf1733781a9c513 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Mon, 27 Jan 2025 16:23:46 -0500 Subject: [PATCH 8/8] update type prop --- .../autodesk/actions/create-folder/create-folder.mjs | 10 ++++++++-- .../autodesk/actions/upload-file/upload-file.mjs | 12 +++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/components/autodesk/actions/create-folder/create-folder.mjs b/components/autodesk/actions/create-folder/create-folder.mjs index 39dda4e98f0c3..e4b5d589a199c 100644 --- a/components/autodesk/actions/create-folder/create-folder.mjs +++ b/components/autodesk/actions/create-folder/create-folder.mjs @@ -45,8 +45,14 @@ export default { label: "Extension Type", description: "The type of folder extension. For BIM 360 Docs folders, use `folders:autodesk.bim360:Folder`. For all other services, use `folders:autodesk.core:Folder`.", options: [ - "folders:autodesk.core:Folder", - "folders:autodesk.bim360:Folder", + { + label: "BIM 360 Docs folders", + value: "folders:autodesk.core:Folder", + }, + { + label: "Other folders", + value: "folders:autodesk.bim360:Folder", + }, ], default: "folders:autodesk.core:Folder", optional: true, diff --git a/components/autodesk/actions/upload-file/upload-file.mjs b/components/autodesk/actions/upload-file/upload-file.mjs index 8cd8b24be8021..f04a1718e6a08 100644 --- a/components/autodesk/actions/upload-file/upload-file.mjs +++ b/components/autodesk/actions/upload-file/upload-file.mjs @@ -48,10 +48,16 @@ export default { type: { type: "string", label: "Extension Type", - description: "The type of file extension. For BIM 360 Docs folders, use `items:autodesk.bim360:File`. For all other services, use `items:autodesk.core:File`.", + description: "The type of file extension. For BIM 360 Docs files, use `items:autodesk.bim360:File`. For all other services, use `items:autodesk.core:File`.", options: [ - "items:autodesk.core:File", - "items:autodesk.bim360:File", + { + label: "BIM 360 Docs files", + value: "items:autodesk.core:File", + }, + { + label: "Other files", + value: "items:autodesk.bim360:File", + }, ], default: "items:autodesk.core:File", optional: true,