Skip to content

Freshservice extended implementation #17602

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

seynadio
Copy link

@seynadio seynadio commented Jul 14, 2025

WHY

  • 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 - Add new-contact source - Update package.json with required dependencies

Summary by CodeRabbit

  • New Features

    • Introduced Freshservice integration with actions to create, update, and retrieve tickets, companies, and contacts.
    • Added actions to assign tickets to agents or groups, close tickets, set ticket priority or status, and list all tickets.
    • Enabled adding internal notes to tickets in both Freshdesk and Freshservice.
    • Added event sources for detecting new contacts and new tickets in Freshservice.
  • Improvements

    • Enhanced ticket update actions with more options and validation.
    • Improved event emission and pagination for new ticket and contact sources.
  • Chores

    • Updated dependencies and package version for Freshservice integration.

seynadio added 2 commits July 14, 2025 10:42
- 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
- Add new-contact source
- Update package.json with required dependencies
- Full parity with Freshdesk integration functionality
Copy link

vercel bot commented Jul 14, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) Jul 14, 2025 10:00am

Copy link

vercel bot commented Jul 14, 2025

@seynadio is attempting to deploy a commit to the Pipedreamers Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Jul 14, 2025

Walkthrough

This update introduces a comprehensive suite of Freshservice integration actions, utilities, and event sources. It includes new actions for ticket, company, and contact management, internal note handling, and ticket assignment. The Freshservice app is extended with property definitions, API methods, and constants. Event sources for new contacts and tickets are added or enhanced, and utility modules support data cleaning and pagination.

Changes

File(s) Change Summary
components/freshdesk/actions/update-ticket/update-ticket.mjs Added optional internal note functionality with new props and conditional API logic; version bump.
components/freshservice/actions/assign-ticket-to-agent/..., assign-ticket-to-group/... New actions to assign tickets to agents or groups via Freshservice API.
components/freshservice/actions/close-ticket/close-ticket.mjs New action to close a Freshservice ticket by updating its status.
components/freshservice/actions/create-company/create-company.mjs New action to create a Freshservice company with multiple optional details.
components/freshservice/actions/create-contact/create-contact.mjs New action to create a Freshservice contact with validation and flexible fields.
components/freshservice/actions/create-ticket/create-ticket.mjs New action to create a Freshservice ticket with extensive field support.
components/freshservice/actions/get-ticket/get-ticket.mjs New action to retrieve a Freshservice ticket by ID.
components/freshservice/actions/list-all-tickets/list-all-tickets.mjs New action to list all Freshservice tickets with ordering and limit options.
components/freshservice/actions/set-ticket-priority/..., set-ticket-status/... New actions to set ticket priority and status in Freshservice.
components/freshservice/actions/update-ticket/update-ticket.mjs New action to update ticket fields or add internal notes, with validation and summary export.
components/freshservice/common/constants.mjs New module exporting ticket status/priority constants, sorting options, and page size.
components/freshservice/common/utils.mjs New utility function to recursively remove null/empty entries from data structures.
components/freshservice/freshservice.app.mjs Major extension: property definitions, API methods for Freshservice resources, utility functions, and improved request handling.
components/freshservice/package.json Version bump to 0.1.0; dependency upgrades and additions ("async-retry", "moment").
components/freshservice/sources/new-contact/new-contact.mjs New event source for polling and emitting new Freshservice contacts.
components/freshservice/sources/new-ticket/new-ticket.mjs Refactored to use paginated ticket fetching, improved event emission, and state tracking; version bump.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Action
    participant FreshserviceApp
    participant FreshserviceAPI

    User->>Action: Provide input (e.g., create/update ticket)
    Action->>FreshserviceApp: Call appropriate method (e.g., createTicket, updateTicket)
    FreshserviceApp->>FreshserviceAPI: Send API request (with cleaned data)
    FreshserviceAPI-->>FreshserviceApp: Return API response
    FreshserviceApp-->>Action: Return result
    Action-->>User: Export summary and result
