Skip to content

[Components] Drift. Actions + Sources #16553

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

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

SokolovskyiK
Copy link
Contributor

@SokolovskyiK SokolovskyiK commented May 4, 2025

Fixes #13240

  • Actions:
    create-contact
    delete-contact
    get-contact
    update-contact

  • Sources:
    new-contact-update
    new-conversation-instant
    new-lead-instance
    new-message-instant

  • Notes:

This task is over a year old. Since then, the Drift API has changed.

There is no longer a create-or-update endpoint — now create and update are separate actions.

Regarding new-lead-instant:

Drift does not support listing contacts — only fetching them by known ID or email.

However, their API now supports webhooks. So I implemented this source using webhooks instead of polling. Unfortunately, there's no "new contact" webhook event available.

To work around this, I created two sources:

new-lead-instance: Emits an event when a contact adds their email in chat.
new-contact-update: Emits an event when a contact is updated.

Polling vs Webhooks:
new-conversation-instant is currently implemented as a polling source.
If needed, I can rewrite it to use webhooks instead.

Summary by CodeRabbit

  • New Features
    • Added actions to create, update, delete, and retrieve contacts in Drift.
    • Introduced new event sources for Drift: New Contact Update, New Conversation, New Message, and New Lead, enabling real-time and polling-based event detection.
  • Improvements
    • Enhanced Drift integration with robust contact management and event handling capabilities.
  • Chores
    • Updated Drift package version to 0.7.0.

Copy link

vercel bot commented May 4, 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) May 5, 2025 6:41pm

Copy link

vercel bot commented May 4, 2025

@SokolovskyiK 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 May 4, 2025

Walkthrough

This update introduces a comprehensive suite of new Drift integration components, including actions for creating, updating, retrieving, and deleting contacts, as well as several event sources for contact and conversation updates. The Drift app interface is significantly expanded to include robust API methods for contact CRUD operations, pagination, and utility helpers. New polling and webhook-based sources are provided for tracking new conversations, messages, leads, and contact updates. The package version is incremented to 0.7.0, and a utility function for object cleanup is added.

Changes

File(s) Change Summary
components/drift/actions/create-contact/create-contact.mjs New action: Create Contact—creates a Drift contact with various properties, prevents duplicates, and logs results.
components/drift/actions/delete-contact/delete-contact.mjs New action: Delete Contact—deletes a Drift contact by email or ID, with summary output.
components/drift/actions/get-contact/get-contact.mjs New action: Get Contact—retrieves a Drift contact by email or ID, returns contact object.
components/drift/actions/update-contact/update-contact.mjs New action: Update Contact—updates a Drift contact by email or ID, supports custom attributes.
components/drift/common/utils.mjs Adds removeNullEntries utility function for deep object cleanup.
components/drift/drift.app.mjs Major expansion: Implements full Drift API client with contact CRUD, pagination, and utility methods.
components/drift/package.json Updates package version from 0.6.0 to 0.7.0.
components/drift/sources/new-contact-update/new-contact-update.mjs New source: emits events on Drift contact updates, fetches full contact details.
components/drift/sources/new-conversation-instant/new-conversation-instant.mjs New polling source: emits events for new Drift conversations, manages deduplication and pagination.
components/drift/sources/new-lead-instance/new-lead-instance.mjs New source: emits events when a lead provides an email in Drift, enriches with contact info.
components/drift/sources/new-message-instant/new-message-instant.mjs New source: emits events for new Drift messages, supports filtering by conversation and contact.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Action
  participant DriftApp
  participant DriftAPI

  User->>Action: Provide contact details
  Action->>DriftApp: Call createContact / updateContact / getContactByEmailOrId
  DriftApp->>DriftAPI: Make HTTP request (create/update/get)
  DriftAPI-->>DriftApp: Respond with contact data
  DriftApp-->>Action: Return contact data or status
  Action-->>User: Output summary and result
Loading
sequenceDiagram
  participant DriftWebhook
  participant Source
  participant DriftApp
  participant DriftAPI

  DriftWebhook->>Source: Send event (e.g., contact_updated)
  Source->>DriftApp: Fetch contact details (getContactById)
  DriftApp->>DriftAPI: GET contact by ID
  DriftAPI-->>DriftApp: Return contact data
  DriftApp-->>Source: Provide contact data
  Source-->>Source: Enrich and emit event
Loading

Assessment against linked issues

Objective Addressed Explanation
Polling Sources: new-conversation-instant, new-lead-instant, new-message-instant (conversation/message context) (#13240)
Actions: create-update-contact, update-known-contact (with contact details/id props) (#13240)
Action: find-user-end-user-id (fetch user by end user id) (#13240) No implementation of an action to fetch user by end user id is present in these changes.

Suggested labels

action

Suggested reviewers

  • luancazarine
  • michelle0927

Poem

A flurry of contacts, a hop and a leap,
Drift’s new features are robust and deep!
With actions and sources, events now abound,
Conversations and leads are easily found.
Contacts are managed, cleaned up with flair—
This bunny’s delighted with changes to share!
🐇✨

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/drift/actions/create-contact/create-contact.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 packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

components/drift/actions/delete-contact/delete-contact.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 packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

components/drift/actions/get-contact/get-contact.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 packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

  • 6 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.
    • Generate unit testing code for this file.
    • 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 generate unit testing code for this file.
    • @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 generate unit testing code.
    • @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 May 4, 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: 17

🧹 Nitpick comments (31)
components/drift/actions/create-contact/create-contact.mjs (3)

53-61: Consider performance implications of object spreading with large customAttributes

The use of ...customAttributes could lead to performance issues if the customAttributes object is very large. Also, be careful when spreading user-provided objects as they might contain properties that could override your predefined attributes.

 const attributes = removeNullEntries({
   email,
   name,
   phone,
   source,
-  ...customAttributes,
+  ...(customAttributes || {}),
 });

63-72: Fix extra space in error message

There's an extra space between Error and the opening backtick in the error message.

 if (existingContact && existingContact.data.length > 0) {
-  throw new Error (`Contact ${email} already exists`);
+  throw new Error(`Contact ${email} already exists`);
 };

74-85: Improve formatting of warning messages in summary

The warning message concatenation could be formatted more clearly. If there are no warnings, the current implementation will still add a newline character.

- $.export("$summary", `Contact ${email} created.` + warnings.join("\n- "));
+ $.export("$summary", `Contact ${email} created.${warnings.length ? '\n- ' + warnings.join("\n- ") : ''}`);
components/drift/sources/new-lead-instance.mjs/new-lead-instance.mjs (2)

30-30: Fix spacing and improve comment clarity

There's an extra space before the assignment operator, and the comment could be more descriptive.

-    body.data.attributes =  result.data.attributes; //inject more data
+    body.data.attributes = result.data.attributes; // Enrich webhook data with full contact attributes

32-36: Consider adding validation for required fields

The code assumes that body.data.endUserId and body.timeStamp will always be present. Consider adding validation to handle cases where these fields might be missing.

+    const id = body.data?.endUserId || `lead-${Date.now()}`;
+    const ts = body.timeStamp || Date.now();
+
     this.$emit(body, {
       summary: `Contact "${email}" ID "${contactId}" updated`,
-      id: body.data.endUserId,
-      ts: body.timeStamp,
+      id,
+      ts,
     });
components/drift/sources/new-message-instant/new-message-instant.mjs (1)

12-17: Add validation for the conversationId prop.

The conversationId is defined as an integer type, but there's no validation to ensure it's a positive number.

Add validation for the conversationId in the run method:

  async run(event) {

    const { body } = event;
    const { drift } = this;

    const warnings = [];

+   if (this.conversationId && (isNaN(this.conversationId) || this.conversationId <= 0)) {
+     warnings.push("Conversation ID must be a positive number");
+   }

    if (this.emailOrId) warnings.push(...drift.checkEmailOrId(this.emailOrId));
    if (warnings.length > 0) console.log(warnings.join("\n- "));
components/drift/actions/get-contact/get-contact.mjs (1)

34-35: Improve the warning message formatting.

The warning messages are joined without a separator, which could make them harder to read.

-    $.export("$summary", `Contact ${contact.attributes.email} ID "${contact.id}"`
-      + " fetched successfully." +  warnings.join("\n- "));
+    $.export("$summary", `Contact ${contact.attributes.email} ID "${contact.id}" fetched successfully.` + 
+      (warnings.length ? "\n- " + warnings.join("\n- ") : ""));
components/drift/sources/new-contact-update/new-contact-update.mjs (2)

24-26: Use consistent object reference pattern.

You're using this.drift while other files use drift directly. Stay consistent with accessing the drift object.

-    const result = await this.drift.getContactById({
+    const result = await drift.getContactById({
       contactId,
     });

32-35: Remove redundant console.log statement.

There are two consecutive console.log statements with similar content.

-    console.log(body);
-
-    console.log("Contact updated webhook payload:", body);
+    console.log("Contact updated webhook payload:", body);
components/drift/actions/update-contact/update-contact.mjs (2)

8-8: Use consistent version numbering across components.

This component uses version "0.0.9" while other new components use "0.0.1". Maintain consistent versioning for new components.

-  version: "0.0.9",
+  version: "0.0.1",

86-86: Improve warning message formatting.

The warning messages are joined without proper formatting, which could make them harder to read.

-    $.export("$summary", `Contact ID ${contactId} updated successfully.` + warnings.join("\n- "));
+    $.export("$summary", `Contact ID ${contactId} updated successfully.` + 
+      (warnings.length ? "\n- " + warnings.join("\n- ") : ""));
components/drift/actions/delete-contact/delete-contact.mjs (2)

28-32: Handle array vs. object response explicitly for clarity

contact = contact.data[0] || contact.data; works, but the implicit truthy check obscures intent and could mask edge–cases (e.g. when data is an empty array). Prefer an explicit test so future readers immediately see why the fallback exists and to avoid accidentally operating on undefined.

-let contact = await drift.getContactByEmailOrId($, emailOrId);
-contact = contact.data[0] || contact.data;
+const res = await drift.getContactByEmailOrId($, emailOrId);
+const contact = Array.isArray(res.data) ? res.data[0] : res.data;
+
+if (!contact) {
+  throw new Error(`No contact returned for "${emailOrId}"`);
+}

39-41: Avoid dangling bullet when no warnings are present

warnings.join("\n- ") always appends a newline + dash, so the summary becomes
…deleted successfully.- when warnings is empty. Build the string conditionally:

-$.export("$summary", `Contact ${contactEmail} ID "${contactId}" deleted successfully.` +
-   warnings.join("\n- "));
+const summary = `Contact ${contactEmail} ID "${contactId}" deleted successfully.`
+  + (warnings.length ? `\n- ${warnings.join("\n- ")}` : "");
+$.export("$summary", summary);
components/drift/drift.app.mjs (2)

108-114: Remove unreachable else semicolon and streamline error path

Biome flags line 113 as unreachable because both branches throw. The trailing semicolon after the } is unnecessary and may confuse linters.

-        } catch (error) {
-          if (error.status === 404) {
-            throw new Error(`No contact found with ID: ${contactId}`);
-          } else {
-            throw error;
-          };
-        }
+        } catch (error) {
+          if (error.status === 404) {
+            throw new Error(`No contact found with ID: ${contactId}`);
+          }
+          throw error;
+        }
🧰 Tools
🪛 Biome (1.9.4)

[error] 113-113: This code is unreachable

... because either this statement ...

... or this statement will throw an exception beforehand

(lint/correctness/noUnreachable)


132-136: Typo in variable name & potential off-by-one

const firtsNewfirstNew. Small typo hinders readability.

Also, throwing when the id is not found may abort an otherwise recoverable poll. Consider logging and returning an empty list instead.

-const firtsNew = arr.indexOf(lastKnown);
-if (firtsNew === -1) throw new Error("Id not found");
-const newest = arr.slice(0, firtsNew);
+const firstNew = arr.indexOf(lastKnown);
+if (firstNew === -1) return [];
+const newest = arr.slice(0, firstNew);
components/drift/sources/new-conversation-instant/new-conversation-instant.mjs (2)

41-43: Debug statement left in source

console.log("here"); looks like leftover debugging noise and will spam logs.

-      console.log("here");

100-101: Event summary references contactId, not the conversation ID

For clarity to users looking at the event feed, consider using the conversation id (matches id field) or include both.

-summary: `New conversation with ID ${conversations[i].contactId}`,
+summary: `New conversation ${conversations[i].id} with contact ${conversations[i].contactId}`,
components/drift/common/methods.mjs (14)

12-15: Fix semicolon placement in conditional block.

There's an unnecessary semicolon after the conditional block which doesn't follow JavaScript best practices.

  isEmptyString(input) {
    if (this.isString(input)) {
      if (input.trim() === "") return true;
-    };
+    }

    return false;
  },

35-38: Fix semicolon placement in for loop.

There's an unnecessary semicolon after the for loop which doesn't follow JavaScript best practices.

    for (let i = 0; i < input.length; i++) {
      if (!this.isString(input[i]))
        return false;
-    };
+    }

62-65: Fix JSDoc comment formatting.

The comment has inconsistent slashes. The closing delimiter should match the opening style.

  /* =============================================================================================
  Function tries to parse the input as JSON,
  If it is not return the value as it was passed
- //==============================================================================================*/
+ ==============================================================================================*/

92-95: Fix semicolon placement after conditional block.

There's an unnecessary semicolon after the conditional block which doesn't follow JavaScript best practices.

    if (!this.isString(input)) {
      warnings.push("URL is not a string");
-
-    };
+    }

