Skip to content

Conversation

@jcortes
Copy link
Collaborator

@jcortes jcortes commented Mar 7, 2025

WHY

Resolves #15769

Summary by CodeRabbit

  • New Features

    • Added actions to create Procore incidents, manpower logs, RFIs, submittals, and timesheets.
    • Introduced instant event sources for real-time notifications on budget snapshots, change order packages, commitment change orders, prime contracts, purchase orders, RFIs, submittals, and timecard entries.
    • New modules added for sandbox actions related to incidents, manpower logs, RFIs, submittals, and timesheets.
    • Enhanced utility functions for better data handling and resource management.
    • New constants and resource names for improved API integration.
    • Introduced new methods for webhook management and event emission.
  • Chores

    • Upgraded the integration version and dependencies.
    • Consolidated shared configuration and utilities while streamlining the app interface.

@jcortes jcortes added action New Action Request trigger / source New trigger / source request refactor Apply this label for component refactors (not net new components) labels Mar 7, 2025
@jcortes jcortes self-assigned this Mar 7, 2025
@vercel
Copy link

vercel bot commented Mar 7, 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 Mar 18, 2025 4:02pm
pipedream-docs ⬜️ Ignored (Inspect) Mar 18, 2025 4:02pm
pipedream-docs-redirect-do-not-edit ⬜️ Ignored (Inspect) Mar 18, 2025 4:02pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2025

Walkthrough

This update introduces several new modules to support Procore API integrations. New action modules enable incident, manpower log, RFI, submittal, and timesheet creation via dedicated API calls. Common modules for constants, resource names, and utilities have been added, along with a revamped Procore application interface. Multiple new "instant" event source modules and a webhook management module are provided, while legacy source modules are removed. Package dependencies and version numbers have also been updated.

Changes

File(s) Change Summary
components/procore/actions/create-incident/create-incident.mjs
components/procore/actions/create-manpower-log/create-manpower-log.mjs
components/procore/actions/create-rfi/create-rfi.mjs
components/procore/actions/create-submittal/create-submittal.mjs
components/procore/actions/create-timesheet/create-timesheet.mjs
Added new action modules for creating incidents, manpower logs, RFIs, submittals, and timesheets. Each module defines its own properties, methods (e.g., createIncident, createManpowerLog, etc.), and a run function that orchestrates API calls.
components/procore/common/constants.mjs
components/procore/common/resource-names.mjs
components/procore/common/utils.mjs
Introduced new common modules to define constants, resource names, and utility functions used across the application.
components/procore/package.json Updated package version from 0.0.4 to 0.1.0 and upgraded @pipedream/platform dependency from ^1.1.1 to ^3.0.3.
components/procore/procore.app.js
components/procore/procore.app.mjs
Removed the legacy procore.app.js and added a new procore.app.mjs module with updated properties, methods, and asynchronous functions for managing Procore resources.
components/procore/sources/common/webhook.mjs Introduced a new webhook management module with properties, hooks (activate, deactivate), and various methods to manage tokens, webhook IDs, and trigger logic.
components/procore/sources/new-budget-snapshot-event-instant/*
components/procore/sources/new-change-order-package-event-instant/*
components/procore/sources/new-commitment-change-order-event-instant/*
components/procore/sources/new-event-instant/*
components/procore/sources/new-prime-contract-event-instant/*
components/procore/sources/new-purchase-order-event-instant/*
components/procore/sources/new-rfi-event-instant/*
components/procore/sources/new-submittal-event-instant/*
components/procore/sources/new-timecard-entry-event-instant/*
Added multiple new “instant” event source modules for various Procore resources. Each provides methods to fetch resource details, process incoming events, generate metadata, and emit enriched events.
components/procore/sources/budget-snapshot/*
components/procore/sources/commitment-change-order/*
components/procore/sources/common.js
components/procore/sources/new-event/*
components/procore/sources/prime-contract-change-order/*
components/procore/sources/prime-contract/*
components/procore/sources/purchase-order/*
components/procore/sources/rfi/*
components/procore/sources/submittal/*
Removed legacy source modules that previously managed event emissions for various Procore resources.

Sequence Diagram(s)

sequenceDiagram
    participant U as User/System
    participant R as run() Method
    participant F as Create* Function
    participant API as Procore API

    U->>R: Invoke action with parameters
    R->>F: Call create* function with structured data
    F->>API: Send POST request to Procore endpoint
    API-->>F: Return response data (e.g., incident ID)
    F-->>R: Return API result
    R-->>U: Return summary message with resource ID
Loading
sequenceDiagram
    participant U as User/System
    participant W as Webhook Module (activate)
    participant DB as Database
    participant API as Procore API

    U->>W: Trigger webhook activation
    W->>W: Generate token and prepare webhook data
    W->>API: Request creation of webhook
    API-->>W: Return webhook ID
    W->>DB: Store webhook and trigger IDs
    W-->>U: Confirm activation details
Loading

Assessment against linked issues

Objective Addressed Explanation
Emit new event when a Request for Information (RFI) is added in Procore. (#[15769])
Emit new event when a submittal is created in Procore. (#[15769])
Emit new event when a new timecard entry is recorded in Procore. (#[15769])
Create a new incident report for a project. (#[15769])
Create a new submittal for project documentation and approvals. (#[15769])

Suggested labels

ai-assisted

Suggested reviewers

  • michelle0927

Poem

Oh, hop along in lines of code,
As features bloom along the road.
I nibble bugs and dance with glee,
New API calls set my spirit free.
A rabbit’s cheer in every node,
With every change, my heart’s bestowed.
(^_^) Hop on into the future!

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/procore/actions/create-submittal/create-submittal.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/procore/actions/create-incident/create-incident.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/procore/actions/create-manpower-log/create-manpower-log.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)

  • 32 others

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

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

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for 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: 12

🧹 Nitpick comments (24)
components/procore/common/utils.mjs (2)

1-7: Well-structured async iteration helper but documentation could be improved.

The iterate function provides a clean way to convert async iterables to arrays, but it lacks JSDoc documentation and error handling for non-iterable inputs.

Consider adding JSDoc documentation and basic error handling:

+/**
+ * Asynchronously iterates over the provided iterable and collects all items into an array
+ * @param {AsyncIterable} iterations - The async iterable to process
+ * @returns {Promise<Array>} Array containing all the iterated items
+ */
 async function iterate(iterations) {
+  if (!iterations || typeof iterations[Symbol.asyncIterator] !== 'function') {
+    throw new TypeError('Input must be an async iterable');
+  }
   const items = [];
   for await (const item of iterations) {
     items.push(item);
   }
   return items;
 }

9-12: Clean implementation of nested property accessor.

The getNestedProperty function elegantly handles property access using the optional chaining operator for safe traversal.

For improved maintainability, consider adding JSDoc documentation:

+/**
+ * Safely retrieves a nested property from an object using dot notation
+ * @param {Object} obj - The object to traverse
+ * @param {string} propertyString - Dot-notation string representing the path to the property
+ * @returns {*} The value at the specified path or undefined if not found
+ */
 function getNestedProperty(obj, propertyString) {
   const properties = propertyString.split(".");
   return properties.reduce((prev, curr) => prev?.[curr], obj);
 }
components/procore/common/resource-names.mjs (1)

3-123: Comprehensive resource mapping with potential for improved organization.

The resource name mapping is thorough and follows consistent naming conventions, but the large flat structure might become difficult to maintain as it grows.

Consider these improvements for better maintainability:

  1. Freeze the object to prevent accidental modifications:
-export default {
+export default Object.freeze({
   BUDGET_LINE_ITEMS: "Budget Line Items",
   // ...
-};
+});
  1. Consider grouping related resources into categories:
 export default Object.freeze({
-  BUDGET_LINE_ITEMS: "Budget Line Items",
-  BUDGET_MODIFICATIONS: "Budget Modifications",
-  // ...other budget items
+  BUDGET: {
+    LINE_ITEMS: "Budget Line Items",
+    MODIFICATIONS: "Budget Modifications",
+    // ...other budget items
+  },
   // ...
 });
components/procore/sources/new-event-instant/new-event-instant.mjs (1)

19-31: Add error handling to the metadata generation.

The generateMeta method builds event metadata from webhook payloads but lacks error handling for malformed inputs.

Add defensive programming to handle cases where body properties might be missing:

 generateMeta(body) {
+  if (!body) {
+    return {
+      id: "unknown",
+      summary: "New Event: unknown",
+      ts: Date.now(),
+    };
+  }
   return {
-    id: body.id,
-    summary: `New Event: ${body.event_type}`,
-    ts: new Date(body.timestamp).getTime(),
+    id: body.id || "unknown",
+    summary: `New Event: ${body.event_type || "unknown"}`,
+    ts: body.timestamp ? new Date(body.timestamp).getTime() : Date.now(),
   };
 },
components/procore/actions/create-rfi/create-rfi.mjs (3)

113-113: Fix typo in success message

There's a typo in "Successfully" (written as "Succesfully").

-    $.export("$summary", `Succesfully created RFI with ID \`${response.id}\`.`);
+    $.export("$summary", `Successfully created RFI with ID \`${response.id}\`.`);

74-81: Add error handling for API request

The createRfi method should include error handling to provide a better user experience when API requests fail.

    createRfi({
      projectId, ...args
    } = {}) {
-      return this.app.post({
-        path: `/projects/${projectId}/rfis`,
-        ...args,
-      });
+      try {
+        return this.app.post({
+          path: `/projects/${projectId}/rfis`,
+          ...args,
+        });
+      } catch (error) {
+        console.error(`Error creating RFI: ${error.message}`);
+        throw error;
+      }
    },

83-115: Validate required inputs before making API request

The run method should validate that required inputs are present before attempting to create an RFI. This will provide clearer error messages to users.

  async run({ $ }) {
    const {
      createRfi,
      companyId,
      projectId,
      subject,
      questionBody,
      rfiManagerId,
      reference,
      isPrivate,
      locationId,
    } = this;

+    // Validate required inputs
+    if (!subject) {
+      throw new Error("Subject is required");
+    }
+    if (!questionBody) {
+      throw new Error("Question body is required");
+    }
+    if (!rfiManagerId) {
+      throw new Error("RFI Manager is required");
+    }

    const response = await createRfi({
      $,
      companyId,
      projectId,
      data: {
        rfi: {
          subject,
          question: {
            body: questionBody,
          },
          rfi_manager_id: rfiManagerId,
          reference,
          private: isPrivate,
          location_id: locationId,
        },
      },
    });
    $.export("$summary", `Succesfully created RFI with ID \`${response.id}\`.`);
    return response;
  },
components/procore/common/constants.mjs (2)

4-9: Inconsistent naming convention in VERSION_PATH object

There's an inconsistency in the naming convention for version paths. While most keys use uppercase (V1_1, V2), the v1_3 key uses lowercase. This could lead to confusion when referencing these constants.

const VERSION_PATH = {
  DEFAULT: "/v1.0",
  V1_1: "/v1.1",
- v1_3: "/v1.3",
+ V1_3: "/v1.3",
  V2: "/v2.0",
};

1-58: Add JSDoc comments for better code documentation

The constants file lacks documentation comments that explain the purpose and usage of these constants. Adding JSDoc comments would improve the maintainability of this module.

Consider adding comments to explain the purpose of key constant groups, for example:

+/**
+ * Placeholder for API version in URL construction
+ */
const VERSION_PLACEHOLDER = "{version}";
+/**
+ * Base URL for Procore API requests
+ */
const BASE_URL = `https://api.procore.com/rest${VERSION_PLACEHOLDER}`;

+/**
+ * API version paths for different endpoints
+ */
const VERSION_PATH = {
  DEFAULT: "/v1.0",
  V1_1: "/v1.1",
  v1_3: "/v1.3",
  V2: "/v2.0",
};
components/procore/actions/create-submittal/create-submittal.mjs (2)

115-115: Fix typo in success message

There's a spelling error in the success message.

- $.export("$summary", `Succesfully created submittal with ID \`${response.id}\`.`);
+ $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);

76-84: Refactor method to clarify parameter handling

The createSubmittal method receives companyId and projectId from the caller, but only uses projectId in the function body. Also, the $ parameter is passed to the function but not explicitly defined in the function signature.

createSubmittal({
-  projectId, ...args
+  $, projectId, companyId, data, ...args
} = {}) {
  return this.app.post({
    versionPath: constants.VERSION_PATH.V1_1,
    path: `/projects/${projectId}/submittals`,
+   $,
+   companyId,
+   data,
    ...args,
  });
},
components/procore/actions/create-incident/create-incident.mjs (3)

1-2: Import constants module for consistency

The create-submittal.mjs file imports and uses the constants module for API version paths, but this file doesn't. For consistency across actions and to future-proof the code, import and use the constants module.

import app from "../../procore.app.mjs";
+import constants from "../../common/constants.mjs";

Then update the createIncident method to use version paths consistently:

createIncident({
  projectId, ...args
} = {}) {
  return this.app.post({
+   versionPath: constants.VERSION_PATH.DEFAULT, // Or appropriate version
    path: `/projects/${projectId}/incidents`,
    ...args,
  });
},

100-100: Fix typo in success message

There's a spelling error in the success message.

- $.export("$summary", `Succesfully created incident with ID \`${response.id}\`.`);
+ $.export("$summary", `Successfully created incident with ID \`${response.id}\`.`);

63-70: Refactor method to clarify parameter handling

Similar to the other action components, this method receives parameters from the caller that aren't explicitly defined in the function signature.

createIncident({
-  projectId, ...args
+  $, projectId, companyId, data, ...args
} = {}) {
  return this.app.post({
    path: `/projects/${projectId}/incidents`,
+   $,
+   companyId,
+   data,
    ...args,
  });
},
components/procore/actions/create-timesheet/create-timesheet.mjs (2)

60-60: Fix typo in success message

There's a spelling error in the success message.

- $.export("$summary", `Succesfully created timesheet with ID \`${response.id}\`.`);
+ $.export("$summary", `Successfully created timesheet with ID \`${response.id}\`.`);

1-2: Import constants module for consistency

Similar to the incident creation action, this file should import and use the constants module for API version paths to maintain consistency across actions.

import app from "../../procore.app.mjs";
+import constants from "../../common/constants.mjs";

Then update the createTimesheet method to use version paths consistently:

createTimesheet({
  projectId, ...args
} = {}) {
  return this.app.post({
+   versionPath: constants.VERSION_PATH.DEFAULT, // Or appropriate version
    path: `/projects/${projectId}/timesheets`,
    ...args,
  });
},
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

82-112: Test coverage recommendation.
The run method is comprehensive, but ensuring test coverage would allow you to verify that all fields (datetime, notes, numWorkers, etc.) are handled properly. Consider adding unit tests or integration tests.

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

292-340: Iterative approach in getIterations is well-structured.
The generator-based pagination logic aligns with typical usage patterns. Make sure you handle partial data or empty responses from the Procore API safely.

components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

16-40: Graceful fallback when projectId is missing.
Your console log helps, but consider returning a more structured warning to the user if the project ID is critical for fetching details.

components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1)

4-10: Add documentation for component status

The PR title indicates these components are untested. Consider adding a note in the description or as a comment to indicate the testing status.

Consider adding a note about testing status:

export default {
  ...common,
  name: "New Change Order Package Event (Instant)",
  key: "procore-new-change-order-package-event-instant",
+  // Note: This component is currently untested
  description: "Emit new event when a new change order package event is created. [See the documentation](https://developers.procore.com/reference/rest/hooks?version=latest).",
  version: "0.0.1",
  type: "source",
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

29-38: Consider documenting pagination behavior

This component uses pagination to fetch budget snapshots, which is different from other similar components that fetch a single resource. It might be helpful to document the expected structure of the returned data.

Add a comment explaining the pagination results:

if (!projectId) {
  console.log("If you need to get more details about the budget snapshot, please provide a project ID.");
  return body;
}

+// Retrieve all rows of the budget snapshot using pagination
const snapshotRows = await app.paginate({
  resourcesFn: app.getBudgetViewSnapshot,
  resourcesFnArgs: {
    companyId,
    budgetViewSnapshotId,
    params: {
      project_id: projectId,
    },
  },
});
components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1)

1-49: General pattern across webhook components

I notice a consistent pattern across all the webhook event source components. Consider creating a more generalized module that could reduce code duplication for these similar instant event handlers.

You could create a factory function or higher-order component that takes the resource name, API method, and metadata details as parameters and returns a configured instant event source. This would reduce duplication and make it easier to maintain these components.

For example, you could create something like:

// instantEventSourceFactory.mjs
import common from "../common/webhook.mjs";

export default function createInstantEventSource({
  name,
  key,
  resourceName,
  resourceIdParam,
  fetchResourceFn,
  missingProjectIdMessage,
}) {
  return {
    ...common,
    name,
    key,
    description: `Emit new event when a new ${name.toLowerCase()} is created. [See the documentation](https://developers.procore.com/reference/rest/hooks?version=latest).`,
    version: "0.0.1",
    type: "source",
    methods: {
      ...common.methods,
      getResourceName() {
        return resourceName;
      },
      async getDataToEmit(body) {
        const { app, companyId, projectId } = this;
        const resourceId = body.resource_id;
        
        if (!projectId) {
          console.log(missingProjectIdMessage);
          return body;
        }
        
        try {
          const resource = await fetchResourceFn(app, {
            companyId,
            [resourceIdParam]: resourceId,
            params: {
              project_id: projectId,
            },
          });
          
          return {
            ...body,
            resource,
          };
        } catch (error) {
          console.error(`Error fetching resource details: ${error.message}`);
          return body;
        }
      },
      generateMeta(body) {
        return {
          id: body.id,
          summary: `New ${name}: ${body.resource_id}`,
          ts: new Date(body.timestamp).getTime(),
        };
      },
    },
  };
}

Then you could use it like:

// new-change-order-package-event-instant.mjs
import createInstantEventSource from "../common/instantEventSourceFactory.mjs";
import resourceNames from "../../common/resource-names.mjs";

export default createInstantEventSource({
  name: "Change Order Package Event",
  key: "procore-new-change-order-package-event-instant",
  resourceName: resourceNames.CHANGE_ORDER_PACKAGES,
  resourceIdParam: "changeOrderPackageId",
  fetchResourceFn: (app, args) => app.getChangeOrderPackage(args),
  missingProjectIdMessage: "If you need to get more details about the change order package, please provide a project ID.",
});

This approach would make it easier to maintain these components and add new ones in the future.

components/procore/sources/common/webhook.mjs (2)

35-35: Correct spelling of the function name.

The method name is spelled as additionalPropperties. Rename it to additionalProperties to match typical naming conventions and avoid confusion.

