Skip to content

[Components] attio - added new components #16411

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 1 commit into
base: master
Choose a base branch
from

Conversation

jcortes
Copy link
Collaborator

@jcortes jcortes commented Apr 23, 2025

WHY

Resolves #16302

Summary by CodeRabbit

  • New Features

    • Added actions for creating and updating person records, creating tasks, and deleting list entries in Attio.
    • Introduced a new event source for instant detection of new notes, tasks, or comments.
    • Added new constants to enhance API integration and resource targeting.
  • Improvements

    • Improved property selection, labeling, and input flexibility for record creation and updates.
    • Unified event subscription interfaces by replacing separate event type and filter methods with subscription objects across all instant event sources.
    • Enhanced webhook management with new dedicated creation and deletion methods.
    • Refined API method calls and property definitions for better modularity and consistency.
    • Updated utility functions to support dynamic property generation and metadata handling.
  • Bug Fixes

    • Updated versioning and summary message formats for actions and event sources.

@jcortes jcortes self-assigned this Apr 23, 2025
Copy link

vercel bot commented Apr 23, 2025

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 Apr 30, 2025 8:24pm
pipedream-docs ⬜️ Ignored (Inspect) Apr 30, 2025 8:24pm
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) Apr 30, 2025 8:24pm

Copy link
Contributor

coderabbitai bot commented Apr 23, 2025

Walkthrough

This update introduces significant enhancements and refactoring to the Attio integration components. It adds new actions for creating and updating person records, as well as creating tasks. The webhook source system is refactored to use a new subscription-based event declaration, replacing the previous event type and filter methods. A new source is added to emit events for new activities (notes, tasks, comments). The Attio app is refactored for modularity, with expanded property definitions and unified HTTP request handling. Supporting constants are centralized in a new module, and multiple component versions are incremented to reflect these changes.

Changes

File(s) Change Summary
components/attio/actions/create-person/create-person.mjs
components/attio/actions/update-person/update-person.mjs
components/attio/actions/create-task/create-task.mjs
New action modules for creating and updating people and tasks in Attio, implementing API payload construction and submission logic.
components/attio/actions/create-note/create-note.mjs
components/attio/actions/delete-list-entry/delete-list-entry.mjs
Refactored to use new method wrappers for API calls, updated property definitions, and improved parameter mapping.
components/attio/actions/create-update-record/create-update-record.mjs Updated version and modified matching attribute props and logic for record upsert.
components/attio/attio.app.mjs Major refactor: expanded propDefinitions for person fields, added async option loaders, unified HTTP request methods, improved modularity, and updated API endpoint usage.
components/attio/common/constants.mjs New module introducing constants for API base URL, version path, default limit, and target object types.
components/attio/package.json Package version updated from 0.2.0 to 0.3.0.
components/attio/sources/common/base.mjs Refactored webhook management: replaced event type/filter methods with subscription-based method, encapsulated webhook creation/deletion logic.
components/attio/sources/new-activity-created-instant/new-activity-created-instant.mjs
components/attio/sources/new-activity-created-instant/test-event.mjs
New source for emitting events on note, task, or comment creation, with sample event for testing.
components/attio/sources/new-note-instant/new-note-instant.mjs
components/attio/sources/note-updated-instant/note-updated-instant.mjs
components/attio/sources/new-object-attribute-instant/new-object-attribute-instant.mjs
components/attio/sources/object-attribute-updated-instant/object-attribute-updated-instant.mjs
Updated from single event type method to new subscription-based event declaration; version increments.
components/attio/sources/new-list-entry-instant/new-list-entry-instant.mjs
components/attio/sources/list-entry-deleted-instant/list-entry-deleted-instant.mjs
components/attio/sources/list-entry-updated-instant/list-entry-updated-instant.mjs
components/attio/sources/new-record-created-instant/new-record-created-instant.mjs
components/attio/sources/record-updated-instant/record-updated-instant.mjs
Replaced getEventType/getFilter methods with getSubscriptions, encapsulating event type and filter in a unified structure; version updates.
components/attio/common/utils.mjs Removed parseValues function; added new utility functions for generating metadata and dynamic property definitions for fields.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AttioAction
    participant AttioApp
    participant AttioAPI

    User->>AttioAction: Trigger "Create Person" or "Update Person" or "Create Task"
    AttioAction->>AttioApp: Call createRecord/updateRecord/postTask with user data
    AttioApp->>AttioAPI: Send HTTP POST/PATCH to Attio endpoint
    AttioAPI-->>AttioApp: Return API response
    AttioApp-->>AttioAction: Return result
    AttioAction-->>User: Output summary and data
