Skip to content

[components] Scrapeless - add new actions #17086

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

Conversation

joy-chanboop
Copy link

@joy-chanboop joy-chanboop commented Jun 12, 2025

WHY

  • Introduced a new README.md for Scrapeless, outlining its features and getting started guide.
  • Implemented multiple Scrapeless actions inclues Crawler, Scraping API, and Universal Scraping API.

Summary by CodeRabbit

  • New Features
    • Introduced modules for scalable web scraping and crawling, including a Universal Scraping API, Scraping API, and Crawler with support for structured data extraction and block avoidance.
    • Added options for targeting specific countries and customizing scraping parameters.
    • Enhanced job monitoring with automatic polling and data aggregation for large-scale crawls.
    • Added new QuickBooks actions for creating, updating, sending, and voiding invoices, estimates, purchase orders, and sales receipts, with flexible input options and validation.
    • Introduced Habitica actions to create, delete, and retrieve challenges, along with group management features.
  • Documentation
    • Added a comprehensive README introducing Scrapeless, its capabilities, use cases, and a quick start guide.
    • Updated Connect API rate limit documentation and reorganized component integration guides.
  • Chores
    • Updated API endpoints and authentication headers for improved connectivity.
    • Incremented version numbers for packages and key actions to reflect latest updates.
    • Added new dependencies and updated package metadata for Scrapeless, Habitica, QuickBooks, and Connect React components.
  • Utilities
    • Added utility functions for type checking, logging, and deep JSON parsing to support internal operations.
    • Added helper functions for building QuickBooks line items with validation.
    • Introduced user ID resolution logic to support new externalUserId parameter with backward compatibility.
  • Data
    • Added extensive country and region options for proxy and localization settings.

- Introduced a new README.md for Scrapeless, outlining its features and getting started guide.
- Implemented multiple Scrapeless actions inclues `Crawler`, `Scraping API`, and `Universal Scraping API`.
Copy link

vercel bot commented Jun 12, 2025

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

1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) Jun 21, 2025 3:29am

Copy link

vercel bot commented Jun 12, 2025

@joy-chanboop is attempting to deploy a commit to the Pipedreamers Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Jun 12, 2025

Important

Review skipped

More than 25% of the files skipped due to max files limit. The review is being skipped to prevent a low-quality review.

117 files out of 224 files are above the max files limit of 100. Please upgrade to Pro plan to get higher limits.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

This update adds comprehensive Scrapeless platform support, including new action modules for crawling, scraping via API, and universal scraping with dynamic input properties. It updates the core app with new API endpoints and methods, adds extensive country constants and utility functions, introduces a detailed README, and increments versions and package dependencies for stability and clarity.

Changes

File(s) Change Summary
components/scrapeless/README.md Added new README introducing Scrapeless, its features, use cases, and getting started instructions.
components/scrapeless/common/constants.mjs Added countryOptions export: array of country/region objects with metadata and flag emojis.
components/scrapeless/common/utils.mjs Added utility functions for type/value checks and a logging helper.
components/scrapeless/scrapeless.app.mjs Updated base URL and headers; added method to create Scrapeless client instance with API key.
components/scrapeless/actions/crawler/crawler.mjs Added "Crawler" action with crawl/scrape API server selection and dynamic property definitions.
components/scrapeless/actions/scraping-api/scraping-api.mjs Added "Scraping API" action for structured data retrieval (Google Search) with dynamic inputs and retry logic.
components/scrapeless/actions/universal-scraping-api/universal-scraping-api.mjs Added "Universal Scraping API" action for scalable web scraping with configurable options.
components/scrapeless/actions/get-scrape-result/get-scrape-result.mjs Updated version from 0.0.1 to 0.0.2.
components/scrapeless/actions/submit-scrape-job/submit-scrape-job.mjs Updated version from 0.0.1 to 0.0.2.
components/scrapeless/package.json Incremented package version from 0.1.0 to 0.2.0; added new dependency @scrapeless-ai/sdk ^1.4.0.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ActionModule
    participant ScrapelessApp
    participant API

    User->>ActionModule: Provide input (e.g., URL, query, options)
    ActionModule->>ScrapelessApp: Call appropriate method (scrapingApi/universalScrapingApi/crawlerCrawl/crawlerScrape)
    ScrapelessApp->>API: Send request (POST to endpoint)
    API-->>ScrapelessApp: Respond with data or job/task ID
    alt Immediate data
        ScrapelessApp-->>ActionModule: Return result
    else Job/task ID
        loop Poll until complete
            ScrapelessApp->>API: Check job/task status
            API-->>ScrapelessApp: Return status/data
        end
        ScrapelessApp-->>ActionModule: Return aggregated result
    end
    ActionModule-->>User: Output result/summary
Loading

Poem

🐇 In fields of code where data flows,
Scrapeless springs, and onward goes.
Crawlers leap, APIs sing,
Flags of countries fluttering.
Utilities sharp, docs so bright,
This update shines a carrot light! 🥕✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

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

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

CodeRabbit Commands (Invoked using PR comments)

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

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Documentation and Community

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

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

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

@pipedream-component-development
Copy link
Collaborator

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🧹 Nitpick comments (8)
components/scrapeless/common/constants.mjs (2)

67-73: Correct the typo in “Google FLights” labels

“FLights” is miss-cased. The canonical brand spelling is “Flights”.

