Resume import — one shared service (WING-503)¶
Both surfaces that ingest a resume file — onboarding (frontend/src/app/features/onboarding/)
and profile (frontend/src/app/features/profile/) — now call the same
ResumeImportService (frontend/src/app/core/resume-import/resume-import.service.ts) instead of
carrying their own copies of the upload/extract/apply pipeline.
Why¶
The two implementations had drifted: onboarding's copy broke on desktop while profile's kept working, which is exactly the failure mode duplicated behavior invites (see the fleet's one-implementation-per-behavior rule). WING-503 collapses them into one service so a fix lands in both surfaces at once.
Shape¶
ResumeImportService.import(file)— validates the file client-side (extension, size), posts it to the edition-appropriate extract endpoint, and returns the extracted text + proposal.proposal-apply.ts(frontend/src/app/core/proposal-apply.ts) — applies the returned proposal to the profile store; shared by both callers.- Callers own only their UI: progress, error display, and where the user lands afterwards.
Endpoints and auth¶
- Desktop (Electron, loopback API):
POST /api/profile/extract-resume-docx— launch token mandatory, even from the trusted dev origin ([RequireLocalToken(allowDevOrigin: false)]; see security.md § 3). The Electron preload exposes the token and the Angular interceptor attaches it. - Cloud (pre-session onboarding):
POST /api/tenant/extract-resume-docx— deliberately public pre-session, rate-limited (tenant-onboard) and size-capped; see the allowlist rationale inTenantSessionMiddleware.ExactMatchPublicPaths.
Tests¶
resume-import.service.spec.ts— service behavior, both success and rejection paths.onboarding.spec.ts/profile.spec.ts— each caller renders the shared service's states.- Backend contract:
ApiSmokeTests.ResumeDocxExtract_TrustedOriginWithoutLaunchToken_IsUnauthorizedpins the desktop route's 401.
Plain-language sibling: resume-import