Skip to content
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

New Components - campaign_monitor #14635

Merged
merged 6 commits into from
Nov 15, 2024
Merged

New Components - campaign_monitor #14635

merged 6 commits into from
Nov 15, 2024

Conversation

michelle0927
Copy link
Collaborator

@michelle0927 michelle0927 commented Nov 12, 2024

Resolves #14557

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced modules for adding subscribers, sending smart transactional emails, and unsubscribing users in the Campaign Monitor application.
    • Added functionality to track email opens, bounces, and new subscribers, enhancing event management.
    • New properties and methods for dynamic options retrieval and improved API interactions.
  • Version Update

    • Updated the Campaign Monitor component version to 0.1.0, with enhanced dependency management.
  • Documentation

    • Metadata and descriptions added to new actions for clarity and user guidance.

Copy link

vercel bot commented Nov 12, 2024

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

3 Skipped Deployments
Name Status Preview Comments Updated (UTC)
docs-v2 ⬜️ Ignored (Inspect) Visit Preview Nov 14, 2024 5:48pm
pipedream-docs ⬜️ Ignored (Inspect) Nov 14, 2024 5:48pm
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) Nov 14, 2024 5:48pm

Copy link
Contributor

coderabbitai bot commented Nov 12, 2024

Walkthrough

This pull request introduces several new modules to the Campaign Monitor integration, including actions for adding subscribers, sending smart transactional emails, and unsubscribing users. It also enhances the main application by adding various properties and methods for better interaction with the Campaign Monitor API. New sources are included for handling bounce events, tracking email opens, and managing new subscribers. The overall structure improves the capability to manage subscriber data and campaign events effectively.

Changes

File Path Change Summary
components/campaign_monitor/actions/add-subscriber/add-subscriber.mjs New module for adding subscribers with properties like clientId, listId, email, etc.
components/campaign_monitor/actions/send-smart-transactional-email/send-smart-transactional-email.mjs New module for sending smart transactional emails with properties like clientId, smartEmailId, etc.
components/campaign_monitor/actions/unsubscribe/unsubscribe.mjs New module for unsubscribing users from a mailing list with properties like clientId, listId, etc.
components/campaign_monitor/campaign_monitor.app.mjs Enhanced application with new properties and methods for API interactions.
components/campaign_monitor/package.json Updated version to 0.1.0 and added dependencies for @pipedream/platform.
components/campaign_monitor/sources/common/base.mjs New base module for managing events related to campaign monitoring.
components/campaign_monitor/sources/new-bounce/new-bounce.mjs New module for handling bounce events from campaign emails.
components/campaign_monitor/sources/new-email-open/new-email-open.mjs New module for tracking email opens from campaigns.
components/campaign_monitor/sources/new-subscriber/new-subscriber.mjs New module for handling new subscribers in the Campaign Monitor integration.

Assessment against linked issues