-    label: "Google FLights",
+    label: "Google Flights",

and

-    label: "Google FLights Chart",
+    label: "Google Flights Chart",

Apply the same change in both entries to keep the UX professional and searchable.


139-1260: Consider externalising the 1 200-line countryOptions array

Embedding such a large literal bloats the bundle, hurts diff-reviewability and invites merge conflicts. Two lightweight alternatives:

  1. Move the JSON into countryOptions.json and import it.
  2. Host it on a small CDN / gist and fetch lazily; cache in memory.

Either preserves type-safety while restoring readability to this module.

components/scrapeless/actions/crawler/crawler.mjs (2)

13-24: Grammar: “an API server”, not “a API server”

-      label: "Please select a API server",
+      label: "Please select an API server",

60-80: Label mis-match for shared prop

label: "URL to Crawl" is misleading when apiServer === 'scrape'. Rename to neutral “URL”.

-        label: "URL to Crawl",
+        label: "URL",
components/scrapeless/actions/scraping-api/scraping-api.mjs (1)

13-21: Grammar tweak

-      label: "Please select a API server",
+      label: "Please select an API server",
components/scrapeless/README.md (1)

1-17: Add a quick code example for instant onboarding

Readers benefit from a minimal “Hello World” snippet showing how to call one of the new actions programmatically. Consider adding a short example under “Getting Started”.

components/scrapeless/actions/universal-scraping-api/universal-scraping-api.mjs (1)

13-21: Grammar + UX nit

Label should read “Please select an API server” (missing article) to keep UI professional.

components/scrapeless/scrapeless.app.mjs (1)

90-118: Input sanitisation & unused variable

crawlerCrawl() ignores extra fields in submitData (e.g., browserOptions) yet still spreads them in the POST body later. Consider validating/whitelisting expected keys to avoid accidental leakage of large objects.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6a4420e and c7161f2.

📒 Files selected for processing (7)
  • components/scrapeless/README.md (1 hunks)
  • components/scrapeless/actions/crawler/crawler.mjs (1 hunks)
  • components/scrapeless/actions/scraping-api/scraping-api.mjs (1 hunks)
  • components/scrapeless/actions/universal-scraping-api/universal-scraping-api.mjs (1 hunks)
  • components/scrapeless/common/constants.mjs (1 hunks)
  • components/scrapeless/common/utils.mjs (1 hunks)
  • components/scrapeless/scrapeless.app.mjs (2 hunks)

@joy-chanboop
Copy link
Author

Hi, could you please review and approve the GitHub Actions workflows for this PR when you have a moment? Thank you!

- fix lint code error
- fix Component check error
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 (3)
components/scrapeless/scrapeless.app.mjs (3)

58-73: ⚠️ Potential issue

No timeout / max-attempts – risk of endless polling

while (true) continuously polls every second with no exit strategy. If the API never returns a result, the step hangs forever and burns credits.

-        while (true) {
-          await new Promise((resolve) => setTimeout(resolve, 1000));
+        const MAX_ATTEMPTS = 120; // 2 min @ 1 s
+        let attempts = 0;
+        while (attempts++ < MAX_ATTEMPTS) {
+          await new Promise((resolve) => setTimeout(resolve, 1000));
           const result = await this.getScrapeResult({
             scrapeJobId: res.taskId,
           });
@@
             return result;
           }
         }
+        throw new Error(`Timed out after ${MAX_ATTEMPTS}s waiting for scrape result`);

138-145: 🛠️ Refactor suggestion

Pagination loop clobbers statusResponse and misuses next URL

Re-assigning statusResponse inside the pagination loop overwrites the object you still rely on, and passing statusResponse.next to _makeRequest({ path }) prepends baseUrl even when next is an absolute URL.

-              let data = statusResponse.data;
-              while (typeof statusResponse === "object" && "next" in statusResponse) {
+              let data  = statusResponse.data;
+              let page  = statusResponse;
+              while (page && typeof page === "object" && page.next) {
                 if (data.length === 0) break;
-                statusResponse = await this._makeRequest({
-                  method: "GET",
-                  path: statusResponse.next,
+                const isAbsolute = /^https?:\/\//.test(page.next);
+                page = await this._makeRequest({
+                  method: "GET",
+                  ...(isAbsolute ? { url: page.next } : { path: page.next }),
                 });
-                data = data.concat(statusResponse.data);
+                data = data.concat(page.data ?? []);
               }
-              statusResponse.data = data;
-              return statusResponse;
+              statusResponse.data = data;
+              return statusResponse;

196-207: ⚠️ Potential issue

Endless polling in crawlerScrape

Same issue as above: the while (true) loop has no termination condition. Add a max-attempt / timeout or propagate the promise returned by monitorJobStatus to reuse that logic.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between c7161f2 and f8de313.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • components/scrapeless/actions/crawler/crawler.mjs (1 hunks)
  • components/scrapeless/actions/get-scrape-result/get-scrape-result.mjs (1 hunks)
  • components/scrapeless/actions/scraping-api/scraping-api.mjs (1 hunks)
  • components/scrapeless/actions/submit-scrape-job/submit-scrape-job.mjs (1 hunks)
  • components/scrapeless/actions/universal-scraping-api/universal-scraping-api.mjs (1 hunks)
  • components/scrapeless/common/constants.mjs (1 hunks)
  • components/scrapeless/common/utils.mjs (1 hunks)
  • components/scrapeless/package.json (1 hunks)
  • components/scrapeless/scrapeless.app.mjs (2 hunks)
✅ Files skipped from review due to trivial changes (3)
  • components/scrapeless/package.json
  • components/scrapeless/actions/submit-scrape-job/submit-scrape-job.mjs
  • components/scrapeless/actions/get-scrape-result/get-scrape-result.mjs
🚧 Files skipped from review as they are similar to previous changes (5)
  • components/scrapeless/common/constants.mjs
  • components/scrapeless/actions/crawler/crawler.mjs
  • components/scrapeless/actions/scraping-api/scraping-api.mjs
  • components/scrapeless/actions/universal-scraping-api/universal-scraping-api.mjs
  • components/scrapeless/common/utils.mjs
🔇 Additional comments (1)
components/scrapeless/scrapeless.app.mjs (1)

183-188: Duplicate POST removed – 👍

Only a single POST is sent to start the scrape job now, preventing unintentional duplicate jobs.
No further issues spotted here.

@joy-chanboop
Copy link
Author

Hi team, I've fixed all the issues pointed out in the review. The component is now ready for re-review. Any chance we could move this forward? Thank you!

@joy-chanboop
Copy link
Author

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

Hi team, I've fixed all the issues pointed out in the review. The component is now ready for re-review. Any chance we could move this forward? Thank you!

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 @joy-chanboop thanks for this great contribution. I noticed there is a lot of polling request strategy to get the status of the service until it is done. Do you think you can make a refactor so it can look similar to this approach in contactout/actions/verify-email-bulk/verify-email-bulk.mjs I'm making this suggestion because we need to support $.flow.rerun step feature as much as we can.

@jcortes jcortes moved this from Ready for PR Review to Changes Required in Component (Source and Action) Backlog Jun 17, 2025
- use nodes integration server to simple request logic
@joy-chanboop joy-chanboop requested a review from jcortes June 18, 2025 03:08
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 @joy-chanboop Just wondering why you are changing the base url of the api! just in case the official docs are here https://apidocs.scrapeless.com/

@joy-chanboop
Copy link
Author

Hi team, I've fixed all the issues pointed out in the review. The component is now ready for re-review. Any chance we could move this forward? Thank you!

@joy-chanboop joy-chanboop requested a review from jcortes June 20, 2025 07:38
@jcortes
Copy link
Collaborator

jcortes commented Jun 20, 2025

Hi @joy-chanboop let me know if this is what you are looking for. If not it's fine you can work also win Pipedream axios lib!

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 @joy-chanboop pending minor change in crawler component!

…nality

- Added @scrapeless-ai/sdk as a dependency.
- Updated API endpoints for scraping and crawling functionalities.
- Implemented error handling and job management for scraping tasks.
- Refactored existing methods to utilize the new SDK for improved performance and reliability.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
components/scrapeless/actions/scraping-api/scraping-api.mjs (1)

114-119: Reuse the existing countryOptions for gl

gl should be a dropdown fed by countryOptions, ensuring valid ISO country codes and consistent UX.

Import countryOptions at the top of the file:

 import scrapeless from "../../scrapeless.app.mjs";
 import { log } from "../../common/utils.mjs";
+import { countryOptions } from "../../common/constants.mjs";

Then update the gl prop definition:

       props.gl = {
         type: "string",
         label: "Country",
         description: "Parameter defines the country to use for the Google search. It's a two-letter country code. (e.g., us for the United States, uk for United Kingdom, or fr for France).",
         default: "us",
+        options: countryOptions,
       };
🧹 Nitpick comments (1)
components/scrapeless/actions/scraping-api/scraping-api.mjs (1)

79-79: Simplify the conditional check using optional chaining.

The current condition job && job?.data?.taskId is redundant since optional chaining already handles null/undefined checks.

-    } else if (job && job?.data?.taskId) {
+    } else if (job?.data?.taskId) {
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5d82077 and fa1e7c7.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • components/scrapeless/actions/crawler/crawler.mjs (1 hunks)
  • components/scrapeless/actions/scraping-api/scraping-api.mjs (1 hunks)
  • components/scrapeless/actions/universal-scraping-api/universal-scraping-api.mjs (1 hunks)
  • components/scrapeless/package.json (2 hunks)
  • components/scrapeless/scrapeless.app.mjs (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • components/scrapeless/package.json
  • components/scrapeless/scrapeless.app.mjs
  • components/scrapeless/actions/crawler/crawler.mjs
  • components/scrapeless/actions/universal-scraping-api/universal-scraping-api.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/scrapeless/actions/scraping-api/scraping-api.mjs

[error] 79-79: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

@joy-chanboop
Copy link
Author

Hi @joy-chanboop pending minor change in crawler component!

Hi Jorge Cortes,

I’ve refactored the action calls using the scrapeless-sdk, and set the official API endpoint as the base URL. I’ve also updated the crawler action key.

Let me know if you have any suggestions or feedback!

@joy-chanboop joy-chanboop requested a review from jcortes June 21, 2025 01:58
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 @joy-chanboop lgtm! Ready for QA! Can you please rebase with master so we don't have conflicts in pnpm-lock.yaml

@jcortes jcortes moved this from Changes Required to Ready for QA in Component (Source and Action) Backlog Jun 21, 2025
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: 11

🧹 Nitpick comments (9)
components/habitica/habitica.app.mjs (1)

101-101: Consider making the client ID configurable.

The hardcoded client ID might need to be configurable or moved to constants for maintainability.

-"x-client": "3a326108-1895-4c23-874e-37668c75f2ad-Pipedream",
+"x-client": `${constants.CLIENT_ID}`,

Then add to constants.mjs:

export default {
  CLIENT_ID: "3a326108-1895-4c23-874e-37668c75f2ad-Pipedream",
  // ... other constants
};
components/leaddyno/leaddyno.app.mjs (2)

4-4: Empty propDefinitions suggests incomplete implementation.

The propDefinitions object is empty, which typically indicates an incomplete component. LeadDyno components would likely need authentication and configuration properties defined.

Would you like me to help generate common propDefinitions for API-based integrations like API keys, base URLs, etc.?


7-9: Replace console.log with appropriate logging.

Using console.log in production code is not recommended. Consider using Pipedream's logging mechanisms or remove this debug code if it's not needed.

-  authKeys() {
-    console.log(Object.keys(this.$auth));
-  },
+  authKeys() {
+    // Return auth keys for debugging/validation purposes
+    return Object.keys(this.$auth);
+  },
components/quickbooks/actions/send-invoice/send-invoice.mjs (1)

37-37: Inconsistent export summary key.

This action uses $.export("$summary", ...) while the void-invoice action uses $.export("summary", ...). Consider standardizing the export key across all actions for consistency.

-$.export("$summary", `Successfully sent invoice with ID ${this.invoiceId}`);
+$.export("summary", `Successfully sent invoice with ID ${this.invoiceId}`);
components/quickbooks/actions/send-estimate/send-estimate.mjs (1)

37-37: Inconsistent export summary key.

This action uses $.export("$summary", ...) while the void-invoice action uses $.export("summary", ...). Consider standardizing the export key across all actions for consistency.

-$.export("$summary", `Successfully sent estimate with ID ${this.estimateId}`);
+$.export("summary", `Successfully sent estimate with ID ${this.estimateId}`);
components/quickbooks/actions/create-estimate/create-estimate.mjs (1)

239-242: Consider validating date formats and the logical relationship between acceptance fields.

The ExpirationDate and AcceptedDate fields are expected in YYYY-MM-DD format but are not validated. Additionally, AcceptedBy and AcceptedDate appear logically related - consider validating that both are provided together if one is specified.

+    // Validate date formats
+    const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
+    if (this.expirationDate && !dateRegex.test(this.expirationDate)) {
+      throw new ConfigurationError("Expiration Date must be in YYYY-MM-DD format");
+    }
+    if (this.acceptedDate && !dateRegex.test(this.acceptedDate)) {
+      throw new ConfigurationError("Accepted Date must be in YYYY-MM-DD format");
+    }
+    
+    // Validate logical relationship
+    if ((this.acceptedBy && !this.acceptedDate) || (!this.acceptedBy && this.acceptedDate)) {
+      throw new ConfigurationError("Both Accepted By and Accepted Date must be provided together");
+    }
+
     const data = {
       Line: lines,
       CustomerRef: {
         value: this.customerRefValue,
       },
       ExpirationDate: this.expirationDate,
       AcceptedBy: this.acceptedBy,
       AcceptedDate: this.acceptedDate,
components/quickbooks/actions/create-purchase-order/create-purchase-order.mjs (1)

201-201: Validate date format for Due Date.

The dueDate field expects YYYY-MM-DD format but is not validated.

+    // Validate date format
+    if (this.dueDate && !/^\d{4}-\d{2}-\d{2}$/.test(this.dueDate)) {
+      throw new ConfigurationError("Due Date must be in YYYY-MM-DD format");
+    }

     const data = {
       Line: lines,
       VendorRef: {
         value: this.vendorRefValue,
       },
       DueDate: this.dueDate,
components/quickbooks/actions/update-estimate/update-estimate.mjs (1)

34-38: Validate date formats before API call.

Similar to create-estimate, the date fields should be validated for correct format.

Add date validation before building the data object:

// Validate date formats
const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
if (this.expirationDate && !dateRegex.test(this.expirationDate)) {
  throw new ConfigurationError("Expiration Date must be in YYYY-MM-DD format");
}
if (this.acceptedDate && !dateRegex.test(this.acceptedDate)) {
  throw new ConfigurationError("Accepted Date must be in YYYY-MM-DD format");
}

Also applies to: 48-50, 296-303

components/quickbooks/actions/update-invoice/update-invoice.mjs (1)

34-38: Add date format validation for consistency.

The dueDate field should be validated for YYYY-MM-DD format before sending to the API.

Add validation before building the data object:

// Validate date format
if (this.dueDate && !/^\d{4}-\d{2}-\d{2}$/.test(this.dueDate)) {
  throw new ConfigurationError("Due Date must be in YYYY-MM-DD format");
}

Also applies to: 318-318

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between fa1e7c7 and 10ad989.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (84)
  • components/alttextify/alttextify.app.mjs (1 hunks)
  • components/amazon_redshift/amazon_redshift.app.mjs (1 hunks)
  • components/capgo/capgo.app.mjs (1 hunks)
  • components/codeberg/codeberg.app.mjs (1 hunks)
  • components/codeqr/codeqr.app.mjs (1 hunks)
  • components/deepsouce/deepsouce.app.mjs (1 hunks)
  • components/elmah_io/elmah_io.app.mjs (2 hunks)
  • components/elmah_io/package.json (2 hunks)
  • components/elmah_io/sources/new-error/new-error.mjs (4 hunks)
  • components/finalscout/finalscout.app.mjs (1 hunks)
  • components/habitica/.gitignore (0 hunks)
  • components/habitica/actions/create-challenge/create-challenge.mjs (1 hunks)
  • components/habitica/actions/delete-challenge/delete-challenge.mjs (1 hunks)
  • components/habitica/actions/get-challenge/get-challenge.mjs (1 hunks)
  • components/habitica/common/constants.mjs (1 hunks)
  • components/habitica/habitica.app.mjs (1 hunks)
  • components/habitica/package.json (1 hunks)
  • components/keboola/keboola.app.mjs (1 hunks)
  • components/leaddyno/leaddyno.app.mjs (1 hunks)
  • components/leaddyno/package.json (1 hunks)
  • components/picqer/picqer.app.mjs (1 hunks)
  • components/predictleads/predictleads.app.mjs (1 hunks)
  • components/quickbooks/actions/create-ap-aging-report/create-ap-aging-report.mjs (1 hunks)
  • components/quickbooks/actions/create-bill/create-bill.mjs (2 hunks)
  • components/quickbooks/actions/create-customer/create-customer.mjs (1 hunks)
  • components/quickbooks/actions/create-estimate/create-estimate.mjs (1 hunks)
  • components/quickbooks/actions/create-invoice/create-invoice.mjs (2 hunks)
  • components/quickbooks/actions/create-payment/create-payment.mjs (1 hunks)
  • components/quickbooks/actions/create-pl-report/create-pl-report.mjs (1 hunks)
  • components/quickbooks/actions/create-purchase-order/create-purchase-order.mjs (1 hunks)
  • components/quickbooks/actions/create-purchase/create-purchase.mjs (2 hunks)
  • components/quickbooks/actions/create-sales-receipt/create-sales-receipt.mjs (2 hunks)
  • components/quickbooks/actions/delete-purchase/delete-purchase.mjs (1 hunks)
  • components/quickbooks/actions/get-bill/get-bill.mjs (1 hunks)
  • components/quickbooks/actions/get-customer/get-customer.mjs (1 hunks)
  • components/quickbooks/actions/get-invoice/get-invoice.mjs (1 hunks)
  • components/quickbooks/actions/get-my-company/get-my-company.mjs (1 hunks)
  • components/quickbooks/actions/get-payment/get-payment.mjs (1 hunks)
  • components/quickbooks/actions/get-purchase-order/get-purchase-order.mjs (1 hunks)
  • components/quickbooks/actions/get-purchase/get-purchase.mjs (1 hunks)
  • components/quickbooks/actions/get-sales-receipt/get-sales-receipt.mjs (1 hunks)
  • components/quickbooks/actions/get-time-activity/get-time-activity.mjs (1 hunks)
  • components/quickbooks/actions/search-accounts/search-accounts.mjs (1 hunks)
  • components/quickbooks/actions/search-customers/search-customers.mjs (1 hunks)
  • components/quickbooks/actions/search-invoices/search-invoices.mjs (1 hunks)
  • components/quickbooks/actions/search-items/search-items.mjs (1 hunks)
  • components/quickbooks/actions/search-products/search-products.mjs (1 hunks)
  • components/quickbooks/actions/search-purchases/search-purchases.mjs (1 hunks)
  • components/quickbooks/actions/search-query/search-query.mjs (1 hunks)
  • components/quickbooks/actions/search-services/search-services.mjs (1 hunks)
  • components/quickbooks/actions/search-time-activities/search-time-activities.mjs (1 hunks)
  • components/quickbooks/actions/search-vendors/search-vendors.mjs (1 hunks)
  • components/quickbooks/actions/send-estimate/send-estimate.mjs (1 hunks)
  • components/quickbooks/actions/send-invoice/send-invoice.mjs (1 hunks)
  • components/quickbooks/actions/sparse-update-invoice/sparse-update-invoice.mjs (1 hunks)
  • components/quickbooks/actions/update-customer/update-customer.mjs (1 hunks)
  • components/quickbooks/actions/update-estimate/update-estimate.mjs (1 hunks)
  • components/quickbooks/actions/update-invoice/update-invoice.mjs (1 hunks)
  • components/quickbooks/actions/update-item/update-item.mjs (1 hunks)
  • components/quickbooks/actions/void-invoice/void-invoice.mjs (1 hunks)
  • components/quickbooks/common/utils.mjs (2 hunks)
  • components/quickbooks/package.json (1 hunks)
  • components/quickbooks/quickbooks.app.mjs (4 hunks)
  • components/quickbooks/sources/new-customer-created/new-customer-created.mjs (1 hunks)
  • components/quickbooks/sources/new-customer-updated/new-customer-updated.mjs (1 hunks)
  • components/quickbooks/sources/new-employee-created/new-employee-created.mjs (1 hunks)
  • components/quickbooks/sources/new-employee-updated/new-employee-updated.mjs (1 hunks)
  • components/quickbooks/sources/new-invoice-created/new-invoice-created.mjs (1 hunks)
  • components/quickbooks/sources/new-invoice-updated/new-invoice-updated.mjs (1 hunks)
  • components/quickbooks/sources/new-item-created/new-item-created.mjs (1 hunks)
  • components/quickbooks/sources/new-item-updated/new-item-updated.mjs (1 hunks)
  • components/v1ce/v1ce.app.mjs (1 hunks)
  • components/yay_com/yay_com.app.mjs (1 hunks)
  • docs-v2/.tool-versions (1 hunks)
  • docs-v2/pages/connect/api.mdx (1 hunks)
  • docs-v2/pages/connect/components/index.mdx (1 hunks)
  • docs-v2/pages/connect/mcp/openai.mdx (1 hunks)
  • packages/connect-react/CHANGELOG.md (1 hunks)
  • packages/connect-react/package.json (1 hunks)
  • packages/connect-react/src/components/ComponentForm.tsx (2 hunks)
  • packages/connect-react/src/components/ControlApp.tsx (3 hunks)
  • packages/connect-react/src/components/RemoteOptionsContainer.tsx (2 hunks)
  • packages/connect-react/src/hooks/form-context.tsx (6 hunks)
  • packages/connect-react/src/utils/resolve-user-id.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • components/habitica/.gitignore
✅ Files skipped from review due to trivial changes (61)
  • components/keboola/keboola.app.mjs
  • components/amazon_redshift/amazon_redshift.app.mjs
  • components/finalscout/finalscout.app.mjs
  • components/picqer/picqer.app.mjs
  • components/codeqr/codeqr.app.mjs
  • components/codeberg/codeberg.app.mjs
  • components/deepsouce/deepsouce.app.mjs
  • components/v1ce/v1ce.app.mjs
  • components/capgo/capgo.app.mjs
  • components/alttextify/alttextify.app.mjs
  • components/predictleads/predictleads.app.mjs
  • components/yay_com/yay_com.app.mjs
  • docs-v2/.tool-versions
  • components/quickbooks/actions/delete-purchase/delete-purchase.mjs
  • components/quickbooks/actions/get-time-activity/get-time-activity.mjs
  • components/quickbooks/actions/create-payment/create-payment.mjs
  • components/quickbooks/sources/new-invoice-updated/new-invoice-updated.mjs
  • components/elmah_io/package.json
  • components/quickbooks/actions/get-payment/get-payment.mjs
  • components/quickbooks/actions/create-purchase/create-purchase.mjs
  • components/habitica/common/constants.mjs
  • components/quickbooks/actions/get-purchase-order/get-purchase-order.mjs
  • components/quickbooks/actions/search-query/search-query.mjs
  • components/quickbooks/actions/create-pl-report/create-pl-report.mjs
  • components/quickbooks/actions/get-purchase/get-purchase.mjs
  • components/quickbooks/package.json
  • components/leaddyno/package.json
  • components/quickbooks/actions/create-invoice/create-invoice.mjs
  • components/quickbooks/actions/search-customers/search-customers.mjs
  • components/quickbooks/actions/create-sales-receipt/create-sales-receipt.mjs
  • components/quickbooks/sources/new-item-updated/new-item-updated.mjs
  • components/quickbooks/actions/get-bill/get-bill.mjs
  • components/quickbooks/actions/get-customer/get-customer.mjs
  • components/quickbooks/actions/search-invoices/search-invoices.mjs
  • components/quickbooks/actions/search-accounts/search-accounts.mjs
  • packages/connect-react/package.json
  • components/quickbooks/actions/search-items/search-items.mjs
  • components/quickbooks/actions/get-invoice/get-invoice.mjs
  • components/quickbooks/actions/get-sales-receipt/get-sales-receipt.mjs
  • components/quickbooks/sources/new-customer-updated/new-customer-updated.mjs
  • components/quickbooks/actions/update-item/update-item.mjs
  • components/quickbooks/actions/get-my-company/get-my-company.mjs
  • components/quickbooks/actions/create-ap-aging-report/create-ap-aging-report.mjs
  • components/quickbooks/actions/create-bill/create-bill.mjs
  • components/quickbooks/sources/new-invoice-created/new-invoice-created.mjs
  • packages/connect-react/src/components/RemoteOptionsContainer.tsx
  • components/quickbooks/sources/new-customer-created/new-customer-created.mjs
  • components/quickbooks/actions/search-vendors/search-vendors.mjs
  • components/quickbooks/actions/search-time-activities/search-time-activities.mjs
  • components/quickbooks/actions/search-purchases/search-purchases.mjs
  • components/quickbooks/sources/new-item-created/new-item-created.mjs
  • components/quickbooks/actions/create-customer/create-customer.mjs
  • components/quickbooks/actions/sparse-update-invoice/sparse-update-invoice.mjs
  • components/quickbooks/sources/new-employee-created/new-employee-created.mjs
  • packages/connect-react/CHANGELOG.md
  • components/quickbooks/actions/search-services/search-services.mjs
  • components/quickbooks/sources/new-employee-updated/new-employee-updated.mjs
  • components/habitica/actions/get-challenge/get-challenge.mjs
  • components/quickbooks/actions/search-products/search-products.mjs
  • components/quickbooks/actions/update-customer/update-customer.mjs
  • docs-v2/pages/connect/api.mdx
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/connect-react/src/components/ControlApp.tsx (1)
packages/connect-react/src/hooks/form-context.tsx (1)
  • useFormContext (58-67)
🔇 Additional comments (46)
docs-v2/pages/connect/mcp/openai.mdx (2)

156-156: Update cURL endpoint to /v1/responses
The request now targets the new /v1/responses endpoint, aligning with the Node.js client.responses.create example.


161-161: Switch to the input property in the payload
Replacing the "messages" array with a single "input" string correctly matches the /v1/responses API specification.

packages/connect-react/src/components/ControlApp.tsx (1)

5-5: LGTM! Clean migration from userId to externalUserId.

The changes correctly implement the user ID migration by:

  • Importing and using useFormContext to access externalUserId
  • Passing the resolved externalUserId to the useAccounts hook

The implementation maintains the same functionality while adopting the new user identifier pattern.

Also applies to: 32-32, 78-78

docs-v2/pages/connect/components/index.mdx (2)

19-19: Good terminology improvement.

Changing "public demo app" to "SDK playground" is more descriptive and accurate.


22-29: Excellent documentation reorganization.

The restructured implementation section improves logical flow by:

  • First introducing server SDK/REST API options
  • Then covering the frontend React SDK as a separate subsection
  • Providing clearer section headers and descriptions

This makes it easier for developers to understand their integration options.

packages/connect-react/src/utils/resolve-user-id.ts (1)

1-29: Excellent utility function for user ID migration.

The implementation correctly handles all migration scenarios:

  • ✅ Prioritizes externalUserId when both are provided
  • ✅ Falls back to deprecated userId with appropriate warning
  • ✅ Handles empty string edge cases
  • ✅ Provides clear error when neither ID is supplied
  • ✅ Well-documented with JSDoc
  • ✅ Type-safe return with warning information

This is a solid foundation for the user ID migration across the package.

packages/connect-react/src/components/ComponentForm.tsx (2)

13-20: Good deprecation documentation.

The JSDoc clearly marks userId as deprecated and directs users to use externalUserId instead.


32-35: Excellent use of TypeScript union types for migration.

The union type correctly enforces that exactly one of externalUserId or userId must be provided, preventing invalid combinations while maintaining backward compatibility. The never type effectively prevents simultaneous usage.

packages/connect-react/src/hooks/form-context.tsx (3)

43-45: Good type structure for migration.

Adding externalUserId while marking userId as deprecated with proper JSDoc documentation helps guide users through the migration.


87-105: Excellent deprecation warning implementation.

The warning logic is well-designed:

  • ✅ Uses useMemo for efficient ID resolution
  • ✅ Handles warnings in useEffect to avoid render side effects
  • ✅ Only shows warnings in development mode
  • ✅ Provides clear, actionable warning messages
  • ✅ Distinguishes between "both provided" and "deprecated usage" scenarios

This approach provides excellent developer experience during migration.


577-578: Smart backward compatibility approach.

Exposing userId in the context set to the resolved external user ID maintains backward compatibility while ensuring all internal logic uses the new identifier. This allows existing consumers to continue working without breaking changes.

components/habitica/package.json (3)

3-3: LGTM: Appropriate version bump for new functionality.

The minor version increment from 0.0.2 to 0.1.0 is appropriate for adding new challenge management actions.


5-5: LGTM: Main entry point updated correctly.

The main entry point change from the dist directory to direct source file aligns with the new file structure.


15-16: LGTM: Required dependency added.

The @pipedream/platform dependency is necessary for the axios import used in the app module.

components/habitica/actions/create-challenge/create-challenge.mjs (4)

1-1: LGTM: Correct import path.

The relative import path correctly references the app module.


4-8: LGTM: Standard Pipedream action structure.

The action follows the correct structure with appropriate key naming convention, descriptive name, and proper version.


17-25: LGTM: Dynamic prop definition with context.

The group prop correctly uses dynamic options based on the type selection, enhancing user experience.


63-78: LGTM: Proper async implementation with error handling.

The run method correctly passes the $ parameter for error handling and provides meaningful feedback via export summary.

components/habitica/actions/delete-challenge/delete-challenge.mjs (3)

1-1: LGTM: Correct import path.

The relative import path correctly references the app module.


4-8: LGTM: Standard Pipedream action structure.

The action follows the correct structure with appropriate key naming convention and descriptive documentation link.


18-25: LGTM: Proper delete implementation with feedback.

The delete operation correctly passes the challengeId and provides meaningful success feedback.

components/habitica/habitica.app.mjs (3)

1-2: LGTM: Correct imports.

The imports correctly reference the Pipedream platform and local constants.


89-105: LGTM: Well-structured HTTP client method.

The _makeRequest method properly handles authentication headers and provides a clean interface for API calls.


107-147: LGTM: Comprehensive CRUD methods.

All the API methods follow consistent patterns and properly destructure parameters. The REST endpoints are correctly structured.

components/elmah_io/elmah_io.app.mjs (3)

1-3: LGTM: Appropriate import addition for error handling.

Adding ConfigurationError to the imports supports the enhanced error handling implemented in the _makeRequest method.


11-11: LGTM: Helpful clarification of permission requirements.

Adding the specific permission requirement (logs_read) improves user experience by clearly communicating API key requirements.


16-19: LGTM: Robust defensive programming with optional chaining.

The optional chaining with fallback to an empty array prevents runtime errors when getLogs() returns undefined or null.

components/elmah_io/sources/new-error/new-error.mjs (8)

5-5: LGTM: Simplified query for better component control.

Removing the "isNew:true" condition allows the component to manage deduplication internally through its state tracking mechanism.


9-9: LGTM: Standard version increment.

Version bump from 0.0.3 to 0.0.4 follows standard practices for component updates.


11-11: LGTM: Improved documentation with API reference.

Adding the documentation link enhances developer experience by providing direct access to API specifications.


29-33: LGTM: Excellent UX improvement with permission alert.

The informational alert clearly communicates required API key permissions, preventing configuration issues and user confusion.


39-39: LGTM: Minor clarity improvement.

The summary text provides clearer event identification.


47-47: LGTM: Explicit return for clarity.

Adding an explicit return statement improves code readability and makes the intent clear.


60-62: LGTM: Proper defensive programming with early returns.

The early returns when no messages are found prevent unnecessary processing and potential errors with empty arrays.

Also applies to: 85-87


65-65: LGTM: Improved state management and efficiency.

The changes enhance the component by:

  • Storing lastEventDatetime in a variable for efficiency
  • Setting the last event datetime after processing to ensure state consistency
  • Using proper conditional logic for the from parameter

Also applies to: 70-70, 79-81, 90-90

components/leaddyno/leaddyno.app.mjs (3)

1-11: Verify PR context - file doesn't match PR objectives.

This file appears to be for LeadDyno component, but the PR objectives indicate this should be about "[components] Scrapeless - add new actions." Please confirm this is the correct file for review.

Likely an incorrect or invalid review comment.


3-3: ```shell
#!/bin/bash

Search for any remaining references to "habitica" across .js, .mjs, and .json files

rg "habitica" -g ".js" -g ".mjs" -g "*.json" -A 2 -B 2


---

`1-1`: ```bash
#!/usr/bin/env bash
set -e

echo "🔍 Searching for 'defineApp' references in .js/.mjs files..."
grep -R -n --include \*.js --include \*.mjs "defineApp" . || echo "✅ No 'defineApp' occurrences found."

echo
echo "🔍 Searching for plain object default exports ('export default {') in .js/.mjs files..."
grep -R -n --include \*.js --include \*.mjs -E "export default[[:space:]]*\\{" . || echo "✅ No default object exports found."
components/quickbooks/common/utils.mjs (1)

17-46: LGTM! Well-implemented recursive parsing function.

The parseObject function correctly handles recursive parsing of nested structures including arrays and objects, with proper error handling for invalid JSON.

components/quickbooks/quickbooks.app.mjs (4)

12-12: LGTM! Good correction to the description.

The description fix improves clarity for the invoiceId property.


119-154: LGTM! Well-implemented property definitions.

The new estimateId and vendor property definitions follow the established patterns and include appropriate async options with proper mapping functions.


476-547: LGTM! Comprehensive address property definitions.

The billing and shipping address properties are well-structured and follow consistent naming conventions. All properties are appropriately marked as optional.


630-699: LGTM! Consistent method implementations.

The new methods follow the established pattern of using _makeRequest with appropriate paths and HTTP methods. The implementation is consistent with existing methods in the codebase.

components/quickbooks/actions/void-invoice/void-invoice.mjs (1)

1-55: LGTM! Well-implemented void invoice action.

The implementation correctly:

  • Retrieves the invoice first to get the required SyncToken
  • Handles errors appropriately for missing invoice or SyncToken
  • Follows the QuickBooks API requirements for voiding invoices
  • Provides clear success feedback

The logic flow and error handling are solid.

components/quickbooks/actions/update-estimate/update-estimate.mjs (1)

212-228: Well-designed helper method for conditional property addition.

The addIfDefined method effectively reduces code duplication and provides flexibility with transformation support.

components/quickbooks/actions/update-invoice/update-invoice.mjs (1)

236-256: Excellent validation and error handling for invoice retrieval.

This validation pattern with clear error messages is exemplary and should be adopted by other update actions in this codebase.

@joy-chanboop joy-chanboop force-pushed the feature/scrapeless-new-actions branch 3 times, most recently from 71cd30a to fa1e7c7 Compare June 21, 2025 03:35
@joy-chanboop
Copy link
Author

Hi @joy-chanboop lgtm! Ready for QA! Can you please rebase with master so we don't have conflicts in pnpm-lock.yaml

Hi Jorge Cortes,

I’ve rebased my branch onto the latest master, but it seems that my PR history has been polluted with unrelated commits from other contributors. I’m concerned this might make the PR harder to review or merge cleanly.

Would you mind helping with the merge directly on GitHub? I really appreciate your support—thank you!

Best regards

@joy-chanboop joy-chanboop requested a review from jcortes June 21, 2025 03:38
@vunguyenhung vunguyenhung moved this from Ready for QA to Changes Required in Component (Source and Action) Backlog Jun 23, 2025
@vunguyenhung
Copy link
Collaborator

Hello everyone, I have tested this PR and there're some test cases failed or needed improvement.

Please check the test report below for more information
https://vunguyenhung.notion.site/components-Scrapeless-add-new-actions-219bf548bb5e81ed84cad80ed5c71b57

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
User submitted Submitted by a user
Projects
Development

Successfully merging this pull request may close these issues.

5 participants