Skip to content

fix: Improve ProgramForm validation to avoid duplicate backend requests#4216

Open
mrkeshav-05 wants to merge 11 commits intoOWASP:mainfrom
mrkeshav-05:fix/issue-3977
Open

fix: Improve ProgramForm validation to avoid duplicate backend requests#4216
mrkeshav-05 wants to merge 11 commits intoOWASP:mainfrom
mrkeshav-05:fix/issue-3977

Conversation

@mrkeshav-05
Copy link
Contributor

@mrkeshav-05 mrkeshav-05 commented Mar 9, 2026

Proposed change

This PR removes the frontend duplicate name check in ProgramForm that previously made an extra backend request using GetMyPrograms. Instead, name uniqueness is now handled solely by backend validation.

Resolves: #3977

Changes:

  • Removed checkNameUniquenessSync query-based validation.
  • Handle name uniqueness through backend GraphQL validation.
  • Map GraphQL extensions errors to form field validation.
image

Checklist

  • Required: I followed the contributing workflow
  • Required: I verified that my code works as intended and resolves the issue as described
  • Required: I ran make check-test locally: all warnings addressed, tests passed
  • I used AI for code, documentation, tests, or communication related to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Translate DB unique-constraint IntegrityErrors into GraphQL VALIDATION_ERROR for program name; remove client-side uniqueness queries; surface backend field validation in ProgramForm via extractGraphQLErrors; adjust page submit handlers to suppress generic toasts for validation errors; update tests to mock GraphQL validation responses.

Changes

Cohort / File(s) Summary
Backend mutation
backend/apps/mentorship/api/internal/mutations/program.py
Add _handle_program_save_integrity_error; catch IntegrityError in create_program/update_program and raise GraphQLError with VALIDATION_ERROR for name.
Program form component
frontend/src/components/ProgramForm.tsx
Remove Apollo client uniqueness query and currentProgramKey prop; change onSubmit to return Promise<void>; introduce backendErrors map, clear server errors on input change, and populate from extracted GraphQL validation errors on submit.
Page submit handlers
frontend/src/app/my/mentorship/programs/create/page.tsx, frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
Import extractGraphQLErrors; suppress generic toasts/global error handling when hasValidationErrors is true; rethrow validation errors; stop passing currentProgramKey to ProgramForm.
GraphQL error utility
frontend/src/utils/helpers/handleGraphQLError.ts
Add getGraphQLErrors to support Apollo v4 errors array and graphQLErrors; refactor extractGraphQLErrors to normalize validation and unmapped errors.
Frontend tests — ProgramForm
frontend/__tests__/unit/components/ProgramForm.test.tsx
Remove Apollo client/query mocks; mock onSubmit to reject with GraphQL errors; update tests to assert server-side validation messages, clearing on input, non-validation error behavior, and success flows.
Frontend tests — error helper
frontend/__tests__/unit/utils/handleGraphQLError.test.ts
Add tests for Apollo v4 errors array shape to ensure extractGraphQLErrors extracts validation/unmapped errors consistently.
Manifest / package
package.json
Test/dependency updates related to frontend test changes.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • arkid15r
  • kasya
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: improving ProgramForm validation by eliminating duplicate backend requests, which aligns with the core objective.
Description check ✅ Passed The description directly addresses the changeset by explaining the removal of frontend duplicate name checks and the shift to backend GraphQL validation, matching the implemented changes.
Linked Issues check ✅ Passed All primary objectives from #3977 are met: removed checkNameUniquenessSync query-based validation [#3977], implemented backend GraphQL validation for name uniqueness [#3977], and mapped GraphQL extension errors to form fields [#3977].
Out of Scope Changes check ✅ Passed All changes align with the scope of removing frontend duplicate requests and centralizing validation on the backend; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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.

❤️ Share

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

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 7 files

Confidence score: 3/5

  • There is moderate merge risk because frontend/src/components/ProgramForm.tsx has a concrete validation gap (severity 6/10, high confidence) where persisted backend name errors are ignored on submit, which can let users repeatedly submit already-invalid input.
  • frontend/src/utils/helpers/handleGraphQLError.ts has a medium issue where broad errors array detection may misclassify non-GraphQL failures, potentially hiding fallback messages and making error feedback less reliable.
  • Given one user-facing validation regression risk plus one error-handling robustness issue, this looks mergeable with caution after targeted verification.
  • Pay close attention to frontend/src/components/ProgramForm.tsx and frontend/src/utils/helpers/handleGraphQLError.ts - submit validation and error classification behavior may produce incorrect user feedback.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/src/components/ProgramForm.tsx">

<violation number="1" location="frontend/src/components/ProgramForm.tsx:120">
P2: Submit validation ignores persisted backend name errors, allowing repeated submissions for already-invalid input.</violation>
</file>

<file name="frontend/src/utils/helpers/handleGraphQLError.ts">

<violation number="1" location="frontend/src/utils/helpers/handleGraphQLError.ts:34">
P2: Broad `errors` array detection can misclassify non-GraphQL errors and suppress fallback error messages.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 `@backend/apps/mentorship/api/internal/mutations/program.py`:
- Around line 53-59: The _handle_program_save_integrity_error function currently
assumes every IntegrityError is due to the "name" unique constraint; change it
to inspect the IntegrityError (e.g., exc.orig or str(exc)) to detect which
constraint was violated (name vs key or fallback) and set the GraphQLError
message and extensions["field"] accordingly (e.g., "name" or "key" or a generic
validation field) before re-raising the GraphQLError from the original
exception.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b0a2d9a8-ec9e-4c8a-b24e-538d384445c8

📥 Commits

Reviewing files that changed from the base of the PR and between 57e17c5 and 0b593e1.

📒 Files selected for processing (7)
  • backend/apps/mentorship/api/internal/mutations/program.py
  • frontend/__tests__/unit/components/ProgramForm.test.tsx
  • frontend/__tests__/unit/utils/handleGraphQLError.test.ts
  • frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx
  • frontend/src/app/my/mentorship/programs/create/page.tsx
  • frontend/src/components/ProgramForm.tsx
  • frontend/src/utils/helpers/handleGraphQLError.ts

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 9, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

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 `@frontend/src/components/ProgramForm.tsx`:
- Around line 133-140: The current try/catch in the ProgramForm submission
swallows all errors so parent pages never receive non-validation errors; modify
the submit error handling in the function that calls await onSubmit(e) to use
extractGraphQLErrors(error) and, after calling
setBackendErrors(validationErrors) when hasValidationErrors is true, rethrow the
original error when hasValidationErrors is false (or when validationErrors are
absent) so upstream handlers in create/page.tsx and edit/page.tsx can run;
reference the onSubmit call site, extractGraphQLErrors, setBackendErrors and
ensure non-validation errors are not suppressed but propagated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7157db7d-be9a-4496-91b6-78f53485f5fd

📥 Commits

Reviewing files that changed from the base of the PR and between 0b593e1 and c1810e1.

📒 Files selected for processing (2)
  • frontend/src/components/ProgramForm.tsx
  • frontend/src/utils/helpers/handleGraphQLError.ts

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 9, 2026
coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 9, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
frontend/src/components/ProgramForm.tsx (2)

133-140: ⚠️ Potential issue | 🟠 Major

Rethrow non-validation failures.

This catch still swallows network/server/unmapped GraphQL errors. When extractGraphQLErrors does not return field validation errors, the original exception needs to bubble so the page-level handlers can show their generic error state/toast.

Suggested fix
     try {
       await onSubmit(e)
     } catch (error) {
       const { validationErrors, hasValidationErrors } = extractGraphQLErrors(error)
       if (hasValidationErrors) {
         setBackendErrors(validationErrors)
+      } else {
+        throw error
       }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/ProgramForm.tsx` around lines 133 - 140, The catch
block around awaiting onSubmit(e) currently swallows all errors; after calling
extractGraphQLErrors(error) you must rethrow the original error when
hasValidationErrors is false so non-validation failures bubble to page-level
handlers. Update the try/catch that calls onSubmit(e) to only call
setBackendErrors(validationErrors) when hasValidationErrors is true and
otherwise throw the received error (keep references to onSubmit,
extractGraphQLErrors, setBackendErrors to locate the change).

62-76: ⚠️ Potential issue | 🟠 Major

Backend field errors only render for name.

setBackendErrors(validationErrors) stores a generic field-error map, but the validation pipeline only reads backendErrors.name. If the backend returns a field-level error for description, startedAt, endedAt, or menteesLimit, this component drops it instead of surfacing it on the form. Please merge backendErrors[field] into each field’s displayed error, not just name.

Suggested direction
-  const errors = useFormValidation(
+  const localErrors = useFormValidation(
     [
       ...getCommonValidationRules(formData, touched, validateNameLocal, validateEndDateLocal),
       {
         field: 'menteesLimit',
         shouldValidate: touched.menteesLimit ?? false,
         validator: () => validateMenteesLimit(formData.menteesLimit),
       },
     ],
     [formData, touched, backendErrors]
   )
+
+  const errors = {
+    ...localErrors,
+    name: backendErrors.name ?? localErrors.name,
+    description: backendErrors.description ?? localErrors.description,
+    startedAt: backendErrors.startedAt ?? localErrors.startedAt,
+    endedAt: backendErrors.endedAt ?? localErrors.endedAt,
+    menteesLimit: backendErrors.menteesLimit ?? localErrors.menteesLimit,
+  }

Also applies to: 94-103, 136-138

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/components/ProgramForm.tsx` around lines 62 - 76, The component
only reads backendErrors.name when computing field errors, so backend field
errors for other fields (e.g., description, startedAt, endedAt, menteesLimit)
are never surfaced; update each field validator and the UI error resolution to
merge backendErrors[field] into the displayed error (e.g., in validateNameLocal
and analogous validateDescriptionLocal/validateStartedAtLocal, etc.) and when
rendering field error props use combinedError = localValidationError ||
backendErrors[field]; also ensure handleInputChange clears only
backendErrors[name] as it does now and that setBackendErrors stores the full
Record<string,string> returned from the server so these backendErrors[field]
merges work everywhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@frontend/src/components/ProgramForm.tsx`:
- Around line 133-140: The catch block around awaiting onSubmit(e) currently
swallows all errors; after calling extractGraphQLErrors(error) you must rethrow
the original error when hasValidationErrors is false so non-validation failures
bubble to page-level handlers. Update the try/catch that calls onSubmit(e) to
only call setBackendErrors(validationErrors) when hasValidationErrors is true
and otherwise throw the received error (keep references to onSubmit,
extractGraphQLErrors, setBackendErrors to locate the change).
- Around line 62-76: The component only reads backendErrors.name when computing
field errors, so backend field errors for other fields (e.g., description,
startedAt, endedAt, menteesLimit) are never surfaced; update each field
validator and the UI error resolution to merge backendErrors[field] into the
displayed error (e.g., in validateNameLocal and analogous
validateDescriptionLocal/validateStartedAtLocal, etc.) and when rendering field
error props use combinedError = localValidationError || backendErrors[field];
also ensure handleInputChange clears only backendErrors[name] as it does now and
that setBackendErrors stores the full Record<string,string> returned from the
server so these backendErrors[field] merges work everywhere.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4d7d3a3b-580f-4674-bb0b-c3c1bad6e608

📥 Commits

Reviewing files that changed from the base of the PR and between 7908117 and 5ed27f3.

📒 Files selected for processing (1)
  • frontend/src/components/ProgramForm.tsx

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="frontend/src/components/ProgramForm.tsx">

<violation number="1" location="frontend/src/components/ProgramForm.tsx:140">
P1: Non-validation submit errors are swallowed after removing the rethrow branch, preventing upstream error handling for real backend/network failures.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copilot AI review requested due to automatic review settings March 18, 2026 21:04
@sonarqubecloud
Copy link

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR removes ProgramForm’s client-side “duplicate program name” lookup (which triggered an extra GetMyPrograms request) and instead relies on backend uniqueness enforcement, with frontend support for mapping GraphQL validation errors (via extensions) to field-level form errors.

Changes:

  • Frontend: remove query-based name uniqueness validation in ProgramForm and surface backend validation errors on the form.
  • Frontend: expand GraphQL error extraction to support Apollo Client v4 CombinedGraphQLErrors (errors array).
  • Backend: translate IntegrityError during program create/update into a GraphQLError with extensions for field-level validation.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
frontend/src/utils/helpers/handleGraphQLError.ts Adds support for extracting GraphQL errors from either errors or graphQLErrors.
frontend/src/components/ProgramForm.tsx Removes frontend uniqueness query; captures backend validation errors and wires them into name validation.
frontend/src/app/my/mentorship/programs/create/page.tsx Suppresses failure toast for backend validation errors and rethrows so the form can render field errors.
frontend/src/app/my/mentorship/programs/[programKey]/edit/page.tsx Same as create page: suppress toast for validation errors; rethrow for form-level handling.
frontend/tests/unit/utils/handleGraphQLError.test.ts Adds tests for Apollo v4 errors-based error shape.
frontend/tests/unit/components/ProgramForm.test.tsx Replaces uniqueness-query tests with backend validation-error rendering tests.
backend/apps/mentorship/api/internal/mutations/program.py Catches IntegrityError on create/save and raises a GraphQLError with validation extensions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +55 to +58
msg = "A program with this name already exists."
raise GraphQLError(
msg,
extensions={"code": "VALIDATION_ERROR", "field": "name"},
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.

Improve ProgramForm validation to avoid duplicate backend requests

2 participants