Objective Addressed Explanation
Emit new event when campaign email bounces. Required prop: Campaign ID. (#14557)
Emit new event when an email from a campaign is opened. Required prop: Campaign ID. (#14557)
Emit new event when a new subscriber is added to a list. Required prop: List ID. (#14557)
Sends an intelligent transactional email to a specified recipient. Required props include 'email', 'subject', and 'content'. (#14557)
Removes a subscriber from a mailing list given their email address. The required prop is 'email'. (#14557)

Possibly related PRs

Suggested labels

action, trigger / source

Suggested reviewers

  • GTFalcao

Poem

In fields of code, we hop and play,
New subscribers join us every day.
With emails sent and bounces tracked,
Our Campaign Monitor's now fully packed!
Hooray for actions, both big and small,
Together we build, we conquer all! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ 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.

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 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.

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

🧹 Outside diff range and nitpick comments (10)
components/campaign_monitor/sources/new-subscriber/new-subscriber.mjs (1)

1-10: LGTM! Consider documenting version strategy.

The component setup follows best practices with clear naming and appropriate deduplication strategy. Starting with version 0.0.1 is good for a new component.

Consider adding a comment explaining the versioning strategy and what changes would trigger version increments.

components/campaign_monitor/sources/new-bounce/new-bounce.mjs (2)

6-8: Enhance the component description for better clarity.

The current description is concise but could be more informative. Consider adding details about when bounces occur and what information is included in the event.

-  description: "Emit new event when a campaign email bounces",
+  description: "Emit new event when a campaign email bounces. This includes hard bounces (permanent failures) and soft bounces (temporary failures) with the recipient's email address and bounce details.",

34-42: Consider adding pagination parameters.

The getArgs method sets up ordering but doesn't handle pagination, which might be necessary for handling large volumes of bounce data.

Consider adding pagination parameters:

 getArgs() {
   return {
     campaignId: this.campaignId,
     params: {
       orderfield: "date",
       orderdirection: "desc",
+      page: this.page || 1,
+      pagesize: 100, // Adjust based on API limits
     },
   };
 },
components/campaign_monitor/actions/unsubscribe/unsubscribe.mjs (2)

1-8: LGTM! Consider starting with version 1.0.0

The module definition and imports look good. The description is well-documented with a link to the API reference. However, since this is a new component that will be published to users, consider starting with version 1.0.0 instead of 0.0.1 to indicate it's ready for production use.

-  version: "0.0.1",
+  version: "1.0.0",

36-46: Add error handling and improve the success message

While the implementation is functional, consider these improvements:

  1. Add error handling for API failures
  2. Include the list name in the success message for better context
   async run({ $ }) {
+    const list = await this.campaignMonitor.getList({
+      $,
+      listId: this.listId,
+    });
+
     const response = await this.campaignMonitor.unsubscribeSubscriber({
       $,
       listId: this.listId,
       data: {
         EmailAddress: this.subscriber,
       },
     });
-    $.export("$summary", `Successfully unsubscribed ${this.subscriber}`);
+    $.export("$summary", `Successfully unsubscribed ${this.subscriber} from list "${list.Title}"`);
     return response;
   },
components/campaign_monitor/sources/new-email-open/new-email-open.mjs (2)

7-7: Enhance the component description for clarity.

The current description could be more informative about when events are emitted and what data is included.

-  description: "Emit new event when an email from a campaign is opened",
+  description: "Emit new event when an email from a campaign is opened. Each event includes the subscriber's email address and timestamp of the open.",

13-28: Add required field validation to props.

Both clientId and campaignId appear to be required fields but this isn't explicitly specified in the prop definitions.

     clientId: {
       propDefinition: [
         common.props.campaignMonitor,
         "clientId",
       ],
+      required: true,
     },
     campaignId: {
       propDefinition: [
         common.props.campaignMonitor,
         "campaignId",
         (c) => ({
           clientId: c.clientId,
         }),
       ],
+      required: true,
     },
components/campaign_monitor/actions/add-subscriber/add-subscriber.mjs (1)

37-42: Add phone number format validation

The phone prop accepts any string format. Consider adding format validation to ensure valid international phone numbers.

   phone: {
     type: "string",
     label: "Phone",
     description: "The phone number of the subscriber. Example: `+5012398752`",
+    pattern: "^\\+[1-9]\\d{1,14}$",
+    pattern_description: "Must be in E.164 format starting with + followed by country code and number",
     optional: true,
   },
components/campaign_monitor/sources/common/base.mjs (2)

28-28: Consider initializing lastTs to current time to prevent processing old events

Currently, _getLastTs() returns 0 if lastTs is not set, which will cause the first run to process all historical events since the Unix epoch. If the intention is to process only new events from the time the source is deployed, consider initializing lastTs to the current timestamp during deployment.

Modify the _getLastTs method:

 _getLastTs() {
-  return this.db.get("lastTs") || 0;
+  return this.db.get("lastTs") || Date.now();
 }

79-81: Pass the max parameter when calling processEvent in run method

In the run method, processEvent is called without the max parameter. If max is an important parameter for limiting the number of events processed, consider passing it explicitly or setting a default value within processEvent.

Set a default value for max in the processEvent method:

 async processEvent(max = 100) { // Set a default value for max
   const lastTs = this._getLastTs();
   // ...

Or pass the max parameter when calling processEvent in run:

 async run() {
-  await this.processEvent();
+  await this.processEvent(100); // Specify the desired max value
 },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 04742bd and 9bd98df.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • components/campaign_monitor/actions/add-subscriber/add-subscriber.mjs (1 hunks)
  • components/campaign_monitor/actions/send-smart-transactional-email/send-smart-transactional-email.mjs (1 hunks)
  • components/campaign_monitor/actions/unsubscribe/unsubscribe.mjs (1 hunks)
  • components/campaign_monitor/campaign_monitor.app.mjs (1 hunks)
  • components/campaign_monitor/package.json (2 hunks)
  • components/campaign_monitor/sources/common/base.mjs (1 hunks)
  • components/campaign_monitor/sources/new-bounce/new-bounce.mjs (1 hunks)
  • components/campaign_monitor/sources/new-email-open/new-email-open.mjs (1 hunks)
  • components/campaign_monitor/sources/new-subscriber/new-subscriber.mjs (1 hunks)
🔇 Additional comments (17)
components/campaign_monitor/package.json (2)

3-3: LGTM: Version bump follows semantic versioning

The version increment from 0.0.1 to 0.1.0 appropriately reflects the addition of new features (polling sources and actions) while maintaining semantic versioning principles.


15-17: Verify @pipedream/platform version compatibility

The dependency on @pipedream/platform is correctly specified, but let's verify the compatibility of version ^3.0.3 with the new Campaign Monitor components.

✅ Verification successful

@pipedream/platform version is compatible
No breaking changes found for version 3.x in CHANGELOG.md.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for latest @pipedream/platform version and its compatibility

# Check the latest version available
npm view @pipedream/platform version

# Look for any breaking changes in the codebase that might affect platform version 3.x
rg -l "breaking.*change" --glob "CHANGELOG.md"

Length of output: 91

components/campaign_monitor/sources/new-subscriber/new-subscriber.mjs (2)

28-36: Verify the ordering field name with Campaign Monitor API.

Please verify that "date" is the correct field name for ordering in the Campaign Monitor API. Consider making this a constant if it's used elsewhere.


11-22: Verify the listId prop dependencies and validation.

The prop definition looks correct, but let's verify:

  1. That the common.props.campaignMonitor reference is properly defined in the base module
  2. That clientId is always available when needed
components/campaign_monitor/sources/new-bounce/new-bounce.mjs (3)

31-33: Verify the listBounces method implementation.

The method references this.campaignMonitor.listBounces. Let's verify its implementation.

#!/bin/bash
# Description: Verify the listBounces method implementation
# Expected: Find the method definition in the campaign_monitor app

rg -A 10 "listBounces.*\{" components/campaign_monitor/

3-52: Verify integration with other Campaign Monitor components.

Let's ensure this component aligns with other Campaign Monitor components and follows the same patterns.

#!/bin/bash
# Description: Verify consistency across Campaign Monitor components
# Expected: Find similar patterns in other components

# Check for consistent component structure
fd -e mjs . components/campaign_monitor/sources/ -x echo "=== {} ===" \; cat {}

# Check for consistent prop usage
rg -l "propDefinition.*clientId|propDefinition.*campaignId" components/campaign_monitor/sources/

11-28: Verify prop definitions in the common base module.

The props structure looks good, with proper handling of the clientId-campaignId dependency. Let's verify the prop definitions in the base module.

components/campaign_monitor/actions/unsubscribe/unsubscribe.mjs (2)

9-35: LGTM! Props configuration is well-structured

The props configuration implements a clean dependency chain (clientId → listId → subscriber) and correctly uses propDefinitions from the Campaign Monitor app. All required fields mentioned in the PR objectives are present.


40-42: Verify the API contract for EmailAddress field

The EmailAddress field is capitalized. Let's verify this matches the Campaign Monitor API contract.

components/campaign_monitor/sources/new-email-open/new-email-open.mjs (1)

31-33: Verify Campaign Monitor API integration.

Ensure that the listOpens method exists in the Campaign Monitor API client and handles the provided parameters correctly.

components/campaign_monitor/actions/send-smart-transactional-email/send-smart-transactional-email.mjs (1)

1-8: LGTM! Component metadata is well-structured.

The component configuration follows best practices with clear naming, proper versioning, and helpful documentation links.

components/campaign_monitor/actions/add-subscriber/add-subscriber.mjs (2)

1-8: LGTM! Component metadata is well-structured.

The component metadata follows best practices with clear naming, proper versioning, and helpful documentation links.


72-79: Verify API field mappings

Let's verify that the field names match the Campaign Monitor API requirements.

components/campaign_monitor/campaign_monitor.app.mjs (2)

73-90: Ensure proper handling of clientId in smartEmailId options

The options function for smartEmailId expects clientId in params. Verify that clientId is correctly passed and handled in all scenarios to fetch smart emails appropriately.

Run the following script to confirm usage:

#!/bin/bash
# Description: Check that `clientId` is correctly utilized when fetching smart emails.

# Search for usages of `listSmartEmails` and ensure `clientId` is included in parameters.
rg 'listSmartEmails\(\{' -A 5 | rg 'clientId'

37-55: Validate presence of listId in subscriber options

In the subscriber property definition, the options function relies on listId. Ensure that listId is provided to avoid issues when fetching subscribers.

Run the following script to check if listId is always passed correctly:

✅ Verification successful

Verified: listId is consistently provided in all subscriber options.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that `listId` is used wherever `subscriber` options are fetched.

# Find occurrences where `subscriber` property is defined without `listId` context.
rg 'subscriber: \{' -A 10 | rg -L 'listId'

Length of output: 120

components/campaign_monitor/sources/common/base.mjs (2)

39-44: Ensure derived components implement getResourceFn and generateMeta methods

The methods getResourceFn() and generateMeta() currently throw errors indicating they are not implemented. This base class expects these methods to be overridden. Please ensure that all components inheriting from this base class provide concrete implementations of these methods to avoid runtime errors.

Run the following script to verify that derived components implement these methods:


51-65: Verify that paginate returns results sorted by date in descending order

The processEvent method assumes that the results returned by this.campaignMonitor.paginate() are sorted in descending order based on the timestamp field (tsField). This is important because the loop breaks when it encounters an item with a timestamp less than lastTs, under the assumption that no subsequent items will have newer timestamps. If the data isn't sorted in descending order, some events may be missed.

To confirm that the paginate method returns results in the correct order, run the following script:

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

🧹 Outside diff range and nitpick comments (3)
components/campaign_monitor/campaign_monitor.app.mjs (3)

40-55: Simplify pagination in subscriber options

The page increment page + 1 is unnecessary as the pagination should be handled by the API method itself.

  async options({
    listId, page,
  }) {
    const { Results: subscribers } = await this.listSubscribers({
      listId,
      params: {
-       page: page + 1,
+       page,
      },
    });

91-102: Enhance consentToTrack description

The description could be more specific about the implications of each option and legal requirements.

-    description: "Whether the subscriber has given permission to have their email opens and clicks tracked",
+    description: "Specify tracking consent status: 'Yes' to enable tracking, 'No' to disable, or 'Unchanged' to maintain current preference. This affects email opens and click tracking in compliance with privacy regulations.",

224-249: Enhance pagination robustness

The pagination implementation could be improved:

  1. Initialize page parameter
  2. Add type validation for max parameter
  async *paginate({
    fn,
    args,
    max,
  }) {
+   if (max !== undefined && (!Number.isInteger(max) || max < 0)) {
+     throw new Error('max must be a non-negative integer');
+   }
    args = {
      ...args,
      params: {
        ...args?.params,
+       page: args?.params?.page || 1,
        pagesize: DEFAULT_PAGE_SIZE,
      },
    };
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 9bd98df and 24c7853.

📒 Files selected for processing (1)
  • components/campaign_monitor/campaign_monitor.app.mjs (1 hunks)
🔇 Additional comments (2)
components/campaign_monitor/campaign_monitor.app.mjs (2)

108-121: Add error handling to _makeRequest method

The previous review comment about adding error handling is still valid. This is critical for proper error management.


1-2: Verify Campaign Monitor's API pagination limits

The DEFAULT_PAGE_SIZE of 1000 seems high. Please verify this against Campaign Monitor's API documentation to ensure it doesn't exceed their rate limits or recommended page sizes.

Copy link
Collaborator

@GTFalcao GTFalcao left a comment

Choose a reason for hiding this comment

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

Hi @michelle0927 , I left a comment regarding a possible duplicate prop, which can impact in testing.

GTFalcao
GTFalcao previously approved these changes Nov 13, 2024
Copy link
Collaborator

@GTFalcao GTFalcao left a comment

Choose a reason for hiding this comment

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

LGTM!

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

🧹 Outside diff range and nitpick comments (3)
components/campaign_monitor/campaign_monitor.app.mjs (3)

1-2: Consider reducing the DEFAULT_PAGE_SIZE value

The current page size of 1000 might lead to high memory usage when dealing with large datasets. Consider reducing this value to a more moderate size (e.g., 100-250) to balance between network requests and memory consumption.


91-102: Consider using an enum for consentToTrack options

The consentToTrack options are hardcoded strings. Consider defining these as constants to prevent typos and improve maintainability.

Add this at the top of the file:

const CONSENT_OPTIONS = {
  YES: "Yes",
  NO: "No",
  UNCHANGED: "Unchanged",
};

Then update the options:

-options: [
-  "Yes",
-  "No",
-  "Unchanged",
-],
+options: Object.values(CONSENT_OPTIONS),

1-270: Consider adding runtime type checking for API responses

The app makes extensive use of destructuring API responses without validating their structure. Consider adding a runtime type checking library like zod or joi to validate API responses and prevent runtime errors.

This would help catch API changes early and provide better error messages. Example implementation:

import { z } from 'zod';

const ClientSchema = z.object({
  ClientID: z.string(),
  Name: z.string(),
});

const listClientsResponseSchema = z.array(ClientSchema);

async listClients(opts = {}) {
  const response = await this._makeRequest({
    path: "/clients.json",
    ...opts,
  });
  return listClientsResponseSchema.parse(response);
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c1ea1f4 and 90b4b2b.

📒 Files selected for processing (1)
  • components/campaign_monitor/campaign_monitor.app.mjs (1 hunks)

@michelle0927
Copy link
Collaborator Author

/approve

@michelle0927 michelle0927 merged commit 973f4f2 into master Nov 15, 2024
12 checks passed
@michelle0927 michelle0927 deleted the issue-14557 branch November 15, 2024 20:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Components] campaign_monitor
2 participants