Skip to content

Frontend fix#97

Merged
ssjablonski merged 5 commits into
mainfrom
frontend-fix
Jun 24, 2025
Merged

Frontend fix#97
ssjablonski merged 5 commits into
mainfrom
frontend-fix

Conversation

@ssjablonski

@ssjablonski ssjablonski commented Jun 24, 2025

Copy link
Copy Markdown
Contributor

Summary by Sourcery

Standardize “user not in group” errors to use Forbidden status, add frontend redirection on 403/409 responses, and fix React effect dependencies to avoid infinite updates.

Enhancements:

  • Change backend "user not in group" exceptions and handler responses from HTTP 409 (Conflict) to 403 (Forbidden)
  • Add Axios response interceptor in frontend to redirect users to the trips page on 403 or 409 errors
  • Include missing dependency arrays for useEffect hooks in bill-splitting pages to prevent infinite re-renders

Tests:

  • Update controller tests to expect HTTP 403 Forbidden instead of 409 Conflict for relevant endpoints

@sourcery-ai

sourcery-ai Bot commented Jun 24, 2025

Copy link
Copy Markdown

Reviewer's Guide

Implements global client-side redirection on forbidden/conflict errors, standardizes backend group authorization responses to 403 Forbidden, aligns tests accordingly, and corrects hook dependencies in bill-splitting components.

Sequence diagram for frontend error handling and redirection on 403/409 responses

sequenceDiagram
    participant User as actor User
    participant Frontend as Frontend (React)
    participant Backend as Backend (Spring)

    User->>Frontend: Initiates API request
    Frontend->>Backend: Sends request
    Backend-->>Frontend: Responds with 403/409 error
    Frontend->>Frontend: Axios interceptor detects 403/409
    Frontend->>User: Redirects to /trips page
Loading

Class diagram for updated UserIsNotInGroupException and exception handler

classDiagram
    class UserIsNotInGroupException {
        <<exception>>
        +UserIsNotInGroupException(String message)
    }
    class FareShareResponseEntityExceptionHandler {
        +userIsNotInGroupException(Exception ex, WebRequest request): ResponseEntity<ErrorDetails>
    }
    UserIsNotInGroupException <|-- RuntimeException
    FareShareResponseEntityExceptionHandler ..> UserIsNotInGroupException : handles
Loading

File-Level Changes

Change Details Files
Added a response interceptor to redirect on forbidden or conflict errors
  • Installed axiosInstance.interceptors.response to catch responses and errors
  • Checked error.response.status for 403 and 409
  • Redirected client to '/trips' when status matches
  • Propagated errors by rejecting the promise
frontend/lib/axiosInstance.ts
Updated backend error responses for group authorization to use 403 Forbidden
  • Changed HttpStatus.CONFLICT to HttpStatus.FORBIDDEN in the exception handler
  • Updated @ResponseStatus annotation on UserIsNotInGroupException to FORBIDDEN
backend/src/main/java/share/fare/backend/exception/FareShareResponseEntityExceptionHandler.java
backend/src/main/java/share/fare/backend/exception/UserIsNotInGroupException.java
Adjusted tests to expect 403 Forbidden for unauthorized group actions
  • Replaced status().isConflict() with status().isForbidden() in GroupChatControllerTest
  • Replaced status().isConflict() with status().isForbidden() in GroupInvitationControllerTest
  • Replaced status().isConflict() with status().isForbidden() in GroupMembershipControllerTest
  • Replaced status().isConflict() with status().isForbidden() in VoteControllerTest
backend/src/test/java/share/fare/backend/controller/GroupChatControllerTest.java
backend/src/test/java/share/fare/backend/controller/GroupInvitationControllerTest.java
backend/src/test/java/share/fare/backend/controller/GroupMembershipControllerTest.java
backend/src/test/java/share/fare/backend/controller/VoteControllerTest.java
Fixed effect hook dependencies for bill-splitting pages to run when trip changes
  • Added [trip] dependency to useEffect in trips/[id]/bill-splitting page
  • Added [trip] dependency to useEffect in ExpensesDashboard component
frontend/app/(root)/trips/[id]/bill-splitting/page.tsx
frontend/components/bill-splitting/ExpensesDashboard.tsx

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@gemini-code-assist gemini-code-assist 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.

Summary of Changes

Hello @ssjablonski, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the application's error handling by updating the backend to use the more semantically appropriate 403 Forbidden HTTP status code for scenarios where a user lacks necessary group permissions. Concurrently, it improves frontend robustness by correctly managing useEffect dependencies and introduces a global mechanism to gracefully handle and redirect users upon encountering specific authorization-related errors from the API.

Highlights

  • Backend Error Handling Refinement: Corrected the HTTP status code for UserIsNotInGroupException from 409 Conflict to 403 Forbidden across the exception handler and the exception definition itself. This change aligns with REST best practices, as 403 Forbidden is semantically more appropriate for authorization failures where a user lacks the necessary permissions.
  • Frontend useEffect Dependency Fixes: Enhanced frontend stability by adding trip to the dependency arrays of useEffect hooks in page.tsx (line 60) and ExpensesDashboard.tsx (line 37). This ensures that data fetching functions are correctly re-executed when the trip object changes, preventing stale data or infinite re-renders.
  • Global Frontend Error Redirection: Implemented a new global Axios response interceptor in axiosInstance.ts to automatically redirect users to the /trips page when the application receives 403 Forbidden or 409 Conflict HTTP status codes from the backend. This improves user experience by gracefully handling unauthorized access or conflicting states.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@sourcery-ai sourcery-ai 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.

Hey @ssjablonski - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +26 to +33
if (
error.response &&
(error.response.status === 403 || error.response.status === 409)
) {
if (typeof window !== 'undefined') {
window.location.href = '/trips';
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (code-quality): Merge nested if conditions (merge-nested-ifs)

Suggested change
if (
error.response &&
(error.response.status === 403 || error.response.status === 409)
) {
if (typeof window !== 'undefined') {
window.location.href = '/trips';
}
}
if (error.response &&
(error.response.status === 403 || error.response.status === 409) && typeof window !== 'undefined') {
window.location.href = '/trips';
}


ExplanationReading deeply nested conditional code is confusing, since you have to keep track of which
conditions relate to which levels. We therefore strive to reduce nesting where
possible, and the situation where two if conditions can be combined using
and is an easy win.

@github-actions

Copy link
Copy Markdown

Code Coverage

File Coverage [57.17%]
UserIsNotInGroupException.java 100% 🍏
FareShareResponseEntityExceptionHandler.java 56.8%
Total Project Coverage 81.9% 🍏

@ssjablonski ssjablonski merged commit f3b97f2 into main Jun 24, 2025
1 check passed

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request improves backend API semantics and frontend stability. The change from HTTP 409 to 403 for authorization errors is semantically correct. The fixes to useEffect dependencies prevent infinite loops. The review includes suggestions for the frontend code, specifically addressing the global Axios interceptor handling HTTP 409 errors and aligning the useEffect hooks with React's best practices.

Comment on lines +23 to +36
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
if (
error.response &&
(error.response.status === 403 || error.response.status === 409)
) {
if (typeof window !== 'undefined') {
window.location.href = '/trips';
}
}
return Promise.reject(error);
},
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The global response interceptor redirects users to /trips for 403 or 409 status codes. However, a 409 status code is used for various conflict scenarios where the user should see an inline error message on the current page. Remove the 409 status check from this interceptor.1

axiosInstance.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response && error.response.status === 403) {
      if (typeof window !== 'undefined') {
        window.location.href = '/trips';
      }
    }
    return Promise.reject(error);
  },
);

Style Guide References

Footnotes

  1. 409 status code should be handled locally, not globally. (link)

Comment on lines 57 to +60
useEffect(() => {
if (!trip || !trip.id) return;
fetchData();
});
}, [trip]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To adhere to React best practices and prevent potential issues with stale closures, wrap the fetchData function in useCallback and include it in the dependency array.1

Style Guide References

Footnotes

  1. Using useCallback ensures that the function instance is stable across renders unless its dependencies change, preventing unnecessary re-renders and potential issues with stale closures. (link)

Comment on lines 35 to +37
useEffect(() => {
fetchGroupExpenses();
}, []);
}, [trip]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To fully align with React best practices, wrap fetchGroupExpenses in a useCallback hook with trip as its dependency, and then add fetchGroupExpenses to the dependency array of this useEffect.1

const fetchGroupExpenses = useCallback(async () => {
  // ... function body
}, [trip]);

useEffect(() => {
  fetchGroupExpenses();
}, [fetchGroupExpenses]);

Style Guide References

Footnotes

  1. Using useCallback ensures that the function instance is stable across renders unless its dependencies change, preventing unnecessary re-renders and potential issues with stale closures. (link)

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.

2 participants