Skip to content

feat: improve ticket detail retrieval and feedback handling#92

Merged
Polqt merged 1 commit intomainfrom
feat/student-feedback
Feb 28, 2026
Merged

feat: improve ticket detail retrieval and feedback handling#92
Polqt merged 1 commit intomainfrom
feat/student-feedback

Conversation

@wenjan19
Copy link
Collaborator

@wenjan19 wenjan19 commented Feb 28, 2026

manki ko

Summary by Sourcery

Improve ticket detail endpoints and align feedback submission and error handling between backend and frontend.

New Features:

  • Expose a dedicated endpoint to retrieve ticket details by ticket number separately from ID-based lookup.

Enhancements:

  • Restrict ticket detail lookup by ID to integer IDs only and enforce access control based on the requesting user or staff status.
  • Adjust ticket feedback creation on the backend to accept multipart form data for payload fields, supporting file uploads alongside feedback metadata.
  • Improve frontend API error handling for ticket and feedback operations to surface backend detail messages and structured validation errors.
  • Update frontend feedback creation to send data as FormData instead of JSON to match backend expectations.

Summary by CodeRabbit

  • New Features

    • Added new ticket lookup endpoint by ticket number for improved searchability.
  • Bug Fixes

    • Enhanced error handling to provide more detailed and informative error messages across API operations.
    • Improved authorization checks with proper 404 responses for unauthorized ticket access.
    • Updated feedback submission to use optimized data transmission format.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 28, 2026

Reviewer's Guide

Splits ticket retrieval into ID- and number-based endpoints with consistent authorization, aligns backend feedback creation with form-data submission, and improves frontend error handling and feedback API calls to work with the new backend behavior.

Sequence diagram for ticket detail retrieval by ID and number

sequenceDiagram
    actor User
    participant FrontendApp
    participant TicketApi
    participant BackendTicketsRouter
    participant DjangoORM
    participant Database

    User->>FrontendApp: Open ticket detail view (by id or ticket_number)
    FrontendApp->>TicketApi: getTicket(identifier)
    alt numeric identifier
        TicketApi->>BackendTicketsRouter: GET /tickets/{id}
        BackendTicketsRouter->>DjangoORM: Ticket.objects.annotate(comments_count=Count(comments)).get(id=id)
    else non_numeric identifier
        TicketApi->>BackendTicketsRouter: GET /tickets/number/{ticket_number}
        BackendTicketsRouter->>DjangoORM: Ticket.objects.annotate(comments_count=Count(comments)).get(ticket_number=ticket_number)
    end

    DjangoORM->>Database: Query Ticket with comments_count
    Database-->>DjangoORM: Ticket or not found
    alt ticket found
        DjangoORM-->>BackendTicketsRouter: Ticket instance
        BackendTicketsRouter->>BackendTicketsRouter: Check ticket.student == request.user or request.user.is_staff
        alt authorized
            BackendTicketsRouter-->>TicketApi: 200 TicketSchema.from_orm(ticket, request)
            TicketApi-->>FrontendApp: Ticket data
            FrontendApp-->>User: Render ticket detail
        else unauthorized
            BackendTicketsRouter-->>TicketApi: 404 {detail: Not found.}
            TicketApi-->>FrontendApp: Error(detail)
            FrontendApp-->>User: Show not found error
        end
    else ticket not found
        BackendTicketsRouter-->>TicketApi: 404 {detail: Not found.}
        TicketApi-->>FrontendApp: Error(detail)
        FrontendApp-->>User: Show not found error
    end
Loading

Sequence diagram for creating ticket feedback with form data and detailed errors

sequenceDiagram
    actor User
    participant FrontendApp
    participant FeedbackApi
    participant BackendTicketsRouter
    participant DjangoORM
    participant Database

    User->>FrontendApp: Submit feedback form (rating, comments)
    FrontendApp->>FeedbackApi: createFeedback(ticketId, payload)
    FeedbackApi->>FeedbackApi: Build FormData
    FeedbackApi->>FeedbackApi: formData.append(rating, payload.rating)
    FeedbackApi->>FeedbackApi: formData.append(comments, payload.comments)

    FeedbackApi->>BackendTicketsRouter: POST /tickets/{id}/feedback/
    BackendTicketsRouter->>DjangoORM: get_object_or_404(Ticket, id=id)
    DjangoORM->>Database: Query Ticket by id
    Database-->>DjangoORM: Ticket or not found
    alt ticket found
        DjangoORM-->>BackendTicketsRouter: Ticket instance
        BackendTicketsRouter->>BackendTicketsRouter: Check ticket.student == request.user
        BackendTicketsRouter->>BackendTicketsRouter: Check ticket.status == resolved
        alt owner and resolved
            BackendTicketsRouter->>BackendTicketsRouter: Validate TicketFeedbackCreateSchema from Form data
            alt validation ok
                BackendTicketsRouter->>Database: Create TicketFeedback
                Database-->>BackendTicketsRouter: Saved feedback
                BackendTicketsRouter-->>FeedbackApi: 201 TicketFeedbackSchema
                FeedbackApi-->>FrontendApp: Feedback data
                FrontendApp-->>User: Show success state
            else validation error
                BackendTicketsRouter-->>FeedbackApi: 400 {detail: [ {msg: error1}, {msg: error2} ]}
                FeedbackApi->>FeedbackApi: Parse body.detail array to detail string
                FeedbackApi-->>FrontendApp: Error(message or detail)
                FrontendApp-->>User: Show validation error messages
            end
        else not owner or not resolved
            BackendTicketsRouter-->>FeedbackApi: 403 {detail: Not allowed to submit feedback}
            FeedbackApi-->>FrontendApp: Error(message or detail)
            FrontendApp-->>User: Show permission error
        end
    else ticket not found
        BackendTicketsRouter-->>FeedbackApi: 404 {detail: Not found.}
        FeedbackApi-->>FrontendApp: Error(message or detail)
        FrontendApp-->>User: Show not found error
    end
Loading

File-Level Changes

Change Details Files
Refactor ticket detail endpoint into separate ID and ticket-number routes and tighten access control.
  • Change route from '/{ticket_id}' with str param to '/{id}' with int param and always query by primary key
  • Extract ticket-number lookup into new '/number/{ticket_number}' endpoint
  • Ensure both endpoints annotate tickets with comments_count and enforce that only the ticket owner or staff can access them
  • Use TicketSchema.from_orm(ticket, request) consistently for responses
backend/apps/tickets/views.py
Adjust feedback creation API to accept multipart form data and align frontend with backend validation/error responses.
  • Change create_feedback view to take payload from Form(...) instead of JSON body while keeping optional file attachment
  • Update frontend createFeedback call to send FormData with rating and optional comments, dropping explicit JSON Content-Type header
  • Enhance frontend feedback API error handling to surface 'detail' field or array of validation messages when present
  • Update deleteFeedback error handling to include backend 'detail' message when available
backend/apps/tickets/views.py
frontend/src/lib/api/feedback.ts
Standardize frontend ticket API error propagation to include backend 'detail' messages where available.
  • Update shared ticket API handleRes helper to prefer backend 'detail' when throwing errors
  • Update deleteTicket error handling to include backend 'detail' in the thrown Error message
frontend/src/lib/api/ticket.ts

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 28, 2026

📝 Walkthrough

Walkthrough

Backend ticket endpoints now accept numeric IDs with authorization checks returning 404 when unauthorized, include a new endpoint to fetch by ticket number, and return properly serialized TicketSchema responses. Frontend feedback creation converts to FormData format, and error handling across frontend API utilities improves by falling back to body.detail fields.

Changes

Cohort / File(s) Summary
Backend Ticket Endpoints
backend/apps/tickets/views.py
Replaced string ticket_id parameter with integer id; added authorization checks returning 404 on unauthorized access; introduced new endpoint /number/{ticket_number} for ticket number-based lookup; updated create_feedback signature to use Form-wrapped payload instead of direct schema parameter; returns TicketSchema.from_orm serialization.
Frontend Error Handling
frontend/src/lib/api/feedback.ts, frontend/src/lib/api/ticket.ts
Enhanced error message extraction across both files by including body.detail as fallback alongside body.message and res.statusText; createFeedback converted to send FormData instead of JSON, conditionally appending comments; deleteFeedback updated to reference body.detail in error messages.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • Polqt

Poem

🐰 A ticket's ID now integers shine,
With Form-wrapped feedback, all fine,
Authorization guards the way,
While error details light the day,
From strings to types, we hop and dance!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: reworked ticket detail endpoints (improved retrieval) and adjusted feedback handling (FormData changes, error handling).

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/student-feedback

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

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

Hey - I've left some high level feedback:

  • The error handling logic that derives a message from body.message/body.detail (including the array-of-objects case) is now duplicated and slightly inconsistent between feedback.ts and ticket.ts (and within feedback.ts for delete vs. other calls); consider extracting a shared helper to keep behavior uniform and easier to maintain.
  • The split between /tickets/{id} (numeric id) and /tickets/number/{ticket_number} changes the original behavior where a single endpoint accepted either ids or ticket numbers; if any callers still rely on the old path shape or on passing ticket numbers to /tickets/{...}, you may want to add a compatibility layer or explicitly deprecate the old usage.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The error handling logic that derives a message from `body.message`/`body.detail` (including the array-of-objects case) is now duplicated and slightly inconsistent between `feedback.ts` and `ticket.ts` (and within `feedback.ts` for delete vs. other calls); consider extracting a shared helper to keep behavior uniform and easier to maintain.
- The split between `/tickets/{id}` (numeric id) and `/tickets/number/{ticket_number}` changes the original behavior where a single endpoint accepted either ids or ticket numbers; if any callers still rely on the old path shape or on passing ticket numbers to `/tickets/{...}`, you may want to add a compatibility layer or explicitly deprecate the old usage.

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.


@router.post("/{id}/feedback/", response={201: TicketFeedbackSchema, 400: dict, 403: dict})
def create_feedback(request, id: int, payload: TicketFeedbackCreateSchema, attachment: UploadedFile = File(None)):
def create_feedback(request, id: int, payload: TicketFeedbackCreateSchema = Form(...), attachment: UploadedFile = File(None)):
Copy link
Owner

Choose a reason for hiding this comment

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

winyan ko

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.

🧹 Nitpick comments (1)
backend/apps/tickets/views.py (1)

246-258: Add query optimization to avoid N+1 queries.

Both ticket_detail and ticket_detail_by_number lack select_related and prefetch_related that other endpoints use (e.g., ticket_list at lines 75-77). Since TicketSchema.from_orm accesses student, category, priority, and attachments_tickets, this will cause multiple additional queries per request.

♻️ Proposed fix
 `@router.get`("/{id}", response={200: TicketSchema, 404: dict})
 def ticket_detail(request, id: int):
-    ticket = get_object_or_404(Ticket.objects.annotate(comments_count=Count('comments')), id=id)
+    ticket = get_object_or_404(
+        Ticket.objects.select_related('category', 'priority', 'student')
+        .prefetch_related('attachments_tickets')
+        .annotate(comments_count=Count('comments')),
+        id=id
+    )
     if ticket.student != request.user and not request.user.is_staff:
         return {"detail": "Not found."}, 404
     return 200, TicketSchema.from_orm(ticket, request)

 `@router.get`("/number/{ticket_number}", response={200: TicketSchema, 404: dict})
 def ticket_detail_by_number(request, ticket_number: str):
-    ticket = get_object_or_404(Ticket.objects.annotate(comments_count=Count('comments')), ticket_number=ticket_number)
+    ticket = get_object_or_404(
+        Ticket.objects.select_related('category', 'priority', 'student')
+        .prefetch_related('attachments_tickets')
+        .annotate(comments_count=Count('comments')),
+        ticket_number=ticket_number
+    )
     if ticket.student != request.user and not request.user.is_staff:
         return {"detail": "Not found."}, 404
     return 200, TicketSchema.from_orm(ticket, request)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/apps/tickets/views.py` around lines 246 - 258, Both ticket_detail and
ticket_detail_by_number fetch Ticket without the same
select_related/prefetch_related used in ticket_list, causing N+1 queries when
TicketSchema.from_orm accesses relations; update the queryset in both functions
(the get_object_or_404 calls inside ticket_detail and ticket_detail_by_number)
to include the same optimizations as ticket_list by adding
.select_related('student', 'category', 'priority') and
.prefetch_related('attachments_tickets') while keeping the
annotate(Count('comments')) so related objects are loaded in a single query.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@backend/apps/tickets/views.py`:
- Around line 246-258: Both ticket_detail and ticket_detail_by_number fetch
Ticket without the same select_related/prefetch_related used in ticket_list,
causing N+1 queries when TicketSchema.from_orm accesses relations; update the
queryset in both functions (the get_object_or_404 calls inside ticket_detail and
ticket_detail_by_number) to include the same optimizations as ticket_list by
adding .select_related('student', 'category', 'priority') and
.prefetch_related('attachments_tickets') while keeping the
annotate(Count('comments')) so related objects are loaded in a single query.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 231b298 and c1cca1d.

📒 Files selected for processing (3)
  • backend/apps/tickets/views.py
  • frontend/src/lib/api/feedback.ts
  • frontend/src/lib/api/ticket.ts

@Polqt Polqt merged commit abc5fa1 into main Feb 28, 2026
3 of 4 checks passed
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