From cb2134837b1c3b96b4e2c78bd3f3cade94f7da36 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 15 Oct 2024 17:59:01 -0300 Subject: [PATCH 1/6] everhour init --- .../actions/create-task/create-task.mjs | 76 ++++++++++ .../actions/start-timer/start-timer.mjs | 44 ++++++ .../actions/stop-timer/stop-timer.mjs | 17 +++ components/everhour/everhour.app.mjs | 135 +++++++++++++++++- .../new-client-instant/new-client-instant.mjs | 54 +++++++ .../new-task-instant/new-task-instant.mjs | 48 +++++++ .../task-time-updated-instant.mjs | 50 +++++++ 7 files changed, 420 insertions(+), 4 deletions(-) create mode 100644 components/everhour/actions/create-task/create-task.mjs create mode 100644 components/everhour/actions/start-timer/start-timer.mjs create mode 100644 components/everhour/actions/stop-timer/stop-timer.mjs create mode 100644 components/everhour/sources/new-client-instant/new-client-instant.mjs create mode 100644 components/everhour/sources/new-task-instant/new-task-instant.mjs create mode 100644 components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs diff --git a/components/everhour/actions/create-task/create-task.mjs b/components/everhour/actions/create-task/create-task.mjs new file mode 100644 index 0000000000000..a74f5cf39bbaf --- /dev/null +++ b/components/everhour/actions/create-task/create-task.mjs @@ -0,0 +1,76 @@ +import everhour from "../../everhour.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "everhour-create-task", + name: "Create Task", + description: "Creates a new task in Everhour. [See the documentation](https://everhour.docs.apiary.io/)", + version: "0.0.1", + type: "action", + props: { + everhour, + projectId: { + propDefinition: [ + everhour, + "projectId", + ], + }, + name: { + type: "string", + label: "Task Name", + description: "The name of the task to be created", + optional: true, + }, + section: { + type: "string", + label: "Section", + description: "The section of the task", + optional: true, + }, + labels: { + type: "string[]", + label: "Labels", + description: "An array of labels associated with the task", + optional: true, + }, + position: { + type: "integer", + label: "Position", + description: "The position of the task", + optional: true, + }, + description: { + type: "string", + label: "Description", + description: "A description of the task", + optional: true, + }, + dueon: { + type: "string", + label: "Due Date", + description: "The due date of the task (ISO 8601 format)", + optional: true, + }, + status: { + type: "string", + label: "Status", + description: "The status of the task", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.everhour.createTask({ + projectId: this.projectId, + name: this.name, + section: this.section, + labels: this.labels, + position: this.position, + description: this.description, + dueon: this.dueon, + status: this.status, + }); + + $.export("$summary", `Successfully created task with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/everhour/actions/start-timer/start-timer.mjs b/components/everhour/actions/start-timer/start-timer.mjs new file mode 100644 index 0000000000000..573a58587323f --- /dev/null +++ b/components/everhour/actions/start-timer/start-timer.mjs @@ -0,0 +1,44 @@ +import everhour from "../../everhour.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "everhour-start-timer", + name: "Start Timer", + description: "Begins a new timer for a task. [See the documentation](https://everhour.docs.apiary.io/#reference/0/timers/start-timer)", + version: "0.0.1", + type: "action", + props: { + everhour, + taskId: { + propDefinition: [ + everhour, + "taskId", + (c) => ({ + projectId: c.projectId, + }), + ], + }, + userdate: { + type: "string", + label: "User Date", + description: "Date string to associate with the timer. Format as 'YYYY-MM-DD'. Optional", + optional: true, + }, + comment: { + type: "string", + label: "Comment", + description: "An optional comment to associate with the timer", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.everhour.startTimer({ + taskId: this.taskId, + userdate: this.userdate, + comment: this.comment, + }); + + $.export("$summary", `Successfully started a timer for task ID: ${this.taskId}`); + return response; + }, +}; diff --git a/components/everhour/actions/stop-timer/stop-timer.mjs b/components/everhour/actions/stop-timer/stop-timer.mjs new file mode 100644 index 0000000000000..bea3559619c4e --- /dev/null +++ b/components/everhour/actions/stop-timer/stop-timer.mjs @@ -0,0 +1,17 @@ +import everhour from "../../everhour.app.mjs"; + +export default { + key: "everhour-stop-timer", + name: "Stop Timer", + description: "Halts the current running timer. [See the documentation](https://everhour.docs.apiary.io/#reference/timers/stop-timer)", + version: "0.0.{{ts}}", + type: "action", + props: { + everhour, + }, + async run({ $ }) { + const response = await this.everhour.stopTimer(); + $.export("$summary", "Successfully stopped the timer"); + return response; + }, +}; diff --git a/components/everhour/everhour.app.mjs b/components/everhour/everhour.app.mjs index f17704bba377a..6be33b9748efd 100644 --- a/components/everhour/everhour.app.mjs +++ b/components/everhour/everhour.app.mjs @@ -1,11 +1,138 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "everhour", - propDefinitions: {}, + propDefinitions: { + projectId: { + type: "string", + label: "Project ID", + description: "The ID of the project", + async options() { + const projects = await this.listProjects(); + return projects.map((project) => ({ + label: project.name, + value: project.id, + })); + }, + }, + taskId: { + type: "string", + label: "Task ID", + description: "The ID of the task", + async options(opts) { + const tasks = await this.getProjectTasks(opts.projectId); + return tasks.map((task) => ({ + label: task.name, + value: task.id, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.everhour.com"; + }, + async _makeRequest(opts = {}) { + const { + $ = this, method = "GET", path = "/", headers, ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + method, + url: this._baseUrl() + path, + headers: { + ...headers, + "Authorization": `Bearer ${this.$auth.api_key}`, + }, + }); + }, + async listProjects(opts = {}) { + return this._makeRequest({ + path: "/projects", + ...opts, + }); + }, + async getProjectTasks(projectId, opts = {}) { + return this._makeRequest({ + path: `/projects/${projectId}/tasks`, + ...opts, + }); + }, + async emitClientCreatedEvent(projectId, opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/webhooks", + data: { + event: "api:client:created", + target: "/your_webhook_url_for_client_created", + projectId, + }, + ...opts, + }); + }, + async emitTaskCreatedEvent(projectId, opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/webhooks", + data: { + event: "api:task:created", + target: "/your_webhook_url_for_task_created", + projectId, + }, + ...opts, + }); + }, + async emitTimeUpdatedEvent(projectId, opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/webhooks", + data: { + event: "api:time:updated", + target: "/your_webhook_url_for_time_updated", + projectId, + }, + ...opts, + }); + }, + async createTask({ + projectId, name, section, labels, position, description, dueon, status, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/projects/${projectId}/tasks`, + data: { + name, + section, + labels, + position, + description, + dueon, + status, + }, + ...opts, + }); + }, + async startTimer({ + taskId, userdate, comment, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: "/timers/start", + data: { + taskId, + userdate, + comment, + }, + ...opts, + }); + }, + async stopTimer(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/timers/stop", + ...opts, + }); }, }, }; diff --git a/components/everhour/sources/new-client-instant/new-client-instant.mjs b/components/everhour/sources/new-client-instant/new-client-instant.mjs new file mode 100644 index 0000000000000..9a131b37d72de --- /dev/null +++ b/components/everhour/sources/new-client-instant/new-client-instant.mjs @@ -0,0 +1,54 @@ +import everhour from "../../everhour.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "everhour-new-client-instant", + name: "New Client Instant", + description: "Emit new event when a client is added. [See the documentation](https://everhour.docs.apiary.io/)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + everhour, + db: "$.service.db", + projectId: { + propDefinition: [ + everhour, + "projectId", + ], + }, + }, + hooks: { + async deploy() { + const clients = await this.getClients(); + clients.slice(0, 50).forEach((client) => { + this.$emit(client, { + id: client.id, + summary: `New Client: ${client.name}`, + ts: Date.parse(client.created), + }); + }); + }, + async activate() { + await this.everhour.emitClientCreatedEvent(this.projectId); + }, + async deactivate() { + await this.everhour.removeWebhook(); + }, + }, + methods: { + async getClients() { + return this.everhour.listClients(); + }, + }, + async run() { + const clients = await this.getClients(); + clients.forEach((client) => { + this.$emit(client, { + id: client.id, + summary: `New Client: ${client.name}`, + ts: Date.parse(client.created), + }); + }); + }, +}; diff --git a/components/everhour/sources/new-task-instant/new-task-instant.mjs b/components/everhour/sources/new-task-instant/new-task-instant.mjs new file mode 100644 index 0000000000000..af2edcbcba1b3 --- /dev/null +++ b/components/everhour/sources/new-task-instant/new-task-instant.mjs @@ -0,0 +1,48 @@ +import everhour from "../../everhour.app.mjs"; + +export default { + key: "everhour-new-task-instant", + name: "New Task Created", + description: "Emit new event when a task is created. [See the documentation](https://everhour.docs.apiary.io/)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + everhour, + db: "$.service.db", + projectId: { + propDefinition: [ + everhour, + "projectId", + ], + }, + }, + hooks: { + async deploy() { + const tasks = await this.everhour.getProjectTasks(this.projectId); + for (const task of tasks.slice(0, 50)) { + this.$emit(task, { + id: task.id, + summary: `New task: ${task.name}`, + ts: Date.parse(task.createdAt), + }); + } + }, + async activate() { + await this.everhour.emitTaskCreatedEvent(this.projectId); + }, + async deactivate() { + // Implement deactivation if an API endpoint exists + }, + }, + async run() { + const tasks = await this.everhour.getProjectTasks(this.projectId); + for (const task of tasks) { + this.$emit(task, { + id: task.id, + summary: `New task: ${task.name}`, + ts: Date.parse(task.createdAt), + }); + } + }, +}; diff --git a/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs b/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs new file mode 100644 index 0000000000000..95e4075302b38 --- /dev/null +++ b/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs @@ -0,0 +1,50 @@ +import everhour from "../../everhour.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "everhour-task-time-updated-instant", + name: "Task Time Updated", + description: "Emit new event when a task's time spent is modified in Everhour. [See the documentation](https://everhour.docs.apiary.io/)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + everhour, + db: "$.service.db", + projectId: { + propDefinition: [ + everhour, + "projectId", + ], + }, + }, + hooks: { + async deploy() { + await this.everhour.emitTimeUpdatedEvent(this.projectId); + }, + async activate() { + await this.everhour.emitTimeUpdatedEvent(this.projectId); + }, + async deactivate() { + // Currently, Everhour API does not provide an endpoint to delete a webhook. + // If such an API becomes available, you should implement it here. + }, + }, + async run() { + const events = await axios(this, { + method: "GET", + url: `${this.everhour._baseUrl()}/projects/${this.projectId}/time-record-events`, + headers: { + "Authorization": `Bearer ${this.everhour.$auth.api_key}`, + }, + }); + + for (const event of events) { + this.$emit(event, { + id: event.id.toString(), + summary: `Task Time Updated: ${event.id}`, + ts: new Date(event.spentAt).getTime(), + }); + } + }, +}; From 362bfeaf62e6549b2bed7aab282ad130e5242cbb Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 16 Oct 2024 16:18:33 -0300 Subject: [PATCH 2/6] init --- .../actions/create-task/create-task.mjs | 50 +++-- .../actions/start-timer/start-timer.mjs | 24 ++- .../actions/stop-timer/stop-timer.mjs | 2 +- components/everhour/common/constants.mjs | 12 ++ components/everhour/common/utils.mjs | 24 +++ components/everhour/everhour.app.mjs | 186 +++++++++++------- components/everhour/package.json | 18 ++ components/everhour/sources/common/base.mjs | 63 ++++++ .../new-client-instant/new-client-instant.mjs | 58 ++---- .../sources/new-client-instant/test-event.mjs | 7 + .../sources/new-task-instant/test-event.mjs | 7 + .../task-time-updated-instant/test-event.mjs | 7 + 12 files changed, 311 insertions(+), 147 deletions(-) create mode 100644 components/everhour/common/constants.mjs create mode 100644 components/everhour/common/utils.mjs create mode 100644 components/everhour/package.json create mode 100644 components/everhour/sources/common/base.mjs create mode 100644 components/everhour/sources/new-client-instant/test-event.mjs create mode 100644 components/everhour/sources/new-task-instant/test-event.mjs create mode 100644 components/everhour/sources/task-time-updated-instant/test-event.mjs diff --git a/components/everhour/actions/create-task/create-task.mjs b/components/everhour/actions/create-task/create-task.mjs index a74f5cf39bbaf..af6ce6cea5bdb 100644 --- a/components/everhour/actions/create-task/create-task.mjs +++ b/components/everhour/actions/create-task/create-task.mjs @@ -1,5 +1,6 @@ +import { STATUS_OPTIONS } from "../../common/constants.mjs"; +import { parseObject } from "../../common/utils.mjs"; import everhour from "../../everhour.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "everhour-create-task", @@ -18,19 +19,22 @@ export default { name: { type: "string", label: "Task Name", - description: "The name of the task to be created", - optional: true, + description: "The name of the task to be created.", }, - section: { - type: "string", - label: "Section", - description: "The section of the task", - optional: true, + sectionId: { + propDefinition: [ + everhour, + "sectionId", + ({ projectId }) => ({ + projectId, + }), + ], }, - labels: { - type: "string[]", - label: "Labels", - description: "An array of labels associated with the task", + tags: { + propDefinition: [ + everhour, + "tags", + ], optional: true, }, position: { @@ -45,29 +49,33 @@ export default { description: "A description of the task", optional: true, }, - dueon: { + dueOn: { type: "string", label: "Due Date", - description: "The due date of the task (ISO 8601 format)", + description: "The due date of the task. **Format: YYYY-MM-DD**", optional: true, }, status: { type: "string", label: "Status", description: "The status of the task", + options: STATUS_OPTIONS, optional: true, }, }, async run({ $ }) { const response = await this.everhour.createTask({ + $, projectId: this.projectId, - name: this.name, - section: this.section, - labels: this.labels, - position: this.position, - description: this.description, - dueon: this.dueon, - status: this.status, + data: { + name: this.name, + section: this.sectionId, + tags: this.tags && parseObject(this.tags), + position: this.position, + description: this.description, + dueOn: this.dueOn, + status: this.status, + }, }); $.export("$summary", `Successfully created task with ID: ${response.id}`); diff --git a/components/everhour/actions/start-timer/start-timer.mjs b/components/everhour/actions/start-timer/start-timer.mjs index 573a58587323f..81d203782731c 100644 --- a/components/everhour/actions/start-timer/start-timer.mjs +++ b/components/everhour/actions/start-timer/start-timer.mjs @@ -1,5 +1,4 @@ import everhour from "../../everhour.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "everhour-start-timer", @@ -9,19 +8,25 @@ export default { type: "action", props: { everhour, + projectId: { + propDefinition: [ + everhour, + "projectId", + ], + }, taskId: { propDefinition: [ everhour, "taskId", - (c) => ({ - projectId: c.projectId, + ({ projectId }) => ({ + projectId, }), ], }, - userdate: { + userDate: { type: "string", label: "User Date", - description: "Date string to associate with the timer. Format as 'YYYY-MM-DD'. Optional", + description: "Date string to associate with the timer. Format as 'YYYY-MM-DD'", optional: true, }, comment: { @@ -33,9 +38,12 @@ export default { }, async run({ $ }) { const response = await this.everhour.startTimer({ - taskId: this.taskId, - userdate: this.userdate, - comment: this.comment, + $, + data: { + task: this.taskId, + userDate: this.userDate, + comment: this.comment, + }, }); $.export("$summary", `Successfully started a timer for task ID: ${this.taskId}`); diff --git a/components/everhour/actions/stop-timer/stop-timer.mjs b/components/everhour/actions/stop-timer/stop-timer.mjs index bea3559619c4e..e533e1afe3c65 100644 --- a/components/everhour/actions/stop-timer/stop-timer.mjs +++ b/components/everhour/actions/stop-timer/stop-timer.mjs @@ -4,7 +4,7 @@ export default { key: "everhour-stop-timer", name: "Stop Timer", description: "Halts the current running timer. [See the documentation](https://everhour.docs.apiary.io/#reference/timers/stop-timer)", - version: "0.0.{{ts}}", + version: "0.0.1", type: "action", props: { everhour, diff --git a/components/everhour/common/constants.mjs b/components/everhour/common/constants.mjs new file mode 100644 index 0000000000000..6807a98f3405f --- /dev/null +++ b/components/everhour/common/constants.mjs @@ -0,0 +1,12 @@ +export const LIMIT = 100; + +export const STATUS_OPTIONS = [ + { + label: "Open", + value: "open", + }, + { + label: "Close", + value: "close", + }, +]; diff --git a/components/everhour/common/utils.mjs b/components/everhour/common/utils.mjs new file mode 100644 index 0000000000000..dcc9cc61f6f41 --- /dev/null +++ b/components/everhour/common/utils.mjs @@ -0,0 +1,24 @@ +export const parseObject = (obj) => { + if (!obj) return undefined; + + if (Array.isArray(obj)) { + return obj.map((item) => { + if (typeof item === "string") { + try { + return JSON.parse(item); + } catch (e) { + return item; + } + } + return item; + }); + } + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + return obj; +}; diff --git a/components/everhour/everhour.app.mjs b/components/everhour/everhour.app.mjs index 6be33b9748efd..8123076c57705 100644 --- a/components/everhour/everhour.app.mjs +++ b/components/everhour/everhour.app.mjs @@ -1,4 +1,5 @@ import { axios } from "@pipedream/platform"; +import { LIMIT } from "./common/constants.mjs"; export default { type: "app", @@ -8,11 +9,68 @@ export default { type: "string", label: "Project ID", description: "The ID of the project", + async options({ page }) { + const projects = await this.listProjects({ + params: { + limit: LIMIT, + page: page + 1, + }, + }); + + return projects.map(({ + name: label, id: value, + }) => ({ + label, + value, + })); + }, + }, + sectionId: { + type: "string", + label: "Section ID", + description: "The section id of the task", + async options({ projectId }) { + const sections = await this.listSections({ + projectId, + }); + + return sections.map(({ + name: label, id: value, + }) => ({ + label, + value, + })); + }, + }, + tags: { + type: "string[]", + label: "Tag IDs", + description: "The tag ids of the task", async options() { - const projects = await this.listProjects(); - return projects.map((project) => ({ - label: project.name, - value: project.id, + const tags = await this.listTags(); + + return tags.map(({ + name: label, id: value, + }) => ({ + label, + value, + })); + }, + }, + labels: { + type: "string[]", + label: "Tags", + description: "An array of tags associated with the task", + async options({ projectId }) { + const sections = await this.listSections({ + projectId, + }); + + return sections.map(({ + name: label, id: value, + }) => ({ + label, + value, })); }, }, @@ -20,11 +78,15 @@ export default { type: "string", label: "Task ID", description: "The ID of the task", - async options(opts) { - const tasks = await this.getProjectTasks(opts.projectId); - return tasks.map((task) => ({ - label: task.name, - value: task.id, + async options({ projectId }) { + const tasks = await this.getProjectTasks({ + projectId, + }); + return tasks.map(({ + name: label, id: value, + }) => ({ + label, + value, })); }, }, @@ -33,106 +95,84 @@ export default { _baseUrl() { return "https://api.everhour.com"; }, - async _makeRequest(opts = {}) { - const { - $ = this, method = "GET", path = "/", headers, ...otherOpts - } = opts; - return axios($, { - ...otherOpts, - method, + _headers() { + return { + "X-Api-Key": `${this.$auth.api_token}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + const config = { url: this._baseUrl() + path, - headers: { - ...headers, - "Authorization": `Bearer ${this.$auth.api_key}`, - }, - }); + headers: this._headers(), + ...opts, + }; + console.log("config: ", config); + return axios($, config); }, - async listProjects(opts = {}) { + listProjects(opts = {}) { return this._makeRequest({ path: "/projects", ...opts, }); }, - async getProjectTasks(projectId, opts = {}) { + listSections({ + projectId, opts, + }) { return this._makeRequest({ - path: `/projects/${projectId}/tasks`, + path: `/projects/${projectId}/sections`, ...opts, }); }, - async emitClientCreatedEvent(projectId, opts = {}) { + listTags() { return this._makeRequest({ - method: "POST", - path: "/webhooks", - data: { - event: "api:client:created", - target: "/your_webhook_url_for_client_created", - projectId, - }, - ...opts, + path: "/tags", }); }, - async emitTaskCreatedEvent(projectId, opts = {}) { + getProjectTasks({ + projectId, ...opts + }) { return this._makeRequest({ - method: "POST", - path: "/webhooks", - data: { - event: "api:task:created", - target: "/your_webhook_url_for_task_created", - projectId, - }, + path: `/projects/${projectId}/tasks`, ...opts, }); }, - async emitTimeUpdatedEvent(projectId, opts = {}) { + createTask({ + projectId, ...opts + }) { return this._makeRequest({ method: "POST", - path: "/webhooks", - data: { - event: "api:time:updated", - target: "/your_webhook_url_for_time_updated", - projectId, - }, + path: `/projects/${projectId}/tasks`, ...opts, }); }, - async createTask({ - projectId, name, section, labels, position, description, dueon, status, ...opts - }) { + startTimer(opts = {}) { return this._makeRequest({ method: "POST", - path: `/projects/${projectId}/tasks`, - data: { - name, - section, - labels, - position, - description, - dueon, - status, - }, + path: "/timers", ...opts, }); }, - async startTimer({ - taskId, userdate, comment, ...opts - }) { + stopTimer(opts = {}) { return this._makeRequest({ - method: "POST", - path: "/timers/start", - data: { - taskId, - userdate, - comment, - }, + method: "DELETE", + path: "/timers/current", ...opts, }); }, - async stopTimer(opts = {}) { + createWebhook(opts = {}) { return this._makeRequest({ method: "POST", - path: "/timers/stop", + path: "/hooks", ...opts, }); }, + deleteWebhook(webhookId) { + return this._makeRequest({ + method: "DELETE", + path: `/hooks/${webhookId}`, + }); + }, }, }; diff --git a/components/everhour/package.json b/components/everhour/package.json new file mode 100644 index 0000000000000..9093fbb1e87e5 --- /dev/null +++ b/components/everhour/package.json @@ -0,0 +1,18 @@ +{ + "name": "@pipedream/everhour", + "version": "0.1.0", + "description": "Pipedream Everhour Components", + "main": "everhour.app.mjs", + "keywords": [ + "pipedream", + "everhour" + ], + "homepage": "https://pipedream.com/apps/everhour", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" + } +} diff --git a/components/everhour/sources/common/base.mjs b/components/everhour/sources/common/base.mjs new file mode 100644 index 0000000000000..845d80d702d3b --- /dev/null +++ b/components/everhour/sources/common/base.mjs @@ -0,0 +1,63 @@ +import everhour from "../../everhour.app.mjs"; + +export default { + props: { + everhour, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + projectId: { + propDefinition: [ + everhour, + "projectId", + ], + }, + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + }, + hooks: { + async activate() { + const response = await this.everhour.createWebhook({ + data: { + targetUrl: this.http.endpoint, + events: this.getEventType(), + project: this.projectId, + }, + }); + this._setHookId(response.id); + }, + async deactivate() { + const webhookId = this._getHookId(); + await this.everhour.deleteWebhook(webhookId); + }, + }, + async run({ + body, headers, + }) { + console.log("headers: ", headers); + + if (headers["X-Hook-Secret"]) { + return this.http.respond({ + status: 200, + headers: { + "X-Hook-Secret": headers["X-Hook-Secret"], + }, + }); + } + + const ts = Date.parse(new Date()); + this.$emit(body, { + id: `${body.resource}-${ts}`, + summary: this.getSummary(body), + ts: ts, + }); + }, +}; diff --git a/components/everhour/sources/new-client-instant/new-client-instant.mjs b/components/everhour/sources/new-client-instant/new-client-instant.mjs index 9a131b37d72de..0ce203722f513 100644 --- a/components/everhour/sources/new-client-instant/new-client-instant.mjs +++ b/components/everhour/sources/new-client-instant/new-client-instant.mjs @@ -1,54 +1,24 @@ -import everhour from "../../everhour.app.mjs"; -import { axios } from "@pipedream/platform"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "everhour-new-client-instant", - name: "New Client Instant", - description: "Emit new event when a client is added. [See the documentation](https://everhour.docs.apiary.io/)", + name: "New Client (Instant)", + description: "Emit new event when a client is added.", version: "0.0.{{ts}}", type: "source", dedupe: "unique", - props: { - everhour, - db: "$.service.db", - projectId: { - propDefinition: [ - everhour, - "projectId", - ], - }, - }, - hooks: { - async deploy() { - const clients = await this.getClients(); - clients.slice(0, 50).forEach((client) => { - this.$emit(client, { - id: client.id, - summary: `New Client: ${client.name}`, - ts: Date.parse(client.created), - }); - }); - }, - async activate() { - await this.everhour.emitClientCreatedEvent(this.projectId); - }, - async deactivate() { - await this.everhour.removeWebhook(); - }, - }, methods: { - async getClients() { - return this.everhour.listClients(); + ...common.methods, + getEventType() { + return [ + "api:client:created", + ]; + }, + getSummary(body) { + return `New Client: ${body.name}`; }, }, - async run() { - const clients = await this.getClients(); - clients.forEach((client) => { - this.$emit(client, { - id: client.id, - summary: `New Client: ${client.name}`, - ts: Date.parse(client.created), - }); - }); - }, + sampleEmit, }; diff --git a/components/everhour/sources/new-client-instant/test-event.mjs b/components/everhour/sources/new-client-instant/test-event.mjs new file mode 100644 index 0000000000000..912a48198cf59 --- /dev/null +++ b/components/everhour/sources/new-client-instant/test-event.mjs @@ -0,0 +1,7 @@ +export default { + "project": "project_slug", + "translated": 100, + "resource": "resource_slug", + "event": "translation_completed", + "language": "lang_code" +} \ No newline at end of file diff --git a/components/everhour/sources/new-task-instant/test-event.mjs b/components/everhour/sources/new-task-instant/test-event.mjs new file mode 100644 index 0000000000000..912a48198cf59 --- /dev/null +++ b/components/everhour/sources/new-task-instant/test-event.mjs @@ -0,0 +1,7 @@ +export default { + "project": "project_slug", + "translated": 100, + "resource": "resource_slug", + "event": "translation_completed", + "language": "lang_code" +} \ No newline at end of file diff --git a/components/everhour/sources/task-time-updated-instant/test-event.mjs b/components/everhour/sources/task-time-updated-instant/test-event.mjs new file mode 100644 index 0000000000000..912a48198cf59 --- /dev/null +++ b/components/everhour/sources/task-time-updated-instant/test-event.mjs @@ -0,0 +1,7 @@ +export default { + "project": "project_slug", + "translated": 100, + "resource": "resource_slug", + "event": "translation_completed", + "language": "lang_code" +} \ No newline at end of file From 190f0b7a6ff5623b58cd590c4caeea578fbce620 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 16 Oct 2024 16:19:28 -0300 Subject: [PATCH 3/6] pnpm update --- pnpm-lock.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cdf597432a0a3..3c5731854acad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3173,6 +3173,12 @@ importers: components/eventee: specifiers: {} + components/everhour: + specifiers: + '@pipedream/platform': ^3.0.3 + dependencies: + '@pipedream/platform': 3.0.3 + components/eversign: specifiers: '@pipedream/platform': ^1.1.1 From 509f341642bbea1528d1a0949935deda19ebb5d6 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 18 Oct 2024 11:22:24 -0300 Subject: [PATCH 4/6] [Components] everhour #13219 Sources - New Client (Instant) - New Task (Instant) - New Task Time Updated (Instant) Actions - Create Task - Start Timer - Stop Timer --- components/everhour/everhour.app.mjs | 6 +- components/everhour/sources/common/base.mjs | 4 +- .../new-client-instant/new-client-instant.mjs | 2 +- .../new-task-instant/new-task-instant.mjs | 54 +++++------------ .../task-time-updated-instant.mjs | 56 +++++------------- .../task-time-updated-instant/test-event.mjs | 58 +++++++++++++++++-- 6 files changed, 88 insertions(+), 92 deletions(-) diff --git a/components/everhour/everhour.app.mjs b/components/everhour/everhour.app.mjs index 8123076c57705..c87e3d01422f5 100644 --- a/components/everhour/everhour.app.mjs +++ b/components/everhour/everhour.app.mjs @@ -103,13 +103,11 @@ export default { _makeRequest({ $ = this, path, ...opts }) { - const config = { + return axios($, { url: this._baseUrl() + path, headers: this._headers(), ...opts, - }; - console.log("config: ", config); - return axios($, config); + }); }, listProjects(opts = {}) { return this._makeRequest({ diff --git a/components/everhour/sources/common/base.mjs b/components/everhour/sources/common/base.mjs index 845d80d702d3b..d3d4119973397 100644 --- a/components/everhour/sources/common/base.mjs +++ b/components/everhour/sources/common/base.mjs @@ -44,11 +44,11 @@ export default { }) { console.log("headers: ", headers); - if (headers["X-Hook-Secret"]) { + if (headers["x-hook-secret"]) { return this.http.respond({ status: 200, headers: { - "X-Hook-Secret": headers["X-Hook-Secret"], + "X-Hook-Secret": headers["x-hook-secret"], }, }); } diff --git a/components/everhour/sources/new-client-instant/new-client-instant.mjs b/components/everhour/sources/new-client-instant/new-client-instant.mjs index 0ce203722f513..5ccb8956d4608 100644 --- a/components/everhour/sources/new-client-instant/new-client-instant.mjs +++ b/components/everhour/sources/new-client-instant/new-client-instant.mjs @@ -6,7 +6,7 @@ export default { key: "everhour-new-client-instant", name: "New Client (Instant)", description: "Emit new event when a client is added.", - version: "0.0.{{ts}}", + version: "0.0.1", type: "source", dedupe: "unique", methods: { diff --git a/components/everhour/sources/new-task-instant/new-task-instant.mjs b/components/everhour/sources/new-task-instant/new-task-instant.mjs index af2edcbcba1b3..46911b10ee821 100644 --- a/components/everhour/sources/new-task-instant/new-task-instant.mjs +++ b/components/everhour/sources/new-task-instant/new-task-instant.mjs @@ -1,48 +1,24 @@ -import everhour from "../../everhour.app.mjs"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "everhour-new-task-instant", - name: "New Task Created", - description: "Emit new event when a task is created. [See the documentation](https://everhour.docs.apiary.io/)", - version: "0.0.{{ts}}", + name: "New Task Created (Instant)", + description: "Emit new event when a task is created.", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - everhour, - db: "$.service.db", - projectId: { - propDefinition: [ - everhour, - "projectId", - ], + methods: { + ...common.methods, + getEventType() { + return [ + "api:task:created", + ]; }, - }, - hooks: { - async deploy() { - const tasks = await this.everhour.getProjectTasks(this.projectId); - for (const task of tasks.slice(0, 50)) { - this.$emit(task, { - id: task.id, - summary: `New task: ${task.name}`, - ts: Date.parse(task.createdAt), - }); - } - }, - async activate() { - await this.everhour.emitTaskCreatedEvent(this.projectId); + getSummary(body) { + return `New Task Created: ${body.payload.data.id}`; }, - async deactivate() { - // Implement deactivation if an API endpoint exists - }, - }, - async run() { - const tasks = await this.everhour.getProjectTasks(this.projectId); - for (const task of tasks) { - this.$emit(task, { - id: task.id, - summary: `New task: ${task.name}`, - ts: Date.parse(task.createdAt), - }); - } }, + sampleEmit, }; diff --git a/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs b/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs index 95e4075302b38..f655e4258f87c 100644 --- a/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs +++ b/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs @@ -1,50 +1,24 @@ -import everhour from "../../everhour.app.mjs"; -import { axios } from "@pipedream/platform"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "everhour-task-time-updated-instant", - name: "Task Time Updated", - description: "Emit new event when a task's time spent is modified in Everhour. [See the documentation](https://everhour.docs.apiary.io/)", - version: "0.0.{{ts}}", + name: "New Task Time Updated (Instant)", + description: "Emit new event when a task's time spent is modified in Everhour.", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - everhour, - db: "$.service.db", - projectId: { - propDefinition: [ - everhour, - "projectId", - ], + methods: { + ...common.methods, + getEventType() { + return [ + "api:time:updated", + ]; }, - }, - hooks: { - async deploy() { - await this.everhour.emitTimeUpdatedEvent(this.projectId); - }, - async activate() { - await this.everhour.emitTimeUpdatedEvent(this.projectId); - }, - async deactivate() { - // Currently, Everhour API does not provide an endpoint to delete a webhook. - // If such an API becomes available, you should implement it here. + getSummary(body) { + return `Task Time Updated: ${body.payload.data.id}`; }, }, - async run() { - const events = await axios(this, { - method: "GET", - url: `${this.everhour._baseUrl()}/projects/${this.projectId}/time-record-events`, - headers: { - "Authorization": `Bearer ${this.everhour.$auth.api_key}`, - }, - }); - - for (const event of events) { - this.$emit(event, { - id: event.id.toString(), - summary: `Task Time Updated: ${event.id}`, - ts: new Date(event.spentAt).getTime(), - }); - } - }, + sampleEmit, }; diff --git a/components/everhour/sources/task-time-updated-instant/test-event.mjs b/components/everhour/sources/task-time-updated-instant/test-event.mjs index 912a48198cf59..5d4caa2116255 100644 --- a/components/everhour/sources/task-time-updated-instant/test-event.mjs +++ b/components/everhour/sources/task-time-updated-instant/test-event.mjs @@ -1,7 +1,55 @@ export default { - "project": "project_slug", - "translated": 100, - "resource": "resource_slug", - "event": "translation_completed", - "language": "lang_code" + "event": "api:time:updated", + "payload": { + "id": "ev:188193235916608", + "data": { + "user": 1362384, + "history": [ + { + "id": 370578249, + "time": 60, + "previousTime": 0, + "action": "TIMER", + "source": "internal", + "createdAt": "2024-10-18 13:58:23", + "createdBy": 1362384 + } + ], + "lockReasons": [], + "cost": 42, + "isLocked": false, + "manualTime": 0, + "id": 214588860, + "date": "2024-10-18", + "time": 60, + "timerTime": 60, + "pastDateTime": 0, + "task": { + "createdBy": 1362384, + "position": 4, + "projects": [ + "ev:188193235916605" + ], + "section": 1163621, + "comments": 0, + "completed": false, + "id": "ev:188193235916608", + "type": "task", + "name": "Project Management", + "status": "open", + "labels": [], + "createdAt": "2024-10-15 19:25:59", + "time": { + "total": 60, + "users": { + "1362384": 60 + }, + "timerTime": 60 + } + }, + "createdAt": "2024-10-18 13:58:23", + "costRate": 2500 + } + }, + "createdAt": "2024-10-18 13:59:20" } \ No newline at end of file From 72e7bfa00e13e9ba88af4c551426064ae361774a Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 22 Oct 2024 11:29:22 -0300 Subject: [PATCH 5/6] [Components] everhour #13219 Sources - New Client (Instant) - New Task (Instant) - New Task Time Updated (Instant) Actions - Create Task - Start Timer - Stop Timer --- components/everhour/sources/common/base.mjs | 13 +++------ .../new-client-instant/new-client-instant.mjs | 2 +- .../sources/new-client-instant/test-event.mjs | 28 +++++++++++++++---- .../new-task-instant/new-task-instant.mjs | 14 ++++++++++ .../sources/new-task-instant/test-event.mjs | 27 ++++++++++++++---- .../task-time-updated-instant.mjs | 14 ++++++++++ 6 files changed, 78 insertions(+), 20 deletions(-) diff --git a/components/everhour/sources/common/base.mjs b/components/everhour/sources/common/base.mjs index d3d4119973397..40eeb696c6bd3 100644 --- a/components/everhour/sources/common/base.mjs +++ b/components/everhour/sources/common/base.mjs @@ -8,12 +8,6 @@ export default { customResponse: true, }, db: "$.service.db", - projectId: { - propDefinition: [ - everhour, - "projectId", - ], - }, }, methods: { _getHookId() { @@ -22,6 +16,9 @@ export default { _setHookId(hookId) { this.db.set("hookId", hookId); }, + getExtraData() { + return {}; + }, }, hooks: { async activate() { @@ -29,7 +26,7 @@ export default { data: { targetUrl: this.http.endpoint, events: this.getEventType(), - project: this.projectId, + ...this.getExtraData(), }, }); this._setHookId(response.id); @@ -42,8 +39,6 @@ export default { async run({ body, headers, }) { - console.log("headers: ", headers); - if (headers["x-hook-secret"]) { return this.http.respond({ status: 200, diff --git a/components/everhour/sources/new-client-instant/new-client-instant.mjs b/components/everhour/sources/new-client-instant/new-client-instant.mjs index 5ccb8956d4608..5ed199c80ee61 100644 --- a/components/everhour/sources/new-client-instant/new-client-instant.mjs +++ b/components/everhour/sources/new-client-instant/new-client-instant.mjs @@ -17,7 +17,7 @@ export default { ]; }, getSummary(body) { - return `New Client: ${body.name}`; + return `New Client: ${body.payload.data.name}`; }, }, sampleEmit, diff --git a/components/everhour/sources/new-client-instant/test-event.mjs b/components/everhour/sources/new-client-instant/test-event.mjs index 912a48198cf59..31104ffe31bf7 100644 --- a/components/everhour/sources/new-client-instant/test-event.mjs +++ b/components/everhour/sources/new-client-instant/test-event.mjs @@ -1,7 +1,25 @@ export default { - "project": "project_slug", - "translated": 100, - "resource": "resource_slug", - "event": "translation_completed", - "language": "lang_code" + "event": "api:client:created", + "payload": { + "id": "9381500", + "data": { + "projects": [], + "id": 9381500, + "name": "Client Name", + "createdAt": "2024-10-22 14:25:29", + "lineItemMask": "%MEMBER% :: %PROJECT% :: for %PERIOD%", + "paymentDueDays": 0, + "reference": "", + "businessDetails": "", + "email": [ + "client@email.com" + ], + "invoicePublicNotes": "", + "excludedLabels": [], + "status": "active", + "enableResourcePlanner": false, + "favorite": false + } + }, + "createdAt": "2024-10-22 14:25:29" } \ No newline at end of file diff --git a/components/everhour/sources/new-task-instant/new-task-instant.mjs b/components/everhour/sources/new-task-instant/new-task-instant.mjs index 46911b10ee821..385cc425bfb68 100644 --- a/components/everhour/sources/new-task-instant/new-task-instant.mjs +++ b/components/everhour/sources/new-task-instant/new-task-instant.mjs @@ -9,8 +9,22 @@ export default { version: "0.0.1", type: "source", dedupe: "unique", + props: { + ...common.props, + projectId: { + propDefinition: [ + common.props.everhour, + "projectId", + ], + }, + }, methods: { ...common.methods, + getExtraData() { + return { + project: this.projectId, + }; + }, getEventType() { return [ "api:task:created", diff --git a/components/everhour/sources/new-task-instant/test-event.mjs b/components/everhour/sources/new-task-instant/test-event.mjs index 912a48198cf59..c324485881340 100644 --- a/components/everhour/sources/new-task-instant/test-event.mjs +++ b/components/everhour/sources/new-task-instant/test-event.mjs @@ -1,7 +1,24 @@ export default { - "project": "project_slug", - "translated": 100, - "resource": "resource_slug", - "event": "translation_completed", - "language": "lang_code" + "event": "api:task:created", + "payload": { + "id": "ev:188217209666811", + "data": { + "createdBy": 1362384, + "iteration": "Section Name", + "position": 5, + "projects": [ + "ev:188193235916605" + ], + "section": 1164091, + "comments": 0, + "completed": false, + "id": "ev:188217209666811", + "type": "task", + "name": "Task name", + "status": "open", + "labels": [], + "createdAt": "2024-10-18 14:01:36" + } + }, + "createdAt": "2024-10-18 14:01:36" } \ No newline at end of file diff --git a/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs b/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs index f655e4258f87c..4bf9034b807b3 100644 --- a/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs +++ b/components/everhour/sources/task-time-updated-instant/task-time-updated-instant.mjs @@ -9,8 +9,22 @@ export default { version: "0.0.1", type: "source", dedupe: "unique", + props: { + ...common.props, + projectId: { + propDefinition: [ + common.props.everhour, + "projectId", + ], + }, + }, methods: { ...common.methods, + getExtraData() { + return { + project: this.projectId, + }; + }, getEventType() { return [ "api:time:updated", From 66f962a5fef6b0f6af799a1ef2e855a62e472d4a Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 25 Oct 2024 17:27:47 -0300 Subject: [PATCH 6/6] fix status options --- components/everhour/common/constants.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/everhour/common/constants.mjs b/components/everhour/common/constants.mjs index 6807a98f3405f..5f6b8b3d178be 100644 --- a/components/everhour/common/constants.mjs +++ b/components/everhour/common/constants.mjs @@ -6,7 +6,7 @@ export const STATUS_OPTIONS = [ value: "open", }, { - label: "Close", - value: "close", + label: "Closed", + value: "closed", }, ];