From e27cc8639ad60cfc11409c7178f947ea3cad64b0 Mon Sep 17 00:00:00 2001 From: Lucas Caresia Date: Wed, 24 Aug 2022 13:56:02 -0300 Subject: [PATCH 1/7] Added actions and sourcs --- .../timecamp/actions/common/constants.mjs | 12 +++ .../actions/create-task/create-task.mjs | 70 ++++++++++++++++++ .../create-time-entry/create-time-entry.mjs | 73 +++++++++++++++++++ components/timecamp/package.json | 21 ++++++ .../timecamp/sources/new-task/new-task.mjs | 42 +++++++++++ .../sources/new-time-entry/new-time-entry.mjs | 65 +++++++++++++++++ components/timecamp/timecamp.app.mjs | 67 ++++++++++++++++- 7 files changed, 346 insertions(+), 4 deletions(-) create mode 100644 components/timecamp/actions/common/constants.mjs create mode 100644 components/timecamp/actions/create-task/create-task.mjs create mode 100644 components/timecamp/actions/create-time-entry/create-time-entry.mjs create mode 100644 components/timecamp/package.json create mode 100644 components/timecamp/sources/new-task/new-task.mjs create mode 100644 components/timecamp/sources/new-time-entry/new-time-entry.mjs diff --git a/components/timecamp/actions/common/constants.mjs b/components/timecamp/actions/common/constants.mjs new file mode 100644 index 0000000000000..b1a38857cc490 --- /dev/null +++ b/components/timecamp/actions/common/constants.mjs @@ -0,0 +1,12 @@ +export default { + BUDGET_UNITS: [ + { + label: "Hours", + value: "hours", + }, + { + label: "Fee", + value: "fee", + }, + ], +}; diff --git a/components/timecamp/actions/create-task/create-task.mjs b/components/timecamp/actions/create-task/create-task.mjs new file mode 100644 index 0000000000000..950ef8287584e --- /dev/null +++ b/components/timecamp/actions/create-task/create-task.mjs @@ -0,0 +1,70 @@ +import timecamp from "../../timecamp.app.mjs"; +import constants from "../common/constants.mjs"; + +export default { + name: "Create Task", + version: "0.0.4", + key: "timecamp-create-task", + description: "Creates a task. [See docs here](https://developer.timecamp.com/docs/timecamp-api/b3A6NTg5ODUxMA-create-new-task)", + type: "action", + props: { + timecamp, + name: { + label: "Name", + description: "The name of the task", + type: "string", + }, + note: { + label: "Description", + description: "The description of the task", + type: "string", + optional: true, + }, + tags: { + label: "Tags", + description: "The tags of the task. E.g. `IT, R&D`", + type: "string", + optional: true, + }, + billable: { + label: "Billable", + description: "The task is billable", + type: "boolean", + optional: true, + }, + budgetUnit: { + label: "Budget Unit", + description: "The task is billable", + type: "string", + options: constants.BUDGET_UNITS, + optional: true, + }, + parentId: { + title: "Parent Task ID", + propDefinition: [ + timecamp, + "taskId", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.timecamp.createTask({ + $, + data: { + name: this.name, + note: this.note, + tags: this.tags, + billable: this.billable, + budget_unit: this.budgetUnit, + parent_id: this.parentId, + }, + }); + + if (response) { + $.export("$summary", `Successfully created task with id ${Object.values(response)[0].task_id}`); + } + + return response; + }, +}; diff --git a/components/timecamp/actions/create-time-entry/create-time-entry.mjs b/components/timecamp/actions/create-time-entry/create-time-entry.mjs new file mode 100644 index 0000000000000..195ecd0c89e92 --- /dev/null +++ b/components/timecamp/actions/create-time-entry/create-time-entry.mjs @@ -0,0 +1,73 @@ +import { ConfigurationError } from "@pipedream/platform"; +import timecamp from "../../timecamp.app.mjs"; + +export default { + name: "Create Time Entry", + version: "0.0.1", + key: "timecamp-create-timeentry", + description: "Creates a time entry. [See docs here](https://developer.timecamp.com/docs/timecamp-api/b3A6NTY2NDIyOQ-create-time-entry)", + type: "action", + props: { + timecamp, + date: { + label: "Date", + description: "The date will be entry create. E.g. `2021-03-02`", + type: "string", + optional: true, + }, + start: { + label: "Start Time", + description: "The entry start time. E.g. `2020-02-02 13:05:47`", + type: "string", + optional: true, + }, + end: { + label: "End Time", + description: "The entry end time. E.g. `2020-02-02 15:21:31`", + type: "string", + optional: true, + }, + duration: { + label: "Duration", + description: "The time entry duration in seconds if don't have `Start` and `End`. E.g. `7200`", + type: "integer", + optional: true, + }, + note: { + label: "Description", + description: "The description of the time entry", + type: "string", + optional: true, + }, + taskId: { + propDefinition: [ + timecamp, + "taskId", + ], + optional: true, + }, + }, + async run({ $ }) { + if (!this.duration || (this.start && !this.end)) { + throw new ConfigurationError("Is needed `Start` and `End` or `Duration`"); + } + + const response = await this.timecamp.createTimeEntry({ + $, + data: { + date: this.date, + start: this.start, + end: this.end, + duration: this.duration, + note: this.note, + task_id: this.taskId, + }, + }); + + if (response) { + $.export("$summary", `Successfully created time entry with id ${response.entry_id}`); + } + + return response; + }, +}; diff --git a/components/timecamp/package.json b/components/timecamp/package.json new file mode 100644 index 0000000000000..c27f122b87a45 --- /dev/null +++ b/components/timecamp/package.json @@ -0,0 +1,21 @@ +{ + "name": "@pipedream/timecamp", + "version": "0.0.1", + "description": "Pipedream TimeCamp Components", + "main": "timecamp.app.mjs", + "keywords": [ + "pipedream", + "timecamp" + ], + "homepage": "https://pipedream.com/apps/timecamp", + "author": "Pipedream (https://pipedream.com/)", + "license": "MIT", + "gitHead": "e12480b94cc03bed4808ebc6b13e7fdb3a1ba535", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^1.1.0", + "dayjs": "^1.11.5" + } +} diff --git a/components/timecamp/sources/new-task/new-task.mjs b/components/timecamp/sources/new-task/new-task.mjs new file mode 100644 index 0000000000000..060aeaea7d678 --- /dev/null +++ b/components/timecamp/sources/new-task/new-task.mjs @@ -0,0 +1,42 @@ +import timecamp from "../../timecamp.app.mjs"; + +export default { + name: "New Task", + version: "0.0.1", + key: "timecamp-new-task", + description: "Emit new event on each created task.", + type: "source", + dedupe: "unique", + props: { + timecamp, + db: "$.service.db", + timer: { + type: "$.interface.timer", + static: { + intervalSeconds: 15 * 60, // 15 minutes + }, + }, + }, + methods: { + emitEvent(data) { + this.$emit(data, { + id: data.task_id, + summary: `New task with id ${data.task_id}`, + ts: Date.parse(data.add_date), + }); + }, + async emitAllTasks() { + const tasks = await this.timecamp.getTasks({}); + + tasks.reverse().forEach(this.emitEvent); + }, + }, + hooks: { + async deploy() { + await this.emitAllTasks(); + }, + }, + async run() { + await this.emitAllTasks(); + }, +}; diff --git a/components/timecamp/sources/new-time-entry/new-time-entry.mjs b/components/timecamp/sources/new-time-entry/new-time-entry.mjs new file mode 100644 index 0000000000000..5a074221cc1cd --- /dev/null +++ b/components/timecamp/sources/new-time-entry/new-time-entry.mjs @@ -0,0 +1,65 @@ +import timecamp from "../../timecamp.app.mjs"; +import dayjs from "dayjs"; + +export default { + name: "New Time Entry", + version: "0.0.1", + key: "timecamp-new-time-entry", + description: "Emit new event on each created time entry.", + type: "source", + dedupe: "unique", + props: { + timecamp, + db: "$.service.db", + timer: { + type: "$.interface.timer", + static: { + intervalSeconds: 15 * 60, // 15 minutes + }, + }, + }, + methods: { + _getLastSyncDate() { + return this.db.get("lastSyncDate"); + }, + _setLastSyncDate(lastSyncDate) { + return this.db.set("lastSyncDate", lastSyncDate); + }, + emitEvent(data) { + this.$emit(data, { + id: data.id, + summary: `New task with id ${data.id}`, + ts: Date.parse(data.add_date), + }); + }, + async emitTimeEntries() { + const lastSyncDate = this._getLastSyncDate(); + + const timeEntries = await this.timecamp.getTimeEntries({ + params: { + from: lastSyncDate, + to: dayjs().format("YYYY-MM-DD"), + }, + }); + + timeEntries.reverse().forEach(this.emitEvent); + }, + }, + hooks: { + async deploy() { + const lastSyncDate = dayjs().subtract(1, "month") + .format("YYYY-MM-DD"); + + this._setLastSyncDate(lastSyncDate); + + await this.emitTimeEntries(); + }, + }, + async run() { + await this.emitTimeEntries(); + + const lastSyncDate = dayjs().format("YYYY-MM-DD"); + + this._setLastSyncDate(lastSyncDate); + }, +}; diff --git a/components/timecamp/timecamp.app.mjs b/components/timecamp/timecamp.app.mjs index 3c260e481b07e..ff5a56ec01e54 100644 --- a/components/timecamp/timecamp.app.mjs +++ b/components/timecamp/timecamp.app.mjs @@ -1,11 +1,70 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "timecamp", - propDefinitions: {}, + propDefinitions: { + taskId: { + label: "Task ID", + description: "The task ID", + type: "string", + async options() { + const tasks = await this.getTasks(); + + return tasks.map((task) => ({ + label: task.name, + value: task.task_id, + })); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _apiToken() { + return this.$auth.api_token; + }, + _apiUrl() { + return "https://www.timecamp.com/third_party/api"; + }, + async _makeRequest({ + $ = this, path, ...args + }) { + return axios($, { + url: `${this._apiUrl()}${path}`, + headers: { + Authorization: this._apiToken(), + }, + ...args, + }); + }, + async getTasks({ ...args } = {}) { + const response = await this._makeRequest({ + path: "/tasks", + ...args, + }); + + return Object.values(response); + }, + async getTimeEntries({ ...args } = {}) { + const response = await this._makeRequest({ + path: "/entries", + ...args, + }); + + return Object.values(response); + }, + async createTask({ ...args } = {}) { + return this._makeRequest({ + path: "/tasks", + method: "post", + ...args, + }); + }, + async createTimeEntry({ ...args } = {}) { + return this._makeRequest({ + path: "/entries", + method: "post", + ...args, + }); }, }, }; From 306930dfdc56e8c9b4efe4fba954ce991ad11dae Mon Sep 17 00:00:00 2001 From: Lucas Caresia Date: Wed, 24 Aug 2022 13:56:56 -0300 Subject: [PATCH 2/7] pnpm --- pnpm-lock.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f3fdb8bb7b7dd..b87fba11168a4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -832,6 +832,9 @@ importers: components/grist: specifiers: {} + components/growsurf: + specifiers: {} + components/guru: specifiers: {} @@ -1826,6 +1829,14 @@ importers: dependencies: '@pipedream/platform': 0.10.0 + components/timecamp: + specifiers: + '@pipedream/platform': ^1.1.0 + dayjs: ^1.11.5 + dependencies: + '@pipedream/platform': 1.1.1 + dayjs: 1.11.5 + components/tmetric: specifiers: {} @@ -10133,6 +10144,10 @@ packages: resolution: {integrity: sha512-xxwlswWOlGhzgQ4TKzASQkUhqERI3egRNqgV4ScR8wlANA/A9tZ7miXa44vTTKEq5l7vWoL5G57bG3zA+Kow0A==} dev: false + /dayjs/1.11.5: + resolution: {integrity: sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==} + dev: false + /debug/2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: From 97e4fe3ab0de7aa370326c518856a1390783cab4 Mon Sep 17 00:00:00 2001 From: feyzullah Date: Wed, 24 Aug 2022 21:21:50 +0300 Subject: [PATCH 3/7] Update components/timecamp/actions/create-task/create-task.mjs 0.0.4 => 0.0.1 --- components/timecamp/actions/create-task/create-task.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/timecamp/actions/create-task/create-task.mjs b/components/timecamp/actions/create-task/create-task.mjs index 950ef8287584e..e67cd51bc6fcd 100644 --- a/components/timecamp/actions/create-task/create-task.mjs +++ b/components/timecamp/actions/create-task/create-task.mjs @@ -3,7 +3,7 @@ import constants from "../common/constants.mjs"; export default { name: "Create Task", - version: "0.0.4", + version: "0.0.1", key: "timecamp-create-task", description: "Creates a task. [See docs here](https://developer.timecamp.com/docs/timecamp-api/b3A6NTg5ODUxMA-create-new-task)", type: "action", From f430e3c9517e7b448b3f80c08153bfeb9fba3d46 Mon Sep 17 00:00:00 2001 From: Lucas Caresia Date: Thu, 25 Aug 2022 17:00:46 -0300 Subject: [PATCH 4/7] Fixed create-task action --- components/timecamp/actions/create-task/create-task.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/timecamp/actions/create-task/create-task.mjs b/components/timecamp/actions/create-task/create-task.mjs index e67cd51bc6fcd..fe297e238030d 100644 --- a/components/timecamp/actions/create-task/create-task.mjs +++ b/components/timecamp/actions/create-task/create-task.mjs @@ -3,7 +3,7 @@ import constants from "../common/constants.mjs"; export default { name: "Create Task", - version: "0.0.1", + version: "0.0.7", key: "timecamp-create-task", description: "Creates a task. [See docs here](https://developer.timecamp.com/docs/timecamp-api/b3A6NTg5ODUxMA-create-new-task)", type: "action", @@ -40,7 +40,7 @@ export default { optional: true, }, parentId: { - title: "Parent Task ID", + label: "Parent Task ID", propDefinition: [ timecamp, "taskId", From 5516483c903d3ff55d4ff8370d9212c637e10a99 Mon Sep 17 00:00:00 2001 From: Lucas Caresia Date: Thu, 25 Aug 2022 17:05:45 -0300 Subject: [PATCH 5/7] Fixed create-time-entry action --- components/timecamp/actions/create-task/create-task.mjs | 2 +- .../timecamp/actions/create-time-entry/create-time-entry.mjs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/timecamp/actions/create-task/create-task.mjs b/components/timecamp/actions/create-task/create-task.mjs index fe297e238030d..c52b32e8b68f9 100644 --- a/components/timecamp/actions/create-task/create-task.mjs +++ b/components/timecamp/actions/create-task/create-task.mjs @@ -3,7 +3,7 @@ import constants from "../common/constants.mjs"; export default { name: "Create Task", - version: "0.0.7", + version: "0.0.1", key: "timecamp-create-task", description: "Creates a task. [See docs here](https://developer.timecamp.com/docs/timecamp-api/b3A6NTg5ODUxMA-create-new-task)", type: "action", diff --git a/components/timecamp/actions/create-time-entry/create-time-entry.mjs b/components/timecamp/actions/create-time-entry/create-time-entry.mjs index 195ecd0c89e92..f5596344f9ec9 100644 --- a/components/timecamp/actions/create-time-entry/create-time-entry.mjs +++ b/components/timecamp/actions/create-time-entry/create-time-entry.mjs @@ -3,7 +3,7 @@ import timecamp from "../../timecamp.app.mjs"; export default { name: "Create Time Entry", - version: "0.0.1", + version: "0.1.3", key: "timecamp-create-timeentry", description: "Creates a time entry. [See docs here](https://developer.timecamp.com/docs/timecamp-api/b3A6NTY2NDIyOQ-create-time-entry)", type: "action", @@ -48,7 +48,7 @@ export default { }, }, async run({ $ }) { - if (!this.duration || (this.start && !this.end)) { + if (!this.duration && (!this.start || !this.end)) { throw new ConfigurationError("Is needed `Start` and `End` or `Duration`"); } From 6a92b044bd3bc051e398c2f3350b3542f10a86ec Mon Sep 17 00:00:00 2001 From: Lucas Caresia Date: Thu, 25 Aug 2022 17:06:34 -0300 Subject: [PATCH 6/7] Fixed new-time-entry source --- components/timecamp/sources/new-time-entry/new-time-entry.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/timecamp/sources/new-time-entry/new-time-entry.mjs b/components/timecamp/sources/new-time-entry/new-time-entry.mjs index 5a074221cc1cd..4ff135b93d073 100644 --- a/components/timecamp/sources/new-time-entry/new-time-entry.mjs +++ b/components/timecamp/sources/new-time-entry/new-time-entry.mjs @@ -28,7 +28,7 @@ export default { emitEvent(data) { this.$emit(data, { id: data.id, - summary: `New task with id ${data.id}`, + summary: `New time entry with id ${data.id}`, ts: Date.parse(data.add_date), }); }, From 3b0b88ba70b7e97ade355b9b3036da5cb149e1af Mon Sep 17 00:00:00 2001 From: Lucas Caresia Date: Thu, 25 Aug 2022 21:19:28 -0300 Subject: [PATCH 7/7] Update create-task.mjs --- components/timecamp/actions/create-task/create-task.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/timecamp/actions/create-task/create-task.mjs b/components/timecamp/actions/create-task/create-task.mjs index c52b32e8b68f9..0be59ac0ee518 100644 --- a/components/timecamp/actions/create-task/create-task.mjs +++ b/components/timecamp/actions/create-task/create-task.mjs @@ -34,7 +34,7 @@ export default { }, budgetUnit: { label: "Budget Unit", - description: "The task is billable", + description: "The budget unit of the task", type: "string", options: constants.BUDGET_UNITS, optional: true,