Skip to content

Conversation

@jcortes
Copy link
Collaborator

@jcortes jcortes commented Dec 16, 2025

WHY

Resolves #15118

Summary by CodeRabbit

  • New Features

    • Added Recruiterflow actions: Add Candidate to Job; Create Candidate; Create Job; Create Placement; Move Candidate to Stage; Search Candidates (advanced filters).
    • Added safe JSON parsing utility for nested/JSON-like inputs.
    • Added connector prop pickers for IDs and custom field inputs; new constants for defaults.
  • Chores

    • Expanded connector API surface with list/create/search endpoints (candidates, jobs, placements, companies, contacts, users, locations, departments, employment types).
    • Package version bumped to 0.1.0.

✏️ Tip: You can customize this high-level summary in your review settings.

@jcortes jcortes self-assigned this Dec 16, 2025
@vercel
Copy link

vercel bot commented Dec 16, 2025

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

1 Skipped Deployment
Project Deployment Review Updated (UTC)
pipedream-docs-redirect-do-not-edit Ignored Ignored Dec 19, 2025 3:38pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 16, 2025

Walkthrough

Adds a Recruiterflow integration: app client with many API methods and dynamic propDefinitions, a depth-limited JSON parsing utility, constants, package metadata update, and six new action modules to create/search/manage candidates, jobs, and placements.

Changes

Cohort / File(s) Change Summary
Core API & Utilities
components/recruiterflow/recruiterflow.app.mjs, components/recruiterflow/common/utils.mjs, components/recruiterflow/common/constants.mjs, components/recruiterflow/package.json
Added a Recruiterflow app client with _baseUrl, _headers, _makeRequest, dynamic propDefinitions (candidateId, jobId, companyId, contactId, userId, locationId, departmentId, employmentTypeId, customFields) and many public API methods (create/list/search/add/move for candidates/jobs/placements, list endpoints). Added parseJson utility (depth-limited, circular-safe) and constants (DEFAULT_LIMIT, RATE_FREQUENCIES). Bumped package version to 0.1.0 and added @pipedream/platform dependency.
Candidate Actions
components/recruiterflow/actions/create-candidate/create-candidate.mjs, components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs, components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs, components/recruiterflow/actions/search-candidates/search-candidates.mjs
Added actions for creating a candidate (maps many fields, serializes nested JSON via utils.parseJson), adding a candidate to a job (payload: id, job_id, added_time), moving a candidate to a stage (mutual-exclusivity validation between stageId and stageName, requires jobId when using stageName), and searching candidates (supports JSON filters or builds filters from simple fields; pagination and include_count supported). Each action exposes metadata, props, a run() calling corresponding app method, and exports a summary.
Job & Placement Actions
components/recruiterflow/actions/create-job/create-job.mjs, components/recruiterflow/actions/create-placement/create-placement.mjs
Added create-job (comprehensive job fields, JSON parsing for hiringTeam/customFields, maps props to snake_case API payload) and create-placement (required: userId, candidateId, jobId, jobStartDate; many optional date/financial/work/revenue/custom fields; conditional nested payload construction; uses utils.parseJson). Both call app methods and export summaries.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Runner as Task Runner ($)
    participant Action as Action Module (run)
    participant Utils as utils.parseJson
    participant App as recruiterflow.app
    participant API as Recruiterflow API

    Note over Runner,Action: Action invoked with props
    Runner->>Action: invoke run({ $ }) with props
    alt complex JSON inputs present
        Action->>Utils: parseJson(complex inputs)
        Utils-->>Action: parsed objects
    end
    Action->>App: call method (e.g., createCandidate/createJob/addCandidateToJob/searchCandidates) with { $, data }
    App->>API: _makeRequest -> HTTP request (headers, payload)
    API-->>App: response
    App-->>Action: return response
    Action->>Runner: $.export("$summary", ...) and return response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Areas needing extra attention:
    • components/recruiterflow/recruiterflow.app.mjs_makeRequest behavior, HTTP error handling, endpoint paths/payload shapes, and correctness of async option implementations in propDefinitions.
    • components/recruiterflow/common/utils.mjs — recursive parseJson logic, maxDepth behavior, and circular reference protection.
    • Action payload mappings — consistent camelCase → snake_case conversions and conditional inclusion of optional fields in create-candidate, create-job, and create-placement.
    • Validation and mutual-exclusivity logic in move-candidate-to-stage.
    • components/recruiterflow/package.json — version bump and added dependency.

