From 75f1423d342b7242a11fa3f15019479bc200e49a Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 1 May 2025 12:28:24 -0300 Subject: [PATCH 1/7] kiwihr init --- .../approve-leave-request.mjs | 47 ++++ .../create-employee/create-employee.mjs | 63 +++++ .../update-employee-record.mjs | 61 +++++ components/kiwihr/kiwihr.app.mjs | 241 +++++++++++++++++- .../new-employee-instant.mjs | 112 ++++++++ .../new-leave-request-instant.mjs | 63 +++++ .../updated-employee-record-instant.mjs | 125 +++++++++ 7 files changed, 708 insertions(+), 4 deletions(-) create mode 100644 components/kiwihr/actions/approve-leave-request/approve-leave-request.mjs create mode 100644 components/kiwihr/actions/create-employee/create-employee.mjs create mode 100644 components/kiwihr/actions/update-employee-record/update-employee-record.mjs create mode 100644 components/kiwihr/sources/new-employee-instant/new-employee-instant.mjs create mode 100644 components/kiwihr/sources/new-leave-request-instant/new-leave-request-instant.mjs create mode 100644 components/kiwihr/sources/updated-employee-record-instant/updated-employee-record-instant.mjs diff --git a/components/kiwihr/actions/approve-leave-request/approve-leave-request.mjs b/components/kiwihr/actions/approve-leave-request/approve-leave-request.mjs new file mode 100644 index 0000000000000..390af8b179057 --- /dev/null +++ b/components/kiwihr/actions/approve-leave-request/approve-leave-request.mjs @@ -0,0 +1,47 @@ +import kiwihr from "../../kiwihr.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "kiwihr-approve-leave-request", + name: "Approve Leave Request", + description: "Approve a pending leave request for an employee. [See the documentation](https://api.kiwihr.it/api/docs/mutation.doc.html)", + version: "0.0.{{ts}}", + type: "action", + props: { + kiwihr, + leaveRequestId: { + propDefinition: [ + kiwihr, + "leaveRequestId", + ], + }, + approvalDate: { + propDefinition: [ + kiwihr, + "approvalDate", + ], + optional: true, + }, + message: { + propDefinition: [ + kiwihr, + "message", + ], + optional: true, + }, + }, + async run({ $ }) { + try { + const response = await this.kiwihr.approveLeaveRequest({ + leaveRequestId: this.leaveRequestId, + approvalDate: this.approvalDate, + message: this.message, + }); + + $.export("$summary", `Successfully approved leave request with ID ${this.leaveRequestId}`); + return response; + } catch (error) { + throw new Error(`Failed to approve leave request: ${error.message}`); + } + }, +}; diff --git a/components/kiwihr/actions/create-employee/create-employee.mjs b/components/kiwihr/actions/create-employee/create-employee.mjs new file mode 100644 index 0000000000000..d4e7d62053fce --- /dev/null +++ b/components/kiwihr/actions/create-employee/create-employee.mjs @@ -0,0 +1,63 @@ +import kiwihr from "../../kiwihr.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "kiwihr-create-employee", + name: "Create Employee", + description: "Add a new employee to kiwiHR. [See the documentation](https://api.kiwihr.com/api/docs/mutation.doc.html)", + version: "0.0.{{ts}}", + type: "action", + props: { + kiwihr, + employeeName: { + type: "string", + label: "Employee Name", + description: "Name of the employee to add.", + }, + email: { + type: "string", + label: "Email", + description: "Email of the employee to add.", + }, + startDate: { + type: "string", + label: "Start Date", + description: "Start date of the employee to add. Format: YYYY-MM-DD", + }, + department: { + propDefinition: [ + kiwihr, + "department", + ], + optional: true, + }, + jobTitle: { + type: "string", + label: "Job Title", + description: "Job title of the employee. Optional.", + optional: true, + }, + location: { + propDefinition: [ + kiwihr, + "location", + ], + optional: true, + }, + }, + async run({ $ }) { + const data = { + employeeName: this.employeeName, + email: this.email, + startDate: this.startDate, + department: this.department, + jobTitle: this.jobTitle, + location: this.location, + }; + + const response = await this.kiwihr.createEmployee(data); + + $.export("$summary", `Successfully created employee ${this.employeeName}`); + return response; + }, +}; diff --git a/components/kiwihr/actions/update-employee-record/update-employee-record.mjs b/components/kiwihr/actions/update-employee-record/update-employee-record.mjs new file mode 100644 index 0000000000000..4a7e5912ca634 --- /dev/null +++ b/components/kiwihr/actions/update-employee-record/update-employee-record.mjs @@ -0,0 +1,61 @@ +import kiwihr from "../../kiwihr.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "kiwihr-update-employee-record", + name: "Update Employee Record", + description: "Update an existing employee's record in kiwiHR. [See the documentation](https://api.kiwihr.it/api/docs/mutation.doc.html)", + version: "0.0.{{ts}}", + type: "action", + props: { + kiwihr, + employeeId: { + propDefinition: [ + kiwihr, + "employeeId", + ], + }, + jobTitle: { + propDefinition: [ + kiwihr, + "jobTitle", + ], + optional: true, + }, + department: { + propDefinition: [ + kiwihr, + "department", + ], + optional: true, + }, + supervisor: { + propDefinition: [ + kiwihr, + "supervisor", + ], + optional: true, + }, + }, + async run({ $ }) { + const data = { + ...(this.jobTitle && { + positionId: this.jobTitle, + }), + ...(this.department && { + teamId: this.department, + }), + ...(this.supervisor && { + managerId: this.supervisor, + }), + }; + + const response = await this.kiwihr.updateEmployee({ + employeeId: this.employeeId, + ...data, + }); + + $.export("$summary", `Successfully updated employee record for ID: ${this.employeeId}`); + return response; + }, +}; diff --git a/components/kiwihr/kiwihr.app.mjs b/components/kiwihr/kiwihr.app.mjs index e013be46e0b4f..66cfeb298f42b 100644 --- a/components/kiwihr/kiwihr.app.mjs +++ b/components/kiwihr/kiwihr.app.mjs @@ -1,11 +1,244 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "kiwihr", - propDefinitions: {}, + propDefinitions: { + department: { + type: "string", + label: "Department", + description: "ID of the department to filter employees. Optional.", + optional: true, + async options() { + const items = await this.getDepartments(); + return items.map((e) => ({ + value: e.id, + label: e.name, + })); + }, + }, + location: { + type: "string", + label: "Location", + description: "ID of the location to filter employees. Optional.", + optional: true, + async options() { + const items = await this.getLocations(); + return items.map((e) => ({ + value: e.id, + label: e.name, + })); + }, + }, + leaveType: { + type: "string", + label: "Leave Type", + description: "Type of leave to filter requests. Optional.", + optional: true, + async options() { + const items = await this.getLeaveTypes(); + return items.map((e) => ({ + value: e.id, + label: e.name, + })); + }, + }, + requestStatus: { + type: "string", + label: "Request Status", + description: "Status of the leave request to filter.", + optional: true, + async options() { + return [ + { + label: "Pending", + value: "pending", + }, + { + label: "Approved", + value: "approved", + }, + { + label: "Rejected", + value: "rejected", + }, + ]; + }, + }, + employeeId: { + type: "string", + label: "Employee ID", + description: "ID of the employee to watch for updates.", + }, + employeeName: { + type: "string", + label: "Employee Name", + description: "Name of the employee to add.", + }, + email: { + type: "string", + label: "Email", + description: "Email of the employee to add.", + }, + startDate: { + type: "string", + label: "Start Date", + description: "Start date of the employee to add.", + }, + jobTitle: { + type: "string", + label: "Job Title", + description: "Job title of the employee. Optional.", + optional: true, + }, + supervisor: { + type: "string", + label: "Supervisor", + description: "ID of the supervisor for the employee. Optional.", + optional: true, + async options() { + const items = await this.getEmployees(); + return items.map((e) => ({ + value: e.id, + label: `${e.firstName} ${e.lastName}`, + })); + }, + }, + leaveRequestId: { + type: "string", + label: "Leave Request ID", + description: "ID of the leave request to approve.", + }, + approvalDate: { + type: "string", + label: "Approval Date", + description: "Date of approval for the leave request. Optional.", + optional: true, + }, + message: { + type: "string", + label: "Message", + description: "Approval message. Optional.", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.kiwihr.com/api"; + }, + async _makeRequest(opts = {}) { + const { + $ = this, method = "GET", path = "/", headers, ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + method, + url: this._baseUrl() + path, + headers: { + ...headers, + "X-Api-Key": this.$auth.api_key, + }, + }); + }, + async getDepartments(opts = {}) { + return this._makeRequest({ + path: "/departments", + ...opts, + }); + }, + async getLocations(opts = {}) { + return this._makeRequest({ + path: "/locations", + ...opts, + }); + }, + async getLeaveTypes(opts = {}) { + return this._makeRequest({ + path: "/leave/types", + ...opts, + }); + }, + async getEmployees(opts = {}) { + return this._makeRequest({ + path: "/employees", + ...opts, + }); + }, + async createEmployee(data) { + return this._makeRequest({ + method: "POST", + path: "/employees", + data, + }); + }, + async updateEmployee({ + employeeId, ...data + }) { + return this._makeRequest({ + method: "PUT", + path: `/employees/${employeeId}`, + data, + }); + }, + async approveLeaveRequest({ + leaveRequestId, approvalDate, message, + }) { + return this._makeRequest({ + method: "POST", + path: `/leave/requests/${leaveRequestId}/approve`, + data: { + approvalDate, + message, + }, + }); + }, + async addEmployee({ + employeeName, email, startDate, department, jobTitle, location, + }) { + return this.createEmployee({ + employeeName, + email, + startDate, + department, + jobTitle, + location, + }); + }, + async onEmployeeAdded({ + department, location, + }) { + const employees = await this.getEmployees({ + params: { + department, + location, + }, + }); + // Handle new employee event + }, + async onLeaveRequestCreated({ + leaveType, requestStatus, + }) { + const requests = await this._makeRequest({ + path: "/leave/requests", + method: "GET", + params: { + leaveType, + requestStatus, + }, + }); + // Handle leave request event + }, + async onEmployeeUpdated({ + employeeId, jobTitle, department, + }) { + const employee = await this.getEmployees({ + params: { + employeeId, + jobTitle, + department, + }, + }); + // Handle employee updated event }, }, }; diff --git a/components/kiwihr/sources/new-employee-instant/new-employee-instant.mjs b/components/kiwihr/sources/new-employee-instant/new-employee-instant.mjs new file mode 100644 index 0000000000000..97e11816fa39e --- /dev/null +++ b/components/kiwihr/sources/new-employee-instant/new-employee-instant.mjs @@ -0,0 +1,112 @@ +import kiwihr from "../../kiwihr.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "kiwihr-new-employee-instant", + name: "New Employee Instant", + description: "Emit new event when a new employee is added to KiwiHR. [See the documentation](https://api.kiwihr.com/api/docs/)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + kiwihr: { + type: "app", + app: "kiwihr", + }, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + department: { + propDefinition: [ + kiwihr, + "department", + ], + }, + location: { + propDefinition: [ + kiwihr, + "location", + ], + }, + }, + methods: { + _getLastEmployeeAddedDate() { + return this.db.get("lastEmployeeAddedDate") || null; + }, + _setLastEmployeeAddedDate(date) { + this.db.set("lastEmployeeAddedDate", date); + }, + }, + hooks: { + async deploy() { + const params = { + departmentId: this.department, + locationId: this.location, + limit: 50, + sort: [ + { + field: "createdAt", + direction: "desc", + }, + ], + }; + const employees = await this.kiwihr.getEmployees({ + params, + }); + + for (const employee of employees.reverse()) { + const event = { + id: employee.id, + summary: `New employee: ${employee.firstName} ${employee.lastName}`, + ts: Date.parse(employee.createdAt), + }; + this.$emit(employee, event); + } + + if (employees.length > 0) { + const latestEmployee = employees[0]; + this._setLastEmployeeAddedDate(latestEmployee.createdAt); + } + }, + async activate() { + // Hook to create a webhook subscription if supported + }, + async deactivate() { + // Hook to delete a webhook subscription if created + }, + }, + async run(event) { + const { + department, location, + } = this; + await this.kiwihr.onEmployeeAdded({ + department, + location, + }); + + const latestDate = this._getLastEmployeeAddedDate(); + const employees = await this.kiwihr.getEmployees({ + params: { + departmentId: department, + locationId: location, + createdSince: latestDate, + }, + }); + + for (const employee of employees) { + const event = { + id: employee.id, + summary: `New employee: ${employee.firstName} ${employee.lastName}`, + ts: Date.parse(employee.createdAt), + }; + this.$emit(employee, event); + } + + if (employees.length > 0) { + const latestEmployee = employees[0]; + this._setLastEmployeeAddedDate(latestEmployee.createdAt); + } + }, +}; diff --git a/components/kiwihr/sources/new-leave-request-instant/new-leave-request-instant.mjs b/components/kiwihr/sources/new-leave-request-instant/new-leave-request-instant.mjs new file mode 100644 index 0000000000000..e08441daea164 --- /dev/null +++ b/components/kiwihr/sources/new-leave-request-instant/new-leave-request-instant.mjs @@ -0,0 +1,63 @@ +import kiwihr from "../../kiwihr.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "kiwihr-new-leave-request-instant", + name: "New Leave Request Created", + description: "Emit new event when a leave request is created or submitted by an employee. [See the documentation](https://api.kiwihr.com/api/docs/)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + kiwihr: { + type: "app", + app: "kiwihr", + }, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + leaveType: { + propDefinition: [ + kiwihr, + "leaveType", + ], + }, + requestStatus: { + propDefinition: [ + kiwihr, + "requestStatus", + ], + }, + }, + hooks: { + async deploy() { + const leaveRequests = await this.kiwihr.onLeaveRequestCreated({ + leaveType: this.leaveType, + requestStatus: this.requestStatus, + }); + leaveRequests.slice(-50).forEach((request) => { + this.$emit(request, { + id: request.id, + summary: `New leave request from ${request.employeeName}`, + ts: new Date(request.createdDate).getTime(), + }); + }); + }, + async activate() { + // This source does not require creating webhooks via an API request + }, + async deactivate() { + // This source does not require deleting webhooks via an API request + }, + }, + async run(event) { + const { body } = event; + this.$emit(body, { + id: body.id, + summary: `New leave request from ${body.employeeName}`, + ts: new Date(body.createdDate).getTime(), + }); + }, +}; diff --git a/components/kiwihr/sources/updated-employee-record-instant/updated-employee-record-instant.mjs b/components/kiwihr/sources/updated-employee-record-instant/updated-employee-record-instant.mjs new file mode 100644 index 0000000000000..906d95139b150 --- /dev/null +++ b/components/kiwihr/sources/updated-employee-record-instant/updated-employee-record-instant.mjs @@ -0,0 +1,125 @@ +import kiwihr from "../../kiwihr.app.mjs"; +import crypto from "crypto"; +import { axios } from "@pipedream/platform"; + +export default { + key: "kiwihr-updated-employee-record-instant", + name: "KiwiHR Updated Employee Record Instant", + description: "Emit new event when an employee record is updated in KiwiHR. [See the documentation](https://api.kiwihr.it/api/docs)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + kiwihr, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + employeeId: { + propDefinition: [ + kiwihr, + "employeeId", + ], + }, + department: { + propDefinition: [ + kiwihr, + "department", + ], + }, + jobTitle: { + propDefinition: [ + kiwihr, + "jobTitle", + ], + }, + }, + methods: { + _getWebhookId() { + return this.db.get("webhookId"); + }, + _setWebhookId(id) { + this.db.set("webhookId", id); + }, + }, + hooks: { + async deploy() { + const employees = await this.kiwihr.getEmployees({ + params: { + limit: 50, + order: "desc", + }, + }); + for (const employee of employees.items) { + this.$emit(employee, { + id: employee.id, + summary: `Employee Updated: ${employee.firstName} ${employee.lastName}`, + ts: Date.parse(employee.updatedAt), + }); + } + }, + async activate() { + const hookResponse = await axios(this, { + method: "POST", + url: `${this.kiwihr._baseUrl()}/webhooks`, + headers: { + "X-Api-Key": this.kiwihr.$auth.api_key, + }, + data: { + targetUrl: this.http.endpoint, + eventTypes: [ + "employee.updated", + ], + }, + }); + this._setWebhookId(hookResponse.id); + }, + async deactivate() { + const webhookId = this._getWebhookId(); + if (webhookId) { + await axios(this, { + method: "DELETE", + url: `${this.kiwihr._baseUrl()}/webhooks/${webhookId}`, + headers: { + "X-Api-Key": this.kiwihr.$auth.api_key, + }, + }); + } + }, + }, + async run(event) { + const signature = event.headers["x-signature"]; + const computedSignature = crypto + .createHmac("sha256", this.kiwihr.$auth.api_key) + .update(event.body) + .digest("base64"); + + if (signature !== computedSignature) { + this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + + const { + employeeId, jobTitle, department, + } = event.body; + + if (employeeId !== this.employeeId) return; + if (this.jobTitle && jobTitle !== this.jobTitle) return; + if (this.department && department !== this.department) return; + + this.$emit(event.body, { + id: event.body.id, + summary: `Updated Employee: ${event.body.firstName} ${event.body.lastName}`, + ts: Date.parse(event.body.updatedAt), + }); + + this.http.respond({ + status: 200, + body: "OK", + }); + }, +}; From 9856c12d8f7d3013765f2d0e20cba7c9f61f4852 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 5 May 2025 19:12:03 -0300 Subject: [PATCH 2/7] [Components] kiwihr #15318 Sources - New Employee Actions - Create Employee - Update Employee Record --- .../approve-leave-request.mjs | 47 --- .../create-employee/create-employee.mjs | 193 +++++++-- .../update-employee-record.mjs | 185 ++++++-- components/kiwihr/common/constants.mjs | 7 + components/kiwihr/common/mutations.mjs | 56 +++ components/kiwihr/common/queries.mjs | 82 ++++ components/kiwihr/common/utils.mjs | 6 + components/kiwihr/kiwihr.app.mjs | 396 ++++++++++-------- components/kiwihr/package.json | 5 +- components/kiwihr/sources/common/base.mjs | 59 +++ .../new-employee-instant.mjs | 112 ----- .../sources/new-employee/new-employee.mjs | 35 ++ .../sources/new-employee/test-event.mjs | 29 ++ .../new-leave-request-instant.mjs | 63 --- .../updated-employee-record-instant.mjs | 125 ------ 15 files changed, 816 insertions(+), 584 deletions(-) delete mode 100644 components/kiwihr/actions/approve-leave-request/approve-leave-request.mjs create mode 100644 components/kiwihr/common/constants.mjs create mode 100644 components/kiwihr/common/mutations.mjs create mode 100644 components/kiwihr/common/queries.mjs create mode 100644 components/kiwihr/common/utils.mjs create mode 100644 components/kiwihr/sources/common/base.mjs delete mode 100644 components/kiwihr/sources/new-employee-instant/new-employee-instant.mjs create mode 100644 components/kiwihr/sources/new-employee/new-employee.mjs create mode 100644 components/kiwihr/sources/new-employee/test-event.mjs delete mode 100644 components/kiwihr/sources/new-leave-request-instant/new-leave-request-instant.mjs delete mode 100644 components/kiwihr/sources/updated-employee-record-instant/updated-employee-record-instant.mjs diff --git a/components/kiwihr/actions/approve-leave-request/approve-leave-request.mjs b/components/kiwihr/actions/approve-leave-request/approve-leave-request.mjs deleted file mode 100644 index 390af8b179057..0000000000000 --- a/components/kiwihr/actions/approve-leave-request/approve-leave-request.mjs +++ /dev/null @@ -1,47 +0,0 @@ -import kiwihr from "../../kiwihr.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "kiwihr-approve-leave-request", - name: "Approve Leave Request", - description: "Approve a pending leave request for an employee. [See the documentation](https://api.kiwihr.it/api/docs/mutation.doc.html)", - version: "0.0.{{ts}}", - type: "action", - props: { - kiwihr, - leaveRequestId: { - propDefinition: [ - kiwihr, - "leaveRequestId", - ], - }, - approvalDate: { - propDefinition: [ - kiwihr, - "approvalDate", - ], - optional: true, - }, - message: { - propDefinition: [ - kiwihr, - "message", - ], - optional: true, - }, - }, - async run({ $ }) { - try { - const response = await this.kiwihr.approveLeaveRequest({ - leaveRequestId: this.leaveRequestId, - approvalDate: this.approvalDate, - message: this.message, - }); - - $.export("$summary", `Successfully approved leave request with ID ${this.leaveRequestId}`); - return response; - } catch (error) { - throw new Error(`Failed to approve leave request: ${error.message}`); - } - }, -}; diff --git a/components/kiwihr/actions/create-employee/create-employee.mjs b/components/kiwihr/actions/create-employee/create-employee.mjs index d4e7d62053fce..6fe21d1a67898 100644 --- a/components/kiwihr/actions/create-employee/create-employee.mjs +++ b/components/kiwihr/actions/create-employee/create-employee.mjs @@ -1,63 +1,192 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { parseError } from "../../common/utils.mjs"; import kiwihr from "../../kiwihr.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "kiwihr-create-employee", name: "Create Employee", description: "Add a new employee to kiwiHR. [See the documentation](https://api.kiwihr.com/api/docs/mutation.doc.html)", - version: "0.0.{{ts}}", + version: "0.0.1", type: "action", props: { kiwihr, - employeeName: { - type: "string", - label: "Employee Name", - description: "Name of the employee to add.", + firstName: { + propDefinition: [ + kiwihr, + "firstName", + ], + }, + lastName: { + propDefinition: [ + kiwihr, + "lastName", + ], }, email: { - type: "string", - label: "Email", - description: "Email of the employee to add.", + propDefinition: [ + kiwihr, + "email", + ], + }, + workPhones: { + propDefinition: [ + kiwihr, + "workPhones", + ], + optional: true, + }, + employmentStartDate: { + propDefinition: [ + kiwihr, + "employmentStartDate", + ], + optional: true, + }, + aboutMe: { + propDefinition: [ + kiwihr, + "aboutMe", + ], + optional: true, + }, + gender: { + propDefinition: [ + kiwihr, + "gender", + ], + optional: true, + }, + managerId: { + propDefinition: [ + kiwihr, + "managerId", + ], + optional: true, + }, + nationality: { + propDefinition: [ + kiwihr, + "nationality", + ], + optional: true, + }, + teamIds: { + propDefinition: [ + kiwihr, + "teamIds", + ], + optional: true, }, - startDate: { - type: "string", - label: "Start Date", - description: "Start date of the employee to add. Format: YYYY-MM-DD", + positionId: { + propDefinition: [ + kiwihr, + "positionId", + ], + optional: true, + }, + locationId: { + propDefinition: [ + kiwihr, + "locationId", + ], + optional: true, }, - department: { + birthDate: { propDefinition: [ kiwihr, - "department", + "birthDate", ], optional: true, }, - jobTitle: { - type: "string", - label: "Job Title", - description: "Job title of the employee. Optional.", + personalPhone: { + propDefinition: [ + kiwihr, + "personalPhone", + ], optional: true, }, - location: { + personalEmail: { propDefinition: [ kiwihr, - "location", + "personalEmail", + ], + optional: true, + }, + addressStreet: { + propDefinition: [ + kiwihr, + "addressStreet", + ], + optional: true, + }, + addressCity: { + propDefinition: [ + kiwihr, + "addressCity", + ], + optional: true, + }, + addressState: { + propDefinition: [ + kiwihr, + "addressState", + ], + optional: true, + }, + addressPostalCode: { + propDefinition: [ + kiwihr, + "addressPostalCode", + ], + optional: true, + }, + addressCountry: { + propDefinition: [ + kiwihr, + "addressCountry", + ], + optional: true, + }, + pronouns: { + propDefinition: [ + kiwihr, + "pronouns", ], optional: true, }, }, async run({ $ }) { - const data = { - employeeName: this.employeeName, - email: this.email, - startDate: this.startDate, - department: this.department, - jobTitle: this.jobTitle, - location: this.location, - }; + try { + const { + kiwihr, + addressStreet, + addressCity, + addressState, + addressPostalCode, + addressCountry, + ...user + } = this; + + const address = {}; + if (addressStreet) address.street = addressStreet; + if (addressCity) address.city = addressCity; + if (addressState) address.state = addressState; + if (addressPostalCode) address.postalCode = addressPostalCode; + if (addressCountry) address.country = addressCountry; + + if (Object.keys(address)) { + user.address = address; + } - const response = await this.kiwihr.createEmployee(data); + const response = await kiwihr.createEmployee({ + user, + }); - $.export("$summary", `Successfully created employee ${this.employeeName}`); - return response; + $.export("$summary", `Successfully created employee ${this.firstName} ${this.lastName}`); + return response; + } catch ({ response }) { + const error = parseError(response); + throw new ConfigurationError(error); + } }, }; diff --git a/components/kiwihr/actions/update-employee-record/update-employee-record.mjs b/components/kiwihr/actions/update-employee-record/update-employee-record.mjs index 4a7e5912ca634..31e76607d9b48 100644 --- a/components/kiwihr/actions/update-employee-record/update-employee-record.mjs +++ b/components/kiwihr/actions/update-employee-record/update-employee-record.mjs @@ -1,11 +1,12 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { parseError } from "../../common/utils.mjs"; import kiwihr from "../../kiwihr.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "kiwihr-update-employee-record", name: "Update Employee Record", description: "Update an existing employee's record in kiwiHR. [See the documentation](https://api.kiwihr.it/api/docs/mutation.doc.html)", - version: "0.0.{{ts}}", + version: "0.0.1", type: "action", props: { kiwihr, @@ -15,47 +16,181 @@ export default { "employeeId", ], }, - jobTitle: { + firstName: { propDefinition: [ kiwihr, - "jobTitle", + "firstName", ], optional: true, }, - department: { + lastName: { propDefinition: [ kiwihr, - "department", + "lastName", ], optional: true, }, - supervisor: { + workPhones: { propDefinition: [ kiwihr, - "supervisor", + "workPhones", + ], + optional: true, + }, + employmentStartDate: { + propDefinition: [ + kiwihr, + "employmentStartDate", + ], + optional: true, + }, + aboutMe: { + propDefinition: [ + kiwihr, + "aboutMe", + ], + optional: true, + }, + gender: { + propDefinition: [ + kiwihr, + "gender", + ], + optional: true, + }, + managerId: { + propDefinition: [ + kiwihr, + "managerId", + ], + optional: true, + }, + nationality: { + propDefinition: [ + kiwihr, + "nationality", + ], + optional: true, + }, + teamIds: { + propDefinition: [ + kiwihr, + "teamIds", + ], + optional: true, + }, + positionId: { + propDefinition: [ + kiwihr, + "positionId", + ], + optional: true, + }, + locationId: { + propDefinition: [ + kiwihr, + "locationId", + ], + optional: true, + }, + birthDate: { + propDefinition: [ + kiwihr, + "birthDate", + ], + optional: true, + }, + personalPhone: { + propDefinition: [ + kiwihr, + "personalPhone", + ], + optional: true, + }, + personalEmail: { + propDefinition: [ + kiwihr, + "personalEmail", + ], + optional: true, + }, + addressStreet: { + propDefinition: [ + kiwihr, + "addressStreet", + ], + optional: true, + }, + addressCity: { + propDefinition: [ + kiwihr, + "addressCity", + ], + optional: true, + }, + addressState: { + propDefinition: [ + kiwihr, + "addressState", + ], + optional: true, + }, + addressPostalCode: { + propDefinition: [ + kiwihr, + "addressPostalCode", + ], + optional: true, + }, + addressCountry: { + propDefinition: [ + kiwihr, + "addressCountry", + ], + optional: true, + }, + pronouns: { + propDefinition: [ + kiwihr, + "pronouns", ], optional: true, }, }, async run({ $ }) { - const data = { - ...(this.jobTitle && { - positionId: this.jobTitle, - }), - ...(this.department && { - teamId: this.department, - }), - ...(this.supervisor && { - managerId: this.supervisor, - }), - }; + try { + const { + kiwihr, + employeeId, + addressStreet, + addressCity, + addressState, + addressPostalCode, + addressCountry, + ...user + } = this; + + const address = {}; + if (addressStreet) address.street = addressStreet; + if (addressCity) address.city = addressCity; + if (addressState) address.state = addressState; + if (addressPostalCode) address.postalCode = addressPostalCode; + if (addressCountry) address.country = addressCountry; + + if (Object.keys(address) > 0) { + user.address = address; + } - const response = await this.kiwihr.updateEmployee({ - employeeId: this.employeeId, - ...data, - }); + const response = await kiwihr.updateEmployee({ + id: employeeId, + user, + }); - $.export("$summary", `Successfully updated employee record for ID: ${this.employeeId}`); - return response; + $.export("$summary", `Successfully updated employee record for ID: ${this.employeeId}`); + return response; + } catch ({ response }) { + const error = parseError(response); + throw new ConfigurationError(error); + } }, }; diff --git a/components/kiwihr/common/constants.mjs b/components/kiwihr/common/constants.mjs new file mode 100644 index 0000000000000..40128a95544bf --- /dev/null +++ b/components/kiwihr/common/constants.mjs @@ -0,0 +1,7 @@ +export const LIMIT = 100; + +export const GENDER_OPTIONS = [ + "MALE", + "FEMALE", + "DIVERSE", +]; diff --git a/components/kiwihr/common/mutations.mjs b/components/kiwihr/common/mutations.mjs new file mode 100644 index 0000000000000..04b679303dbb4 --- /dev/null +++ b/components/kiwihr/common/mutations.mjs @@ -0,0 +1,56 @@ +import { gql } from "graphql-request"; + +const createEmployee = gql` + mutation createEmployee($user: CreateUserInput!) { + createUser(user: $user) { + id + firstName + lastName + email + gender + workPhones + employmentStartDate + employmentEndDate + aboutMe + employeeNumber + birthDate + personalEmail + invitationStatus + language + isActive + nationality + personalPhone + pronouns + } + } +`; + +const updateEmployee = gql` + mutation updateEmployee($id: ID!, $user: UpdateUserInput!) { + updateUser(id: $id, user: $user) { + id + firstName + lastName + email + gender + workPhones + employmentStartDate + employmentEndDate + aboutMe + employeeNumber + birthDate + personalEmail + invitationStatus + language + isActive + nationality + personalPhone + pronouns + } + } +`; + +export default { + createEmployee, + updateEmployee, +}; diff --git a/components/kiwihr/common/queries.mjs b/components/kiwihr/common/queries.mjs new file mode 100644 index 0000000000000..870768a878ccb --- /dev/null +++ b/components/kiwihr/common/queries.mjs @@ -0,0 +1,82 @@ +import { gql } from "graphql-request"; + +const listUsers = gql` + query listUsers($offset: Int, $limit: Int, $sort: [SortArgInput!], $filter: UserFilterInput) { + users(offset: $offset, limit: $limit, sort: $sort, filter: $filter) { + items { + id + firstName + lastName + email + gender + workPhones + employmentStartDate + employmentEndDate + aboutMe + birthDate + personalEmail + invitationStatus + language + isActive + nationality + personalPhone + pronouns + } + } + } +`; + +const listManagers = gql` + query listManagers($offset: Int, $limit: Int, $sort: [SortArgInput!], $filter: AvailableManagersFilterInput) { + availableManagers(offset: $offset, limit: $limit, sort: $sort, filter: $filter) { + items { + id + firstName + lastName + email + isActive + } + } + } +`; + +const listTeams = gql` + query listTeams($offset: Int, $limit: Int, $sort: [SortArgInput!]) { + teams(offset: $offset, limit: $limit, sort: $sort) { + items { + id + name + } + } + } +`; + +const listPositions = gql` + query listPositions($offset: Int, $limit: Int, $sort: [SortArgInput!]) { + positions(offset: $offset, limit: $limit, sort: $sort) { + items { + id + name + } + } + } +`; + +const listLocations = gql` + query listLocations($offset: Int, $limit: Int, $sort: [SortArgInput!]) { + locations(offset: $offset, limit: $limit, sort: $sort) { + items { + id + name + } + } + } +`; + +export default { + listUsers, + listManagers, + listTeams, + listPositions, + listLocations, +}; diff --git a/components/kiwihr/common/utils.mjs b/components/kiwihr/common/utils.mjs new file mode 100644 index 0000000000000..bc708574b1c2d --- /dev/null +++ b/components/kiwihr/common/utils.mjs @@ -0,0 +1,6 @@ +export const parseError = (response) => { + const errors = response.errors[0].data; + const errorsArray = Object.entries(errors); + const error = errorsArray[0]; + return `${error[0]} ${error[1].message}`; +}; diff --git a/components/kiwihr/kiwihr.app.mjs b/components/kiwihr/kiwihr.app.mjs index 66cfeb298f42b..b1467f1e368e2 100644 --- a/components/kiwihr/kiwihr.app.mjs +++ b/components/kiwihr/kiwihr.app.mjs @@ -1,244 +1,282 @@ -import { axios } from "@pipedream/platform"; +import { GraphQLClient } from "graphql-request"; +import { + GENDER_OPTIONS, LIMIT, +} from "./common/constants.mjs"; +import mutations from "./common/mutations.mjs"; +import queries from "./common/queries.mjs"; export default { type: "app", app: "kiwihr", propDefinitions: { - department: { - type: "string", - label: "Department", - description: "ID of the department to filter employees. Optional.", - optional: true, - async options() { - const items = await this.getDepartments(); - return items.map((e) => ({ - value: e.id, - label: e.name, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the employee", + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of the employee", + }, + email: { + type: "string", + label: "Email", + description: "The email of the employee", + }, + workPhones: { + type: "string[]", + label: "Work Phones", + description: "A list of employee's work phone numbers", + }, + employmentStartDate: { + type: "string", + label: "Employment Start Date", + description: "User's work started date. **Format YYYY-MM-DD**", + }, + aboutMe: { + type: "string", + label: "About Me", + description: "Short info about the user", + }, + gender: { + type: "string", + label: "Gender", + description: "The gender of the employee", + options: GENDER_OPTIONS, + }, + managerId: { + type: "string", + label: "Manager ID", + description: "ID of the user's manager", + async options({ page }) { + const { availableManagers: { items } } = await this.listManagers({ + sort: [ + { + "field": "firstName", + "direction": "asc", + }, + ], + limit: LIMIT, + offset: LIMIT * page, + }); + + return items.map(({ + id: value, firstName, lastName, email, + }) => ({ + label: `${firstName} ${lastName} (${email})`, + value, })); }, }, - location: { + nationality: { type: "string", - label: "Location", - description: "ID of the location to filter employees. Optional.", - optional: true, - async options() { - const items = await this.getLocations(); - return items.map((e) => ({ - value: e.id, - label: e.name, + label: "Nationality", + description: "2-digit Employee's nationality in [ISO 3166-1](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)", + }, + teamIds: { + type: "string[]", + label: "Team IDs", + description: "List of IDs of teams user belongs to", + async options({ page }) { + const { teams: { items } } = await this.listTeams({ + limit: LIMIT, + offset: LIMIT * page, + }); + + return items.map(({ + id: value, name: label, + }) => ({ + label, + value, })); }, }, - leaveType: { + positionId: { type: "string", - label: "Leave Type", - description: "Type of leave to filter requests. Optional.", - optional: true, - async options() { - const items = await this.getLeaveTypes(); - return items.map((e) => ({ - value: e.id, - label: e.name, + label: "Position ID", + description: "Employee's prosition ID", + async options({ page }) { + const { positions: { items } } = await this.listPositions({ + limit: LIMIT, + offset: LIMIT * page, + }); + + return items.map(({ + id: value, name: label, + }) => ({ + label, + value, })); }, }, - requestStatus: { - type: "string", - label: "Request Status", - description: "Status of the leave request to filter.", - optional: true, - async options() { - return [ - { - label: "Pending", - value: "pending", - }, - { - label: "Approved", - value: "approved", - }, - { - label: "Rejected", - value: "rejected", - }, - ]; + locationId: { + type: "string", + label: "Location ID", + description: "Employee's location ID", + async options({ page }) { + const { locations: { items } } = await this.listLocations({ + limit: LIMIT, + offset: LIMIT * page, + }); + + return items.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); }, }, - employeeId: { + birthDate: { type: "string", - label: "Employee ID", - description: "ID of the employee to watch for updates.", + label: "Birth Date", + description: "Employee's date of birth", }, - employeeName: { + personalPhone: { type: "string", - label: "Employee Name", - description: "Name of the employee to add.", + label: "Personal Phone", + description: "Employee's personal phone number", }, - email: { + personalEmail: { type: "string", - label: "Email", - description: "Email of the employee to add.", + label: "Personal Email", + description: "Employee's personal email address", }, - startDate: { + addressStreet: { type: "string", - label: "Start Date", - description: "Start date of the employee to add.", + label: "Street", + description: "The employee's street address", }, - jobTitle: { + addressCity: { type: "string", - label: "Job Title", - description: "Job title of the employee. Optional.", - optional: true, + label: "City", + description: "The employee's city address", }, - supervisor: { + addressState: { type: "string", - label: "Supervisor", - description: "ID of the supervisor for the employee. Optional.", - optional: true, - async options() { - const items = await this.getEmployees(); - return items.map((e) => ({ - value: e.id, - label: `${e.firstName} ${e.lastName}`, - })); - }, + label: "State", + description: "The employee's state address", + }, + addressPostalCode: { + type: "string", + label: "Postal Code", + description: "The employee's postal code address", }, - leaveRequestId: { + addressCountry: { type: "string", - label: "Leave Request ID", - description: "ID of the leave request to approve.", + label: "Country", + description: "The employee's country address", }, - approvalDate: { + pronouns: { type: "string", - label: "Approval Date", - description: "Date of approval for the leave request. Optional.", - optional: true, + label: "pronouns", + description: "The employee's pronouns", }, - message: { + employeeId: { type: "string", - label: "Message", - description: "Approval message. Optional.", - optional: true, + label: "Employee ID", + description: "ID of the employee you want to update", + async options({ page }) { + const { users: { items } } = await this.listUsers({ + limit: LIMIT, + offset: LIMIT * page, + }); + + return items.map(({ + id: value, firstName, lastName, email, + }) => ({ + label: `${firstName} ${lastName} (${email})`, + value, + })); + }, }, }, methods: { _baseUrl() { - return "https://api.kiwihr.com/api"; - }, - async _makeRequest(opts = {}) { - const { - $ = this, method = "GET", path = "/", headers, ...otherOpts - } = opts; - return axios($, { - ...otherOpts, - method, - url: this._baseUrl() + path, - headers: { - ...headers, - "X-Api-Key": this.$auth.api_key, - }, - }); + return `${this.$auth.api_url}/api/graphql`; }, - async getDepartments(opts = {}) { - return this._makeRequest({ - path: "/departments", - ...opts, - }); + _headers() { + return { + "X-Api-Key": `${this.$auth.api_key}`, + }; }, - async getLocations(opts = {}) { - return this._makeRequest({ - path: "/locations", - ...opts, - }); + getClient() { + const url = this._baseUrl(); + const options = { + headers: this._headers(), + }; + return new GraphQLClient(url, options); }, - async getLeaveTypes(opts = {}) { - return this._makeRequest({ - path: "/leave/types", - ...opts, - }); + _makeRequest({ + query, variables, + } = {}) { + return this.getClient().request(query, variables); }, - async getEmployees(opts = {}) { + createEmployee(variables) { return this._makeRequest({ - path: "/employees", - ...opts, + query: mutations.createEmployee, + variables, }); }, - async createEmployee(data) { + updateEmployee(variables) { return this._makeRequest({ - method: "POST", - path: "/employees", - data, + query: mutations.updateEmployee, + variables, }); }, - async updateEmployee({ - employeeId, ...data - }) { + listUsers(variables) { return this._makeRequest({ - method: "PUT", - path: `/employees/${employeeId}`, - data, + query: queries.listUsers, + variables, }); }, - async approveLeaveRequest({ - leaveRequestId, approvalDate, message, - }) { + listManagers(variables) { return this._makeRequest({ - method: "POST", - path: `/leave/requests/${leaveRequestId}/approve`, - data: { - approvalDate, - message, - }, + query: queries.listManagers, + variables, }); }, - async addEmployee({ - employeeName, email, startDate, department, jobTitle, location, - }) { - return this.createEmployee({ - employeeName, - email, - startDate, - department, - jobTitle, - location, + listTeams(variables) { + return this._makeRequest({ + query: queries.listTeams, + variables, }); }, - async onEmployeeAdded({ - department, location, - }) { - const employees = await this.getEmployees({ - params: { - department, - location, - }, + listPositions(variables) { + return this._makeRequest({ + query: queries.listPositions, + variables, }); - // Handle new employee event }, - async onLeaveRequestCreated({ - leaveType, requestStatus, - }) { - const requests = await this._makeRequest({ - path: "/leave/requests", - method: "GET", - params: { - leaveType, - requestStatus, - }, + listLocations(variables) { + return this._makeRequest({ + query: queries.listLocations, + variables, }); - // Handle leave request event }, - async onEmployeeUpdated({ - employeeId, jobTitle, department, + async *paginate({ + fn, variables = {}, fieldName, maxResults = null, }) { - const employee = await this.getEmployees({ - params: { - employeeId, - jobTitle, - department, - }, - }); - // Handle employee updated event + let hasMore = false; + let count = 0; + let page = 0; + + do { + variables.limit = LIMIT; + variables.offset = LIMIT * page++; + const data = await fn(variables); + for (const d of data[fieldName].items) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } + } + + hasMore = data[fieldName].items.length; + + } while (hasMore); }, }, }; diff --git a/components/kiwihr/package.json b/components/kiwihr/package.json index 1bb1546c948e9..e864a91de979a 100644 --- a/components/kiwihr/package.json +++ b/components/kiwihr/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/kiwihr", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream kiwiHR Components", "main": "kiwihr.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/kiwihr/sources/common/base.mjs b/components/kiwihr/sources/common/base.mjs new file mode 100644 index 0000000000000..817d1939b0620 --- /dev/null +++ b/components/kiwihr/sources/common/base.mjs @@ -0,0 +1,59 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import kiwihr from "../../kiwihr.app.mjs"; + +export default { + props: { + kiwihr, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastId() { + return this.db.get("lastId") || 0; + }, + _setLastId(lastId) { + this.db.set("lastId", lastId); + }, + async emitEvent(maxResults = false) { + const lastId = this._getLastId(); + + const response = this.kiwihr.paginate({ + fn: this.getFunction(), + fieldName: this.getFieldName(), + variables: this.getVariables(), + maxResults, + }); + + let responseArray = []; + for await (const item of response) { + if (item.id <= lastId) break; + responseArray.push(item); + } + + if (responseArray.length) { + this._setLastId(responseArray[0].id); + } + + for (const item of responseArray.reverse()) { + this.$emit(item, { + id: item.id, + summary: this.getSummary(item), + ts: Date.now(), + }); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, +}; diff --git a/components/kiwihr/sources/new-employee-instant/new-employee-instant.mjs b/components/kiwihr/sources/new-employee-instant/new-employee-instant.mjs deleted file mode 100644 index 97e11816fa39e..0000000000000 --- a/components/kiwihr/sources/new-employee-instant/new-employee-instant.mjs +++ /dev/null @@ -1,112 +0,0 @@ -import kiwihr from "../../kiwihr.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "kiwihr-new-employee-instant", - name: "New Employee Instant", - description: "Emit new event when a new employee is added to KiwiHR. [See the documentation](https://api.kiwihr.com/api/docs/)", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - kiwihr: { - type: "app", - app: "kiwihr", - }, - http: { - type: "$.interface.http", - customResponse: false, - }, - db: "$.service.db", - department: { - propDefinition: [ - kiwihr, - "department", - ], - }, - location: { - propDefinition: [ - kiwihr, - "location", - ], - }, - }, - methods: { - _getLastEmployeeAddedDate() { - return this.db.get("lastEmployeeAddedDate") || null; - }, - _setLastEmployeeAddedDate(date) { - this.db.set("lastEmployeeAddedDate", date); - }, - }, - hooks: { - async deploy() { - const params = { - departmentId: this.department, - locationId: this.location, - limit: 50, - sort: [ - { - field: "createdAt", - direction: "desc", - }, - ], - }; - const employees = await this.kiwihr.getEmployees({ - params, - }); - - for (const employee of employees.reverse()) { - const event = { - id: employee.id, - summary: `New employee: ${employee.firstName} ${employee.lastName}`, - ts: Date.parse(employee.createdAt), - }; - this.$emit(employee, event); - } - - if (employees.length > 0) { - const latestEmployee = employees[0]; - this._setLastEmployeeAddedDate(latestEmployee.createdAt); - } - }, - async activate() { - // Hook to create a webhook subscription if supported - }, - async deactivate() { - // Hook to delete a webhook subscription if created - }, - }, - async run(event) { - const { - department, location, - } = this; - await this.kiwihr.onEmployeeAdded({ - department, - location, - }); - - const latestDate = this._getLastEmployeeAddedDate(); - const employees = await this.kiwihr.getEmployees({ - params: { - departmentId: department, - locationId: location, - createdSince: latestDate, - }, - }); - - for (const employee of employees) { - const event = { - id: employee.id, - summary: `New employee: ${employee.firstName} ${employee.lastName}`, - ts: Date.parse(employee.createdAt), - }; - this.$emit(employee, event); - } - - if (employees.length > 0) { - const latestEmployee = employees[0]; - this._setLastEmployeeAddedDate(latestEmployee.createdAt); - } - }, -}; diff --git a/components/kiwihr/sources/new-employee/new-employee.mjs b/components/kiwihr/sources/new-employee/new-employee.mjs new file mode 100644 index 0000000000000..22cd6bd18f1c1 --- /dev/null +++ b/components/kiwihr/sources/new-employee/new-employee.mjs @@ -0,0 +1,35 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "kiwihr-new-employee", + name: "New Employee", + description: "Emit new event when a new employee is added to KiwiHR.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getFunction() { + return this.kiwihr.listUsers; + }, + getFieldName() { + return "users"; + }, + getVariables() { + return { + sort: [ + { + "field": "id", + "direction": "desc", + }, + ], + }; + }, + getSummary(item) { + return `New employee: ${item.firstName} ${item.lastName}`; + }, + }, + sampleEmit, +}; diff --git a/components/kiwihr/sources/new-employee/test-event.mjs b/components/kiwihr/sources/new-employee/test-event.mjs new file mode 100644 index 0000000000000..b91aec335a337 --- /dev/null +++ b/components/kiwihr/sources/new-employee/test-event.mjs @@ -0,0 +1,29 @@ +export default { + "id": 123, + "firstName": "String", + "lastName": "String", + "email": "String", + "gender": "String", + "workPhones": ["String"], + "employmentStartDate": "String", + "employmentEndDate": "String", + "aboutMe": "String", + "employeeNumber": "String", + "birthDate": "String", + "personalEmail": "String", + "address": "String", + "invitationStatus": "String", + "language": "String", + "position": "String", + "team": "String", + "teams": ["String"], + "location": "String", + "isActive": true, + "workSchedule": "String", + "manager": "String", + "nationality": "String", + "personalPhone": "String", + "socialAccounts": ["String"], + "pronouns": "String", + "customFieldValues": "String", +} \ No newline at end of file diff --git a/components/kiwihr/sources/new-leave-request-instant/new-leave-request-instant.mjs b/components/kiwihr/sources/new-leave-request-instant/new-leave-request-instant.mjs deleted file mode 100644 index e08441daea164..0000000000000 --- a/components/kiwihr/sources/new-leave-request-instant/new-leave-request-instant.mjs +++ /dev/null @@ -1,63 +0,0 @@ -import kiwihr from "../../kiwihr.app.mjs"; -import { axios } from "@pipedream/platform"; - -export default { - key: "kiwihr-new-leave-request-instant", - name: "New Leave Request Created", - description: "Emit new event when a leave request is created or submitted by an employee. [See the documentation](https://api.kiwihr.com/api/docs/)", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - kiwihr: { - type: "app", - app: "kiwihr", - }, - http: { - type: "$.interface.http", - customResponse: false, - }, - db: "$.service.db", - leaveType: { - propDefinition: [ - kiwihr, - "leaveType", - ], - }, - requestStatus: { - propDefinition: [ - kiwihr, - "requestStatus", - ], - }, - }, - hooks: { - async deploy() { - const leaveRequests = await this.kiwihr.onLeaveRequestCreated({ - leaveType: this.leaveType, - requestStatus: this.requestStatus, - }); - leaveRequests.slice(-50).forEach((request) => { - this.$emit(request, { - id: request.id, - summary: `New leave request from ${request.employeeName}`, - ts: new Date(request.createdDate).getTime(), - }); - }); - }, - async activate() { - // This source does not require creating webhooks via an API request - }, - async deactivate() { - // This source does not require deleting webhooks via an API request - }, - }, - async run(event) { - const { body } = event; - this.$emit(body, { - id: body.id, - summary: `New leave request from ${body.employeeName}`, - ts: new Date(body.createdDate).getTime(), - }); - }, -}; diff --git a/components/kiwihr/sources/updated-employee-record-instant/updated-employee-record-instant.mjs b/components/kiwihr/sources/updated-employee-record-instant/updated-employee-record-instant.mjs deleted file mode 100644 index 906d95139b150..0000000000000 --- a/components/kiwihr/sources/updated-employee-record-instant/updated-employee-record-instant.mjs +++ /dev/null @@ -1,125 +0,0 @@ -import kiwihr from "../../kiwihr.app.mjs"; -import crypto from "crypto"; -import { axios } from "@pipedream/platform"; - -export default { - key: "kiwihr-updated-employee-record-instant", - name: "KiwiHR Updated Employee Record Instant", - description: "Emit new event when an employee record is updated in KiwiHR. [See the documentation](https://api.kiwihr.it/api/docs)", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - kiwihr, - http: { - type: "$.interface.http", - customResponse: true, - }, - db: "$.service.db", - employeeId: { - propDefinition: [ - kiwihr, - "employeeId", - ], - }, - department: { - propDefinition: [ - kiwihr, - "department", - ], - }, - jobTitle: { - propDefinition: [ - kiwihr, - "jobTitle", - ], - }, - }, - methods: { - _getWebhookId() { - return this.db.get("webhookId"); - }, - _setWebhookId(id) { - this.db.set("webhookId", id); - }, - }, - hooks: { - async deploy() { - const employees = await this.kiwihr.getEmployees({ - params: { - limit: 50, - order: "desc", - }, - }); - for (const employee of employees.items) { - this.$emit(employee, { - id: employee.id, - summary: `Employee Updated: ${employee.firstName} ${employee.lastName}`, - ts: Date.parse(employee.updatedAt), - }); - } - }, - async activate() { - const hookResponse = await axios(this, { - method: "POST", - url: `${this.kiwihr._baseUrl()}/webhooks`, - headers: { - "X-Api-Key": this.kiwihr.$auth.api_key, - }, - data: { - targetUrl: this.http.endpoint, - eventTypes: [ - "employee.updated", - ], - }, - }); - this._setWebhookId(hookResponse.id); - }, - async deactivate() { - const webhookId = this._getWebhookId(); - if (webhookId) { - await axios(this, { - method: "DELETE", - url: `${this.kiwihr._baseUrl()}/webhooks/${webhookId}`, - headers: { - "X-Api-Key": this.kiwihr.$auth.api_key, - }, - }); - } - }, - }, - async run(event) { - const signature = event.headers["x-signature"]; - const computedSignature = crypto - .createHmac("sha256", this.kiwihr.$auth.api_key) - .update(event.body) - .digest("base64"); - - if (signature !== computedSignature) { - this.http.respond({ - status: 401, - body: "Unauthorized", - }); - return; - } - - const { - employeeId, jobTitle, department, - } = event.body; - - if (employeeId !== this.employeeId) return; - if (this.jobTitle && jobTitle !== this.jobTitle) return; - if (this.department && department !== this.department) return; - - this.$emit(event.body, { - id: event.body.id, - summary: `Updated Employee: ${event.body.firstName} ${event.body.lastName}`, - ts: Date.parse(event.body.updatedAt), - }); - - this.http.respond({ - status: 200, - body: "OK", - }); - }, -}; From e535fbb939b4a4f1978e40a35cfffaaf64e85089 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 5 May 2025 19:13:37 -0300 Subject: [PATCH 3/7] pnpm update --- pnpm-lock.yaml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0cafdf2882d35..1fc6a87c8073d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3176,8 +3176,7 @@ importers: components/danny_test_app: {} - components/dappier: - specifiers: {} + components/dappier: {} components/darksky_api: dependencies: @@ -6924,7 +6923,11 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/kiwihr: {} + components/kiwihr: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/kizeo_forms: dependencies: From b47e4cec61b8df9f1940e4837e64f9897f5777f7 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 5 May 2025 20:16:08 -0300 Subject: [PATCH 4/7] Update components/kiwihr/actions/update-employee-record/update-employee-record.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../actions/update-employee-record/update-employee-record.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/kiwihr/actions/update-employee-record/update-employee-record.mjs b/components/kiwihr/actions/update-employee-record/update-employee-record.mjs index 31e76607d9b48..be316b1dff4f9 100644 --- a/components/kiwihr/actions/update-employee-record/update-employee-record.mjs +++ b/components/kiwihr/actions/update-employee-record/update-employee-record.mjs @@ -177,7 +177,8 @@ export default { if (addressPostalCode) address.postalCode = addressPostalCode; if (addressCountry) address.country = addressCountry; - if (Object.keys(address) > 0) { +- if (Object.keys(address) > 0) { ++ if (Object.keys(address).length > 0) { user.address = address; } From bbbeffd0cd2235b8cda32780104a66af33c6e2e9 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 5 May 2025 20:16:47 -0300 Subject: [PATCH 5/7] Update components/kiwihr/actions/create-employee/create-employee.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- components/kiwihr/actions/create-employee/create-employee.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/kiwihr/actions/create-employee/create-employee.mjs b/components/kiwihr/actions/create-employee/create-employee.mjs index 6fe21d1a67898..c4ba441c558e1 100644 --- a/components/kiwihr/actions/create-employee/create-employee.mjs +++ b/components/kiwihr/actions/create-employee/create-employee.mjs @@ -174,7 +174,7 @@ export default { if (addressPostalCode) address.postalCode = addressPostalCode; if (addressCountry) address.country = addressCountry; - if (Object.keys(address)) { + if (Object.keys(address).length > 0) { user.address = address; } From ee0dbd07fae63baac2b825646c7ee3f75af22820 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 6 May 2025 12:27:39 -0300 Subject: [PATCH 6/7] Update components/kiwihr/kiwihr.app.mjs Co-authored-by: michelle0927 --- components/kiwihr/kiwihr.app.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/kiwihr/kiwihr.app.mjs b/components/kiwihr/kiwihr.app.mjs index b1467f1e368e2..2bf9b792e3c76 100644 --- a/components/kiwihr/kiwihr.app.mjs +++ b/components/kiwihr/kiwihr.app.mjs @@ -95,7 +95,7 @@ export default { positionId: { type: "string", label: "Position ID", - description: "Employee's prosition ID", + description: "Employee's position ID", async options({ page }) { const { positions: { items } } = await this.listPositions({ limit: LIMIT, From d24b5418677f7d6f511746d8127a3456078d23cf Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 6 May 2025 12:28:28 -0300 Subject: [PATCH 7/7] Update components/kiwihr/actions/update-employee-record/update-employee-record.mjs Co-authored-by: michelle0927 --- .../actions/update-employee-record/update-employee-record.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/kiwihr/actions/update-employee-record/update-employee-record.mjs b/components/kiwihr/actions/update-employee-record/update-employee-record.mjs index be316b1dff4f9..64f4e8bdd8afc 100644 --- a/components/kiwihr/actions/update-employee-record/update-employee-record.mjs +++ b/components/kiwihr/actions/update-employee-record/update-employee-record.mjs @@ -177,8 +177,7 @@ export default { if (addressPostalCode) address.postalCode = addressPostalCode; if (addressCountry) address.country = addressCountry; -- if (Object.keys(address) > 0) { -+ if (Object.keys(address).length > 0) { + if (Object.keys(address).length > 0) { user.address = address; }