Skip to content

fix(experiences): persist interview submissions with review status - #1641

Merged
KaranUnique merged 7 commits into
Canopus-Labs:mainfrom
nyxsky404:fix/935-interview-experience-persist
Aug 9, 2026
Merged

fix(experiences): persist interview submissions with review status#1641
KaranUnique merged 7 commits into
Canopus-Labs:mainfrom
nyxsky404:fix/935-interview-experience-persist

Conversation

@nyxsky404

@nyxsky404 nyxsky404 commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Pull Request Description

Related Issue

Closes #935

Summary

Interview experience submit only wrote to React state, so a refresh dropped the card even though the UI said it was up for review. Added /api/interview-experiences (create, mine, approved, status update), wired the form to POST with an idempotency key, and show pending/approved/rejected on cards.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

  • vitest run tests/interviewExperienceController.unit.test.js (pass)
  • vitest run tests/requireModerator.unit.test.js (pass)

Checklist

  • My code follows the project's guidelines
  • I have tested my changes
  • I have linked the related issue

Made with Cursor

Looks good to me. Ready to merge.

nyxsky404 and others added 4 commits August 8, 2026 20:03
Form only updated React state, so reload wiped cards despite the
moderation success copy. Add a backend resource, save on submit,
reload via client key, and surface pending/approved/rejected.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add idempotent creates, restrict status updates to MODERATOR_EMAILS,
and load approved experiences into the Common tab.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Reject blank or missing keys so retries cannot bypass the unique
partial index and create duplicate submissions.

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@nyxsky404, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bad8e739-fd40-413d-a291-a5be36c3f0bc

📥 Commits

Reviewing files that changed from the base of the PR and between 41d9784 and c223e71.

📒 Files selected for processing (4)
  • backend/Input_validators/ValidateInterviewExperience.js
  • backend/controllers/interviewExperienceController.js
  • backend/tests/interviewExperienceController.unit.test.js
  • frontend/src/pages/InterviewExperiences/InterviewExperiences.jsx
📝 Walkthrough

Walkthrough

The PR adds persisted interview-experience submissions, validation, idempotency, moderator authorization, API routes, retrieval states, and frontend integration with pending, approved, and rejected statuses.

Changes

Interview experience workflow

Layer / File(s) Summary
Experience contracts and persistence
backend/.env.example, backend/Input_validators/..., backend/models/...
Adds MODERATOR_EMAILS, Zod request validation, the InterviewExperience model, embedded rounds, defaults, and idempotency indexes.
Authentication and route wiring
backend/middlewares/..., backend/routes/..., backend/server.js, backend/tests/requireModerator.unit.test.js
Adds optional authentication, moderator checks, CSRF validation, interview-experience routes, rate-limited server registration, and moderator authorization tests.
Submission and moderation API
backend/controllers/..., backend/tests/interviewExperienceController.unit.test.js
Adds persistence, duplicate handling, approved and visitor-owned retrieval, moderation updates, client-shape mapping, and controller tests.
Frontend persistence and status display
frontend/src/pages/InterviewExperiences/..., frontend/src/utils/apiPaths.js
Replaces local-only submission with API requests, persistent client keys, dynamic loading, retry states, fallback content, and status badges.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Contributor
  participant InterviewExperiences
  participant interviewExperienceRoutes
  participant createInterviewExperience
  participant InterviewExperience

  Contributor->>InterviewExperiences: Submit experience
  InterviewExperiences->>interviewExperienceRoutes: POST with client and idempotency keys
  interviewExperienceRoutes->>createInterviewExperience: Validate and authorize
  createInterviewExperience->>InterviewExperience: Find or create submission
  InterviewExperience-->>createInterviewExperience: Pending document
  createInterviewExperience-->>InterviewExperiences: Normalized response
  InterviewExperiences-->>Contributor: Show pending status
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes persisting interview submissions and assigning review status.
Linked Issues check ✅ Passed The changes persist submissions, support reloadable user data, handle failures and retries, and represent pending, approved, and rejected states for issue [#935].
Out of Scope Changes check ✅ Passed The backend, frontend, authorization, validation, routing, and tests directly support the interview-experience persistence and moderation objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@github-actions github-actions Bot added the merge ready PR is mergeable and has no conflicts label Aug 8, 2026
Allowlist status updates and validate ObjectIds before Mongo writes, and
add double-submit CSRF middleware on mutating experience routes.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
backend/tests/interviewExperienceController.unit.test.js (1)

126-154: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test duplicate-key recovery after create.

This test covers the pre-create lookup only. Add a test where the first findOne returns null, InterviewExperience.create rejects with { code: 11000 }, and the recovery lookup returns the saved submission. This protects the concurrent retry path.

🤖 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 `@backend/tests/interviewExperienceController.unit.test.js` around lines 126 -
154, Add a test alongside the existing duplicate-key test for the concurrent
retry path: configure the first InterviewExperience.findOne call to return null,
make InterviewExperience.create reject with an error whose code is 11000, then
configure the recovery lookup to return the saved submission. Assert the
controller responds with status 200 and the recovered experience, and verify
create was attempted and the recovery findOne lookup occurred.
🤖 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.

Inline comments:
In `@backend/controllers/interviewExperienceController.js`:
- Around line 48-50: Update the submission creation logic in the interview
experience controller so anonymous requests without a non-empty
payload.clientKey return HTTP 400 before persistence; authenticated requests
identified by req.user._id and anonymous requests with clientKey must continue
to work. Add a unit test covering anonymous creation without clientKey and
asserting the 400 response.

In `@frontend/src/pages/InterviewExperiences/InterviewExperiences.jsx`:
- Around line 29-40: Update getOrCreateClientKey so every generated client key
uses cryptographic randomness via crypto.randomUUID() or
crypto.getRandomValues(), removing the Math.random() and timestamp-only
fallbacks. If Web Crypto is unavailable or storage access fails, return a
disabled/absent key rather than enabling anonymous submission retrieval through
GET /mine.

---

Nitpick comments:
In `@backend/tests/interviewExperienceController.unit.test.js`:
- Around line 126-154: Add a test alongside the existing duplicate-key test for
the concurrent retry path: configure the first InterviewExperience.findOne call
to return null, make InterviewExperience.create reject with an error whose code
is 11000, then configure the recovery lookup to return the saved submission.
Assert the controller responds with status 200 and the recovered experience, and
verify create was attempted and the recovery findOne lookup occurred.
🪄 Autofix

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 472864c4-4e80-4d21-9da8-3da12c8040c8

📥 Commits

Reviewing files that changed from the base of the PR and between 33a3c81 and 5f31229.

📒 Files selected for processing (11)
  • backend/.env.example
  • backend/Input_validators/ValidateInterviewExperience.js
  • backend/controllers/interviewExperienceController.js
  • backend/middlewares/authMiddleware.js
  • backend/models/InterviewExperience.js
  • backend/routes/interviewExperienceRoutes.js
  • backend/server.js
  • backend/tests/interviewExperienceController.unit.test.js
  • backend/tests/requireModerator.unit.test.js
  • frontend/src/pages/InterviewExperiences/InterviewExperiences.jsx
  • frontend/src/utils/apiPaths.js

Comment thread backend/controllers/interviewExperienceController.js
Comment thread frontend/src/pages/InterviewExperiences/InterviewExperiences.jsx Outdated
Require clientKey for unauthenticated creates and generate keys only
with Web Crypto so /mine cannot be enumerated via weak anon keys.

Co-authored-by: Cursor <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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.

Inline comments:
In `@frontend/src/pages/InterviewExperiences/InterviewExperiences.jsx`:
- Around line 47-48: Update the client-key retrieval logic around
CLIENT_KEY_STORAGE to validate stored values before returning them, accepting
only UUID or 32-hex Web Crypto formats. Remove invalid legacy anon- or
timestamp/random-based values instead of sending them to /mine, and apply
equivalent rejection or migration in the /mine backend path.
- Around line 542-548: Remove the unconditional clientKey guard from the
submission flow around the create request so authenticated users can submit
without Web Crypto support. Continue including clientKey when available, and
send the request without it when unavailable so the API enforces the requirement
only for anonymous submissions; preserve the existing submitting-state and API
error handling.
🪄 Autofix

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7a48dfa3-40ea-4bc3-9d84-9660ee6b0ca6

📥 Commits

Reviewing files that changed from the base of the PR and between 5f31229 and 41d9784.

📒 Files selected for processing (3)
  • backend/controllers/interviewExperienceController.js
  • backend/tests/interviewExperienceController.unit.test.js
  • frontend/src/pages/InterviewExperiences/InterviewExperiences.jsx
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/tests/interviewExperienceController.unit.test.js
  • backend/controllers/interviewExperienceController.js

Comment thread frontend/src/pages/InterviewExperiences/InterviewExperiences.jsx Outdated
Comment thread frontend/src/pages/InterviewExperiences/InterviewExperiences.jsx Outdated
Only UUID/hex crypto keys are accepted for anonymous identity, and
authenticated submits no longer require a clientKey on the client.

Co-authored-by: Cursor <cursoragent@cursor.com>
@KaranUnique
KaranUnique merged commit 951f2e7 into Canopus-Labs:main Aug 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Interview experience form confirms moderation without saving the submission

2 participants