feat(scan): add Workday API support#535
Conversation
📝 Walkthrough🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scan.mjs`:
- Around line 149-166: fetchWorkdayAll can silently return partial results when
the pagination cap is hit; update the function (fetchWorkdayAll) to detect when
the loop exits due to maxPages being reached and signal that the results were
truncated (e.g., add a returned flag like _truncated: true and include
_pagesFetched or _itemsFetched), and also emit a warning (console.warn or
existing logger) when postings.length === limit on the final iteration and page
=== maxPages - 1 so operators can see the cap was hit.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 90d96e3c-cdb5-4a92-8c7d-ac6ca20e74d7
📥 Commits
Reviewing files that changed from the base of the PR and between 62b767d and 43d365812cc298d44b992b7301ea34e13efedf1b.
📒 Files selected for processing (1)
scan.mjs
Detect /wday/cxs/{tenant}/{site}/jobs URLs as a Workday provider via
the explicit api: field. fetchWorkdayAll paginates POST requests at
limit=20 (Workday's hard cap on most public job APIs) up to a 5000-job
safety bound. parseWorkday extracts title and locationsText, and builds
the public job URL as host + /en-US/{site} + externalPath.
Unlocks federal contractors that run Workday boards (Leidos, GDIT,
Booz Allen Hamilton, RTX, Amentum, Parsons, DXC, Accenture Federal)
for zero-token scanning. Backwards compatible — fetchJson takes an
optional options parameter (default {}) so the same helper handles
both Workday's POST + body and the existing GET semantics for
Greenhouse / Ashby / Lever.
Closes santifer#533
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
43d3658 to
3df0671
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@scan.mjs`:
- Around line 156-165: The loop that reads Workday responses currently uses
`const postings = json.jobPostings || []`, which masks unexpected payloads;
update the code (inside the loop where `fetchJson` is called) to validate that
`json.jobPostings` exists and is an Array and throw a descriptive Error if it is
missing or not an array so the failure surfaces into `errors` instead of
silently producing zero jobs; reference the `fetchJson` call and the
`postings`/`allPostings` handling to add this runtime check and throw (include
tenant/context in the error message) before pushing into `allPostings`.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 2df78ce3-66f2-47f4-ab8f-b0165ac11c64
📥 Commits
Reviewing files that changed from the base of the PR and between 43d365812cc298d44b992b7301ea34e13efedf1b and 3df0671.
📒 Files selected for processing (1)
scan.mjs
| for (let page = 0; page < maxPages; page++) { | ||
| const json = await fetchJson(url, { | ||
| method: 'POST', | ||
| headers: { 'Content-Type': 'application/json' }, | ||
| body: JSON.stringify({ appliedFacets: {}, limit, offset: page * limit, searchText: '' }), | ||
| }); | ||
| const postings = json.jobPostings || []; | ||
| pages = page + 1; | ||
| if (postings.length === 0) break; | ||
| allPostings.push(...postings); |
There was a problem hiding this comment.
Fail fast on unexpected Workday payloads.
json.jobPostings || [] turns any 200-level error payload or schema drift into a silent “0 jobs” result. That undercounts the tenant and hides the failure from the summary. Throw if jobPostings is not an array so this lands in errors instead.
💡 Proposed fix
for (let page = 0; page < maxPages; page++) {
const json = await fetchJson(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ appliedFacets: {}, limit, offset: page * limit, searchText: '' }),
});
- const postings = json.jobPostings || [];
+ if (!Array.isArray(json.jobPostings)) {
+ throw new Error(`Unexpected Workday response shape from ${url}: missing jobPostings array`);
+ }
+ const postings = json.jobPostings;
pages = page + 1;
if (postings.length === 0) break;As per coding guidelines, **/*.mjs: Check for command injection, path traversal, and SSRF. Ensure scripts handle missing data/ directories gracefully.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scan.mjs` around lines 156 - 165, The loop that reads Workday responses
currently uses `const postings = json.jobPostings || []`, which masks unexpected
payloads; update the code (inside the loop where `fetchJson` is called) to
validate that `json.jobPostings` exists and is an Array and throw a descriptive
Error if it is missing or not an array so the failure surfaces into `errors`
instead of silently producing zero jobs; reference the `fetchJson` call and the
`postings`/`allPostings` handling to add this runtime check and throw (include
tenant/context in the error message) before pushing into `allPostings`.
|
Addressed in 3df0671. fetchWorkdayAll now tracks lastPageSize and pages and emits a stderr warning when we exit via the maxPages bound |
/santifer#487, scan-auth LinkedIn santifer#379, hiringcafe-scan)
Brings in Windsurf session work: - Gmail rejection scanner with --apply-excel + Excel output - Hiring Cafe scanner (saved-search URL + API replay) - LinkedIn scanner (PR santifer#379) via scan-auth harness - scan.mjs upgrades: Workday API (santifer#535), location filter (santifer#490), --verify (santifer#487) - connections-match multi-CSV + scan-history cross-reference - Upstream sync to v1.6.0 (FR/JA/PT/RU mode translations, writing style calibration) - CV metrics: $100K savings + $800K budget added Conflicts resolved by taking worktree side for VERSION, DATA_CONTRACT.md, modes/_shared.md, .github/workflows/release.yml.
… PDF) - Level 3 WebSearch additions (08:30Z): Federato (Greenhouse), Jobgether Lever x3, Oddball, Automattic, fwddeploy AI FDE, Remote Raven (Remotive) - 5 A-G reports written (santifer#531-535): - santifer#531 Federato FDE ML Engineer 3.6/5 — Remote-US/Canada, $155-180K, 2+y matches Deepak 2.5y; LLMs+agentic+evals+production monitoring 1:1; PDF generated - santifer#532 Oddball Applied AI/ML 1.8/5 — federal contractor, US Citizen + clearance for billable work; F-1 OPT structural blocker - santifer#533 Automattic Applied AI Internal Tooling 2.7/5 — NYC NoHo 5d on-site + TS/Node primary; F-1 OPT relocation + sponsorship gap; merge-tracker skipped (existing santifer#247 higher score) - santifer#534 Jobgether/fwddeploy FinServ FDE 2.0/5 — anonymized end client + 5+y senior floor (2x gap); recruiter-aggregator pattern - santifer#535 Remote Raven AI & Cloud 1.2/5 — $10/hr full-time offshore economics; F-1 OPT prevailing-wage incompatible - 3 Lever Jobgether URLs marked [!] error: posting closed - merge-tracker: +4 added, 1 skipped (Automattic dup); verify-pipeline 0/0 - cleanup-low-scores: archived 4 below-threshold reports + santifer#533 manual move - Switched from session branch claude/relaxed-euler-irNFS to main per _profile.md Git Push Policy https://claude.ai/code/session_overnight-2026-05-05T08:30Z
Detect /wday/cxs/{tenant}/{site}/jobs URLs as a Workday provider via the explicit api: field. fetchWorkdayAll paginates POST requests at limit=20 (Workday's hard cap on most public job APIs) up to a 5000-job safety bound. parseWorkday extracts title and locationsText, and builds the public job URL as host + /en-US/{site} + externalPath.
Unlocks federal contractors that run Workday boards (Leidos, GDIT, Booz Allen Hamilton, RTX, Amentum, Parsons, DXC, Accenture Federal) for zero-token scanning. Backwards compatible — fetchJson takes an optional options parameter (default {}) so the same helper handles both Workday's POST + body and the existing GET semantics for Greenhouse / Ashby / Lever.
Closes #533
What does this PR do?
Related issue
Type of change
Checklist
node test-all.mjsand all tests passQuestions? Join the Discord for faster feedback.
Summary by CodeRabbit
New Features
Enhancements