-  async additionalPropperties() {
+  async additionalProperties() {

119-119: Validate the existence of the success field before mapping.

If the success field doesn’t exist or is not an array, this code may store undefined in the database. Consider gracefully handling empty or invalid responses.

-    setWebhookTriggerIds(triggerResponses?.success.map(({ id }) => id));
+    setWebhookTriggerIds(
+      Array.isArray(triggerResponses?.success)
+        ? triggerResponses.success.map(({ id }) => id)
+        : []
+    );
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 74caf69 and 2869d06.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (2 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
  • components/procore/sources/submittal/submittal.js
  • components/procore/sources/budget-snapshot/budget-snapshot.js
  • components/procore/procore.app.js
  • components/procore/sources/new-event/new-event.js
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/sources/purchase-order/purchase-order.js
  • components/procore/sources/common.js
  • components/procore/sources/rfi/rfi.js
🧰 Additional context used
🪛 GitHub Actions: Pull Request Checks
components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs

[error] 1-1: Component folder name, component file name without extension and component key without slug should be the same! See the docs: https://pipedream.com/docs/components/guidelines/#folder-structure

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (20)
components/procore/common/utils.mjs (1)

14-17: Good export pattern.

The default export pattern cleanly exports both utility functions in a single object.

components/procore/package.json (2)

3-3: Version bump is appropriate for new features.

Increasing from 0.0.4 to 0.1.0 follows semantic versioning for adding new features without breaking changes.


19-19:

❓ Verification inconclusive

Verify compatibility with major platform version upgrade.

The @pipedream/platform dependency has been updated from ^1.1.1 to ^3.0.3, which is a major version jump that might introduce breaking changes.

Please ensure all component code is compatible with this new platform version. You might want to check:


🏁 Script executed:

#!/bin/bash
# Search for any deprecated API usage in the codebase
cd components/procore
grep -r "deprecated" --include="*.mjs" --include="*.js" .

# Look for any explicit version checks or version-specific code
grep -r "platform.*version\|version.*platform" --include="*.mjs" --include="*.js" .

Length of output: 167


Compatibility Check Needed for @pipedream/platform Upgrade
The update from ^1.1.1 to ^3.0.3 is a major version bump. While our automated search for deprecated APIs and version-specific code (using grep) returned no output, this result isn’t conclusive evidence that there are no breaking changes. Please perform a manual review—especially in critical flows that rely on this dependency—to ensure no API changes adversely affect the component.

components/procore/common/resource-names.mjs (1)

1-2: Good documentation with source references.

Including references to the official Procore API documentation and CSV source is helpful for future maintenance.

components/procore/sources/new-event-instant/new-event-instant.mjs (2)

3-9: Good component metadata with documentation link.

The module provides clear metadata including name, key, and a helpful description with a link to the API documentation.

Since the PR title mentions these components are "untested", ensure this module is properly tested before promoting to production use.


10-18: Clean property extension pattern.

The props section correctly extends common properties and adds the resourceName property using the recommended propDefinition pattern.

components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1)

1-47: LGTM! Component implements webhook handler correctly

The component correctly imports common webhook functionality and implements the necessary methods for handling submittal events.

components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1)

1-47: LGTM! Component implements webhook handler correctly

The component correctly imports common webhook functionality and implements the necessary methods for handling RFI events.

components/procore/actions/create-submittal/create-submittal.mjs (1)

1-118: Add unit tests for this component

The PR title indicates these components are untested. To ensure reliability, especially for API integrations, unit tests should be added to verify the functionality works as expected.

Would you like me to help generate unit test templates for this component? This could include tests for:

  1. Validating proper API calls are made with correct parameters
  2. Handling of optional parameters
  3. Error handling scenarios
  4. Response processing
components/procore/actions/create-manpower-log/create-manpower-log.mjs (3)

1-2: Use explicit relative import paths or confirm correctness.
Make sure "../../procore.app.mjs" is the correct relative path. Mistakes in path references could break the import in certain environments.


3-8: Export metadata looks clear.
The key, name, description, version, and type fields are well-structured. This metadata will help developers and users quickly identify the purpose of this action.


9-71: Validate required props or handle undefined gracefully.
Some props (e.g., companyId, projectId) are presumably required for API calls. Ensure the action prevents unexpected undefined values in downstream processes.

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

1-5: Imports and constants references appear valid.
No issues detected with the imports from @pipedream/platform or local modules. Good to see well-organized references.


6-53: Ensure correct integer parsing for companyId and projectId.
The prop definitions specify integer types. Confirm values are correctly parsed (e.g., from strings) in all usage contexts to avoid unexpected type issues.


66-71: Consider defaulting to empty array or descriptive fallback for locationId.
The optional locationId returns an empty array if no companyId or projectId is provided. Confirm this fallback is handled gracefully by downstream code.


145-186: Centralizing request logic is a great design choice.
The _getBaseUrl(), _getHeaders(), and makeRequest() methods keep the code DRY and easy to maintain.


187-199: Pagination approach for companies and projects is consistent.
Using params { per_page, page } is fine, but confirm large data sets are handled gracefully.

components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (3)

1-3: Confirm dependencies on ../common/webhook.mjs.
Ensure that common is properly exported in webhook.mjs so all inherited logic remains valid.


4-11: Metadata for event source is descriptive.
The name, key, and description provide clarity on the module's purpose and usage.


41-47: Leverage robust ID checks in generateMeta().
You're constructing an event ID from body.id, but if id is unexpectedly missing, it could cause inconsistencies. Validate body.id or default gracefully.

@jcortes jcortes force-pushed the procore-new-components branch from 2869d06 to 1a6b1ac Compare March 7, 2025 16:08
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (3)
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

73-80: 🛠️ Refactor suggestion

Add error handling for API failures.

Currently, if an error occurs during createManpowerLog(), the action might silently fail at runtime. Consider wrapping API calls in robust try-catch blocks or returning informative errors.

createManpowerLog({
  projectId, ...args
} = {}) {
+  try {
    return this.app.post({
      path: `/projects/${projectId}/manpower_logs`,
      ...args,
    });
+  } catch (err) {
+    throw new Error(`Error creating manpower log: ${err.message}`);
+  }
},
components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1)

16-41: 🛠️ Refactor suggestion

Add error handling for the API call

The getDataToEmit method makes an API call to retrieve change order package details, but it doesn't handle potential API errors. If the app.getChangeOrderPackage call fails, the error will propagate up and might cause the entire webhook processing to fail.

async getDataToEmit(body) {
  const {
    app,
    companyId,
    projectId,
  } = this;
  const { resource_id: changeOrderPackageId } = body;

  if (!projectId) {
    console.log("If you need to get more details about the change order package, please provide a project ID.");
    return body;
  }

+  try {
    const resource = await app.getChangeOrderPackage({
      companyId,
      changeOrderPackageId,
      params: {
        project_id: projectId,
      },
    });

    return {
      ...body,
      resource,
    };
+  } catch (error) {
+    console.error(`Error fetching change order package details: ${error.message}`);
+    return body;
+  }
},
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

16-44: 🛠️ Refactor suggestion

Add error handling for pagination function

The getDataToEmit method uses pagination to fetch budget view snapshot details, but it doesn't handle potential API errors. If the app.paginate call fails, the error will propagate up and might cause the webhook processing to fail.

async getDataToEmit(body) {
  const {
    app,
    companyId,
    projectId,
  } = this;
  const { resource_id: budgetViewSnapshotId } = body;

  if (!projectId) {
    console.log("If you need to get more details about the budget snapshot, please provide a project ID.");
    return body;
  }

+  try {
    const snapshotRows = await app.paginate({
      resourcesFn: app.getBudgetViewSnapshot,
      resourcesFnArgs: {
        companyId,
        budgetViewSnapshotId,
        params: {
          project_id: projectId,
        },
      },
    });

    return {
      ...body,
      snapshotRows,
    };
+  } catch (error) {
+    console.error(`Error fetching budget view snapshot details: ${error.message}`);
+    return body;
+  }
},
🧹 Nitpick comments (11)
components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1)

24-27: Consider enhancing the log message with more guidance

The log message could be more helpful by providing guidance on where to find or configure the project ID.

-        console.log("If you need to get more details about the timecard entry, please provide the project ID.");
+        console.log("If you need to get more details about the timecard entry, please provide a project ID in the component configuration.");
components/procore/actions/create-rfi/create-rfi.mjs (2)

113-113: Fix typo in success message

There's a spelling error in the success message.

-    $.export("$summary", `Succesfully created RFI with ID \`${response.id}\`.`);
+    $.export("$summary", `Successfully created RFI with ID \`${response.id}\`.`);

73-82: Consider using API versioning consistently

The createRfi method doesn't specify a version path, unlike the createSubmittal method in the other file which uses constants.VERSION_PATH.V1_1. Consider using versioning consistently across all API calls.

  createRfi({
    projectId, ...args
  } = {}) {
    return this.app.post({
+     versionPath: constants.VERSION_PATH.V1_1, // or the appropriate version
      path: `/projects/${projectId}/rfis`,
      ...args,
    });
  },

Don't forget to import constants:

+import constants from "../../common/constants.mjs";
 import app from "../../procore.app.mjs";
components/procore/actions/create-submittal/create-submittal.mjs (1)

115-115: Fix typo in success message

There's a spelling error in the success message.

-    $.export("$summary", `Succesfully created submittal with ID \`${response.id}\`.`);
+    $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);
components/procore/actions/create-incident/create-incident.mjs (2)

100-100: Fix typo in success message

There's a spelling error in the success message.

-    $.export("$summary", `Succesfully created incident with ID \`${response.id}\`.`);
+    $.export("$summary", `Successfully created incident with ID \`${response.id}\`.`);

62-71: Consider using API versioning consistently

The createIncident method doesn't specify a version path, unlike the createSubmittal method which uses constants.VERSION_PATH.V1_1. Consider using versioning consistently across all API calls.

  createIncident({
    projectId, ...args
  } = {}) {
    return this.app.post({
+     versionPath: constants.VERSION_PATH.V1_1, // or the appropriate version
      path: `/projects/${projectId}/incidents`,
      ...args,
    });
  },

Don't forget to import constants:

+import constants from "../../common/constants.mjs";
 import app from "../../procore.app.mjs";
components/procore/actions/create-manpower-log/create-manpower-log.mjs (2)

45-49: Consider using numeric type for numHours

The property is defined as a string type, but since it represents a numeric value (hours), it would be more appropriate to use a numeric type like 'number' or 'integer'.

numHours: {
-  type: "string",
+  type: "number",
  label: "Number Of Hours",
  description: "The number of hours for each worker",
  optional: true,
},

110-110: Fix typo in success message

There's a spelling error in the success message.

-$.export("$summary", `Succesfully created manpower log with ID \`${response.id}\`.`);
+$.export("$summary", `Successfully created manpower log with ID \`${response.id}\`.`);
components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1)

24-27: Consider providing richer user feedback
When the projectId is missing, the debug log may not be sufficient. You might want to throw a more explicit exception or handle this scenario upstream to ensure the user is aware that the change event details could not be fetched.

components/procore/sources/common/webhook.mjs (2)

35-64: Minor naming nitpick
The method name additionalPropperties might be a typo for additionalProperties. Renaming it will improve clarity.

-async additionalPropperties() {
+async additionalProperties() {

214-216: Add error handling for getDataToEmit
Currently, this method simply returns the body, but if you extend it in the future to make external API calls, ensure you handle potential errors as you do in your other derived classes.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 2869d06 and 1a6b1ac.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (2 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/common.js
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/sources/new-event/new-event.js
  • components/procore/procore.app.js
  • components/procore/sources/rfi/rfi.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
  • components/procore/sources/submittal/submittal.js
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/sources/purchase-order/purchase-order.js
  • components/procore/sources/budget-snapshot/budget-snapshot.js
🚧 Files skipped from review as they are similar to previous changes (10)
  • components/procore/package.json
  • components/procore/sources/new-event-instant/new-event-instant.mjs
  • components/procore/common/utils.mjs
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/common/resource-names.mjs
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs
  • components/procore/actions/create-timesheet/create-timesheet.mjs
  • components/procore/common/constants.mjs
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (8)
components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1)

1-49: Overall implementation looks good!

The module is well-structured and follows the established pattern for Procore webhook sources. The component correctly imports the common webhook functionality and resource names, and implements the necessary methods to handle timecard entry events.

components/procore/actions/create-rfi/create-rfi.mjs (1)

1-116: Implementation looks good, but testing is needed

The action is well-structured with appropriate props and methods for creating an RFI in Procore.

Since this component is marked as "untested" in the PR title, please ensure you test this functionality thoroughly before merging.

components/procore/actions/create-submittal/create-submittal.mjs (1)

1-118: Implementation looks good, but testing is needed

The action is well-structured with appropriate props and methods for creating a submittal in Procore.

Since this component is marked as "untested" in the PR title, please ensure you test this functionality thoroughly before merging.

components/procore/actions/create-incident/create-incident.mjs (1)

1-103: Implementation looks good, but testing is needed

The action is well-structured with appropriate props and methods for creating an incident in Procore.

Since this component is marked as "untested" in the PR title, please ensure you test this functionality thoroughly before merging.

components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (2)

16-35: Add error handling for the API call
The getDataToEmit method calls app.getChangeEvent without handling potential HTTP or network errors. A surrounding try/catch block will protect the webhook flow from unexpected failures.

Below is an example fix, matching a prior recommendation:

async getDataToEmit(body) {
  const {
    app,
    companyId,
    projectId,
  } = this;
  const { resource_id: changeEventId } = body;

  if (!projectId) {
    console.log("If you need to get more details about the commitment change order, please provide a project ID.");
    return body;
  }

+ try {
    const resource = await app.getChangeEvent({
      companyId,
      changeEventId,
      params: {
        project_id: projectId,
      },
    });
    return {
      ...body,
      resource,
    };
+ } catch (error) {
+   console.error(`Error fetching change event details: ${error.message}`);
+   return body;
+ }
}

41-47: Validate the timestamp field
The generateMeta method references body.timestamp, but there's no guarantee this property exists or follows a valid date format. Consider a fallback timestamp to avoid runtime errors and keep event ordering.

components/procore/sources/common/webhook.mjs (2)

156-158: Implement the generateMeta method
Currently, it throws a ConfigurationError, which can cause runtime failures in derived sources. Provide a real implementation or ensure child classes override it.


177-179: Override or remove getResourceName
The base method throws an error, but derived classes may rely on it for generating triggers. If every derived class must override it, consider turning it into an abstract or mandatory requirement in your docs.

@jcortes jcortes force-pushed the procore-new-components branch from 1a6b1ac to 75fee6f Compare March 7, 2025 16:43
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (11)
components/procore/actions/create-submittal/create-submittal.mjs (2)

115-115: Fix typo in success message.

There's a spelling error in the success message.

-    $.export("$summary", `Succesfully created submittal with ID \`${response.id}\`.`);
+    $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);

76-84: Add error handling for API calls.

The createSubmittal method lacks error handling, which could lead to silent failures or confusing error messages.

  createSubmittal({
    projectId, ...args
  } = {}) {
+   try {
      return this.app.post({
        versionPath: constants.VERSION_PATH.V1_1,
        path: `/projects/${projectId}/submittals`,
        ...args,
      });
+   } catch (error) {
+     throw new Error(`Error creating submittal: ${error.message}`);
+   }
  },
components/procore/actions/create-manpower-log/create-manpower-log.mjs (2)

45-49: Change numHours type from string to number.

The numHours property is defined with type "string" but would be more appropriately defined as a number since it represents a quantity.

  numHours: {
-    type: "string",
+    type: "number",
    label: "Number Of Hours",
    description: "The number of hours for each worker",
    optional: true,
  },

110-110: Fix typo in success message.

There's a spelling error in the success message.

-    $.export("$summary", `Succesfully created manpower log with ID \`${response.id}\`.`);
+    $.export("$summary", `Successfully created manpower log with ID \`${response.id}\`.`);
components/procore/procore.app.mjs (1)

279-280: Fix version path casing inconsistency.

The version path constant is using lowercase v1_3 while other version paths use uppercase format (like V1_1).

-        versionPath: constants.VERSION_PATH.V1_3,
+        versionPath: constants.VERSION_PATH.V1_3,

Verify that the constant is defined using uppercase in the constants file.

components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

29-35: Add error handling for getPrimeContract API call.

The call to app.getPrimeContract lacks error handling, which could cause the webhook to fail silently if the API request encounters an issue.

-      const resource = await app.getPrimeContract({
-        companyId,
-        primeContractId,
-        params: {
-          project_id: projectId,
-        },
-      });
+      let resource;
+      try {
+        resource = await app.getPrimeContract({
+          companyId,
+          primeContractId,
+          params: {
+            project_id: projectId,
+          },
+        });
+      } catch (error) {
+        console.error(`Failed to fetch prime contract details: ${error.message}`);
+        // Return the original body if we can't get additional details
+        return body;
+      }
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

45-51: Validate the incoming timestamp
If body.timestamp is invalid, new Date(body.timestamp).getTime() will yield NaN. Consider falling back to Date.now() or providing error handling to ensure a valid timestamp.

-  ts: new Date(body.timestamp).getTime(),
+  ts: Number.isNaN(new Date(body.timestamp).getTime())
+    ? Date.now()
+    : new Date(body.timestamp).getTime(),
components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1)

41-47: Validate the incoming timestamp
Just like other event sources, relying on new Date(body.timestamp).getTime() without checking validity can yield NaN. Consider a fallback or additional validation.

components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1)

41-47: Validate the incoming timestamp
Similarly, guard against invalid body.timestamp values to prevent potential NaN results.

components/procore/sources/common/webhook.mjs (2)

35-35: Fix method name spelling
The function is named additionalPropperties, which is a misspelling. Rename it to additionalProperties for clarity and consistency.

-  async additionalPropperties() {
+  async additionalProperties() {

66-153: Consider robust error handling in activation/deactivation hooks
The activate and deactivate hooks rely on multiple async API calls. If any step fails, you risk partial success states (e.g., webhook created but triggers not fully set), which can lead to inconsistent data. Wrap the API calls in try/catch blocks and implement a rollback strategy or partial clean-up as needed.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1a6b1ac and 75fee6f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (30)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (2 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/sources/rfi/rfi.js
  • components/procore/sources/budget-snapshot/budget-snapshot.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
  • components/procore/sources/common.js
  • components/procore/sources/submittal/submittal.js
  • components/procore/sources/new-event/new-event.js
  • components/procore/procore.app.js
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/sources/purchase-order/purchase-order.js
🚧 Files skipped from review as they are similar to previous changes (12)
  • components/procore/common/resource-names.mjs
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/actions/create-rfi/create-rfi.mjs
  • components/procore/sources/new-event-instant/new-event-instant.mjs
  • components/procore/common/utils.mjs
  • components/procore/actions/create-incident/create-incident.mjs
  • components/procore/common/constants.mjs
  • components/procore/actions/create-timesheet/create-timesheet.mjs
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore/package.json
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (9)
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

73-80: Add better error handling for API failures.

Currently, if an error occurs during createManpowerLog(), the action might silently fail at runtime. Consider wrapping API calls in robust try-catch blocks or returning informative errors.

+    try {
       return this.app.post({
         path: `/projects/${projectId}/manpower_logs`,
         ...args,
       });
+    } catch (err) {
+      throw new Error(`Error creating manpower log: ${err.message}`);
+    }
components/procore/procore.app.mjs (2)

229-229: Fix prime contract endpoint path to use plural form.

The Procore API uses plural form for resource endpoints. Update the path to use /prime_contracts/ instead of /prime_contract/.

-        path: `/prime_contract/${primeContractId}`,
+        path: `/prime_contracts/${primeContractId}`,

292-336: Add robust error handling to pagination logic.

The getIterations generator method lacks error handling for API calls, which could cause the entire pagination process to fail if an error occurs during any request.

async *getIterations({
  resourcesFn, resourcesFnArgs, resourceName,
  max = constants.DEFAULT_MAX,
}) {
  let page = 1;
  let resourcesCount = 0;

  while (true) {
+   try {
      const response =
        await resourcesFn({
          ...resourcesFnArgs,
          params: {
            ...resourcesFnArgs?.params,
            page,
            per_page: constants.DEFAULT_LIMIT,
          },
        });

      const nextResources = resourceName
        ? utils.getNestedProperty(response, resourceName)
        : response;

      if (!nextResources?.length) {
        console.log("No more resources found");
        return;
      }

      for (const resource of nextResources) {
        yield resource;
        resourcesCount += 1;

        if (resourcesCount >= max) {
          console.log("Reached max resources");
          return;
        }
      }

      if (nextResources.length < constants.DEFAULT_LIMIT) {
        console.log("No next page found");
        return;
      }

      page += 1;
+   } catch (error) {
+     console.error(`Error fetching resources on page ${page}: ${error.message}`);
+     throw error; // Re-throw to allow the caller to handle the error
+   }
  }
},
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

16-40: Verify this component will work with the corrected prime contract endpoint.

The getDataToEmit method calls app.getPrimeContract, which currently uses an incorrect singular endpoint path (/prime_contract/). Once the path is corrected in the app file to use plural form (/prime_contracts/), verify that this component still works correctly.

components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

29-38: Add error handling for the pagination call
This code directly calls app.paginate without catching potential errors, which could cause unhandled exceptions. The exact fix requested in a previous review is still applicable.

components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1)

29-35: Add error handling for the API call
Similar to previous feedback, app.getPurchaseOrderContract may fail due to network or API issues. Adding a try/catch block ensures more robust error handling.

components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1)

29-35: Add error handling for the API call
Continue the pattern of adding a try/catch block around app.getChangeEvent to handle unexpected failures gracefully.

components/procore/sources/common/webhook.mjs (2)

156-158: Implement or remove the default generateMeta
Leaving this unimplemented can cause runtime errors if derived classes forget to override it. This was flagged in a previous review.


177-179: Implement or override getResourceName
This method throws a ConfigurationError by default, which will break sources if unimplemented. Also flagged in a prior review.

@jcortes jcortes force-pushed the procore-new-components branch from 75fee6f to a294ed8 Compare March 10, 2025 16:59
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: 6

♻️ Duplicate comments (2)
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

16-26: 🛠️ Refactor suggestion

Add error handling for pagination calls.

app.paginate could fail, potentially causing the source to crash or silently fail. Wrap it in a try/catch block or handle errors more gracefully to enhance reliability.

async getDataToEmit(body) {
  const {
    app,
    companyId,
    projectId,
  } = this;
  const { resource_id: budgetViewSnapshotId } = body;

+  try {
    const snapshotRows = await app.paginate({
      resourcesFn: app.getBudgetViewSnapshot,
      resourcesFnArgs: {
        companyId,
        budgetViewSnapshotId,
        params: {
          project_id: projectId,
        },
      },
    });
+    return {
+      ...body,
+      snapshotRows,
+    };
+  } catch (err) {
+    console.error("Error during paginate:", err.message);
+    return body;
+  }
}
components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1)

