diff --git a/components/workday/actions/change-business-title/change-business-title.mjs b/components/workday/actions/change-business-title/change-business-title.mjs new file mode 100644 index 0000000000000..669f616c20540 --- /dev/null +++ b/components/workday/actions/change-business-title/change-business-title.mjs @@ -0,0 +1,34 @@ +import workday from "../../workday.app.mjs"; + +export default { + key: "workday-change-business-title", + name: "Change Business Title", + description: "Change the business title of a worker. [See the documentation](https://community.workday.com/sites/default/files/file-hosting/restapi/#common/v1/post-/workers/-ID-/businessTitleChanges)", + version: "0.0.1", + type: "action", + props: { + workday, + workerId: { + propDefinition: [ + workday, + "workerId", + ], + }, + proposedBusinessTitle: { + type: "string", + label: "Proposed Business Title", + description: "The new business title for the worker", + }, + }, + async run({ $ }) { + const response = await this.workday.changeBusinessTitle({ + $, + workerId: this.workerId, + data: { + proposedBusinessTitle: this.proposedBusinessTitle, + }, + }); + $.export("$summary", `Successfully changed business title for worker ${this.workerId}`); + return response; + }, +}; diff --git a/components/workday/actions/create-job-change/create-job-change.mjs b/components/workday/actions/create-job-change/create-job-change.mjs new file mode 100644 index 0000000000000..722cdfe6fa87b --- /dev/null +++ b/components/workday/actions/create-job-change/create-job-change.mjs @@ -0,0 +1,60 @@ +import workday from "../../workday.app.mjs"; + +export default { + key: "workday-create-job-change", + name: "Create Job Change", + description: "Create a job change for a worker. [See the documentation](https://community.workday.com/sites/default/files/file-hosting/restapi/#common/v1/post-/workers/-ID-/jobChanges)", + version: "0.0.1", + type: "action", + props: { + workday, + workerId: { + propDefinition: [ + workday, + "workerId", + ], + }, + supervisoryOrganizationId: { + propDefinition: [ + workday, + "supervisoryOrganizationId", + ], + }, + jobChangeReasonId: { + propDefinition: [ + workday, + "jobChangeReasonId", + ], + }, + moveManagersTeam: { + type: "boolean", + label: "Move Managers Team", + description: "Indicates whether teams reporting to the ~Manager~ moved with them during the Change Job Event", + optional: true, + }, + effective: { + type: "string", + label: "Effective", + description: "The date this business process takes effect. Example: `2025-08-09T07:00:00.000Z`", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.workday.createJobChange({ + $, + workerId: this.workerId, + data: { + supervisoryOrganization: { + id: this.supervisoryOrganizationId, + }, + jobChangeReason: { + id: this.jobChangeReasonId, + }, + moveManagersTeam: this.moveManagersTeam, + effective: this.effective, + }, + }); + $.export("$summary", `Successfully created job change for worker ${this.workerId}`); + return response; + }, +}; diff --git a/components/workday/actions/get-worker/get-worker.mjs b/components/workday/actions/get-worker/get-worker.mjs new file mode 100644 index 0000000000000..6cd06617f6c71 --- /dev/null +++ b/components/workday/actions/get-worker/get-worker.mjs @@ -0,0 +1,26 @@ +import workday from "../../workday.app.mjs"; + +export default { + key: "workday-get-worker", + name: "Get Worker", + description: "Get a worker. [See the documentation](https://community.workday.com/sites/default/files/file-hosting/restapi/#common/v1/get-/workers/-ID-)", + version: "0.0.1", + type: "action", + props: { + workday, + workerId: { + propDefinition: [ + workday, + "workerId", + ], + }, + }, + async run({ $ }) { + const response = await this.workday.getWorker({ + $, + workerId: this.workerId, + }); + $.export("$summary", `Successfully fetched worker ${this.workerId}`); + return response; + }, +}; diff --git a/components/workday/actions/list-organization-types/list-organization-types.mjs b/components/workday/actions/list-organization-types/list-organization-types.mjs new file mode 100644 index 0000000000000..e3bb86024a1ed --- /dev/null +++ b/components/workday/actions/list-organization-types/list-organization-types.mjs @@ -0,0 +1,35 @@ +import workday from "../../workday.app.mjs"; + +export default { + key: "workday-list-organization-types", + name: "List Organization Types", + description: "List organization types. [See the documentation](https://community.workday.com/sites/default/files/file-hosting/restapi/#common/v1/organizationTypes)", + version: "0.0.1", + type: "action", + props: { + workday, + maxResults: { + propDefinition: [ + workday, + "maxResults", + ], + }, + }, + async run({ $ }) { + const results = this.workday.paginate({ + fn: this.workday.listOrganizationTypes, + args: { + $, + }, + max: this.maxResults, + }); + + const data = []; + for await (const result of results) { + data.push(result); + } + + $.export("$summary", `Successfully fetched ${data.length} organization types`); + return data; + }, +}; diff --git a/components/workday/actions/list-supervisory-organizations/list-supervisory-organizations.mjs b/components/workday/actions/list-supervisory-organizations/list-supervisory-organizations.mjs new file mode 100644 index 0000000000000..7577b9d46ca47 --- /dev/null +++ b/components/workday/actions/list-supervisory-organizations/list-supervisory-organizations.mjs @@ -0,0 +1,35 @@ +import workday from "../../workday.app.mjs"; + +export default { + key: "workday-list-supervisory-organizations", + name: "List Supervisory Organizations", + description: "List supervisory organizations. [See the documentation](https://community.workday.com/sites/default/files/file-hosting/restapi/#common/v1/get-/supervisoryOrganizations)", + version: "0.0.1", + type: "action", + props: { + workday, + maxResults: { + propDefinition: [ + workday, + "maxResults", + ], + }, + }, + async run({ $ }) { + const results = this.workday.paginate({ + fn: this.workday.listSupervisoryOrganizations, + args: { + $, + }, + max: this.maxResults, + }); + + const data = []; + for await (const result of results) { + data.push(result); + } + + $.export("$summary", `Successfully fetched ${data.length} supervisory organizations`); + return data; + }, +}; diff --git a/components/workday/actions/list-worker-payslips/list-worker-payslips.mjs b/components/workday/actions/list-worker-payslips/list-worker-payslips.mjs new file mode 100644 index 0000000000000..9624646507ac3 --- /dev/null +++ b/components/workday/actions/list-worker-payslips/list-worker-payslips.mjs @@ -0,0 +1,42 @@ +import workday from "../../workday.app.mjs"; + +export default { + key: "workday-list-worker-payslips", + name: "List Worker Payslips", + description: "List the payslips for a worker. [See the documentation](https://community.workday.com/sites/default/files/file-hosting/restapi/#common/v1/get-/workers/-ID-/paySlips)", + version: "0.0.1", + type: "action", + props: { + workday, + workerId: { + propDefinition: [ + workday, + "workerId", + ], + }, + maxResults: { + propDefinition: [ + workday, + "maxResults", + ], + }, + }, + async run({ $ }) { + const results = this.workday.paginate({ + fn: this.workday.listWorkerPayslips, + args: { + $, + workerId: this.workerId, + }, + max: this.maxResults, + }); + + const data = []; + for await (const result of results) { + data.push(result); + } + + $.export("$summary", `Successfully fetched ${data.length} payslips for worker ${this.workerId}`); + return data; + }, +}; diff --git a/components/workday/actions/search-workers/search-workers.mjs b/components/workday/actions/search-workers/search-workers.mjs new file mode 100644 index 0000000000000..b54db20999b4b --- /dev/null +++ b/components/workday/actions/search-workers/search-workers.mjs @@ -0,0 +1,44 @@ +import workday from "../../workday.app.mjs"; + +export default { + key: "workday-search-workers", + name: "Search Workers", + description: "Retrieve a list of workers based on a search query. [See the documentation](https://community.workday.com/sites/default/files/file-hosting/restapi/#common/v1/get-/workers)", + version: "0.0.1", + type: "action", + props: { + workday, + search: { + type: "string", + label: "Search", + description: "The search query to use to filter workers", + optional: true, + }, + maxResults: { + propDefinition: [ + workday, + "maxResults", + ], + }, + }, + async run({ $ }) { + const results = this.workday.paginate({ + fn: this.workday.listWorkers, + args: { + $, + params: { + search: this.search, + }, + }, + max: this.maxResults, + }); + + const data = []; + for await (const result of results) { + data.push(result); + } + + $.export("$summary", `Successfully fetched ${data.length} workers`); + return data; + }, +}; diff --git a/components/workday/package.json b/components/workday/package.json index eaae95f6b5489..77854e2d2875b 100644 --- a/components/workday/package.json +++ b/components/workday/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/workday", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Workday Components", "main": "workday.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.1.0" } -} \ No newline at end of file +} diff --git a/components/workday/sources/common/base-polling.mjs b/components/workday/sources/common/base-polling.mjs new file mode 100644 index 0000000000000..d70e12b8ac29d --- /dev/null +++ b/components/workday/sources/common/base-polling.mjs @@ -0,0 +1,16 @@ +import workday from "../../workday.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + workday, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: {}, +}; diff --git a/components/workday/sources/new-worker-created/new-worker-created.mjs b/components/workday/sources/new-worker-created/new-worker-created.mjs new file mode 100644 index 0000000000000..e5dcec74b4455 --- /dev/null +++ b/components/workday/sources/new-worker-created/new-worker-created.mjs @@ -0,0 +1,68 @@ +import common from "../common/base-polling.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "workday-new-worker-created", + name: "New Worker Created", + description: "Emit new event for each new worker created in Workday. [See the documentation](https://community.workday.com/sites/default/files/file-hosting/restapi/#common/v1/get-/workers)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + _getPreviousIds() { + return this.db.get("previousIds") || {}; + }, + _setPreviousIds(ids) { + this.db.set("previousIds", ids); + }, + generateMeta(worker) { + return { + id: worker.id, + summary: `New worker created: ${worker.descriptor}`, + ts: Date.now(), + }; + }, + async processEvent(limit) { + const results = this.workday.paginate({ + fn: this.workday.listWorkers, + }); + + const previousIds = this._getPreviousIds(); + let workers = []; + + for await (const worker of results) { + if (previousIds[worker.id]) { + continue; + } + workers.push(worker); + previousIds[worker.id] = true; + } + + this._setPreviousIds(previousIds); + + if (!workers?.length) { + return; + } + + if (limit) { + workers = workers.slice(0, limit); + } + + for (const worker of workers) { + const meta = this.generateMeta(worker); + this.$emit(worker, meta); + } + }, + }, + hooks: { + async deploy() { + await this.processEvent(25); + }, + }, + async run() { + await this.processEvent(); + }, + sampleEmit, +}; diff --git a/components/workday/sources/new-worker-created/test-event.mjs b/components/workday/sources/new-worker-created/test-event.mjs new file mode 100644 index 0000000000000..32acd72d2399a --- /dev/null +++ b/components/workday/sources/new-worker-created/test-event.mjs @@ -0,0 +1,14 @@ +export default { + "id": "dd7b18bd75894bd7995a50d42d484956", + "descriptor": "Amelia Casias", + "href": "https://i-09598dd57ca10acdb.workdayeducation.com/api/api/v1/unified01/workers/dd7b18bd75894bd7995a50d42d484956", + "isManager": true, + "primaryWorkPhone": "+1 (773) 721-0897", + "primaryWorkEmail": "acasias@workday.net", + "primarySupervisoryOrganization": { + "descriptor": "Global Support - North America Group", + "id": "2b9dd606a65442768420d425f580a1c6", + "href": "https://i-09598dd57ca10acdb.workdayeducation.com/api/api/v1/unified01/supervisoryOrganizations/2b9dd606a65442768420d425f580a1c6" + }, + "businessTitle": "Manager, Global Support" +} \ No newline at end of file diff --git a/components/workday/workday.app.mjs b/components/workday/workday.app.mjs index 7487a54f70f8e..2ac130224eaad 100644 --- a/components/workday/workday.app.mjs +++ b/components/workday/workday.app.mjs @@ -1,11 +1,171 @@ +import { axios } from "@pipedream/platform"; +const DEFAULT_LIMIT = 100; + export default { type: "app", app: "workday", - propDefinitions: {}, + propDefinitions: { + workerId: { + type: "string", + label: "Worker ID", + description: "The ID of a worker", + async options({ page }) { + const { data } = await this.listWorkers({ + params: { + limit: DEFAULT_LIMIT, + offset: page * DEFAULT_LIMIT, + }, + }); + return data?.map((worker) => ({ + label: worker.descriptor, + value: worker.id, + })) || []; + }, + }, + supervisoryOrganizationId: { + type: "string", + label: "Supervisory Organization ID", + description: "The ID of a supervisory organization", + async options({ page }) { + const { data } = await this.listSupervisoryOrganizations({ + params: { + limit: DEFAULT_LIMIT, + offset: page * DEFAULT_LIMIT, + }, + }); + return data?.map((organization) => ({ + label: organization.descriptor, + value: organization.id, + })) || []; + }, + }, + jobChangeReasonId: { + type: "string", + label: "Job Change Reason ID", + description: "The ID of a job change reason", + async options({ page }) { + const { data } = await this.listJobChangeReasons({ + params: { + limit: DEFAULT_LIMIT, + offset: page * DEFAULT_LIMIT, + }, + }); + return data?.map((reason) => ({ + label: reason.descriptor, + value: reason.id, + })) || []; + }, + }, + maxResults: { + type: "integer", + label: "Max Results", + description: "The maximum number of results to return", + optional: true, + default: 100, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return `https://${this.$auth.domain}/ccx/api/v1/${this.$auth.tenant_id}`; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: { + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }, + ...opts, + }); + }, + getWorker({ + workerId, ...opts + }) { + return this._makeRequest({ + path: `/workers/${workerId}`, + ...opts, + }); + }, + listWorkers(opts = {}) { + return this._makeRequest({ + path: "/workers", + ...opts, + }); + }, + listOrganizationTypes(opts = {}) { + return this._makeRequest({ + path: "/organizationTypes", + ...opts, + }); + }, + listSupervisoryOrganizations(opts = {}) { + return this._makeRequest({ + path: "/supervisoryOrganizations", + ...opts, + }); + }, + listJobChangeReasons(opts = {}) { + return this._makeRequest({ + path: "/jobChangeReasons", + ...opts, + }); + }, + listWorkerPayslips({ + workerId, ...opts + }) { + return this._makeRequest({ + path: `/workers/${workerId}/paySlips`, + ...opts, + }); + }, + createJobChange({ + workerId, ...opts + }) { + return this._makeRequest({ + path: `/workers/${workerId}/jobChanges`, + method: "POST", + ...opts, + }); + }, + changeBusinessTitle({ + workerId, ...opts + }) { + return this._makeRequest({ + path: `/workers/${workerId}/businessTitleChanges`, + method: "POST", + ...opts, + }); + }, + async *paginate({ + fn, args = {}, max, + }) { + args = { + ...args, + params: { + ...args?.params, + limit: DEFAULT_LIMIT, + offset: 0, + }, + }; + let hasMore, count = 0; + do { + const { + data, total, + } = await fn(args); + if (!data?.length) { + return; + } + for (const item of data) { + yield item; + count++; + if (max && count >= max) { + return; + } + } + hasMore = count < total; + args.params.offset += args.params.limit; + } while (hasMore); }, }, -}; \ No newline at end of file +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a767b14eeb93a..67eaca4206aee 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1123,8 +1123,7 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/attentive: - specifiers: {} + components/attentive: {} components/attio: dependencies: @@ -2968,8 +2967,7 @@ importers: components/coinbase_commerce: {} - components/coingecko: - specifiers: {} + components/coingecko: {} components/coinlore: dependencies: @@ -8401,8 +8399,7 @@ importers: specifier: ^1.2.1 version: 1.7.7 - components/matrix: - specifiers: {} + components/matrix: {} components/mattermost: dependencies: @@ -12708,8 +12705,7 @@ importers: specifier: ^1.6.2 version: 1.6.6 - components/sherpa: - specifiers: {} + components/sherpa: {} components/shift4: dependencies: @@ -15597,7 +15593,11 @@ importers: specifier: ^1.1.1 version: 1.6.6 - components/workday: {} + components/workday: + dependencies: + '@pipedream/platform': + specifier: ^3.1.0 + version: 3.1.0 components/workflow_max: dependencies: