Skip to content

implement workflow for EmploymentForm - #48

Merged
codebyNorthsteep merged 18 commits into
mainfrom
enhancement/implementServiceAndMapperInForms
Apr 7, 2026
Merged

implement workflow for EmploymentForm#48
codebyNorthsteep merged 18 commits into
mainfrom
enhancement/implementServiceAndMapperInForms

Conversation

@codebyNorthsteep

@codebyNorthsteep codebyNorthsteep commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

This pull request introduces a comprehensive implementation for employment form management, including new endpoints, DTOs, services, and entity/model adjustments. The changes enable HR users to create employment forms, managers to approve them, and the system to convert approved forms into staff records. Additionally, the codebase is refactored to use a consistent ApprovalStatus enum and to generalize relationships to the Staff entity.

Key changes are grouped below by theme:

Employment Form Feature Implementation

  • Added EmploymentFormController with endpoints for creating employment forms (HR), approving forms (Manager), and listing pending forms (HR/Manager), enforcing role-based access control.
  • Implemented EmploymentFormService with logic to create employment forms, prevent duplicate SSNs, retrieve pending forms, and finalize employment by converting forms to staff records.
  • Introduced repository methods in EmploymentFormRepository for finding forms by status and checking for duplicate SSNs.
  • Added DTOs (CreateEmploymentDTO, EmploymentFormDTO, UpdateEmploymentDTO) for form data transfer and validation, and created EmploymentMapper for conversions between entities and DTOs. [1] [2] [3] [4]

Model and Entity Refactoring

  • Refactored EmploymentForm and related DTOs to use the new ApprovalStatus enum instead of the previous Status type, and generalized relationships from HR/Management to Staff for both creator and approver. [1] [2] [3]
  • Updated Staff entity to include a new relationship for created employment forms and imported the relevant form model. [1] [2]
  • Added ApprovalStatus enum to standardize approval state management.

Repository and Mapper Enhancements

  • Extended StaffRepository with methods to check for existing staff by SSN and to find staff by email for authentication/authorization logic.
  • Introduced StaffMapper for converting between Staff entities and DTOs.

Cleanup and Other Adjustments

  • Removed the empty FormController class, as its responsibilities are now handled by the new employment form controller.
  • Minor adjustment to ReportForm attachments mapping for consistency.
  • Added skeleton for ReportFormService for future report form handling.

These changes collectively establish a robust foundation for employment form workflows, improve code maintainability, and ensure proper validation and authorization throughout the process.

Summary by CodeRabbit

  • New Features

    • Employment form workflow: create employment forms (HR), approve & finalize employment (CEO/CTO), and list pending forms. Approval creates staff records.
  • Refactor

    • Enforced unique, non-nullable email on employment records and introduced standardized approval statuses (PENDING/APPROVED/REJECTED). Centralized mapping between forms, DTOs, and staff.
  • Chores / Breaking Changes

    • Removed legacy staff entity types and updated related relationships.
  • Tests

    • Added controller tests for form creation (success and validation failure).

…mploymentMapper; add StaffMapper for staff entity conversion
…or staff entity conversion and implement error handling in createEmployee method
…ploymentForm and getPendingForms endpoints in EmploymentFormController; update EmploymentFormService to return EmploymentFormDTO and handle staff creation
…ethod and add validation for social security number
…ms with the logged-in HR staff; update service method to accept HR identifier
@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown

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

Walkthrough

Adds an employment form workflow: new REST controller endpoints to create, approve, and list pending forms; DTOs, mappers, and service logic to create/approve forms and finalize staff; repository and entity changes (ApprovalStatus enum, unique email, Staff relations); removes HR/Management/Consultant entities; adds controller tests.

Changes

Cohort / File(s) Summary
Controller Layer
src/main/java/org/example/cyberwatch/features/form/controller/EmploymentFormController.java, src/main/java/org/example/cyberwatch/features/form/controller/FormController.java
Added EmploymentFormController with POST /employment, POST /{id}/approve, GET /pending (role-restricted). Removed empty FormController.
Service Layer
src/main/java/org/example/cyberwatch/features/form/service/EmploymentFormService.java, src/main/java/org/example/cyberwatch/features/form/service/ReportFormService.java
New EmploymentFormService managing create/getPending/approveAndFinalize flows (dup-SSN checks, staff creation, form deletion). ReportFormService annotated as @Service and uses repository injection.
Entity & Persistence
src/main/java/org/example/cyberwatch/features/form/model/EmploymentForm.java, src/main/java/org/example/cyberwatch/features/staff/model/Staff.java, src/main/java/org/example/cyberwatch/features/form/model/ReportForm.java
EmploymentForm now uses ApprovalStatus, createdBy/approvedBy reference Staff, email marked unique=true, nullable=false. Staff gains createdForms relation. ReportForm removed @OrderColumn.
DTOs & Mappers
src/main/java/org/example/cyberwatch/features/form/model/CreateEmploymentDTO.java, src/main/java/org/example/cyberwatch/features/form/model/EmploymentFormDTO.java, src/main/java/org/example/cyberwatch/features/form/model/EmploymentMapper.java, src/main/java/org/example/cyberwatch/features/staff/model/StaffMapper.java
Added CreateEmploymentDTO (validation). Updated EmploymentFormDTO (use ApprovalStatus, removed validations and approvedDate). Added EmploymentMapper (entity↔DTO, form→staff) and StaffMapper.
Repositories & Enums
src/main/java/org/example/cyberwatch/features/form/repository/EmploymentFormRepository.java, src/main/java/org/example/cyberwatch/features/staff/repository/StaffRepository.java, src/main/java/org/example/cyberwatch/shared/model/enums/ApprovalStatus.java
Added repo methods: findByStatus, existsBySocialSecurityNumber (EmploymentFormRepo); existsBySocialSecurityNumber, findByEmail (StaffRepo). New ApprovalStatus enum (PENDING, APPROVED, REJECTED).
Staff domain removals
src/main/java/org/example/cyberwatch/features/staff/model/HR.java, src/main/java/org/example/cyberwatch/features/staff/model/Management.java, src/main/java/org/example/cyberwatch/features/staff/model/Consultant.java
Removed HR, Management, and Consultant entity classes and their JPA mappings.
Controller Tests
src/test/java/org/example/cyberwatch/form/EmploymentFormControllerTests.java
Added @WebMvcTest for EmploymentFormController: tests for valid creation and invalid-email payloads with mocked EmploymentFormService and HR auth.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Controller as EmploymentFormController
    participant Service as EmploymentFormService
    participant Mapper as EmploymentMapper
    participant EmpRepo as EmploymentFormRepository
    participant StaffRepo as StaffRepository
    participant DB as Database

    Client->>Controller: POST /api/forms/employment (CreateEmploymentDTO, Auth)
    Controller->>Service: createForm(dto, loggedInHr)
    Service->>EmpRepo: existsBySocialSecurityNumber(ssn)
    EmpRepo->>DB: Query by SSN
    DB-->>EmpRepo: result
    Service->>StaffRepo: existsBySocialSecurityNumber(ssn)
    StaffRepo->>DB: Query by SSN
    DB-->>StaffRepo: result
    Service->>StaffRepo: findByEmail(loggedInHr)
    StaffRepo->>DB: Query by email
    DB-->>StaffRepo: HR Staff
    Service->>Mapper: toEntity(dto)
    Mapper-->>Service: EmploymentForm
    Service->>EmpRepo: save(form)
    EmpRepo->>DB: Persist
    DB-->>EmpRepo: Saved form
    Service->>Mapper: toDTO(savedForm)
    Mapper-->>Service: EmploymentFormDTO
    Service-->>Controller: EmploymentFormDTO
    Controller-->>Client: 201 Created
Loading
sequenceDiagram
    participant Client
    participant Controller as EmploymentFormController
    participant Service as EmploymentFormService
    participant Mapper as EmploymentMapper
    participant EmpRepo as EmploymentFormRepository
    participant StaffRepo as StaffRepository
    participant DB as Database

    Client->>Controller: POST /api/forms/{id}/approve
    Controller->>Service: approveAndFinalizeEmployment(id)
    Service->>EmpRepo: findById(id)
    EmpRepo->>DB: Query by ID
    DB-->>EmpRepo: EmploymentForm
    Service->>Service: validate status == PENDING
    Service->>Mapper: formToStaff(form)
    Mapper-->>Service: Staff
    Service->>StaffRepo: save(staff)
    StaffRepo->>DB: Persist Staff
    DB-->>StaffRepo: Saved Staff
    Service->>EmpRepo: deleteById(id)
    EmpRepo->>DB: Delete form
    DB-->>EmpRepo: Deleted
    Service-->>Controller: void
    Controller-->>Client: 204 No Content
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • gitnes94
  • Ericthilen

Poem

🐇 I hopped a DTO through moonlit code,
mapped the fields where new staff grow,
pending forms I nudged to light,
approved — they sprang from data's night,
nibbling bugs, I watch the workflow flow.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.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 'implement workflow for EmploymentForm' accurately reflects the main objective of introducing employment-form management endpoints, service logic, and related models to support approval workflows.

✏️ 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 enhancement/implementServiceAndMapperInForms

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: 10

🧹 Nitpick comments (7)
src/main/java/org/example/cyberwatch/features/staff/model/Staff.java (1)

63-64: Initialize createdForms eagerly.

This is the only collection on Staff that starts as null. Keeping it uninitialized makes transient Staff instances behave differently from reportForms and assignedTickets, and simple calls like getCreatedForms().add(...) will blow up.

♻️ Proposed fix
-    private Set<EmploymentForm> createdForms;
+    private Set<EmploymentForm> createdForms = new HashSet<>();
🤖 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/staff/model/Staff.java` around
lines 63 - 64, The createdForms collection in Staff is left null causing NPEs
when used on transient instances; initialize it like the other collections
(reportForms, assignedTickets) by instantiating createdForms (e.g., new
HashSet<>()) at declaration or in the Staff constructor and ensure its getter
getCreatedForms() returns the non-null Set so calls like
getCreatedForms().add(...) are safe; update the field initialization for
createdForms in the Staff class accordingly.
src/main/java/org/example/cyberwatch/features/staff/model/StaffMapper.java (1)

21-30: toEntity is not safe as an update mapper yet.

It drops both id and socialSecurityNumber, so a future profile-update flow built on this will create a new Staff that violates the entity’s required fields instead of updating the existing row. Prefer mutating an existing entity here, or map the immutable fields explicitly.

♻️ Safer shape for updates
-    public Staff toEntity(StaffDTO dto) {
-        Staff staff = new Staff();
+    public void updateEntity(StaffDTO dto, Staff staff) {
         staff.setFirstName(dto.getFirstName());
         staff.setLastName(dto.getLastName());
         staff.setEmail(dto.getEmail());
         staff.setPhoneNumber(dto.getPhoneNumber());
         staff.setRole(dto.getRole());
         staff.setDepartment(dto.getDepartment());
-        return staff;
     }
🤖 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/staff/model/StaffMapper.java`
around lines 21 - 30, The toEntity(StaffDTO dto) mapper drops immutable/required
fields (id and socialSecurityNumber) which will cause updates to create invalid
new Staff rows; change toEntity to either accept an existing Staff to mutate
(e.g., toEntity(StaffDTO dto, Staff existing)) or, if kept as factory,
explicitly copy dto.getId() and dto.getSocialSecurityNumber() into the new Staff
(or validate presence) so id and socialSecurityNumber are preserved for update
flows; update usages of toEntity to pass an existing Staff when performing
updates and keep toEntity only for creates if you choose that split.
src/test/java/org/example/cyberwatch/form/EmploymentFormControllerTests.java (1)

28-94: The security-sensitive endpoints are still untested.

This suite only covers the create route, but the PR also adds manager approval, pending-form listing, and role checks. Please add happy-path plus 403 coverage for those routes so access-control regressions are caught in the slice tests.

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

In `@src/test/java/org/example/cyberwatch/form/EmploymentFormControllerTests.java`
around lines 28 - 94, Add slice tests for the newly introduced manager-approval
and pending-form listing endpoints and include 403 checks for role enforcement:
in EmploymentFormControllerTests, add a happy-path test that mocks
employmentFormService.getPendingForms() and asserts GET
"/api/forms/employment/pending" returns 200 with expected JSON (use mockMvc and
objectMapper), add a happy-path test that mocks
employmentFormService.approveForm(id, ...) and asserts PUT/POST to the
controller's manager-approval endpoint returns 200/204 (include csrf() for
non-GET), and for each endpoint add a corresponding test using `@WithMockUser`
with a non-authorized role to assert status().isForbidden(); when creating these
tests reference the mocked service methods employmentFormService.getPendingForms
and employmentFormService.approveForm and reuse mockMvc, objectMapper, and the
existing `@MockitoBean` employmentFormService to stub returns.
src/main/java/org/example/cyberwatch/features/form/model/CreateEmploymentDTO.java (1)

49-52: createdDate and hrId should not be in a creation request DTO.

These fields are server-side concerns: createdDate is auto-generated via @CreationTimestamp on the entity, and hrId is derived from the authenticated user. Including them in the creation DTO implies clients can set them, which is misleading and could be a security concern if the mapper were to copy them.

Consider removing these fields from CreateEmploymentDTO or marking them as ignored during mapping.

🤖 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/form/model/CreateEmploymentDTO.java`
around lines 49 - 52, Remove server-controlled fields from the creation DTO:
delete createdDate and hrId from CreateEmploymentDTO (or at minimum stop
exposing them) and update any mapping logic that maps CreateEmploymentDTO to the
Employment entity (e.g., the mapper that handles CreateEmploymentDTO ->
Employment) to not copy these fields; instead let the entity populate
createdDate via `@CreationTimestamp` and derive hrId from the authenticated
principal when constructing the Employment entity on the server side.
src/main/java/org/example/cyberwatch/features/form/controller/EmploymentFormController.java (1)

27-31: Consider handling null Authentication parameter.

If this endpoint is called without proper authentication (e.g., during testing or misconfiguration), authentication.getName() will throw an NPE. While Spring Security should prevent unauthenticated access, defensive coding would add resilience.

🤖 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/form/controller/EmploymentFormController.java`
around lines 27 - 31, The createEmploymentForm method currently calls
authentication.getName() without null-checking; update createEmploymentForm to
defensively handle null Authentication (and null getName()) by checking if
authentication == null || authentication.getName() == null and if so either
return an appropriate 401/403 ResponseEntity (e.g.,
ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()) or throw an
AccessDeniedException, otherwise continue to call
employmentFormService.createForm with the authenticated principal; ensure you
reference the Authentication parameter in createEmploymentForm and keep the
loggedInHr variable assignment guarded by this check.
src/main/java/org/example/cyberwatch/features/form/service/EmploymentFormService.java (1)

41-43: Mixed language comment and assumption about authentication identifier.

The comment contains Swedish ("Antar att du har en sådan metod"). Also, authentication.getName() returning an email depends on how authentication is configured—it could return a username, ID, or email. This assumption should be documented or verified.