16-26: 🛠️ Refactor suggestion

Handle potential errors when fetching change event details.

The code directly calls app.getChangeEvent without a try/catch. If the API call fails, the error may cause downstream issues or incomplete event data. Add error handling to gracefully handle failures.

async getDataToEmit(body) {
  const {
    app,
    companyId,
  } = this;
  const { resource_id: changeEventId, project_id: projectId } = body;

+  try {
    const resource = await app.getChangeEvent({
      companyId,
      changeEventId,
      params: {
        project_id: projectId,
      },
    });
    return {
      ...body,
      resource,
    };
+  } catch (err) {
+    console.error("Failed to retrieve change event details:", err.message);
+    return body;
+  }
}
🧹 Nitpick comments (25)
components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1)

4-5: Consider addressing ESLint disabled rules.

While disabling these rules may be intentional for sandbox components, it would be good to document why these specific rules are being disabled or consider addressing them if possible.

components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs (1)

4-5: Consider addressing ESLint disabled rules.

While disabling these rules may be intentional for sandbox components, it would be good to document why these specific rules are being disabled or consider addressing them if possible.

components/procore_sandbox/actions/create-rfi/create-rfi.mjs (1)

4-5: Consider addressing ESLint disabled rules.

While disabling these rules may be intentional for sandbox components, it would be good to document why these specific rules are being disabled or consider addressing them if possible.

components/procore_sandbox/actions/create-submittal/create-submittal.mjs (1)

4-5: Consider addressing ESLint disabled rules.

While disabling these rules may be intentional for sandbox components, it would be good to document why these specific rules are being disabled or consider addressing them if possible.

components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1)

4-6: ESLint disables should be temporary

The ESLint disable comments for required properties should be addressed before the final version. While they might be necessary during development, make sure to follow Pipedream's linting requirements for production code.

components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (2)

4-6: ESLint disables should be temporary

The ESLint disable comments for required properties should be addressed before the final version. While they might be necessary during development, make sure to follow Pipedream's linting requirements for production code.


14-15: Remove unnecessary trailing newline

There's an extra empty line at the end of the file that should be removed.

  version: "0.0.1",
};
-
components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

4-6: ESLint disables should be temporary

The ESLint disable comments for required properties should be addressed before the final version. While they might be necessary during development, make sure to follow Pipedream's linting requirements for production code.

components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs (1)

4-6: ESLint disables should be temporary

The ESLint disable comments for required properties should be addressed before the final version. While they might be necessary during development, make sure to follow Pipedream's linting requirements for production code.

components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1)

16-37: Add error handling for missing projectId

The getDataToEmit method doesn't have any error handling or log message when the projectId is missing. This may cause issues if the Procore API requires a projectId to retrieve timecard entry details.

Consider adding a check and log message when projectId is not provided:

    async getDataToEmit(body) {
      const {
        app,
        companyId,
      } = this;
      const {
        resource_id: timecardEntryId,
        project_id: projectId,
      } = body;

+     if (!projectId) {
+       console.log("If you need to get more details about the timecard entry, please provide a project ID.");
+       return body;
+     }
+
      const resource = await app.getTimecardEntry({
        companyId,
        timecardEntryId,
        params: {
          project_id: projectId,
        },
      });
      return {
        ...body,
        resource,
      };
    },
components/procore_sandbox/common/utils.mjs (2)

3-33: Optimize the reduce method to avoid O(n²) time complexity

The current implementation of buildPropDefinitions uses spread syntax on accumulators in the reduce function, which can lead to O(n²) time complexity for large objects.

Consider refactoring to avoid spread on the accumulator:

function buildPropDefinitions({
  app = {}, props = {},
}) {
  return Object.entries(props)
    .reduce((newProps, [
      key,
      prop,
    ]) => {
      if (!prop.propDefinition) {
-        return {
-          ...newProps,
-          [key]: prop,
-        };
+        newProps[key] = prop;
+        return newProps;
      }

      const [
        , ...propDefinitionItems
      ] = prop.propDefinition;

-      return {
-        ...newProps,
-        [key]: {
-          ...prop,
-          propDefinition: [
-            app,
-            ...propDefinitionItems,
-          ],
-        },
-      };
+      newProps[key] = {
+        ...prop,
+        propDefinition: [
+          app,
+          ...propDefinitionItems,
+        ],
+      };
+      return newProps;
    }, {});
}
🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)


35-50: Consider adding error handling for missing component properties

The getAppProps function doesn't have any error handling for cases where component or component.props might be undefined or null. This could cause runtime errors in edge cases.

Consider adding more robust error handling:

function getAppProps(component = {}) {
+  if (!component || !component.props) {
+    return { props: { app } };
+  }
+
  const {
    // eslint-disable-next-line no-unused-vars
    app: procore,
    ...otherProps
  } = component.props;
  return {
    props: {
      app,
      ...buildPropDefinitions({
        app,
        props: otherProps,
      }),
    },
  };
}
components/procore/actions/create-submittal/create-submittal.mjs (3)

116-116: Fix typo in success message

There's a typo in the success message: "Succesfully" should be "Successfully".

- $.export("$summary", `Succesfully created submittal with ID \`${response.id}\`.`);
+ $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);

77-85: Add error handling in createSubmittal method

The createSubmittal method doesn't include any error handling for API failures. Consider adding try/catch or allowing error propagation for better debugging.

For example:

  createSubmittal({
    projectId, ...args
  } = {}) {
+   if (!projectId) {
+     throw new Error("Project ID is required to create a submittal");
+   }
    return this.app.post({
      versionPath: constants.VERSION_PATH.V1_1,
      path: `/projects/${projectId}/submittals`,
      ...args,
    });
  },

10-75: Consider adding validation for required fields

The component requires several fields but doesn't validate them before making the API call. Consider adding validation for required fields like number.

components/procore/actions/create-incident/create-incident.mjs (4)

100-100: Fix typo in success message

There's a typo in the success message: "Succesfully" should be "Successfully".

- $.export("$summary", `Succesfully created incident with ID \`${response.id}\`.`);
+ $.export("$summary", `Successfully created incident with ID \`${response.id}\`.`);

63-70: Add error handling in createIncident method

The createIncident method doesn't include any error handling for API failures or for cases where required parameters are missing. Consider adding validation for the projectId parameter.

For example:

  createIncident({
    projectId, ...args
  } = {}) {
+   if (!projectId) {
+     throw new Error("Project ID is required to create an incident");
+   }
    return this.app.post({
      path: `/projects/${projectId}/incidents`,
      ...args,
    });
  },

32-36: Improve eventDate documentation and validation

The eventDate field description is good, but adding validation would help users provide correctly formatted dates.

Consider adding a more specific type or validation in the run method:

  eventDate: {
    type: "string", 
    label: "Event Date",
    description: "Iso8601 datetime of Incident occurrence. If time is unknown, send in the date at `0:00` project time converted to UTC. Eg. `2025-04-01T00:00:00Z`.",
+   pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z$",
+   examples: ["2025-04-01T00:00:00Z"],
  },

Or add validation in the run method before making the API call.


72-102: Add validation for required fields before API call

The component requires several fields but doesn't validate them before making the API call. Consider adding validation for required fields like title and eventDate.

  async run({ $ }) {
    const {
      createIncident,
      companyId,
      projectId,
      title,
      description,
      eventDate,
      isPrivate,
      recordable,
      timeUnknown,
    } = this;

+   // Validate required fields
+   if (!title) {
+     throw new Error("Title is required");
+   }
+   if (!eventDate) {
+     throw new Error("Event Date is required");
+   }
+
    const response = await createIncident({
      $,
      companyId,
      projectId,
      data: {
        incident: {
          title,
          description,
          event_date: eventDate,
          private: isPrivate,
          recordable,
          time_unknown: timeUnknown,
        },
      },
    });
    $.export("$summary", `Succesfully created incident with ID \`${response.id}\`.`);
    return response;
  },
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

26-32: Catch potential errors when retrieving prime contract
If getPrimeContract fails, the code will throw an unhandled error. A try-catch block or equivalent error handling pattern can improve resilience.

components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1)

38-45: Validate timestamp presence.

The generateMeta function correctly extracts a timestamp from body.timestamp. If body.timestamp is missing or not in a valid format, new Date(body.timestamp).getTime() may return NaN. Consider defensive checks to ensure a valid timestamp is available.

generateMeta(body) {
  + if (!body.timestamp || Number.isNaN(new Date(body.timestamp).getTime())) {
  +   console.warn("Missing or invalid timestamp. Defaulting to current time.");
  +   return {
  +     id: body.id,
  +     summary: `New Purchase Order Event: ${body.resource_id}`,
  +     ts: Date.now(),
  +   };
  + }
  return {
    id: body.id,
    summary: `New Purchase Order Event: ${body.resource_id}`,
    ts: new Date(body.timestamp).getTime(),
  };
},
components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1)

39-45: Check for invalid timestamps.

As with similar event sources, consider handling cases where body.timestamp might be invalid or missing.

components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

42-48: Check for invalid or missing timestamps.

new Date(body.timestamp).getTime() can return NaN if body.timestamp is undefined or invalid. Consider a fallback or validation to avoid potential errors or ambiguous timestamps.

components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1)

38-44: Validate timestamp usage.

new Date(body.timestamp).getTime() might result in an invalid timestamp if body.timestamp is null or incorrectly formatted. Validate that timestamp is a valid date to avoid erroneous or missing event times.

components/procore/sources/common/webhook.mjs (1)

220-236: Handle missing or malformed body data.

If body lacks the expected fields (e.g., resource_id, timestamp), processResource(resource) might behave unexpectedly. Consider validating required fields or gracefully handling incomplete requests.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 75fee6f and a294ed8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (1 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
  • components/procore_sandbox/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore_sandbox/common/utils.mjs (1 hunks)
  • components/procore_sandbox/package.json (1 hunks)
  • components/procore_sandbox/procore_sandbox.app.mjs (1 hunks)
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/sources/submittal/submittal.js
  • components/procore/sources/budget-snapshot/budget-snapshot.js
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
  • components/procore/sources/common.js
  • components/procore/procore.app.js
  • components/procore/sources/new-event/new-event.js
  • components/procore/sources/purchase-order/purchase-order.js
  • components/procore/sources/rfi/rfi.js
✅ Files skipped from review due to trivial changes (2)
  • components/procore_sandbox/package.json
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
🚧 Files skipped from review as they are similar to previous changes (5)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/actions/create-rfi/create-rfi.mjs
  • components/procore/sources/new-event-instant/new-event-instant.mjs
  • components/procore/common/utils.mjs
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/procore_sandbox/common/utils.mjs

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (48)
components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (2)

1-13: Code follows the standard sandbox component pattern.

The implementation correctly extends the main Procore component while applying sandbox-specific properties. This sandbox wrapper pattern ensures testing can be done in isolation from production components.


10-12: Verify the component key and description.

The key correctly follows the naming convention with the procore_sandbox- prefix. The description includes a link to the API documentation, which is helpful for developers.

components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs (2)

1-13: Code follows the standard sandbox component pattern.

The implementation correctly extends the main Procore component while applying sandbox-specific properties. This sandbox wrapper pattern is consistent with other components in this PR.


10-12: Verify the component key and description.

The key correctly follows the naming convention with the procore_sandbox- prefix. The description explains that the component emits events based on selected resource names and includes a helpful link to the API documentation.

components/procore_sandbox/actions/create-rfi/create-rfi.mjs (2)

1-13: Code follows the standard sandbox component pattern.

The implementation correctly extends the main Procore component while applying sandbox-specific properties. This sandbox wrapper pattern is consistent with the other components in this PR.


10-12: Verify the component key and description.

The key correctly follows the naming convention with the procore_sandbox- prefix. The description clearly explains the action's purpose and includes a helpful link to the API documentation.

components/procore_sandbox/actions/create-submittal/create-submittal.mjs (3)

1-13: Code follows the standard sandbox component pattern.

The implementation correctly extends the main Procore component while applying sandbox-specific properties. This sandbox wrapper pattern is consistent across all the components in this PR.


10-12: Verify the component key and description.

The key correctly follows the naming convention with the procore_sandbox- prefix. The description clearly explains the action's purpose and includes a helpful link to the API documentation.


7-13: Remember to add tests for these components.

Per the PR objectives, these components are currently untested. Consider adding tests to verify their functionality before merging to production.

components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1)

1-13: Code structure looks good but needs testing as indicated in PR title

The component correctly extends the main Procore component and utilizes common utilities. The ESLint disables for required properties appear necessary for this pattern, and the documentation link is properly included.

Since the PR title mentions these components are "untested", I recommend adding test cases to verify the webhook functionality works correctly with the Procore sandbox environment before merging.

Could you confirm that this component has been manually tested with the Procore sandbox API? If not, consider adding automated tests or documenting test steps to verify this component's functionality.

components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs (1)

1-13: Component structure is consistent with other sandbox components

The implementation correctly extends the main Procore component and incorporates necessary properties for the sandbox environment. The version numbering (0.0.1) appropriately indicates this is a new component.

As mentioned in the PR title that these components are "untested", ensure you add proper test coverage for this action component.

Have you verified that the API endpoint in the documentation link works correctly in the sandbox environment? The timesheet creation functionality should be tested with various input combinations to ensure robustness.

components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1)

1-13: Implementation follows the established pattern for sandbox components

This component correctly extends the main Procore component and follows the same pattern as other sandbox components. The documentation link provides a good reference for users.

As with the other components in this PR, testing is needed according to the PR title.

Consider adding integration tests that verify webhook registration and event processing specifically for submittal events in the sandbox environment. This will ensure the component behaves as expected when deployed.

components/procore_sandbox/actions/create-incident/create-incident.mjs (1)

1-13: Follows consistent implementation pattern but requires testing

The component correctly extends the main Procore component and maintains consistency with other sandbox components. The documentation link is appropriate for the create incident functionality.

Since these components are marked as "untested" in the PR title, testing should be prioritized.

Before finalizing this PR, I recommend:

  1. Creating a test plan for each component
  2. Verifying API responses in the sandbox environment
  3. Ensuring error handling is properly implemented for scenarios like invalid incident details

This will help prevent issues when these components are used in production workflows.

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

1-11: Clean implementation of environment-specific app

Nice refactoring to inherit the base Procore functionality while maintaining sandbox-specific differences. The use of the spread operator for both the main export and methods object provides clean inheritance while the getEnvironment() method appropriately identifies this as a sandbox environment.

components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (2)

7-13: Consistent component extension pattern

Good work following the established pattern for extending components across environments. The sandbox implementation cleanly inherits from the base component while providing its unique key and proper versioning.


4-6:

❓ Verification inconclusive

Consider addressing ESLint disabled rules

While the current implementation works, you've disabled two ESLint rules that are typically important for Pipedream components. Is there a specific reason why type and name properties are omitted?


🏁 Script executed:

#!/bin/bash
# Check if other event sources follow the same pattern with disabled ESLint rules
grep -r "eslint-disable pipedream/required-properties" --include="*.mjs" components/procore*/sources/

Length of output: 2976


Attention: Clarify ESLint Rule Disabling in Event Sources

It appears that disabling the piperdream/required-properties-type and piperdream/required-properties-name rules is a common pattern across all event instant source files. If this is an intentional decision, please add a brief inline comment that explains why these properties are omitted. Otherwise, consider updating the implementation to include the missing type and name properties to conform with standard Pipedream component practices.

  • File: components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (and similar files across the repository)
  • Action: Add an inline rationale if the disables are intentional, or address the missing properties if not.
components/procore/common/resource-names.mjs (1)

1-123: Well-organized resource name constants

Great job creating a centralized module for Procore resource names. The comprehensive list with consistent naming conventions will make the code more maintainable and less error-prone. The comments explaining the source of the constants are also helpful.

components/procore/package.json (3)

3-3: Version increment follows semantic versioning

Increasing from 0.0.4 to 0.1.0 is appropriate for a minor feature release with backward-compatible additions.


5-5: Conversion to ES modules

The change from .js to .mjs for the main entry point aligns with modern JavaScript practices and explicitly indicates ES module usage.


16-17:

✅ Verification successful

Verify uuid package version

The uuid version ^11.1.0 appears unusually high - the latest stable release as of early 2024 was in the 9.x range. Please verify this is the correct version you intend to use.


🏁 Script executed:

#!/bin/bash
# Check latest version of uuid package
npm view uuid versions | tail -n 5

# Check if this version is being used in other Pipedream components
grep -r "\"uuid\":" --include="package.json" components/

Length of output: 283


UUID version confirmed: The version specified in components/procore/package.json (^11.1.0) is valid based on the npm registry evidence. The registry now includes versions in the 11.x range (with 11.1.0 as the latest), so the concern that the version appears unusually high compared to earlier 9.x releases no longer applies.

  • The npm registry output confirms that version 11.1.0 is a valid stable release.
  • The previous assertion regarding an unusually high version is now outdated.
  • Please disregard the suggestion to change the uuid version unless further usage-related issues arise.
components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1)

7-7: Key now matches file/folder name

The component key now correctly matches the folder and file name structure. This resolves the previous issue that was causing the pipeline to fail.

components/procore/actions/create-timesheet/create-timesheet.mjs (2)

27-31: Consider adding explicit date format validation
The timesheet date must comply with Procore’s YYYY-MM-DD format requirements, but the code does not enforce or validate it.


34-37: Add a default parameter object
Unlike other actions, createTimesheet({ projectId, ...args }) does not define a default argument, which may lead to errors when invoked without arguments.

components/procore/actions/create-manpower-log/create-manpower-log.mjs (2)

73-80: Improve error handling
Adding a try-catch block around the createManpowerLog() API call helps surface errors clearly if the request fails.


44-49:

❓ Verification inconclusive

Revisit the data type for numHours
numHours is defined as a string, but it may be more appropriate to model it as an integer or float, especially if it represents the duration of work.

Run the following script to search for usage of numHours and verify whether it is indeed expected to be numeric:


🏁 Script executed:

#!/bin/bash
rg -A4 'numHours'

Length of output: 1414


Revisit the data type for numHours

It’s currently defined as a string (lines 44–49 of components/procore/actions/create-manpower-log/create-manpower-log.mjs), but since it represents the number of hours worked, it might be more appropriate to use a numeric type (e.g., number or float). Please verify whether downstream processes expect a numeric value and consider updating the schema to reflect this if so.

components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (4)

1-3: Imports look good.

These imports from shared modules are set up consistently.


4-10: Event source definition is clear.

The basic configuration (name, key, description, version, type) effectively describes the event source. No issues spotted.


11-15: Resource name method is concise.

Defining the resource name in a separate method aids clarity and reusability. No concerns here.


16-37: Add error handling for the API call.

Although functional, the getDataToEmit method does not handle potential errors when calling app.getPurchaseOrderContract. If this call fails (e.g., due to connectivity or authorization issues), the error will propagate upward and may halt the source’s event emission. A try/catch block (or equivalent error-handling strategy) can help gracefully handle failures.

This recommendation replicates a prior comment from an older revision.

async getDataToEmit(body) {
  const {
    app,
    companyId,
  } = this;
  const {
    resource_id: purchaseOrderContractId,
    project_id: projectId,
  } = body;

-  const resource = await app.getPurchaseOrderContract({
-    companyId,
-    purchaseOrderContractId,
-    params: {
-      project_id: projectId,
-    },
-  });
-  return {
-    ...body,
-    resource,
-  };
+  try {
+    const resource = await app.getPurchaseOrderContract({
+      companyId,
+      purchaseOrderContractId,
+      params: {
+        project_id: projectId,
+      },
+    });
+    return {
+      ...body,
+      resource,
+    };
+  } catch (error) {
+    console.error(`Error fetching purchase order contract details: ${error.message}`);
+    // Return the original webhook payload so we don't disrupt further processing
+    return body;
+  }
}
components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (4)

1-3: Imports look good.

Imports from shared modules and resource names are consistent with the other event sources.


4-10: Event source configuration is straightforward.

No issues found with the basic event source settings.


11-15: Resource name is well-defined.

Use of the shared resource-names module ensures consistency.


16-38: Add error handling for the API call.

Similar to the purchase order event source, the getDataToEmit method should handle potential errors from app.getChangeOrderPackage. An unhandled error can crash the webhook flow.

This repeats a prior suggestion from older commits.

async getDataToEmit(body) {
  const {
    app,
    companyId,
  } = this;
  const {
    resource_id: changeOrderPackageId,
    project_id: projectId,
  } = body;

-  const resource = await app.getChangeOrderPackage({
-    companyId,
-    changeOrderPackageId,
-    params: {
-      project_id: projectId,
-    },
-  });
-  return {
-    ...body,
-    resource,
-  };
+  try {
+    const resource = await app.getChangeOrderPackage({
+      companyId,
+      changeOrderPackageId,
+      params: {
+        project_id: projectId,
+      },
+    });
+    return {
+      ...body,
+      resource,
+    };
+  } catch (error) {
+    console.error(`Error fetching change order package details: ${error.message}`);
+    return body;
+  }
}
components/procore/procore.app.mjs (9)

1-5: Imports and support modules are appropriate.

The usage of axios from @pipedream/platform, along with internal constants, resource names, and utilities, is consistent and clear.


6-8: Application definition is well-formed.

Specifying type: "app" and app: "procore" is straightforward and aligns with typical patterns.


9-172: Property definitions are thorough and user-friendly.

Each prop definition (companyId, projectId, etc.) is described well, returning dynamic options with pagination. This makes for a good user experience in the UI.


186-202: getUrl and getHeaders are neatly structured.

They concisely establish the base URL and request headers, accommodating the environment, versioning, and optional company scope. No issues noted.


203-211: Centralized request logic improves maintainability.

The makeRequest method standardizes HTTP requests, which is helpful for consistency across different methods. Good approach.


224-236: Listing companies and projects with versioned endpoints.

The usage of versionPath: constants.VERSION_PATH.V1_1 for the /projects endpoint is correct, reflecting the Procore API’s versioning approach.


262-269: Use plural endpoint for prime contracts.

The Procore API documentation indicates the path for prime contracts is /prime_contracts/. The current code uses a singular form (/prime_contract/), which may cause 404 or other errors.

This echoes an earlier comment provided in a previous review.

getPrimeContract({
  primeContractId, ...args
} = {}) {
  return this.makeRequest({
-    path: `/prime_contract/${primeContractId}`,
+    path: `/prime_contracts/${primeContractId}`,
    ...args,
  });
},

337-381: Add robust error handling to pagination logic.

In getIterations, an error in resourcesFn currently escapes unhandled. Implementing a try/catch around the request would prevent the entire iteration from silently failing and provide a clearer error message.

This recommendation has been previously suggested.

while (true) {
+  try {
    const response =
      await resourcesFn({
        ...resourcesFnArgs,
        params: {
          ...resourcesFnArgs?.params,
          page,
          per_page: constants.DEFAULT_LIMIT,
        },
      });

    const nextResources = resourceName
      ? utils.getNestedProperty(response, resourceName)
      : response;

    if (!nextResources?.length) {
      console.log("No more resources found");
      return;
    }

    for (const resource of nextResources) {
      yield resource;
      resourcesCount += 1;

      if (resourcesCount >= max) {
        console.log("Reached max resources");
        return;
      }
    }

    if (nextResources.length < constants.DEFAULT_LIMIT) {
      console.log("No next page found");
      return;
    }

    page += 1;
+  } catch (error) {
+    console.error(`Error fetching resources on page ${page}: ${error.message}`);
+    throw error; // Avoid silent failures
+  }
}

382-387: Iterating helper is consistent.

The paginate method properly wraps the generator for convenience. This is a good pattern to unify iteration logic.

components/procore/common/constants.mjs (3)

50-65: LGTM overall.

Exporting a consolidated object of constants simplifies cross-module references and promotes maintainability. No immediate issues detected here.


7-7:

❓ Verification inconclusive

Consider validating placeholder usage.

BASE_URL relies on string placeholders (ENV_PLACEHOLDER and VERSION_PLACEHOLDER). Ensure these placeholders are always replaced with valid values at runtime to avoid unexpected API call failures or malformed requests.


🏁 Script executed:

#!/bin/bash
# Description: Check all usage occurrences of BASE_URL to ensure placeholders are replaced.
rg -A 3 "BASE_URL"

Length of output: 66344


Action: Validate both runtime replacements for ENV_PLACEHOLDER and VERSION_PLACEHOLDER.

The search output confirms that in components/procore/common/constants.mjs the BASE_URL is defined with placeholders. In the corresponding app code (e.g. in components/procore/procore.app.mjs), we see the ENV_PLACEHOLDER is replaced via a string replacement, but there’s no similar evidence that VERSION_PLACEHOLDER is being substituted at runtime. Please review the usage to ensure that both placeholders are always updated with valid values (or fallbacks) to avoid malformed API URLs or unexpected API call failures.

  • Verify that runtime logic properly replaces both ENV_PLACEHOLDER and VERSION_PLACEHOLDER.
  • If not, consider adding validations or default values for any missing substitutions.

31-37:

✅ Verification successful

Ensure that constant names align with their usage.

While these constants (TOKEN, IS_FIRST_RUN, LAST_DATE_AT) indicate keys for storing state, confirm they're actually used in the codebase with consistent naming. Unused or misnamed constants can lead to confusion and technical debt.


🏁 Script executed:

#!/bin/bash
# Description: Verify usage references for these constants in the codebase.
rg "(TOKEN|IS_FIRST_RUN|LAST_DATE_AT)"

Length of output: 8824


Constants Naming Verification Complete

The constants TOKEN, IS_FIRST_RUN, and LAST_DATE_AT defined in components/procore/common/constants.mjs are used consistently in the procore components (e.g., they are referenced with the expected string values "token", "isFirstRun", and "lastDateAt"). This confirms that in the context of procore, the naming aligns with their usage.

  • Usage in procore state handling (e.g., in /sources/common/webhook.mjs) correctly refers to these constants.
  • Note: There is a slight naming discrepancy in another module—components/dropinblog/common/constants.mjs defines LAST_DATE_AT as "lastCreatedAt". Consider reviewing this for global consistency if needed.
components/procore/sources/common/webhook.mjs (3)

176-178: Remind implementers to provide the resource name.

getResourceName() also throws a ConfigurationError. Ensure that each derived module implements it to avoid errors when creating webhooks.


153-157:

✅ Verification successful

Clarify placeholder methods.

generateMeta() throws a ConfigurationError, indicating child classes must override it. This is a valid design choice, but ensure each child source indeed overrides it to prevent runtime errors.


🏁 Script executed:

#!/bin/bash
# Description: Verify that all derived classes override generateMeta properly.
rg -A 5 "generateMeta"

Length of output: 105963


Ensure all sources override the placeholder generateMeta() method

The implementation in components/procore/sources/common/webhook.mjs is an intentional placeholder that throws a ConfigurationError to signal that derived classes must provide their own implementation. Our verification confirms that almost all child source files override generateMeta() appropriately. Please ensure that any new source extending this base also provides a custom implementation to avoid runtime errors.


79-81:

❓ Verification inconclusive

Confirm token generation meets security requirements.

While generating a base64-encoded UUID usually provides enough randomness, verify that this approach satisfies security requirements. If stronger entropy is needed, consider using a cryptographically secure token.


🏁 Script executed:

#!/bin/bash
# Description: Check references to token usage; ensure no stricter cryptography is needed.
rg "token" 

Length of output: 67317


Token Generation Security – Confirm Cryptographic Robustness

After reviewing the token usage across the codebase, the pattern used in components/procore/sources/common/webhook.mjs—generating a UUID and encoding it in base64—is in line with how tokens are handled elsewhere. That said, please verify that the uuid() function is indeed producing a cryptographically secure (v4) UUID. If this token is used in a security‐critical context, you might consider switching to a method such as Node’s built-in crypto.randomBytes to provide stronger entropy.

Copy link
Collaborator

@GTFalcao GTFalcao left a comment

Choose a reason for hiding this comment

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

I left a few comments regarding prop descriptions.

Also, a reminder: deleted components should be unpublished (or redirected to a new one) when this is merged

@jcortes jcortes force-pushed the procore-new-components branch from a294ed8 to 6800610 Compare March 10, 2025 22:02
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (5)
components/procore/actions/create-timesheet/create-timesheet.mjs (3)

34-41: 🛠️ Refactor suggestion

Add default parameter and improve parameter handling

Unlike other action methods, this one doesn't provide a default empty object for parameters, which could lead to errors if called without arguments.

Apply this improvement:

  createTimesheet({
-   projectId, ...args
+   $, projectId, companyId, data, ...args
-  }) {
+  } = {}) {
    return this.app.post({
      path: `/projects/${projectId}/timesheets`,
+     $,
+     companyId,
+     data,
      ...args,
    });
  },

27-31: 🛠️ Refactor suggestion

Validate Timesheet Date to Use ISO 8601 Format

The current implementation lacks explicit validation for the timesheet's date property. Procore expects dates to adhere to the ISO 8601 standard.

Add validation in the run method:

  async run({ $ }) {
    const {
      createTimesheet,
      companyId,
      projectId,
      date,
    } = this;

+   // Validate date is in ISO 8601 format (YYYY-MM-DD)
+   const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
+   if (!dateRegex.test(date)) {
+     throw new Error("Date must be in ISO 8601 format (YYYY-MM-DD)");
+   }

    const response = await createTimesheet({

43-63: 🛠️ Refactor suggestion

Implement error handling

Currently, if the API call fails, no exception is caught, potentially causing a silent failure.

Implement error handling:

  async run({ $ }) {
    const {
      createTimesheet,
      companyId,
      projectId,
      date,
    } = this;

+   try {
      const response = await createTimesheet({
        $,
        companyId,
        projectId,
        data: {
          timesheet: {
            date,
          },
        },
      });
      $.export("$summary", `Successfully created timesheet with ID \`${response.id}\`.`);
      return response;
+   } catch (error) {
+     $.export("error", error);
+     throw new Error(`Failed to create timesheet: ${error.message}`);
+   }
  },
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

73-80: 🛠️ Refactor suggestion

Add better error handling for API failures

Currently, if an error occurs during createManpowerLog(), the action might silently fail at runtime.

Implement error handling:

  createManpowerLog({
    projectId, ...args
  } = {}) {
+   try {
      return this.app.post({
        path: `/projects/${projectId}/manpower_logs`,
        ...args,
      });
+   } catch (err) {
+     throw new Error(`Error creating manpower log: ${err.message}`);
+   }
  },
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

38-44: 🛠️ Refactor suggestion

Validate Timestamp Presence and Validity

In the generateMeta function, if body.timestamp is missing or not in a valid format, new Date(body.timestamp).getTime() will yield NaN, which can cause issues downstream.

Add validation for the timestamp:

  generateMeta(body) {
+   const timestamp = body.timestamp 
+     ? new Date(body.timestamp).getTime() 
+     : Date.now();
+     
+   // Check if timestamp is valid
+   const ts = isNaN(timestamp) ? Date.now() : timestamp;
    
    return {
      id: body.id,
      summary: `New Prime Contract Event: ${body.resource_id}`,
-     ts: new Date(body.timestamp).getTime(),
+     ts,
    };
  },
🧹 Nitpick comments (12)
components/procore_sandbox/common/utils.mjs (5)

3-33: Consider optimizing the reduce operation for performance

The buildPropDefinitions function uses spread syntax within a reducer, which can lead to O(n²) time complexity as flagged by static analysis. For larger objects, this could cause performance issues.

function buildPropDefinitions({
  app = {}, props = {},
}) {
-  return Object.entries(props)
-    .reduce((newProps, [
-      key,
-      prop,
-    ]) => {
-      if (!prop.propDefinition) {
-        return {
-          ...newProps,
-          [key]: prop,
-        };
-      }
-
-      const [
-        , ...propDefinitionItems
-      ] = prop.propDefinition;
-
-      return {
-        ...newProps,
-        [key]: {
-          ...prop,
-          propDefinition: [
-            app,
-            ...propDefinitionItems,
-          ],
-        },
-      };
-    }, {});
+  const newProps = {};
+  for (const [key, prop] of Object.entries(props)) {
+    if (!prop.propDefinition) {
+      newProps[key] = prop;
+      continue;
+    }
+    
+    const [, ...propDefinitionItems] = prop.propDefinition;
+    
+    newProps[key] = {
+      ...prop,
+      propDefinition: [
+        app,
+        ...propDefinitionItems,
+      ],
+    };
+  }
+  return newProps;
}
🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)


35-50: Clarify the purpose of the unused variable

The procore variable is explicitly marked as unused with an eslint comment. If this variable is truly unnecessary, consider removing the renaming.

function getAppProps(component = {}) {
  const {
-    // eslint-disable-next-line no-unused-vars
-    app: procore,
+    app: _, // Ignoring the component's app property as we use the imported app
    ...otherProps
  } = component.props;
  return {
    props: {
      app,
      ...buildPropDefinitions({
        app,
        props: otherProps,
      }),
    },
  };
}

1-55: Add JSDoc comments for better function documentation

The utility functions lack JSDoc comments explaining their purpose, parameters, and return values, which would improve code readability and maintainability.

import app from "../procore_sandbox.app.mjs";

+/**
+ * Builds property definitions by ensuring each prop with a propDefinition
+ * includes the app as the first element in the propDefinition array.
+ *
+ * @param {Object} options - The options object
+ * @param {Object} options.app - The app object to include in prop definitions
+ * @param {Object} options.props - The properties to process
+ * @return {Object} A new object with processed properties
+ */
function buildPropDefinitions({
  app = {}, props = {},
}) {
  // ... existing implementation
}

+/**
+ * Creates a new props object that includes the imported app and 
+ * processed properties from the component.
+ *
+ * @param {Object} component - The component object
+ * @param {Object} component.props - The component's properties
+ * @return {Object} An object with a props property containing the app and processed properties
+ */
function getAppProps(component = {}) {
  // ... existing implementation
}

export default {
  buildPropDefinitions,
  getAppProps,
};
🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)


1-55: Add error handling for edge cases

The functions lack error handling for potential edge cases, such as if prop.propDefinition exists but isn't an array, or if component.props is undefined.

function buildPropDefinitions({
  app = {}, props = {},
}) {
  return Object.entries(props)
    .reduce((newProps, [
      key,
      prop,
    ]) => {
-      if (!prop.propDefinition) {
+      if (!prop.propDefinition || !Array.isArray(prop.propDefinition)) {
        return {
          ...newProps,
          [key]: prop,
        };
      }

      const [
        , ...propDefinitionItems
      ] = prop.propDefinition;

      return {
        ...newProps,
        [key]: {
          ...prop,
          propDefinition: [
            app,
            ...propDefinitionItems,
          ],
        },
      };
    }, {});
}

function getAppProps(component = {}) {
+  if (!component || !component.props) {
+    return { props: { app } };
+  }
+
  const {
    // eslint-disable-next-line no-unused-vars
    app: procore,
    ...otherProps
  } = component.props;
  return {
    props: {
      app,
      ...buildPropDefinitions({
        app,
        props: otherProps,
      }),
    },
  };
}
🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)


1-2: Consider adding a comment explaining the purpose of this module

Adding a file-level comment would help developers understand the overall purpose of this utility module within the Procore integration.

+/**
+ * Utility functions for Procore component properties management.
+ * These utilities help standardize how component properties are handled
+ * across different Procore integration components.
+ */
import app from "../procore_sandbox.app.mjs";
components/procore/actions/create-submittal/create-submittal.mjs (1)

28-32: Consider adding validation for the required number field

The number field is required but lacks validation to ensure it's not empty before submission.

Add validation in the run method:

  async run({ $ }) {
    const {
      createSubmittal,
      companyId,
      projectId,
      number,
      description,
      title,
      type,
      isPrivate,
      revision,
      locationId,
    } = this;
+   
+   if (!number?.trim()) {
+     throw new Error("Number is required for creating a submittal");
+   }
components/procore/actions/create-manpower-log/create-manpower-log.mjs (2)

32-37: Add validation for datetime format

The datetime field description specifies a format (2025-04-01T00:00:00Z), but there's no validation to ensure the input conforms to this format.

Add validation in the run method:

  async run({ $ }) {
    const {
      createManpowerLog,
      companyId,
      projectId,
      datetime,
      notes,
      numWorkers,
      numHours,
      userId,
      locationId,
    } = this;

+   // Validate datetime format if provided
+   if (datetime) {
+     const isoDatetimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
+     if (!isoDatetimeRegex.test(datetime)) {
+       throw new Error("Datetime must be in ISO 8601 format (YYYY-MM-DDThh:mm:ssZ)");
+     }
+   }

    const response = await createManpowerLog({

44-49: Consider changing numHours type to numeric

numHours is defined as a string type, but it represents a numeric value (hours). This might cause issues if calculations are performed on this value.

Change the type to numeric:

  numHours: {
-   type: "string",
+   type: "number",
    label: "Number Of Hours",
    description: "The number of hours for each worker",
    optional: true,
  },
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

42-48: Handle invalid timestamps.
If body.timestamp is missing or invalid, new Date(body.timestamp).getTime() might yield NaN. Consider a safer fallback:

generateMeta(body) {
  return {
    id: body.id,
    summary: `New Budget Snapshot Event: ${body.resource_id}`,
-   ts: new Date(body.timestamp).getTime(),
+   ts: isNaN(new Date(body.timestamp).getTime())
+     ? Date.now()
+     : new Date(body.timestamp).getTime(),
  };
},
components/procore/sources/common/webhook.mjs (3)

35-63: Consider supporting multi-page project listings.

Currently, additionalProps() fetches only one page of data. If users have more than 100 projects, they must manually page through them. For a smoother user experience, consider iterating over all pages or allowing a “Load more” mechanism.


64-120: Handle partial trigger creation failures.

When bulk-creating triggers, a single trigger failure may cause partial success. Consider adding checks or a fallback to ensure the overall flow is more robust, especially if one or more triggers cannot be created.


220-237: Add test coverage for the run() method.

The request handling logic, token validation, and response codes appear correct, but there are no tests to confirm they behave as intended. Consider adding tests to verify the success and failure paths.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between a294ed8 and 6800610.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (1 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
  • components/procore_sandbox/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore_sandbox/common/utils.mjs (1 hunks)
  • components/procore_sandbox/package.json (1 hunks)
  • components/procore_sandbox/procore_sandbox.app.mjs (1 hunks)
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/new-event/new-event.js
  • components/procore/sources/purchase-order/purchase-order.js
  • components/procore/sources/rfi/rfi.js
  • components/procore/sources/common.js
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/sources/budget-snapshot/budget-snapshot.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
  • components/procore/sources/submittal/submittal.js
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/procore.app.js
🚧 Files skipped from review as they are similar to previous changes (28)
  • components/procore_sandbox/package.json
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs
  • components/procore_sandbox/procore_sandbox.app.mjs
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore/common/resource-names.mjs
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore/package.json
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs
  • components/procore/common/utils.mjs
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore/actions/create-rfi/create-rfi.mjs
  • components/procore_sandbox/actions/create-incident/create-incident.mjs
  • components/procore/actions/create-incident/create-incident.mjs
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/sources/new-event-instant/new-event-instant.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/procore_sandbox/common/utils.mjs

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
🔇 Additional comments (8)
components/procore/procore.app.mjs (2)

262-269: Use plural endpoint for prime contracts.
A previous review recommended using the plural form /prime_contracts/ for this endpoint. The current code still uses the singular form, which might not match Procore's REST API specification.

- path: `/prime_contract/${primeContractId}`,
+ path: `/prime_contracts/${primeContractId}`,

337-381: Add robust error handling to pagination logic.
A previous review suggested wrapping the call to resourcesFn in a try/catch block within the while (true) loop to gracefully handle potential network or API errors.

components/procore/common/constants.mjs (1)

1-66: All constants look good!
The constants here are clearly defined, consistently named, and well-organized for maintainability.

components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

26-35: Consider adding error handling to pagination.
As seen in similar modules, if the paginate call fails, the error might bubble up. A try/catch could safeguard the event emission process.

components/procore/sources/common/webhook.mjs (4)

1-34: Looks solid overall.

The imports and base props (including webhookType) are clearly defined. This section appears functionally correct.


121-152: Deactivation logic is consistent.

Deleting triggers and the hook in sequence is correct. No issues or improvements stand out here.


155-157: generateMeta() is still unimplemented.

This repeats a prior note: since this method is invoked (line 218) but throws an error, any actual webhook event will cause a runtime error. Either provide a basic implementation or override it in a subclass.


176-178: getResourceName() is still unimplemented.

This method is required (line 103) to build triggers for the chosen resource. Overriding it with the correct resource name or implementing logic dynamically would avoid the ConfigurationError.

@jcortes jcortes changed the title [Components] procore - new components - untested [Components] procore - new components Mar 10, 2025
luancazarine
luancazarine previously approved these changes Mar 11, 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, LGTM! Ready for QA!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (11)
components/procore_sandbox/common/utils.mjs (3)

1-55: Great utility functions for prop management, with minor performance concerns.

The utility functions effectively handle component properties and app context integration. However, there are performance optimizations that could be made.

The static analysis tool correctly identified potential performance issues with using the spread operator in reducers. This can lead to O(n²) time complexity when working with large objects:

function buildPropDefinitions({
  app = {}, props = {},
}) {
  return Object.entries(props)
    .reduce((newProps, [
      key,
      prop,
    ]) => {
      if (!prop.propDefinition) {
-        return {
-          ...newProps,
-          [key]: prop,
-        };
+        newProps[key] = prop;
+        return newProps;
      }

      const [
        , ...propDefinitionItems
      ] = prop.propDefinition;

-      return {
-        ...newProps,
-        [key]: {
-          ...prop,
-          propDefinition: [
-            app,
-            ...propDefinitionItems,
-          ],
-        },
-      };
+      newProps[key] = {
+        ...prop,
+        propDefinition: [
+          app,
+          ...propDefinitionItems,
+        ],
+      };
+      return newProps;
    }, {});
}

This approach mutates the accumulator directly, which is more efficient for large objects.

🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)


35-50: Consider documenting the purpose of the utility functions.

The getAppProps function swaps out the component's app property with the imported app, which is an important behavior that should be documented.

/**
 * Processes component props by replacing the component's app property 
 * with the imported app instance and processing all other props 
 * through buildPropDefinitions.
 * @param {Object} component - The component with props to process
 * @returns {Object} An object with processed props
 */
function getAppProps(component = {}) {

36-40: Unused variable could be simplified.

The eslint comment acknowledges that procore is unused, but there's a cleaner approach.

  const {
-    // eslint-disable-next-line no-unused-vars
-    app: procore,
+    app: _, // Explicitly ignore
    ...otherProps
  } = component.props;

Or even simpler:

  const {
-    // eslint-disable-next-line no-unused-vars
-    app: procore,
+    app,  // We can remove this line entirely if we don't need it
    ...otherProps
  } = component.props;
components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1)

36-40: Consider improving error handling

The current approach of simply logging errors and returning the original body is functional but basic. Consider adding more detailed error reporting or retrying mechanisms for more robust webhook processing.

      } catch (error) {
-        console.log(error.message || error);
+        console.error(`Failed to fetch submittal data: ${error.message || error}`);
+        // Optional: Add monitoring or reporting for failed webhook enrichment
         return body;
      }
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

44-49: Consider changing numHours type to numeric

The numHours property is defined as a string, but it represents a numeric value. Using a numeric type would be more appropriate for this field.

    numHours: {
-      type: "string",
+      type: "number",
      label: "Number Of Hours",
      description: "The number of hours for each worker",
      optional: true,
    },
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

38-41: Enhance error logging with detailed information

The current error handling simply logs the error message. Consider adding more context to help with debugging.

      } catch (error) {
-        console.log(error.message || error);
+        console.error(`Failed to fetch prime contract data for ID ${primeContractId} in project ${projectId}: ${error.message || error}`);
         return body;
      }
components/procore/procore.app.mjs (1)

174-176: Make the environment configurable for improved testing flexibility
Right now, the getEnvironment() method always returns production. If you plan on expanding support for sandbox or other environments, consider making this user-configurable or determined by environment variables.

components/procore/sources/common/webhook.mjs (4)

35-63: Handle large project lists gracefully.
Currently, the options method fetches a maximum of 100 projects at once. Consider adding pagination logic or caching if you anticipate more than 100 projects, to avoid partial listings.


65-120: Consider robust error handling during activation.
The activate hook lacks fallback or rollback logic if createWebhooksHook() or bulkCreateWebhooksTriggers() partially fails (e.g., some triggers fail to create). Consider a try/catch block or transaction-like approach to handle partial failures more gracefully.


121-152: Consider robust error handling during deactivation.
Similar to the activate hook, the deactivate hook relies on bulkDeleteWebhooksTriggers() and deleteWebhooksHook(). If there is a partial failure, it could leave inconsistent webhook data. Adding a fallback or retry approach would improve reliability.


158-163: Consider storing tokens securely.
Storing the token as plain text in the database might be fine for a small integration, but for better security, you may want to encrypt or obfuscate it, or move it to an environment variable if needed.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6800610 and ee74d49.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (1 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
  • components/procore_sandbox/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore_sandbox/common/utils.mjs (1 hunks)
  • components/procore_sandbox/package.json (1 hunks)
  • components/procore_sandbox/procore_sandbox.app.mjs (1 hunks)
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/budget-snapshot/budget-snapshot.js
  • components/procore/sources/common.js
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/sources/submittal/submittal.js
  • components/procore/procore.app.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
  • components/procore/sources/rfi/rfi.js
  • components/procore/sources/new-event/new-event.js
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/sources/purchase-order/purchase-order.js
🚧 Files skipped from review as they are similar to previous changes (28)
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs
  • components/procore_sandbox/package.json
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs
  • components/procore_sandbox/actions/create-incident/create-incident.mjs
  • components/procore/sources/new-event-instant/new-event-instant.mjs
  • components/procore_sandbox/procore_sandbox.app.mjs
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs
  • components/procore/common/utils.mjs
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs
  • components/procore/actions/create-rfi/create-rfi.mjs
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs
  • components/procore/package.json
  • components/procore/common/resource-names.mjs
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs
  • components/procore/actions/create-incident/create-incident.mjs
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs
  • components/procore/actions/create-timesheet/create-timesheet.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/procore_sandbox/common/utils.mjs

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (14)
components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1)

43-48: Validate timestamp before conversion

If body.timestamp is missing or in an invalid format, new Date(body.timestamp).getTime() will return NaN, which could cause issues downstream.

    generateMeta(body) {
      return {
        id: body.id,
        summary: `New Submittal Event: ${body.resource_id}`,
-        ts: new Date(body.timestamp).getTime(),
+        ts: body.timestamp ? new Date(body.timestamp).getTime() : Date.now(),
      };
    },
components/procore/actions/create-submittal/create-submittal.mjs (1)

87-117: Code implementation looks good but lacks error handling

The component follows a good pattern for Procore API integration, matching the structure of other actions. However, there's no error handling if the API call fails, which could result in unhandled exceptions.

Consider implementing error handling for the API call:

  async run({ $ }) {
    const {
      createSubmittal,
      companyId,
      projectId,
      number,
      description,
      title,
      type,
      isPrivate,
      revision,
      locationId,
    } = this;
+   try {
      const response = await createSubmittal({
        $,
        companyId,
        projectId,
        data: {
          submittal: {
            number,
            description,
            title,
            type,
            private: isPrivate,
            revision,
            location_id: locationId,
          },
        },
      });
      $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);
      return response;
+   } catch (error) {
+     $.export("error", error);
+     throw new Error(`Failed to create submittal: ${error.message}`);
+   }
  },
components/procore/actions/create-manpower-log/create-manpower-log.mjs (2)

73-80: Add better error handling for API failures.

Currently, if an error occurs during createManpowerLog(), the action might silently fail at runtime. Consider wrapping API calls in robust try-catch blocks or returning informative errors.

+    try {
       return this.app.post({
         path: `/projects/${projectId}/manpower_logs`,
         ...args,
       });
+    } catch (err) {
+      throw new Error(`Error creating manpower log: ${err.message}`);
+    }

82-112: Add error handling to run method

The run method lacks error handling for the API call, which could result in unhandled exceptions.

Implement error handling:

  async run({ $ }) {
    const {
      createManpowerLog,
      companyId,
      projectId,
      datetime,
      notes,
      numWorkers,
      numHours,
      userId,
      locationId,
    } = this;

+   try {
      const response = await createManpowerLog({
        $,
        companyId,
        projectId,
        data: {
          manpower_log: {
            datetime,
            notes,
            num_workers: numWorkers,
            num_hours: numHours,
            user_id: userId,
            location_id: locationId,
          },
        },
      });
      $.export("$summary", `Successfully created manpower log with ID \`${response.id}\`.`);
      return response;
+   } catch (error) {
+     $.export("error", error);
+     throw new Error(`Failed to create manpower log: ${error.message}`);
+   }
  },
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

43-49: Validate Timestamp Presence and Validity

In the function defined in components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs, the timestamp is derived via:

ts: new Date(body.timestamp).getTime(),

If body.timestamp is missing or not in a valid format, this expression will yield NaN, which can cause issues downstream.

Implement a safeguard:

    generateMeta(body) {
      return {
        id: body.id,
        summary: `New Prime Contract Event: ${body.resource_id}`,
-        ts: new Date(body.timestamp).getTime(),
+        ts: body.timestamp ? new Date(body.timestamp).getTime() : Date.now(),
      };
    },
components/procore/procore.app.mjs (2)

262-269: Use plural /prime_contracts/ endpoint for consistency with Procore docs
The Procore API documentation indicates that prime contracts use the plural endpoint /prime_contracts/. Retaining the singular form risks incorrect calls or 404 errors.

- path: `/prime_contract/${primeContractId}`,
+ path: `/prime_contracts/${primeContractId}`,

337-381: Add robust error handling for pagination
A try/catch around the paginated requests helps prevent unhandled exceptions if any given request fails, aligning with best practices for resilient systems.

 while (true) {
+  try {
     const response = await resourcesFn({
       ...
     });
     ...
+  } catch (error) {
+    console.error(`Error fetching resources on page ${page}: ${error}`);
+    throw error;
+  }
 }
components/procore/common/constants.mjs (1)

1-66: Looks good!
All constants are clearly named, well-organized, and appear consistent with your usage patterns. No issues flagged.

components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

26-45: Great addition of try/catch error handling
This ensures that API errors won't crash the entire flow, improving overall reliability for the source.

components/procore/sources/common/webhook.mjs (5)

1-2: All good on imports.
No concerns here. The imports for UUID and ConfigurationError look fine.


6-33: Props look solid.
The defined props are comprehensive and logically grouped. The previous mismatch between whebhookType and this.webhookType appears resolved.


155-157: Implement the generateMeta method to avoid runtime errors.
Currently, calling this method (line 218) will throw a ConfigurationError. Provide an implementation to emit event metadata properly.


177-178: Define or override getResourceName.
This method is required to specify the resource for event triggers. As written, it will always throw an error if invoked.


220-236: Request validation and response logic look correct.
The run method properly checks the bearer token, returns 401 if unauthorized, and 200 otherwise, then emits the parsed payload. This meets basic webhook best practices.

@jcortes jcortes force-pushed the procore-new-components branch from ee74d49 to e118338 Compare March 13, 2025 16:18
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 (6)
components/procore/actions/create-submittal/create-submittal.mjs (1)

87-118: 🛠️ Refactor suggestion

Add error handling to prevent unhandled exceptions

The run method lacks error handling for the API call, which could result in unhandled exceptions if the request fails.

async run({ $ }) {
  const {
    createSubmittal,
    companyId,
    projectId,
    number,
    description,
    title,
    type,
    isPrivate,
    revision,
    locationId,
  } = this;
+ try {
    const response = await createSubmittal({
      $,
      companyId,
      projectId,
      data: {
        submittal: {
          number,
          description,
          title,
          type,
          private: isPrivate,
          revision,
          location_id: locationId,
        },
      },
    });
    $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);
    return response;
+ } catch (error) {
+   $.export("error", error);
+   throw new Error(`Failed to create submittal: ${error.message}`);
+ }
},
components/procore/actions/create-timesheet/create-timesheet.mjs (3)

34-41: 🛠️ Refactor suggestion

Add default parameter and improve parameter handling

Unlike other action methods, this one doesn't provide a default empty object for parameters, which could lead to errors if called without arguments.

createTimesheet({
-  projectId, ...args
+  $, projectId, companyId, data, ...args
-}) {
+} = {}) {
  return this.app.post({
    path: `/projects/${projectId}/timesheets`,
+   $,
+   companyId,
+   data,
    ...args,
  });
},

27-31: 🛠️ Refactor suggestion

Validate timesheet date to use ISO 8601 format

The current implementation lacks explicit validation for the date property. Procore expects dates to adhere to the ISO 8601 standard (YYYY-MM-DD).

While the description includes an example format, consider adding validation to ensure any provided date complies with the required format.


43-63: 🛠️ Refactor suggestion

Add error handling to prevent unhandled exceptions

The run method lacks error handling for the API call, which could result in unhandled exceptions if the request fails.

async run({ $ }) {
  const {
    createTimesheet,
    companyId,
    projectId,
    date,
  } = this;

+ try {
    const response = await createTimesheet({
      $,
      companyId,
      projectId,
      data: {
        timesheet: {
          date,
        },
      },
    });
    $.export("$summary", `Successfully created timesheet with ID \`${response.id}\`.`);
    return response;
+ } catch (error) {
+   $.export("error", error);
+   throw new Error(`Failed to create timesheet: ${error.message}`);
+ }
},
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

82-112: 🛠️ Refactor suggestion

Add error handling to run method

The run method lacks error handling for the API call, which could result in unhandled exceptions.

async run({ $ }) {
  const {
    createManpowerLog,
    companyId,
    projectId,
    datetime,
    notes,
    numWorkers,
    numHours,
    userId,
    locationId,
  } = this;

+ try {
    const response = await createManpowerLog({
      $,
      companyId,
      projectId,
      data: {
        manpower_log: {
          datetime,
          notes,
          num_workers: numWorkers,
          num_hours: numHours,
          user_id: userId,
          location_id: locationId,
        },
      },
    });
    $.export("$summary", `Successfully created manpower log with ID \`${response.id}\`.`);
    return response;
+ } catch (error) {
+   $.export("error", error);
+   throw new Error(`Failed to create manpower log: ${error.message}`);
+ }
},
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

43-48: 🛠️ Refactor suggestion

Validate timestamp presence and validity

In the generateMeta function, the timestamp is derived via new Date(body.timestamp).getTime(). If body.timestamp is missing or not in a valid format, this expression will yield NaN, which can cause issues downstream.

generateMeta(body) {
  return {
    id: body.id,
    summary: `New Prime Contract Event: ${body.resource_id}`,
-   ts: new Date(body.timestamp).getTime(),
+   ts: body.timestamp ? new Date(body.timestamp).getTime() : Date.now(),
  };
},
🧹 Nitpick comments (7)
components/procore_sandbox/common/utils.mjs (4)

3-33: Consider optimizing the buildPropDefinitions function to avoid O(n²) complexity.

The function currently uses spread syntax on accumulators within the reduce operation, which can lead to O(n²) time complexity as the number of properties grows. This issue is flagged by the Biome static analysis tool.

function buildPropDefinitions({
  app = {}, props = {},
}) {
-  return Object.entries(props)
-    .reduce((newProps, [
-      key,
-      prop,
-    ]) => {
-      if (!prop.propDefinition) {
-        return {
-          ...newProps,
-          [key]: prop,
-        };
-      }
-
-      const [
-        , ...propDefinitionItems
-      ] = prop.propDefinition;
-
-      return {
-        ...newProps,
-        [key]: {
-          ...prop,
-          propDefinition: [
-            app,
-            ...propDefinitionItems,
-          ],
-        },
-      };
-    }, {});
+  const newProps = {};
+  
+  Object.entries(props).forEach(([key, prop]) => {
+    if (!prop.propDefinition) {
+      newProps[key] = prop;
+      return;
+    }
+    
+    const [, ...propDefinitionItems] = prop.propDefinition;
+    
+    newProps[key] = {
+      ...prop,
+      propDefinition: [
+        app,
+        ...propDefinitionItems,
+      ],
+    };
+  });
+  
+  return newProps;
}

This refactored approach avoids the accumulator spread operations while maintaining the same functionality.

🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)


35-50: Potentially misleading variable renaming in destructuring.

The function renames app to procore during destructuring, but then imports and uses a different app instance. This could cause confusion for developers trying to understand the code flow.

function getAppProps(component = {}) {
  const {
-    // eslint-disable-next-line no-unused-vars
-    app: procore,
+    app: _, // Explicitly showing we're ignoring the component's app prop
    ...otherProps
  } = component.props;
  return {
    props: {
      app, // Using the imported app instance
      ...buildPropDefinitions({
        app,
        props: otherProps,
      }),
    },
  };
}

1-1: Consider making the imported app configurable.

The utility functions are tightly coupled to the specific imported app instance, which could limit reusability across different app variations or testing scenarios.

-import app from "../procore_sandbox.app.mjs";
+import defaultApp from "../procore_sandbox.app.mjs";

function buildPropDefinitions({
  app = {}, props = {},
}) {
  // Function implementation remains the same
}

-function getAppProps(component = {}) {
+function getAppProps(component = {}, customApp = defaultApp) {
  const {
    // eslint-disable-next-line no-unused-vars
    app: procore,
    ...otherProps
  } = component.props;
  return {
    props: {
-      app,
+      app: customApp,
      ...buildPropDefinitions({
-        app,
+        app: customApp,
        props: otherProps,
      }),
    },
  };
}

This change would make the utilities more flexible while maintaining backward compatibility.

Also applies to: 35-50


41-48: Add error handling for edge cases.

The utility functions don't handle potential edge cases, such as when component or component.props are undefined or when propDefinition is not an array.

Consider adding validation to ensure robustness:

function getAppProps(component = {}) {
+  if (!component || typeof component !== 'object') {
+    return { props: { app } };
+  }
+
  const {
    // eslint-disable-next-line no-unused-vars
    app: procore,
    ...otherProps
-  } = component.props;
+  } = component.props || {};
  return {
    props: {
      app,
      ...buildPropDefinitions({
        app,
        props: otherProps,
      }),
    },
  };
}

Similarly in buildPropDefinitions, consider adding validation for prop.propDefinition to ensure it's an array before destructuring.

components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

32-37: Validate datetime format in ISO 8601

While the description includes an example format (2025-04-01T00:00:00Z), the component doesn't validate that the provided datetime adheres to the expected ISO 8601 format.

Consider adding validation or a more detailed description to ensure users provide correctly formatted datetime strings. This will help prevent API errors due to format issues.

components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (2)

16-41: Use error-level logging for clarity.
While the error is handled in a try/catch, consider using console.error instead of console.log to emphasize the severity of failures:

- console.log(error.message || error);
+ console.error(error.message || error);

Also, double-check that returning the unmodified body on error is the desired behavior. Depending on your requirements, it may be helpful to emit partial or alternative data, or even halt execution.


43-49: Validate the presence of timestamps.
If body.timestamp is missing or invalid, new Date(body.timestamp).getTime() may yield NaN. Consider a fallback or additional validation:

