Skip to content

Throw a UserAdminException when deleting a referenced group#98

Merged
flomillot merged 4 commits into
mainfrom
refactor/group-still-referenced-exception
May 13, 2026
Merged

Throw a UserAdminException when deleting a referenced group#98
flomillot merged 4 commits into
mainfrom
refactor/group-still-referenced-exception

Conversation

@flomillot
Copy link
Copy Markdown
Contributor

@flomillot flomillot commented May 13, 2026

Summary

Group deletion (main change)

Replace the generic DataIntegrityViolationException (previously swallowed in UserGroupController) with a typed UserAdminException mapped to HTTP 422 through the global UserAdminExceptionHandler, so the API exposes a stable business error code instead of a Spring DAO exception.

  • New USER_ADMIN_GROUP_STILL_REFERENCED business error code and UserAdminException.groupStillReferenced(name) factory.
  • UserGroupService.deleteGroups now throws the typed exception (message: `Group `<name>` is still referenced by users`) instead of DataIntegrityViolationException.
  • try/catch removed from UserGroupController.deleteGroups — the global advice produces the 422.

Profile deletion (dead-code cleanup)

The try { ... } catch (DataIntegrityViolationException e) block in UserProfileController.deleteProfiles was unreachable: nothing in UserProfileService.deleteProfiles raises that exception (the previous foreign-key behaviour wasn't actually being thrown there). Removed:

  • The try/catch and the now-unused DataIntegrityViolationException import.
  • The @ApiResponse(responseCode = "422", ...) annotation that documented a status the endpoint can no longer produce.

No behavioural change for profile deletion.

Replace the generic DataIntegrityViolationException raised when trying
to delete a group that still has users with a dedicated business
exception (USER_ADMIN_GROUP_STILL_REFERENCED) so the error is mapped to
HTTP 422 through the global exception handler and exposes a stable
business error code to clients.

Signed-off-by: Florent MILLOT <75525996+flomillot@users.noreply.github.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

The PR refactors group deletion error handling by replacing a low-level database exception (DataIntegrityViolationException) with a domain-level custom exception (UserAdminException). This centralizes error mapping in the exception handler framework instead of spreading it across the controller, resulting in cleaner separation of concerns.

Changes

Error handling for referenced groups

Layer / File(s) Summary
Error code and exception factory
src/main/java/org/gridsuite/useradmin/server/error/UserAdminBusinessErrorCode.java, src/main/java/org/gridsuite/useradmin/server/error/UserAdminException.java
New enum constant USER_ADMIN_GROUP_STILL_REFERENCED and static factory method groupStillReferenced(String name) establish a domain exception for groups still containing users.
Exception to HTTP status mapping
src/main/java/org/gridsuite/useradmin/server/error/UserAdminExceptionHandler.java
Exception handler maps the new business error code to HTTP 422 UNPROCESSABLE_ENTITY.
Service layer exception change
src/main/java/org/gridsuite/useradmin/server/service/UserGroupService.java
deleteGroups throws UserAdminException.groupStillReferenced(name) instead of DataIntegrityViolationException when group contains users, and removes the related import.
Controller endpoint simplification
src/main/java/org/gridsuite/useradmin/server/controller/UserGroupController.java
deleteGroups endpoint removes try/catch for database exceptions; it now only checks deletion count and returns 204 or 404, with 422 responses delegated to the exception handler framework.
🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title 'Throw a UserAdminException when deleting a referenced group' clearly and specifically summarizes the main change: replacing DataIntegrityViolationException with a typed UserAdminException when a group deletion fails due to existing references.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing the new business error code, exception handling flow, and the removal of dead code in profile deletion.

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


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
Copy Markdown

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/org/gridsuite/useradmin/server/service/UserGroupService.java (1)

103-115: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add defensive exception handling for concurrent user references in delete flow.

Lines 107–112 perform a pre-check for group users, but this check is not atomic with the actual delete at line 114. Under concurrent updates, users can be assigned to a group between the check and the delete, causing deleteAllByNameIn() to fail with DataIntegrityViolationException. Unlike UserProfileController (lines 87–89), UserGroupController has no handler for this exception, so it will surface as a 500 error instead of the expected 422.

Wrap the delete call in a try-catch to convert late-arriving DataIntegrityViolationException back to UserAdminException.groupStillReferenced():

Suggested fix
+import org.springframework.dao.DataIntegrityViolationException;
@@
     `@Transactional`
     public long deleteGroups(List<String> names) {
         adminRightService.assertIsAdmin();
@@
-        return userGroupRepository.deleteAllByNameIn(names);
+        try {
+            return userGroupRepository.deleteAllByNameIn(names);
+        } catch (DataIntegrityViolationException e) {
+            // defensive fallback for concurrent references created after pre-check
+            String referencedName = names.stream()
+                .map(userGroupRepository::findByName)
+                .flatMap(Optional::stream)
+                .filter(group -> !CollectionUtils.isEmpty(group.getUsers()))
+                .map(GroupInfosEntity::getName)
+                .findFirst()
+                .orElse("unknown");
+            throw UserAdminException.groupStillReferenced(referencedName);
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/org/gridsuite/useradmin/server/service/UserGroupService.java`
around lines 103 - 115, In UserGroupService.deleteGroups, make the delete
atomic-safe by wrapping the call to userGroupRepository.deleteAllByNameIn(names)
in a try-catch that catches DataIntegrityViolationException and rethrows
UserAdminException.groupStillReferenced(...) so late-arriving user assignments
convert to the expected 422; keep the existing pre-checks that use
userGroupRepository.findByName(...) and adminRightService.assertIsAdmin(), and
ensure DataIntegrityViolationException is imported so the catch maps DB
integrity failures to UserAdminException.groupStillReferenced.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/main/java/org/gridsuite/useradmin/server/service/UserGroupService.java`:
- Around line 103-115: In UserGroupService.deleteGroups, make the delete
atomic-safe by wrapping the call to userGroupRepository.deleteAllByNameIn(names)
in a try-catch that catches DataIntegrityViolationException and rethrows
UserAdminException.groupStillReferenced(...) so late-arriving user assignments
convert to the expected 422; keep the existing pre-checks that use
userGroupRepository.findByName(...) and adminRightService.assertIsAdmin(), and
ensure DataIntegrityViolationException is imported so the catch maps DB
integrity failures to UserAdminException.groupStillReferenced.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 909e37c1-8569-4af4-a144-19506438ad0c

📥 Commits

Reviewing files that changed from the base of the PR and between e8d3585 and d8cc439.

📒 Files selected for processing (5)
  • src/main/java/org/gridsuite/useradmin/server/controller/UserGroupController.java
  • src/main/java/org/gridsuite/useradmin/server/error/UserAdminBusinessErrorCode.java
  • src/main/java/org/gridsuite/useradmin/server/error/UserAdminException.java
  • src/main/java/org/gridsuite/useradmin/server/error/UserAdminExceptionHandler.java
  • src/main/java/org/gridsuite/useradmin/server/service/UserGroupService.java

Replace the generic DataIntegrityViolationException raised when trying
to delete a profile that is still referenced by users with a dedicated
business exception (USER_ADMIN_PROFILE_STILL_REFERENCED) so the error
is mapped to HTTP 422 through the global exception handler and exposes
a stable business error code to clients. The check is now explicit
through a UserInfosRepository query rather than relying on the database
foreign-key constraint failure.

Signed-off-by: Florent MILLOT <75525996+flomillot@users.noreply.github.com>
@flomillot flomillot changed the title Throw a UserAdminException when deleting a referenced group Throw a UserAdminException when deleting a referenced group or profile May 13, 2026
flomillot added 2 commits May 13, 2026 12:37
Drop the explicit JPQL @query: with no top-level `profileName` field on
UserInfosEntity, Spring Data resolves `ProfileName` as the nested
`profile.name` traversal automatically, and the method name no longer
needs the underscore form rejected by checkstyle.

Signed-off-by: Florent MILLOT <75525996+flomillot@users.noreply.github.com>
Drop the explicit existence check in UserProfileService.deleteProfiles
together with the USER_ADMIN_PROFILE_STILL_REFERENCED business error
code, the matching factory and handler mapping, and the
UserInfosRepository.existsByProfileName query. The original
DataIntegrityViolationException catch in UserProfileController was
unreachable, so the controller try/catch (and its now-misleading 422
ApiResponse) are kept removed.

Signed-off-by: Florent MILLOT <75525996+flomillot@users.noreply.github.com>
@flomillot flomillot changed the title Throw a UserAdminException when deleting a referenced group or profile Throw a UserAdminException when deleting a referenced group May 13, 2026
@sonarqubecloud
Copy link
Copy Markdown

@flomillot flomillot merged commit 7e59639 into main May 13, 2026
5 checks passed
@flomillot flomillot deleted the refactor/group-still-referenced-exception branch May 13, 2026 16:05
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