Skip to content

Conversation

@michelle0927
Copy link
Collaborator

@michelle0927 michelle0927 commented Aug 27, 2025

Resolves #18089

Summary by CodeRabbit

  • New Features
    • Added BambooHR actions: List Applications (with filters, sorting, pagination), Get Application, Update Application Status, Add Application Comment, and Download Resume.
    • Enabled resume download to your workflow’s file system.
    • Improved BambooHR app inputs with dynamic pickers for Application, Job, and Status populated from your account.
  • Chores
    • Bumped BambooHR package version to 0.7.0.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 27, 2025

Walkthrough

Adds BambooHR integration capabilities: app client with request helpers and dynamic prop definitions; new actions to list and get applications, download a resume file, update application status, and add an application comment; shared constants; package version bump.

Changes

Cohort / File(s) Change Summary
BambooHR App Client
components/bamboohr/bamboohr.app.mjs
Replaces stub with axios-based client. Adds base URL/auth, request wrapper, and methods: get/list applications, list jobs/statuses, add comment, update status, download file. Adds dynamic propDefinitions for applicationId, jobId, statusId. Removes authKeys().
Application Actions
components/bamboohr/actions/get-application/get-application.mjs, components/bamboohr/actions/list-applications/list-applications.mjs
New actions: fetch a single application; list applications with filtering/sorting via constants and propDefinitions.
Resume Download Action
components/bamboohr/actions/download-resume/download-resume.mjs
New action to download an application’s resume file: fetches application, validates resumeFileId, downloads file, writes to /tmp/<filename>, returns file info. Uses ConfigurationError.
Application Mutations
components/bamboohr/actions/update-application-status/update-application-status.mjs, components/bamboohr/actions/add-application-comment/add-application-comment.mjs
New actions to update application status and add a comment via BambooHR client helpers; export summaries and return API responses.
Shared Constants
components/bamboohr/common/constants.mjs
Adds arrays: APPLICATION_STATUS_GROUPS, JOB_STATUS_GROUPS, APPLICATION_SORT_FIELDS; default export aggregates them.
Package Metadata
components/bamboohr/package.json
Bumps version from 0.6.0 to 0.7.0.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Action as Download Resume Action
  participant App as BambooHR App Client
  participant API as BambooHR API
  participant FS as File System (/tmp)

  User->>Action: Run with applicationId, filename
  Action->>App: getApplication(applicationId)
  App->>API: GET /applications/{id}
  API-->>App: 200 { resumeFileId }
  App-->>Action: application details
  Action->>Action: validate resumeFileId or throw ConfigurationError
  Action->>App: downloadFile(fileId)
  App->>API: GET /files/{fileId} (arraybuffer)
  API-->>App: 200 binary
  App-->>Action: binary buffer
  Action->>FS: write /tmp/filename
  Action-->>User: { filename, downloadedFilepath }
  Note over Action: Exports "$summary": Downloaded resume
Loading
sequenceDiagram
  autonumber
  actor User
  participant Action as Update Application Status Action
  participant App as BambooHR App Client
  participant API as BambooHR API

  User->>Action: Run with applicationId, statusId
  Action->>App: updateApplicationStatus(applicationId, { status })
  App->>API: POST /applications/{id}/status
  API-->>App: 200 OK
  App-->>Action: response
  Action-->>User: response
  Note over Action: Exports "$summary": Updated status
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Bulk download candidate resumes [#18089] Only single-application resume download is implemented; no bulk or batching utility is added.
Retrieve candidate profile fields (name, contact, job applied, links, attachments) [#18089] Actions return application data, but it’s unclear if all specified fields/attachments are exposed.
Optionally update candidate status or tags programmatically [#18089]

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Add Application Comment action (components/bamboohr/actions/add-application-comment/add-application-comment.mjs; entire file) Commenting on applications isn’t requested by the issue; objectives mention resumes, data access, and status/tags only.

Poem

A rabbit taps keys with caffeinated cheer,
New BambooHR tricks now hop in here! 🐇
Fetch, list, and download—resumes in tow,
Update statuses—onward we go.
Soon: bulk bounds and fields galore—
I twitch my whiskers, asking for more!

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-18089

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

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • 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.
  • 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 the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

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

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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.

@vercel
Copy link

vercel bot commented Aug 27, 2025

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

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
pipedream-docs Ignored Ignored Aug 27, 2025 5:36pm
pipedream-docs-redirect-do-not-edit Ignored Ignored Aug 27, 2025 5:36pm

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 (9)
components/bamboohr/common/constants.mjs (2)

10-19: Normalize job status values or decouple display vs API values.

Mixed casing ("Open", "On Hold") vs upper snake ("ALL", "DRAFT_AND_OPEN") risks passing invalid filters downstream. Prefer label/value pairs to keep UI labels human-friendly while sending canonical API values.

Apply if API expects uppercase codes:

-const JOB_STATUS_GROUPS = [
-  "ALL",
-  "DRAFT_AND_OPEN",
-  "Open",
-  "Filled",
-  "Draft",
-  "Deleted",
-  "On Hold",
-  "Canceled",
-];
+const JOB_STATUS_GROUPS = [
+  { label: "All", value: "ALL" },
+  { label: "Draft and Open", value: "DRAFT_AND_OPEN" },
+  { label: "Open", value: "OPEN" },
+  { label: "Filled", value: "FILLED" },
+  { label: "Draft", value: "DRAFT" },
+  { label: "Deleted", value: "DELETED" },
+  { label: "On Hold", value: "ON_HOLD" },
+  { label: "Canceled", value: "CANCELED" },
+];

21-29: Validate sort field names.

Keys like "first_name" and "created_date" may need camelCase per API ("firstName", "createdDate"). Mismatch will break sorting.

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

11-21: Harden option labels and add identifiers for disambiguation.

Avoid crashing on missing names and make options distinguishable by showing the ID.

Apply:

-        return applications?.map((application) => ({
-          label: `${application.applicant.firstName} ${application.applicant.lastName}`,
-          value: application.id,
-        })) || [];
+        return applications?.map((application) => ({
+          label: `${application?.applicant?.firstName ?? "Unknown"} ${application?.applicant?.lastName ?? ""} (#${application?.id})`.trim(),
+          value: application?.id,
+        })) || [];

28-34: Improve job option labels and null-safety.

Include the job ID and guard optional properties.

-        return jobs?.map((job) => ({
-          label: job.title.label,
-          value: job.id,
-        })) || [];
+        return jobs?.map((job) => ({
+          label: `${job?.title?.label ?? "Untitled Job"} (#${job?.id})`,
+          value: job?.id,
+        })) || [];

37-46: Clarify description and label for status.

This refers to application status, not job status. Also include ID in label.

-      description: "The ID of a job status",
+      description: "The ID of an application status",
@@
-        return statuses?.map((status) => ({
-          label: status.name,
-          value: status.id,
-        })) || [];
+        return statuses?.map((status) => ({
+          label: `${status?.name ?? "Unknown Status"} (#${status?.id})`,
+          value: status?.id,
+        })) || [];

56-63: Set default Accept header and simplify auth assignment.

Prevents occasional 406s and avoids unnecessary template strings.

-      return axios($, {
+      return axios($, {
         url: `${this._baseUrl()}${path}`,
         auth: {
-          username: `${this.$auth.api_key}`,
+          username: this.$auth.api_key,
           password: "x",
         },
+        headers: {
+          Accept: "application/json",
+          ...(opts.headers || {}),
+        },
         ...opts,
       });
components/bamboohr/actions/add-application-comment/add-application-comment.mjs (1)

23-31: Validate non-empty comment and trim payload.

Prevents avoidable 4xx from the API and keeps stored text clean.

   async run({ $ }) {
-    const response = await this.bamboohr.addApplicationComment({
+    if (!this.comment?.trim()) {
+      throw new Error("Comment cannot be empty");
+    }
+    const response = await this.bamboohr.addApplicationComment({
       $,
       applicationId: this.applicationId,
       data: {
-        comment: this.comment,
+        comment: this.comment.trim(),
         type: "comment",
       },
     });
components/bamboohr/actions/download-resume/download-resume.mjs (1)

19-23: Align description with behavior.

Update the description to reflect saving into the synced directory rather than hardcoding /tmp.

-      description: "The filename to save the downloaded file as in the `/tmp` directory",
+      description: "The filename to save the downloaded file as within the selected synced directory",
components/bamboohr/actions/list-applications/list-applications.mjs (1)

90-91: Guard summary against missing applications array.

Prevent a runtime error if the response shape changes.

-    $.export("$summary", `Found ${response.applications.length} applications`);
+    $.export("$summary", `Found ${response?.applications?.length ?? 0} applications`);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8c66e63 and a6887ec.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • components/bamboohr/actions/add-application-comment/add-application-comment.mjs (1 hunks)
  • components/bamboohr/actions/download-resume/download-resume.mjs (1 hunks)
  • components/bamboohr/actions/get-application/get-application.mjs (1 hunks)
  • components/bamboohr/actions/list-applications/list-applications.mjs (1 hunks)
  • components/bamboohr/actions/update-application-status/update-application-status.mjs (1 hunks)
  • components/bamboohr/bamboohr.app.mjs (1 hunks)
  • components/bamboohr/common/constants.mjs (1 hunks)
  • components/bamboohr/package.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
components/bamboohr/actions/get-application/get-application.mjs (4)
components/bamboohr/actions/add-application-comment/add-application-comment.mjs (1)
  • response (24-31)
components/bamboohr/actions/download-resume/download-resume.mjs (1)
  • response (40-43)
components/bamboohr/actions/list-applications/list-applications.mjs (1)
  • response (76-89)
components/bamboohr/actions/update-application-status/update-application-status.mjs (1)
  • response (25-31)
components/bamboohr/actions/update-application-status/update-application-status.mjs (3)
components/bamboohr/actions/add-application-comment/add-application-comment.mjs (1)
  • response (24-31)
components/bamboohr/actions/get-application/get-application.mjs (1)
  • response (19-22)
components/bamboohr/actions/list-applications/list-applications.mjs (1)
  • response (76-89)
components/bamboohr/actions/download-resume/download-resume.mjs (1)
components/bamboohr/actions/get-application/get-application.mjs (1)
  • response (19-22)
components/bamboohr/actions/add-application-comment/add-application-comment.mjs (2)
components/bamboohr/actions/get-application/get-application.mjs (1)
  • response (19-22)
components/bamboohr/actions/update-application-status/update-application-status.mjs (1)
  • response (25-31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: pnpm publish
  • GitHub Check: Verify TypeScript components
  • GitHub Check: Lint Code Base
  • GitHub Check: Publish TypeScript components
🔇 Additional comments (11)
components/bamboohr/package.json (1)

3-3: SemVer bump looks appropriate.

New actions and non-breaking client changes justify a minor version bump to 0.7.0. No other concerns.

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

1-8: Confirm application status group tokens match BambooHR API.

Values like "ALL_ACTIVE" and "NEW" might differ from API enums. If these feed request params directly, please verify and align.

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

109-116: Return full HTTP response for file downloads

To ensure you can access the Content-Disposition (filename) and Content-Type (MIME type) headers—critical for bulk resume exports—update the downloadFile method as follows:

• File: components/bamboohr/bamboohr.app.mjs
• Method: downloadFile
• Change around lines 109–116:

    return this._makeRequest({
      path: `/files/${fileId}`,
      responseType: "arraybuffer",
+     returnFullResponse: true,
      ...opts,
    });

• After adding this flag, confirm that _makeRequest honors returnFullResponse and returns the full HTTP response object (including headers) instead of just response.data.
• If _makeRequest does not yet support this option, you’ll need to extend it to conditionally return the raw response when returnFullResponse: true.
• Finally, update any consumers of downloadFile that currently expect a Buffer (or raw data) to instead destructure the returned { data, headers } and adjust accordingly (e.g. parse headers['content-disposition'] and headers['content-type']).

components/bamboohr/actions/get-application/get-application.mjs (1)

3-26: Solid, minimal action wiring.

Props, call, and summary follow platform conventions. LGTM.

components/bamboohr/actions/add-application-comment/add-application-comment.mjs (1)

6-7: Confirm whether the type field is required and its allowed values.

If BambooHR defaults comment type or expects a specific enum, consider omitting or validating it accordingly.

components/bamboohr/actions/update-application-status/update-application-status.mjs (2)

24-33: Double-check payload key & enrich the summary

Before merging, confirm that BambooHR’s “Change Applicant’s Status” endpoint expects a JSON body of {"status": <id>} (not {"statusId": <id>}), since updateApplicationStatus({ …data }) simply spreads your data through to the request body via this._makeRequest(documentation.bamboohr.com).

Once you’ve verified the correct key, please update the action’s summary to include both IDs for clearer traceability:

   // components/bamboohr/actions/update-application-status/update-application-status.mjs
     async run({ $ }) {
       const response = await this.bamboohr.updateApplicationStatus({
         $,
         applicationId: this.applicationId,
         data: {
           status: this.statusId,
         },
       });
-      $.export("$summary", "Updated status of application.");
+      $.export("$summary", `Updated application ${this.applicationId} status to ${this.statusId}.`);
       return response;
  • Location: components/bamboohr/actions/update-application-status/update-application-status.mjs
  • Reference the client method here: components/bamboohr/bamboohr.app.mjs (lines 100–106)(documentation.bamboohr.com).
  • Once you’ve confirmed the payload shape against BambooHR’s ATS API docs, you’re good to go.

6-6: ✅ Confirmed: “Change Applicant’s Status” Endpoint and Payload

  • The slug post-applicant-status-1 correctly refers to the “Change Applicant’s Status” endpoint, not a generic “application” endpoint.
    URL:
    POST https://{companyDomain}.bamboohr.com/api/v1/applicant_tracking/applications/{applicationId}/status (documentation.bamboohr.com)

  • Request body (application/json) expects:
    status: the ID of the new status to apply (developers.getknit.dev)
    note (optional): a text note providing context for the change (documentation.bamboohr.com)

No update to the link or description is required.

components/bamboohr/actions/download-resume/download-resume.mjs (2)

24-29: Use syncDir and sanitize filename; avoid redundant base64 conversion.

  • syncDir is declared but unused; writing to /tmp ignores the synced directory.
  • Writing to /tmp/${this.filename} without sanitization permits path traversal.
  • The base64 round-trip is unnecessary if the client returns a Buffer.

Apply this diff within the selected ranges:

-    const rawcontent = response.toString("base64");
-    const buffer = Buffer.from(rawcontent, "base64");
-    const downloadedFilepath = `/tmp/${this.filename}`;
-    fs.writeFileSync(downloadedFilepath, buffer);
+    const safeFilename = path.basename(this.filename);
+    const downloadedFilepath = `${this.syncDir}/${safeFilename}`;
+    // response is expected to be a Buffer from the app client
+    fs.writeFileSync(downloadedFilepath, response);

And add the import for path (outside the selected range):

import path from "path";

If downloadFile might not return a Buffer, guard the write:

fs.writeFileSync(downloadedFilepath, Buffer.isBuffer(response) ? response : Buffer.from(response));

Also applies to: 45-49

⛔ Skipped due to learnings
Learnt from: js07
PR: PipedreamHQ/pipedream#17375
File: components/tinypng/actions/convert-image/convert-image.mjs:33-37
Timestamp: 2025-07-01T16:56:20.177Z
Learning: 'dir' props with sync functionality do not expose a path to the sync directory directly. For files to be synced, they must be either: (1) written to `process.env.STASH_DIR` (`/tmp/__stash`), or (2) written to `/tmp` and have their file path returned from the `run` method as a string, `[filepath]`, `[_, filepath]`, `{ filePath }`, `{ filepath }`, or `{ path }`.
Learnt from: js07
PR: PipedreamHQ/pipedream#17375
File: components/tinypng/actions/compress-image/compress-image.mjs:18-23
Timestamp: 2025-07-01T17:01:46.327Z
Learning: In TinyPNG compress-image action (components/tinypng/actions/compress-image/compress-image.mjs), the syncDir property uses accessMode: "read" because this action only reads input files and returns API responses without writing files to /tmp, unlike other TinyPNG actions that save processed files to disk.

31-38: Verify getApplication() response shape for resumeFileId

I inspected components/bamboohr/bamboohr.app.mjs around the getApplication method and it simply proxies the API response via this._makeRequest—there’s no code mapping or renaming of any resume or resumeFileId fields. I also ran searches for both resumeFileId and resume in that file and found no matches.

Please manually confirm against the BambooHR API (e.g., via the developer docs or by logging the raw response) that:

  • resumeFileId is returned at the top level of the JSON payload, as assumed by your destructure in download-resume.mjs;
  • if instead it’s nested (for example under application.resume.fileId), update the destructuring in components/bamboohr/actions/download-resume/download-resume.mjs accordingly.

For example, if the field is nested you might need:

- const { resumeFileId } = await this.bamboohr.getApplication({ /*…*/ });
+ const { resume: { fileId: resumeFileId } } = await this.bamboohr.getApplication({ /*…*/ });
components/bamboohr/actions/list-applications/list-applications.mjs (2)

62-67: Confirm BambooHR API parameter name & timestamp format

The current code defines the filter as newSince. According to BambooHR’s Recruiting API, the “Get Applications” endpoint uses the query parameter newOrUpdatedSince, and it requires an ISO 8601 UTC timestamp (for example, 2024-01-01T13:00:00Z). Please verify against the official BambooHR documentation and update the parameter name and format if needed.

• File: components/bamboohr/actions/list-applications/list-applications.mjs
– Lines 62–67


18-31: Clarify filter handling in list-applications action

The current implementation in components/bamboohr/actions/list-applications/list-applications.mjs unconditionally passes both

applicationStatusId: this.statusId,  // from statusId
applicationStatus: this.statusGroup, // from statusGroup

to the BambooHR API (lines 80–81). It’s not obvious which filter the API will honor when both are provided.

Please do one of the following:

• Consult the BambooHR API/SDK documentation to determine which parameter takes precedence when both are sent.
– If one wins (e.g. statusId overrides statusGroup), update the parameter descriptions to document that behavior.
– If the API rejects requests with both, or if it applies them in an unexpected way, enforce mutual exclusivity in the action’s run method (e.g. throw a validation error if both statusId and statusGroup are defined).

• Alternatively, decide on a sensible default (e.g. treat statusId as higher priority), implement that in code, and document it clearly in each property’s description.

This will prevent user confusion and avoid unintended API queries.

@vunguyenhung vunguyenhung merged commit 069b8f7 into master Aug 29, 2025
10 checks passed
@vunguyenhung vunguyenhung deleted the issue-18089 branch August 29, 2025 03:45
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.

[ACTION] Add BambooHR MCP support (bulk resume export + candidate data access)

4 participants