return {
  id: body.id,
  summary: `New Commitment Change Order Event: ${body.resource_id}`,
- ts: new Date(body.timestamp).getTime(),
+ ts: body.timestamp ? new Date(body.timestamp).getTime() : Date.now(),
};
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between ee74d49 and e118338.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (1 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
  • components/procore_sandbox/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore_sandbox/common/utils.mjs (1 hunks)
  • components/procore_sandbox/package.json (1 hunks)
  • components/procore_sandbox/procore_sandbox.app.mjs (1 hunks)
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/submittal/submittal.js
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/sources/rfi/rfi.js
  • components/procore/sources/purchase-order/purchase-order.js
  • components/procore/sources/new-event/new-event.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
  • components/procore/sources/common.js
  • components/procore/sources/budget-snapshot/budget-snapshot.js
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/procore.app.js
🚧 Files skipped from review as they are similar to previous changes (28)
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore_sandbox/package.json
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore/common/utils.mjs
  • components/procore/common/resource-names.mjs
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore_sandbox/actions/create-incident/create-incident.mjs
  • components/procore_sandbox/procore_sandbox.app.mjs
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs
  • components/procore/sources/new-event-instant/new-event-instant.mjs
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore/actions/create-incident/create-incident.mjs
  • components/procore/package.json
  • components/procore/common/constants.mjs
  • components/procore/actions/create-rfi/create-rfi.mjs
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore/sources/common/webhook.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/procore_sandbox/common/utils.mjs

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

⏰ Context from checks skipped due to timeout of 90000ms (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Lint Code Base
🔇 Additional comments (9)
components/procore_sandbox/common/utils.mjs (1)

52-55: LGTM - Clean module exports!

The module exports an object with named functions, which is a good practice for utility modules as it allows for selective importing by consumers.

components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

26-41: Excellent error handling implementation

The error handling in the getDataToEmit method is well-implemented. It properly catches any errors from the API call to retrieve prime contract details and provides a fallback by returning the original body. This makes the component more robust and prevents webhook processing from failing completely when encountering API issues.

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

266-266: Use the correct plural endpoint for prime contracts.

According to Procore’s documentation, the endpoint should be /prime_contracts/ instead of /prime_contract/, which could cause errors or unexpected behavior.

- path: `/prime_contract/${primeContractId}`,
+ path: `/prime_contracts/${primeContractId}`,

337-381: Add error handling to the pagination logic.

If resourcesFn throws an error while fetching data, the while (true) loop will break. Consider wrapping the network call in a try/catch block so that you can log and handle errors gracefully.

components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

51-51: Validate the presence of body.timestamp.

If body.timestamp is missing or not a valid date, new Date(body.timestamp).getTime() returns NaN, which may cause issues downstream. Consider adding a safeguard.

components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1)

48-48: Verify presence of body.timestamp or provide a fallback.

If body.timestamp is missing or invalid, new Date(body.timestamp).getTime() will yield NaN. Ensure this field is always set properly before converting to a timestamp, or handle the fallback scenario:

ts: new Date(body.timestamp).getTime(),
+ // or, safely handle missing timestamp:
+ // ts: body.timestamp ? new Date(body.timestamp).getTime() : Date.now(),
components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (3)

1-3: Imports look good.
These imports cleanly bring in common webhook functionality and resource names, and there's no indication of unused imports or version conflicts.


4-10: Source metadata is well-defined.
The name, key, description, and version are clear and descriptive, matching typical patterns for Pipedream sources.


13-15: Resource name method is straightforward.
Returning resourceNames.CHANGE_EVENTS correctly aligns with the intended resource type for Procore.

@jcortes jcortes force-pushed the procore-new-components branch from e118338 to 3ee8b3b Compare March 14, 2025 04:36
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

♻️ Duplicate comments (5)
components/procore/actions/create-submittal/create-submittal.mjs (1)

87-117: 🛠️ Refactor suggestion

Add error handling for API calls

The API call in the run method lacks error handling, which could result in unhandled exceptions when the API request fails.

  async run({ $ }) {
    const {
      createSubmittal,
      companyId,
      projectId,
      number,
      description,
      title,
      type,
      isPrivate,
      revision,
      locationId,
    } = this;
+   try {
      const response = await createSubmittal({
        $,
        companyId,
        projectId,
        data: {
          submittal: {
            number,
            description,
            title,
            type,
            private: isPrivate,
            revision,
            location_id: locationId,
          },
        },
      });
      $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);
      return response;
+   } catch (error) {
+     $.export("error", error);
+     throw new Error(`Failed to create submittal: ${error.message}`);
+   }
  },
components/procore/actions/create-timesheet/create-timesheet.mjs (2)

27-31: 🛠️ Refactor suggestion

Add date format validation for ISO 8601

The date field is described as requiring a specific format, but there's no validation to ensure it adheres to the ISO 8601 standard as expected by Procore's API.

Consider adding a custom validation function that checks if the date follows the required format:

  date: {
    type: "string",
    label: "Date",
    description: "The date of the timesheet. Eg. `2025-04-01`.",
+   validate: (value) => {
+     const regex = /^\d{4}-\d{2}-\d{2}$/;
+     if (!regex.test(value)) {
+       return "Date must be in YYYY-MM-DD format (ISO 8601)";
+     }
+     return true;
+   },
  },

43-63: 🛠️ Refactor suggestion

Implement error handling in run method

The run method makes an API call without any error handling, which could result in unhandled exceptions if the API call fails.

async run({ $ }) {
  const {
    createTimesheet,
    companyId,
    projectId,
    date,
  } = this;

+  try {
    const response = await createTimesheet({
      $,
      companyId,
      projectId,
      data: {
        timesheet: {
          date,
        },
      },
    });
    $.export("$summary", `Successfully created timesheet with ID \`${response.id}\`.`);
    return response;
+  } catch (error) {
+    $.export("error", error);
+    throw new Error(`Failed to create timesheet: ${error.message}`);
+  }
},
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

82-112: 🛠️ Refactor suggestion

Add error handling to run method

The run method lacks error handling for the API call, which could result in unhandled exceptions.

async run({ $ }) {
  const {
    createManpowerLog,
    companyId,
    projectId,
    datetime,
    notes,
    numWorkers,
    numHours,
    userId,
    locationId,
  } = this;

+  try {
    const response = await createManpowerLog({
      $,
      companyId,
      projectId,
      data: {
        manpower_log: {
          datetime,
          notes,
          num_workers: numWorkers,
          num_hours: numHours,
          user_id: userId,
          location_id: locationId,
        },
      },
    });
    $.export("$summary", `Successfully created manpower log with ID \`${response.id}\`.`);
    return response;
+  } catch (error) {
+    $.export("error", error);
+    throw new Error(`Failed to create manpower log: ${error.message}`);
+  }
},
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

43-48: 🛠️ Refactor suggestion

Validate timestamp presence and validity

If body.timestamp is missing or invalid, new Date(body.timestamp).getTime() will yield NaN, which can cause downstream issues.

generateMeta(body) {
  return {
    id: body.id,
    summary: `New Prime Contract Event: ${body.resource_id}`,
-   ts: new Date(body.timestamp).getTime(),
+   ts: body.timestamp ? new Date(body.timestamp).getTime() : Date.now(),
  };
},
🧹 Nitpick comments (8)
components/procore_sandbox/common/utils.mjs (2)

35-50: Clarify the app property extraction and usage pattern.

The function extracts the app property from component.props and renames it to procore, but then doesn't use it (indicated by the eslint-disable comment). Instead, it uses the imported app module. This pattern is confusing and could lead to issues if the component's app property differs from the imported app.

Consider one of these approaches:

  1. Use the extracted app property instead of the imported one:
function getAppProps(component = {}) {
  const {
-    // eslint-disable-next-line no-unused-vars
-    app: procore,
+    app: componentApp,
    ...otherProps
  } = component.props;
  return {
    props: {
-      app,
+      app: componentApp,
      ...buildPropDefinitions({
-        app,
+        app: componentApp,
        props: otherProps,
      }),
    },
  };
}
  1. Or add a comment explaining why the imported app is used instead of the component's app property.

1-55: Add JSDoc documentation to improve maintainability.

The utility functions lack documentation, which would help other developers understand their purpose, parameters, and return values.

Add JSDoc comments to both functions:

import app from "../procore_sandbox.app.mjs";

+/**
+ * Builds property definitions by ensuring each property with a propDefinition includes the app reference.
+ * @param {Object} options - The options object
+ * @param {Object} [options.app={}] - The app reference to include in propDefinitions
+ * @param {Object} [options.props={}] - The properties to process
+ * @returns {Object} The processed properties with updated propDefinitions
+ */
function buildPropDefinitions({
  app = {}, props = {},
}) {
  // ...existing code
}

+/**
+ * Extracts and processes component properties to ensure they include the app reference.
+ * @param {Object} [component={}] - The component object
+ * @returns {Object} An object containing processed props with the app reference
+ */
function getAppProps(component = {}) {
  // ...existing code
}

Additionally, consider adding error handling for unexpected inputs or invalid property definitions.

🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

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

21-27: Consider refactoring repetitive “label-value” mappings into a helper function.
You repeat the same pattern of mapping API results into { label, value } objects across multiple prop definitions. Centralizing this logic into a reusable helper could reduce code duplication and simplify maintenance.

- return results.map(({
-   id: value, name: label,
- }) => ({
-   label,
-   value,
- }));
+ // Example helper usage:
+ const formatOptions = (items) => items.map(({ id: value, name: label }) => ({ label, value }));
+ ...
+ return formatOptions(results);

Also applies to: 49-55, 88-94, 115-121, 138-144, 164-170


203-211: Add try/catch around the makeRequest method for improved error handling.
Currently, any request error will bubble up with minimal context. Wrapping this call in a try/catch (or implementing a global request interceptor) ensures you can log or handle errors consistently across all API calls.

async makeRequest({
  $ = this, path, headers, versionPath, companyId, ...args
} = {}) {
+ try {
    return await axios($, {
      ...args,
      url: this.getUrl(path, versionPath),
      headers: this.getHeaders(headers, companyId),
    });
+ } catch (error) {
+   console.error(`Error in makeRequest: ${error.message}`);
+   throw error;
+ }
}
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

26-45: Consider rethrowing errors in getDataToEmit for visibility.
Logging the error is useful, but rethrowing (or returning an error object) can help consumers of this source handle failures appropriately, ensuring no silent failures in webhook processing.

} catch (error) {
  console.log(error.message || error);
+ throw error; // Alternatively, return a structured error response
}
components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1)

16-42: Rethrow errors in getDataToEmit to avoid silent failures.
Console logging is valuable, but rethrowing or returning an error ensures consuming systems can properly detect and handle the failure.

} catch (error) {
  console.log(error.message || error);
+ throw error;
}
components/procore/sources/common/webhook.mjs (2)

38-93: Lack of error handling in the activate() hook.
If createWebhooksHook or bulkCreateWebhooksTriggers fail, the code won't attempt to roll back partially created resources. This could leave the system in an inconsistent state. Consider wrapping these calls in a try/catch block or implementing a rollback strategy.


94-125: No fallback on partial failures in deactivate() hook.
Similar to activate(), if a call to bulkDeleteWebhooksTriggers or deleteWebhooksHook fails, there’s no rollback or retry logic. Consider adding error handling to ensure resources are cleaned up properly, even if one of these calls fails.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between e118338 and 3ee8b3b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (1 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
  • components/procore_sandbox/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore_sandbox/common/utils.mjs (1 hunks)
  • components/procore_sandbox/package.json (1 hunks)
  • components/procore_sandbox/procore_sandbox.app.mjs (1 hunks)
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/purchase-order/purchase-order.js
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/sources/rfi/rfi.js
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
  • components/procore/sources/common.js
  • components/procore/sources/budget-snapshot/budget-snapshot.js
  • components/procore/procore.app.js
  • components/procore/sources/submittal/submittal.js
  • components/procore/sources/new-event/new-event.js
🚧 Files skipped from review as they are similar to previous changes (28)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs
  • components/procore_sandbox/actions/create-incident/create-incident.mjs
  • components/procore/common/resource-names.mjs
  • components/procore_sandbox/procore_sandbox.app.mjs
  • components/procore/common/utils.mjs
  • components/procore/package.json
  • components/procore/sources/new-event-instant/new-event-instant.mjs
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/actions/create-rfi/create-rfi.mjs
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore_sandbox/package.json
  • components/procore/common/constants.mjs
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore/actions/create-incident/create-incident.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/procore_sandbox/common/utils.mjs

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (13)
components/procore/actions/create-timesheet/create-timesheet.mjs (1)

34-41: Add default parameter and improve parameter handling

Unlike other action methods, this one doesn't provide a default empty object for parameters.

createTimesheet({
-  projectId, ...args
+  $, projectId, companyId, data, ...args
-}) {
+} = {}) {
  return this.app.post({
    path: `/projects/${projectId}/timesheets`,
+   $,
+   companyId,
+   data,
    ...args,
  });
},
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

73-80: Add better error handling for API failures

The createManpowerLog method lacks proper error handling.

createManpowerLog({
  projectId, ...args
} = {}) {
+  try {
    return this.app.post({
      path: `/projects/${projectId}/manpower_logs`,
      ...args,
    });
+  } catch (err) {
+    throw new Error(`Error creating manpower log: ${err.message}`);
+  }
},
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

26-41: Good implementation of error handling

The error handling in the getDataToEmit method appropriately catches and logs any errors during the API call, fallback to returning the original body. This ensures the webhook processing continues even if the additional data cannot be retrieved.

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

262-268: Use the plural endpoint /prime_contracts/ to match Procore’s documented API.
Procore documentation and prior reviews indicate the endpoint should be /prime_contracts/. Using /prime_contract/ may prevent correct data retrieval.

- path: `/prime_contract/${primeContractId}`,
+ path: `/prime_contracts/${primeContractId}`,

337-381: Add robust error handling to the getIterations pagination logic.
A past review comment suggests wrapping the call to resourcesFn in a try/catch block. This prevents the entire generator from failing silently when the API call encounters an error.

while (true) {
+  try {
    const response = await resourcesFn({ ... });
    ...
+  } catch (error) {
+    console.error(`Error fetching paginated resources (page ${page}): ${error.message}`);
+    throw error;
+  }
}

230-236:

❓ Verification inconclusive

Verify differing API version usage for projects (V1_1) and users (V1_3).
It’s possible these calls intentionally use different versions, but confirm it’s correct to mix versions for project and user data. If not, consider aligning them.

Also applies to: 311-320


🏁 Script executed:

#!/bin/bash
# Compare usage of V1_1 vs V1_3 endpoints across the codebase
rg --json "versionPath: constants.VERSION_PATH"

Length of output: 4032


API Version Usage Check: Verify Consistency Between V1_1 and V1_3 Usage

The code in components/procore/procore.app.mjs shows that endpoints for project-related calls (e.g., in listProjects at lines 230–236) use constants.VERSION_PATH.V1_1, while another section (around line 316) uses constants.VERSION_PATH.V1_3. Please confirm that this intentional version mix (projects using V1_1 versus users or related calls using V1_3) aligns with the API requirements. If this discrepancy is unintended, consider updating the versions for consistency.

  • Verify that using V1_1 for project endpoints and V1_3 for user endpoints (or other related endpoints) is correct.
  • In particular, check the rationale for the difference between lines 230–236 (V1_1) and lines 311–320 (V1_3).
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

48-53: Validate the body.timestamp field in generateMeta.
If body.timestamp is missing or invalid, new Date(body.timestamp).getTime() results in NaN. A past review comment highlighted the same risk. Consider fallback logic or an explicit check.

generateMeta(body) {
  return {
    id: body.id,
    summary: `New Budget Snapshot Event: ${body.resource_id}`,
-   ts: new Date(body.timestamp).getTime(),
+   ts: body.timestamp
+     ? new Date(body.timestamp).getTime()
+     : Date.now(),
  };
}
components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1)

44-49: Validate the body.timestamp field in generateMeta.
If body.timestamp is missing or invalid, new Date(body.timestamp).getTime() becomes NaN. A previous review comment mentioned the same issue in related files. Provide a fallback or validation.

generateMeta(body) {
  return {
    id: body.id,
    summary: `New Commitment Change Order Event: ${body.resource_id}`,
-   ts: new Date(body.timestamp).getTime(),
+   ts: body.timestamp
+     ? new Date(body.timestamp).getTime()
+     : Date.now(),
  };
}
components/procore/sources/common/webhook.mjs (5)

1-5: Imports look good.
No immediate concerns regarding correctness or security.


6-36: Consider validating prop inputs and handling empty cases gracefully.
Though these props are well-defined, it may be beneficial to ensure that eventTypes isn't empty or has a fallback behavior for scenarios where the user doesn't provide any event types.

Do you want me to run a verification script to look for usage of these props downstream and confirm that empty or invalid inputs won't break the flow?


128-130: Unimplemented generateMeta method (same issue previously flagged).
This method is invoked in processResource(), causing a runtime error. You must implement or override this method.


149-151: Unimplemented getResourceName method (same issue previously flagged).
This method is required for proper trigger creation. You must implement or override it in derived classes.


193-210: Verify token-based authorization logic in run().
Currently, any mismatch in the Authorization header returns a 401 and exits. Ensure you have tested scenarios where:

  • No Authorization header is present
  • The token is expired or invalid
  • Authorization is needed for partial event processing

If more robust security or custom error handling is required (e.g., logging or rate-limiting), consider implementing it here.

Do you want me to generate a test script that sends various Authorization headers to confirm the correct status codes and responses?

@jcortes jcortes force-pushed the procore-new-components branch from 3ee8b3b to 16d21fe Compare March 14, 2025 15:31
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 (3)
components/procore_sandbox/common/utils.mjs (1)

3-33: 🛠️ Refactor suggestion

Optimize reducer implementation to avoid O(n²) complexity.

The current implementation uses spread operators (...newProps) inside the reducer accumulator, which can lead to O(n²) time complexity when processing large property objects. This issue has been flagged by the static analysis tool.

Consider using a for...of loop instead:

function buildPropDefinitions({
  app = {}, props = {},
}) {
-  return Object.entries(props)
-    .reduce((newProps, [
-      key,
-      prop,
-    ]) => {
-      if (!prop.propDefinition) {
-        return {
-          ...newProps,
-          [key]: prop,
-        };
-      }
-
-      const [
-        , ...propDefinitionItems
-      ] = prop.propDefinition;
-
-      return {
-        ...newProps,
-        [key]: {
-          ...prop,
-          propDefinition: [
-            app,
-            ...propDefinitionItems,
-          ],
-        },
-      };
-    }, {});
+  const newProps = {};
+  
+  for (const [key, prop] of Object.entries(props)) {
+    if (!prop.propDefinition) {
+      newProps[key] = prop;
+      continue;
+    }
+    
+    const [, ...propDefinitionItems] = prop.propDefinition;
+    
+    newProps[key] = {
+      ...prop,
+      propDefinition: [
+        app,
+        ...propDefinitionItems,
+      ],
+    };
+  }
+  
+  return newProps;
}
🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

components/procore/sources/common/webhook.mjs (2)

128-130: ⚠️ Potential issue

Implement generateMeta or remove its usage to prevent runtime errors.

Because it’s invoked at line 190, this unimplemented method will throw a ConfigurationError. Provide a meaningful implementation or omit the call.


149-151: ⚠️ Potential issue

Implement getResourceName or remove its usage to prevent activation failures.

This method is called at line 76 when creating triggers. As it’s not implemented, the code will throw a ConfigurationError, breaking the activation flow.

🧹 Nitpick comments (8)
components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1)

39-41: Consider more robust error handling instead of only logging.

Relying solely on console.log() can lead to silent failures if callers expect an exception or structured error handling. A try/catch with an explicit error throw or $ export could help track failures in your pipeline.

Here's a possible example:

try {
  const resource = await app.getTimecardEntry({
    companyId,
    timecardEntryId,
    params: {
      project_id: projectId,
    },
  });
  return {
    ...body,
    resource,
  };
} catch (error) {
-  console.log(error.message || error);
+  $.export("error", error);
+  throw new Error(`Failed to retrieve timecard entry: ${error.message}`);
}
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

16-46: Use console.error for error messages and consider verifying projectId.

Currently, errors are logged with console.log. To better convey issue severity and maintain standard logging practices, consider using console.error. Additionally, consider handling cases where projectId may be missing or invalid before invoking app.paginate, to prevent unexpected errors.

 try {
   const snapshotRows = await app.paginate({
     ...
   });
   return {
     ...body,
     snapshotRows,
   };
 } catch (error) {
-  console.log(error.message || error);
+  console.error(error.message || error);
   return body;
 }
components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1)

16-42: Use console.error for error logging and verify projectId consistency.

To ensure proper visibility of errors, replace console.log with console.error. Also, if projectId is critical for retrieving purchase order details, consider adding checks to avoid potential undefined behavior.

 try {
   const resource = await app.getPurchaseOrderContract({
     ...
   });
   return {
     ...body,
     resource,
   };
 } catch (error) {
-  console.log(error.message || error);
+  console.error(error.message || error);
   return body;
 }
components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (2)

16-42: Use console.error for error messages and validate the presence of projectId.

Replace console.log(error) with console.error(error) for clarity, and consider early-return checks for projectId if it’s mandatory for app.getChangeEvent.

 try {
   const resource = await app.getChangeEvent({
     ...
   });
   return {
     ...body,
     resource,
   };
 } catch (error) {
-  console.log(error.message || error);
+  console.error(error.message || error);
   return body;
 }

43-49: Ensure body.timestamp is validated or defaulted.

As with the other event sources, if body.timestamp is missing or invalid, this could lead to a NaN timestamp. Add validation or fallback logic to ensure robust event metadata.

components/procore/sources/common/webhook.mjs (3)

38-93: Consider adding failure handling in the activation process.

If creating the webhook or triggers fails, there’s no catch for partial or complete failures. This might leave the system in an inconsistent state. Consider wrapping these operations in try/catch blocks or adding a rollback mechanism for partial failures.


94-125: Gracefully handle partial failures in the deactivation process.

Similar to the activation flow, if trigger deletion succeeds but webhook deletion fails (or vice versa), you may leave behind orphaned hooks or triggers. Consider adding error handling or a transaction-like approach to preserve consistency.


197-201: Enhance error feedback for unauthorized access.

