From 8a5f65c8d7b6380be003a0bb242c37784728cd3f Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Tue, 13 May 2025 13:05:33 -0400 Subject: [PATCH 1/4] new actions --- components/wakatime/.gitignore | 3 - .../fetch-daily-heartbeats.mjs | 30 ++++ .../actions/list-projects/list-projects.mjs | 30 ++++ .../log-coding-activity.mjs | 146 ++++++++++++++++++ components/wakatime/app/wakatime.app.ts | 13 -- components/wakatime/package.json | 8 +- components/wakatime/wakatime.app.mjs | 55 +++++++ 7 files changed, 266 insertions(+), 19 deletions(-) delete mode 100644 components/wakatime/.gitignore create mode 100644 components/wakatime/actions/fetch-daily-heartbeats/fetch-daily-heartbeats.mjs create mode 100644 components/wakatime/actions/list-projects/list-projects.mjs create mode 100644 components/wakatime/actions/log-coding-activity/log-coding-activity.mjs delete mode 100644 components/wakatime/app/wakatime.app.ts create mode 100644 components/wakatime/wakatime.app.mjs diff --git a/components/wakatime/.gitignore b/components/wakatime/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/wakatime/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/wakatime/actions/fetch-daily-heartbeats/fetch-daily-heartbeats.mjs b/components/wakatime/actions/fetch-daily-heartbeats/fetch-daily-heartbeats.mjs new file mode 100644 index 0000000000000..5a03cd50e9d01 --- /dev/null +++ b/components/wakatime/actions/fetch-daily-heartbeats/fetch-daily-heartbeats.mjs @@ -0,0 +1,30 @@ +import wakatime from "../../wakatime.app.mjs"; + +export default { + key: "wakatime-fetch-daily-heartbeats", + name: "Fetch Daily Heartbeats", + description: "Fetch your daily coding activity from WakaTime. [See the documentation](https://wakatime.com/developers#heartbeats)", + version: "0.0.1", + type: "action", + props: { + wakatime, + date: { + type: "string", + label: "Date", + description: "The date to fetch heartbeats for (YYYY-MM-DD)", + }, + }, + async run({ $ }) { + const response = await this.wakatime.listHeartbeats({ + $, + params: { + date: this.date, + }, + }); + + $.export("$summary", `Retrieved ${response.data.length} heartbeat${response.data.length === 1 + ? "" + : "s"} for ${this.date}`); + return response; + }, +}; diff --git a/components/wakatime/actions/list-projects/list-projects.mjs b/components/wakatime/actions/list-projects/list-projects.mjs new file mode 100644 index 0000000000000..b9e4de02c66cf --- /dev/null +++ b/components/wakatime/actions/list-projects/list-projects.mjs @@ -0,0 +1,30 @@ +import wakatime from "../../wakatime.app.mjs"; + +export default { + key: "wakatime-list-projects", + name: "List Projects", + description: "List all your WakaTime projects. [See the documentation](https://wakatime.com/developers#projects)", + version: "0.0.1", + type: "action", + props: { + wakatime, + q: { + type: "string", + label: "Query", + description: "Filter project names by a search term", + optional: true, + }, + }, + async run({ $ }) { + const { data } = await this.wakatime.listProjects({ + $, + params: { + q: this.q, + }, + }); + $.export("$summary", `Successfully retrieved ${data.length} project${data.length === 1 + ? "" + : "s"}`); + return data; + }, +}; diff --git a/components/wakatime/actions/log-coding-activity/log-coding-activity.mjs b/components/wakatime/actions/log-coding-activity/log-coding-activity.mjs new file mode 100644 index 0000000000000..ff167112c33ea --- /dev/null +++ b/components/wakatime/actions/log-coding-activity/log-coding-activity.mjs @@ -0,0 +1,146 @@ +import wakatime from "../../wakatime.app.mjs"; + +export default { + key: "wakatime-log-coding-activity", + name: "Log Coding Activity", + description: "Log coding activity to WakaTime. [See the documentation](https://wakatime.com/developers#heartbeats)", + version: "0.0.1", + type: "action", + props: { + wakatime, + entity: { + type: "string", + label: "Entity", + description: "The entity heartbeat is logging time against, such as an absolute file path or domain", + }, + type: { + type: "string", + label: "Type", + description: "Type of entity (file, app or domain)", + default: "file", + options: [ + "file", + "app", + "domain", + ], + }, + time: { + type: "string", + label: "Time", + description: "UNIX epoch timestamp; numbers after decimal point are fractions of a second", + }, + category: { + type: "string", + label: "Category", + description: "The category for this activity", + optional: true, + options: [ + "coding", + "building", + "indexing", + "debugging", + "browsing", + "running tests", + "writing tests", + "manual testing", + "writing docs", + "communicating", + "code reviewing", + "researching", + "learning", + "designing", + ], + }, + project: { + propDefinition: [ + wakatime, + "project", + ], + }, + projectRootCount: { + type: "integer", + label: "Project Root Count", + description: "Count of the number of folders in the project root path (optional); for ex: if the project folder is /Users/user/projects/wakatime and the entity path is /Users/user/projects/wakatime/models/user.py then the project_root_count is 5 and the relative entity path after removing 5 prefix folders is models/user.py", + optional: true, + }, + branch: { + type: "string", + label: "Branch", + description: "Branch name", + optional: true, + }, + language: { + type: "string", + label: "Language", + description: "Programming language", + optional: true, + }, + dependencies: { + type: "string", + label: "Dependencies", + description: "Comma separated list of dependencies", + optional: true, + }, + lines: { + type: "integer", + label: "Lines", + description: "Total number of lines in the entity (when entity type is file)", + optional: true, + }, + lineAdditions: { + type: "integer", + label: "Line Additions", + description: "Number of lines added since last heartbeat in the current file", + optional: true, + }, + lineDeletions: { + type: "integer", + label: "Line Deletions", + description: "Number of lines removed since last heartbeat in the current file", + optional: true, + }, + lineNo: { + type: "integer", + label: "Line Number", + description: "Current line row number of cursor with the first line starting at 1", + optional: true, + }, + cursorPos: { + type: "integer", + label: "Cursor Position", + description: "Current cursor column position starting from 1", + optional: true, + }, + isWrite: { + type: "boolean", + label: "Is Write", + description: "Whether this heartbeat was triggered from writing to a file", + optional: true, + }, + }, + async run({ $ }) { + const { data } = await this.wakatime.createHeartbeat({ + $, + data: { + entity: this.entity, + type: this.type, + category: this.category, + time: this.time, + project: this.project, + project_root_count: this.projectRootCount, + branch: this.branch, + language: this.language, + dependencies: this.dependencies, + lines: this.lines, + line_additions: this.lineAdditions, + line_deletions: this.lineDeletions, + line_no: this.lineNo, + cursor_pos: this.cursorPos, + is_write: this.isWrite, + }, + }); + + $.export("$summary", `Successfully logged coding activity for ${this.entity}`); + return data; + }, +}; diff --git a/components/wakatime/app/wakatime.app.ts b/components/wakatime/app/wakatime.app.ts deleted file mode 100644 index 29f7fa5f246b6..0000000000000 --- a/components/wakatime/app/wakatime.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "wakatime", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); diff --git a/components/wakatime/package.json b/components/wakatime/package.json index 5dd0a0aa554b4..ee720abe13ebc 100644 --- a/components/wakatime/package.json +++ b/components/wakatime/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/wakatime", - "version": "0.0.3", + "version": "0.1.0", "description": "Pipedream WakaTime Components", - "main": "dist/app/wakatime.app.mjs", + "main": "wakatime.app.mjs", "keywords": [ "pipedream", "wakatime" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/wakatime", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/wakatime/wakatime.app.mjs b/components/wakatime/wakatime.app.mjs new file mode 100644 index 0000000000000..abd8bbfcea6f8 --- /dev/null +++ b/components/wakatime/wakatime.app.mjs @@ -0,0 +1,55 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "wakatime", + propDefinitions: { + project: { + type: "string", + label: "Project", + description: "The name of a project", + optional: true, + async options() { + const { data } = await this.listProjects(); + return data?.map(({ name }) => name) || []; + }, + }, + }, + methods: { + _baseUrl() { + return "https://wakatime.com/api/v1"; + }, + _makeRequest({ + $ = this, + path, + ...otherOpts + }) { + return axios($, { + url: `${this._baseUrl()}/${path}`, + headers: { + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }, + ...otherOpts, + }); + }, + listProjects(opts = {}) { + return this._makeRequest({ + path: "/users/current/projects", + ...opts, + }); + }, + listHeartbeats(opts = {}) { + return this._makeRequest({ + path: "/users/current/heartbeats", + ...opts, + }); + }, + createHeartbeat(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/users/current/heartbeats", + ...opts, + }); + }, + }, +}; From 236eed434191b57e98d11bc95cc5cf9c75d545c1 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Tue, 13 May 2025 14:44:55 -0400 Subject: [PATCH 2/4] new sources --- .../actions/list-projects/list-projects.mjs | 21 ++- .../log-coding-activity.mjs | 9 +- components/wakatime/sources/common/base.mjs | 92 ++++++++++ .../new-commit-created/new-commit-created.mjs | 39 +++++ .../sources/new-commit-created/test-event.mjs | 32 ++++ .../new-goal-created/new-goal-created.mjs | 22 +++ .../sources/new-goal-created/test-event.mjs | 160 ++++++++++++++++++ .../new-project-created.mjs | 22 +++ .../new-project-created/test-event.mjs | 16 ++ components/wakatime/wakatime.app.mjs | 69 +++++++- 10 files changed, 470 insertions(+), 12 deletions(-) create mode 100644 components/wakatime/sources/common/base.mjs create mode 100644 components/wakatime/sources/new-commit-created/new-commit-created.mjs create mode 100644 components/wakatime/sources/new-commit-created/test-event.mjs create mode 100644 components/wakatime/sources/new-goal-created/new-goal-created.mjs create mode 100644 components/wakatime/sources/new-goal-created/test-event.mjs create mode 100644 components/wakatime/sources/new-project-created/new-project-created.mjs create mode 100644 components/wakatime/sources/new-project-created/test-event.mjs diff --git a/components/wakatime/actions/list-projects/list-projects.mjs b/components/wakatime/actions/list-projects/list-projects.mjs index b9e4de02c66cf..cdd94751957fb 100644 --- a/components/wakatime/actions/list-projects/list-projects.mjs +++ b/components/wakatime/actions/list-projects/list-projects.mjs @@ -16,15 +16,24 @@ export default { }, }, async run({ $ }) { - const { data } = await this.wakatime.listProjects({ - $, - params: { - q: this.q, + const results = this.wakatime.paginate({ + fn: this.wakatime.listProjects, + args: { + $, + params: { + q: this.q, + }, }, }); - $.export("$summary", `Successfully retrieved ${data.length} project${data.length === 1 + + const projects = []; + for await (const project of results) { + projects.push(project); + } + + $.export("$summary", `Successfully retrieved ${projects.length} project${projects.length === 1 ? "" : "s"}`); - return data; + return projects; }, }; diff --git a/components/wakatime/actions/log-coding-activity/log-coding-activity.mjs b/components/wakatime/actions/log-coding-activity/log-coding-activity.mjs index ff167112c33ea..f17ce992cb3ad 100644 --- a/components/wakatime/actions/log-coding-activity/log-coding-activity.mjs +++ b/components/wakatime/actions/log-coding-activity/log-coding-activity.mjs @@ -56,6 +56,7 @@ export default { wakatime, "project", ], + optional: true, }, projectRootCount: { type: "integer", @@ -70,10 +71,10 @@ export default { optional: true, }, language: { - type: "string", - label: "Language", - description: "Programming language", - optional: true, + propDefinition: [ + wakatime, + "language", + ], }, dependencies: { type: "string", diff --git a/components/wakatime/sources/common/base.mjs b/components/wakatime/sources/common/base.mjs new file mode 100644 index 0000000000000..64f1fe73d7a0b --- /dev/null +++ b/components/wakatime/sources/common/base.mjs @@ -0,0 +1,92 @@ +import wakatime from "../../wakatime.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + wakatime, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + getArgs() { + return {}; + }, + getResourceKey() { + return "data"; + }, + getTsField() { + return "created_at"; + }, + generateMeta(item) { + return { + id: item.id, + summary: this.getSummary(item), + ts: Date.parse(item[this.getTsField()]), + }; + }, + async processEvent(max) { + const lastTs = this._getLastTs(); + let maxTs = lastTs; + + const resourceFn = this.getResourceFn(); + const args = this.getArgs(); + const resourceKey = this.getResourceKey(); + const tsField = this.getTsField(); + + const results = this.wakatime.paginate({ + fn: resourceFn, + args, + resourceKey, + }); + + let items = []; + for await (const item of results) { + const ts = Date.parse(item[tsField]); + if (ts > lastTs) { + items.push(item); + maxTs = Math.max(maxTs, ts); + } + } + + if (!items.length) { + return; + } + + if (max && items.length >= max) { + items = items.slice(0, max); + } + + this._setLastTs(maxTs); + + for (const item of items.reverse()) { + const meta = this.generateMeta(item); + this.$emit(item, meta); + } + }, + getResourceFn() { + throw new Error("getResourceFn is not implemented"); + }, + getSummary() { + throw new Error("getSummary is not implemented"); + }, + }, + hooks: { + async deploy() { + await this.processEvent(25); + }, + }, + async run() { + await this.processEvent(); + }, +}; diff --git a/components/wakatime/sources/new-commit-created/new-commit-created.mjs b/components/wakatime/sources/new-commit-created/new-commit-created.mjs new file mode 100644 index 0000000000000..cd30289caab3e --- /dev/null +++ b/components/wakatime/sources/new-commit-created/new-commit-created.mjs @@ -0,0 +1,39 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "wakatime-new-commit-created", + name: "New Commit Created", + description: "Emit new event when a new commit is created in WakaTime. [See the documentation](https://wakatime.com/developers#commits)", + version: "0.0.1}", + type: "source", + dedupe: "unique", + props: { + ...common.props, + project: { + propDefinition: [ + common.props.wakatime, + "project", + ], + }, + }, + methods: { + ...common.methods, + getResourceFn() { + return this.wakatime.listCommits; + }, + getArgs() { + return { + project: this.project, + }; + }, + getResourceKey() { + return "commits"; + }, + getSummary(item) { + return `New Commit Created: ${item.message}`; + }, + }, + sampleEmit, +}; diff --git a/components/wakatime/sources/new-commit-created/test-event.mjs b/components/wakatime/sources/new-commit-created/test-event.mjs new file mode 100644 index 0000000000000..34fef5d9c55d8 --- /dev/null +++ b/components/wakatime/sources/new-commit-created/test-event.mjs @@ -0,0 +1,32 @@ +export default { + "id": "", + "hash": "", + "branch": "master", + "ref": "refs/heads/master", + "author_date": "2015-08-09T05:25:54Z", + "author_avatar_url": "", + "author_url": "", + "author_html_url": "", + "author_username": "", + "author_id": null, + "author_name": "", + "author_email": "", + "committer_date": "2015-08-09T05:25:54Z", + "committer_avatar_url": "", + "committer_url": "", + "committer_html_url": "", + "committer_username": "", + "committer_name": "", + "committer_email": "", + "message": "First commit", + "url": "", + "html_url": "", + "created_at": "2025-05-13T18:29:30Z", + "human_readable_total_with_seconds": "unknown", + "human_readable_natural_date": "", + "human_readable_date": "Aug 9, 2015", + "truncated_hash": "ab9e353", + "is_author_found": true, + "human_readable_total": "unknown", + "total_seconds": 0 + } \ No newline at end of file diff --git a/components/wakatime/sources/new-goal-created/new-goal-created.mjs b/components/wakatime/sources/new-goal-created/new-goal-created.mjs new file mode 100644 index 0000000000000..df46841751fb3 --- /dev/null +++ b/components/wakatime/sources/new-goal-created/new-goal-created.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "wakatime-new-goal-created", + name: "New Goal Created", + description: "Emit new event when a new goal is created in WakaTime. [See the documentation](https://wakatime.com/developers#goals)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceFn() { + return this.wakatime.listGoals; + }, + getSummary(item) { + return `New Goal Created: ${item.title}`; + }, + }, + sampleEmit, +}; diff --git a/components/wakatime/sources/new-goal-created/test-event.mjs b/components/wakatime/sources/new-goal-created/test-event.mjs new file mode 100644 index 0000000000000..0b1831a58cb27 --- /dev/null +++ b/components/wakatime/sources/new-goal-created/test-event.mjs @@ -0,0 +1,160 @@ +export default { + "id": "", + "type": "coding", + "custom_title": null, + "seconds": 3600, + "delta": "day", + "ignore_zero_days": true, + "improve_by_percent": null, + "is_inverse": false, + "is_enabled": true, + "snooze_until": null, + "modified_at": null, + "created_at": "2025-05-13T18:22:16Z", + "chart_data": [ + { + "range": { + "start": "2025-05-07T04:00:00Z", + "end": "2025-05-08T03:59:59Z", + "date": "2025-05-07", + "text": "Wed May 07", + "timezone": "America/Detroit" + }, + "actual_seconds": 0, + "actual_seconds_text": "0 secs", + "goal_seconds": 3600, + "goal_seconds_text": "1 hr", + "range_status": "ignored", + "range_status_reason": "coded 0 secs which is 1 hr less than your daily goal, but this day doesn’t count because it started before your goal was created", + "range_status_reason_short": "0s (1h less than goal) (before goal created)" + }, + { + "range": { + "start": "2025-05-08T04:00:00Z", + "end": "2025-05-09T03:59:59Z", + "date": "2025-05-08", + "text": "Thu May 08", + "timezone": "America/Detroit" + }, + "actual_seconds": 0, + "actual_seconds_text": "0 secs", + "goal_seconds": 3600, + "goal_seconds_text": "1 hr", + "range_status": "ignored", + "range_status_reason": "coded 0 secs which is 1 hr less than your daily goal, but this day doesn’t count because it started before your goal was created", + "range_status_reason_short": "0s (1h less than goal) (before goal created)" + }, + { + "range": { + "start": "2025-05-09T04:00:00Z", + "end": "2025-05-10T03:59:59Z", + "date": "2025-05-09", + "text": "Fri May 09", + "timezone": "America/Detroit" + }, + "actual_seconds": 0, + "actual_seconds_text": "0 secs", + "goal_seconds": 3600, + "goal_seconds_text": "1 hr", + "range_status": "ignored", + "range_status_reason": "coded 0 secs which is 1 hr less than your daily goal, but this day doesn’t count because it started before your goal was created", + "range_status_reason_short": "0s (1h less than goal) (before goal created)" + }, + { + "range": { + "start": "2025-05-10T04:00:00Z", + "end": "2025-05-11T03:59:59Z", + "date": "2025-05-10", + "text": "Sat May 10", + "timezone": "America/Detroit" + }, + "actual_seconds": 0, + "actual_seconds_text": "0 secs", + "goal_seconds": 3600, + "goal_seconds_text": "1 hr", + "range_status": "ignored", + "range_status_reason": "coded 0 secs which is 1 hr less than your daily goal, but this day doesn’t count because it started before your goal was created", + "range_status_reason_short": "0s (1h less than goal) (before goal created)" + }, + { + "range": { + "start": "2025-05-11T04:00:00Z", + "end": "2025-05-12T03:59:59Z", + "date": "2025-05-11", + "text": "Sun May 11", + "timezone": "America/Detroit" + }, + "actual_seconds": 0, + "actual_seconds_text": "0 secs", + "goal_seconds": 3600, + "goal_seconds_text": "1 hr", + "range_status": "ignored", + "range_status_reason": "coded 0 secs which is 1 hr less than your daily goal, but this day doesn’t count because it started before your goal was created", + "range_status_reason_short": "0s (1h less than goal) (before goal created)" + }, + { + "range": { + "start": "2025-05-12T04:00:00Z", + "end": "2025-05-13T03:59:59Z", + "date": "2025-05-12", + "text": "Yesterday", + "timezone": "America/Detroit" + }, + "actual_seconds": 0, + "actual_seconds_text": "0 secs", + "goal_seconds": 3600, + "goal_seconds_text": "1 hr", + "range_status": "ignored", + "range_status_reason": "coded 0 secs which is 1 hr less than your daily goal, but this day doesn’t count because it started before your goal was created", + "range_status_reason_short": "0s (1h less than goal) (before goal created)" + }, + { + "range": { + "start": "2025-05-13T04:00:00Z", + "end": "2025-05-14T03:59:59Z", + "date": "2025-05-13", + "text": "Today", + "timezone": "America/Detroit" + }, + "actual_seconds": 0, + "actual_seconds_text": "0 secs", + "goal_seconds": 3600, + "goal_seconds_text": "1 hr", + "range_status": "pending", + "range_status_reason": "coded 0 secs which is 1 hr less than your daily goal, but the day isn’t over yet", + "range_status_reason_short": "0s (1h less than goal) (day isn’t over yet)" + } + ], + "projects": [], + "is_tweeting": false, + "shared_with": [], + "range_text": "from 2025-05-07 until 2025-05-13", + "cumulative_status": "ignored", + "subscribers": [ + { + "user_id": "", + "full_name": null, + "display_name": "", + "username": null, + "email": null, + "email_frequency": "Daily" + } + ], + "languages": [], + "ignore_days": [], + "status": "pending", + "editors": [], + "status_percent_calculated": 100, + "is_snoozed": false, + "title": "Code 1 hr per day", + "average_status": "success", + "owner": { + "id": "e44ad5aa-f3b5-4bce-bb1b-d87c6dd36ec0", + "full_name": null, + "display_name": "@e44ad5aa-f3b5-4bce-bb1b-d87c6dd36ec0", + "username": null, + "email": null, + "photo": "https://wakatime.com/photo/e44ad5aa-f3b5-4bce-bb1b-d87c6dd36ec0" + }, + "is_current_user_owner": true + } \ No newline at end of file diff --git a/components/wakatime/sources/new-project-created/new-project-created.mjs b/components/wakatime/sources/new-project-created/new-project-created.mjs new file mode 100644 index 0000000000000..5b9ed6b783d05 --- /dev/null +++ b/components/wakatime/sources/new-project-created/new-project-created.mjs @@ -0,0 +1,22 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "wakatime-new-project-created", + name: "New Project Created", + description: "Emit new event when a new project is created in WakaTime. [See the documentation](https://wakatime.com/developers#projects)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceFn() { + return this.wakatime.listProjects; + }, + getSummary(item) { + return `New Project Created: ${item.name}`; + }, + }, + sampleEmit, +}; diff --git a/components/wakatime/sources/new-project-created/test-event.mjs b/components/wakatime/sources/new-project-created/test-event.mjs new file mode 100644 index 0000000000000..18f03bd1c528f --- /dev/null +++ b/components/wakatime/sources/new-project-created/test-event.mjs @@ -0,0 +1,16 @@ +export default { + "id": "", + "name": "Project", + "color": null, + "first_heartbeat_at": null, + "last_heartbeat_at": null, + "created_at": "2025-05-13T16:14:50Z", + "badge": null, + "clients": [], + "url": "/projects/Project", + "repository": null, + "has_public_url": false, + "urlencoded_name": "Project", + "human_readable_last_heartbeat_at": null, + "human_readable_first_heartbeat_at": null + } \ No newline at end of file diff --git a/components/wakatime/wakatime.app.mjs b/components/wakatime/wakatime.app.mjs index abd8bbfcea6f8..69ffb9d669ae8 100644 --- a/components/wakatime/wakatime.app.mjs +++ b/components/wakatime/wakatime.app.mjs @@ -8,9 +8,26 @@ export default { type: "string", label: "Project", description: "The name of a project", + async options({ page }) { + const { data } = await this.listProjects({ + params: { + page, + }, + }); + return data?.map(({ name }) => name) || []; + }, + }, + language: { + type: "string", + label: "Language", + description: "The name of a programming language", optional: true, - async options() { - const { data } = await this.listProjects(); + async options({ page }) { + const { data } = await this.listProgrammingLanguages({ + params: { + page, + }, + }); return data?.map(({ name }) => name) || []; }, }, @@ -44,6 +61,26 @@ export default { ...opts, }); }, + listProgrammingLanguages(opts = {}) { + return this._makeRequest({ + path: "/program_languages", + ...opts, + }); + }, + listCommits({ + project, ...opts + }) { + return this._makeRequest({ + path: `/users/current/projects/${project}/commits`, + ...opts, + }); + }, + listGoals(opts = {}) { + return this._makeRequest({ + path: "/users/current/goals", + ...opts, + }); + }, createHeartbeat(opts = {}) { return this._makeRequest({ method: "POST", @@ -51,5 +88,33 @@ export default { ...opts, }); }, + async *paginate({ + fn, args, resourceKey, max, + }) { + args = { + ...args, + params: { + ...args?.params, + page: 1, + }, + }; + let hasMore = false; + let count = 0; + do { + const response = await fn(args); + const items = response[resourceKey]; + if (!items?.length) { + return; + } + for (const item of items) { + yield item; + if (max && ++count >= max) { + return; + } + } + hasMore = args?.params?.page < response.total_pages; + args.params.page++; + } while (hasMore); + }, }, }; From abded72dc8649a858b6b34c69ccc4af5ec3f526e Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Tue, 13 May 2025 14:45:40 -0400 Subject: [PATCH 3/4] pnpm-lock.yaml --- pnpm-lock.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e33078d6ec1d3..1ba9e3520534f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14228,7 +14228,11 @@ importers: specifier: ^0.10.0 version: 0.10.0 - components/wakatime: {} + components/wakatime: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/wappalyzer: dependencies: From dfc4e28d5c6d619e784e40d0a66b43216983a372 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Tue, 13 May 2025 14:56:28 -0400 Subject: [PATCH 4/4] updates --- .../sources/new-commit-created/new-commit-created.mjs | 2 +- components/wakatime/wakatime.app.mjs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/components/wakatime/sources/new-commit-created/new-commit-created.mjs b/components/wakatime/sources/new-commit-created/new-commit-created.mjs index cd30289caab3e..3f6fb59fd093f 100644 --- a/components/wakatime/sources/new-commit-created/new-commit-created.mjs +++ b/components/wakatime/sources/new-commit-created/new-commit-created.mjs @@ -6,7 +6,7 @@ export default { key: "wakatime-new-commit-created", name: "New Commit Created", description: "Emit new event when a new commit is created in WakaTime. [See the documentation](https://wakatime.com/developers#commits)", - version: "0.0.1}", + version: "0.0.1", type: "source", dedupe: "unique", props: { diff --git a/components/wakatime/wakatime.app.mjs b/components/wakatime/wakatime.app.mjs index 69ffb9d669ae8..447160afd140d 100644 --- a/components/wakatime/wakatime.app.mjs +++ b/components/wakatime/wakatime.app.mjs @@ -102,7 +102,9 @@ export default { let count = 0; do { const response = await fn(args); - const items = response[resourceKey]; + const items = resourceKey + ? response[resourceKey] + : response.data; if (!items?.length) { return; }