From 62e645fc6d80fc42cfbe0bd69da08c2864aedf2a Mon Sep 17 00:00:00 2001 From: seynadio <79858321+seynadio@users.noreply.github.com> Date: Mon, 14 Jul 2025 13:47:12 +0200 Subject: [PATCH 1/3] Complete Freshservice integration to match Freshdesk functionality - Add comprehensive app with all prop definitions and API methods - Add constants and utilities for consistent data handling - Add all 11 actions matching Freshdesk: * create-ticket, update-ticket (with internal notes support) * get-ticket, list-all-tickets * create-contact, create-company * assign-ticket-to-agent, assign-ticket-to-group * close-ticket, set-ticket-priority, set-ticket-status - Add internal notes support in update-ticket action - Enhance new-ticket source with proper date filtering and pagination - Add new-contact source with complete implementation - Update package.json with required dependencies (moment, async-retry) - Full parity with Freshdesk integration functionality --- .../assign-ticket-to-agent.mjs | 37 +++ .../assign-ticket-to-group.mjs | 37 +++ .../actions/close-ticket/close-ticket.mjs | 31 +++ .../actions/create-company/create-company.mjs | 109 ++++++++ .../actions/create-contact/create-contact.mjs | 110 ++++++++ .../actions/create-ticket/create-ticket.mjs | 142 ++++++++++ .../actions/get-ticket/get-ticket.mjs | 27 ++ .../list-all-tickets/list-all-tickets.mjs | 55 ++++ .../set-ticket-priority.mjs | 39 +++ .../set-ticket-status/set-ticket-status.mjs | 39 +++ .../actions/update-ticket/update-ticket.mjs | 163 +++++++++++ components/freshservice/common/constants.mjs | 72 +++++ components/freshservice/common/utils.mjs | 29 ++ components/freshservice/freshservice.app.mjs | 259 +++++++++++++++++- components/freshservice/package.json | 6 +- .../sources/new-contact/new-contact.mjs | 84 ++++++ .../sources/new-ticket/new-ticket.mjs | 60 +++- 17 files changed, 1284 insertions(+), 15 deletions(-) create mode 100644 components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs create mode 100644 components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs create mode 100644 components/freshservice/actions/close-ticket/close-ticket.mjs create mode 100644 components/freshservice/actions/create-company/create-company.mjs create mode 100644 components/freshservice/actions/create-contact/create-contact.mjs create mode 100644 components/freshservice/actions/create-ticket/create-ticket.mjs create mode 100644 components/freshservice/actions/get-ticket/get-ticket.mjs create mode 100644 components/freshservice/actions/list-all-tickets/list-all-tickets.mjs create mode 100644 components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs create mode 100644 components/freshservice/actions/set-ticket-status/set-ticket-status.mjs create mode 100644 components/freshservice/actions/update-ticket/update-ticket.mjs create mode 100644 components/freshservice/common/constants.mjs create mode 100644 components/freshservice/common/utils.mjs create mode 100644 components/freshservice/sources/new-contact/new-contact.mjs diff --git a/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs b/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs new file mode 100644 index 0000000000000..afb9ce4634495 --- /dev/null +++ b/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs @@ -0,0 +1,37 @@ +import freshservice from "../../freshservice.app.mjs"; + +export default { + key: "freshservice-assign-ticket-to-agent", + name: "Assign Ticket to Agent", + description: "Assign a ticket to an agent in Freshservice. [See the documentation](https://api.freshservice.com/v2/#update_ticket)", + version: "0.0.1", + type: "action", + props: { + freshservice, + ticketId: { + propDefinition: [ + freshservice, + "ticketId", + ], + }, + responder_id: { + propDefinition: [ + freshservice, + "agentId", + ], + }, + }, + async run({ $ }) { + const response = await this.freshservice.updateTicket({ + ticketId: this.ticketId, + data: { + responder_id: this.responder_id, + }, + $, + }); + + const ticketName = await this.freshservice.getTicketName(this.ticketId); + $.export("$summary", `Successfully assigned ticket "${ticketName}" to agent`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs b/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs new file mode 100644 index 0000000000000..b1e501118fb30 --- /dev/null +++ b/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs @@ -0,0 +1,37 @@ +import freshservice from "../../freshservice.app.mjs"; + +export default { + key: "freshservice-assign-ticket-to-group", + name: "Assign Ticket to Group", + description: "Assign a ticket to a group in Freshservice. [See the documentation](https://api.freshservice.com/v2/#update_ticket)", + version: "0.0.1", + type: "action", + props: { + freshservice, + ticketId: { + propDefinition: [ + freshservice, + "ticketId", + ], + }, + group_id: { + propDefinition: [ + freshservice, + "groupId", + ], + }, + }, + async run({ $ }) { + const response = await this.freshservice.updateTicket({ + ticketId: this.ticketId, + data: { + group_id: this.group_id, + }, + $, + }); + + const ticketName = await this.freshservice.getTicketName(this.ticketId); + $.export("$summary", `Successfully assigned ticket "${ticketName}" to group`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/actions/close-ticket/close-ticket.mjs b/components/freshservice/actions/close-ticket/close-ticket.mjs new file mode 100644 index 0000000000000..a10fd0dafa58f --- /dev/null +++ b/components/freshservice/actions/close-ticket/close-ticket.mjs @@ -0,0 +1,31 @@ +import freshservice from "../../freshservice.app.mjs"; + +export default { + key: "freshservice-close-ticket", + name: "Close Ticket", + description: "Close a ticket in Freshservice. [See the documentation](https://api.freshservice.com/v2/#update_ticket)", + version: "0.0.1", + type: "action", + props: { + freshservice, + ticketId: { + propDefinition: [ + freshservice, + "ticketId", + ], + }, + }, + async run({ $ }) { + const response = await this.freshservice.updateTicket({ + ticketId: this.ticketId, + data: { + status: 5, // Closed + }, + $, + }); + + const ticketName = await this.freshservice.getTicketName(this.ticketId); + $.export("$summary", `Successfully closed ticket "${ticketName}"`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/actions/create-company/create-company.mjs b/components/freshservice/actions/create-company/create-company.mjs new file mode 100644 index 0000000000000..ad5979cc436ca --- /dev/null +++ b/components/freshservice/actions/create-company/create-company.mjs @@ -0,0 +1,109 @@ +import freshservice from "../../freshservice.app.mjs"; +import { removeNullEntries } from "../../common/utils.mjs"; + +export default { + key: "freshservice-create-company", + name: "Create Company", + description: "Create a new company in Freshservice. [See the documentation](https://api.freshservice.com/v2/#create_company)", + version: "0.0.1", + type: "action", + props: { + freshservice, + name: { + type: "string", + label: "Name", + description: "Name of the company", + }, + description: { + type: "string", + label: "Description", + description: "Description of the company", + optional: true, + }, + note: { + type: "string", + label: "Note", + description: "Note about the company", + optional: true, + }, + domains: { + type: "string[]", + label: "Domains", + description: "Domains associated with the company", + optional: true, + }, + primary_email: { + type: "string", + label: "Primary Email", + description: "Primary email of the company", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "Phone number of the company", + optional: true, + }, + address: { + type: "string", + label: "Address", + description: "Address of the company", + optional: true, + }, + city: { + type: "string", + label: "City", + description: "City of the company", + optional: true, + }, + state: { + type: "string", + label: "State", + description: "State of the company", + optional: true, + }, + zip_code: { + type: "string", + label: "Zip Code", + description: "Zip code of the company", + optional: true, + }, + country: { + type: "string", + label: "Country", + description: "Country of the company", + optional: true, + }, + custom_fields: { + type: "object", + label: "Custom Fields", + description: "Custom fields as a JSON object", + optional: true, + }, + }, + async run({ $ }) { + const { + custom_fields, + domains, + ...otherProps + } = this; + + const data = removeNullEntries(otherProps); + + if (custom_fields) { + data.custom_fields = this.freshservice.parseIfJSONString(custom_fields); + } + + if (domains && domains.length > 0) { + data.domains = domains; + } + + const response = await this.freshservice.createCompany({ + data, + $, + }); + + $.export("$summary", `Successfully created company: ${response.company?.name}`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/actions/create-contact/create-contact.mjs b/components/freshservice/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..3495b0d139bbc --- /dev/null +++ b/components/freshservice/actions/create-contact/create-contact.mjs @@ -0,0 +1,110 @@ +import { ConfigurationError } from "@pipedream/platform"; +import freshservice from "../../freshservice.app.mjs"; +import { removeNullEntries } from "../../common/utils.mjs"; + +export default { + key: "freshservice-create-contact", + name: "Create Contact", + description: "Create a new contact in Freshservice. [See the documentation](https://api.freshservice.com/v2/#create_requester)", + version: "0.0.1", + type: "action", + props: { + freshservice, + first_name: { + type: "string", + label: "First Name", + description: "First name of the contact", + }, + last_name: { + type: "string", + label: "Last Name", + description: "Last name of the contact", + optional: true, + }, + email: { + type: "string", + label: "Email", + description: "Primary email address of the contact", + optional: true, + }, + secondary_emails: { + type: "string[]", + label: "Secondary Emails", + description: "Additional email addresses of the contact", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "Phone number of the contact", + optional: true, + }, + mobile: { + type: "string", + label: "Mobile", + description: "Mobile number of the contact", + optional: true, + }, + job_title: { + type: "string", + label: "Job Title", + description: "Job title of the contact", + optional: true, + }, + department_id: { + type: "string", + label: "Department ID", + description: "Department ID of the contact", + optional: true, + }, + company_id: { + propDefinition: [ + freshservice, + "companyId", + ], + optional: true, + }, + address: { + type: "string", + label: "Address", + description: "Address of the contact", + optional: true, + }, + time_zone: { + type: "string", + label: "Time Zone", + description: "Time zone of the contact", + optional: true, + }, + language: { + type: "string", + label: "Language", + description: "Language of the contact", + optional: true, + }, + }, + async run({ $ }) { + const { + secondary_emails, + ...otherProps + } = this; + + if (!this.email && !this.phone) { + throw new ConfigurationError("Either email or phone must be provided"); + } + + const data = removeNullEntries(otherProps); + + if (secondary_emails && secondary_emails.length > 0) { + data.secondary_emails = secondary_emails; + } + + const response = await this.freshservice.createContact({ + data, + $, + }); + + $.export("$summary", `Successfully created contact: ${response.requester?.first_name} ${response.requester?.last_name || ""}`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/actions/create-ticket/create-ticket.mjs b/components/freshservice/actions/create-ticket/create-ticket.mjs new file mode 100644 index 0000000000000..9c2603e160d0d --- /dev/null +++ b/components/freshservice/actions/create-ticket/create-ticket.mjs @@ -0,0 +1,142 @@ +import freshservice from "../../freshservice.app.mjs"; +import { removeNullEntries } from "../../common/utils.mjs"; + +export default { + key: "freshservice-create-ticket", + name: "Create Ticket", + description: "Create a new ticket in Freshservice. [See the documentation](https://api.freshservice.com/v2/#create_ticket)", + version: "0.0.1", + type: "action", + props: { + freshservice, + subject: { + type: "string", + label: "Subject", + description: "Subject of the ticket", + }, + description: { + type: "string", + label: "Description", + description: "Description of the ticket", + }, + email: { + type: "string", + label: "Email", + description: "Email of the requester", + }, + priority: { + propDefinition: [ + freshservice, + "ticketPriority", + ], + optional: true, + }, + status: { + propDefinition: [ + freshservice, + "ticketStatus", + ], + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "Phone number of the requester", + optional: true, + }, + name: { + type: "string", + label: "Name", + description: "Name of the requester", + optional: true, + }, + company_id: { + propDefinition: [ + freshservice, + "companyId", + ], + optional: true, + }, + type: { + type: "string", + label: "Type", + description: "Type of the ticket", + options: [ + "Incident", + "Service Request", + "Change", + "Problem", + "Release", + ], + optional: true, + }, + source: { + type: "string", + label: "Source", + description: "Source of the ticket", + options: [ + "Email", + "Portal", + "Phone", + "Chat", + "Feedback Widget", + "Yammer", + "AWS Cloudwatch", + "Pagerduty", + "Walkup", + "Slack", + ], + optional: true, + }, + urgency: { + type: "string", + label: "Urgency", + description: "Urgency of the ticket", + options: [ + "Low", + "Medium", + "High", + "Critical", + ], + optional: true, + }, + impact: { + type: "string", + label: "Impact", + description: "Impact of the ticket", + options: [ + "Low", + "Medium", + "High", + "Critical", + ], + optional: true, + }, + custom_fields: { + type: "object", + label: "Custom Fields", + description: "Custom fields as a JSON object", + optional: true, + }, + }, + async run({ $ }) { + const { + custom_fields, + ...otherProps + } = this; + + const data = removeNullEntries(otherProps); + + if (custom_fields) { + data.custom_fields = this.freshservice.parseIfJSONString(custom_fields); + } + + const response = await this.freshservice.createTicket({ + data, + $, + }); + + $.export("$summary", `Successfully created ticket: ${response.ticket?.subject || response.ticket?.id}`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/actions/get-ticket/get-ticket.mjs b/components/freshservice/actions/get-ticket/get-ticket.mjs new file mode 100644 index 0000000000000..ecee11e8c2809 --- /dev/null +++ b/components/freshservice/actions/get-ticket/get-ticket.mjs @@ -0,0 +1,27 @@ +import freshservice from "../../freshservice.app.mjs"; + +export default { + key: "freshservice-get-ticket", + name: "Get Ticket", + description: "Get details of a ticket in Freshservice. [See the documentation](https://api.freshservice.com/v2/#view_ticket)", + version: "0.0.1", + type: "action", + props: { + freshservice, + ticketId: { + propDefinition: [ + freshservice, + "ticketId", + ], + }, + }, + async run({ $ }) { + const response = await this.freshservice.getTicket({ + ticketId: this.ticketId, + $, + }); + + $.export("$summary", `Successfully retrieved ticket: ${response.ticket?.subject || this.ticketId}`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/actions/list-all-tickets/list-all-tickets.mjs b/components/freshservice/actions/list-all-tickets/list-all-tickets.mjs new file mode 100644 index 0000000000000..7fa6382267d06 --- /dev/null +++ b/components/freshservice/actions/list-all-tickets/list-all-tickets.mjs @@ -0,0 +1,55 @@ +import freshservice from "../../freshservice.app.mjs"; + +export default { + key: "freshservice-list-all-tickets", + name: "List All Tickets", + description: "List all tickets in Freshservice. [See the documentation](https://api.freshservice.com/v2/#list_all_tickets)", + version: "0.0.1", + type: "action", + props: { + freshservice, + orderBy: { + propDefinition: [ + freshservice, + "orderBy", + ], + optional: true, + }, + orderType: { + propDefinition: [ + freshservice, + "orderType", + ], + optional: true, + }, + limit: { + type: "integer", + label: "Limit", + description: "Maximum number of tickets to return (max 100)", + optional: true, + default: 30, + max: 100, + }, + }, + async run({ $ }) { + const params = { + per_page: this.limit, + }; + + if (this.orderBy) { + params.order_by = this.orderBy; + } + if (this.orderType) { + params.order_type = this.orderType; + } + + const response = await this.freshservice.listTickets({ + params, + $, + }); + + const tickets = response.tickets || []; + $.export("$summary", `Successfully retrieved ${tickets.length} tickets`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs b/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs new file mode 100644 index 0000000000000..da298bf54cffc --- /dev/null +++ b/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs @@ -0,0 +1,39 @@ +import freshservice from "../../freshservice.app.mjs"; +import { TICKET_PRIORITY } from "../../common/constants.mjs"; + +export default { + key: "freshservice-set-ticket-priority", + name: "Set Ticket Priority", + description: "Set the priority of a ticket in Freshservice. [See the documentation](https://api.freshservice.com/v2/#update_ticket)", + version: "0.0.1", + type: "action", + props: { + freshservice, + ticketId: { + propDefinition: [ + freshservice, + "ticketId", + ], + }, + priority: { + propDefinition: [ + freshservice, + "ticketPriority", + ], + }, + }, + async run({ $ }) { + const response = await this.freshservice.updateTicket({ + ticketId: this.ticketId, + data: { + priority: this.priority, + }, + $, + }); + + const ticketName = await this.freshservice.getTicketName(this.ticketId); + const priorityLabel = TICKET_PRIORITY[this.priority]; + $.export("$summary", `Successfully set priority of ticket "${ticketName}" to ${priorityLabel}`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs b/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs new file mode 100644 index 0000000000000..b705d31e1aaa0 --- /dev/null +++ b/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs @@ -0,0 +1,39 @@ +import freshservice from "../../freshservice.app.mjs"; +import { TICKET_STATUS } from "../../common/constants.mjs"; + +export default { + key: "freshservice-set-ticket-status", + name: "Set Ticket Status", + description: "Set the status of a ticket in Freshservice. [See the documentation](https://api.freshservice.com/v2/#update_ticket)", + version: "0.0.1", + type: "action", + props: { + freshservice, + ticketId: { + propDefinition: [ + freshservice, + "ticketId", + ], + }, + status: { + propDefinition: [ + freshservice, + "ticketStatus", + ], + }, + }, + async run({ $ }) { + const response = await this.freshservice.updateTicket({ + ticketId: this.ticketId, + data: { + status: this.status, + }, + $, + }); + + const ticketName = await this.freshservice.getTicketName(this.ticketId); + const statusLabel = TICKET_STATUS[this.status]; + $.export("$summary", `Successfully set status of ticket "${ticketName}" to ${statusLabel}`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/actions/update-ticket/update-ticket.mjs b/components/freshservice/actions/update-ticket/update-ticket.mjs new file mode 100644 index 0000000000000..1200602e8502e --- /dev/null +++ b/components/freshservice/actions/update-ticket/update-ticket.mjs @@ -0,0 +1,163 @@ +import { ConfigurationError } from "@pipedream/platform"; +import freshservice from "../../freshservice.app.mjs"; +import { TICKET_STATUS, TICKET_PRIORITY } from "../../common/constants.mjs"; +import { removeNullEntries } from "../../common/utils.mjs"; + +export default { + key: "freshservice-update-ticket", + name: "Update Ticket", + description: "Update a ticket in Freshservice. Optionally add an internal note instead of updating the ticket. [See the documentation](https://api.freshservice.com/v2/#update_ticket)", + version: "0.0.1", + type: "action", + props: { + freshservice, + ticketId: { + propDefinition: [ + freshservice, + "ticketId", + ], + }, + status: { + propDefinition: [ + freshservice, + "ticketStatus", + ], + optional: true, + }, + priority: { + propDefinition: [ + freshservice, + "ticketPriority", + ], + optional: true, + }, + subject: { + type: "string", + label: "Subject", + description: "Subject of the ticket", + optional: true, + }, + description: { + type: "string", + label: "Description", + description: "Description of the ticket", + optional: true, + }, + group_id: { + propDefinition: [ + freshservice, + "groupId", + ], + optional: true, + }, + responder_id: { + propDefinition: [ + freshservice, + "agentId", + ], + optional: true, + }, + email: { + type: "string", + label: "Email", + description: "Email of the requester", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "Phone number of the requester", + optional: true, + }, + name: { + type: "string", + label: "Name", + description: "Name of the requester", + optional: true, + }, + type: { + type: "string", + label: "Type", + description: "Type of the ticket", + options: [ + "Incident", + "Service Request", + "Change", + "Problem", + "Release", + ], + optional: true, + }, + custom_fields: { + type: "object", + label: "Custom Fields", + description: "Custom fields as a JSON object", + optional: true, + }, + internalNote: { + type: "boolean", + label: "Internal Note", + description: "If true, add an internal note instead of updating the ticket", + optional: true, + default: false, + }, + noteBody: { + type: "string", + label: "Note Body", + description: "Body of the internal note (only used if Internal Note is true)", + optional: true, + }, + }, + async run({ $ }) { + const { + ticketId, + internalNote, + noteBody, + custom_fields, + ...otherProps + } = this; + + // Handle internal note creation + if (internalNote) { + if (!noteBody) { + throw new ConfigurationError("Note Body is required when Internal Note is enabled"); + } + + const noteResponse = await this.freshservice.createNote({ + ticketId, + data: { + body: noteBody, + private: true, + }, + $, + }); + + $.export("$summary", `Successfully added internal note to ticket ${ticketId}`); + return noteResponse; + } + + // Handle ticket update + const data = removeNullEntries(otherProps); + + if (custom_fields) { + data.custom_fields = this.freshservice.parseIfJSONString(custom_fields); + } + + // Validate that at least one field is provided for update + if (Object.keys(data).length === 0) { + throw new ConfigurationError("At least one field must be provided to update the ticket"); + } + + const response = await this.freshservice.updateTicket({ + ticketId, + data, + $, + }); + + const statusLabel = TICKET_STATUS[response.ticket?.status] || response.ticket?.status; + const priorityLabel = TICKET_PRIORITY[response.ticket?.priority] || response.ticket?.priority; + + $.export("$summary", `Successfully updated ticket ${ticketId}${statusLabel ? ` (Status: ${statusLabel})` : ""}${priorityLabel ? ` (Priority: ${priorityLabel})` : ""}`); + return response; + }, +}; \ No newline at end of file diff --git a/components/freshservice/common/constants.mjs b/components/freshservice/common/constants.mjs new file mode 100644 index 0000000000000..ebd1d2a80c03f --- /dev/null +++ b/components/freshservice/common/constants.mjs @@ -0,0 +1,72 @@ +export const PAGE_SIZE = 100; +export const DB_LAST_DATE_CHECK = "lastDateCheck"; + +export const TICKET_STATUS = { + 2: "Open", + 3: "Pending", + 4: "Resolved", + 5: "Closed", +}; + +export const TICKET_PRIORITY = { + 1: "Low", + 2: "Medium", + 3: "High", + 4: "Urgent", +}; + +export const STATUS_OPTIONS = [ + { + label: "Open", + value: 2, + }, + { + label: "Pending", + value: 3, + }, + { + label: "Resolved", + value: 4, + }, + { + label: "Closed", + value: 5, + }, +]; + +export const PRIORITY_OPTIONS = [ + { + label: "Low", + value: 1, + }, + { + label: "Medium", + value: 2, + }, + { + label: "High", + value: 3, + }, + { + label: "Urgent", + value: 4, + }, +]; + +export const TICKET_SORT_OPTIONS = [ + "created_at", + "due_by", + "updated_at", + "status", +]; + +export const ORDER_TYPE_OPTIONS = [ + { + label: "Ascending", + value: "asc", + }, + { + label: "Descending", + value: "desc", + }, +]; \ No newline at end of file diff --git a/components/freshservice/common/utils.mjs b/components/freshservice/common/utils.mjs new file mode 100644 index 0000000000000..30173b1128130 --- /dev/null +++ b/components/freshservice/common/utils.mjs @@ -0,0 +1,29 @@ +function removeNullEntries(obj) { + if (typeof obj === "number") { + return obj; + } + if (typeof obj === "boolean") { + return obj; + } + if (typeof obj === "string") { + return obj; + } + if (Array.isArray(obj)) { + return obj.filter((item) => item !== null && item !== undefined && item !== "") + .map((item) => removeNullEntries(item)); + } + if (typeof obj === "object" && obj !== null) { + const result = {}; + for (const key in obj) { + if (obj[key] !== null && obj[key] !== undefined && obj[key] !== "") { + result[key] = removeNullEntries(obj[key]); + } + } + return result; + } + return obj; +} + +export { + removeNullEntries, +}; \ No newline at end of file diff --git a/components/freshservice/freshservice.app.mjs b/components/freshservice/freshservice.app.mjs index 44f4558e415e2..48bcdb9d47021 100644 --- a/components/freshservice/freshservice.app.mjs +++ b/components/freshservice/freshservice.app.mjs @@ -1,9 +1,133 @@ import { axios } from "@pipedream/platform"; +import { + DB_LAST_DATE_CHECK, + PAGE_SIZE, + STATUS_OPTIONS, + PRIORITY_OPTIONS, + TICKET_SORT_OPTIONS, + ORDER_TYPE_OPTIONS, +} from "./common/constants.mjs"; +import { removeNullEntries } from "./common/utils.mjs"; export default { type: "app", app: "freshservice", - propDefinitions: {}, + propDefinitions: { + companyId: { + type: "string", + label: "Company ID", + description: "ID of the company to which the ticket belongs", + async options({ page }) { + const { companies } = await this.getCompanies({ + params: { + page: page + 1, + per_page: PAGE_SIZE, + }, + }); + return companies?.map((company) => ({ + label: company.name, + value: company.id, + })) || []; + }, + }, + ticketId: { + type: "string", + label: "Ticket ID", + description: "ID of the ticket", + async options({ page }) { + const { tickets } = await this.listTickets({ + params: { + page: page + 1, + per_page: PAGE_SIZE, + }, + }); + return tickets?.map((ticket) => ({ + label: ticket.subject, + value: ticket.id, + })) || []; + }, + }, + agentId: { + type: "string", + label: "Agent ID", + description: "ID of the agent", + async options({ page }) { + const { agents } = await this.getAgents({ + params: { + page: page + 1, + per_page: PAGE_SIZE, + }, + }); + return agents?.map((agent) => ({ + label: `${agent.contact.name} (${agent.contact.email})`, + value: agent.id, + })) || []; + }, + }, + groupId: { + type: "string", + label: "Group ID", + description: "ID of the group", + async options({ page }) { + const { groups } = await this.getGroups({ + params: { + page: page + 1, + per_page: PAGE_SIZE, + }, + }); + return groups?.map((group) => ({ + label: group.name, + value: group.id, + })) || []; + }, + }, + ticketStatus: { + type: "integer", + label: "Status", + description: "Status of the ticket", + options: STATUS_OPTIONS, + }, + ticketPriority: { + type: "integer", + label: "Priority", + description: "Priority of the ticket", + options: PRIORITY_OPTIONS, + }, + contactEmail: { + type: "string", + label: "Contact Email", + description: "Email of the contact", + async options({ + page, companyId, + }) { + const contacts = await this.getContacts({ + params: { + page: page + 1, + per_page: PAGE_SIZE, + company_id: companyId, + }, + }); + return contacts?.map((contact) => ({ + label: contact.email, + value: contact.email, + })) || []; + }, + }, + orderBy: { + type: "string", + label: "Order By", + description: "Sort tickets by this field", + options: TICKET_SORT_OPTIONS, + default: "created_at", + }, + orderType: { + type: "string", + label: "Order Type", + description: "Sort order", + options: ORDER_TYPE_OPTIONS, + default: "desc", + }, + }, methods: { _domain() { return this.$auth.domain; @@ -14,11 +138,17 @@ export default { _apiUrl() { return `https://${this._domain()}.freshservice.com/api`; }, + _getHeaders() { + return { + "Content-Type": "application/json", + }; + }, async _makeRequest({ $ = this, path, ...args }) { return axios($, { url: `${this._apiUrl()}${path}`, + headers: this._getHeaders(), auth: { username: this._apiKey(), password: "X", @@ -26,6 +156,133 @@ export default { ...args, }); }, + base64Encode(str) { + return Buffer.from(str).toString("base64"); + }, + parseIfJSONString(value) { + try { + return typeof value === "string" ? JSON.parse(value) : value; + } catch (error) { + return value; + } + }, + async setLastDateChecked(value) { + await this.db.set(DB_LAST_DATE_CHECK, value); + }, + async getLastDateChecked() { + return await this.db.get(DB_LAST_DATE_CHECK) || "2021-01-01T00:00:00Z"; + }, + // Company methods + async createCompany(args = {}) { + return this._makeRequest({ + method: "POST", + path: "/v2/companies", + ...args, + }); + }, + async getCompanies(args = {}) { + return this._makeRequest({ + path: "/v2/companies", + ...args, + }); + }, + // Contact methods + async createContact(args = {}) { + return this._makeRequest({ + method: "POST", + path: "/v2/requesters", + ...args, + }); + }, + async getContacts(args = {}) { + return this._makeRequest({ + path: "/v2/requesters", + ...args, + }); + }, + async searchContacts(args = {}) { + return this._makeRequest({ + path: "/v2/requesters/search", + ...args, + }); + }, + async filterContacts(args = {}) { + return this._makeRequest({ + path: "/v2/requesters/filter", + ...args, + }); + }, + // Agent methods + async getAgents(args = {}) { + return this._makeRequest({ + path: "/v2/agents", + ...args, + }); + }, + // Group methods + async getGroups(args = {}) { + return this._makeRequest({ + path: "/v2/groups", + ...args, + }); + }, + // Ticket methods + async createTicket(args = {}) { + return this._makeRequest({ + method: "POST", + path: "/v2/tickets", + ...args, + }); + }, + async getTicket({ + ticketId, ...args + }) { + return this._makeRequest({ + path: `/v2/tickets/${ticketId}`, + ...args, + }); + }, + async listTickets(args = {}) { + return this._makeRequest({ + path: "/v2/tickets", + ...args, + }); + }, + async updateTicket({ + ticketId, ...args + }) { + return this._makeRequest({ + method: "PUT", + path: `/v2/tickets/${ticketId}`, + ...args, + }); + }, + async searchTickets(args = {}) { + return this._makeRequest({ + path: "/v2/tickets/search", + ...args, + }); + }, + async filterTickets(args = {}) { + return this._makeRequest({ + path: "/v2/tickets/filter", + ...args, + }); + }, + async getTicketName(ticketId) { + const { ticket } = await this.getTicket({ ticketId }); + return ticket?.subject || ticketId; + }, + // Note methods + async createNote({ + ticketId, ...args + }) { + return this._makeRequest({ + method: "POST", + path: `/v2/tickets/${ticketId}/notes`, + ...args, + }); + }, async getTickets(args = {}) { return this._makeRequest({ path: "/v2/tickets", diff --git a/components/freshservice/package.json b/components/freshservice/package.json index 728e72d3243f5..f0fd59701b21d 100644 --- a/components/freshservice/package.json +++ b/components/freshservice/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/freshservice", - "version": "0.0.2", + "version": "0.1.0", "description": "Pipedream Freshservice Components", "main": "freshservice.app.mjs", "keywords": [ @@ -14,6 +14,8 @@ "access": "public" }, "dependencies": { - "@pipedream/platform": "^1.5.1" + "@pipedream/platform": "^3.0.3", + "async-retry": "^1.3.3", + "moment": "2.29.4" } } diff --git a/components/freshservice/sources/new-contact/new-contact.mjs b/components/freshservice/sources/new-contact/new-contact.mjs new file mode 100644 index 0000000000000..1b0856eeeb159 --- /dev/null +++ b/components/freshservice/sources/new-contact/new-contact.mjs @@ -0,0 +1,84 @@ +import moment from "moment"; +import freshservice from "../../freshservice.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + name: "New Contact", + version: "0.0.1", + key: "freshservice-new-contact", + description: "Emit new event for each created contact. [See documentation](https://api.freshservice.com/v2/#list_all_requesters)", + type: "source", + dedupe: "unique", + props: { + freshservice, + db: "$.service.db", + timer: { + type: "$.interface.timer", + static: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + emitEvent(data) { + this.$emit(data, { + id: data.id, + summary: `New contact: ${data.first_name} ${data.last_name || ""} (${data.email})`, + ts: Date.parse(data.created_at), + }); + }, + async *getContacts(params = {}) { + let page = 1; + let hasMore = true; + + while (hasMore) { + const { requesters } = await this.freshservice.filterContacts({ + params: { + ...params, + page, + per_page: 100, + }, + }); + + if (!requesters || requesters.length === 0) { + hasMore = false; + } else { + for (const contact of requesters) { + yield contact; + } + page++; + } + } + }, + async emitEvents() { + const lastDateChecked = await this.freshservice.getLastDateChecked(); + const formattedDate = moment(lastDateChecked).format("YYYY-MM-DDTHH:mm:ss[Z]"); + + const params = { + query: `"created_at:>'${formattedDate}'"`, + order_by: "created_at", + order_type: "asc", + }; + + let maxCreatedAt = lastDateChecked; + + for await (const contact of this.getContacts(params)) { + this.emitEvent(contact); + + if (contact.created_at > maxCreatedAt) { + maxCreatedAt = contact.created_at; + } + } + + await this.freshservice.setLastDateChecked(maxCreatedAt); + }, + }, + hooks: { + async deploy() { + await this.emitEvents(); + }, + }, + async run() { + await this.emitEvents(); + }, +}; \ No newline at end of file diff --git a/components/freshservice/sources/new-ticket/new-ticket.mjs b/components/freshservice/sources/new-ticket/new-ticket.mjs index c34f7e71676d8..88f76f7b72fed 100644 --- a/components/freshservice/sources/new-ticket/new-ticket.mjs +++ b/components/freshservice/sources/new-ticket/new-ticket.mjs @@ -1,15 +1,16 @@ -import app from "../../freshservice.app.mjs"; +import moment from "moment"; +import freshservice from "../../freshservice.app.mjs"; import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; export default { name: "New Ticket", - version: "0.0.2", + version: "0.0.3", key: "freshservice-new-ticket", - description: "Emit new event for each created ticket. [See documentation](https://api.freshservice.com/#view_all_ticket)", + description: "Emit new event for each created ticket. [See documentation](https://api.freshservice.com/v2/#list_all_tickets)", type: "source", dedupe: "unique", props: { - app, + freshservice, db: "$.service.db", timer: { type: "$.interface.timer", @@ -22,19 +23,54 @@ export default { emitEvent(data) { this.$emit(data, { id: data.id, - summary: `New ticket with ID ${data.id}`, + summary: `New ticket: ${data.subject || data.id}`, ts: Date.parse(data.created_at), }); }, + async *getTickets(params = {}) { + let page = 1; + let hasMore = true; + + while (hasMore) { + const { tickets } = await this.freshservice.filterTickets({ + params: { + ...params, + page, + per_page: 100, + }, + }); + + if (!tickets || tickets.length === 0) { + hasMore = false; + } else { + for (const ticket of tickets) { + yield ticket; + } + page++; + } + } + }, async emitEvents() { - const { tickets: resources } = await this.app.getTickets({ - params: { - filter: "new_and_my_open", - order_type: "desc", - }, - }); + const lastDateChecked = await this.freshservice.getLastDateChecked(); + const formattedDate = moment(lastDateChecked).format("YYYY-MM-DDTHH:mm:ss[Z]"); + + const params = { + query: `"created_at:>'${formattedDate}'"`, + order_by: "created_at", + order_type: "asc", + }; + + let maxCreatedAt = lastDateChecked; + + for await (const ticket of this.getTickets(params)) { + this.emitEvent(ticket); + + if (ticket.created_at > maxCreatedAt) { + maxCreatedAt = ticket.created_at; + } + } - resources.reverse().forEach(this.emitEvent); + await this.freshservice.setLastDateChecked(maxCreatedAt); }, }, hooks: { From dede90c9a82e4373f32c1d2b73efcb9f8951a179 Mon Sep 17 00:00:00 2001 From: Job Nijenhuis Date: Mon, 21 Jul 2025 08:54:39 +0200 Subject: [PATCH 2/3] Fix code review issues from PR #17608 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove duplicate getTickets method in freshservice.app.mjs - Add error handling for getTicketName calls in all ticket actions - Fix prop naming consistency (group_id -> groupId) in assign-ticket-to-group - Refactor constants to eliminate duplication between status/priority definitions - Update default date in getLastDateChecked to 30 days ago instead of hardcoded 2021 date All changes address CodeRabbit review comments to improve code quality and maintainability. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../assign-ticket-to-agent.mjs | 8 ++- .../assign-ticket-to-group.mjs | 12 +++-- .../actions/close-ticket/close-ticket.mjs | 8 ++- .../set-ticket-priority.mjs | 11 ++-- .../set-ticket-status/set-ticket-status.mjs | 11 ++-- components/freshservice/common/constants.mjs | 50 ++++++------------- components/freshservice/freshservice.app.mjs | 10 ++-- 7 files changed, 53 insertions(+), 57 deletions(-) diff --git a/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs b/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs index afb9ce4634495..cd3cd8b6cf989 100644 --- a/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs +++ b/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs @@ -30,8 +30,12 @@ export default { $, }); - const ticketName = await this.freshservice.getTicketName(this.ticketId); - $.export("$summary", `Successfully assigned ticket "${ticketName}" to agent`); + try { + const ticketName = await this.freshservice.getTicketName(this.ticketId); + $.export("$summary", `Successfully assigned ticket "${ticketName}" to agent`); + } catch (error) { + $.export("$summary", `Successfully assigned ticket ${this.ticketId} to agent`); + } return response; }, }; \ No newline at end of file diff --git a/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs b/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs index b1e501118fb30..7dc5e2a9a79f9 100644 --- a/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs +++ b/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs @@ -14,7 +14,7 @@ export default { "ticketId", ], }, - group_id: { + groupId: { propDefinition: [ freshservice, "groupId", @@ -25,13 +25,17 @@ export default { const response = await this.freshservice.updateTicket({ ticketId: this.ticketId, data: { - group_id: this.group_id, + group_id: this.groupId, }, $, }); - const ticketName = await this.freshservice.getTicketName(this.ticketId); - $.export("$summary", `Successfully assigned ticket "${ticketName}" to group`); + try { + const ticketName = await this.freshservice.getTicketName(this.ticketId); + $.export("$summary", `Successfully assigned ticket "${ticketName}" to group`); + } catch (error) { + $.export("$summary", `Successfully assigned ticket ${this.ticketId} to group`); + } return response; }, }; \ No newline at end of file diff --git a/components/freshservice/actions/close-ticket/close-ticket.mjs b/components/freshservice/actions/close-ticket/close-ticket.mjs index a10fd0dafa58f..09cc7e726f034 100644 --- a/components/freshservice/actions/close-ticket/close-ticket.mjs +++ b/components/freshservice/actions/close-ticket/close-ticket.mjs @@ -24,8 +24,12 @@ export default { $, }); - const ticketName = await this.freshservice.getTicketName(this.ticketId); - $.export("$summary", `Successfully closed ticket "${ticketName}"`); + try { + const ticketName = await this.freshservice.getTicketName(this.ticketId); + $.export("$summary", `Successfully closed ticket "${ticketName}"`); + } catch (error) { + $.export("$summary", `Successfully closed ticket ${this.ticketId}`); + } return response; }, }; \ No newline at end of file diff --git a/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs b/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs index da298bf54cffc..92702507addc9 100644 --- a/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs +++ b/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs @@ -31,9 +31,14 @@ export default { $, }); - const ticketName = await this.freshservice.getTicketName(this.ticketId); - const priorityLabel = TICKET_PRIORITY[this.priority]; - $.export("$summary", `Successfully set priority of ticket "${ticketName}" to ${priorityLabel}`); + try { + const ticketName = await this.freshservice.getTicketName(this.ticketId); + const priorityLabel = TICKET_PRIORITY[this.priority]; + $.export("$summary", `Successfully set priority of ticket "${ticketName}" to ${priorityLabel}`); + } catch (error) { + const priorityLabel = TICKET_PRIORITY[this.priority]; + $.export("$summary", `Successfully set priority of ticket ${this.ticketId} to ${priorityLabel}`); + } return response; }, }; \ No newline at end of file diff --git a/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs b/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs index b705d31e1aaa0..45e83b64d6a69 100644 --- a/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs +++ b/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs @@ -31,9 +31,14 @@ export default { $, }); - const ticketName = await this.freshservice.getTicketName(this.ticketId); - const statusLabel = TICKET_STATUS[this.status]; - $.export("$summary", `Successfully set status of ticket "${ticketName}" to ${statusLabel}`); + try { + const ticketName = await this.freshservice.getTicketName(this.ticketId); + const statusLabel = TICKET_STATUS[this.status]; + $.export("$summary", `Successfully set status of ticket "${ticketName}" to ${statusLabel}`); + } catch (error) { + const statusLabel = TICKET_STATUS[this.status]; + $.export("$summary", `Successfully set status of ticket ${this.ticketId} to ${statusLabel}`); + } return response; }, }; \ No newline at end of file diff --git a/components/freshservice/common/constants.mjs b/components/freshservice/common/constants.mjs index ebd1d2a80c03f..6ca15a3f67bda 100644 --- a/components/freshservice/common/constants.mjs +++ b/components/freshservice/common/constants.mjs @@ -15,43 +15,21 @@ export const TICKET_PRIORITY = { 4: "Urgent", }; -export const STATUS_OPTIONS = [ - { - label: "Open", - value: 2, - }, - { - label: "Pending", - value: 3, - }, - { - label: "Resolved", - value: 4, - }, - { - label: "Closed", - value: 5, - }, -]; +export const STATUS_OPTIONS = Object.entries(TICKET_STATUS).map(([ + value, + label, +]) => ({ + label, + value: parseInt(value), +})); -export const PRIORITY_OPTIONS = [ - { - label: "Low", - value: 1, - }, - { - label: "Medium", - value: 2, - }, - { - label: "High", - value: 3, - }, - { - label: "Urgent", - value: 4, - }, -]; +export const PRIORITY_OPTIONS = Object.entries(TICKET_PRIORITY).map(([ + value, + label, +]) => ({ + label, + value: parseInt(value), +})); export const TICKET_SORT_OPTIONS = [ "created_at", diff --git a/components/freshservice/freshservice.app.mjs b/components/freshservice/freshservice.app.mjs index 48bcdb9d47021..3a4419c708091 100644 --- a/components/freshservice/freshservice.app.mjs +++ b/components/freshservice/freshservice.app.mjs @@ -170,7 +170,9 @@ export default { await this.db.set(DB_LAST_DATE_CHECK, value); }, async getLastDateChecked() { - return await this.db.get(DB_LAST_DATE_CHECK) || "2021-01-01T00:00:00Z"; + const thirtyDaysAgo = new Date(); + thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); + return await this.db.get(DB_LAST_DATE_CHECK) || thirtyDaysAgo.toISOString(); }, // Company methods async createCompany(args = {}) { @@ -283,11 +285,5 @@ export default { ...args, }); }, - async getTickets(args = {}) { - return this._makeRequest({ - path: "/v2/tickets", - ...args, - }); - }, }, }; From 3e24a9eaa7360d5330431ac17e1198216abe2497 Mon Sep 17 00:00:00 2001 From: Job Nijenhuis Date: Mon, 21 Jul 2025 11:53:15 +0200 Subject: [PATCH 3/3] Fix lint errors and update pnpm lockfile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix all ESLint errors in freshservice component (newlines, trailing spaces, formatting) - Remove unused removeNullEntries import from freshservice.app.mjs - Update pnpm-lock.yaml to match package.json dependencies All CI checks should now pass. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../assign-ticket-to-agent.mjs | 2 +- .../assign-ticket-to-group.mjs | 2 +- .../actions/close-ticket/close-ticket.mjs | 2 +- .../actions/create-company/create-company.mjs | 4 ++-- .../actions/create-contact/create-contact.mjs | 4 ++-- .../actions/create-ticket/create-ticket.mjs | 4 ++-- .../actions/get-ticket/get-ticket.mjs | 2 +- .../list-all-tickets/list-all-tickets.mjs | 2 +- .../set-ticket-priority.mjs | 2 +- .../set-ticket-status/set-ticket-status.mjs | 2 +- .../actions/update-ticket/update-ticket.mjs | 16 +++++++++----- components/freshservice/common/constants.mjs | 6 +++--- components/freshservice/common/utils.mjs | 2 +- components/freshservice/freshservice.app.mjs | 9 +++++--- .../sources/new-contact/new-contact.mjs | 8 +++---- .../sources/new-ticket/new-ticket.mjs | 6 +++--- pnpm-lock.yaml | 21 ++++++++----------- 17 files changed, 50 insertions(+), 44 deletions(-) diff --git a/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs b/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs index cd3cd8b6cf989..625c254a50c89 100644 --- a/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs +++ b/components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs @@ -38,4 +38,4 @@ export default { } return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs b/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs index 7dc5e2a9a79f9..dd5d97a446c2b 100644 --- a/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs +++ b/components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs @@ -38,4 +38,4 @@ export default { } return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/actions/close-ticket/close-ticket.mjs b/components/freshservice/actions/close-ticket/close-ticket.mjs index 09cc7e726f034..c0275fd17ef5a 100644 --- a/components/freshservice/actions/close-ticket/close-ticket.mjs +++ b/components/freshservice/actions/close-ticket/close-ticket.mjs @@ -32,4 +32,4 @@ export default { } return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/actions/create-company/create-company.mjs b/components/freshservice/actions/create-company/create-company.mjs index ad5979cc436ca..028a3db28388e 100644 --- a/components/freshservice/actions/create-company/create-company.mjs +++ b/components/freshservice/actions/create-company/create-company.mjs @@ -89,7 +89,7 @@ export default { } = this; const data = removeNullEntries(otherProps); - + if (custom_fields) { data.custom_fields = this.freshservice.parseIfJSONString(custom_fields); } @@ -106,4 +106,4 @@ export default { $.export("$summary", `Successfully created company: ${response.company?.name}`); return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/actions/create-contact/create-contact.mjs b/components/freshservice/actions/create-contact/create-contact.mjs index 3495b0d139bbc..783fddd8c96e7 100644 --- a/components/freshservice/actions/create-contact/create-contact.mjs +++ b/components/freshservice/actions/create-contact/create-contact.mjs @@ -94,7 +94,7 @@ export default { } const data = removeNullEntries(otherProps); - + if (secondary_emails && secondary_emails.length > 0) { data.secondary_emails = secondary_emails; } @@ -107,4 +107,4 @@ export default { $.export("$summary", `Successfully created contact: ${response.requester?.first_name} ${response.requester?.last_name || ""}`); return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/actions/create-ticket/create-ticket.mjs b/components/freshservice/actions/create-ticket/create-ticket.mjs index 9c2603e160d0d..ca8b7dbe2e3dd 100644 --- a/components/freshservice/actions/create-ticket/create-ticket.mjs +++ b/components/freshservice/actions/create-ticket/create-ticket.mjs @@ -126,7 +126,7 @@ export default { } = this; const data = removeNullEntries(otherProps); - + if (custom_fields) { data.custom_fields = this.freshservice.parseIfJSONString(custom_fields); } @@ -139,4 +139,4 @@ export default { $.export("$summary", `Successfully created ticket: ${response.ticket?.subject || response.ticket?.id}`); return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/actions/get-ticket/get-ticket.mjs b/components/freshservice/actions/get-ticket/get-ticket.mjs index ecee11e8c2809..7e9f3faa2133e 100644 --- a/components/freshservice/actions/get-ticket/get-ticket.mjs +++ b/components/freshservice/actions/get-ticket/get-ticket.mjs @@ -24,4 +24,4 @@ export default { $.export("$summary", `Successfully retrieved ticket: ${response.ticket?.subject || this.ticketId}`); return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/actions/list-all-tickets/list-all-tickets.mjs b/components/freshservice/actions/list-all-tickets/list-all-tickets.mjs index 7fa6382267d06..4bc9ebdb15f26 100644 --- a/components/freshservice/actions/list-all-tickets/list-all-tickets.mjs +++ b/components/freshservice/actions/list-all-tickets/list-all-tickets.mjs @@ -52,4 +52,4 @@ export default { $.export("$summary", `Successfully retrieved ${tickets.length} tickets`); return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs b/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs index 92702507addc9..66ae3a494d65a 100644 --- a/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs +++ b/components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs @@ -41,4 +41,4 @@ export default { } return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs b/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs index 45e83b64d6a69..8ba23e41131d5 100644 --- a/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs +++ b/components/freshservice/actions/set-ticket-status/set-ticket-status.mjs @@ -41,4 +41,4 @@ export default { } return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/actions/update-ticket/update-ticket.mjs b/components/freshservice/actions/update-ticket/update-ticket.mjs index 1200602e8502e..b2eaa4eaa7b20 100644 --- a/components/freshservice/actions/update-ticket/update-ticket.mjs +++ b/components/freshservice/actions/update-ticket/update-ticket.mjs @@ -1,6 +1,8 @@ import { ConfigurationError } from "@pipedream/platform"; import freshservice from "../../freshservice.app.mjs"; -import { TICKET_STATUS, TICKET_PRIORITY } from "../../common/constants.mjs"; +import { + TICKET_STATUS, TICKET_PRIORITY, +} from "../../common/constants.mjs"; import { removeNullEntries } from "../../common/utils.mjs"; export default { @@ -138,7 +140,7 @@ export default { // Handle ticket update const data = removeNullEntries(otherProps); - + if (custom_fields) { data.custom_fields = this.freshservice.parseIfJSONString(custom_fields); } @@ -156,8 +158,12 @@ export default { const statusLabel = TICKET_STATUS[response.ticket?.status] || response.ticket?.status; const priorityLabel = TICKET_PRIORITY[response.ticket?.priority] || response.ticket?.priority; - - $.export("$summary", `Successfully updated ticket ${ticketId}${statusLabel ? ` (Status: ${statusLabel})` : ""}${priorityLabel ? ` (Priority: ${priorityLabel})` : ""}`); + + $.export("$summary", `Successfully updated ticket ${ticketId}${statusLabel + ? ` (Status: ${statusLabel})` + : ""}${priorityLabel + ? ` (Priority: ${priorityLabel})` + : ""}`); return response; }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/common/constants.mjs b/components/freshservice/common/constants.mjs index 6ca15a3f67bda..b07ed4023c90a 100644 --- a/components/freshservice/common/constants.mjs +++ b/components/freshservice/common/constants.mjs @@ -10,7 +10,7 @@ export const TICKET_STATUS = { export const TICKET_PRIORITY = { 1: "Low", - 2: "Medium", + 2: "Medium", 3: "High", 4: "Urgent", }; @@ -44,7 +44,7 @@ export const ORDER_TYPE_OPTIONS = [ value: "asc", }, { - label: "Descending", + label: "Descending", value: "desc", }, -]; \ No newline at end of file +]; diff --git a/components/freshservice/common/utils.mjs b/components/freshservice/common/utils.mjs index 30173b1128130..247577d17c4f4 100644 --- a/components/freshservice/common/utils.mjs +++ b/components/freshservice/common/utils.mjs @@ -26,4 +26,4 @@ function removeNullEntries(obj) { export { removeNullEntries, -}; \ No newline at end of file +}; diff --git a/components/freshservice/freshservice.app.mjs b/components/freshservice/freshservice.app.mjs index 3a4419c708091..3c08c26fb378b 100644 --- a/components/freshservice/freshservice.app.mjs +++ b/components/freshservice/freshservice.app.mjs @@ -7,7 +7,6 @@ import { TICKET_SORT_OPTIONS, ORDER_TYPE_OPTIONS, } from "./common/constants.mjs"; -import { removeNullEntries } from "./common/utils.mjs"; export default { type: "app", @@ -161,7 +160,9 @@ export default { }, parseIfJSONString(value) { try { - return typeof value === "string" ? JSON.parse(value) : value; + return typeof value === "string" + ? JSON.parse(value) + : value; } catch (error) { return value; } @@ -272,7 +273,9 @@ export default { }); }, async getTicketName(ticketId) { - const { ticket } = await this.getTicket({ ticketId }); + const { ticket } = await this.getTicket({ + ticketId, + }); return ticket?.subject || ticketId; }, // Note methods diff --git a/components/freshservice/sources/new-contact/new-contact.mjs b/components/freshservice/sources/new-contact/new-contact.mjs index 1b0856eeeb159..e6b171a4b675b 100644 --- a/components/freshservice/sources/new-contact/new-contact.mjs +++ b/components/freshservice/sources/new-contact/new-contact.mjs @@ -53,7 +53,7 @@ export default { async emitEvents() { const lastDateChecked = await this.freshservice.getLastDateChecked(); const formattedDate = moment(lastDateChecked).format("YYYY-MM-DDTHH:mm:ss[Z]"); - + const params = { query: `"created_at:>'${formattedDate}'"`, order_by: "created_at", @@ -61,10 +61,10 @@ export default { }; let maxCreatedAt = lastDateChecked; - + for await (const contact of this.getContacts(params)) { this.emitEvent(contact); - + if (contact.created_at > maxCreatedAt) { maxCreatedAt = contact.created_at; } @@ -81,4 +81,4 @@ export default { async run() { await this.emitEvents(); }, -}; \ No newline at end of file +}; diff --git a/components/freshservice/sources/new-ticket/new-ticket.mjs b/components/freshservice/sources/new-ticket/new-ticket.mjs index 88f76f7b72fed..0bc953fb4e6f3 100644 --- a/components/freshservice/sources/new-ticket/new-ticket.mjs +++ b/components/freshservice/sources/new-ticket/new-ticket.mjs @@ -53,7 +53,7 @@ export default { async emitEvents() { const lastDateChecked = await this.freshservice.getLastDateChecked(); const formattedDate = moment(lastDateChecked).format("YYYY-MM-DDTHH:mm:ss[Z]"); - + const params = { query: `"created_at:>'${formattedDate}'"`, order_by: "created_at", @@ -61,10 +61,10 @@ export default { }; let maxCreatedAt = lastDateChecked; - + for await (const ticket of this.getTickets(params)) { this.emitEvent(ticket); - + if (ticket.created_at > maxCreatedAt) { maxCreatedAt = ticket.created_at; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b38fc416d13e0..63d41d4fa82fd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5019,8 +5019,14 @@ importers: components/freshservice: dependencies: '@pipedream/platform': - specifier: ^1.5.1 - version: 1.6.6 + specifier: ^3.0.3 + version: 3.1.0 + async-retry: + specifier: ^1.3.3 + version: 1.3.3 + moment: + specifier: 2.29.4 + version: 2.29.4 components/freshstatus: dependencies: @@ -9840,8 +9846,7 @@ importers: components/paypro: {} - components/payrexx: - specifiers: {} + components/payrexx: {} components/paystack: dependencies: @@ -15877,14 +15882,6 @@ importers: specifier: ^6.0.0 version: 6.2.0 - modelcontextprotocol/node_modules2/@modelcontextprotocol/sdk/dist/cjs: {} - - modelcontextprotocol/node_modules2/@modelcontextprotocol/sdk/dist/esm: {} - - modelcontextprotocol/node_modules2/zod-to-json-schema/dist/cjs: {} - - modelcontextprotocol/node_modules2/zod-to-json-schema/dist/esm: {} - packages/ai: dependencies: '@pipedream/sdk':