Skip to content

Conversation

@michelle0927
Copy link
Collaborator

@michelle0927 michelle0927 commented Oct 23, 2025

Resolves #18819

Summary by CodeRabbit

  • New Features

    • Added five event sources for Microsoft Dynamics 365 Sales to emit events when opportunities are created or when close date, close probability, estimated value, or stage change.
    • Improved opportunities retrieval with built-in pagination and a new list-opportunities endpoint for efficient handling of large datasets.
  • Chores

    • Component and action version metadata updated (package and action version bumps).

@vercel
Copy link

vercel bot commented Oct 23, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
pipedream-docs Ignored Ignored Oct 24, 2025 3:50pm
pipedream-docs-redirect-do-not-edit Ignored Ignored Oct 24, 2025 3:50pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 23, 2025

Walkthrough

Adds a reusable polling base and pagination support, introduces five opportunity-related event sources for Microsoft Dynamics 365 Sales, adds listOpportunities and paginate to the app, and applies metadata/version bumps to actions and package.json.

Changes

Cohort / File(s) Summary
Version bumps
components/microsoft_dynamics_365_sales/actions/create-custom-entity/create-custom-entity.mjs, components/microsoft_dynamics_365_sales/actions/find-contact/find-contact.mjs
Updated action metadata version from 0.0.3 → 0.0.4.
Core app enhancements
components/microsoft_dynamics_365_sales/microsoft_dynamics_365_sales.app.mjs
_makeRequest signature updated to accept optional url override; added listOpportunities(opts) method; added async *paginate({ fn, args = {}, max }) to handle OData @odata.nextLink pagination.
Package metadata
components/microsoft_dynamics_365_sales/package.json
Bumped package version 0.1.1 → 0.2.0 and dependencies["@pipedream/platform"] ^3.0.3 → ^3.1.0.
Common polling base
components/microsoft_dynamics_365_sales/sources/common/common.mjs
New reusable polling source with props (microsoftDynamics365Sales, db, timer), timestamp helpers (_getLastTs, _setLastTs), abstract hooks (getResourceFn, getTsField, generateMeta), processEvent(max) core flow, and run().
New opportunity triggers
components/microsoft_dynamics_365_sales/sources/new-opportunity-created/new-opportunity-created.mjs, components/microsoft_dynamics_365_sales/sources/opportunity-stage-updated/opportunity-stage-updated.mjs, components/microsoft_dynamics_365_sales/sources/opportunity-close-date-updated/opportunity-close-date-updated.mjs, components/microsoft_dynamics_365_sales/sources/opportunity-close-probability-updated/opportunity-close-probability-updated.mjs, components/microsoft_dynamics_365_sales/sources/opportunity-estimated-value-updated/opportunity-estimated-value-updated.mjs
Five new source modules extending the common base; each implements getResourceFnlistOpportunities, getArgs, getTsField, state persistence helpers (_getX/_setX) where needed, getRelevantResults change-detection logic, and generateMeta, and deploy/run hooks to emit events.

Sequence Diagram(s)

