diff --git a/components/hathr_ai/hathr_ai.app.mjs b/components/hathr_ai/hathr_ai.app.mjs index 2629cbc8cf1f2..e065e3312e3d7 100644 --- a/components/hathr_ai/hathr_ai.app.mjs +++ b/components/hathr_ai/hathr_ai.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/hr_cloud/README.md b/components/hr_cloud/README.md new file mode 100644 index 0000000000000..9940cdbbfc0fb --- /dev/null +++ b/components/hr_cloud/README.md @@ -0,0 +1,12 @@ +# Overview + +HR Cloud is a human resources management system (HRMS) that helps businesses manage their employee data, payroll, benefits, time tracking, and more. This integration enables you to automate your HR workflows by connecting HR Cloud with thousands of other apps on Pipedream. + +# Getting Started + +To use this integration, you'll need an HR Cloud account and an API key. To get started, + +1. Log in to your HR Cloud account +2. Navigate to Settings > API Settings +3. Create a new API key or use an existing one + diff --git a/components/hr_cloud/actions/create-employee/create-employee.mjs b/components/hr_cloud/actions/create-employee/create-employee.mjs new file mode 100644 index 0000000000000..f9bed64eeda36 --- /dev/null +++ b/components/hr_cloud/actions/create-employee/create-employee.mjs @@ -0,0 +1,121 @@ +import hrCloud from "../../hr_cloud.app.mjs"; + +export default { + key: "hr_cloud-create-employee", + name: "Create Employee", + description: "Create a new employee record in the system. [See the documentation](https://help.hrcloud.com/api/#/employee#POST_employee)", + version: "0.0.1", + type: "action", + props: { + hrCloud, + firstName: { + propDefinition: [ + hrCloud, + "firstName", + ], + }, + lastName: { + propDefinition: [ + hrCloud, + "lastName", + ], + }, + email: { + propDefinition: [ + hrCloud, + "email", + ], + }, + jobTitle: { + propDefinition: [ + hrCloud, + "jobTitle", + ], + }, + departmentId: { + propDefinition: [ + hrCloud, + "departmentId", + ], + }, + startDate: { + propDefinition: [ + hrCloud, + "startDate", + ], + }, + locationId: { + propDefinition: [ + hrCloud, + "locationId", + ], + }, + employmentStatus: { + propDefinition: [ + hrCloud, + "employmentStatusId", + ], + }, + employeeNumber: { + propDefinition: [ + hrCloud, + "employeeNumber", + ], + }, + recordStatus: { + propDefinition: [ + hrCloud, + "recordStatus", + ], + }, + address: { + propDefinition: [ + hrCloud, + "address", + ], + }, + city: { + propDefinition: [ + hrCloud, + "city", + ], + }, + state: { + propDefinition: [ + hrCloud, + "state", + ], + }, + zip: { + propDefinition: [ + hrCloud, + "zip", + ], + }, + }, + async run({ $ }) { + const response = await this.hrCloud.createEmployee({ + $, + data: { + xFirstName: this.firstName, + xLastName: this.lastName, + xEmail: this.email, + xFullName: `${this.firstName} ${this.lastName}`, + xPositionLookup: this.jobTitle, + xDepartmentLookup: this.departmentId, + xStartDate: this.startDate, + xLocationLookup: this.locationId, + xEmploymentStatusLookup: this.employmentStatus, + xEmployeeNumber: this.employeeNumber, + xRecordStatus: this.recordStatus, + xAddress1: this.address, + xCity: this.city, + xState: this.state, + xZipCode: this.zip, + }, + }); + + $.export("$summary", `Successfully created employee: ${this.firstName} ${this.lastName}`); + return response; + }, +}; diff --git a/components/hr_cloud/actions/create-task/create-task.mjs b/components/hr_cloud/actions/create-task/create-task.mjs new file mode 100644 index 0000000000000..03dbe365fb002 --- /dev/null +++ b/components/hr_cloud/actions/create-task/create-task.mjs @@ -0,0 +1,107 @@ +import hrCloud from "../../hr_cloud.app.mjs"; + +export default { + key: "hr_cloud-create-task", + name: "Create Task", + description: "Creates a new task. [See the documentation](https://help.hrcloud.com/api/#/task#POST_tasks)", + version: "0.0.1", + type: "action", + props: { + hrCloud, + applicationCode: { + propDefinition: [ + hrCloud, + "applicationCode", + ], + reloadProps: true, + }, + title: { + propDefinition: [ + hrCloud, + "title", + ], + }, + employeeIds: { + propDefinition: [ + hrCloud, + "employeeId", + ], + type: "string[]", + label: "Employee IDs", + description: "Array of related employee IDs", + }, + assigneeType: { + propDefinition: [ + hrCloud, + "assigneeType", + ], + reloadProps: true, + }, + assignedEmployeeId: { + propDefinition: [ + hrCloud, + "employeeId", + ], + label: "Assignee Employee ID", + description: "ID of assigned employee", + hidden: true, + optional: true, + }, + }, + additionalProps(existingProps) { + const props = {}; + + if (this.assigneeType === "SpecificEmployee") { + existingProps.assignedEmployeeId.hidden = false; + existingProps.assignedEmployeeId.optional = false; + } + + if (this.assigneeType === "Hierarchy") { + props.hierarchyLevel = { + type: "integer", + label: "Hierarchy Level", + description: "Level of upper hierarchy level. From 1 to 9", + max: 9, + }; + } + + if (this.applicationCode === "coreHr" || this.applicationCode === "benefits") { + props.fixedDueDate = { + type: "string", + label: "Fixed Due Date", + description: "Fixed DueDate to complete task (YYYY-MM-DD)", + }; + } + + if (this.applicationCode === "onboard" || this.applicationCode === "offboard") { + props.relativeDueDate = { + type: "string", + label: "Relative Due Date", + description: "Relative DueDate for StartDate or SeparationDate to complete task. Example: `{\"timeUnit\": \"Day\", \"direction\": \"After\", \"offset\": 10}`", + }; + } + + return props; + }, + async run({ $ }) { + const response = await this.hrCloud.createTask({ + $, + data: { + taskType: "task", + applicationCode: this.applicationCode, + title: this.title, + relatedToEmployeeIds: this.employeeIds, + assigneeType: this.assigneeType, + assignedEmployeeId: this.assignedEmployeeId, + hierarchyLevel: this.hierarchyLevel, + fixedDueDate: this.fixedDueDate, + relativeDueDate: typeof this.relativeDueDate === "string" + ? JSON.parse(this.relativeDueDate) + : this.relativeDueDate, + }, + }); + + $.export("$summary", `Successfully created task \`${this.title}\``); + return response; + }, +}; diff --git a/components/hr_cloud/actions/update-employee/update-employee.mjs b/components/hr_cloud/actions/update-employee/update-employee.mjs new file mode 100644 index 0000000000000..9f8be4bab5502 --- /dev/null +++ b/components/hr_cloud/actions/update-employee/update-employee.mjs @@ -0,0 +1,80 @@ +import hrCloud from "../../hr_cloud.app.mjs"; + +export default { + key: "hr_cloud-update-employee", + name: "Update Employee", + description: "Update an existing employee. [See the documentation](https://help.hrcloud.com/api/#/employee#PUT_employee)", + version: "0.0.1", + type: "action", + props: { + hrCloud, + employeeId: { + propDefinition: [ + hrCloud, + "employeeId", + ], + }, + email: { + propDefinition: [ + hrCloud, + "email", + ], + optional: true, + }, + firstName: { + propDefinition: [ + hrCloud, + "firstName", + ], + optional: true, + }, + lastName: { + propDefinition: [ + hrCloud, + "lastName", + ], + optional: true, + }, + address: { + propDefinition: [ + hrCloud, + "address", + ], + }, + city: { + propDefinition: [ + hrCloud, + "city", + ], + }, + state: { + propDefinition: [ + hrCloud, + "state", + ], + }, + zip: { + propDefinition: [ + hrCloud, + "zip", + ], + }, + }, + async run({ $ }) { + const response = await this.hrCloud.updateEmployee({ + $, + data: { + Id: this.employeeId, + xPersonalEmail: this.email, + xFirstName: this.firstName, + xLastName: this.lastName, + xAddress1: this.address, + xCity: this.city, + xState: this.state, + xZipCode: this.zip, + }, + }); + $.export("$summary", `Successfully updated employee: ${response[0].xFirstName} ${response[0].xLastName}`); + return response; + }, +}; diff --git a/components/hr_cloud/hr_cloud.app.mjs b/components/hr_cloud/hr_cloud.app.mjs index ee69105dfa01f..87c44a684ca8f 100644 --- a/components/hr_cloud/hr_cloud.app.mjs +++ b/components/hr_cloud/hr_cloud.app.mjs @@ -1,11 +1,307 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "hr_cloud", - propDefinitions: {}, + propDefinitions: { + departmentId: { + type: "string", + label: "Department", + description: "The ID of an employee department", + async options({ page }) { + const departments = await this.getDepartments({ + params: { + page: page + 1, + }, + }); + return departments.map((department) => ({ + label: department.xDepartmentName, + value: department.Id, + })); + }, + }, + jobTitle: { + type: "string", + label: "Job Title", + description: "The ID of a job title", + async options({ page }) { + const jobTitles = await this.getJobTitles({ + params: { + page: page + 1, + }, + }); + return jobTitles.map((jobTitle) => ({ + label: jobTitle.xPositionTitle, + value: jobTitle.Id, + })); + }, + }, + employeeId: { + type: "string", + label: "Employee", + description: "The ID of an employee", + async options({ page }) { + const employees = await this.getEmployees({ + params: { + page: page + 1, + }, + }); + return employees.map((employee) => ({ + label: `${employee.xFirstName} ${employee.xLastName}`, + value: employee.Id, + })); + }, + }, + locationId: { + type: "string", + label: "Location ID", + description: "The ID of a location", + async options({ page }) { + const locations = await this.getLocations({ + params: { + page: page + 1, + }, + }); + return locations.map((location) => ({ + label: location.xLocationName, + value: location.Id, + })); + }, + }, + employmentStatusId: { + type: "string", + label: "Employment Status ID", + description: "The ID of an employment status", + async options({ page }) { + const statuses = await this.getEmploymentStatus({ + params: { + page: page + 1, + }, + }); + return statuses.map((status) => ({ + label: status.xType, + value: status.Id, + })); + }, + }, + firstName: { + type: "string", + label: "First Name", + description: "The employee's first name", + }, + lastName: { + type: "string", + label: "Last Name", + description: "The employee's last name", + }, + email: { + type: "string", + label: "Email", + description: "The employee's email address", + }, + startDate: { + type: "string", + label: "Start Date", + description: "The employee's start date (YYYY-MM-DD)", + }, + employeeNumber: { + type: "string", + label: "Employee Number", + description: "Unique employee number", + optional: true, + }, + recordStatus: { + type: "string", + label: "Record Status", + description: "The employee status", + options: [ + "Active", + "Inactive", + ], + default: "Active", + optional: true, + }, + address: { + type: "string", + label: "Street Address", + description: "The street address of the employee", + optional: true, + }, + city: { + type: "string", + label: "City", + description: "The city of the employee", + optional: true, + }, + state: { + type: "string", + label: "State", + description: "The state of the employee", + optional: true, + }, + zip: { + type: "integer", + label: "Zip", + description: "The zip code of the employee", + optional: true, + }, + applicationCode: { + type: "string", + label: "Application Code", + description: "Alpha-numeric code for application", + options: [ + "coreHr", + "onboard", + "benefits", + "offboard", + ], + }, + title: { + type: "string", + label: "Title", + description: "The title of a task", + }, + assigneeType: { + type: "string", + label: "Assignee Type", + description: "Type of assignee", + options: [ + "Employee", + "Manager", + "ManagersManager", + "HrAdmin", + "HrUser", + "HrOperation", + "ItUser", + "ItOperation", + "SpecificEmployee", + "Hierarchy", + ], + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://corehr-api.hrcloud.com/v1/cloud"; + }, + _authHeaders() { + return { + "customer_key": `${this.$auth.consumer_key}`, + "customer_secret": `${this.$auth.consumer_secret}`, + "Content-Type": "application/json", + }; + }, + async _makeRequest({ + $ = this, + path, + method = "GET", + params = {}, + data = {}, + }) { + const config = { + method, + url: `${this._baseUrl()}${path}`, + headers: this._authHeaders(), + params, + data, + }; + + try { + const response = await axios($, config); + return response; + } catch (error) { + console.error(`Error with request to ${path}: ${error.message}`); + if (error.response?.status === 404) { + throw new Error(`API endpoint not found (404): ${path}. Please verify the API URL structure in the HR Cloud documentation.`); + } + throw error; + } + }, + getDepartments(args = {}) { + return this._makeRequest({ + path: "/xDepartment", + ...args, + }); + }, + getJobTitles(args = {}) { + return this._makeRequest({ + path: "/xPosition", + ...args, + }); + }, + getLocations(args = {}) { + return this._makeRequest({ + path: "/xLocation", + ...args, + }); + }, + getEmploymentStatus(args = {}) { + return this._makeRequest({ + path: "/xEmploymentStatus", + ...args, + }); + }, + getEmployees(args = {}) { + return this._makeRequest({ + path: "/xEmployee", + ...args, + }); + }, + getApplicants(args = {}) { + return this._makeRequest({ + path: "/xApplicant", + ...args, + }); + }, + getTasks(args = {}) { + return this._makeRequest({ + path: "/xTask", + ...args, + }); + }, + createEmployee(args = {}) { + return this._makeRequest({ + method: "POST", + path: "/xEmployee", + ...args, + }); + }, + createTask(args = {}) { + return this._makeRequest({ + method: "POST", + path: "/xTask/Portal", + ...args, + }); + }, + updateEmployee(args = {}) { + return this._makeRequest({ + method: "PUT", + path: "/xEmployee", + ...args, + }); + }, + async *paginate({ + resourceFn, + params = {}, + max, + }) { + params = { + ...params, + page: 1, + }; + let total, count = 0; + do { + const items = await resourceFn({ + params, + }); + for (const item of items) { + yield item; + if (max && ++count >= max) { + return; + } + } + total = items?.length; + params.page++; + } while (total > 0); }, }, -}; \ No newline at end of file +}; diff --git a/components/hr_cloud/package.json b/components/hr_cloud/package.json index f88060820f9bd..c4c3ca3727f22 100644 --- a/components/hr_cloud/package.json +++ b/components/hr_cloud/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/hr_cloud", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream HR Cloud Components", "main": "hr_cloud.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } -} \ No newline at end of file +} diff --git a/components/hr_cloud/sources/common/base.mjs b/components/hr_cloud/sources/common/base.mjs new file mode 100644 index 0000000000000..74cdaccc96bf2 --- /dev/null +++ b/components/hr_cloud/sources/common/base.mjs @@ -0,0 +1,77 @@ +import hrCloud from "../../hr_cloud.app.mjs"; +import { + DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, ConfigurationError, +} from "@pipedream/platform"; + +export default { + props: { + hrCloud, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + hooks: { + async deploy() { + let results = await this.getResults(); + results = results.slice(-1 * 25); + results.forEach((item) => this.emitEvent(item)); + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + async getResults() { + const lastTs = this._getLastTs(); + let maxTs = lastTs; + + const resourceFn = this.getResourceFn(); + const params = this.getParams(); + const tsField = this.getTsField(); + + const items = this.hrCloud.paginate({ + resourceFn, + params, + }); + + const results = []; + for await (const item of items) { + const ts = Date.parse(item[tsField]); + if (ts >= lastTs) { + results.push(item); + maxTs = Math.max(maxTs, ts); + } + } + + this._setLastTs(maxTs); + return results; + }, + getParams() { + return {}; + }, + emitEvent(item) { + const meta = this.generateMeta(item); + this.$emit(item, meta); + }, + getResourceFn() { + throw new ConfigurationError("getResourceFn is not implemented"); + }, + getTsField() { + throw new ConfigurationError("getTsField is not implemented"); + }, + generateMeta() { + throw new ConfigurationError("generateMeta is not implemented"); + }, + }, + async run() { + const results = await this.getResults(); + results.forEach((item) => this.emitEvent(item)); + }, +}; diff --git a/components/hr_cloud/sources/new-applicant-created/new-applicant-created.mjs b/components/hr_cloud/sources/new-applicant-created/new-applicant-created.mjs new file mode 100644 index 0000000000000..7aef00236dbc6 --- /dev/null +++ b/components/hr_cloud/sources/new-applicant-created/new-applicant-created.mjs @@ -0,0 +1,27 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "hr_cloud-new-applicant-created", + name: "New Applicant Created", + description: "Emit new event when a new applicant is created. [See the documentation](https://help.hrcloud.com/api/#/applicant#GET_applicants)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceFn() { + return this.hrCloud.getApplicants; + }, + getTsField() { + return "xAppliedOn"; + }, + generateMeta(applicant) { + return { + id: applicant.Id, + summary: `New Applicant: ${applicant.xFirstName} ${applicant.xLastName}`, + ts: Date.parse(applicant[this.getTsField()]), + }; + }, + }, +}; diff --git a/components/hr_cloud/sources/new-employee-created/new-employee-created.mjs b/components/hr_cloud/sources/new-employee-created/new-employee-created.mjs new file mode 100644 index 0000000000000..93c774c9a10b6 --- /dev/null +++ b/components/hr_cloud/sources/new-employee-created/new-employee-created.mjs @@ -0,0 +1,27 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "hr_cloud-new-employee-created", + name: "New Employee Created", + description: "Emit new event when a new employee is added to the system. [See the documentation](https://help.hrcloud.com/api/#/employee#GET_employee)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceFn() { + return this.hrCloud.getEmployees; + }, + getTsField() { + return "xCreatedOn"; + }, + generateMeta(employee) { + return { + id: employee.Id, + summary: `New Employee: ${employee.xFirstName} ${employee.xLastName}`, + ts: Date.parse(employee[this.getTsField()]), + }; + }, + }, +}; diff --git a/components/hr_cloud/sources/new-task-created/new-task-created.mjs b/components/hr_cloud/sources/new-task-created/new-task-created.mjs new file mode 100644 index 0000000000000..5da347eb1503f --- /dev/null +++ b/components/hr_cloud/sources/new-task-created/new-task-created.mjs @@ -0,0 +1,27 @@ +import common from "../common/base.mjs"; + +export default { + ...common, + key: "hr_cloud-new-task-created", + name: "New Task Created", + description: "Emit new event when a new task is created. [See the documentation](https://help.hrcloud.com/api/#/task#GET_tasks)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getResourceFn() { + return this.hrCloud.getTasks; + }, + getTsField() { + return "xCreatedOn"; + }, + generateMeta(task) { + return { + id: task.Id, + summary: `New Task: ${task.xSubject}`, + ts: Date.parse(task[this.getTsField()]), + }; + }, + }, +}; diff --git a/components/jenkins/jenkins.app.mjs b/components/jenkins/jenkins.app.mjs index 073a4102420cd..b5d94814e1071 100644 --- a/components/jenkins/jenkins.app.mjs +++ b/components/jenkins/jenkins.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/neo4j_auradb/neo4j_auradb.app.mjs b/components/neo4j_auradb/neo4j_auradb.app.mjs index 841fa8b270cd2..177312a2ce978 100644 --- a/components/neo4j_auradb/neo4j_auradb.app.mjs +++ b/components/neo4j_auradb/neo4j_auradb.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/components/splunk/splunk.app.mjs b/components/splunk/splunk.app.mjs index 8dd1d6fa5f0d0..13ba053e30d09 100644 --- a/components/splunk/splunk.app.mjs +++ b/components/splunk/splunk.app.mjs @@ -8,4 +8,4 @@ export default { console.log(Object.keys(this.$auth)); }, }, -}; \ No newline at end of file +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 592d1b48d1d81..d726b2a9e83c3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5730,8 +5730,7 @@ importers: components/hasura: {} - components/hathr_ai: - specifiers: {} + components/hathr_ai: {} components/have_i_been_pwned: {} @@ -5991,7 +5990,11 @@ importers: specifier: ^1.6.0 version: 1.6.6 - components/hr_cloud: {} + components/hr_cloud: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/hr_partner: {} @@ -8248,8 +8251,7 @@ importers: components/neetokb: {} - components/neo4j_auradb: - specifiers: {} + components/neo4j_auradb: {} components/neon_api_keys: dependencies: