feat(auth/talent): add linkedin_url + is_job_ready to /me; snake_case onboarding profile DTOs#159
Conversation
…vel, linkedin_url, avatar_url)
…tion_level, linkedin_url)
|
Warning Review limit reached
More reviews will be available in 51 minutes and 26 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThis PR adds a new authenticated password-change endpoint with full validation and error handling, and extends the auth response to include talent profile fields ( ChangesChange Password Feature
Talent Profile Integration and DTO Migration
🎯 3 (Moderate) | ⏱️ ~25 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 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. Comment |
There was a problem hiding this comment.
Pull request overview
Adds frontend-requested API consistency updates by (1) extending /auth/me with talent profile-derived LinkedIn + job readiness fields, and (2) switching talent onboarding/profile DTO request bodies to snake_case; additionally introduces an authenticated password change endpoint.
Changes:
GET /auth/me: includelinkedin_url(fromtalent_profiles.linkedin_url) andis_job_ready(derived fromtalent_profiles.status === 'job_ready').PATCH /talent/onboarding/profileandPOST /talent/profile: accept snake_case fields (education_level,linkedin_url,avatar_url).- Add
POST /auth/change-passwordwith throttling, validation, server-side refresh-token revocation (viaupdatePassword), and cookie clearing.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/shared/messages/success.messages.ts | Adds PASSWORD_CHANGED success message used by change-password flow. |
| src/shared/messages/error.messages.ts | Adds change-password specific error messages. |
| src/modules/talent/talent.service.ts | Updates onboarding/profile persistence to read snake_case DTO fields. |
| src/modules/talent/dto/set-profile.dto.ts | Renames onboarding profile DTO fields + validation messages to snake_case. |
| src/modules/talent/dto/save-talent-profile.dto.ts | Renames talent profile DTO fields + validation messages to snake_case. |
| src/modules/auth/dto/change-password.dto.ts | Introduces DTO + validation for change-password request body. |
| src/modules/auth/auth.service.ts | Implements changePassword() and extends getProfile() to return new /me fields. |
| src/modules/auth/auth.service.get-profile.spec.ts | Adds unit tests for /me linkedin/job-ready derivation (talent scenarios). |
| src/modules/auth/auth.service.change-password.spec.ts | Adds unit tests for changePassword() success and failure modes. |
| src/modules/auth/auth.controller.ts | Adds POST /auth/change-password endpoint (auth + throttled) and clears cookies on success. |
| src/modules/auth/auth.controller.change-password.spec.ts | Adds controller-level unit tests for change-password delegation and cookie clearing. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /** LinkedIn URL — only present for talent users; null when not set. */ | ||
| linkedin_url?: string | null; | ||
| /** True when the talent has reached Job Ready status. */ | ||
| is_job_ready?: boolean; |
| async getProfile(userId: string): Promise<AuthUser> { | ||
| const user = await this.usersService.findOne(userId); | ||
| const profile = | ||
| user.role === UserRole.TALENT | ||
| ? await this.talentProfileRepository.findOne({ | ||
| where: { user_id: userId }, | ||
| select: { track: true }, | ||
| select: { track: true, linkedin_url: true, status: true }, | ||
| }) | ||
| : null; | ||
|
|
||
| return { | ||
| ...this.toAuthUser(user), | ||
| track: profile?.track ?? null, | ||
| linkedin_url: profile?.linkedin_url ?? null, | ||
| is_job_ready: profile?.status === TalentProfileStatus.JOB_READY, | ||
| }; |
| /** | ||
| * Controller-level tests for POST /auth/change-password. | ||
| * | ||
| * Focus: | ||
| * - Delegates to authService.changePassword with the correct userId and DTO | ||
| * - Calls clearAuthCookies so the browser loses its auth cookies after the change | ||
| * - Surfaces the service result directly to the caller | ||
| * - ThrottlerGuard is applied (cannot brute-force currentPassword) | ||
| */ |
…eady-snake-case # Conflicts: # src/modules/auth/auth.controller.change-password.spec.ts # src/modules/auth/auth.service.change-password.spec.ts # src/modules/auth/auth.service.ts # src/modules/auth/dto/change-password.dto.ts # src/modules/talent/dto/set-profile.dto.ts # src/modules/talent/talent.service.ts
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/modules/talent/talent.service.ts (1)
188-214: 🧹 Nitpick | 🔵 Trivial | 💤 Low valueConsider updating logged field names to snake_case for consistency.
The
personalise()method still logs'educationLevel'(camelCase) inavailableDataPointsandincompleteFieldswhile the entity and DTO now useeducation_level. This creates inconsistency in logged/analytics data if downstream systems expect consistent naming.♻️ Suggested fix for consistent logging
const availableDataPoints: string[] = ['track']; if (profile.goal) availableDataPoints.push('goal'); if (profile.region) availableDataPoints.push('region'); - if (profile.education_level) availableDataPoints.push('educationLevel'); + if (profile.education_level) availableDataPoints.push('education_level'); const isFullyPersonalised = !!profile.goal && !!profile.region && !!profile.education_level; try { this.logger.log( JSON.stringify({ event: 'talent_personalisation', userId, timestamp: new Date().toISOString(), availableDataPoints, assessmentsGenerated: true, recommendationsGenerated: isFullyPersonalised, incompleteFields: isFullyPersonalised ? [] - : ['goal', 'region', 'educationLevel'].filter( - (f) => - !profile[ - f === 'educationLevel' - ? 'education_level' - : (f as keyof TalentProfile) - ], + : ['goal', 'region', 'education_level'].filter( + (f) => !profile[f as keyof TalentProfile], ), }), );🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/modules/talent/talent.service.ts` around lines 188 - 214, In personalise(), ensure logged field names use snake_case to match the entity/DTO: update availableDataPoints to push 'education_level' instead of 'educationLevel', and when building incompleteFields change the array ['goal','region','educationLevel'] to ['goal','region','education_level'] and adjust the filtering logic that checks profile[...] to reference profile['education_level'] (or keep the existing conditional mapping but return 'education_level') so both availableDataPoints and incompleteFields consistently use 'education_level'; keep the rest of the logging structure unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@src/modules/talent/talent.service.ts`:
- Around line 188-214: In personalise(), ensure logged field names use
snake_case to match the entity/DTO: update availableDataPoints to push
'education_level' instead of 'educationLevel', and when building
incompleteFields change the array ['goal','region','educationLevel'] to
['goal','region','education_level'] and adjust the filtering logic that checks
profile[...] to reference profile['education_level'] (or keep the existing
conditional mapping but return 'education_level') so both availableDataPoints
and incompleteFields consistently use 'education_level'; keep the rest of the
logging structure unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9d55f65a-436e-45ea-bdac-bce057c645d1
📒 Files selected for processing (11)
src/modules/auth/auth.controller.change-password.spec.tssrc/modules/auth/auth.controller.tssrc/modules/auth/auth.service.change-password.spec.tssrc/modules/auth/auth.service.get-profile.spec.tssrc/modules/auth/auth.service.tssrc/modules/auth/dto/change-password.dto.tssrc/modules/talent/dto/save-talent-profile.dto.tssrc/modules/talent/dto/set-profile.dto.tssrc/modules/talent/talent.service.tssrc/shared/messages/error.messages.tssrc/shared/messages/success.messages.ts
Summary
Two related API consistency fixes requested by the frontend team.
1.
GET /auth/me— new fieldslinkedin_urlandis_job_readyare now included for talent users:{ "id": "435b72ff-...", "email": "testtalent5@mailsac.com", "first_name": "Test", "last_name": "Talent", "fullname": "Test Talent", "avatar_url": null, "country": "Unknown", "role": "talent", "is_verified": true, "onboarding_complete": true, "track": "frontend_developer", "linkedin_url": "https://www.linkedin.com/in/alexsmith", "is_job_ready": true }linkedin_url— sourced fromtalent_profiles.linkedin_url;nullwhen not setis_job_ready—truewhentalent_profiles.status === 'job_ready',falseotherwisenull/false2.
PATCH /talent/onboarding/profile+POST /talent/profile— snake_case bodyeducationLeveleducation_levellinkedinUrllinkedin_urlavatarUrlavatar_urllinkedinProfile(POST /talent/profile)linkedin_urlTests
getProfile()covering allTalentProfileStatusvalues, null profile, and null linkedin url🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Chores