97-100: Fix semicolon placement after conditional block.

There's an unnecessary semicolon after the conditional block which doesn't follow JavaScript best practices.

    if (this.isEmptyString(input)) {
      warnings.push("URL is empty string");
      return warnings;
-    };
+    }

104-109: Fix semicolon placement and improve regex check.

There's an unnecessary semicolon after the conditional block, and the regex test could be improved.

    // Reject if spaces, tabs, or newlines are present
-    if ((/[ \t\n]/.test(trimmedInput))) {
+    if (/[ \t\n]/.test(trimmedInput)) {
      warnings.push( "Url contains invalid characters like space, backslash etc., please check." +
      this._reasonMsg(input));
      return warnings;
-    };
+    }

119-121: Fix semicolon placement after conditional block.

There's an unnecessary semicolon after the conditional block which doesn't follow JavaScript best practices.

    if (dubiousMatches) {
      warnings.push(" URL contains dubious or non-standard characters " + this._reasonMsg(input) );
-    };
+    }

130-135: Fix semicolon placement and improve warning message.

There's an unnecessary semicolon after the conditional block and the warning message could be better formatted.

      // Warn if user typed only one slash (e.g., https:/)
      if (/^(https?):\/(?!\/)/.test(input)) {
-
        warnings.push(` It looks like you're missing one slash after "${urlObject.protocol}".` +
        `Did you mean "${urlObject.protocol}//..."? ${this._reasonMsg(input)} `);
-
-      };
+      }

146-150: Fix semicolon placement after catch block.

There's an unnecessary semicolon after the catch block which doesn't follow JavaScript best practices.

      } catch (err) {
        warnings.push(" URL contains potentionally unacceptable characters\"" +
          this._reasonMsg(input));
-
-      };
+      }

147-148: Fix typo in warning message.

The warning message contains a typo in the word "potentially".

-        warnings.push(" URL contains potentionally unacceptable characters\"" +
+        warnings.push(" URL contains potentially unacceptable characters\"" +
          this._reasonMsg(input));

198-204: Consider using a more robust email validation regex.

The current regex is fairly minimal. While it checks basic structure, it might be worth considering a more comprehensive pattern that better matches email RFC standards.

For a more robust but still reasonable email validation, consider:

-    const basicPattern = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
+    // More comprehensive pattern checking for valid characters and structure
+    const basicPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;

This pattern better enforces valid characters in the local and domain parts, and requires a minimum domain extension length.


255-274: Optimize the checkEmailOrId implementation.

Currently, we convert the input to a number twice. We can optimize this by storing the result of the first conversion.

  checkEmailOrId(input) {
    const warnings = [];
    const trimmed = this.trimIfString(input);
    
+    // Convert to number once
+    const numericValue = Number(trimmed);
    
    // If it's an ID like number or string (e.g 12345 or "12345");
    // it's Valid. Return empty warnings array.
-    if (this.isIdNumber(Number(trimmed))) return warnings;
+    if (this.isIdNumber(numericValue)) return warnings;

    // If it's not a string.
    if (!this.isString(trimmed)) {
      warnings.push("Provided value is not an email or ID-like value (e.g., 1234 or '1234').");
      return warnings;
    }

    warnings.push(...this.checkIfEmailValid(trimmed));
    return warnings;
  },

283-285: Improve error source determination.

The current method of determining the error source is somewhat simplified. Errors can come from other sources beyond API responses and internal code.

Consider a more detailed error classification:

-    const thrower = error?.response?.status
-      ? "API response"
-      : "Internal Code";
+    let thrower = "Internal Code";
+    
+    if (error?.response?.status) {
+      thrower = `API response (${error.response.status})`;
+    } else if (error?.code === 'ECONNREFUSED' || error?.code === 'ENOTFOUND') {
+      thrower = "Network connectivity";
+    } else if (error?.code === 'ETIMEDOUT') {
+      thrower = "Request timeout";
+    }

1-302: Overall good structure but consider adding common validation interfaces.

The utility module provides a comprehensive set of validation and helper functions. However, it would be beneficial to introduce some higher-level validation interfaces that combine multiple checks for common scenarios.

Consider adding a high-level validation method that combines related checks:

validateContactInput(input) {
  const warnings = [];
  
  // Check required fields
  if (!input.email && !input.id) {
    warnings.push("Either email or id must be provided for contact operations");
  }
  
  // Validate email if provided
  if (input.email) {
    warnings.push(...this.checkIfEmailValid(input.email));
  }
  
  // Validate id if provided
  if (input.id && !this.isIdNumber(Number(input.id))) {
    warnings.push("Contact ID must be a positive integer");
  }
  
  return warnings;
},

This would help standardize validation across different actions that operate on contacts.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8f75260 and f888650.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • components/drift/actions/create-contact/create-contact.mjs (1 hunks)
  • components/drift/actions/delete-contact/delete-contact.mjs (1 hunks)
  • components/drift/actions/get-contact/get-contact.mjs (1 hunks)
  • components/drift/actions/update-contact/update-contact.mjs (1 hunks)
  • components/drift/common/methods.mjs (1 hunks)
  • components/drift/common/utils.mjs (1 hunks)
  • components/drift/drift.app.mjs (1 hunks)
  • components/drift/package.json (1 hunks)
  • components/drift/sources/new-contact-update/new-contact-update.mjs (1 hunks)
  • components/drift/sources/new-conversation-instant/new-conversation-instant.mjs (1 hunks)
  • components/drift/sources/new-lead-instance.mjs/new-lead-instance.mjs (1 hunks)
  • components/drift/sources/new-message-instant/new-message-instant.mjs (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
components/drift/common/utils.mjs

[error] 19-19: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

components/drift/drift.app.mjs

[error] 113-113: This code is unreachable

... because either this statement ...

... or this statement will throw an exception beforehand

(lint/correctness/noUnreachable)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: pnpm publish
🔇 Additional comments (4)
components/drift/package.json (1)

3-3: Version increment looks appropriate

The package version has been properly incremented from 0.6.0 to 0.7.0, which aligns with the significant feature additions in this PR (new actions and sources for the Drift integration).

components/drift/actions/create-contact/create-contact.mjs (1)

45-52: LGTM: Validation setup is well implemented

The email validation setup and warnings collection are well implemented.

components/drift/sources/new-message-instant/new-message-instant.mjs (1)

65-65:

❓ Verification inconclusive

Verify the ID field used for event deduplication.

You're using body.data.endUserId as the event ID, but this field may not exist in new_message events. Consider using the message ID if available.


🏁 Script executed:

#!/bin/bash
# Check Drift webhook documentation to verify fields in new_message events
curl -s https://devdocs.drift.com/docs/webhook-events-1 | grep -A 20 "new_message"

Length of output: 102475


🏁 Script executed:

#!/bin/bash
# Fetch and extract the JSON schema for the Message model from Drift docs
curl -s https://devdocs.drift.com/docs/message-model \
  | sed -n 's/&quot;/"/g' \
  | sed -n -n '/<code class="rdmd-code lang-json/,/<\/code>/p' \
  | sed 's/<[^>]*>//g'

Length of output: 160


Confirm Message payload ID field for deduplication

Please verify whether the new_message webhook’s data object actually includes an endUserId field. The docs show that data follows the Message model but don’t list endUserId—you may need to fall back to the message’s unique ID instead.

• Location: components/drift/sources/new-message-instant/new-message-instant.mjs:65
• Suggested change (after confirming available fields):

- id: body.data.endUserId,
+ id: body.data.id || body.data.messageId,
components/drift/sources/new-conversation-instant/new-conversation-instant.mjs (1)

84-85: Ensure numeric sort when IDs are strings

a.id - b.id gives NaN if id is a string. Cast explicitly:

-conversations.sort((a, b) => a.id - b.id);
+conversations.sort((a, b) => Number(a.id) - Number(b.id));

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: 1

♻️ Duplicate comments (2)
components/drift/drift.app.mjs (2)

45-46: Add fallback error handling for missing OAuth token

Similar to the issue in the _makeRequest method, the OAuth token should have appropriate error handling here.

- "Authorization": `Bearer ${this.$auth?.oauth_access_token}`,
+ "Authorization": `Bearer ${this.$auth?.oauth_access_token ?? (() => {
+   throw new Error("Drift OAuth access token is required");
+ })()}`,

32-34: 🛠️ Refactor suggestion

Add fallback error handling for missing OAuth token

The current implementation doesn't handle the case where this.$auth?.oauth_access_token is undefined. This could lead to silent failures with unclear error messages.

- "Authorization": `Bearer ${this.$auth?.oauth_access_token}`,
+ "Authorization": `Bearer ${this.$auth?.oauth_access_token ?? (() => {
+   throw new Error("Drift OAuth access token is required");
+ })()}`,
🧹 Nitpick comments (4)
components/drift/drift.app.mjs (4)

40-48: Reduce duplication by leveraging existing _makeRequest method

The getNextPage method duplicates the authorization header logic that already exists in _makeRequest. This could lead to inconsistencies if authentication requirements change.

- getNextPage($, url) {
-   return axios($, {
-     method: "GET",
-     url,
-     headers: {
-       "Authorization": `Bearer ${this.$auth?.oauth_access_token}`,
-     },
-   });
- },
+ getNextPage($, url) {
+   return axios($, {
+     method: "GET",
+     url,
+     headers: {
+       "Authorization": `Bearer ${this.$auth?.oauth_access_token}`,
+     },
+   });
+ },

Alternatively, you could modify _makeRequest to accept a full URL instead of constructing it from base URL and path, then use it for both cases.


124-127: Remove unnecessary semicolon

For consistency with the rest of the codebase, remove the unnecessary semicolon after the closing brace.

  if (!response?.data?.length) {
    throw new Error(`No contact found with email: ${email}`);
-  };
+  }

133-134: Fix typo in variable name

The variable name "firtsNew" appears to be a typo for "firstNew".

- const firtsNew = arr.indexOf(lastKnown);
- if (firtsNew === -1) throw new Error("Id not found");
+ const firstNew = arr.indexOf(lastKnown);
+ if (firstNew === -1) throw new Error("Id not found");

8-138: Add JSDoc documentation for improved code readability

The methods in this file lack documentation. Adding JSDoc comments would improve code clarity and maintainability, especially for other developers who might need to work with this code in the future.

For example, add documentation to the getContactByEmailOrId method:

/**
 * Retrieves a contact by either email or ID
 * @param {Object} $ - Pipedream event object
 * @param {string|number} emailOrId - Either an email address or a contact ID
 * @returns {Promise<Object>} - The contact data
 * @throws {Error} If no contact is found or if there's an API error
 */
async getContactByEmailOrId($, emailOrId) {
  // Existing implementation
}

Consider adding similar documentation to all methods in this file.

🧰 Tools
🪛 Biome (1.9.4)

[error] 113-113: This code is unreachable

... because either this statement ...

... or this statement will throw an exception beforehand

(lint/correctness/noUnreachable)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f888650 and 6f9160a.

📒 Files selected for processing (6)
  • components/drift/actions/get-contact/get-contact.mjs (1 hunks)
  • components/drift/drift.app.mjs (1 hunks)
  • components/drift/sources/new-contact-update/new-contact-update.mjs (1 hunks)
  • components/drift/sources/new-conversation-instant/new-conversation-instant.mjs (1 hunks)
  • components/drift/sources/new-lead-instance.mjs/new-lead-instance.mjs (1 hunks)
  • components/drift/sources/new-message-instant/new-message-instant.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • components/drift/sources/new-lead-instance.mjs/new-lead-instance.mjs
  • components/drift/sources/new-message-instant/new-message-instant.mjs
  • components/drift/actions/get-contact/get-contact.mjs
  • components/drift/sources/new-conversation-instant/new-conversation-instant.mjs
  • components/drift/sources/new-contact-update/new-contact-update.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/drift/drift.app.mjs

[error] 113-113: This code is unreachable

... because either this statement ...

... or this statement will throw an exception beforehand

(lint/correctness/noUnreachable)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components

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: 2

🧹 Nitpick comments (2)
components/drift/sources/new-lead-instance/new-lead-instance.mjs (2)

3-12: Component setup looks good but consider adding webhook setup instructions

The component is well-structured with appropriate key, name, and description. The document link is helpful for reference.

However, since this is a webhook-based source, consider adding setup instructions either in the description or as a separate hooks section to guide users on how to configure the webhook in Drift.


32-36: Update the summary message for consistency

The summary message indicates an update ("Contact X updated"), but this component is designed to trigger when a new lead adds their email in chat (as stated in the description).

this.$emit(body, {
-  summary: `Contact "${email}" ID "${contactId}" updated`,
+  summary: `New lead identified: "${email}" (ID: "${contactId}")`,
  id: body.data.endUserId,
  ts: body.timeStamp,
});
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6f9160a and 87d0c6f.

📒 Files selected for processing (1)
  • components/drift/sources/new-lead-instance/new-lead-instance.mjs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (1)
components/drift/sources/new-lead-instance/new-lead-instance.mjs (1)

17-20: Good event filtering logic

The event filtering logic is clear and appropriately logs and ignores non-relevant events.

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: 0

🧹 Nitpick comments (2)
components/drift/drift.app.mjs (2)

107-118: Remove unnecessary semicolons

There are unnecessary semicolons at the end of blocks that should be removed for consistency.

        if (!response?.data?.length) {
          throw new Error(`No contact found with email: ${email}`);
-        };
-      };
+        }
+      }