Returning a bare 401 status without an explanatory body may hinder debugging. Consider adding a brief responsive message or logging for clarity.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3ee8b3b and 16d21fe.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (1 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
  • components/procore_sandbox/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore_sandbox/common/utils.mjs (1 hunks)
  • components/procore_sandbox/package.json (1 hunks)
  • components/procore_sandbox/procore_sandbox.app.mjs (1 hunks)
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/new-event/new-event.js
  • components/procore/sources/purchase-order/purchase-order.js
  • components/procore/sources/rfi/rfi.js
  • components/procore/sources/common.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/procore.app.js
  • components/procore/sources/budget-snapshot/budget-snapshot.js
  • components/procore/sources/submittal/submittal.js
🚧 Files skipped from review as they are similar to previous changes (28)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore_sandbox/package.json
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs
  • components/procore_sandbox/procore_sandbox.app.mjs
  • components/procore_sandbox/actions/create-incident/create-incident.mjs
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs
  • components/procore/sources/new-event-instant/new-event-instant.mjs
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/common/resource-names.mjs
  • components/procore/package.json
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/common/utils.mjs
  • components/procore/actions/create-timesheet/create-timesheet.mjs
  • components/procore/common/constants.mjs
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore/actions/create-rfi/create-rfi.mjs
  • components/procore/actions/create-incident/create-incident.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/procore_sandbox/common/utils.mjs

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

⏰ 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 (11)
components/procore_sandbox/common/utils.mjs (2)

35-50: LGTM! Good function design for component prop handling.

The function properly extracts the app property, renaming it to avoid confusion, and reuses the buildPropDefinitions function to process other properties. The ESLint disable comment is appropriately used for the unused variable.


52-55: LGTM! Clean export of utility functions.

The export syntax correctly makes both utility functions available to other modules.

components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1)

43-49:

❓ Verification inconclusive

Verify that the correct ID is used for event metadata.

The code uses body.id in generateMeta(), whereas the resource ID appears to be in body.resource_id. Confirm that body.id is indeed populated (and unique) for the timecard entry. Otherwise, consider using body.resource_id as the id.


🏁 Script executed:

#!/bin/bash
# Description: Search for references to `resource_id` vs `body.id` in this component and check for usage patterns.
rg -A 3 'generateMeta'
rg -A 3 'body\.id'
rg -A 3 'body\.resource_id'

Length of output: 65703


Action: Verify the correctness of the event metadata ID

The current implementation uses body.id to populate the metadata, while the event’s summary references body.resource_id. In other similar components, we see that the unique identifier is typically taken directly from the event payload (e.g., topic.id or AlertId), which suggests that using a distinct identifier is intentional. However, please confirm that for Procore’s timecard event, body.id is indeed available and uniquely identifies the event entry. If not, it might be more appropriate to use body.resource_id for the metadata’s id.

• Check the incoming payload structure for new timecard entries to ensure that body.id is reliably populated and unique.
• If you find that body.id is undefined or not unique across events, update the implementation to use body.resource_id as the identifier.

components/procore/actions/create-submittal/create-submittal.mjs (1)

87-118: Add error handling for API failures.

The API call in run() lacks a try-catch block, which can cause unhandled exceptions at runtime. Re-throwing an error or exporting it via $ ensures that downstream processes handle it gracefully.

async run({ $ }) {
  const {
    createSubmittal,
    companyId,
    projectId,
    number,
    description,
    title,
    type,
    isPrivate,
    revision,
    locationId,
  } = this;

- const response = await createSubmittal({
-   $,
-   companyId,
-   projectId,
-   data: {
-     submittal: {
-       number,
-       description,
-       title,
-       type,
-       private: isPrivate,
-       revision,
-       location_id: locationId,
-     },
-   },
- });
- $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);
- return response;

+ try {
+   const response = await createSubmittal({
+     $,
+     companyId,
+     projectId,
+     data: {
+       submittal: {
+         number,
+         description,
+         title,
+         type,
+         private: isPrivate,
+         revision,
+         location_id: locationId,
+       },
+     },
+   });
+   $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);
+   return response;
+ } catch (error) {
+   $.export("error", error);
+   throw new Error(`Failed to create submittal: ${error.message}`);
+ }
}
components/procore/actions/create-manpower-log/create-manpower-log.mjs (3)

32-37: Add datetime format validation.

Revisit the previous suggestion to validate datetime so that the provided string matches the ISO 8601 format. This prevents issues when optional fields are supplied in an invalid format.

datetime: {
  type: "string",
  label: "Datetime",
  description: "The datetime of the record. Eg. `2025-04-01T00:00:00Z`.",
  optional: true,
+ validate: (value) => {
+   if (!value) return true; // Skip validation if not provided
+   const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
+   if (!regex.test(value)) {
+     return "Datetime must be in ISO 8601 format (YYYY-MM-DDThh:mm:ssZ)";
+   }
+   return true;
+ },
},

73-80: Add robust error handling for the API call.

Wrapping the createManpowerLog() request in a try-catch block ensures any failures are caught and surfaced, preventing silent runtime errors.

createManpowerLog({
  projectId, ...args
} = {}) {
-  return this.app.post({
-    path: `/projects/${projectId}/manpower_logs`,
-    ...args,
-  });
+  try {
+    return this.app.post({
+      path: `/projects/${projectId}/manpower_logs`,
+      ...args,
+    });
+  } catch (err) {
+    throw new Error(`Error creating manpower log: ${err.message}`);
+  }
},

82-112: Wrap the run method in a try-catch block.

Similar to the API call, the top-level async run() should handle errors gracefully to prevent unhandled exceptions.

async run({ $ }) {
  const {
    createManpowerLog,
    companyId,
    projectId,
    datetime,
    notes,
    numWorkers,
    numHours,
    userId,
    locationId,
  } = this;

- const response = await createManpowerLog({
-   $,
-   companyId,
-   projectId,
-   data: {
-     manpower_log: {
-       datetime,
-       notes,
-       num_workers: numWorkers,
-       num_hours: numHours,
-       user_id: userId,
-       location_id: locationId,
-     },
-   },
- });
- $.export("$summary", `Successfully created manpower log with ID \`${response.id}\`.`);
- return response;

+ try {
+   const response = await createManpowerLog({
+     $,
+     companyId,
+     projectId,
+     data: {
+       manpower_log: {
+         datetime,
+         notes,
+         num_workers: numWorkers,
+         num_hours: numHours,
+         user_id: userId,
+         location_id: locationId,
+       },
+     },
+   });
+   $.export("$summary", `Successfully created manpower log with ID \`${response.id}\`.`);
+   return response;
+ } catch (error) {
+   $.export("error", error);
+   throw new Error(`Failed to create manpower log: ${error.message}`);
+ }
}
components/procore/procore.app.mjs (2)

262-269: Use plural resource path for prime contracts.

According to Procore API documentation, prime contracts require a plural path (/prime_contracts/). Verify if /prime_contract/ is correct or if it should be /prime_contracts/.

getPrimeContract({
  primeContractId, ...args
} = {}) {
  return this.makeRequest({
-    path: `/prime_contract/${primeContractId}`,
+    path: `/prime_contracts/${primeContractId}`,
    ...args,
  });
},

337-381: Add error handling for paginated requests.

A failure at any point in getIterations() halts iteration, but no explicit handling is in place. Consider catching errors and either re-throwing them or handling them to avoid partial data or silent failures.

while (true) {
+  try {
    const response =
      await resourcesFn({
        ...resourcesFnArgs,
        params: {
          ...resourcesFnArgs?.params,
          page,
          per_page: constants.DEFAULT_LIMIT,
        },
      });

    const nextResources = resourceName
      ? utils.getNestedProperty(response, resourceName)
      : response;

    if (!nextResources?.length) {
      console.log("No more resources found");
      return;
    }

    for (const resource of nextResources) {
      yield resource;
      resourcesCount += 1;

      if (resourcesCount >= max) {
        console.log("Reached max resources");
        return;
      }
    }

    if (nextResources.length < constants.DEFAULT_LIMIT) {
      console.log("No next page found");
      return;
    }

    page += 1;
+  } catch (error) {
+    console.error(`Error fetching resources on page ${page}: ${error.message}`);
+    throw error;
+  }
}
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

47-53: Validate body.timestamp to avoid potential NaN result.

This is a known concern from previous discussions. If body.timestamp is missing or invalid, new Date(body.timestamp).getTime() will produce NaN, potentially causing downstream issues. Consider adding a fallback or validation.

components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1)

43-49: Add checks for body.timestamp before computing ts.

If body.timestamp is missing or malformed, new Date(body.timestamp).getTime() can yield NaN. Implement a fallback or guard clause to ensure the emitted event has a valid timestamp.

Would you like to confirm the presence of timestamp in all possible event payloads, or should we add a default if it's missing?

@jcortes jcortes force-pushed the procore-new-components branch from 16d21fe to 69028f0 Compare March 17, 2025 20:52
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

♻️ Duplicate comments (2)
components/procore/actions/create-submittal/create-submittal.mjs (1)

87-118: ⚠️ Potential issue

Add error handling to run method

The run method lacks error handling for the API call, which could result in unhandled exceptions.

async run({ $ }) {
  const {
    createSubmittal,
    companyId,
    projectId,
    number,
    description,
    title,
    type,
    isPrivate,
    revision,
    locationId,
  } = this;
+  try {
    const response = await createSubmittal({
      $,
      companyId,
      projectId,
      data: {
        submittal: {
          number,
          description,
          title,
          type,
          private: isPrivate,
          revision,
          location_id: locationId,
        },
      },
    });
    $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);
    return response;
+  } catch (error) {
+    $.export("error", error);
+    throw new Error(`Failed to create submittal: ${error.message}`);
+  }
},
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

82-112: ⚠️ Potential issue

Add error handling to run method

The run method lacks error handling for the API call, which could result in unhandled exceptions.

async run({ $ }) {
  const {
    createManpowerLog,
    companyId,
    projectId,
    datetime,
    notes,
    numWorkers,
    numHours,
    userId,
    locationId,
  } = this;

+  try {
    const response = await createManpowerLog({
      $,
      companyId,
      projectId,
      data: {
        manpower_log: {
          datetime,
          notes,
          num_workers: numWorkers,
          num_hours: numHours,
          user_id: userId,
          location_id: locationId,
        },
      },
    });
    $.export("$summary", `Successfully created manpower log with ID \`${response.id}\`.`);
    return response;
+  } catch (error) {
+    $.export("error", error);
+    throw new Error(`Failed to create manpower log: ${error.message}`);
+  }
},
🧹 Nitpick comments (10)
components/procore_sandbox/common/utils.mjs (1)

35-50: Clarify app variable naming and usage.

The current implementation has potentially confusing app references:

  1. You import app at the top of the file
  2. Extract app from component.props but rename it to procore (and mark it unused)
  3. Then use the imported app in the returned object

This naming and variable usage could lead to confusion. Consider one of these clearer approaches:

- function getAppProps(component = {}) {
-   const {
-     // eslint-disable-next-line no-unused-vars
-     app: procore,
-     ...otherProps
-   } = component.props;
-   return {
-     props: {
-       app,
-       ...buildPropDefinitions({
-         app,
-         props: otherProps,
-       }),
-     },
-   };
- }

// Option 1: Use consistent naming if intentionally ignoring component's app
+ function getAppProps(component = {}) {
+   const {
+     // eslint-disable-next-line no-unused-vars
+     app: componentApp, // Clearly named to show it's different from imported app
+     ...otherProps
+   } = component.props;
+   return {
+     props: {
+       app, // Using the imported app
+       ...buildPropDefinitions({
+         app,
+         props: otherProps,
+       }),
+     },
+   };
+ }

// Option 2: Actually use the component's app if that was the intention
+ function getAppProps(component = {}) {
+   const {
+     app: componentApp,
+     ...otherProps
+   } = component.props;
+   return {
+     props: {
+       app: componentApp || app, // Use component's app or fall back to imported app
+       ...buildPropDefinitions({
+         app: componentApp || app,
+         props: otherProps,
+       }),
+     },
+   };
+ }
components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (2)

46-48: Add timestamp validation to prevent NaN values

The timestamp conversion doesn't check if body.timestamp exists or is valid before converting it to milliseconds. If body.timestamp is missing or invalid, this will result in NaN, which can cause downstream issues.

generateMeta(body) {
  return {
    id: body.id,
    summary: `New Timecard Entry Event: ${body.resource_id}`,
-   ts: new Date(body.timestamp).getTime(),
+   ts: body.timestamp ? new Date(body.timestamp).getTime() : Date.now(),
  };
},

26-41: Enhance error logging in getDataToEmit

The current error handling only logs to console, which might not be sufficient for troubleshooting in a production environment. Consider adding more context to the error message.

try {
  const resource = await app.getTimecardEntry({
    companyId,
    timecardEntryId,
    params: {
      project_id: projectId,
    },
  });
  return {
    ...body,
    resource,
  };
} catch (error) {
-  console.log(error.message || error);
+  console.log(`Failed to get timecard entry details (ID: ${timecardEntryId}): ${error.message || error}`);
+  console.log(`projectId: ${projectId}, companyId: ${companyId}`);
  return body;
}
components/procore/actions/create-submittal/create-submittal.mjs (1)

77-85: Add error handling to createSubmittal method

The createSubmittal method directly returns the result of app.post without any error handling, which could lead to unhandled exceptions if network issues occur.

createSubmittal({
  projectId, ...args
} = {}) {
+  try {
    return this.app.post({
      versionPath: constants.VERSION_PATH.V1_1,
      path: `/projects/${projectId}/submittals`,
      ...args,
    });
+  } catch (error) {
+    throw new Error(`Error in createSubmittal: ${error.message}`);
+  }
},
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

45-49: Consider changing numHours type to number

The numHours property is defined as a string type, which seems inconsistent for a field representing hours. Consider changing it to a number type for better data consistency.

numHours: {
-  type: "string",
+  type: "number",
  label: "Number Of Hours",
  description: "The number of hours for each worker",
  optional: true,
},
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

26-41: Enhance error handling in getDataToEmit

The error handling is minimal, only logging the error message to the console without additional context. Consider adding more information for easier troubleshooting.

try {
  const resource = await app.getPrimeContract({
    companyId,
    primeContractId,
    params: {
      project_id: projectId,
    },
  });
  return {
    ...body,
    resource,
  };
} catch (error) {
-  console.log(error.message || error);
+  console.log(`Failed to get prime contract details (ID: ${primeContractId}): ${error.message || error}`);
+  console.log(`projectId: ${projectId}, companyId: ${companyId}`);
  return body;
}
components/procore/procore.app.mjs (1)

203-211: Consider adding error handling for makeRequest
Currently, makeRequest does not handle request errors. A simple try/catch here can ensure that any unexpected failures are logged or handled appropriately without requiring each caller to re-implement error handling.

+async makeRequest({
+  $ = this, path, headers, versionPath, companyId, ...args
+} = {}) {
+  try {
+    return await axios($, {
+      ...args,
+      url: this.getUrl(path, versionPath),
+      headers: this.getHeaders(headers, companyId),
+    });
+  } catch (error) {
+    console.error(`Request failed: ${error.message}`);
+    throw error;
+  }
+}
components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1)

44-49: Verify presence of body.timestamp
Similar to other Procore event sources, ensure body.timestamp can never be undefined or invalid to prevent NaN from creeping in. A quick validation or fallback measure can help.

generateMeta(body) {
  return {
    id: body.id,
    summary: `New Purchase Order Event: ${body.resource_id}`,
-   ts: new Date(body.timestamp).getTime(),
+   ts: Number.isNaN(new Date(body.timestamp).getTime())
+     ? Date.now()
+     : new Date(body.timestamp).getTime(),
  };
},
components/procore/sources/common/webhook.mjs (2)

182-184: Add validation to the getDataToEmit method.

This method currently returns the raw webhook body without any validation or sanitization, which could lead to processing invalid data or security issues.

-    async getDataToEmit(body) {
-      return body;
-    }
+    async getDataToEmit(body) {
+      // Validate the body has the expected structure
+      if (!body || typeof body !== "object") {
+        console.log("Invalid webhook payload received:", body);
+        return null;
+      }
+      
+      // You can add additional validation logic here
+      return body;
+    }

194-207: Improve the error handling in the run method.

The current implementation doesn't properly handle potential errors that might occur during processing. Additionally, consider using a constant-time comparison for the token validation to protect against timing attacks.

  async run({
    body, headers: { authorization },
  }) {
-    const token = this.getToken();
-    if (authorization !== `Bearer ${token}`) {
-      console.log("Authorization header does not match the expected token");
-      return this.http.respond({
-        status: 401,
-      });
-    }
-    this.http.respond({
-      status: 200,
-    });
-
-    const resource = await this.getDataToEmit(body);
-    this.processResource(resource);
+    try {
+      const token = this.getToken();
+      const expectedAuth = `Bearer ${token}`;
+      
+      // Basic validation of authorization header
+      if (!authorization || typeof authorization !== 'string') {
+        console.log("Missing or invalid authorization header");
+        return this.http.respond({
+          status: 401,
+        });
+      }
+      
+      // Compare authorization token
+      if (authorization !== expectedAuth) {
+        console.log("Authorization header does not match the expected token");
+        return this.http.respond({
+          status: 401,
+        });
+      }
+      
+      // Respond immediately to the webhook request
+      this.http.respond({
+        status: 200,
+      });
+      
+      // Process the webhook data asynchronously
+      const resource = await this.getDataToEmit(body);
+      if (resource) {
+        this.processResource(resource);
+      }
+    } catch (error) {
+      console.error("Error processing webhook:", error);
+      
+      // Ensure we respond to the webhook even if processing fails
+      if (!this.http.responded) {
+        this.http.respond({
+          status: 500,
+        });
+      }
+    }
  },
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 16d21fe and 69028f0.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (1 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
  • components/procore_sandbox/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore_sandbox/common/utils.mjs (1 hunks)
  • components/procore_sandbox/package.json (1 hunks)
  • components/procore_sandbox/procore_sandbox.app.mjs (1 hunks)
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/new-event/new-event.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
  • components/procore/sources/rfi/rfi.js
  • components/procore/sources/budget-snapshot/budget-snapshot.js
  • components/procore/sources/common.js
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/sources/submittal/submittal.js
  • components/procore/procore.app.js
  • components/procore/sources/purchase-order/purchase-order.js
🚧 Files skipped from review as they are similar to previous changes (28)
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs
  • components/procore_sandbox/package.json
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs
  • components/procore_sandbox/procore_sandbox.app.mjs
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/sources/new-event-instant/new-event-instant.mjs
  • components/procore/common/resource-names.mjs
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore/actions/create-incident/create-incident.mjs
  • components/procore_sandbox/actions/create-incident/create-incident.mjs
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/common/utils.mjs
  • components/procore/actions/create-timesheet/create-timesheet.mjs
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore/actions/create-rfi/create-rfi.mjs
  • components/procore/common/constants.mjs
  • components/procore/package.json
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/procore_sandbox/common/utils.mjs

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
  • GitHub Check: Verify TypeScript components
🔇 Additional comments (13)
components/procore_sandbox/common/utils.mjs (1)

6-32: Optimize reducer implementation to avoid O(n²) complexity.

The current implementation uses spread operators (...newProps) inside the reducer accumulator, which can lead to O(n²) time complexity when processing large property objects. This was flagged by static analysis.

Consider using an alternative approach that doesn't rely on spreads in the accumulator:

function buildPropDefinitions({
  app = {}, props = {},
}) {
-  return Object.entries(props)
-    .reduce((newProps, [
-      key,
-      prop,
-    ]) => {
-      if (!prop.propDefinition) {
-        return {
-          ...newProps,
-          [key]: prop,
-        };
-      }
-
-      const [
-        , ...propDefinitionItems
-      ] = prop.propDefinition;
-
-      return {
-        ...newProps,
-        [key]: {
-          ...prop,
-          propDefinition: [
-            app,
-            ...propDefinitionItems,
-          ],
-        },
-      };
-    }, {});
+  const newProps = {};
+  
+  for (const [key, prop] of Object.entries(props)) {
+    if (!prop.propDefinition) {
+      newProps[key] = prop;
+      continue;
+    }
+    
+    const [, ...propDefinitionItems] = prop.propDefinition;
+    
+    newProps[key] = {
+      ...prop,
+      propDefinition: [
+        app,
+        ...propDefinitionItems,
+      ],
+    };
+  }
+  
+  return newProps;
}
🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

components/procore/actions/create-manpower-log/create-manpower-log.mjs (2)

32-37: Add datetime format validation

The datetime field includes an example format but lacks validation to ensure it follows the ISO 8601 standard required by Procore's API.

datetime: {
  type: "string",
  label: "Datetime",
  description: "The datetime of the record. Eg. `2025-04-01T00:00:00Z`.",
  optional: true,
+ validate: (value) => {
+   if (!value) return true; // Skip validation if not provided since it's optional
+   const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
+   if (!regex.test(value)) {
+     return "Datetime must be in ISO 8601 format (YYYY-MM-DDThh:mm:ssZ)";
+   }
+   return true;
+ },
},

73-80: Add error handling to createManpowerLog method

Currently, if an error occurs during createManpowerLog(), the action might silently fail at runtime.

createManpowerLog({
  projectId, ...args
} = {}) {
+  try {
    return this.app.post({
      path: `/projects/${projectId}/manpower_logs`,
      ...args,
    });
+  } catch (err) {
+    throw new Error(`Error creating manpower log: ${err.message}`);
+  }
},
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

46-48: Validate timestamp presence and validity

If body.timestamp is missing or not in a valid format, the expression will yield NaN, which can cause issues downstream.

generateMeta(body) {
  return {
    id: body.id,
    summary: `New Prime Contract Event: ${body.resource_id}`,
-   ts: new Date(body.timestamp).getTime(),
+   ts: body.timestamp && !isNaN(new Date(body.timestamp)) 
+     ? new Date(body.timestamp).getTime() 
+     : Date.now(),
  };
},
components/procore/procore.app.mjs (4)

262-269: Use plural endpoint for prime contracts
This endpoint should be updated to use the plural form /prime_contracts/{primeContractId}—matching Procore’s official documentation—rather than the current singular form.


337-381: Add robust error handling in pagination
Similar to a past suggestion, wrapping the API call within try/catch will prevent unexpected crashes when an error occurs during resource retrieval.


178-179: Validate OAuth token retrieval
Ensure that this.$auth.oauth_access_token is always a valid token, since failing to retrieve a fresh token will cause downstream calls to fail. You might consider refreshing or validating tokens before each request if Procore’s OAuth flow requires it.


311-320:

❓ Verification inconclusive

Confirm correctness of version path for listing users
Before merging, confirm that the listUsers method truly requires constants.VERSION_PATH.V1_3. If the Procore API endpoint for users is on a different version, you may encounter compatibility issues.


🏁 Script executed:

#!/bin/bash
# Verifying the "listUsers" API endpoint version in Procore docs:
# (User must confirm the correct version path or check any mismatch)
echo "Please confirm if the listUsers endpoint is indeed v1.3."

Length of output: 122


Action Required: Verify Procore API Endpoint Version

The current implementation of listUsers uses constants.VERSION_PATH.V1_3. The executed script only echoed a prompt without providing confirmation. Please double-check the Procore API documentation to ensure that version v1.3 is indeed the correct endpoint version for listing users. If a different version is required, update the version path accordingly.

  • Location: components/procore/procore.app.mjs (lines 311-320)
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (2)

1-3: Imports and common inheritance look good
No issues with the import statements or the spread operator combining the common webhook properties.


48-53: Validate timestamp field
As previously flagged, if body.timestamp is missing or malformed, new Date(body.timestamp).getTime() will yield NaN. Consider adding a fallback or validation to avoid unexpected results.

components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1)