sequenceDiagram
    participant Timer as Timer
    participant Source as Source (e.g., new-opportunity-created)
    participant Common as Common Base
    participant App as microsoft_dynamics_365_sales.app
    participant API as Dynamics 365 API
    participant DB as Storage

    Timer->>Source: run()
    Source->>Common: processEvent(max)
    Common->>DB: _getLastTs()
    DB-->>Common: lastTs
    Common->>Source: getResourceFn()
    Source-->>Common: listOpportunities
    Common->>Source: getArgs()
    Source-->>Common: args (orderby, params)
    Common->>App: paginate({ fn: listOpportunities, args, max })
    loop pages
        App->>API: _makeRequest(path or url)
        API-->>App: results + @odata.nextLink
        App-->>Common: yielded items
    end
    loop items
        Common->>Source: getTsField()
        Source-->>Common: "createdon" / "modifiedon"
        Common->>Source: getRelevantResults(batch)
        Source->>DB: _getX() / _setX() (state read/write)
        Source-->>Common: relevant items
        Common->>Source: generateMeta(item)
        Source-->>Common: meta { id, summary, ts }
        Common->>Common: emit(item, meta)
    end
    Common->>DB: _setLastTs(maxTs)
    DB-->>Common: ack
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A rabbit hops where pages flow,
New chances bloom and timestamps grow.
Stages, dates, and probabilities sway,
I nibble deltas, then send them away.
Hooray — events hop out to play! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The PR description provided is minimal, containing only "Resolves #18819" without any content following the required template structure. The template specifies a "WHY" section that should be completed by the author to explain the rationale and purpose of the changes. This section is entirely missing, leaving the description incomplete and not aligned with the repository's expectations for PR documentation. Complete the PR description by adding the missing "WHY" section explaining the purpose of these changes. Provide context on why these five opportunity-level triggers are being added, how they address the requirements in issue #18819, and any relevant implementation notes. This will help reviewers and future maintainers understand the motivation behind the changes.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title "Microsoft Dynamics - new sources" clearly identifies that the PR adds new source modules to the Microsoft Dynamics integration. While the title could be more specific by mentioning opportunity-level triggers, it accurately captures the primary change and is not misleading or overly vague. A teammate reviewing the history would understand that new sources are being introduced to the component.
Linked Issues Check ✅ Passed The PR successfully implements all five opportunity-level triggers required by issue #18819: new opportunity created (new-opportunity-created.mjs), opportunity stage changes (opportunity-stage-updated.mjs), close date changes (opportunity-close-date-updated.mjs), probability changes (opportunity-close-probability-updated.mjs), and estimated value changes (opportunity-estimated-value-updated.mjs). Supporting infrastructure has been added including the paginate utility method, listOpportunities method, and a common polling source base class (sources/common/common.mjs) that enables these triggers to function. All required coding objectives from the linked issue are met.
Out of Scope Changes Check ✅ Passed Most changes are directly aligned with the objective to create five opportunity-level triggers. The core implementation files (five source modules, common base, and enhanced app methods) all support the linked issue requirements. However, version bumps to unrelated action files (create-custom-entity.mjs and find-contact.mjs from 0.0.3 to 0.0.4) appear tangential to the opportunity trigger objectives, though the package.json version bump (0.1.1 to 0.2.0) and dependency update are reasonable for this feature release.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-18819

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

Comment @coderabbitai help to get the list of available commands and usage tips.

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 (6)
components/microsoft_dynamics_365_sales/microsoft_dynamics_365_sales.app.mjs (1)

110-131: Consider avoiding mutation of the args parameter.

The pagination logic is correct and handles OData-style pagination properly. However, line 129 mutates the input args object by setting args.url = nextLink. While this works in the current usage context (where getArgs() creates fresh objects), mutating input parameters can lead to unexpected behavior if the args object is reused by callers.

Consider this alternative that avoids mutation:

 async *paginate({
   fn, args = {}, max,
 }) {
   let count = 0;
-  let nextLink = null;
+  let currentArgs = { ...args };

   do {
-    const response = await fn(args);
+    const response = await fn(currentArgs);
     const items = response.value;
     if (!items?.length) {
       return;
     }
     for (const item of items) {
       yield item;
       if (max && ++count >= max) {
         return;
       }
     }
-    nextLink = response["@odata.nextLink"];
-    args.url = nextLink;
-  } while (nextLink);
+    const nextLink = response["@odata.nextLink"];
+    if (!nextLink) break;
+    currentArgs = { ...args, url: nextLink };
+  } while (true);
 },
components/microsoft_dynamics_365_sales/sources/common/common.mjs (3)

43-51: Ensure results are ordered by timestamp DESC for correctness of early-break.

The break on “older than lastTs” is only correct if pages are sorted by getTsField() descending. Please verify every extending source sets $orderby: "<tsField> desc" in getArgs(). Optionally, set a safe default when absent.

Optional fallback:

       const args = this.getArgs();
       const tsField = this.getTsField();
+      // Default to DESC order by timestamp if caller didn’t specify
+      if (!args?.params?.$orderby) {
+        args.params = { ...(args.params ?? {}), "$orderby": `${tsField} desc` };
+      }

70-73: Emit oldest-first for chronological consistency (optional).

Emitting in ascending timestamp order avoids “newest-first” bursts.