🤖 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/form/service/EmploymentFormService.java`
around lines 41 - 43, The inline comment contains Swedish and assumes
authentication.getName() is an email; replace the Swedish comment and remove the
assumption, and instead explicitly verify or document what
authentication.getName() returns (email vs username/ID) and use the matching
repository lookup: either staffRepository.findByEmail(loggedInHr) or
staffRepository.findByUsername(loggedInHr), or extract the email from
authentication.getPrincipal() if needed; update the code paths around setHrId(),
Staff, staffRepository.findByEmail and loggedInHr to use the correct identifier
and add a brief JavaDoc/comment stating which identifier is expected by
authentication configuration.
src/main/java/org/example/cyberwatch/features/form/model/EmploymentFormDTO.java (1)

39-40: Remove or clarify the leftover comment.

The comment //Long when fetching the Id? appears to be a development note that should be resolved or removed before merging.

🤖 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/form/model/EmploymentFormDTO.java`
around lines 39 - 40, Remove the leftover developer comment above the hrId field
in EmploymentFormDTO; either delete the line "//Long when fetching the Id?" or
replace it with a clear Javadoc or inline comment that explains the purpose of
the hrId field (e.g., what it represents and when it is used) so the field
declaration private Long hrId; is self-explanatory.
🤖 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/form/controller/EmploymentFormController.java`:
- Around line 34-39: Update the incorrect role names in the PreAuthorize
annotations so they match the Role enum: in EmploymentFormController change
hasRole('MANAGER') to hasRole('PROJECT_MANAGER') in the
approveForm(`@PathVariable` Long id) method and likewise replace
hasRole('MANAGER') with hasRole('PROJECT_MANAGER') in the getPendingForms(...)
method (leave hasRole('HR') as-is); ensure the annotations on these methods
reference the enum value PROJECT_MANAGER exactly.

In
`@src/main/java/org/example/cyberwatch/features/form/model/CreateEmploymentDTO.java`:
- Around line 46-47: The `@NotNull` on the status field of CreateEmploymentDTO
conflicts with EmploymentFormService.createForm()'s defaulting logic; remove the
`@NotNull` annotation from the private ApprovalStatus status field in
CreateEmploymentDTO so null values can pass validation and let
EmploymentFormService.createForm() apply the PENDING default (or alternatively,
if you prefer strictness, remove the defaulting branch in
EmploymentFormService.createForm() instead—pick one approach and make the
corresponding change).

In
`@src/main/java/org/example/cyberwatch/features/form/model/EmploymentForm.java`:
- Around line 67-73: EmploymentForm now maps createdBy and approvedBy to Staff
(fields EmploymentForm.createdBy and EmploymentForm.approvedBy) which conflicts
with existing inverse mappings in HR and Management; either remove the
`@OneToMany`(mappedBy = "createdBy") from HR and `@OneToMany`(mappedBy =
"approvedBy") from Management so Staff is the sole inverse owner, or revert
EmploymentForm to reference HR and Management instead—pick one approach and make
the entity annotations consistent (EmploymentForm, Staff, HR, Management,
createdBy, approvedBy). Also add a DB migration that backfills hr_id and
approver_management_id into the new Staff FK (or vice versa depending on chosen
mapping) so existing rows map to the correct staff records (ensure the migration
copies/joins old entity IDs to the correct staff_id values and updates the
foreign key columns).

In
`@src/main/java/org/example/cyberwatch/features/form/model/EmploymentMapper.java`:
- Around line 56-62: The updateEntity(UpdateEmploymentDTO dto, EmploymentForm
entity) currently only copies status and ignores other updatable fields; modify
it to check each field on UpdateEmploymentDTO (e.g., getSsn(), getFirstName(),
getLastName(), getEmail(), getPhone(), getRole(), getDepartment(), getStatus())
and when a DTO getter is non-null (or non-empty if required) call the
corresponding EmploymentForm setter (e.g., setSsn(), setFirstName(),
setLastName(), setEmail(), setPhone(), setRole(), setDepartment(), setStatus())
so all intended updates are applied; keep the existing null guards for
dto/entity and ensure you only overwrite entity fields when the DTO provides a
value.
- Around line 11-26: The toDTO(EmploymentForm entity) method lacks a null check
and will NPE if entity is null; update toDTO in EmploymentMapper to return null
(or an empty/default EmploymentFormDTO per project conventions) when entity is
null, e.g., check if entity == null at the start of toDTO and short-circuit
before accessing any getters; reference the toDTO method, EmploymentForm
parameter, and EmploymentFormDTO return type when making the change.

In
`@src/main/java/org/example/cyberwatch/features/form/model/UpdateEmploymentDTO.java`:
- Around line 18-51: UpdateEmploymentDTO declares many validated fields
(socialSecurityNumber, firstName, lastName, email, phoneNumber, role,
department, status) but is unused by any controller and
EmploymentMapper.updateEntity() only copies status, creating a misleading API
contract; fix by either removing the DTO entirely if no update endpoint is
intended, or updating the API and mapping logic so the DTO is actually consumed:
add a controller endpoint that accepts UpdateEmploymentDTO and modify
EmploymentMapper.updateEntity(UpdateEmploymentDTO, Employment) to copy the
validated properties (or alternatively strip validation and fields down to only
status if only status updates are supported), ensuring validation annotations
match the supported update behavior.

In
`@src/main/java/org/example/cyberwatch/features/form/repository/EmploymentFormRepository.java`:
- Line 14: The create flow currently only checks
EmploymentFormRepository.existsBySocialSecurityNumber(...), which misses SSNs
already persisted to Staff (because approveAndFinalizeEmployment(...) moves data
into Staff and deletes the form); update the form creation path to also consult
StaffRepository.existsBySocialSecurityNumber(socialSecurityNumber) and reject
the submission if either repository reports existence. Locate the
service/controller method that saves new EmploymentForm (the code that calls
EmploymentFormRepository.save(...) or performs the current
existsBySocialSecurityNumber check) and add a second guard calling
StaffRepository.existsBySocialSecurityNumber(...), returning the same validation
error path when true.

In
`@src/main/java/org/example/cyberwatch/features/form/service/EmploymentFormService.java`:
- Around line 44-50: The defaulting to ApprovalStatus.PENDING is unreachable and
done on the DTO after mapping; remove the `@NotNull` on CreateEmploymentDTO.status
(if you want service-side defaulting) and ensure the default is applied before
converting into the entity — i.e., in EmploymentFormService set
form.setStatus(ApprovalStatus.PENDING) when form.getStatus()==null prior to
calling employmentMapper.toEntity(form), or alternatively apply the default
directly on the mapped entity (formEntity.setStatus(ApprovalStatus.PENDING))
before saving with employmentFormRepository.save and returning
employmentMapper.toDTO(...).
- Around line 36-39: The duplicate-SSN safety net currently checks only
employmentFormRepository.existsBySocialSecurityNumber(...) in
EmploymentFormService; add an additional check against the Staff table (via
staffRepository, e.g. staffRepository.existsBySocialSecurityNumber(...) or
equivalent) before accepting a new EmploymentForm and before
approveAndFinalizeEmployment() persists a Staff record, and throw the same
IllegalStateException (or a consistent domain exception) with a clear message
when a matching SSN is found to prevent a unique-constraint failure on save.

In
`@src/main/java/org/example/cyberwatch/features/staff/repository/StaffRepository.java`:
- Line 11: The email field is not guaranteed unique yet code relies on
StaffRepository.findByEmail(...) returning a single result (used by
EmploymentFormService.createForm), so add a uniqueness constraint: update the
Staff entity's email property to include `@Column`(name = "email", unique = true,
nullable = false) (or create a DB migration to add a UNIQUE constraint on the
email column) and run/verify migrations so findByEmail(...) cannot return
duplicates at runtime.

---

Nitpick comments:
In
`@src/main/java/org/example/cyberwatch/features/form/controller/EmploymentFormController.java`:
- Around line 27-31: The createEmploymentForm method currently calls
authentication.getName() without null-checking; update createEmploymentForm to
defensively handle null Authentication (and null getName()) by checking if
authentication == null || authentication.getName() == null and if so either
return an appropriate 401/403 ResponseEntity (e.g.,
ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()) or throw an
AccessDeniedException, otherwise continue to call
employmentFormService.createForm with the authenticated principal; ensure you
reference the Authentication parameter in createEmploymentForm and keep the
loggedInHr variable assignment guarded by this check.

In
`@src/main/java/org/example/cyberwatch/features/form/model/CreateEmploymentDTO.java`:
- Around line 49-52: Remove server-controlled fields from the creation DTO:
delete createdDate and hrId from CreateEmploymentDTO (or at minimum stop
exposing them) and update any mapping logic that maps CreateEmploymentDTO to the
Employment entity (e.g., the mapper that handles CreateEmploymentDTO ->
Employment) to not copy these fields; instead let the entity populate
createdDate via `@CreationTimestamp` and derive hrId from the authenticated
principal when constructing the Employment entity on the server side.

In
`@src/main/java/org/example/cyberwatch/features/form/model/EmploymentFormDTO.java`:
- Around line 39-40: Remove the leftover developer comment above the hrId field
in EmploymentFormDTO; either delete the line "//Long when fetching the Id?" or
replace it with a clear Javadoc or inline comment that explains the purpose of
the hrId field (e.g., what it represents and when it is used) so the field
declaration private Long hrId; is self-explanatory.

In
`@src/main/java/org/example/cyberwatch/features/form/service/EmploymentFormService.java`:
- Around line 41-43: The inline comment contains Swedish and assumes
authentication.getName() is an email; replace the Swedish comment and remove the
assumption, and instead explicitly verify or document what
authentication.getName() returns (email vs username/ID) and use the matching
repository lookup: either staffRepository.findByEmail(loggedInHr) or
staffRepository.findByUsername(loggedInHr), or extract the email from
authentication.getPrincipal() if needed; update the code paths around setHrId(),
Staff, staffRepository.findByEmail and loggedInHr to use the correct identifier
and add a brief JavaDoc/comment stating which identifier is expected by
authentication configuration.

In `@src/main/java/org/example/cyberwatch/features/staff/model/Staff.java`:
- Around line 63-64: The createdForms collection in Staff is left null causing
NPEs when used on transient instances; initialize it like the other collections
(reportForms, assignedTickets) by instantiating createdForms (e.g., new
HashSet<>()) at declaration or in the Staff constructor and ensure its getter
getCreatedForms() returns the non-null Set so calls like
getCreatedForms().add(...) are safe; update the field initialization for
createdForms in the Staff class accordingly.

In `@src/main/java/org/example/cyberwatch/features/staff/model/StaffMapper.java`:
- Around line 21-30: The toEntity(StaffDTO dto) mapper drops immutable/required
fields (id and socialSecurityNumber) which will cause updates to create invalid
new Staff rows; change toEntity to either accept an existing Staff to mutate
(e.g., toEntity(StaffDTO dto, Staff existing)) or, if kept as factory,
explicitly copy dto.getId() and dto.getSocialSecurityNumber() into the new Staff
(or validate presence) so id and socialSecurityNumber are preserved for update
flows; update usages of toEntity to pass an existing Staff when performing
updates and keep toEntity only for creates if you choose that split.

In
`@src/test/java/org/example/cyberwatch/form/EmploymentFormControllerTests.java`:
- Around line 28-94: Add slice tests for the newly introduced manager-approval
and pending-form listing endpoints and include 403 checks for role enforcement:
in EmploymentFormControllerTests, add a happy-path test that mocks
employmentFormService.getPendingForms() and asserts GET
"/api/forms/employment/pending" returns 200 with expected JSON (use mockMvc and
objectMapper), add a happy-path test that mocks
employmentFormService.approveForm(id, ...) and asserts PUT/POST to the
controller's manager-approval endpoint returns 200/204 (include csrf() for
non-GET), and for each endpoint add a corresponding test using `@WithMockUser`
with a non-authorized role to assert status().isForbidden(); when creating these
tests reference the mocked service methods employmentFormService.getPendingForms
and employmentFormService.approveForm and reuse mockMvc, objectMapper, and the
existing `@MockitoBean` employmentFormService to stub returns.
🪄 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: ac0d7bf5-3ba3-4a70-b14e-00d5093a84e6

📥 Commits

Reviewing files that changed from the base of the PR and between a67b8d1 and aa958f6.

📒 Files selected for processing (16)
  • src/main/java/org/example/cyberwatch/features/form/controller/EmploymentFormController.java
  • src/main/java/org/example/cyberwatch/features/form/controller/FormController.java
  • src/main/java/org/example/cyberwatch/features/form/model/CreateEmploymentDTO.java
  • src/main/java/org/example/cyberwatch/features/form/model/EmploymentForm.java
  • src/main/java/org/example/cyberwatch/features/form/model/EmploymentFormDTO.java
  • src/main/java/org/example/cyberwatch/features/form/model/EmploymentMapper.java
  • src/main/java/org/example/cyberwatch/features/form/model/ReportForm.java
  • src/main/java/org/example/cyberwatch/features/form/model/UpdateEmploymentDTO.java
  • src/main/java/org/example/cyberwatch/features/form/repository/EmploymentFormRepository.java
  • src/main/java/org/example/cyberwatch/features/form/service/EmploymentFormService.java
  • src/main/java/org/example/cyberwatch/features/form/service/ReportFormService.java
  • src/main/java/org/example/cyberwatch/features/staff/model/Staff.java
  • src/main/java/org/example/cyberwatch/features/staff/model/StaffMapper.java
  • src/main/java/org/example/cyberwatch/features/staff/repository/StaffRepository.java
  • src/main/java/org/example/cyberwatch/shared/model/enums/ApprovalStatus.java
  • src/test/java/org/example/cyberwatch/form/EmploymentFormControllerTests.java
💤 Files with no reviewable changes (2)
  • src/main/java/org/example/cyberwatch/features/form/model/ReportForm.java
  • src/main/java/org/example/cyberwatch/features/form/controller/FormController.java

Comment thread src/main/java/org/example/cyberwatch/features/form/model/CreateEmploymentDTO.java Outdated
Comment thread src/main/java/org/example/cyberwatch/features/form/model/EmploymentMapper.java Outdated
Comment thread src/main/java/org/example/cyberwatch/features/form/model/UpdateEmploymentDTO.java Outdated
public interface StaffRepository extends JpaRepository<Staff, Long> {
boolean existsBySocialSecurityNumber(String socialSecurityNumber);

Optional<Staff> findByEmail(String email);

@coderabbitai coderabbitai Bot Apr 6, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd 'Staff.java$' src --exec sed -n '40,46p' {}

echo
rg -n -C2 '\bfindByEmail\s*\(' src

echo
rg -ni --iglob '*.sql' --iglob '*.xml' --iglob '*.yml' --iglob '*.yaml' 'staff.*email|email.*staff|unique.*email|email.*unique'

Repository: ithsjava25/project-backend-super-cool-team

Length of output: 1640


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check Staff entity for `@Table` and `@UniqueConstraint` annotations
echo "=== Checking Staff.java for JPA annotations ==="
fd 'Staff.java$' src --exec cat {} | sed -n '1,50p'

echo
echo "=== Searching for migration files ==="
fd -e 'sql' -e 'xml' -e 'yml' -e 'yaml' . | grep -iE '(migration|changelog|flyway|liquibase|db|schema)' | head -20

echo
echo "=== Searching for `@Table` or `@UniqueConstraint` in Staff.java ==="
rg -n '@Table|@UniqueConstraint' src/main/java/org/example/cyberwatch/features/staff/model/Staff.java

Repository: ithsjava25/project-backend-super-cool-team

Length of output: 2286


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get the full Staff.java file
echo "=== Full Staff.java content ==="
wc -l src/main/java/org/example/cyberwatch/features/staff/model/Staff.java
cat src/main/java/org/example/cyberwatch/features/staff/model/Staff.java

echo
echo "=== All migration files ==="
find src/main/resources/db -type f -name '*.sql' -o -name '*.xml'

echo
echo "=== Check migration V1 for Staff/email ==="
cat src/main/resources/db/migration/V1__add_ticket_assignee.sql | head -30

Repository: ithsjava25/project-backend-super-cool-team

Length of output: 3165


Add unique constraint to email column before relying on single-result lookups.

EmploymentFormService.createForm(...) resolves the logged-in HR through findByEmail(...) with .orElseThrow(), which expects a single result. However, the Staff entity does not enforce uniqueness on the email field—unlike socialSecurityNumber which explicitly has @Column(unique = true). The database migration files do not define a unique constraint either. Without this constraint, the lookup can fail at runtime if duplicates exist.

Add @Column(name = "email", unique = true, nullable = false) to the email field or create a migration to enforce this at the database level.

🤖 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/staff/repository/StaffRepository.java`
at line 11, The email field is not guaranteed unique yet code relies on
StaffRepository.findByEmail(...) returning a single result (used by
EmploymentFormService.createForm), so add a uniqueness constraint: update the
Staff entity's email property to include `@Column`(name = "email", unique = true,
nullable = false) (or create a DB migration to add a UNIQUE constraint on the
email column) and run/verify migrations so findByEmail(...) cannot return
duplicates at runtime.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@Column(name = "email", unique = true, nullable = false)
@Email(message = "Email should be valid")
@NotBlank(message = "Email cannot be blank")
private String email;