26-41: Solid error-handling pattern
Wrapping the Procore API request in a try/catch block here will help ensure the source remains resilient in case of network or API errors. Good job!

components/procore/sources/common/webhook.mjs (2)

124-126: Implement the generateMeta method to avoid runtime errors.

This method is invoked in the processResource method (line 190), yet it throws a ConfigurationError by default. Implement it to provide the necessary metadata for emitted events.

Example implementation:

-    generateMeta() {
-      throw new ConfigurationError("generateMeta is not implemented");
-    }
+    generateMeta(resource) {
+      return {
+        id: resource?.id ?? Date.now(),
+        summary: resource?.title || resource?.name || "Event from Procore",
+        ts: Date.now(),
+      };
+    }

145-147: Define getResourceName or override it in derived classes.

This method is required for creating triggers (line 75). It currently throws a ConfigurationError, which will break the source if invoked.

-    getResourceName() {
-      throw new ConfigurationError("getResourceName is not implemented");
-    }
+    getResourceName() {
+      // Return resource name here, e.g. "budget_snapshot", "rfi", etc.
+      return this.resourceName || "default_resource";
+    }

@jcortes
Copy link
Collaborator Author

jcortes commented Mar 18, 2025

/approve

@jcortes jcortes force-pushed the procore-new-components branch from 69028f0 to 1a74dc3 Compare March 18, 2025 16:02
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

♻️ Duplicate comments (6)
components/procore/actions/create-submittal/create-submittal.mjs (1)

87-118: 🛠️ Refactor suggestion

Add error handling to run method

The component lacks error handling for API calls, which could lead to unhandled exceptions if the Procore API returns an error.

Implement try/catch block for better error handling:

async run({ $ }) {
  const {
    createSubmittal,
    companyId,
    projectId,
    number,
    description,
    title,
    type,
    isPrivate,
    revision,
    locationId,
  } = this;
  
+  try {
    const response = await createSubmittal({
      $,
      companyId,
      projectId,
      data: {
        submittal: {
          number,
          description,
          title,
          type,
          private: isPrivate,
          revision,
          location_id: locationId,
        },
      },
    });
    $.export("$summary", `Successfully created submittal with ID \`${response.id}\`.`);
    return response;
+  } catch (error) {
+    $.export("error", error);
+    throw new Error(`Failed to create submittal: ${error.message}`);
+  }
},
components/procore/actions/create-manpower-log/create-manpower-log.mjs (2)

73-80: 🛠️ Refactor suggestion

Add error handling to createManpowerLog method

The method lacks error handling for API calls, which could lead to unhandled errors at runtime.

Implement try/catch block for better error handling:

createManpowerLog({
  projectId, ...args
} = {}) {
+  try {
    return this.app.post({
      path: `/projects/${projectId}/manpower_logs`,
      ...args,
    });
+  } catch (err) {
+    throw new Error(`Error creating manpower log: ${err.message}`);
+  }
},

82-112: 🛠️ Refactor suggestion

Add error handling to run method

The component lacks error handling for API calls, which could lead to unhandled exceptions if the Procore API returns an error.

Implement try/catch block for better error handling:

async run({ $ }) {
  const {
    createManpowerLog,
    companyId,
    projectId,
    datetime,
    notes,
    numWorkers,
    numHours,
    userId,
    locationId,
  } = this;

+  try {
    const response = await createManpowerLog({
      $,
      companyId,
      projectId,
      data: {
        manpower_log: {
          datetime,
          notes,
          num_workers: numWorkers,
          num_hours: numHours,
          user_id: userId,
          location_id: locationId,
        },
      },
    });
    $.export("$summary", `Successfully created manpower log with ID \`${response.id}\`.`);
    return response;
+  } catch (error) {
+    $.export("error", error);
+    throw new Error(`Failed to create manpower log: ${error.message}`);
+  }
},
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

43-49: ⚠️ Potential issue

Validate timestamp to avoid potential NaN.
If body.timestamp is missing or invalid, new Date(body.timestamp).getTime() yields NaN. Use a fallback or validation logic to ensure a valid timestamp.

Apply this diff to handle invalid timestamps gracefully:

 generateMeta(body) {
   return {
     id: body.id,
     summary: `New Prime Contract Event: ${body.resource_id}`,
-    ts: new Date(body.timestamp).getTime(),
+    ts: Number.isNaN(new Date(body.timestamp).getTime())
+      ? Date.now()
+      : new Date(body.timestamp).getTime(),
   };
 },
components/procore/procore.app.mjs (1)

262-269: ⚠️ Potential issue

Endpoint path mismatch with Procore docs.
According to Procore’s API docs, prime contracts should use the plural endpoint: /prime_contracts/{primeContractId}.

Proposed fix:

 getPrimeContract({
   primeContractId, ...args
 } = {}) {
   return this.makeRequest({
-    path: `/prime_contract/${primeContractId}`,
+    path: `/prime_contracts/${primeContractId}`,
     ...args,
   });
 },
components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1)

47-53: ⚠️ Potential issue

Validate timestamp to avoid NaN in ts.
If body.timestamp is missing or invalid, new Date(body.timestamp).getTime() results in NaN. Provide a fallback or validation.

Recommended fix:

 generateMeta(body) {
   return {
     id: body.id,
     summary: `New Budget Snapshot Event: ${body.resource_id}`,
-    ts: new Date(body.timestamp).getTime(),
+    ts: Number.isNaN(new Date(body.timestamp).getTime())
+      ? Date.now()
+      : new Date(body.timestamp).getTime(),
   };
 },
🧹 Nitpick comments (9)
components/procore_sandbox/common/utils.mjs (1)

35-50: Review the app property handling for clarity.

The function destructures the app property from component.props and renames it to procore but never uses it. Instead, you're using the imported app module. This could lead to confusion about the source of the app object being used.

Consider using a more explicit naming approach:

function getAppProps(component = {}) {
  const {
-    // eslint-disable-next-line no-unused-vars
-    app: procore,
+    // We ignore the component's app property and use the imported app
+    app: componentApp,
    ...otherProps
  } = component.props;
  return {
    props: {
      app,
      ...buildPropDefinitions({
        app,
        props: otherProps,
      }),
    },
  };
}
components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (2)

38-41: Improve error handling for API failures

The current implementation logs errors but silently continues by returning the original body. This could lead to confusing behavior downstream when the expected resource data is missing.

Consider adding more robust error handling:

} catch (error) {
  console.log(error.message || error);
+ console.log("If you need to get more details about the timecard entry, please provide a project ID.");
  return body;
}

16-42: API call lacks fallback strategy for missing projectId

The code attempts to get a timecard entry but doesn't have a clear fallback if projectId is undefined, which would cause Procore's API to reject the request.

Add a conditional check before making the API call:

try {
+  if (!projectId) {
+    console.log("Project ID is required to fetch detailed timecard entry information");
+    return body;
+  }
  const resource = await app.getTimecardEntry({
    companyId,
    timecardEntryId,
    params: {
      project_id: projectId,
    },
  });
components/procore/actions/create-submittal/create-submittal.mjs (1)

28-32: Consider marking number field as required

The number field doesn't have an optional property specified, which implicitly makes it required. For clarity and consistency with other fields, consider explicitly marking it as required.

number: {
  type: "string",
  label: "Number",
  description: "The Number of the Submittal",
+ optional: false,
},
components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

44-49: Change numHours type from string to number

The numHours field is defined as a string type, but its description suggests it represents a numeric value. This could lead to data type issues or validation problems when interacting with the Procore API.

Change the type to better match the field's purpose:

numHours: {
-  type: "string",
+  type: "number",
  label: "Number Of Hours",
  description: "The number of hours for each worker",
  optional: true,
},
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1)

16-42: Consider additional fallback for primeContractId.
If resource_id is missing or invalid, the call to app.getPrimeContract might fail. A quick guard (e.g., return early if primeContractId is undefined) could improve resilience.

components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (2)

16-42: Consider strengthening error handling and logging behavior.
Currently, the catch block only logs the error with console.log and returns body. Using console.error might provide clearer visibility for production logs. Additionally, ensure you verify that projectId and resource_id are present in the request, as missing values may lead to unpredictable behavior.


43-49: Ensure safe handling of optional/unknown timestamp and ID fields.
If body.timestamp or body.id is missing or undefined, new Date(body.timestamp).getTime() or body.id could produce unexpected results. Consider adding fallback logic or validations to avoid potential runtime errors.

components/procore/sources/common/webhook.mjs (1)

92-121: Consider adding error handling to the deactivate hook.
If removing triggers or deleting the webhook fails midway, the component might be left in an inconsistent state. A try/catch block would help ensure partial resources are properly cleaned up.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 69028f0 and 1a74dc3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (47)
  • components/procore/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore/common/constants.mjs (1 hunks)
  • components/procore/common/resource-names.mjs (1 hunks)
  • components/procore/common/utils.mjs (1 hunks)
  • components/procore/package.json (1 hunks)
  • components/procore/procore.app.js (0 hunks)
  • components/procore/procore.app.mjs (1 hunks)
  • components/procore/sources/budget-snapshot/budget-snapshot.js (0 hunks)
  • components/procore/sources/commitment-change-order/commitment-change-order.js (0 hunks)
  • components/procore/sources/common.js (0 hunks)
  • components/procore/sources/common/webhook.mjs (1 hunks)
  • components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore/sources/new-event/new-event.js (0 hunks)
  • components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js (0 hunks)
  • components/procore/sources/prime-contract/prime-contract.js (0 hunks)
  • components/procore/sources/purchase-order/purchase-order.js (0 hunks)
  • components/procore/sources/rfi/rfi.js (0 hunks)
  • components/procore/sources/submittal/submittal.js (0 hunks)
  • components/procore_sandbox/actions/create-incident/create-incident.mjs (1 hunks)
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs (1 hunks)
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs (1 hunks)
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs (1 hunks)
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs (1 hunks)
  • components/procore_sandbox/common/utils.mjs (1 hunks)
  • components/procore_sandbox/package.json (1 hunks)
  • components/procore_sandbox/procore_sandbox.app.mjs (1 hunks)
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs (1 hunks)
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs (1 hunks)
💤 Files with no reviewable changes (10)
  • components/procore/sources/prime-contract/prime-contract.js
  • components/procore/sources/rfi/rfi.js
  • components/procore/sources/common.js
  • components/procore/sources/commitment-change-order/commitment-change-order.js
  • components/procore/sources/submittal/submittal.js
  • components/procore/sources/new-event/new-event.js
  • components/procore/sources/purchase-order/purchase-order.js
  • components/procore/procore.app.js
  • components/procore/sources/budget-snapshot/budget-snapshot.js
  • components/procore/sources/prime-contract-change-order/prime-contract-change-order.js
🚧 Files skipped from review as they are similar to previous changes (27)
  • components/procore_sandbox/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore_sandbox/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs
  • components/procore_sandbox/sources/new-event-instant/new-event-instant.mjs
  • components/procore_sandbox/actions/create-submittal/create-submittal.mjs
  • components/procore_sandbox/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs
  • components/procore_sandbox/actions/create-manpower-log/create-manpower-log.mjs
  • components/procore_sandbox/sources/new-timecard-entry-event-instant/new-timecard-entry-event-instant.mjs
  • components/procore_sandbox/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs
  • components/procore_sandbox/actions/create-incident/create-incident.mjs
  • components/procore_sandbox/package.json
  • components/procore_sandbox/procore_sandbox.app.mjs
  • components/procore/common/utils.mjs
  • components/procore/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/common/resource-names.mjs
  • components/procore_sandbox/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore_sandbox/actions/create-timesheet/create-timesheet.mjs
  • components/procore/sources/new-event-instant/new-event-instant.mjs
  • components/procore/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
  • components/procore/actions/create-incident/create-incident.mjs
  • components/procore/sources/new-change-order-package-event-instant/new-change-order-package-event-instant.mjs
  • components/procore/actions/create-timesheet/create-timesheet.mjs
  • components/procore/common/constants.mjs
  • components/procore/sources/new-purchase-order-event-instant/new-purchase-order-event-instant.mjs
  • components/procore_sandbox/sources/new-submittal-event-instant/new-submittal-event-instant.mjs
  • components/procore/package.json
  • components/procore_sandbox/actions/create-rfi/create-rfi.mjs
  • components/procore_sandbox/sources/new-rfi-event-instant/new-rfi-event-instant.mjs
🧰 Additional context used
🪛 Biome (1.9.4)
components/procore_sandbox/common/utils.mjs

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)

⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (16)
components/procore_sandbox/common/utils.mjs (2)

3-33: Optimize reducer implementation to avoid O(n²) complexity.

The current implementation uses spread operators (...newProps) inside the reducer accumulator, which can lead to O(n²) time complexity when processing large property objects. This issue was flagged by static analysis.

Consider using a for...of loop instead of reduce to avoid performance issues:

function buildPropDefinitions({
  app = {}, props = {},
}) {
-  return Object.entries(props)
-    .reduce((newProps, [
-      key,
-      prop,
-    ]) => {
-      if (!prop.propDefinition) {
-        return {
-          ...newProps,
-          [key]: prop,
-        };
-      }
-
-      const [
-        , ...propDefinitionItems
-      ] = prop.propDefinition;
-
-      return {
-        ...newProps,
-        [key]: {
-          ...prop,
-          propDefinition: [
-            app,
-            ...propDefinitionItems,
-          ],
-        },
-      };
-    }, {});
+  const newProps = {};
+  
+  for (const [key, prop] of Object.entries(props)) {
+    if (!prop.propDefinition) {
+      newProps[key] = prop;
+      continue;
+    }
+    
+    const [, ...propDefinitionItems] = prop.propDefinition;
+    
+    newProps[key] = {
+      ...prop,
+      propDefinition: [
+        app,
+        ...propDefinitionItems,
+      ],
+    };
+  }
+  
+  return newProps;
}
🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/performance/noAccumulatingSpread)


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

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

(lint/performance/noAccumulatingSpread)


52-55: LGTM!

Appropriately exports both utility functions.

components/procore/actions/create-rfi/create-rfi.mjs (1)

49-64: Review required assignee IDs configuration

The assigneIds property is explicitly marked as optional: false, making it required. Confirm if this is intentional as it forces users to provide assignees for every RFI.

Most Procore components make fields optional when possible to improve flexibility. Consider whether this field should truly be required.

components/procore/actions/create-manpower-log/create-manpower-log.mjs (1)

32-37: Add datetime format validation

The datetime field includes an example format but lacks validation to ensure it follows the ISO 8601 standard required by Procore's API.

Add validation to ensure the datetime follows the required format:

datetime: {
  type: "string",
  label: "Datetime",
  description: "The datetime of the record. Eg. `2025-04-01T00:00:00Z`.",
  optional: true,
+ validate: (value) => {
+   if (!value) return true; // Skip validation if not provided since it's optional
+   const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
+   if (!regex.test(value)) {
+     return "Datetime must be in ISO 8601 format (YYYY-MM-DDThh:mm:ssZ)";
+   }
+   return true;
+ },
},
components/procore/sources/new-prime-contract-event-instant/new-prime-contract-event-instant.mjs (2)

1-3: No concerns regarding import statements.
These imports look consistent and properly reference the shared modules.


4-15: Structural clarity confirmed.
Exporting an object that spreads common while adding specific metadata for prime contract events is a clean approach.

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

1-62: Solid prop definitions.
The props for companyId, projectId, resourceName, and eventTypes are well-structured with suitable async options logic for dynamic selection.


63-172: Comprehensive property listing for additional IDs.
Defining optional user IDs permits flexible configuration. The async selectors in listLocations / listUsers help in large codebases. Great approach.


203-229: General request flow is well-designed.
The makeRequest method abstracts key logic (URL, headers, etc.), centralizing the request flow for clarity.

components/procore/sources/new-budget-snapshot-event-instant/new-budget-snapshot-event-instant.mjs (2)

1-15: Neat extension of shared webhook logic.
Reusing common while customizing the resource name for budget snapshots helps maintain consistency across event sources.


16-46: Cautious approach to data retrieval.
Wrapping pagination in a try/catch ensures partial results are returned in case of errors. This fosters resilience in the webhook flow. Good practice.

components/procore/sources/new-commitment-change-order-event-instant/new-commitment-change-order-event-instant.mjs (1)

1-3: Imports look good.
No issues identified with the import statements.

components/procore/sources/common/webhook.mjs (4)

38-91: Add error handling to the activate hook
If an error occurs while creating the webhook or triggers, partial resources may remain and cause unexpected behavior. Wrap these operations in a try/catch block, and in case of an error, clean up any partially created resources for a consistent state.


124-126: Implement the generateMeta method to avoid ConfigurationError.
This method is invoked by child classes or references in the code, so leaving it unimplemented may lead to errors. Provide a default or a validated metadata object to prevent runtime exceptions.


145-147: Override getResourceName or provide a default implementation.
This method is required for event triggers and cannot remain unimplemented, or it will result in ConfigurationError.


185-192: Add error handling to the processResource method.
If generateMeta or other code inside this method throws, it will break event emission. Wrap it in a try/catch block to prevent unhandled exceptions and optionally emit fallback metadata for visibility.

@jcortes
Copy link
Collaborator Author

jcortes commented Mar 18, 2025

/approve

@jcortes jcortes merged commit 233ece2 into master Mar 18, 2025
11 checks passed
@jcortes jcortes deleted the procore-new-components branch March 18, 2025 21:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action New Action Request refactor Apply this label for component refactors (not net new components) trigger / source New trigger / source request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Components] procore

4 participants