-      relevantResults.forEach((item) => {
+      relevantResults
+        .sort((a, b) => Date.parse(a[this.getTsField()]) - Date.parse(b[this.getTsField()]))
+        .forEach((item) => {
         const meta = this.generateMeta(item);
         this.$emit(item, meta);
       });

7-16: Allow users to cap work per run.

Expose an optional maxResults prop and pass it to processEvent to bound pagination.

   props: {
     microsoftDynamics365Sales,
     db: "$.service.db",
     timer: {
       type: "$.interface.timer",
       default: {
         intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL,
       },
     },
+    maxResults: {
+      type: "integer",
+      label: "Max items to fetch per run",
+      optional: true,
+      default: 50,
+    },
   },
...
   async run() {
-    await this.processEvent();
+    await this.processEvent(this.maxResults);
   },

Also applies to: 76-78

components/microsoft_dynamics_365_sales/sources/opportunity-close-probability-updated/opportunity-close-probability-updated.mjs (2)

49-51: Strengthen event id to avoid collisions and capture multiple changes at same timestamp.

Including separators and the new value yields clearer, more unique ids.

-        id: `${opportunity.opportunityid}${ts}`,
+        id: `${opportunity.opportunityid}:${opportunity.modifiedon}:${opportunity.closeprobability}`,

22-28: Reduce payload with $select (optional).

Limit fields to those used to shrink responses and speed polling.

       return {
         params: {
           "$orderby": "modifiedon desc",
+          "$select": "opportunityid,name,modifiedon,closeprobability",
         },
       };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 088babc and 59320e6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • components/microsoft_dynamics_365_sales/actions/create-custom-entity/create-custom-entity.mjs (1 hunks)
  • components/microsoft_dynamics_365_sales/actions/find-contact/find-contact.mjs (1 hunks)
  • components/microsoft_dynamics_365_sales/microsoft_dynamics_365_sales.app.mjs (3 hunks)
  • components/microsoft_dynamics_365_sales/package.json (2 hunks)
  • components/microsoft_dynamics_365_sales/sources/common/common.mjs (1 hunks)
  • components/microsoft_dynamics_365_sales/sources/new-opportunity-created/new-opportunity-created.mjs (1 hunks)
  • components/microsoft_dynamics_365_sales/sources/opportunity-close-date-updated/opportunity-close-date-updated.mjs (1 hunks)
  • components/microsoft_dynamics_365_sales/sources/opportunity-close-probability-updated/opportunity-close-probability-updated.mjs (1 hunks)
  • components/microsoft_dynamics_365_sales/sources/opportunity-estimated-value-updated/opportunity-estimated-value-updated.mjs (1 hunks)
  • components/microsoft_dynamics_365_sales/sources/opportunity-stage-updated/opportunity-stage-updated.mjs (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
components/microsoft_dynamics_365_sales/sources/opportunity-close-date-updated/opportunity-close-date-updated.mjs (1)
components/microsoft_dynamics_365_sales/sources/common/common.mjs (3)
  • results (53-53)
  • relevantResults (65-65)
  • ts (55-55)
components/microsoft_dynamics_365_sales/sources/opportunity-stage-updated/opportunity-stage-updated.mjs (1)
components/microsoft_dynamics_365_sales/sources/common/common.mjs (3)
  • results (53-53)
  • relevantResults (65-65)
  • ts (55-55)
components/microsoft_dynamics_365_sales/microsoft_dynamics_365_sales.app.mjs (3)
components/microsoft_dynamics_365_sales/actions/create-custom-entity/create-custom-entity.mjs (1)
  • response (153-156)
components/microsoft_dynamics_365_sales/sources/common/common.mjs (2)
  • args (44-44)
  • items (47-51)
components/zep/actions/get-threads/get-threads.mjs (1)
  • max (39-39)
components/microsoft_dynamics_365_sales/sources/opportunity-close-probability-updated/opportunity-close-probability-updated.mjs (1)
components/microsoft_dynamics_365_sales/sources/common/common.mjs (3)
  • results (53-53)
  • relevantResults (65-65)
  • ts (55-55)
components/microsoft_dynamics_365_sales/sources/opportunity-estimated-value-updated/opportunity-estimated-value-updated.mjs (1)
components/microsoft_dynamics_365_sales/sources/common/common.mjs (3)
  • results (53-53)
  • relevantResults (65-65)
  • ts (55-55)
components/microsoft_dynamics_365_sales/sources/common/common.mjs (6)
components/zep/actions/get-threads/get-threads.mjs (1)
  • max (39-39)
components/microsoft_dynamics_365_sales/microsoft_dynamics_365_sales.app.mjs (1)
  • items (118-118)
components/microsoft_dynamics_365_sales/sources/opportunity-close-date-updated/opportunity-close-date-updated.mjs (2)
  • ts (47-47)
  • relevantResults (34-34)
components/microsoft_dynamics_365_sales/sources/opportunity-close-probability-updated/opportunity-close-probability-updated.mjs (2)
  • ts (47-47)
  • relevantResults (34-34)
components/microsoft_dynamics_365_sales/sources/opportunity-estimated-value-updated/opportunity-estimated-value-updated.mjs (2)
  • ts (47-47)
  • relevantResults (34-34)
components/microsoft_dynamics_365_sales/sources/opportunity-stage-updated/opportunity-stage-updated.mjs (2)
  • ts (47-47)
  • relevantResults (34-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Lint Code Base
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (10)
components/microsoft_dynamics_365_sales/actions/create-custom-entity/create-custom-entity.mjs (1)

9-9: LGTM! Metadata version bump.

The version update aligns with the broader PR changes introducing new opportunity sources.

components/microsoft_dynamics_365_sales/package.json (1)

3-3: LGTM! Package version and dependency updates.

The minor version bump to 0.2.0 appropriately reflects the addition of new opportunity-level sources, and the platform dependency update aligns with the new functionality.

Also applies to: 16-16

components/microsoft_dynamics_365_sales/actions/find-contact/find-contact.mjs (1)

7-7: LGTM! Metadata version bump.

Consistent with other version updates in this PR.

components/microsoft_dynamics_365_sales/sources/new-opportunity-created/new-opportunity-created.mjs (1)

1-39: LGTM! Clean implementation for new opportunity creation trigger.

The source correctly extends the common base, orders opportunities by creation date, and generates appropriate metadata. The deploy hook appropriately fetches the 10 most recent opportunities for initial state.

components/microsoft_dynamics_365_sales/sources/opportunity-close-date-updated/opportunity-close-date-updated.mjs (2)

32-44: LGTM! Correct delta detection for close date changes.

The change detection logic correctly filters for opportunities where estimatedclosedate has changed. Note that the conditional at line 37 ensures events are only emitted for opportunities that were previously tracked, avoiding false positives during initial state population.


46-53: LGTM! Metadata generation includes timestamp for uniqueness.

The composite ID ${opportunity.opportunityid}${ts} correctly allows multiple update events for the same opportunity over time while maintaining uniqueness.

components/microsoft_dynamics_365_sales/sources/opportunity-estimated-value-updated/opportunity-estimated-value-updated.mjs (1)

1-55: LGTM! Consistent implementation pattern for estimated value tracking.

This source correctly implements the same delta-detection pattern as the close-date source, tracking changes to estimatedvalue and only emitting events for previously-seen opportunities.

components/microsoft_dynamics_365_sales/sources/opportunity-stage-updated/opportunity-stage-updated.mjs (1)

1-55: LGTM! Consistent implementation for stage change tracking.

This source correctly follows the established delta-detection pattern, tracking changes to stageid with appropriate state management and metadata generation.

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

41-60: LGTM! URL override support enables pagination.

The addition of the url parameter to _makeRequest maintains backward compatibility while enabling explicit URL specification for pagination scenarios.


89-94: LGTM! Clean opportunities listing method.

The new listOpportunities method follows the established pattern of other resource listing methods in this module.

jcortes
jcortes previously approved these changes Oct 23, 2025
Copy link
Collaborator

@jcortes jcortes 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 lgtm! Ready for QA!

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59320e6 and bc2aad9.

📒 Files selected for processing (2)
  • components/microsoft_dynamics_365_sales/sources/opportunity-close-date-updated/opportunity-close-date-updated.mjs (1 hunks)
  • components/microsoft_dynamics_365_sales/sources/opportunity-stage-updated/opportunity-stage-updated.mjs (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/microsoft_dynamics_365_sales/sources/opportunity-close-date-updated/opportunity-close-date-updated.mjs
🧰 Additional context used
🧬 Code graph analysis (1)
components/microsoft_dynamics_365_sales/sources/opportunity-stage-updated/opportunity-stage-updated.mjs (1)
components/microsoft_dynamics_365_sales/sources/common/common.mjs (3)
  • results (53-53)
  • relevantResults (65-65)
  • ts (55-55)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (5)
components/microsoft_dynamics_365_sales/sources/opportunity-stage-updated/opportunity-stage-updated.mjs (5)

1-1: LGTM!

Import statement follows the established pattern for source modules in this component.


3-10: LGTM!

Metadata follows the standard pattern: spreads common base, declares appropriate key/name/description, uses semantic versioning (0.0.1 for new source), and sets dedupe to "unique" for event identification.


13-18: LGTM!

State persistence helpers follow the expected pattern: _getStages() safely defaults to an empty object, and _setStages() persists the updated map. The underscore prefix correctly indicates these are internal methods.


19-31: LGTM!

Resource function, query arguments, and timestamp field are correctly configured: listOpportunities is the appropriate resource, ordering by modifiedon desc ensures the polling logic processes the most recent records first, and the timestamp field is consistent throughout.


46-53: LGTM!

Metadata generation correctly constructs a unique ID from opportunityid and timestamp, and provides a clear summary. The function assumes modifiedon, opportunityid, and name are present in the API response, which is reasonable as these are standard fields in the Dynamics 365 opportunity entity.

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.

Microsoft Dynamics – opportunity triggers: new opportunity, stage change, close date change, probability change, revenue change

2 participants