Loading
sequenceDiagram
    participant SourceComponent
    participant BaseSource
    participant AttioApp
    participant AttioAPI

    SourceComponent->>BaseSource: Activate (subscribe to events)
    BaseSource->>AttioApp: createWebhook with subscriptions
    AttioApp->>AttioAPI: POST /webhooks
    AttioAPI-->>AttioApp: Webhook confirmation
    AttioApp-->>BaseSource: Webhook created
    BaseSource-->>SourceComponent: Ready to receive events
Loading

Assessment against linked issues

Objective Addressed Explanation
Implement webhook source for "new-activity-instant" to emit events on note, task, or comment creation (#16302)
Implement "create-person" action with required name and email, optional company, tags, custom fields (#16302)
Implement "update-person" action to update person info by ID, supporting optional fields (#16302)
Implement "create-task" action for creating tasks for a person record (#16302)

Suggested labels

User submitted

Poem

In the warren of code, a hop and a dash,
New people and tasks join in a flash!
Webhooks now listen, with ears tall and keen,
For notes, tasks, and comments—on every new scene.
Constants are gathered, requests unified—
This rabbit’s delighted, with whiskers of pride!
🐇✨

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/attio/actions/create-note/create-note.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/attio/actions/create-person/create-person.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/attio/actions/create-task/create-task.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)

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

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

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

141-152: 🛠️ Refactor suggestion

Repeat the safe‑offset fix in every loader that paginates

To keep behaviour consistent, apply the page ?? 0 guard (or default
page = 0 in the parameter list) to:

  • entryId.options() – lines 147‑149
  • attributeId.options() – lines 180‑182

Otherwise the first‑page lookup for those selectors can break.
Happy to open a small follow‑up PR if you like.

Also applies to: 177-183

🧹 Nitpick comments (9)
components/attio/actions/create-task/create-task.mjs (1)

16-20: Consider adding date validation for deadline

While the description explains the ISO 8601 format requirement, consider adding validation to ensure the provided date string is valid.

 deadlineAt: {
   type: "string",
   label: "Deadline",
   description: "The deadline of the task in ISO 8601 format (e.g. `2025-04-22T10:00:00Z`)",
+  validate: (value) => {
+    if (!value) return true;
+    const date = new Date(value);
+    return !isNaN(date.getTime()) || "Please provide a valid ISO 8601 date string";
+  },
 },
components/attio/sources/new-activity-created-instant/new-activity-created-instant.mjs (2)

9-9: Consider starting with version 0.0.1.

The component is using version 0.0.3 despite being newly introduced. Usually, new components start with version 0.0.1 and increment as changes are made.

-  version: "0.0.3",
+  version: "0.0.1",

30-36: Consider extracting repeated ID logic to a helper method.

The same ID extraction logic is repeated three times in this method. Consider extracting it to improve readability and maintainability.

  generateMeta(record) {
+   const activityId = record.id.task_id || record.id.note_id || record.id.comment_id;
    return {
-     id: record.id.task_id || record.id.note_id || record.id.comment_id,
-     summary: `New Activity with ID: ${record.id.task_id || record.id.note_id || record.id.comment_id}`,
+     id: activityId,
+     summary: `New Activity with ID: ${activityId}`,
      ts: Date.now(),
    };
  },
components/attio/actions/update-person/update-person.mjs (1)

110-218: Consider shared utilities for common person record operations.

This component shares significant code with the create-person action. Consider extracting the common data structuring logic into a shared utility function to improve maintainability.

You could create a utility function in a common module that takes person fields and returns the structured data object, which both actions could then use.

components/attio/attio.app.mjs (5)

196-204: Expose Accept and Content‑Type defaults in one place

getHeaders() hard‑codes only Authorization. Every Attio endpoint
expects JSON, so callers must remember to pass
headers: { "Content-Type": "application/json" } each time. Consider
centralising the two defaults:

   getHeaders(headers) {
     return {
       ...headers,
+      "Accept": "application/json",
+      "Content-Type": "application/json",
       "Authorization": `Bearer ${this.$auth.oauth_access_token}`,
     };
   },

This removes repetition in the new action components.


214-237: Method priority allows accidental override of HTTP verb

The helpers (post, patch, put, delete) spread ...args after
the hard‑coded method, letting callers silently override the verb.
A typo such as this.post({ method: "GET", … }) would send a GET but
be named “post”, confusing for readers and log inspection.

Move method to the end to make it authoritative:

-    post(args = {}) {
-      return this._makeRequest({
-        method: "POST",
-        ...args,
-      });
+    post(args = {}) {
+      return this._makeRequest({
+        ...args,
+        method: "POST",
+      });
     },

Repeat for the other three helpers.


48-71: Social profile props could share a single reusable definition

linkedin, twitter, facebook, and instagram are structurally
identical. Duplicating them bloats the component and invites
inconsistent edits later. A small helper cuts the noise:

const socialProp = (platform, label = platform) => ({
  type: "string",
  label: label.charAt(0).toUpperCase() + label.slice(1),
  description: `The person's ${label} profile URL.`,
  optional: true,
});

linkedin: socialProp("linkedin"),
twitter: socialProp("twitter", "Twitter handle"),
facebook: socialProp("facebook"),
instagram: socialProp("instagram"),

Not mandatory, but keeps the file readable as more fields are added.


124-135: Return { label, value } objects for better UX

The listId and entryId loaders currently return bare strings for the
value, relying on Pipedream to auto‑label. Returning explicit objects
gives users readable dropdowns immediately — especially helpful for
entryId, which today shows only UUIDs.

-return data?.map(({ id: { entry_id: value } }) => value) || [];
+return data?.map(({ id: { entry_id: value } }) => ({
+  label: value,
+  value,
+})) || [];

Applies similarly to entryId and any other loader that returns raw
IDs.


238-243: Add pagination to listWorkspaceMembers

Large workspaces can have hundreds of members; the current call fetches
only the default page. The Attio API supports limit/offset, so
expose page in the options loader and reuse the existing pagination
pattern.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 106b401 and ae31376.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • components/attio/actions/create-note/create-note.mjs (3 hunks)
  • components/attio/actions/create-person/create-person.mjs (1 hunks)
  • components/attio/actions/create-task/create-task.mjs (1 hunks)
  • components/attio/actions/create-update-record/create-update-record.mjs (1 hunks)
  • components/attio/actions/delete-list-entry/delete-list-entry.mjs (2 hunks)
  • components/attio/actions/update-person/update-person.mjs (1 hunks)
  • components/attio/attio.app.mjs (3 hunks)
  • components/attio/common/constants.mjs (1 hunks)
  • components/attio/package.json (1 hunks)
  • components/attio/sources/common/base.mjs (3 hunks)
  • components/attio/sources/list-entry-deleted-instant/list-entry-deleted-instant.mjs (2 hunks)
  • components/attio/sources/list-entry-updated-instant/list-entry-updated-instant.mjs (2 hunks)
  • components/attio/sources/new-activity-created-instant/new-activity-created-instant.mjs (1 hunks)
  • components/attio/sources/new-activity-created-instant/test-event.mjs (1 hunks)
  • components/attio/sources/new-list-entry-instant/new-list-entry-instant.mjs (2 hunks)
  • components/attio/sources/new-note-instant/new-note-instant.mjs (1 hunks)
  • components/attio/sources/new-object-attribute-instant/new-object-attribute-instant.mjs (1 hunks)
  • components/attio/sources/new-record-created-instant/new-record-created-instant.mjs (2 hunks)
  • components/attio/sources/note-updated-instant/note-updated-instant.mjs (1 hunks)
  • components/attio/sources/object-attribute-updated-instant/object-attribute-updated-instant.mjs (1 hunks)
  • components/attio/sources/record-updated-instant/record-updated-instant.mjs (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
components/attio/sources/common/base.mjs (2)
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-10-08T15:33:38.240Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-07-24T02:06:47.016Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.
⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (46)
components/attio/common/constants.mjs (1)

1-18: Well-structured constants module for Attio API integration

This new constants module effectively centralizes important API configuration values like base URL, version path, and pagination limits. The TARGET_OBJECT enum provides a clean way to reference object types throughout the codebase.

This approach follows best practices by:

  1. Eliminating hardcoded values across the integration
  2. Creating a single source of truth for API configuration
  3. Making future API endpoint or configuration changes easier to maintain
components/attio/sources/common/base.mjs (4)

11-15: Good refactoring of webhook activation with subscription-based model

The webhook activation method now uses the new createWebhook wrapper and a subscription-based model via getSubscriptions() instead of separate event type and filter methods. This approach is more modular and follows the pattern used in other Pipedream integrations.


22-27: Good refactoring of webhook deactivation with wrapper method

The deactivation hook now uses the new deleteWebhook wrapper method, which is consistent with the modular approach applied to the activation hook.


37-39: Abstract method correctly implemented

Replacing the previous abstract methods with a single getSubscriptions() method follows good object-oriented design principles. The error message correctly indicates that subclasses must implement this method.


43-56: Well-implemented webhook wrapper methods

The new createWebhook and deleteWebhook methods properly encapsulate the API calls to create and delete webhooks. They cleanly use the app's HTTP methods and handle path construction. This approach:

  1. Centralizes webhook management logic
  2. Makes the code more maintainable
  3. Follows the DRY principle by avoiding repetition in derived classes
components/attio/package.json (1)

3-3: Appropriate version bump for feature additions

Incrementing the package version from 0.2.0 to 0.3.0 is appropriate given the significant additions (new components for creating/updating persons and tasks) and refactoring (webhook subscription model) in this PR.

components/attio/actions/create-update-record/create-update-record.mjs (1)

8-8: Version incremented for consistency

The action version has been incremented from "0.0.2" to "0.0.3", which maintains version consistency with other updated Attio components in this PR.

components/attio/sources/object-attribute-updated-instant/object-attribute-updated-instant.mjs (1)

9-21: Clean architectural improvement for webhook subscription management

The version increment and refactoring from getEventType() to the new getSubscriptions() method aligns with the broader architectural updates in the Attio integration. This new pattern provides more flexibility by allowing multiple subscriptions if needed in the future, and creates a more consistent approach across all source components.

components/attio/sources/note-updated-instant/note-updated-instant.mjs (1)

9-21: Consistent implementation of the subscription pattern

Good job maintaining consistency with the architectural pattern change. The version increment and implementation of the new getSubscriptions() method follows the same structure as other Attio source components, making the codebase more cohesive and easier to maintain.

components/attio/sources/new-activity-created-instant/test-event.mjs (1)

1-16: Well-structured test event for the new activity source

This test event provides a good representation of an Attio webhook payload for task creation events. The structure includes all necessary fields (webhook_id, event_type, workspace_id, task_id, and actor details), making it valuable for testing the new activity source component.

components/attio/sources/new-note-instant/new-note-instant.mjs (1)

9-21: Systematic application of the new subscription pattern

The version increment and replacement of getEventType() with the new getSubscriptions() method demonstrates a systematic approach to refactoring the Attio integration. This consistent implementation across all source components will make future maintenance and enhancements easier.

components/attio/sources/new-object-attribute-instant/new-object-attribute-instant.mjs (2)

9-9: Version bump is appropriate

The version increment from 0.0.1 to 0.0.2 is appropriate for this change since you're modifying the component's interface without changing its core functionality.


14-21: Good refactoring of event subscription

The new getSubscriptions() method effectively replaces the previous getEventType() method, returning a structured subscription object instead. This approach is more flexible as it allows for multiple subscriptions if needed in the future.

components/attio/sources/list-entry-deleted-instant/list-entry-deleted-instant.mjs (2)

9-9: Version bump is appropriate

The version increment from 0.0.1 to 0.0.2 is appropriate for this change since you're modifying the component's interface without changing its core functionality.


23-38: Good refactoring of event subscription and filter

The new getSubscriptions() method effectively combines the previous getEventType() and getFilter() methods into a single, more structured approach. This unification makes the code more maintainable and aligns with the refactoring pattern applied across other Attio components.

components/attio/sources/new-record-created-instant/new-record-created-instant.mjs (2)

9-9: Version bump is appropriate

The version increment from 0.0.2 to 0.0.3 is appropriate for this change since you're modifying the component's interface without changing its core functionality.


23-38: Good refactoring of event subscription and filter

The new getSubscriptions() method effectively combines the previous getEventType() and getFilter() methods into a single, more structured approach. This makes the component consistent with the broader refactoring pattern across Attio source components.

components/attio/sources/new-list-entry-instant/new-list-entry-instant.mjs (2)

9-9: Version bump is appropriate

The version increment from 0.0.2 to 0.0.3 is appropriate for this change since you're modifying the component's interface without changing its core functionality.


23-38: Good refactoring of event subscription and filter

The new getSubscriptions() method effectively combines the previous getEventType() and getFilter() methods into a single, more structured approach. This unification improves consistency across components and enables more flexible subscription management.

components/attio/sources/list-entry-updated-instant/list-entry-updated-instant.mjs (2)

9-9: Appropriate version increment

Version has been correctly updated from 0.0.1 to 0.0.2 to reflect the interface changes in how webhook subscriptions are handled.


23-38: Good refactoring to unified subscription model

The change from separate getEventType() and getFilter() methods to a single getSubscriptions() method improves modularity and aligns with the broader refactoring across multiple Attio source components. The subscription structure is well-organized and maintains the same filtering logic.

components/attio/actions/create-task/create-task.mjs (2)

1-71: Well-structured new action component

This new "Create Task" action follows best practices for Pipedream components with clear prop definitions, appropriate documentation links, and concise methods. The component correctly implements the Attio API for task creation.


60-63: Good handling of optional assignees

The code correctly handles the case where assigneeIds might be undefined by using optional chaining and providing an empty array fallback.

components/attio/actions/delete-list-entry/delete-list-entry.mjs (4)

7-7: Appropriate version increment

Version has been correctly updated from 0.0.2 to 0.0.3 to reflect the interface changes in the component.


21-23: Improved parameter destructuring

The simplification of the propDefinition callback with direct destructuring improves code readability.


27-36: Good modularization with dedicated API method

Adding a dedicated deleteListEntry method improves code organization and follows the pattern used in other components. The method correctly constructs the API path and forwards additional options.


38-42: Clean method invocation

Run method now correctly uses the new dedicated method instead of directly calling the app's method, which is consistent with the overall refactoring approach.

components/attio/sources/record-updated-instant/record-updated-instant.mjs (2)

9-9: Appropriate version increment

Version has been correctly updated from 0.0.2 to 0.0.3 to reflect the interface changes in how webhook subscriptions are handled.


23-38: Good refactoring to unified subscription model

The change from separate methods to a single getSubscriptions() method improves consistency and aligns with the broader refactoring across Attio source components. The subscription structure effectively maintains the same filtering logic while providing a more organized approach to webhook event handling.

components/attio/actions/create-note/create-note.mjs (7)

7-7: Version increment looks appropriate.

The version has been incremented from 0.0.2 to 0.0.3, which is appropriate given the refactoring and improvements made to this component.


15-23: Good enhancement to the parentObject prop.

The mapper function provides better UX by converting API values to more user-friendly display formats. This improves the component's usability by showing meaningful labels to users instead of raw API values.


25-26: Improved prop labeling.

The updated label and description are more clear and consistent with other components in the integration.


32-33: Improved parameter naming.

Changing from objectId to targetObject provides better semantic clarity about what the parameter represents.


50-57: Good modularization with methods object.

Extracting API call logic into a dedicated method improves code organization and maintainability. This approach also makes the component more consistent with other Attio components.


59-59: Refactored to use the dedicated createNote method.

This change makes the code more maintainable by centralizing the API call logic in the methods section.


71-71: Improved summary formatting.

Using backticks around the ID improves readability in the output, making it easier to distinguish the ID from the rest of the text.

components/attio/sources/new-activity-created-instant/new-activity-created-instant.mjs (2)

4-11: Nice addition of a unified activity source.

This new component efficiently handles multiple activity types (notes, tasks, comments) in a single source, which provides a more streamlined way to track engagement activities.


14-29: Well-structured subscription method.

The getSubscriptions method clearly defines all the event types to listen for without any filters, making it easy to understand what events this source will emit.

components/attio/actions/update-person/update-person.mjs (3)

4-9: Great addition of an update person action.

This well-structured action provides comprehensive options for updating existing person records in Attio.


12-29: Well-designed recordId prop with custom mapper.

The prop definition with custom mapper enhances usability by displaying meaningful person names instead of just IDs in the dropdown.


129-214: Well-structured conditional data payload.

The approach of conditionally including fields only when they're provided is clean and efficient. The nested structure correctly follows the Attio API's expected format.

components/attio/actions/create-person/create-person.mjs (5)

4-9: Great addition of a create person action.

This well-structured action provides comprehensive options for creating new person records in Attio.


12-89: Well-organized props definitions.

The props are well-organized with appropriate labels, descriptions, and leveraging reusable prop definitions from the Attio app.


108-193: Well-structured API call with conditional data fields.

The approach of conditionally including fields only when they're provided is clean and efficient. The structure correctly follows the Attio API's expected format.


195-195: Good use of backticks in summary message.

Using backticks around the ID improves readability in the output, making it easier to distinguish the ID from the rest of the text.


121-121: ⚠️ Potential issue

Fix syntax error in conditional spread operator.

There's a missing parenthesis in the logical condition that could cause unexpected behavior.

-            ...(firstName || lastName) && {
+            ...((firstName || lastName) && {

Likely an incorrect or invalid review comment.

luancazarine
luancazarine previously approved these changes Apr 28, 2025
Copy link
Collaborator

@luancazarine luancazarine left a comment

Choose a reason for hiding this comment

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

Hi @jcortes I just added a minor suggestion. But I'm moving it to Ready for QA anyway

key: "attio-new-activity-created-instant",
name: "New Activity Created (Instant)",
description: "Emit new event when a note, task, or comment is created, useful for tracking engagement in real time.",
version: "0.0.3",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
version: "0.0.3",
version: "0.0.1",

@jcortes jcortes force-pushed the attio-new-components branch from 14a9d5b to 8861d86 Compare April 30, 2025 20:22
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 (1)
components/attio/attio.app.mjs (1)

72-104: Fix potential NaN in offset calculation

In the recordId options function, the offset calculation can result in NaN if page is undefined.

        data: {
          limit: constants.DEFAULT_LIMIT,
-         offset: page * constants.DEFAULT_LIMIT,
+         offset: (page ?? 0) * constants.DEFAULT_LIMIT,
🧹 Nitpick comments (6)
components/attio/actions/create-task/create-task.mjs (3)

46-56: Consider adding validation for linked record properties

The linkedRecordPropsMapper method assumes that this[${prefix}targetObject] and this[${prefix}targetRecordId] exist, but there's no validation to handle cases where they might be undefined.

 linkedRecordPropsMapper(prefix) {
   const {
     [`${prefix}targetObject`]: targetObject,
     [`${prefix}targetRecordId`]: targetRecordId,
   } = this;

+  if (!targetObject || !targetRecordId) {
+    return {};
+  }

   return {
     target_object: targetObject,
     target_record_id: targetRecordId,
   };
 },

57-103: Enhance record label fallback for better UX

In the getLinkedRecordsPropDefinitions method, there's a risk of displaying "undefined" labels if the name array exists but has no valid values.

 targetRecordOptions = data.map(({
   id: { record_id: value },
   values: { name },
 }) => ({
   value,
-  label: name[0]?.value || name[0]?.full_name || "unknown",
+  label: name && (name[0]?.value || name[0]?.full_name) || `Record ${value}`,
 }));

123-156: Run method is well-structured but could use error handling

The method effectively collects inputs, transforms them to the API format, and makes the API call. The export summary provides good feedback to the user.

Consider adding error handling to improve robustness:

 async run({ $ }) {
   const {
     content,
     deadlineAt,
     isCompleted,
     assigneeIds,
     numberOfLinkedRecords,
     linkedRecordPropsMapper,
   } = this;

+  try {
     const response = await this.createTask({
       $,
       data: {
         data: {
           format: "plaintext",
           content,
           deadline_at: deadlineAt,
           is_completed: isCompleted,
           assignees: assigneeIds?.map((id) => ({
             referenced_actor_type: "workspace-member",
             referenced_actor_id: id,
           })) || [],
           linked_records: utils.getFieldsProps({
             numberOfFields: numberOfLinkedRecords,
             fieldName: "linked record",
             propsMapper: linkedRecordPropsMapper,
           }),
         },
       },
     });

     $.export("$summary", `Successfully created task with ID \`${response.data.id.task_id}\`.`);
     return response;
+  } catch (error) {
+    $.export("error", error);
+    throw `Failed to create task: ${error.message}`;
+  }
 },
components/attio/actions/create-update-record/create-update-record.mjs (2)

55-87: Remove commented-out code

The commented-out parseValues function and filtering logic should be removed to keep the codebase clean.

-    // parseValues(attributes, values) {
-    //   for (const [
-    //     key,
-    //     value,
-    //   ] of Object.entries(values)) {
-    //     const {
-    //       type, is_multiselect: isMultiselect,
-    //     } = attributes.find(({ id }) => id.attribute_id === key);
-    //     if (type === "checkbox") {
-    //       values[key] = isMultiselect
-    //         ? value.map((v) => !(v === "false" || v === "0"))
-    //         : !(value === "false" || value === "0");
-    //     }
-    //     if (type === "number" || type === "rating") {
-    //       values[key] = isMultiselect
-    //         ? value.map((v) => +v)
-    //         : +value;
-    //     }
-    //   }
-    //   return values;
-    // },
    async getRelevantAttributes() {
      const stream = utils.paginate({
        fn: this.attio.listAttributes,
        args: {
          objectId: this.objectId,
        },
      });
      const attributes = await utils.streamIterator(stream);
      return attributes.filter((a) => a.is_writable);
-      // return attributes.filter((a) =>
-      //   a.is_writable || (includeAttrId && a.id.attribute_id === this.attributeId));
    },

89-103: Remove debugging console.log statement

There's a console.log statement that should be removed before production.

  async run({ $ }) {
    const {
      attio,
-      // parseValues,
-      // getRelevantAttributes,
      objectId,
      matchingAttribute,
      matchingAttributeValue,
      ...values
    } = this;

-    // const attributes = await getRelevantAttributes();
-
-    console.log("values!!!", JSON.stringify(values, null, 2));
components/attio/attio.app.mjs (1)

105-122: Improve workspaceMember label handling

The workspaceMemberId options function could produce labels with extra spaces if firstName or lastName are undefined.

        }) => ({
          value,
-         label: `${firstName || ""} ${lastName || ""}`.trim(),
+         label: [firstName, lastName].filter(Boolean).join(" ") || `Member ${value}`,
        }),
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 14a9d5b and 8861d86.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (22)
  • components/attio/actions/create-note/create-note.mjs (3 hunks)
  • components/attio/actions/create-person/create-person.mjs (1 hunks)
  • components/attio/actions/create-task/create-task.mjs (1 hunks)
  • components/attio/actions/create-update-record/create-update-record.mjs (4 hunks)
  • components/attio/actions/delete-list-entry/delete-list-entry.mjs (2 hunks)
  • components/attio/actions/update-person/update-person.mjs (1 hunks)
  • components/attio/attio.app.mjs (3 hunks)
  • components/attio/common/constants.mjs (1 hunks)
  • components/attio/common/utils.mjs (2 hunks)
  • components/attio/package.json (1 hunks)
  • components/attio/sources/common/base.mjs (3 hunks)
  • components/attio/sources/list-entry-deleted-instant/list-entry-deleted-instant.mjs (2 hunks)
  • components/attio/sources/list-entry-updated-instant/list-entry-updated-instant.mjs (2 hunks)
  • components/attio/sources/new-activity-created-instant/new-activity-created-instant.mjs (1 hunks)
  • components/attio/sources/new-activity-created-instant/test-event.mjs (1 hunks)
  • components/attio/sources/new-list-entry-instant/new-list-entry-instant.mjs (2 hunks)
  • components/attio/sources/new-note-instant/new-note-instant.mjs (1 hunks)
  • components/attio/sources/new-object-attribute-instant/new-object-attribute-instant.mjs (1 hunks)
  • components/attio/sources/new-record-created-instant/new-record-created-instant.mjs (2 hunks)
  • components/attio/sources/note-updated-instant/note-updated-instant.mjs (1 hunks)
  • components/attio/sources/object-attribute-updated-instant/object-attribute-updated-instant.mjs (1 hunks)
  • components/attio/sources/record-updated-instant/record-updated-instant.mjs (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • components/attio/common/constants.mjs
🚧 Files skipped from review as they are similar to previous changes (17)
  • components/attio/package.json
  • components/attio/sources/object-attribute-updated-instant/object-attribute-updated-instant.mjs
  • components/attio/sources/new-record-created-instant/new-record-created-instant.mjs
  • components/attio/sources/new-note-instant/new-note-instant.mjs
  • components/attio/sources/new-list-entry-instant/new-list-entry-instant.mjs
  • components/attio/sources/new-object-attribute-instant/new-object-attribute-instant.mjs
  • components/attio/sources/list-entry-updated-instant/list-entry-updated-instant.mjs
  • components/attio/sources/list-entry-deleted-instant/list-entry-deleted-instant.mjs
  • components/attio/sources/note-updated-instant/note-updated-instant.mjs
  • components/attio/actions/update-person/update-person.mjs
  • components/attio/sources/common/base.mjs
  • components/attio/sources/new-activity-created-instant/new-activity-created-instant.mjs
  • components/attio/actions/create-note/create-note.mjs
  • components/attio/actions/delete-list-entry/delete-list-entry.mjs
  • components/attio/sources/new-activity-created-instant/test-event.mjs
  • components/attio/sources/record-updated-instant/record-updated-instant.mjs
  • components/attio/actions/create-person/create-person.mjs
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Publish TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (21)
components/attio/actions/create-task/create-task.mjs (4)

1-4: Well-structured imports

The imports are clear and well-organized, following a logical structure by importing the app module first, followed by constants and utilities.


5-44: Component definition and props look good

The action is well-defined with appropriate metadata and clear property definitions. The properties have descriptive labels and helpful descriptions. Good job implementing the dynamic linked records functionality with numberOfLinkedRecords and reloadProps: true.


104-109: LGTM - Simple and clean task creation method

The createTask method is well-implemented, using the app's POST method with the correct path and passing through any additional arguments.


111-122: Well-implemented dynamic props generation

The additionalProps method effectively leverages the utility functions to generate dynamic linked record properties based on the specified count. This is a clean implementation of dynamic property generation.

components/attio/common/utils.mjs (6)

1-2: Clean import of constants

Good job centralizing constants in a separate module and importing them here.


38-41: Nice utility for PascalCase conversion

The toPascalCase function is well-implemented using regex for string transformation, making it reusable across the codebase.


43-56: Well-structured metadata property generator

The getMetadataProp function effectively handles prefixes and labels with clean conditional expressions.


75-86: LGTM - Clean implementation of fields props generator

The getFieldsProps function provides a clean way to generate multiple field properties using Array mapping.


88-122: Async property generator is well-implemented

The getAdditionalProps function effectively handles async operations with proper await chaining. The reduction pattern ensures props are accumulated correctly as promises resolve.


124-131: LGTM - Clean exports

All utility functions are properly exported for reuse throughout the codebase.

components/attio/actions/create-update-record/create-update-record.mjs (4)

8-8: Significant version increment

The version has been updated from 0.0.2 to 0.0.21, which is an unusually large increment. Consider documenting major changes in a changelog or comment.


18-34: Improved record matching approach

Good replacement of attributeId with the more descriptive matchingAttribute and matchingAttributeValue props. This makes the record matching logic more explicit and easier to understand.


36-53: Clean prop generation with simpler attribute filtering

The additionalProps method has been simplified to use api_slug instead of attribute.id.attribute_id and to filter only on is_writable. This is a good improvement for maintainability.


104-120: Improved record upsert structure

The revised structure for upsertRecord correctly uses the matching attribute parameters and simplifies the values object construction.

One suggestion: the debug: true flag might be meant for development and should probably be removed in production.

    const response = await attio.upsertRecord({
      $,
-     debug: true,
      objectId,
      params: {
        matching_attribute: matchingAttribute,
      },
components/attio/attio.app.mjs (7)

1-2: Good centralization of constants

Importing constants from a dedicated module is a great practice for maintainability.


8-71: Comprehensive person-related property definitions

Excellent job creating detailed property definitions for person attributes. Each prop has clear labels, descriptions, and appropriate optional flags.


123-153: Fix offset calculations in remaining options functions

The same NaN issue exists in the entryId options function.

          params: {
            limit: constants.DEFAULT_LIMIT,
-           offset: page * constants.DEFAULT_LIMIT,
+           offset: (page ?? 0) * constants.DEFAULT_LIMIT,

170-193: Same NaN issue in attributeId options

The offset calculation in attributeId options should also handle undefined page.

          params: {
            limit: constants.DEFAULT_LIMIT,
-           offset: page * constants.DEFAULT_LIMIT,
+           offset: (page ?? 0) * constants.DEFAULT_LIMIT,

196-204: Clean helper methods for URL and headers

Good refactoring of URL and headers construction into dedicated helper methods for consistency and maintainability.


205-243: Well-structured HTTP request methods

The generic HTTP wrapper methods and API-specific methods are well-structured, promoting code reuse and consistency.


244-303: Good organization of API methods

The API methods are well-organized with consistent parameter destructuring and use of the generic HTTP wrappers.

Comment on lines +58 to +73
function getFieldProps({
index, fieldName, prefix,
propsMapper = function propsMapper(prefix) {
const { [`${prefix}name`]: name } = this;
return {
name,
};
},
} = {}) {
const { prefix: metaPrefix } = getMetadataProp({
index,
fieldName,
prefix,
});
return propsMapper(metaPrefix);
}
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

Fix context issue in default propsMapper

The default propsMapper function references this which may lead to unexpected behavior when not bound properly.

 function getFieldProps({
   index, fieldName, prefix,
-  propsMapper = function propsMapper(prefix) {
-    const { [`${prefix}name`]: name } = this;
-    return {
-      name,
-    };
-  },
+  propsMapper = (prefix, context) => {
+    const name = context?.[`${prefix}name`];
+    return {
+      name,
+    };
+  },
+  context,
 } = {}) {
   const { prefix: metaPrefix } = getMetadataProp({
     index,
     fieldName,
     prefix,
   });
-  return propsMapper(metaPrefix);
+  return propsMapper(metaPrefix, context);
 }

Also update the call in getFieldsProps to pass the context:

 function getFieldsProps({
-  numberOfFields, fieldName, propsMapper, prefix,
+  numberOfFields, fieldName, propsMapper, prefix, context,
 } = {}) {
   return Array.from({
     length: numberOfFields,
   }).map((_, index) => getFieldProps({
     index,
     fieldName,
     prefix,
     propsMapper,
+    context,
   }));
 }
📝 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
function getFieldProps({
index, fieldName, prefix,
propsMapper = function propsMapper(prefix) {
const { [`${prefix}name`]: name } = this;
return {
name,
};
},
} = {}) {
const { prefix: metaPrefix } = getMetadataProp({
index,
fieldName,
prefix,
});
return propsMapper(metaPrefix);
}
// components/attio/common/utils.mjs
function getFieldProps({
index,
fieldName,
prefix,
propsMapper = (prefix, context) => {
const name = context?.[`${prefix}name`];
return { name };
},
context,
} = {}) {
const { prefix: metaPrefix } = getMetadataProp({
index,
fieldName,
prefix,
});
return propsMapper(metaPrefix, context);
}
function getFieldsProps({
numberOfFields,
fieldName,
propsMapper,
prefix,
context,
} = {}) {
return Array.from({ length: numberOfFields }).map((_, index) =>
getFieldProps({
index,
fieldName,
prefix,
propsMapper,
context,
})
);
}

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] attio
2 participants