Pre-merge checks and finishing touches

❌ Failed checks (3 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description only contains 'Resolves #15118' with no detailed explanation of the changes. The template requires a 'WHY' section that is missing substantive content. Expand the description to explain why these changes are needed, what problems they solve, and key implementation details or design decisions made.
Linked Issues check ⚠️ Warning The PR implements 6 new actions (add-candidate-to-job, create-candidate, create-job, create-placement, move-candidate-to-stage, search-candidates) but linked issue #15118 requires 3 polling sources and 1 save-data action only. Either implement the 3 polling sources (new-candidate-added-to-job-instant, new-candidate-created-instant, new-candidate-stage-changed-instant) and save-data action as specified, or clarify if this PR addresses different objectives than issue #15118.
Out of Scope Changes check ⚠️ Warning The PR introduces 6 action components and API utilities that significantly exceed the scope of linked issue #15118, which specifies 3 polling sources and 1 save-data action only. Verify whether these additional actions (add-candidate-to-job, create-candidate, create-job, create-placement, move-candidate-to-stage, search-candidates) are intentional enhancements beyond the original issue scope, or if they should be addressed in a separate PR.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '[ACTIONS] recruiterflow - new components' clearly describes the addition of new action components for the Recruiterflow integration.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch recruiterflow-new-components

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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d6edb6e and d89b36f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1 hunks)
  • components/recruiterflow/actions/create-candidate/create-candidate.mjs (1 hunks)
  • components/recruiterflow/actions/create-job/create-job.mjs (1 hunks)
  • components/recruiterflow/actions/create-placement/create-placement.mjs (1 hunks)
  • components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1 hunks)
  • components/recruiterflow/actions/search-candidates/search-candidates.mjs (1 hunks)
  • components/recruiterflow/common/utils.mjs (1 hunks)
  • components/recruiterflow/package.json (2 hunks)
  • components/recruiterflow/recruiterflow.app.mjs (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2024-10-30T15:24:39.294Z
Learnt from: jcortes
Repo: PipedreamHQ/pipedream PR: 14467
File: components/gainsight_px/actions/create-account/create-account.mjs:4-6
Timestamp: 2024-10-30T15:24:39.294Z
Learning: In `components/gainsight_px/actions/create-account/create-account.mjs`, the action name should be "Create Account" instead of "Create Memory".

Applied to files:

  • components/recruiterflow/actions/create-candidate/create-candidate.mjs
  • components/recruiterflow/actions/create-placement/create-placement.mjs
  • components/recruiterflow/actions/create-job/create-job.mjs
📚 Learning: 2024-12-12T19:23:09.039Z
Learnt from: jcortes
Repo: PipedreamHQ/pipedream PR: 14935
File: components/sailpoint/package.json:15-18
Timestamp: 2024-12-12T19:23:09.039Z
Learning: When developing Pipedream components, do not add built-in Node.js modules like `fs` to `package.json` dependencies, as they are native modules provided by the Node.js runtime.

Applied to files:

  • components/recruiterflow/package.json
📚 Learning: 2025-06-04T17:52:05.780Z
Learnt from: GTFalcao
Repo: PipedreamHQ/pipedream PR: 16954
File: components/salesloft/salesloft.app.mjs:14-23
Timestamp: 2025-06-04T17:52:05.780Z
Learning: In the Salesloft API integration (components/salesloft/salesloft.app.mjs), the _makeRequest method returns response.data which directly contains arrays for list endpoints like listPeople, listCadences, listUsers, and listAccounts. The propDefinitions correctly call .map() directly on these responses without needing to destructure a nested data property.

Applied to files:

  • components/recruiterflow/recruiterflow.app.mjs
📚 Learning: 2025-09-15T22:01:11.472Z
Learnt from: GTFalcao
Repo: PipedreamHQ/pipedream PR: 18362
File: components/leonardo_ai/actions/generate-image/generate-image.mjs:103-105
Timestamp: 2025-09-15T22:01:11.472Z
Learning: In Pipedream components, pipedream/platform's axios implementation automatically excludes undefined values from HTTP requests, so there's no need to manually check for truthiness before including properties in request payloads.

Applied to files:

  • components/recruiterflow/recruiterflow.app.mjs
🧬 Code graph analysis (6)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (5)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)
  • response (158-188)
components/recruiterflow/actions/create-job/create-job.mjs (1)
  • response (160-163)
