Skip to content

Refactor/consolidate assignee fields - #53

Merged
gitnes94 merged 10 commits into
mainfrom
refactor/consolidate-assignee-fields
Apr 8, 2026
Merged

Refactor/consolidate assignee fields#53
gitnes94 merged 10 commits into
mainfrom
refactor/consolidate-assignee-fields

Conversation

@gitnes94

@gitnes94 gitnes94 commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Fixes issue #29

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Resolved duplicate ticket assignment tracking that could cause inconsistencies in staff workload management.
  • Refactor

    • Consolidated ticket assignment logic for improved data consistency and cleaner database structure.
    • Enhanced ticket assignment API endpoint to return properly typed responses for better integration.

@coderabbitai

coderabbitai Bot commented Apr 8, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR consolidates the ticket assignment relationship by removing the redundant assignee field and its mappings, standardizing on the assignedTo field exclusively. Changes span model definitions (Staff and Ticket entities), service logic (TicketService), controller endpoints (TicketController), and database schema (migration to move data and drop the old column).

Changes

Cohort / File(s) Summary
Entity Model Updates
src/main/java/org/example/cyberwatch/features/staff/model/Staff.java, src/main/java/org/example/cyberwatch/features/ticket/model/Ticket.java
Updated Staff.assignedTickets JPA mapping from mappedBy = "assignee" to mappedBy = "assignedTo" and removed cascade/orphanRemoval options. Removed the duplicate assignee @ManyToOne field from Ticket entirely.
Service Layer
src/main/java/org/example/cyberwatch/features/ticket/service/TicketService.java
Consolidated two assignTicket method overloads into a single unified method signature accepting (Long ticketId, Long staffId, Long assignedById) that now uses setAssignedTo() exclusively and returns TicketResponseDTO.
Controller Endpoint
src/main/java/org/example/cyberwatch/features/ticket/controller/TicketController.java
Updated @PutMapping("/{ticketId}/assign") to call the renamed service method and return typed ResponseEntity<TicketResponseDTO> instead of ResponseEntity<?>.
Database Migration
src/main/resources/db/migration/V2__remove_Duplicate_Assignee_Column.sql
Migrates existing data from assignee_id to assigned_to_id (when target is NULL), drops the foreign key constraint, and removes the obsolete assignee_id column.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related issues

  • Issue #49 — Both changes modify the same TicketController assign endpoint's return type and service method invocation, directly addressing the same functionality.

Possibly related PRs

  • PR #26 — Both modify the ticket-assignment domain model and endpoints; PR #26 introduces the initial assignee relationship while this PR removes the duplicate assignee field in favor of assignedTo.
  • PR #33 — Both address the ticket assignee schema lifecycle; PR #33 adds the assignee_id column while this PR migrates data to assigned_to_id and removes the redundant column.
  • PR #21 — Both modify Staff.assignedTickets one-to-many mapping; PR #21 adds it with mappedBy = "assignee" while this PR updates it to mappedBy = "assignedTo" and removes cascade options.

Suggested reviewers

  • Ericthilen
  • alicewersen-rgb
  • codebyNorthsteep

Poem

🐰 Hopping through the schema deep,
Two assignments turned to one,
Assignee's alias laid to sleep,
AssignedTo's reign has begun!
Migrations prune the tangled vine,
Relationships now clean and fine.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 reflects the main objective of the changeset, which consolidates duplicate assignee tracking from two fields into one.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/consolidate-assignee-fields

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.

@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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/cyberwatch/features/staff/model/Staff.java`:
- Around line 66-67: The assignedTickets field in Staff currently uses ownership
semantics that delete tickets when staff is removed; update the mapping on the
Staff.assignedTickets Set<Ticket> to remove cascade = CascadeType.ALL and
orphanRemoval = true so Ticket lifetime is not tied to Staff, keeping
`@OneToMany`(mappedBy = "assignedTo") only; ensure Ticket.assignedTo remains the
owning side and, where tickets are unassigned or staff deleted, update the code
that manipulates assignedTickets (e.g., removal logic) to set
ticket.setAssignedTo(null) instead of relying on collection removal to delete
rows.

In
`@src/main/java/org/example/cyberwatch/features/ticket/service/TicketService.java`:
- Around line 133-138: The assignment flow in TicketService currently forces
ticket.setStatus(Status.IN_PROGRESS) and logs a status change without running
validateStatusTransition, which lets invalid transitions and false "status
change" records occur; fix by using validateStatusTransition(ticket, oldStatus,
Status.IN_PROGRESS) before changing status and only call ticket.setStatus and
activityLogService.logStatusChange when validateStatusTransition returns success
and oldStatus != Status.IN_PROGRESS; if reopening terminal states
(DRAFT/RESOLVED/CLOSED) on assignment is desired, implement an explicit reopen
transition method (e.g., reopenForAssignment) that invokes
validateStatusTransition with the intended target and documents the semantic,
otherwise keep assignment-only paths that set assignedTo and save without
touching status or logging status changes.

In `@src/main/resources/db/migration/V2_removeDuplicateAssigneeColumn.sql`:
- Around line 4-5: The migration currently drops FK_TICKETS_ON_ASSIGNEE and
assignee_id without preserving data — before removing the legacy column
(assignee_id) in V2_removeDuplicateAssigneeColumn.sql, add a backfill step that
copies non-null values from assignee_id into the canonical column assigned_to_id
and create or ensure the canonical FK exists, then drop the legacy FK/column;
additionally add a guard query that detects rows where both assigned_to_id and
assignee_id are non-null but different and aborts/fails the migration (with an
explicit error) so you don’t silently pick one value.
- Around line 1-5: Rename the migration file so Flyway can detect it by changing
the separator between version and description to a double underscore (rename
current migration to V2__remove_Duplicate_Assignee_Column.sql), and ensure the
SQL in that migration safely preserves data: before running ALTER TABLE tickets
DROP COLUMN IF EXISTS assignee_id; add a data migration step to copy any
non-null assignee_id values into assigned_to_id (e.g., UPDATE tickets SET
assigned_to_id = assignee_id WHERE assigned_to_id IS NULL AND assignee_id IS NOT
NULL), then drop the FK and column as currently written (ALTER TABLE tickets
DROP CONSTRAINT IF EXISTS FK_TICKETS_ON_ASSIGNEE; ALTER TABLE tickets DROP
COLUMN IF EXISTS assignee_id;).
🪄 Autofix (Beta)

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

Review profile: CHILL

Plan: Pro

Run ID: 01277c93-a9a2-4604-9a48-98a62c0f09dc

📥 Commits

Reviewing files that changed from the base of the PR and between 422d6d3 and 17cc233.

📒 Files selected for processing (5)
  • src/main/java/org/example/cyberwatch/features/staff/model/Staff.java
  • src/main/java/org/example/cyberwatch/features/ticket/controller/TicketController.java
  • src/main/java/org/example/cyberwatch/features/ticket/model/Ticket.java
  • src/main/java/org/example/cyberwatch/features/ticket/service/TicketService.java
  • src/main/resources/db/migration/V2_removeDuplicateAssigneeColumn.sql
💤 Files with no reviewable changes (1)
  • src/main/java/org/example/cyberwatch/features/ticket/model/Ticket.java

Comment thread src/main/java/org/example/cyberwatch/features/staff/model/Staff.java Outdated

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

♻️ Duplicate comments (2)
src/main/java/org/example/cyberwatch/features/ticket/service/TicketService.java (1)

133-136: ⚠️ Potential issue | 🟠 Major

Don't log IN_PROGRESS when assignment no longer changes status.

Lines 134-135 only change assignedTo, but Line 136 still records oldStatus → IN_PROGRESS. That leaves the activity log out of sync with the persisted ticket status returned by this method.

🛠️ Minimal fix
         Status oldStatus = ticket.getStatus();
         ticket.setAssignedTo(staff);
         Ticket saved = ticketRepository.save(ticket);
-        activityLogService.logStatusChange(saved, assigner, oldStatus, Status.IN_PROGRESS);
+        if (oldStatus != saved.getStatus()) {
+            activityLogService.logStatusChange(saved, assigner, oldStatus, saved.getStatus());
+        }
         return TicketResponseDTO.from(saved);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/org/example/cyberwatch/features/ticket/service/TicketService.java`
around lines 133 - 136, The code always logs a status change to IN_PROGRESS
after only updating assignedTo; instead, capture oldStatus = ticket.getStatus(),
save via ticketRepository.save(ticket), then compare the persisted status (e.g.
saved.getStatus()) to oldStatus and only call
activityLogService.logStatusChange(saved, assigner, oldStatus,
saved.getStatus()) when they differ; do not hardcode Status.IN_PROGRESS in
activityLogService.logStatusChange unless you also set the ticket status to that
value.
src/main/resources/db/migration/V2__remove_Duplicate_Assignee_Column.sql (1)

4-10: ⚠️ Potential issue | 🔴 Critical

Fail fast if the two assignee columns disagree.

V1__init.sql created both assigned_to_id and assignee_id, and this migration only backfills rows where the canonical column is NULL. If any row has both columns populated with different staff IDs, Line 10 silently drops the legacy value. Add a pre-drop guard that aborts the migration when both columns are non-null and unequal instead of discarding one side.

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

In `@src/main/resources/db/migration/V2__remove_Duplicate_Assignee_Column.sql`
around lines 4 - 10, Add a pre-drop guard that fails the migration if any ticket
has both assigned_to_id and assignee_id populated but with different values:
before running the UPDATE/ALTER TABLE statements check for existence of rows
where assigned_to_id IS NOT NULL AND assignee_id IS NOT NULL AND assigned_to_id
<> assignee_id and abort (raise an exception) with a clear message identifying
the conflicting ticket(s). Locate the migration logic around the UPDATE to
tickets SET assigned_to_id = assignee_id and the subsequent ALTER TABLE ... DROP
COLUMN assignee_id and insert this existence check (and exception) so the
migration fails fast instead of silently discarding differing legacy values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@src/main/java/org/example/cyberwatch/features/ticket/service/TicketService.java`:
- Around line 133-136: The code always logs a status change to IN_PROGRESS after
only updating assignedTo; instead, capture oldStatus = ticket.getStatus(), save
via ticketRepository.save(ticket), then compare the persisted status (e.g.
saved.getStatus()) to oldStatus and only call
activityLogService.logStatusChange(saved, assigner, oldStatus,
saved.getStatus()) when they differ; do not hardcode Status.IN_PROGRESS in
activityLogService.logStatusChange unless you also set the ticket status to that
value.

In `@src/main/resources/db/migration/V2__remove_Duplicate_Assignee_Column.sql`:
- Around line 4-10: Add a pre-drop guard that fails the migration if any ticket
has both assigned_to_id and assignee_id populated but with different values:
before running the UPDATE/ALTER TABLE statements check for existence of rows
where assigned_to_id IS NOT NULL AND assignee_id IS NOT NULL AND assigned_to_id
<> assignee_id and abort (raise an exception) with a clear message identifying
the conflicting ticket(s). Locate the migration logic around the UPDATE to
tickets SET assigned_to_id = assignee_id and the subsequent ALTER TABLE ... DROP
COLUMN assignee_id and insert this existence check (and exception) so the
migration fails fast instead of silently discarding differing legacy values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fdbe9050-7f51-49d5-92c1-73ee4a8cd581

📥 Commits

Reviewing files that changed from the base of the PR and between b955c33 and a3c936e.

📒 Files selected for processing (3)
  • src/main/java/org/example/cyberwatch/features/staff/model/Staff.java
  • src/main/java/org/example/cyberwatch/features/ticket/service/TicketService.java
  • src/main/resources/db/migration/V2__remove_Duplicate_Assignee_Column.sql

@gitnes94
gitnes94 merged commit 81cbf95 into main Apr 8, 2026
1 check passed
@gitnes94
gitnes94 deleted the refactor/consolidate-assignee-fields branch April 8, 2026 07:46
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.

1 participant