diff --git a/components/freshdesk/actions/create-company/create-company.mjs b/components/freshdesk/actions/create-company/create-company.mjs index aeaea1f96851b..9771ebadd4460 100644 --- a/components/freshdesk/actions/create-company/create-company.mjs +++ b/components/freshdesk/actions/create-company/create-company.mjs @@ -1,35 +1,34 @@ -import { removeNullEntries } from "../../common/utils.mjs"; import freshdesk from "../../freshdesk.app.mjs"; export default { key: "freshdesk-create-company", name: "Create a Company", - description: "Create a company. [See docs here](https://developers.freshdesk.com/api/#companies)", - version: "0.0.1", + description: "Create a company. [See the documentation](https://developers.freshdesk.com/api/#create_company)", + version: "0.0.2", type: "action", props: { freshdesk, name: { type: "string", label: "Name", - description: "Name of the company.", + description: "Name of the company", }, description: { type: "string", label: "Description", - description: "Description of the company.", + description: "Description of the company", optional: true, }, note: { type: "string", label: "Note", - description: "Any specific note about the company.", + description: "Any specific note about the company", optional: true, }, industry: { type: "string", label: "Industry", - description: "The industry the company serves in.", + description: "The industry the company serves in", optional: true, }, domains: { @@ -40,18 +39,14 @@ export default { }, }, async run({ $ }) { - const payload = removeNullEntries({ - name: this.name, - domains: this.domains, - note: this.note, - industry: this.industry, - description: this.description, - }); - const response = await this.freshdesk.createCompany({ + const { + freshdesk, ...data + } = this; + const response = await freshdesk.createCompany({ $, - payload, + data, }); - response && $.export("$summary", "Company sucessfully created"); + response && $.export("$summary", "Company successfully created"); return response; }, }; diff --git a/components/freshdesk/actions/create-contact/create-contact.mjs b/components/freshdesk/actions/create-contact/create-contact.mjs index d926d8aba15b2..3722bb55dbc72 100644 --- a/components/freshdesk/actions/create-contact/create-contact.mjs +++ b/components/freshdesk/actions/create-contact/create-contact.mjs @@ -1,11 +1,10 @@ -import { removeNullEntries } from "../../common/utils.mjs"; import freshdesk from "../../freshdesk.app.mjs"; export default { key: "freshdesk-create-contact", name: "Create a Contact", - description: "Create a contact. [See docs here](https://developers.freshdesk.com/api/#create_contact)", - version: "0.0.1", + description: "Create a contact. [See the documentation](https://developers.freshdesk.com/api/#create_contact)", + version: "0.0.2", type: "action", props: { freshdesk, @@ -16,20 +15,20 @@ export default { }, email: { type: "string", - label: "Email", - description: "Primary email address of the contact. If you want to associate additional email(s) with this contact, use the other_emails attribute.", + label: "Email Address", + description: "Primary email address of the contact", optional: true, }, otherEmails: { type: "string[]", - label: "Additional email addresses", - description: "String array of additional email addresses.", + label: "Additional Email Addresses", + description: "One or more additional email addresses for the contact", optional: true, }, phone: { type: "string", - label: "Phone number", - description: "Telephone number of the contact.", + label: "Phone Number", + description: "Phone number of the contact", optional: true, }, companyId: { @@ -41,16 +40,16 @@ export default { }, }, async run({ $ }) { - const data = removeNullEntries({ - name: this.name, - email: this.email, - other_emails: this.otherEmails, - phone: this.phone, - company_id: this.companyId && Number(this.companyId), - }); + const { + companyId, otherEmails, ...data + } = this; const response = await this.freshdesk.createContact({ $, - data, + data: { + other_emails: otherEmails, + company_id: companyId && Number(companyId), + ...data, + }, }); response && $.export("$summary", "Contact successfully created"); return response; diff --git a/components/freshdesk/actions/create-ticket/create-ticket.mjs b/components/freshdesk/actions/create-ticket/create-ticket.mjs index 33bb4bb5c64f2..20a05fb75c651 100644 --- a/components/freshdesk/actions/create-ticket/create-ticket.mjs +++ b/components/freshdesk/actions/create-ticket/create-ticket.mjs @@ -1,11 +1,10 @@ import freshdesk from "../../freshdesk.app.mjs"; -import { removeNullEntries } from "../../common/utils.mjs"; export default { key: "freshdesk-create-ticket", name: "Create a Ticket", - description: "Create a ticket. [See docs here](https://developers.freshdesk.com/api/#tickets)", - version: "0.0.2", + description: "Create a ticket. [See the documentation](https://developers.freshdesk.com/api/#create_ticket)", + version: "0.0.3", type: "action", props: { freshdesk, @@ -14,7 +13,6 @@ export default { freshdesk, "companyId", ], - description: "ID of the company to which this ticket belongs", }, email: { propDefinition: [ @@ -24,7 +22,6 @@ export default { companyId, }), ], - description: "Email address of the requester.", optional: true, }, priority: { @@ -34,28 +31,28 @@ export default { ], default: 1, }, + subject: { + type: "string", + label: "Subject", + description: "Subject of the ticket", + optional: true, + }, description: { type: "string", label: "Description", - description: "HTML content of the ticket.", + description: "HTML content of the ticket", optional: true, }, descriptionText: { type: "string", label: "Description text", - description: "Content of the ticket in plain text.", + description: "Content of the ticket in plain text", optional: true, }, phone: { type: "string", label: "Phone number", - description: "Telephone number of the contact.", - optional: true, - }, - subject: { - type: "string", - label: "Subject", - description: "Subject of the ticket.", + description: "Phone number of the requester. If no contact exists with this phone number on Freshdesk, it will be added as a new contact", optional: true, }, status: { @@ -68,19 +65,16 @@ export default { }, }, async run({ $ }) { - const data = removeNullEntries({ - company_id: this.companyId && Number(this.companyId), - description: this.description, - description_text: this.descriptionText, - email: this.email, - phone: this.phone, - subject: this.subject, - status: this.status && Number(this.status), - priority: this.priority && Number(this.priority), - }); - const response = await this.freshdesk.createTicket({ + const { + freshdesk, companyId, descriptionText, ...data + } = this; + const response = await freshdesk.createTicket({ $, - data, + data: { + company_id: Number(companyId), + description_text: descriptionText, + ...data, + }, }); response && $.export("$summary", "Ticket successfully created"); return response; diff --git a/components/freshdesk/actions/get-ticket/get-ticket.mjs b/components/freshdesk/actions/get-ticket/get-ticket.mjs index 7525915a42fd7..3a59260c47ea4 100644 --- a/components/freshdesk/actions/get-ticket/get-ticket.mjs +++ b/components/freshdesk/actions/get-ticket/get-ticket.mjs @@ -3,23 +3,27 @@ import freshdesk from "../../freshdesk.app.mjs"; export default { key: "freshdesk-get-ticket", name: "Get Ticket Details", - description: "Get a Ticket. [See docs here](https://developers.freshdesk.com/api/#tickets)", - version: "0.0.1", + description: "Get details of a Ticket. [See the documentation](https://developers.freshdesk.com/api/#view_a_ticket)", + version: "0.1.0", type: "action", props: { freshdesk, - id: { - type: "string", - label: "Ticket ID", - description: "Ticket ID.", + ticketId: { + propDefinition: [ + freshdesk, + "ticketId", + ], }, }, async run({ $ }) { - const response = await this.freshdesk.getTicket({ + const { + freshdesk, ticketId, + } = this; + const response = await freshdesk.getTicket({ $, - id: this.id, + ticketId, }); - response && $.export("$summary", "Successfully found ticket"); + response && $.export("$summary", "Successfully retrieved ticket"); return response; }, }; diff --git a/components/freshdesk/actions/list-all-tickets/list-all-tickets.mjs b/components/freshdesk/actions/list-all-tickets/list-all-tickets.mjs index 8a78723c4b532..adefcc4be2ca6 100644 --- a/components/freshdesk/actions/list-all-tickets/list-all-tickets.mjs +++ b/components/freshdesk/actions/list-all-tickets/list-all-tickets.mjs @@ -1,25 +1,69 @@ -// legacy_hash_id: a_A6i5zz -import { axios } from "@pipedream/platform"; +import freshdesk from "../../freshdesk.app.mjs"; export default { key: "freshdesk-list-all-tickets", - name: "List All Tickets", - description: "Use filters to view only specific tickets (those which match the criteria that you choose). By default, only tickets that have not been deleted or marked as spam will be returned, unless you use the 'deleted' filter.", - version: "0.1.1", + name: "List Tickets", + description: + "Fetch up to 100 tickets according to the selected filters. [See the documentation](https://developers.freshdesk.com/api/#list_all_tickets)", + version: "0.2.0", type: "action", props: { - freshdesk: { - type: "app", - app: "freshdesk", + freshdesk, + orderBy: { + type: "string", + label: "Sort By", + description: "Which field to sort tickets by. Defaults to `Created At`", + optional: true, + options: [ + { + value: "created_at", + label: "Created At", + }, + { + value: "due_by", + label: "Due By", + }, + { + value: "updated_at", + label: "Updated At", + }, + { + value: "status", + label: "Status", + }, + ], + }, + orderType: { + type: "string", + label: "Sort Order", + description: + "Whether to sort in ascending or descending order. Defaults to descending", + optional: true, + options: [ + { + label: "Ascending", + value: "asc", + }, + { + label: "Descending", + value: "desc", + }, + ], }, }, async run({ $ }) { - return await axios($, { - url: `https://${this.freshdesk.$auth.domain}.freshdesk.com/api/v2/tickets`, - auth: { - username: `${this.freshdesk.$auth.api_key}:X`, - password: "", + const response = await this.freshdesk.listTickets({ + $, + params: { + order_by: this.orderBy, + order_type: this.orderType, }, }); + + const { length } = response; + $.export("$summary", `Successfully fetched ${length} ticket${length === 1 + ? "" + : "s"}`); + return response; }, }; diff --git a/components/freshdesk/common/constants.mjs b/components/freshdesk/common/constants.mjs index cc946f481251b..afaa9889c90c8 100644 --- a/components/freshdesk/common/constants.mjs +++ b/components/freshdesk/common/constants.mjs @@ -1,13 +1,5 @@ export default { - HTTP_PROTOCOL: "https://", - BASE_PATH: "/api", - VERSION_PATH: "/v2", PAGE_SIZE: 100, - retriableStatusCodes: [ - 408, - 429, - 500, - ], DB_LAST_DATE_CHECK: "DB_LAST_DATE_CHECK", TICKET_STATUS: [ { diff --git a/components/freshdesk/freshdesk.app.mjs b/components/freshdesk/freshdesk.app.mjs index adeec39f3adbf..39fde1cd6025b 100644 --- a/components/freshdesk/freshdesk.app.mjs +++ b/components/freshdesk/freshdesk.app.mjs @@ -1,8 +1,5 @@ import constants from "./common/constants.mjs"; -import { - axios, ConfigurationError, -} from "@pipedream/platform"; -import retry from "async-retry"; +import { axios } from "@pipedream/platform"; export default { type: "app", @@ -11,19 +8,39 @@ export default { companyId: { type: "integer", label: "Company ID", - description: "The ID of the company", + description: "Select a company or provide a company ID", async options() { const response = await this.getCompanies(); - return response.map((project) => ({ - label: project.name, - value: project.id, + return response.map(({ + id, name, + }) => ({ + label: name || id, + value: id, + })); + }, + }, + ticketId: { + type: "integer", + label: "Ticket ID", + description: "Select a ticket or provide a ticket ID", + async options({ page = 0 }) { + const response = await this.listTickets({ + params: { + page: page + 1, + }, + }); + return response.map(({ + id, subject, + }) => ({ + label: subject || id, + value: id, })); }, }, ticketStatus: { type: "integer", label: "Status", - description: "Status of the ticket.", + description: "Status of the ticket", options() { return constants.TICKET_STATUS; }, @@ -31,7 +48,7 @@ export default { ticketPriority: { type: "integer", label: "Priority", - description: "Priority of the ticket.", + description: "Priority of the ticket", options() { return constants.TICKET_PRIORITY; }, @@ -39,14 +56,18 @@ export default { contactEmail: { type: "string", label: "Email", - description: "Contact Email.", + description: "Select a contact or provide a contact's email", async options({ companyId }) { - const response = await this.getCompanies(); - const contacts = response.filter((contact) => contact.company_id === Number(companyId)); - return contacts.map((contact) => ({ - label: contact?.email ?? contact?.name, - value: contact.email, - })); + const contacts = await this.getContacts(); + const numId = Number(companyId); + return contacts + .filter((contact) => contact.company_id === numId) + .map(({ + email, name, + }) => ({ + label: name || email, + value: email, + })); }, }, }, @@ -66,64 +87,32 @@ export default { "Content-Type": "application/json;charset=utf-8", }; }, - _getUrl(path) { - const { - HTTP_PROTOCOL, - BASE_PATH, - VERSION_PATH, - } = constants; - return `${HTTP_PROTOCOL}${this.$auth.domain}${BASE_PATH}${VERSION_PATH}${path}`; - }, - async _makeRequest(args = {}) { - const { - $, - method = "get", - path, - params, - data, - } = args; - const config = { - method, - url: this._getUrl(path), - headers: this._getHeaders(), - params, - data, - }; - - return axios($ ?? this, config); - }, - _isRetriableStatusCode(statusCode) { - constants.retriableStatusCodes.includes(statusCode); + _getDomain() { + const { domain } = this.$auth; + return domain.includes("freshdesk.com") + ? domain + : `${domain}.freshdesk.com`; }, - async _withRetries(apiCall) { - const retryOpts = { - retries: 5, - factor: 2, - }; - return retry(async (bail) => { - try { - const data = await apiCall(); - - return data; - } catch (err) { - - const { status = 500 } = err; - if (!this._isRetriableStatusCode(status)) { - bail(` - Unexpected error (status code: ${status}): - ${JSON.stringify(err.response)} - `); - } - throw new ConfigurationError("Could not get data"); - } - }, retryOpts); + async _makeRequest({ + $ = this, headers, ...args + }) { + return axios($, { + baseURL: `https://${this._getDomain()}/api/v2`, + headers: { + ...this._getHeaders(), + ...headers, + }, + ...args, + }); }, async *filterTickets(params) { let loadedData = 0; do { - const response = await this.searchTickets(params); + const response = await this.searchTickets({ + params, + }); - if (!response || response.results.length === 0) { + if (!response?.results?.length) { return; } loadedData += response.results.length; @@ -139,9 +128,11 @@ export default { async *filterContacts(params) { let loadedData = 0; do { - const response = await this.searchContacts(params); + const response = await this.searchContacts({ + params, + }); - if (!response || response.results.length === 0) { + if (!response?.results?.length) { return; } loadedData += response.results.length; @@ -154,68 +145,63 @@ export default { params.page += 1; } while (true); }, - async createCompany({ - $, payload: data, - }) { + async createCompany(args) { return this._makeRequest({ - $, - path: "/companies", - data, + url: "/companies", method: "post", + ...args, }); }, - async getCompanies($ = undefined) { + async getCompanies(args) { return this._makeRequest({ - $, - path: "/contacts", + url: "/companies", + ...args, }); }, - async getContacts($ = undefined) { + async getContacts(args) { return this._makeRequest({ - $, - path: "/companies", + url: "/contacts", + ...args, }); }, - async createContact({ - $, data, - }) { + async createContact(args) { return this._makeRequest({ - $, - path: "/contacts", - data, + url: "/contacts", method: "post", + ...args, }); }, - async createTicket({ - $, data, - }) { + async createTicket(args) { return this._makeRequest({ - $, - path: "/tickets", - data, + url: "/tickets", method: "post", + ...args, }); }, async getTicket({ - $, id, + ticketId, ...args }) { return this._makeRequest({ - $, - path: `/tickets/${id}`, + url: `/tickets/${ticketId}`, + ...args, + }); + }, + async searchTickets(args) { + return this._makeRequest({ + url: "/search/tickets", + ...args, }); }, - async searchTickets(params, $ = undefined) { + async searchContacts(args) { return this._makeRequest({ - $, - path: "/search/tickets", - params, + url: "/search/contacts", + ...args, }); }, - async searchContacts(params, $ = undefined) { + async listTickets(args) { return this._makeRequest({ - $, - path: "/search/contacts", - params, + url: "/tickets", + ...args, }); }, }, diff --git a/components/freshdesk/package.json b/components/freshdesk/package.json index f58dea64baecb..5a0836d4847b4 100644 --- a/components/freshdesk/package.json +++ b/components/freshdesk/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/freshdesk", - "version": "0.0.4", + "version": "0.1.0", "description": "Pipedream Freshdesk Components", "main": "freshdesk.app.mjs", "keywords": [ @@ -13,7 +13,7 @@ "access": "public" }, "dependencies": { - "@pipedream/platform": "^1.2.0", + "@pipedream/platform": "^3.0.3", "async-retry": "^1.3.3", "moment": "2.29.4" } diff --git a/components/freshdesk/sources/new-contact/new-contact.mjs b/components/freshdesk/sources/new-contact/new-contact.mjs index a829811d88dc8..c965df308e594 100644 --- a/components/freshdesk/sources/new-contact/new-contact.mjs +++ b/components/freshdesk/sources/new-contact/new-contact.mjs @@ -4,15 +4,13 @@ import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; export default { key: "freshdesk-new-contact", - name: "New Contact", - description: "Emit new notifications when a new contact is created", - version: "0.0.2", + name: "New Contact Created", + description: "Emit new event when a contact is created. [See the documentation](https://developers.freshdesk.com/api/#filter_contacts)", + version: "0.0.3", type: "source", props: { freshdesk, timer: { - label: "Polling interval", - description: "Pipedream will poll Harvest API on this schedule", type: "$.interface.timer", default: { intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, @@ -42,7 +40,7 @@ export default { this.$emit(contact, { id: contact.id, - summary: `Contact name: ${contact.name}`, + summary: `New Contact: "${contact.name}"`, ts: Date.parse(contact.created_at), }); } diff --git a/components/freshdesk/sources/new-ticket/new-ticket.mjs b/components/freshdesk/sources/new-ticket/new-ticket.mjs index 9ce8cd0595588..6cab8f07d395c 100644 --- a/components/freshdesk/sources/new-ticket/new-ticket.mjs +++ b/components/freshdesk/sources/new-ticket/new-ticket.mjs @@ -4,15 +4,13 @@ import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; export default { key: "freshdesk-new-ticket", - name: "New Ticket", - description: "Emit new notifications when a new ticket is created", - version: "0.0.2", + name: "New Ticket Created", + description: "Emit new event when a ticket is created. [See the documentation](https://developers.freshdesk.com/api/#filter_tickets)", + version: "0.0.3", type: "source", props: { freshdesk, timer: { - label: "Polling interval", - description: "Pipedream will poll Harvest API on this schedule", type: "$.interface.timer", default: { intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, @@ -28,7 +26,10 @@ export default { lastDateChecked = new Date().toISOString(); this.freshdesk.setLastDateChecked(this.db, lastDateChecked); } - const formatedDate = lastDateChecked.substr(0, (lastDateChecked + "T").indexOf("T")); + const formatedDate = lastDateChecked.substr( + 0, + (lastDateChecked + "T").indexOf("T"), + ); const tickets = await this.freshdesk.filterTickets({ query: `"created_at:>'${formatedDate}'"`, page: 1, @@ -36,16 +37,16 @@ export default { for await (const ticket of tickets) { data.push(ticket); } - data && data.reverse().forEach((ticket) => { - this.freshdesk.setLastDateChecked(this.db, ticket.created_at); - if (moment(ticket.created_at).isAfter(lastDateChecked)) { - this.$emit(ticket, - { + data && + data.reverse().forEach((ticket) => { + this.freshdesk.setLastDateChecked(this.db, ticket.created_at); + if (moment(ticket.created_at).isAfter(lastDateChecked)) { + this.$emit(ticket, { id: ticket.id, - summary: `Ticket number: ${ticket.id}`, + summary: `New Ticket (ID: ${ticket.id})`, ts: Date.parse(ticket.created_at), }); - } - }); + } + }); }, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 86b85a2daa454..5753ef721189f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4771,8 +4771,8 @@ importers: components/freshdesk: dependencies: '@pipedream/platform': - specifier: ^1.2.0 - version: 1.6.6 + specifier: ^3.0.3 + version: 3.0.3 async-retry: specifier: ^1.3.3 version: 1.3.3 @@ -5404,8 +5404,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 - components/google_identity: - specifiers: {} + components/google_identity: {} components/google_maps_platform: dependencies: @@ -5852,8 +5851,7 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/helpdesk: - specifiers: {} + components/helpdesk: {} components/helper_functions: dependencies: @@ -13579,8 +13577,7 @@ importers: components/vectera: {} - components/vectorshift: - specifiers: {} + components/vectorshift: {} components/vend: dependencies: