Skip to content

BE: Volunteer profile GET + PATCH - #50

Merged
natphua merged 2 commits into
mainfrom
BE-volunteer-profile
Nov 11, 2026
Merged

natphua merged 2 commits into
mainfrom
BE-volunteer-profile

Conversation

@WillBalz

@WillBalz WillBalz commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

Describe your changes

Adds GET and PATCH endpoints for volunteer profiles, implemented with @Echandler123. GET returns only the agreed profile fields and positions as { id, name }. PATCH updates only the supplied editable fields, normalizes email and cell phone, allows optional fields to be cleared with null, and rejects invalid or read-only fields.

Successful responses return { volunteer } with Cache-Control: no-store. Missing volunteers return 404; invalid input returns 400; a duplicate email + cell phone pair returns 409.

Per #41, these endpoints are temporarily public. Both TODO(natalie) comments remain unchanged for session and ownership checks after the volunteer-session ticket merges.

Related Issue

Closes #41

Files changed

  • src/app/api/volunteers/[id]/route.ts: Adds GET/PATCH, a shared profile selection, no-store headers on successful responses, and Prisma P2025/P2002 error handling.
  • src/lib/applicant-validation.ts: Adds validateVolunteerFields with an editable-field allowlist, normalization, type checks, and nullable optional fields.
  • src/lib/applicant-validation.test.ts: Adds 17 volunteer validation cases covering the required inputs and failures.
  • src/lib/time-conversion.test.ts: Adds explicit Jest globals imports to the existing time-conversion tests.

How to test

Automated checks passed on an isolated copy of committed c823f5a:

  • npm test -- --runInBand src/lib/applicant-validation.test.ts: 61/61 tests passed.
  • npm run lint: passed.
  • npm run format:check: passed.

Postman verification steps (use the shared test volunteers from #41):

  1. Start the app with npm run dev. Use http://localhost:3000, no authentication for this ticket, and raw JSON with Content-Type: application/json for PATCH.
  2. GET /api/volunteers/test-volunteer-active: expect 200, exactly the profile fields, positions containing only id/name, no hour logs or application, and response header Cache-Control: no-store.
  3. GET /api/volunteers/test-volunteer-inactive: expect 200 with status INACTIVE. GET /api/volunteers/nope: expect 404.
  4. PATCH the active volunteer with { "firstName": " Pat " }: expect 200 with Pat and other fields unchanged. Patch email " New.Email@Example.com " and cellPhone "(617) 555-0199": expect new.email@example.com and 6175550199.
  5. PATCH optional homePhone, emergencyContactRelationship, and photoUrl to null: expect those fields cleared. PATCH photoConsent with true and false: expect 200.
  6. PATCH email "inactive.volunteer@example.com" and cellPhone "6175550102": expect 409 and no changes saved.
  7. Test status, positions, id, and an unknown field separately: expect 400 naming the field. Also test an empty object, blank/null required text, invalid email, short phone, non-boolean photoConsent, non-object bodies, and malformed JSON: expect 400.
  8. PATCH /api/volunteers/nope with { "firstName": "Pat" }: expect 404.
  9. Verify persisted values in Prisma Studio, then restore the shared test data with the seed procedure in BE: Volunteer Profile GET + PATCH #41.

Screenshots

Verified in Postman on November 8, 2026 against committed c823f5a running locally. Every row in both test tables in #41 passed, including the expected 400/404/409 responses. Additional coverage includes unknown fields, null/array/string bodies, both photoConsent values, photoUrl set/clear, and the nine-digit phone boundary.

Prisma Studio confirmed persisted values. The active test volunteer was restored to all seed profile values with a targeted PATCH, then verified with GET; the inactive fixture was unchanged. The full seed script was not run because it also resets unrelated applicant records.

30 Postman screenshots and one Prisma Studio screenshot:

01. GET active profile — 200, exact profile fields and positions

GET active profile — 200, exact profile fields and positions

02. GET response headers — Cache-Control: no-store

GET response headers — Cache-Control: no-store

03. GET inactive profile — 200, INACTIVE

GET inactive profile — 200, INACTIVE

04. GET missing volunteer — 404

GET missing volunteer — 404

05. PATCH firstName — 200, trimmed partial update

PATCH firstName — 200, trimmed partial update

06. PATCH email and cellPhone — 200, normalized values

PATCH email and cellPhone — 200, normalized values

07. PATCH optional fields to null — 200, fields cleared

PATCH optional fields to null — 200, fields cleared

08. PATCH photoConsent true — 200

PATCH photoConsent true — 200

09. PATCH duplicate email + phone — 409

PATCH duplicate email + phone — 409

10. PATCH status — 400, read-only field named

PATCH status — 400, read-only field named

11. PATCH positions — 400, read-only field named

PATCH positions — 400, read-only field named

12. PATCH id — 400, read-only field named

PATCH id — 400, read-only field named

13. PATCH empty object — 400

PATCH empty object — 400

14. PATCH blank firstName — 400

PATCH blank firstName — 400

15. PATCH null firstName — 400

PATCH null firstName — 400

16. PATCH invalid email — 400

PATCH invalid email — 400

17. PATCH short cellPhone — 400

PATCH short cellPhone — 400

18. PATCH non-boolean photoConsent — 400

PATCH non-boolean photoConsent — 400

19. PATCH malformed JSON — 400

PATCH malformed JSON — 400

20. PATCH missing volunteer — 404

PATCH missing volunteer — 404

21. PATCH unknown field — 400, field named

PATCH unknown field — 400, field named

22. PATCH null body — 400

PATCH null body — 400

23. PATCH array body — 400

PATCH array body — 400

24. PATCH string body — 400

PATCH string body — 400

25. PATCH photoConsent false — 200

PATCH photoConsent false — 200

26. PATCH photoUrl string — 200

PATCH photoUrl string — 200

27. PATCH photoUrl null — 200, URL cleared

PATCH photoUrl null — 200, URL cleared

28. PATCH nine-digit cellPhone — 400

PATCH nine-digit cellPhone — 400

29. GET after errors — 200, persisted updates retained

GET after errors — 200, persisted updates retained

30. Prisma Studio — persisted normalized test volunteer values

Prisma Studio — persisted normalized test volunteer values

31. PATCH restore — 200, shared volunteer seed values restored

PATCH restore — 200, shared volunteer seed values restored

Jest output from the committed code:

Test Suites: 1 passed, 1 total
Tests:       61 passed, 61 total
Snapshots:   0 total

Estimated Hours Spent

3 hours

Checklist before requesting a review

  • Satisfies all Deliverables
  • Tested thoroughly
  • No unused dependencies
  • Requested review from Natalie

@WillBalz
WillBalz requested a review from natphua November 9, 2026 04:01

@natphua natphua left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks amazing guys! The PR and endpoints look great as usual. Thanks for the detailed screenshot and error cases as well!

Just a future note: make sure to remove TODO comments after implementing. I will update that myself since it's small, but it's something to remember in later tickets!

Comment thread src/app/api/volunteers/[id]/route.ts Outdated
// TODO: prisma.volunteer.update where the id matches, with `data` set to the cleaned data and
// `select: VOLUNTEER_PROFILE_SELECT`. The cleaned data is typed loosely, so you'll need to cast it
// for Prisma. That's fine here, because the helper already cleaned it.
// Return 200 with { volunteer }.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Once you implement the code, make sure to remove relevant TODO comments

@natphua
natphua merged commit 157631a into main Nov 11, 2026
1 check passed
@natphua
natphua deleted the BE-volunteer-profile branch November 11, 2026 14:18
@github-actions

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BE: Volunteer Profile GET + PATCH

2 participants