Loading
sequenceDiagram
    participant Source
    participant FreshserviceApp
    participant FreshserviceAPI
    participant DB

    Source->>DB: Get last checked date
    Source->>FreshserviceApp: Fetch new contacts/tickets (paginated, filtered by date)
    FreshserviceApp->>FreshserviceAPI: Request resource page
    FreshserviceAPI-->>FreshserviceApp: Return resource page
    FreshserviceApp-->>Source: Return resources
    loop For each new resource
        Source->>Source: Emit event
        Source->>DB: Update max date if newer
    end
    Source->>DB: Store new last checked date
Loading

Suggested labels

User submitted

Poem

In fields of code where tickets sprout,
The rabbits hop and leap about—
Assign, create, and close with glee,
New contacts bloom for all to see.
With constants, utils, and flows anew,
This bunny’s proud of what we do!
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

components/freshdesk/actions/update-ticket/update-ticket.mjs

Oops! Something went wrong! :(

ESLint: 8.57.1

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:801:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:725:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:309:38)
at #link (node:internal/modules/esm/module_job:202:49)

components/freshservice/actions/close-ticket/close-ticket.mjs

Oops! Something went wrong! :(

ESLint: 8.57.1

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:801:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:725:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:309:38)
at #link (node:internal/modules/esm/module_job:202:49)

components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs

Oops! Something went wrong! :(

ESLint: 8.57.1

Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'jsonc-eslint-parser' imported from /eslint.config.mjs
at Object.getPackageJSONURL (node:internal/modules/package_json_reader:255:9)
at packageResolve (node:internal/modules/esm/resolve:767:81)
at moduleResolve (node:internal/modules/esm/resolve:853:18)
at defaultResolve (node:internal/modules/esm/resolve:983:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:801:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:725:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:708:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:309:38)
at #link (node:internal/modules/esm/module_job:202:49)

  • 14 others
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@adolfo-pd adolfo-pd added the User submitted Submitted by a user label Jul 14, 2025
@pipedream-component-development
Copy link
Collaborator

Thank you so much for submitting this! We've added it to our backlog to review, and our team has been notified.

@pipedream-component-development
Copy link
Collaborator

Thanks for submitting this PR! When we review PRs, we follow the Pipedream component guidelines. If you're not familiar, here's a quick checklist:

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (1)
components/freshservice/freshservice.app.mjs (1)

286-291: Remove duplicate method definition.

The getTickets method is a duplicate of the listTickets method defined at lines 245-250. They have identical implementations.

-    async getTickets(args = {}) {
-      return this._makeRequest({
-        path: "/v2/tickets",
-        ...args,
-      });
-    },
🧹 Nitpick comments (3)
components/freshservice/common/utils.mjs (1)

1-25: LGTM: Solid utility function with minor suggestions.

The removeNullEntries function correctly handles recursive cleaning of data structures. The implementation is clean and follows good practices.

Consider these minor improvements:

 function removeNullEntries(obj) {
   if (typeof obj === "number") {
     return obj;
   }
   if (typeof obj === "boolean") {
     return obj;
   }
   if (typeof obj === "string") {
-    return obj;
+    return obj.trim() === "" ? undefined : 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) {
+    for (const key of Object.keys(obj)) {
       if (obj[key] !== null && obj[key] !== undefined && obj[key] !== "") {
         result[key] = removeNullEntries(obj[key]);
       }
     }
     return result;
   }
   return obj;
 }

This handles whitespace-only strings and avoids inherited properties in objects.

components/freshservice/actions/close-ticket/close-ticket.mjs (1)

22-22: Consider using constants for status values.

The hard-coded status value 5 for "Closed" should ideally reference a constant from the constants file to improve maintainability and readability.

If constants are available in components/freshservice/common/constants.mjs, consider:

-        status: 5, // Closed
+        status: TICKET_STATUS.CLOSED, // or similar constant
components/freshservice/common/constants.mjs (1)

4-54: Consider reducing duplication between object and array formats.

The ticket status and priority mappings are duplicated in both object (TICKET_STATUS, TICKET_PRIORITY) and array (STATUS_OPTIONS, PRIORITY_OPTIONS) formats. While they serve different purposes, this duplication could lead to maintenance issues.

Consider generating the array options from the object mappings:

+const generateOptions = (mapping) => 
+  Object.entries(mapping).map(([value, label]) => ({
+    label,
+    value: parseInt(value),
+  }));

-export const STATUS_OPTIONS = [
-  {
-    label: "Open",
-    value: 2,
-  },
-  {
-    label: "Pending", 
-    value: 3,
-  },
-  {
-    label: "Resolved",
-    value: 4,
-  },
-  {
-    label: "Closed",
-    value: 5,
-  },
-];
+export const STATUS_OPTIONS = generateOptions(TICKET_STATUS);

-export const PRIORITY_OPTIONS = [
-  {
-    label: "Low",
-    value: 1,
-  },
-  {
-    label: "Medium",
-    value: 2,
-  },
-  {
-    label: "High", 
-    value: 3,
-  },
-  {
-    label: "Urgent",
-    value: 4,
-  },
-];
+export const PRIORITY_OPTIONS = generateOptions(TICKET_PRIORITY);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e21d334 and 528e720.

📒 Files selected for processing (18)
  • components/freshdesk/actions/update-ticket/update-ticket.mjs (3 hunks)
  • components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs (1 hunks)
  • components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs (1 hunks)
  • components/freshservice/actions/close-ticket/close-ticket.mjs (1 hunks)
  • components/freshservice/actions/create-company/create-company.mjs (1 hunks)
  • components/freshservice/actions/create-contact/create-contact.mjs (1 hunks)
  • components/freshservice/actions/create-ticket/create-ticket.mjs (1 hunks)
  • components/freshservice/actions/get-ticket/get-ticket.mjs (1 hunks)
  • components/freshservice/actions/list-all-tickets/list-all-tickets.mjs (1 hunks)
  • components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs (1 hunks)
  • components/freshservice/actions/set-ticket-status/set-ticket-status.mjs (1 hunks)
  • components/freshservice/actions/update-ticket/update-ticket.mjs (1 hunks)
  • components/freshservice/common/constants.mjs (1 hunks)
  • components/freshservice/common/utils.mjs (1 hunks)
  • components/freshservice/freshservice.app.mjs (2 hunks)
  • components/freshservice/package.json (2 hunks)
  • components/freshservice/sources/new-contact/new-contact.mjs (1 hunks)
  • components/freshservice/sources/new-ticket/new-ticket.mjs (2 hunks)
🧰 Additional context used
🧠 Learnings (11)
components/freshservice/actions/list-all-tickets/list-all-tickets.mjs (2)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-07-04T18:11:59.822Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-10-08T15:33:38.240Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
components/freshservice/package.json (1)
Learnt from: jcortes
PR: PipedreamHQ/pipedream#14935
File: components/sailpoint/package.json:15-18
Timestamp: 2024-12-12T19:23:09.039Z
Learning: When developing Pipedream components, do not add built-in Node.js modules like `fs` to `package.json` dependencies, as they are native modules provided by the Node.js runtime.
components/freshservice/actions/create-company/create-company.mjs (3)
Learnt from: jcortes
PR: PipedreamHQ/pipedream#14467
File: components/gainsight_px/actions/create-account/create-account.mjs:4-6
Timestamp: 2024-10-30T15:24:39.294Z
Learning: In `components/gainsight_px/actions/create-account/create-account.mjs`, the action name should be "Create Account" instead of "Create Memory".
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-07-04T18:11:59.822Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-10-08T15:33:38.240Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs (2)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-07-04T18:11:59.822Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-10-08T15:33:38.240Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
components/freshservice/sources/new-contact/new-contact.mjs (3)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#15376
File: components/monday/sources/name-updated/name-updated.mjs:6-6
Timestamp: 2025-01-23T03:55:15.166Z
Learning: Source names in Monday.com components don't need to start with "New" if they emit events for updated items (e.g., "Name Updated", "Column Value Updated") rather than new items. This follows the component guidelines exception where the "New" prefix is only required when emits are limited to new items.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14265
File: components/the_magic_drip/sources/common.mjs:35-43
Timestamp: 2024-10-10T19:18:27.998Z
Learning: In `components/the_magic_drip/sources/common.mjs`, when processing items in `getAndProcessData`, `savedIds` is intentionally updated with IDs of both emitted and non-emitted items to avoid emitting retroactive events upon first deployment and ensure only new events are emitted as they occur.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common.mjs:97-98
Timestamp: 2024-07-24T02:05:59.531Z
Learning: The `processTimerEvent` method in the `components/salesforce_rest_api/sources/common.mjs` file is intentionally left unimplemented to enforce that subclasses must implement this method, similar to an abstract class in object-oriented programming.
components/freshservice/actions/update-ticket/update-ticket.mjs (1)
Learnt from: jcortes
PR: PipedreamHQ/pipedream#14467
File: components/gainsight_px/actions/create-account/create-account.mjs:4-6
Timestamp: 2024-10-30T15:24:39.294Z
Learning: In `components/gainsight_px/actions/create-account/create-account.mjs`, the action name should be "Create Account" instead of "Create Memory".
components/freshservice/actions/get-ticket/get-ticket.mjs (2)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-10-08T15:33:38.240Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-07-04T18:11:59.822Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
components/freshservice/actions/create-contact/create-contact.mjs (1)
Learnt from: jcortes
PR: PipedreamHQ/pipedream#14467
File: components/gainsight_px/actions/create-account/create-account.mjs:4-6
Timestamp: 2024-10-30T15:24:39.294Z
Learning: In `components/gainsight_px/actions/create-account/create-account.mjs`, the action name should be "Create Account" instead of "Create Memory".
components/freshservice/actions/create-ticket/create-ticket.mjs (3)
Learnt from: jcortes
PR: PipedreamHQ/pipedream#14467
File: components/gainsight_px/actions/create-account/create-account.mjs:4-6
Timestamp: 2024-10-30T15:24:39.294Z
Learning: In `components/gainsight_px/actions/create-account/create-account.mjs`, the action name should be "Create Account" instead of "Create Memory".
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-10-08T15:33:38.240Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12731
File: components/hackerone/actions/get-members/get-members.mjs:3-28
Timestamp: 2024-07-04T18:11:59.822Z
Learning: When exporting a summary message in the `run` method of an action, ensure the message is correctly formatted. For example, in the `hackerone-get-members` action, the correct format is `Successfully retrieved ${response.data.length} members`.
components/freshservice/freshservice.app.mjs (1)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#16954
File: components/salesloft/salesloft.app.mjs:14-23
Timestamp: 2025-06-04T17:52:05.780Z
Learning: In the Salesloft API integration (components/salesloft/salesloft.app.mjs), the _makeRequest method returns response.data which directly contains arrays for list endpoints like listPeople, listCadences, listUsers, and listAccounts. The propDefinitions correctly call .map() directly on these responses without needing to destructure a nested data property.
components/freshservice/sources/new-ticket/new-ticket.mjs (2)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14265
File: components/the_magic_drip/sources/common.mjs:35-43
Timestamp: 2024-10-10T19:18:27.998Z
Learning: In `components/the_magic_drip/sources/common.mjs`, when processing items in `getAndProcessData`, `savedIds` is intentionally updated with IDs of both emitted and non-emitted items to avoid emitting retroactive events upon first deployment and ensure only new events are emitted as they occur.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#15376
File: components/monday/sources/name-updated/name-updated.mjs:6-6
Timestamp: 2025-01-23T03:55:15.166Z
Learning: Source names in Monday.com components don't need to start with "New" if they emit events for updated items (e.g., "Name Updated", "Column Value Updated") rather than new items. This follows the component guidelines exception where the "New" prefix is only required when emits are limited to new items.
🔇 Additional comments (20)
components/freshservice/package.json (2)

3-3: LGTM: Version bump to 0.1.0 is appropriate.

The version bump from 0.0.2 to 0.1.0 correctly reflects the addition of new features as described in the PR objectives.


17-17: Verify compatibility with @pipedream/platform ^3.0.3.

The major version upgrade from ^1.5.1 to ^3.0.3 could introduce breaking changes. Ensure all existing functionality remains compatible with the new platform version.

components/freshservice/actions/set-ticket-status/set-ticket-status.mjs (1)

4-39: LGTM: Well-structured action following Pipedream conventions.

The action implementation is clean and follows best practices:

  • Proper use of property definitions from the main app
  • Consistent error handling with the $ parameter
  • User-friendly summary message with ticket name and human-readable status
  • Appropriate return of the API response
components/freshservice/actions/set-ticket-priority/set-ticket-priority.mjs (1)

4-39: LGTM: Consistent implementation following established patterns.

The action implementation is well-structured and maintains consistency with the set-ticket-status action:

  • Proper use of property definitions and constants
  • Consistent error handling and user feedback
  • Clean structure following Pipedream conventions
components/freshservice/actions/list-all-tickets/list-all-tickets.mjs (1)

3-55: LGTM: Well-implemented list action with proper parameter handling.

The action implementation is excellent:

  • Optional parameters with sensible defaults and limits
  • Conditional parameter construction to avoid unnecessary API parameters
  • Proper fallback handling for the tickets array
  • Summary message format follows established patterns from retrieved learnings
  • Clean structure following Pipedream conventions
components/freshservice/actions/assign-ticket-to-group/assign-ticket-to-group.mjs (1)

1-37: LGTM! Clean implementation of ticket assignment action.

The action correctly implements ticket-to-group assignment using the Freshservice API. The code follows proper patterns with propDefinition references, appropriate API calls, and informative summary messages.

components/freshservice/actions/create-contact/create-contact.mjs (3)

92-94: LGTM! Proper validation ensures required contact information.

The validation correctly enforces that either email or phone must be provided, which aligns with typical contact creation requirements.


96-100: LGTM! Clean data preparation with proper null handling.

The implementation correctly uses removeNullEntries to clean the data and handles the secondary_emails array appropriately by checking for existence and length before adding it to the data object.


107-107: LGTM! Safe property access in summary message.

The summary message uses optional chaining to safely access nested properties, preventing potential runtime errors if the response structure differs.

components/freshservice/actions/assign-ticket-to-agent/assign-ticket-to-agent.mjs (2)

17-22: LGTM! Proper property mapping for agent assignment.

The mapping of agentId propDefinition to responder_id property is correct and follows the Freshservice API field naming convention.


24-36: LGTM! Consistent implementation pattern.

The action follows the same reliable pattern as other ticket assignment actions with proper API calls and informative summary messages.

components/freshservice/actions/create-company/create-company.mjs (3)

93-95: LGTM! Proper custom fields handling.

The conditional parsing of custom fields using parseIfJSONString is appropriate for handling JSON input while avoiding errors on non-JSON values.


97-99: LGTM! Safe array handling for domains.

The conditional check for domains existence and length prevents empty arrays from being sent to the API, which is good practice.


106-106: LGTM! Safe property access in summary message.

The summary message uses optional chaining to safely access the nested company name property, preventing potential runtime errors.

components/freshservice/actions/get-ticket/get-ticket.mjs (1)

1-27: LGTM!

Clean implementation following best practices. Good use of propDefinition for consistency and optional chaining for the summary message.

components/freshservice/actions/update-ticket/update-ticket.mjs (1)

111-163: Well-implemented action with proper validation

Excellent implementation that:

  • Correctly validates required fields for both update paths
  • Uses ConfigurationError for clear error messaging
  • Properly excludes the app instance from API data
  • Provides informative summaries with status/priority labels
components/freshservice/sources/new-ticket/new-ticket.mjs (1)

30-74: Excellent refactor to use pagination and date filtering

Great improvements:

  • Memory-efficient async generator pattern for pagination
  • Date-based filtering to fetch only new tickets since last check
  • Proper tracking of the maximum created date for incremental updates
  • Clean implementation that scales well with large ticket volumes
components/freshservice/common/constants.mjs (1)

4-9: Status code 1 is non-standard in Freshservice API v2
According to the Freshservice API v2 documentation, standard ticket status codes begin at 2 (“Open”) and run through 5 (“Closed”). There is no built-in code 1 to include in TICKET_STATUS, so its omission is intentional and correct.

components/freshservice/sources/new-contact/new-contact.mjs (2)

57-61: Verify the query string format for the filter API.

The query string has quotes around the entire query expression, which might not be the correct format for the Freshservice filter API.

-        query: `"created_at:>'${formattedDate}'"`,
+        query: `created_at:>'${formattedDate}'`,

Please verify the correct query format in the Freshservice API documentation. If the outer quotes are required, please disregard this suggestion.


23-29: Handle potential null/undefined values in contact name.

The summary string concatenation could display "null" or "undefined" if last_name is missing.

-        summary: `New contact: ${data.first_name} ${data.last_name || ""} (${data.email})`,
+        summary: `New contact: ${data.first_name}${data.last_name ? ` ${data.last_name}` : ""} (${data.email})`,
⛔ Skipped due to learnings
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#15376
File: components/monday/sources/name-updated/name-updated.mjs:6-6
Timestamp: 2025-01-23T03:55:15.166Z
Learning: Source names in Monday.com components don't need to start with "New" if they emit events for updated items (e.g., "Name Updated", "Column Value Updated") rather than new items. This follows the component guidelines exception where the "New" prefix is only required when emits are limited to new items.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14229
File: components/americommerce/actions/update-customer/update-customer.mjs:89-94
Timestamp: 2024-10-08T16:42:59.225Z
Learning: When defining boolean properties in AmeriCommerce components (e.g., in `update-customer.mjs`), ensure that the label and description are consistent and clearly indicate the intent, especially when using negations like "No Account", to avoid confusion.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#14265
File: components/the_magic_drip/sources/common.mjs:35-43
Timestamp: 2024-10-10T19:18:27.998Z
Learning: In `components/the_magic_drip/sources/common.mjs`, when processing items in `getAndProcessData`, `savedIds` is intentionally updated with IDs of both emitted and non-emitted items to avoid emitting retroactive events upon first deployment and ensure only new events are emitted as they occur.

Comment on lines +18 to +19
"async-retry": "^1.3.3",
"moment": "2.29.4"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify security status of the fixed moment version.

The fixed version of moment (2.29.4) may have known security vulnerabilities or be outdated. Consider updating to the latest stable version.


🌐 Web query:

What are the known security vulnerabilities in moment.js version 2.29.4 and what is the latest stable version?

💡 Result:

moment.js version 2.29.4 is not known to have any active or recent security vulnerabilities. The major security issue previously reported—a Regular Expression Denial of Service (ReDoS) vulnerability in the RFC2822 parsing code—was patched in 2.29.4; earlier versions (from 2.18.0 up to 2.29.3) were affected by this vulnerability (CVE-2022-31129)[1][2][4]. Cybersecurity vulnerability trackers do not report any unresolved issues for 2.29.4[9].

Key points:

  • 2.29.4 fixes previous ReDoS issue: The ReDoS vulnerability, which affected string-to-date parsing (especially RFC2822 parsing), was patched in 2.29.4. This resolved the main known security risk up to that release[2][4].
  • No new vulnerabilities reported in 2.29.4: Security databases and advisories currently list no recent or active vulnerabilities for this version[9].
  • Input validation still recommended: Even with the patch, developers are advised to limit the length of user-supplied date strings to further mitigate any potential for resource exhaustion[4].

The latest stable version of moment.js is 2.30.1 (released December 27, 2023)[2][10].

A summary table:

Version Known Security Vulnerabilities Notes
2.29.4 None currently known; fixed previous ReDoS ReDoS fixed from <2.29.4
2.30.1 None reported Latest stable release

If you need maximum security and the latest patches—even though 2.29.4 is safe regarding known issues—upgrading to 2.30.1 is recommended[2][10].

Citations:


Confirm moment.js 2.29.4 Security Status & Recommend Upgrade

moment.js 2.29.4 patches the known ReDoS vulnerability (CVE-2022-31129) and currently has no active security issues. The latest stable release is 2.30.1 (Dec 27, 2023). To stay up-to-date with any future fixes and improvements, consider upgrading.

• File: components/freshservice/package.json
Lines: 18–19

Suggested diff:

-   "moment": "2.29.4"
+   "moment": "^2.30.1"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"async-retry": "^1.3.3",
"moment": "2.29.4"
"async-retry": "^1.3.3",
"moment": "^2.30.1"
🤖 Prompt for AI Agents
In components/freshservice/package.json at lines 18 to 19, the moment.js version
is set to 2.29.4. To ensure the package stays current with the latest fixes and
improvements, update the moment.js dependency version from "2.29.4" to "2.30.1".

Comment on lines +120 to +133
if (internalNote && noteBody) {
const response = await freshdesk._makeRequest({
$,
method: "POST",
url: `/tickets/${ticketId}/notes`,
data: {
body: noteBody,
private: true,
},
});

$.export("$summary", `Internal note added to ticket "${ticketName}" (ID: ${ticketId})`);
return response;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add validation for required noteBody when internalNote is enabled

When internalNote is true but noteBody is empty or not provided, the code silently falls through to the ticket update logic. This could confuse users who expect to add a note.

Consider adding validation similar to the Freshservice implementation:

 if (internalNote && noteBody) {
+if (internalNote) {
+  if (!noteBody) {
+    throw new Error("Note Body is required when Internal Note is enabled");
+  }
   const response = await freshdesk._makeRequest({
     $,
     method: "POST",
     url: `/tickets/${ticketId}/notes`,
     data: {
       body: noteBody,
       private: true,
     },
   });

   $.export("$summary", `Internal note added to ticket "${ticketName}" (ID: ${ticketId})`);
   return response;
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In components/freshdesk/actions/update-ticket/update-ticket.mjs around lines 120
to 133, add validation to check if noteBody is provided when internalNote is
true. If noteBody is missing or empty, throw an error or return a clear message
indicating that the note body is required before proceeding. This prevents
silent failures and ensures users are informed when they attempt to add an
internal note without content.

Comment on lines +122 to +132
async run({ $ }) {
const {
custom_fields,
...otherProps
} = this;

const data = removeNullEntries(otherProps);

if (custom_fields) {
data.custom_fields = this.freshservice.parseIfJSONString(custom_fields);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix destructuring to exclude freshservice from API data

The current destructuring only extracts custom_fields, leaving the freshservice app instance in otherProps. This will incorrectly pass the app instance to the API.

Apply this fix:

 async run({ $ }) {
   const {
+    freshservice,
     custom_fields,
     ...otherProps
   } = this;

   const data = removeNullEntries(otherProps);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async run({ $ }) {
const {
custom_fields,
...otherProps
} = this;
const data = removeNullEntries(otherProps);
if (custom_fields) {
data.custom_fields = this.freshservice.parseIfJSONString(custom_fields);
}
async run({ $ }) {
const {
freshservice,
custom_fields,
...otherProps
} = this;
const data = removeNullEntries(otherProps);
if (custom_fields) {
data.custom_fields = this.freshservice.parseIfJSONString(custom_fields);
}
🤖 Prompt for AI Agents
In components/freshservice/actions/create-ticket/create-ticket.mjs around lines
122 to 132, the destructuring only excludes custom_fields but leaves the
freshservice app instance in otherProps, causing it to be sent to the API
incorrectly. Update the destructuring to also exclude freshservice from this by
adding it to the destructuring assignment, so neither custom_fields nor
freshservice are included in otherProps before calling removeNullEntries.

Comment on lines +30 to +41
async *getContacts(params = {}) {
let page = 1;
let hasMore = true;

while (hasMore) {
const { requesters } = await this.freshservice.filterContacts({
params: {
...params,
page,
per_page: 100,
},
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use PAGE_SIZE constant for consistency.

The method uses a hardcoded value of 100 for pagination instead of the PAGE_SIZE constant defined in constants.mjs.

Import the constant at the top of the file:

+import { PAGE_SIZE } from "../../common/constants.mjs";

Then update the pagination parameter:

-            per_page: 100,
+            per_page: PAGE_SIZE,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async *getContacts(params = {}) {
let page = 1;
let hasMore = true;
while (hasMore) {
const { requesters } = await this.freshservice.filterContacts({
params: {
...params,
page,
per_page: 100,
},
});
import { PAGE_SIZE } from "../../common/constants.mjs";
async *getContacts(params = {}) {
let page = 1;
let hasMore = true;
while (hasMore) {
const { requesters } = await this.freshservice.filterContacts({
params: {
...params,
page,
per_page: PAGE_SIZE,
},
});
🤖 Prompt for AI Agents
In components/freshservice/sources/new-contact/new-contact.mjs around lines 30
to 41, replace the hardcoded pagination value 100 with the PAGE_SIZE constant
for consistency. First, import PAGE_SIZE from constants.mjs at the top of the
file, then update the per_page parameter in the filterContacts call to use
PAGE_SIZE instead of 100.

Comment on lines +100 to +114
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,
})) || [];
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify the API response structure for contacts.

The getContacts method response is accessed directly without checking for a wrapper property. Based on other methods in this file, the API might return contacts in a wrapped format.


🏁 Script executed:

#!/bin/bash
# Description: Check how getContacts response is structured in other usages

# Search for other usages of getContacts to understand the response structure
ast-grep --pattern 'getContacts($_) {
  $$$
}'

# Also search for how the response is handled
rg -A 5 'getContacts\(' --type js

Length of output: 84


🏁 Script executed:

#!/bin/bash
# Search for the getContacts method definition and its implementation in the codebase

# 1. Find all occurrences of getContacts across the repo
rg -n "getContacts" .

# 2. Inspect the implementation in components/freshservice/freshservice.app.mjs (if found)
rg -A5 "async getContacts" components/freshservice/freshservice.app.mjs

Length of output: 13294


🏁 Script executed:

#!/bin/bash
# Inspect getTickets implementation and usage in freshservice.app.mjs
rg -n "async getTickets" -A10 components/freshservice/freshservice.app.mjs

# Inspect the _makeRequest method definition in freshservice.app.mjs
rg -n "_makeRequest" -A10 components/freshservice/freshservice.app.mjs

Length of output: 3663


🏁 Script executed:

#!/bin/bash
# Display the top 20 lines of freshservice.app.mjs to inspect imports and axios usage
head -n 20 components/freshservice/freshservice.app.mjs

Length of output: 561


🏁 Script executed:

#!/bin/bash
# Search for contactEmail in propDefinitions
rg -n "contactEmail" -A5 components/freshservice/freshservice.app.mjs

Length of output: 251


The getContacts call returns the raw Axios response object (with a top-level data property), not the array of contacts itself. Your current options handler tries to call .map() on this response object, so it will fail at runtime.

To fix this, extract the array of contacts from response.data. For example:

async options({ page, companyId }) {
  const response = await this.getContacts({
    params: {
      page: page + 1,
      per_page: PAGE_SIZE,
      company_id: companyId,
    },
  });
  const contacts = response.data?.requesters || [];
  return contacts.map(contact => ({
    label: contact.email,
    value: contact.email,
  }));
},

• The Freshservice API returns requesters under response.data.requesters.
• Update line 103 in components/freshservice/freshservice.app.mjs.
• Remove the duplicate getTickets at lines 286–291; the existing listTickets covers this.

🤖 Prompt for AI Agents
In components/freshservice/freshservice.app.mjs around lines 100 to 114, the
options method incorrectly calls .map() on the raw Axios response object
returned by getContacts instead of the contacts array. Fix this by extracting
the contacts array from response.data.requesters before mapping. Also, remove
the duplicate getTickets method found at lines 286 to 291 since listTickets
already covers that functionality.

@seynadio
Copy link
Author

Closing this PR and replacing with #17608 which has only the Freshservice changes (no accidental Freshdesk changes) and represents a complete, comprehensive integration with full parity to Freshdesk functionality

@seynadio seynadio closed this Jul 14, 2025
@seynadio seynadio deleted the freshservice-complete-implementation branch July 14, 2025 11:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
User submitted Submitted by a user
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants