- 
                Notifications
    You must be signed in to change notification settings 
- Fork 5.5k
New Components - bamboohr #18197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Components - bamboohr #18197
Conversation
| WalkthroughAdds 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
 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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Assessment against linked issues
 Assessment against linked issues: Out-of-scope changes
 Poem
 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 unit tests
 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit: 
 SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type  Other keywords and placeholders
 CodeRabbit Configuration File ( | 
| The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
 | 
There was a problem hiding this 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.
⛔ Files ignored due to path filters (1)
- pnpm-lock.yamlis 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 downloadsTo 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
_makeRequesthonorsreturnFullResponseand returns the full HTTP response object (including headers) instead of justresponse.data.
• If_makeRequestdoes not yet support this option, you’ll need to extend it to conditionally return the raw response whenreturnFullResponse: true.
• Finally, update any consumers ofdownloadFilethat currently expect a Buffer (or raw data) to instead destructure the returned{ data, headers }and adjust accordingly (e.g. parseheaders['content-disposition']andheaders['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 thetypefield 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 summaryBefore merging, confirm that BambooHR’s “Change Applicant’s Status” endpoint expects a JSON body of
{"status": <id>}(not{"statusId": <id>}), sinceupdateApplicationStatus({ …data })simply spreads yourdatathrough to the request body viathis._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-1correctly 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
downloadFilemight 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 resumeFileIdI inspected
components/bamboohr/bamboohr.app.mjsaround thegetApplicationmethod and it simply proxies the API response viathis._makeRequest—there’s no code mapping or renaming of anyresumeorresumeFileIdfields. I also ran searches for bothresumeFileIdandresumein 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:
resumeFileIdis returned at the top level of the JSON payload, as assumed by your destructure indownload-resume.mjs;- if instead it’s nested (for example under
application.resume.fileId), update the destructuring incomponents/bamboohr/actions/download-resume/download-resume.mjsaccordingly.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 formatThe current code defines the filter as
newSince. According to BambooHR’s Recruiting API, the “Get Applications” endpoint uses the query parameternewOrUpdatedSince, 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 actionThe current implementation in components/bamboohr/actions/list-applications/list-applications.mjs unconditionally passes both
applicationStatusId: this.statusId, // from statusId applicationStatus: this.statusGroup, // from statusGroupto 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.statusIdoverridesstatusGroup), 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’srunmethod (e.g. throw a validation error if bothstatusIdandstatusGroupare defined).• Alternatively, decide on a sensible default (e.g. treat
statusIdas 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.
Resolves #18089
Summary by CodeRabbit