123-128: Fix typo in variable name

There's a typo in the variable name at line 124 - firtsNew should be firstNew.

-      const firtsNew = arr.indexOf(lastKnown);
+      const firstNew = arr.indexOf(lastKnown);
       if (firtsNew === -1) throw new Error("Id not found");
-      const newest = arr.slice(0, firtsNew);
+      const newest = arr.slice(0, firstNew);
       return newest.reverse();
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 097ff38 and 6d9a37e.

📒 Files selected for processing (9)
  • components/drift/actions/create-contact/create-contact.mjs (1 hunks)
  • components/drift/actions/delete-contact/delete-contact.mjs (1 hunks)
  • components/drift/actions/get-contact/get-contact.mjs (1 hunks)
  • components/drift/actions/update-contact/update-contact.mjs (1 hunks)
  • components/drift/drift.app.mjs (1 hunks)
  • components/drift/sources/new-contact-update/new-contact-update.mjs (1 hunks)
  • components/drift/sources/new-conversation-instant/new-conversation-instant.mjs (1 hunks)
  • components/drift/sources/new-lead-instance/new-lead-instance.mjs (1 hunks)
  • components/drift/sources/new-message-instant/new-message-instant.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (8)
  • components/drift/sources/new-message-instant/new-message-instant.mjs
  • components/drift/actions/update-contact/update-contact.mjs
  • components/drift/actions/create-contact/create-contact.mjs
  • components/drift/actions/get-contact/get-contact.mjs
  • components/drift/sources/new-contact-update/new-contact-update.mjs
  • components/drift/actions/delete-contact/delete-contact.mjs
  • components/drift/sources/new-lead-instance/new-lead-instance.mjs
  • components/drift/sources/new-conversation-instant/new-conversation-instant.mjs
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
🔇 Additional comments (6)
components/drift/drift.app.mjs (6)

8-29: HTTP request method implementation looks good

The _baseUrl() and _makeRequest() methods follow best practices for Pipedream apps with proper authorization header handling and flexible parameter passing.


31-39: Well-structured pagination support

The getNextPage() method properly implements pagination support for the Drift API, maintaining authorization headers consistency.


41-84: CRUD operations for contacts are well-implemented

All contact management methods (getContactByEmail, createContact, updateContact, getContactById, deleteContactById) follow a consistent pattern with appropriate HTTP methods and paths.


90-105: Good error handling for contact lookup by ID

The error handling for contact lookup by ID is well-implemented with specific error messages for 404 responses.


129-141: Well-implemented JSON parsing utility

The parseIfJSONString method safely handles JSON parsing with proper error handling, returning the original input if parsing fails.


142-144: Simple and effective ID validation method

The isIdNumber method provides a clean way to validate if an input is a positive integer ID.

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.

[Components] drift
3 participants