components/recruiterflow/actions/create-placement/create-placement.mjs (1)
  • response (349-352)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1)
  • response (107-110)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (1)
  • response (276-279)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (3)
components/recruiterflow/actions/create-placement/create-placement.mjs (2)
  • data (342-347)
  • response (349-352)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)
  • response (47-54)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)
  • response (158-188)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (6)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)
  • response (47-54)
components/recruiterflow/actions/create-job/create-job.mjs (1)
  • response (160-163)
components/recruiterflow/actions/create-placement/create-placement.mjs (1)
  • response (349-352)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1)
  • response (107-110)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (1)
  • response (276-279)
components/xola/xola.app.mjs (1)
  • experience (102-107)
components/recruiterflow/actions/create-placement/create-placement.mjs (5)
components/recruiterflow/actions/create-job/create-job.mjs (2)
  • data (136-150)
  • response (160-163)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (2)
  • data (86-89)
  • response (107-110)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (2)
  • data (265-270)
  • response (276-279)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)
  • response (47-54)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)
  • response (158-188)
components/recruiterflow/actions/create-job/create-job.mjs (1)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)
  • response (158-188)
components/recruiterflow/recruiterflow.app.mjs (4)
components/recruiterflow/actions/create-job/create-job.mjs (1)
  • data (136-150)
components/recruiterflow/actions/create-placement/create-placement.mjs (1)
  • data (342-347)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1)
  • data (86-89)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (1)
  • data (265-270)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Lint Code Base
  • GitHub Check: Verify TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (9)
components/recruiterflow/package.json (1)

3-17: LGTM!

The version bump to 0.1.0 is appropriate for adding new features, and the @pipedream/platform dependency is correctly added.

components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1)

61-116: LGTM!

The validation logic properly ensures mutual exclusivity between stageId and stageName, and correctly enforces jobId requirement when using stageName. The payload construction and API call follow the established patterns in this PR.

components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)

39-58: LGTM!

Clean and straightforward implementation that follows the established patterns in this PR. The payload construction correctly maps prop names to API field names.

components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)

134-192: LGTM!

The implementation is well-structured with comprehensive field handling. The conditional location wrapping and use of utils.parseJson for complex fields (experience, education, customFields) follows the expected patterns. Props are well-documented with format examples.

components/recruiterflow/actions/create-placement/create-placement.mjs (1)

198-356: LGTM!

The run implementation handles the complex placement payload construction well. The nested structures for financial fields, work quantum, and contacts are properly built. The use of utils.parseJson for revenueSplit and customFields is consistent with other actions.

components/recruiterflow/actions/create-job/create-job.mjs (1)

116-167: LGTM!

The run method correctly destructures props, builds the payload with snake_case keys, conditionally parses JSON fields, and provides a clear summary message. The pattern is consistent with other actions in this integration.

components/recruiterflow/actions/search-candidates/search-candidates.mjs (2)

10-128: LGTM!

The props are well-structured with clear documentation. The dual approach (advanced filters array vs simple search fields) gives users flexibility. Default values for pagination and search options are sensible.


135-287: LGTM!

The run method cleanly handles both advanced filter mode and simple search mode. The filter-building logic is well-organized, and the summary message helpfully includes both the result count and total count when available.

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

124-237: LGTM!

The API methods are well-structured with a clean separation of concerns:

  • _baseUrl() and _headers() provide consistent configuration
  • _makeRequest() centralizes HTTP handling
  • Individual methods are thin wrappers that specify endpoint paths and HTTP methods

This pattern makes the codebase maintainable and easy to extend.

@jcortes jcortes force-pushed the recruiterflow-new-components branch from d89b36f to d4e0cbd Compare December 17, 2025 19: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: 8

♻️ Duplicate comments (2)
components/recruiterflow/actions/create-job/create-job.mjs (2)

94-103: Verify propDefinition array handling for contacts.

Similar to the locations prop, contacts is typed as string[] but uses propDefinition: [app, "contactId"] which likely returns a single ID. Verify the propDefinition supports multiple selections.


55-63: The locations prop type and propDefinition are mismatched.

The prop is typed as "string[]" but references the single-value locationId propDefinition, which will only allow selecting one location. Either use a propDefinition that supports multiple selections or remove the array type if only single locations are supported.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d89b36f and d4e0cbd.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (9)
  • components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1 hunks)
  • components/recruiterflow/actions/create-candidate/create-candidate.mjs (1 hunks)
  • components/recruiterflow/actions/create-job/create-job.mjs (1 hunks)
  • components/recruiterflow/actions/create-placement/create-placement.mjs (1 hunks)
  • components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1 hunks)
  • components/recruiterflow/actions/search-candidates/search-candidates.mjs (1 hunks)
  • components/recruiterflow/common/utils.mjs (1 hunks)
  • components/recruiterflow/package.json (2 hunks)
  • components/recruiterflow/recruiterflow.app.mjs (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2024-10-30T15:24:39.294Z
Learnt from: jcortes
Repo: PipedreamHQ/pipedream PR: 14467
File: components/gainsight_px/actions/create-account/create-account.mjs:4-6
Timestamp: 2024-10-30T15:24:39.294Z
Learning: In `components/gainsight_px/actions/create-account/create-account.mjs`, the action name should be "Create Account" instead of "Create Memory".

Applied to files:

  • components/recruiterflow/actions/create-placement/create-placement.mjs
  • components/recruiterflow/actions/create-candidate/create-candidate.mjs
  • components/recruiterflow/actions/create-job/create-job.mjs
📚 Learning: 2024-12-12T19:23:09.039Z
Learnt from: jcortes
Repo: PipedreamHQ/pipedream PR: 14935
File: components/sailpoint/package.json:15-18
Timestamp: 2024-12-12T19:23:09.039Z
Learning: When developing Pipedream components, do not add built-in Node.js modules like `fs` to `package.json` dependencies, as they are native modules provided by the Node.js runtime.

Applied to files:

  • components/recruiterflow/package.json
📚 Learning: 2025-06-04T17:52:05.780Z
Learnt from: GTFalcao
Repo: PipedreamHQ/pipedream PR: 16954
File: components/salesloft/salesloft.app.mjs:14-23
Timestamp: 2025-06-04T17:52:05.780Z
Learning: In the Salesloft API integration (components/salesloft/salesloft.app.mjs), the _makeRequest method returns response.data which directly contains arrays for list endpoints like listPeople, listCadences, listUsers, and listAccounts. The propDefinitions correctly call .map() directly on these responses without needing to destructure a nested data property.

Applied to files:

  • components/recruiterflow/recruiterflow.app.mjs
📚 Learning: 2025-09-15T22:01:11.472Z
Learnt from: GTFalcao
Repo: PipedreamHQ/pipedream PR: 18362
File: components/leonardo_ai/actions/generate-image/generate-image.mjs:103-105
Timestamp: 2025-09-15T22:01:11.472Z
Learning: In Pipedream components, pipedream/platform's axios implementation automatically excludes undefined values from HTTP requests, so there's no need to manually check for truthiness before including properties in request payloads.

Applied to files:

  • components/recruiterflow/recruiterflow.app.mjs
🧬 Code graph analysis (7)
components/recruiterflow/common/utils.mjs (1)
components/shopify/actions/update-order/update-order.mjs (1)
  • input (56-58)
components/recruiterflow/actions/create-placement/create-placement.mjs (3)
components/recruiterflow/actions/create-job/create-job.mjs (2)
  • data (137-151)
  • response (161-164)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)
  • response (47-54)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)
  • response (158-188)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (5)
components/recruiterflow/actions/create-job/create-job.mjs (2)
  • data (137-151)
  • response (161-164)
components/recruiterflow/actions/create-placement/create-placement.mjs (2)
  • data (342-347)
  • response (349-352)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (2)
  • data (265-270)
  • response (276-279)
components/recruiterflow/recruiterflow.app.mjs (4)
  • data (12-17)
  • data (29-34)
  • data (46-51)
  • data (63-68)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)
  • response (47-54)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (1)
components/recruiterflow/recruiterflow.app.mjs (4)
  • data (12-17)
  • data (29-34)
  • data (46-51)
  • data (63-68)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (7)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)
  • response (47-54)
components/recruiterflow/actions/create-job/create-job.mjs (1)
  • response (161-164)
components/recruiterflow/actions/create-placement/create-placement.mjs (1)
  • response (349-352)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1)
  • response (113-116)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (1)
  • response (276-279)
components/jira/actions/update-issue/update-issue.mjs (1)
  • utils (166-176)
components/xola/xola.app.mjs (1)
  • experience (102-107)
components/recruiterflow/actions/create-job/create-job.mjs (1)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)
  • response (158-188)
components/recruiterflow/recruiterflow.app.mjs (4)
components/recruiterflow/actions/create-job/create-job.mjs (1)
  • data (137-151)
components/recruiterflow/actions/create-placement/create-placement.mjs (1)
  • data (342-347)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1)
  • data (92-95)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (1)
  • data (265-270)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Lint Code Base
🔇 Additional comments (18)
components/recruiterflow/package.json (1)

1-18: LGTM!

Version bump and dependency addition are appropriate for the new Recruiterflow integration features.

components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)

1-59: LGTM!

The action is well-structured with appropriate prop definitions, clear description, and correct API payload mapping.

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

157-174: LGTM!

The internal HTTP helper layer (_baseUrl, _headers, _makeRequest) is well-structured and follows Pipedream conventions for API integrations.


75-86: Inconsistent response handling between list methods and missing pagination support.

The propDefinitions show inconsistent patterns: candidateId, jobId, companyId, and contactId use pagination parameters and call their list methods directly (e.g., const data = await this.listCandidates()), while userId, locationId, departmentId, and employmentTypeId omit pagination and destructure the response (e.g., const { data } = await this.listUsers()).

This inconsistency suggests the API endpoints may return different response structures. Additionally, the four non-paginated propDefinitions will truncate results if there are more records available—add pagination support to these endpoints or verify the API enforces a fixed limit.

components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1)

67-123: LGTM on validation logic and payload construction.

The mutual exclusivity validation between stageId and stageName, along with the conditional jobId requirement, is implemented correctly with clear error messages.

components/recruiterflow/actions/create-candidate/create-candidate.mjs (2)

134-192: LGTM on overall structure and payload mapping.

The action correctly maps camelCase props to snake_case API fields and uses utils.parseJson appropriately for complex array fields.


166-173: Verify location field structure against Recruiterflow API specification.

The payload creates a nested structure { location: { location: "value" } }. While this pattern is unusual, it cannot be confirmed as incorrect without access to the Recruiterflow API documentation. Please verify this matches the expected format in the API specification at https://recruiterflow.com/api or confirm through testing.

components/recruiterflow/actions/create-placement/create-placement.mjs (1)

198-356: LGTM on overall placement payload construction.

The action handles the complex nested structures (monetary fields, rates, work quantum) correctly, and the conditional field attachment pattern is clean.

components/recruiterflow/actions/create-job/create-job.mjs (3)

1-14: LGTM!

The imports and action metadata are correctly structured. The action name follows proper conventions.


154-159: LGTM - Good conditional parsing logic!

The conditional parsing for hiringTeam and customFields correctly ensures these complex fields are only added when provided. This pattern avoids sending null/undefined values to the API.


161-168: LGTM - Proper API call and response handling!

The action correctly invokes app.createJob, exports a meaningful summary, and returns the response.

components/recruiterflow/actions/search-candidates/search-candidates.mjs (7)

1-9: LGTM!

Imports and metadata are properly structured. The description clearly explains the dual-mode search capability.


12-52: Well-structured search controls!

The conjunction, pagination, and includeCount props provide good control over search behavior with sensible defaults.


53-128: Comprehensive simple search field coverage!

The simple search props cover all common candidate search criteria with clear labels and descriptions. The boolean flags for text search (includeNotes, includeFiles, includeEmails) provide good granular control.


129-134: Appropriate annotations for a search action!

The annotations correctly mark this as a read-only, non-destructive, idempotent action that calls external APIs.


165-263: Well-implemented filter building logic!

The simple filter construction is thorough and correct:

  • Each field properly builds its filter object with appropriate type and structure
  • The textSearch implementation intelligently splits by spaces and filters empty strings
  • All API field names use proper snake_case
  • Filter types ("text", "multi-select") are correctly chosen

265-279: Clean payload construction and API call!

The data payload is properly structured with snake_case keys, and the conditional filter attachment (lines 272-274) correctly avoids sending an empty filters array when no filters are present.


281-287: Robust response handling with informative summary!

The response handling uses safe optional chaining (response.data?.length) and conditionally includes the total count in the summary when available, providing clear feedback to users.

luancazarine
luancazarine previously approved these changes Dec 18, 2025
Copy link
Collaborator

@luancazarine luancazarine left a comment

Choose a reason for hiding this comment

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

Hi @jcortes, I just added a small suggestion, but I'll move it to Ready for QA anyway

@vunguyenhung
Copy link
Collaborator

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

Please check test reports below for more information:

@jcortes jcortes force-pushed the recruiterflow-new-components branch from f01094a to a39b4e8 Compare December 19, 2025 14:44
@vunguyenhung
Copy link
Collaborator

Hi everyone, all test cases are passed! Ready for release!

Test reports

@jcortes
Copy link
Collaborator Author

jcortes commented Dec 19, 2025

/approve

@jcortes jcortes force-pushed the recruiterflow-new-components branch from a39b4e8 to 146df99 Compare December 19, 2025 15:38
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/recruiterflow/actions/search-candidates/search-candidates.mjs (1)

28-33: Fix type inconsistency for filters prop.

The filters prop has type: "string[]" but line 161 calls utils.parseJson(filters) on the entire array, not on individual elements. This creates a type/usage mismatch.

Either change to type: "string" and expect a single JSON-encoded array, or keep type: "string[]" and map utils.parseJson over each element.

components/recruiterflow/common/utils.mjs (1)

1-40: Fix depth tracking for object/array recursion.

The maxDepth parameter is only decremented when parsing JSON strings (line 15) but not when recursing into objects (line 31) or arrays (line 34). This allows deeply nested structures to bypass depth protection, potentially causing stack overflow.

Additionally, arrays are not added to the circular reference WeakSet, which could cause infinite recursion if an array contains itself.

🔎 Recommended fixes

Update the function to track depth properly and include arrays in circular detection:

-const parseJson = (input, maxDepth = 100) => {
+const parseJson = (input, maxDepth = 100, _currentDepth = 0) => {
   const seen = new WeakSet();
-  const parse = (value) => {
-    if (maxDepth <= 0) {
+  const parse = (value, depth) => {
+    if (depth >= maxDepth) {
       return value;
     }
     if (typeof(value) === "string") {
       const trimmed = value.trim();
       if (
         (trimmed.startsWith("{") && trimmed.endsWith("}")) ||
         (trimmed.startsWith("[") && trimmed.endsWith("]"))
       ) {
         try {
-          return parseJson(JSON.parse(value), maxDepth - 1);
+          return parseJson(JSON.parse(value), maxDepth, depth + 1);
         } catch (e) {
           return value;
         }
       }
       return value;
     } else if (typeof(value) === "object" && value !== null && !Array.isArray(value)) {
       if (seen.has(value)) {
         return value;
       }
       seen.add(value);
       return Object.entries(value)
         .reduce((acc, [key, val]) => Object.assign(acc, {
-          [key]: parse(val),
+          [key]: parse(val, depth + 1),
         }), {});
     } else if (Array.isArray(value)) {
+      if (seen.has(value)) {
+        return value;
+      }
+      seen.add(value);
-      return value.map((item) => parse(item));
+      return value.map((item) => parse(item, depth + 1));
     }
     return value;
   };

-  return parse(input);
+  return parse(input, _currentDepth);
 };
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (2)

25-37: Hardcoded stage names limit flexibility.

The stage options are hardcoded to common values, which won't work for organizations with custom pipeline stages. Consider making this a freeform string input or dynamically fetching stages via getJobPipeline when jobId is provided.


47-54: Mark userId as optional.

The userId prop is conditionally included in the payload (lines 105-107), indicating it should be optional. However, it's not marked with optional: true, making it required in the UI.

🔎 Proposed fix
     userId: {
       propDefinition: [
         app,
         "userId",
       ],
       label: "User ID",
       description: "The ID of the user moving the candidate",
+      optional: true,
     },
components/recruiterflow/actions/create-job/create-job.mjs (1)

104-109: Type inconsistency for hiringTeam prop remains.

As noted in the previous review, hiringTeam is typed as string[] but utils.parseJson(hiringTeam) on line 155 expects either a single JSON string or requires individual parsing of array elements. Please refer to the earlier review comment for the recommended fix.

Also applies to: 153-156

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a39b4e8 and 146df99.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (10)
  • components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1 hunks)
  • components/recruiterflow/actions/create-candidate/create-candidate.mjs (1 hunks)
  • components/recruiterflow/actions/create-job/create-job.mjs (1 hunks)
  • components/recruiterflow/actions/create-placement/create-placement.mjs (1 hunks)
  • components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1 hunks)
  • components/recruiterflow/actions/search-candidates/search-candidates.mjs (1 hunks)
  • components/recruiterflow/common/constants.mjs (1 hunks)
  • components/recruiterflow/common/utils.mjs (1 hunks)
  • components/recruiterflow/package.json (2 hunks)
  • components/recruiterflow/recruiterflow.app.mjs (1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📚 Learning: 2024-10-30T15:24:39.294Z
Learnt from: jcortes
Repo: PipedreamHQ/pipedream PR: 14467
File: components/gainsight_px/actions/create-account/create-account.mjs:4-6
Timestamp: 2024-10-30T15:24:39.294Z
Learning: In `components/gainsight_px/actions/create-account/create-account.mjs`, the action name should be "Create Account" instead of "Create Memory".

Applied to files:

  • components/recruiterflow/actions/create-placement/create-placement.mjs
  • components/recruiterflow/actions/create-job/create-job.mjs
  • components/recruiterflow/actions/create-candidate/create-candidate.mjs
📚 Learning: 2025-09-11T01:53:51.070Z
Learnt from: GTFalcao
Repo: PipedreamHQ/pipedream PR: 18334
File: components/buddee/actions/list-employees/list-employees.mjs:147-155
Timestamp: 2025-09-11T01:53:51.070Z
Learning: In Buddee list-employees action, the "manager" prop should be a boolean type to filter employees who have direct reports, not an employeeId propDefinition which would send an employee ID instead of the expected boolean value.

Applied to files:

  • components/recruiterflow/actions/create-placement/create-placement.mjs
  • components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs
  • components/recruiterflow/actions/create-job/create-job.mjs
📚 Learning: 2025-07-01T17:07:48.193Z
Learnt from: js07
Repo: PipedreamHQ/pipedream PR: 17375
File: components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs:23-27
Timestamp: 2025-07-01T17:07:48.193Z
Learning: For "dir" props in Pipedream components, whether to mark them as optional depends on the action's file I/O behavior - if an action always writes files as output, the "dir" prop should not be marked as optional.

Applied to files:

  • components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs
📚 Learning: 2025-10-20T01:01:02.970Z
Learnt from: js07
Repo: PipedreamHQ/pipedream PR: 18744
File: components/slack_v2/actions/send-large-message/send-large-message.mjs:49-64
Timestamp: 2025-10-20T01:01:02.970Z
Learning: In components/slack_v2/actions/send-large-message/send-large-message.mjs, the metadata_event_payload prop is typed as string, so the code only needs to handle string-to-JSON parsing and does not need to handle object inputs.

Applied to files:

  • components/recruiterflow/actions/create-job/create-job.mjs
📚 Learning: 2025-09-15T22:01:11.472Z
Learnt from: GTFalcao
Repo: PipedreamHQ/pipedream PR: 18362
File: components/leonardo_ai/actions/generate-image/generate-image.mjs:103-105
Timestamp: 2025-09-15T22:01:11.472Z
Learning: In Pipedream components, pipedream/platform's axios implementation automatically excludes undefined values from HTTP requests, so there's no need to manually check for truthiness before including properties in request payloads.

Applied to files:

  • components/recruiterflow/actions/create-job/create-job.mjs
  • components/recruiterflow/recruiterflow.app.mjs
📚 Learning: 2024-12-12T19:23:09.039Z
Learnt from: jcortes
Repo: PipedreamHQ/pipedream PR: 14935
File: components/sailpoint/package.json:15-18
Timestamp: 2024-12-12T19:23:09.039Z
Learning: When developing Pipedream components, do not add built-in Node.js modules like `fs` to `package.json` dependencies, as they are native modules provided by the Node.js runtime.

Applied to files:

  • components/recruiterflow/package.json
📚 Learning: 2025-06-04T17:52:05.780Z
Learnt from: GTFalcao
Repo: PipedreamHQ/pipedream PR: 16954
File: components/salesloft/salesloft.app.mjs:14-23
Timestamp: 2025-06-04T17:52:05.780Z
Learning: In the Salesloft API integration (components/salesloft/salesloft.app.mjs), the _makeRequest method returns response.data which directly contains arrays for list endpoints like listPeople, listCadences, listUsers, and listAccounts. The propDefinitions correctly call .map() directly on these responses without needing to destructure a nested data property.

Applied to files:

  • components/recruiterflow/recruiterflow.app.mjs
🧬 Code graph analysis (7)
components/recruiterflow/actions/create-placement/create-placement.mjs (1)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)
  • response (158-188)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (2)
components/recruiterflow/actions/create-placement/create-placement.mjs (2)
  • data (344-349)
  • response (351-354)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)
  • response (47-54)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (5)
components/recruiterflow/actions/create-placement/create-placement.mjs (1)
  • response (351-354)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)
  • response (158-188)
components/recruiterflow/actions/create-job/create-job.mjs (1)
  • response (161-164)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1)
  • response (113-116)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (1)
  • response (276-279)
components/recruiterflow/actions/create-job/create-job.mjs (5)
components/recruiterflow/actions/create-placement/create-placement.mjs (2)
  • data (344-349)
  • response (351-354)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (2)
  • data (92-95)
  • response (113-116)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (2)
  • data (265-270)
  • response (276-279)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)
  • response (47-54)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)
  • response (158-188)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (1)
components/recruiterflow/recruiterflow.app.mjs (4)
  • data (13-18)
  • data (30-35)
  • data (47-52)
  • data (64-69)
components/recruiterflow/actions/create-candidate/create-candidate.mjs (5)
components/recruiterflow/actions/create-placement/create-placement.mjs (1)
  • response (351-354)
components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)
  • response (47-54)
components/recruiterflow/actions/create-job/create-job.mjs (1)
  • response (161-164)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1)
  • response (113-116)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (1)
  • response (276-279)
components/recruiterflow/recruiterflow.app.mjs (4)
components/recruiterflow/actions/create-placement/create-placement.mjs (1)
  • data (344-349)
components/recruiterflow/actions/create-job/create-job.mjs (1)
  • data (137-151)
components/recruiterflow/actions/move-candidate-to-stage/move-candidate-to-stage.mjs (1)
  • data (92-95)
components/recruiterflow/actions/search-candidates/search-candidates.mjs (1)
  • data (265-270)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Publish TypeScript components
  • GitHub Check: pnpm publish
  • GitHub Check: Lint Code Base
🔇 Additional comments (10)
components/recruiterflow/package.json (1)

3-16: LGTM!

The version bump to 0.1.0 and the addition of the @pipedream/platform dependency are appropriate for the new action modules being introduced in this PR.

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

1-13: LGTM!

The constants are well-defined. DEFAULT_LIMIT is used for pagination in the app's propDefinitions, and RATE_FREQUENCIES is available for rate-related functionality.

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

158-272: LGTM!

The API methods follow a consistent pattern using the centralized _makeRequest wrapper. The implementation is clean and follows best practices for Pipedream app definitions.


7-157: No changes needed. The response structure inconsistency across propDefinitions is intentional and correctly handled. Paginated endpoints (listCandidates, listJobs, listCompanies, listContacts) return arrays directly in response.data, while non-paginated endpoints (listUsers, listLocations, listDepartments, listEmploymentTypes) return objects with a nested data property. The code properly destructures or assigns accordingly based on each endpoint's actual API response format.

components/recruiterflow/actions/add-candidate-to-job/add-candidate-to-job.mjs (1)

1-59: LGTM!

The action is well-structured with clear prop definitions and follows the established pattern for Recruiterflow actions. The payload construction and API call are correct.

components/recruiterflow/actions/create-candidate/create-candidate.mjs (1)

1-193: LGTM!

The action is comprehensive with well-defined props covering all candidate fields. The payload construction correctly uses utils.parseJson for complex fields (experience, education, customFields), and the conditional location nesting is handled appropriately.

Based on learnings, the platform automatically excludes undefined values from requests, so the conditional spread pattern is valid.

components/recruiterflow/actions/create-placement/create-placement.mjs (2)

1-10: LGTM! Action metadata and imports are properly configured.

The action name, key, description, and imports follow Pipedream conventions correctly.


234-305: Core placement construction logic is sound.

The payload assembly correctly handles required and optional fields, properly nesting monetary values, rates, and work quantum data. The API call pattern is consistent with other actions in this PR.

Past review comments already cover the remaining concerns (workQuantumNumber truthy check, contactIds type mismatch, paired monetary field validation).

Also applies to: 344-358

components/recruiterflow/actions/create-job/create-job.mjs (2)

1-48: LGTM! Action metadata and core props are well-defined.

The action structure follows Pipedream conventions, and required fields use appropriate propDefinitions from the app.


137-169: Core job creation logic is correct.

The data payload construction properly maps all fields to their API equivalents. While optional fields may be undefined in the payload, Pipedream's platform automatically excludes undefined values from HTTP requests, so the earlier nitpick about filtering them is not a concern.

The API call pattern is consistent with other actions in this PR.

@jcortes jcortes merged commit ecb1f17 into master Dec 19, 2025
9 checks passed
@jcortes jcortes deleted the recruiterflow-new-components branch December 19, 2025 17:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Components] recruiterflow

4 participants