is implemented

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

…ss, and enhance dependence checks for social security numbers in EmploymentForm services and repository

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

♻️ Duplicate comments (1)
src/main/java/org/example/cyberwatch/features/form/model/EmploymentForm.java (1)

68-73: ⚠️ Potential issue | 🟠 Major

Ensure FK migration is committed for the Staff association switch.

Line 68 and Line 72 still use legacy FK columns (hr_id, approver_management_id) while the entity target is now Staff. Without an explicit backfill/constraint migration for existing data, those values can map incorrectly (or fail) against staff.employee_id.

#!/bin/bash
set -euo pipefail

echo "=== Entity mappings involved in createdBy/approvedBy ==="
fd -i 'EmploymentForm.java|Staff.java|HR.java|Management.java' src/main/java --exec sed -n '1,260p' {}

echo
echo "=== Migrations touching employment_form and related FK columns ==="
fd -e sql src/main/resources/db/migration --exec rg -n -C2 'employment_form|hr_id|approver_management_id|employee_id|staff_id' {}

Expected verification outcome:

  • Only the intended inverse mappings remain active for createdBy/approvedBy.
  • A migration exists that safely backfills/realigns old FK values before enforcing new references.
🤖 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/form/model/EmploymentForm.java`
around lines 68 - 73, EmploymentForm still maps createdBy and approvedBy to
legacy FK columns hr_id and approver_management_id while target entity is Staff;
add a DB migration that backfills and realigns existing
hr_id/approver_management_id values to the new staff primary key (employee_id or
staff_id as used by Staff) and then add/rename FK columns and constraints
accordingly, then update the entity mapping if you plan to rename columns
(EmploymentForm.createdBy / EmploymentForm.approvedBy) so the `@JoinColumn`
matches the final column name and the FK constraint references Staff's PK;
ensure the migration runs before applying a schema-level FK enforcement and
include a verification step querying employment_form rows to confirm all legacy
ids were converted and constraints can be applied without failures.
🧹 Nitpick comments (1)
src/main/java/org/example/cyberwatch/features/form/model/EmploymentMapper.java (1)

58-61: Make toDTOList null-safe for mapper consistency.

toDTOList will throw if entities is null, while other mapper entrypoints are null-guarded.

Small defensive refactor
     public List<EmploymentFormDTO> toDTOList(List<EmploymentForm> entities) {
+        if (entities == null || entities.isEmpty()) return List.of();
         return entities.stream()
                 .map(this::toDTO)
                 .toList();
     }
🤖 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/form/model/EmploymentMapper.java`
around lines 58 - 61, The toDTOList method in EmploymentMapper should be
null-safe like other mappers: check if the incoming List<EmploymentForm>
entities is null and return an empty list (e.g., Collections.emptyList() or
List.of()) instead of streaming a null reference; update
EmploymentMapper.toDTOList to perform the null check and then map each element
with this::toDTO when non-null.
🤖 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/form/model/EmploymentMapper.java`:
- Around line 39-40: The mapper currently trusts client-provided status in
EmploymentMapper (entity.setStatus(dto.getStatus())); change the mapping so new
entities always get the server-side default status PENDING (e.g.,
entity.setStatus(Status.PENDING) or the appropriate enum constant) instead of
using dto.getStatus(), leaving EmploymentFormService.createForm() unchanged;
update EmploymentMapper where entity.setStatus(dto.getStatus()) is called to
unconditionally assign the PENDING value.

---

Duplicate comments:
In
`@src/main/java/org/example/cyberwatch/features/form/model/EmploymentForm.java`:
- Around line 68-73: EmploymentForm still maps createdBy and approvedBy to
legacy FK columns hr_id and approver_management_id while target entity is Staff;
add a DB migration that backfills and realigns existing
hr_id/approver_management_id values to the new staff primary key (employee_id or
staff_id as used by Staff) and then add/rename FK columns and constraints
accordingly, then update the entity mapping if you plan to rename columns
(EmploymentForm.createdBy / EmploymentForm.approvedBy) so the `@JoinColumn`
matches the final column name and the FK constraint references Staff's PK;
ensure the migration runs before applying a schema-level FK enforcement and
include a verification step querying employment_form rows to confirm all legacy
ids were converted and constraints can be applied without failures.

---

Nitpick comments:
In
`@src/main/java/org/example/cyberwatch/features/form/model/EmploymentMapper.java`:
- Around line 58-61: The toDTOList method in EmploymentMapper should be
null-safe like other mappers: check if the incoming List<EmploymentForm>
entities is null and return an empty list (e.g., Collections.emptyList() or
List.of()) instead of streaming a null reference; update
EmploymentMapper.toDTOList to perform the null check and then map each element
with this::toDTO when non-null.
🪄 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: b3cb244e-231e-4740-b648-cab9027284f7

📥 Commits

Reviewing files that changed from the base of the PR and between aa958f6 and 9e65e11.

📒 Files selected for processing (6)
  • src/main/java/org/example/cyberwatch/features/form/controller/EmploymentFormController.java
  • src/main/java/org/example/cyberwatch/features/form/model/CreateEmploymentDTO.java
  • src/main/java/org/example/cyberwatch/features/form/model/EmploymentForm.java
  • src/main/java/org/example/cyberwatch/features/form/model/EmploymentMapper.java
  • src/main/java/org/example/cyberwatch/features/form/repository/EmploymentFormRepository.java
  • src/main/java/org/example/cyberwatch/features/form/service/EmploymentFormService.java
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/org/example/cyberwatch/features/form/model/CreateEmploymentDTO.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/org/example/cyberwatch/features/form/repository/EmploymentFormRepository.java
  • src/main/java/org/example/cyberwatch/features/form/controller/EmploymentFormController.java
  • src/main/java/org/example/cyberwatch/features/form/service/EmploymentFormService.java

Comment thread src/main/java/org/example/cyberwatch/features/form/model/EmploymentMapper.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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/main/java/org/example/cyberwatch/features/staff/exception/StaffNotFoundException.java (1)

4-4: Remove this unhelpful comment.

This comment is technically inaccurate (this is a constructor, not a method) and doesn't add value—it describes what was done rather than why. Such comments become noise for future maintainers. Consider removing it.

Suggested fix
 public class StaffNotFoundException extends RuntimeException {
-    //Added extra method
     public StaffNotFoundException(Long id) {
         super("Staff with id " + id + " not found");
     }
🤖 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/staff/exception/StaffNotFoundException.java`
at line 4, Remove the unhelpful inline comment "//Added extra method" from the
StaffNotFoundException class; specifically delete that comment near the
StaffNotFoundException constructor and, if desired, replace it with a concise
Javadoc on the constructor explaining its purpose (e.g., that it creates the
exception with a message) rather than a non-informative note.
🤖 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/ticket/controller/TicketController.java`:
- Around line 64-67: Change the controller method signature from
ResponseEntity<?> to ResponseEntity<TicketResponseDTO> in assignTicketToStaff
and map the domain Ticket returned by
ticketService.assignTicketToStaff(ticketId, dto.getStaffId()) to a
TicketResponseDTO before returning; locate the controller method
assignTicketToStaff and replace the raw return with ResponseEntity.ok(<mapped
TicketResponseDTO>) using the existing mapping utility (e.g.,
TicketMapper.toResponse(...) or TicketResponseDTO.from(...)), or construct the
DTO fields explicitly if no mapper exists, ensuring you do not expose internal
entity fields.

---

Nitpick comments:
In
`@src/main/java/org/example/cyberwatch/features/staff/exception/StaffNotFoundException.java`:
- Line 4: Remove the unhelpful inline comment "//Added extra method" from the
StaffNotFoundException class; specifically delete that comment near the
StaffNotFoundException constructor and, if desired, replace it with a concise
Javadoc on the constructor explaining its purpose (e.g., that it creates the
exception with a message) rather than a non-informative note.
🪄 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: ae2323a6-be24-4884-84ab-6b6e14a1f60c

📥 Commits

Reviewing files that changed from the base of the PR and between 19ca615 and 28a07bf.

📒 Files selected for processing (3)
  • src/main/java/org/example/cyberwatch/features/staff/exception/StaffNotFoundException.java
  • src/main/java/org/example/cyberwatch/features/ticket/controller/TicketController.java
  • src/main/resources/db/migration/V1__add_ticket_assignee.sql
💤 Files with no reviewable changes (1)
  • src/main/resources/db/migration/V1__add_ticket_assignee.sql

Comment on lines +64 to 67
public ResponseEntity<?> assignTicketToStaff(
@PathVariable Long ticketId,
@Valid @RequestBody AssignTicketDTO dto) {
return ResponseEntity.ok(ticketService.assignTicketToStaff(ticketId, dto.getStaffId()));

@coderabbitai coderabbitai Bot Apr 7, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the controller/service type mismatch for assignTicketToStaff.

set -euo pipefail

echo "1) Controller method signature:"
rg -nP '\bResponseEntity<\?>\s+assignTicketToStaff\s*\(' -C2

echo
echo "2) Service method return type:"
rg -nP '\bpublic\s+Ticket\s+assignTicketToStaff\s*\(' -C2

echo
echo "3) All assignTicketToStaff declarations/usages for manual contract check:"
rg -nP '\bassignTicketToStaff\s*\(' -C2

Repository: ithsjava25/project-backend-super-cool-team

Length of output: 3741


🏁 Script executed:

#!/bin/bash

echo "=== Other endpoints in TicketController to verify return type pattern ==="
rg -nP 'public ResponseEntity' src/main/java/org/example/cyberwatch/features/ticket/controller/TicketController.java -A2

echo
echo "=== TicketResponseDTO definition ==="
fd -e java -x grep -l 'class TicketResponseDTO' {} \;

echo
echo "=== Check if assignTicketToStaff has performedById or similar actor parameter ==="
rg -nP 'assignTicketToStaff' src/main/java/org/example/cyberwatch/features/ticket/controller/TicketController.java -B5 -A5

echo
echo "=== Full assignTicketToStaff endpoint code ==="
sed -n '62,68p' src/main/java/org/example/cyberwatch/features/ticket/controller/TicketController.java

Repository: ithsjava25/project-backend-super-cool-team

Length of output: 2818


Keep a typed response contract; don't widen to ResponseEntity<?>.

Line 64 weakens the endpoint contract and masks that assignTicketToStaff currently returns a domain Ticket instead of TicketResponseDTO. This makes the API inconsistent with the rest of TicketController (all other endpoints return ResponseEntity<TicketResponseDTO>) and can leak internal entity fields.

Change the return type to ResponseEntity<TicketResponseDTO> and map the service output to the DTO before returning.

🤖 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/controller/TicketController.java`
around lines 64 - 67, Change the controller method signature from
ResponseEntity<?> to ResponseEntity<TicketResponseDTO> in assignTicketToStaff
and map the domain Ticket returned by
ticketService.assignTicketToStaff(ticketId, dto.getStaffId()) to a
TicketResponseDTO before returning; locate the controller method
assignTicketToStaff and replace the raw return with ResponseEntity.ok(<mapped
TicketResponseDTO>) using the existing mapping utility (e.g.,
TicketMapper.toResponse(...) or TicketResponseDTO.from(...)), or construct the
DTO fields explicitly if no mapper exists, ensuring you do not expose internal
entity fields.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Create an issue for this

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh issue create \
  --repo ithsjava25/project-backend-super-cool-team \
  --title "TicketController: Restore typed ResponseEntity<TicketResponseDTO> in assignTicketToStaff" \
  --assignee codebyNorthsteep \
  --body "## Summary

The \`assignTicketToStaff\` endpoint in \`TicketController\` was changed from \`ResponseEntity<TicketResponseDTO>\` to \`ResponseEntity<?>\`, weakening the API contract and making it inconsistent with all other endpoints in the same controller.

Additionally, the underlying service method \`ticketService.assignTicketToStaff\` currently returns a domain \`Ticket\` entity rather than a \`TicketResponseDTO\`, which can inadvertently leak internal entity fields to API consumers.

## Steps to resolve

1. Change the controller method return type back to \`ResponseEntity<TicketResponseDTO>\`.
2. Map the domain \`Ticket\` returned by \`ticketService.assignTicketToStaff(ticketId, dto.getStaffId())\` to a \`TicketResponseDTO\` before returning (using an existing mapper/utility or explicit construction).
3. Ensure no internal entity fields are exposed in the response.

## References

- PR: https://github.com/ithsjava25/project-backend-super-cool-team/pull/48
- Review comment: https://github.com/ithsjava25/project-backend-super-cool-team/pull/48#discussion_r3044021864

Requested by `@codebyNorthsteep`"

Length of output: 71


@codebyNorthsteep, the issue has been created and assigned to you: #49 — TicketController: Restore typed ResponseEntity<TicketResponseDTO> in assignTicketToStaff.

It captures:

  • Restoring the typed ResponseEntity<TicketResponseDTO> return type.
  • Mapping the domain Ticket from the service to a TicketResponseDTO before returning, to avoid leaking internal entity fields.

🧠 Learnings used
Learnt from: gitnes94
Repo: ithsjava25/project-backend-super-cool-team PR: 31
File: src/main/java/org/example/cyberwatch/features/ticket/controller/TicketController.java:47-49
Timestamp: 2026-04-07T06:09:03.792Z
Learning: In `src/main/java/org/example/cyberwatch/features/ticket/controller/TicketController.java`, the `performedById` and `uploadedById` parameters in endpoints (`advanceStatus`, `setStatus`, `reopen`, `assignTicketToStaff`, `uploadFile`) are currently accepted as caller-controlled `RequestParam` values, making the audit trail forgeable. This is a known security issue that will be fixed in a future PR once authentication is implemented. The plan is to resolve the actor server-side via `AuthenticationPrincipal StaffUserDetails principal` and remove `performedById`/`uploadedById` from the public request signatures entirely.

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