diff --git a/pom.xml b/pom.xml
index 4f00361d..9c7b98f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -195,6 +195,7 @@
com.powsybl
powsybl-ws-commons
+ 1.29.0-SNAPSHOT
io.projectreactor
diff --git a/src/main/java/org/gridsuite/directory/server/DirectoryBusinessErrorCode.java b/src/main/java/org/gridsuite/directory/server/DirectoryBusinessErrorCode.java
new file mode 100644
index 00000000..fbe4e575
--- /dev/null
+++ b/src/main/java/org/gridsuite/directory/server/DirectoryBusinessErrorCode.java
@@ -0,0 +1,39 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.gridsuite.directory.server;
+
+import com.powsybl.ws.commons.error.BusinessErrorCode;
+
+/**
+ * @author Mohamed Ben-rejeb {@literal }
+ *
+ * Business error codes emitted by the directory service.
+ */
+public enum DirectoryBusinessErrorCode implements BusinessErrorCode {
+ DIRECTORY_PERMISSION_DENIED("directory.permissionDenied"),
+ DIRECTORY_ELEMENT_NAME_BLANK("directory.elementNameBlank"),
+ DIRECTORY_ROOT_ALREADY_EXISTS("directory.rootAlreadyExists"),
+ DIRECTORY_NOT_DIRECTORY("directory.notDirectory"),
+ DIRECTORY_ELEMENT_NAME_CONFLICT("directory.elementNameConflict"),
+ DIRECTORY_MOVE_IN_DESCENDANT_NOT_ALLOWED("directory.moveInDescendantNotAllowed"),
+ DIRECTORY_MOVE_SELECTION_EMPTY("directory.moveSelectionEmpty"),
+ DIRECTORY_ELEMENT_NOT_FOUND("directory.elementNotFound"),
+ DIRECTORY_DIRECTORY_NOT_FOUND_IN_PATH("directory.directoryNotFoundInPath"),
+ DIRECTORY_NOTIFICATION_UNKNOWN("directory.notificationUnknown"),
+ DIRECTORY_CANNOT_DELETE_ELEMENT("directory.cannotDeleteElement"),
+ DIRECTORY_REMOTE_ERROR("directory.remoteError");
+
+ private final String code;
+
+ DirectoryBusinessErrorCode(String code) {
+ this.code = code;
+ }
+
+ public String value() {
+ return code;
+ }
+}
diff --git a/src/main/java/org/gridsuite/directory/server/DirectoryException.java b/src/main/java/org/gridsuite/directory/server/DirectoryException.java
index a5f585b1..43c431aa 100644
--- a/src/main/java/org/gridsuite/directory/server/DirectoryException.java
+++ b/src/main/java/org/gridsuite/directory/server/DirectoryException.java
@@ -6,51 +6,63 @@
*/
package org.gridsuite.directory.server;
+import com.powsybl.ws.commons.error.AbstractPowsyblWsException;
+import com.powsybl.ws.commons.error.BusinessErrorCode;
+import com.powsybl.ws.commons.error.PowsyblWsProblemDetail;
import lombok.NonNull;
import java.util.Objects;
+import java.util.Optional;
import java.util.UUID;
/**
* @author Abdelsalem Hedhili
+ * @author Mohamed Ben-rejeb {@literal }
*/
-public class DirectoryException extends RuntimeException {
+public class DirectoryException extends AbstractPowsyblWsException {
- private final Type type;
+ private final DirectoryBusinessErrorCode errorCode;
+ private final PowsyblWsProblemDetail remoteError;
- public DirectoryException(Type type) {
- super(Objects.requireNonNull(type.name()));
- this.type = type;
+ public DirectoryException(DirectoryBusinessErrorCode errorCode, String message) {
+ this(errorCode, message, null);
}
- public DirectoryException(Type type, String message) {
- super(message);
- this.type = type;
+ public DirectoryException(DirectoryBusinessErrorCode errorCode, String message, PowsyblWsProblemDetail remoteError) {
+ super(Objects.requireNonNull(message, "message must not be null"));
+ this.errorCode = Objects.requireNonNull(errorCode, "errorCode must not be null");
+ this.remoteError = remoteError;
}
public static DirectoryException createNotificationUnknown(@NonNull String action) {
- return new DirectoryException(Type.UNKNOWN_NOTIFICATION, String.format("The notification type '%s' is unknown", action));
+ return new DirectoryException(DirectoryBusinessErrorCode.DIRECTORY_NOTIFICATION_UNKNOWN,
+ String.format("The notification type '%s' is unknown", action));
}
public static DirectoryException createElementNotFound(@NonNull String type, @NonNull UUID uuid) {
- return new DirectoryException(Type.NOT_FOUND, String.format("%s '%s' not found !", type, uuid));
+ return new DirectoryException(DirectoryBusinessErrorCode.DIRECTORY_ELEMENT_NOT_FOUND,
+ String.format("%s '%s' not found !", type, uuid));
}
public static DirectoryException createElementNameAlreadyExists(@NonNull String name) {
- return new DirectoryException(Type.NAME_ALREADY_EXISTS, String.format("Element with the same name '%s' already exists in the directory !", name));
+ return new DirectoryException(DirectoryBusinessErrorCode.DIRECTORY_ELEMENT_NAME_CONFLICT,
+ String.format("Element with the same name '%s' already exists in the directory !", name));
}
- Type getType() {
- return type;
+ public static DirectoryException of(DirectoryBusinessErrorCode errorCode, String message, Object... args) {
+ return new DirectoryException(errorCode, args.length == 0 ? message : String.format(message, args));
}
- public enum Type {
- NOT_ALLOWED,
- NOT_FOUND,
- NOT_DIRECTORY,
- IS_DIRECTORY,
- UNKNOWN_NOTIFICATION,
- NAME_ALREADY_EXISTS,
- MOVE_IN_DESCENDANT_NOT_ALLOWED,
+ public Optional getErrorCode() {
+ return Optional.of(errorCode);
+ }
+
+ @Override
+ public Optional getBusinessErrorCode() {
+ return Optional.ofNullable(errorCode);
+ }
+
+ public Optional getRemoteError() {
+ return Optional.ofNullable(remoteError);
}
}
diff --git a/src/main/java/org/gridsuite/directory/server/DirectoryService.java b/src/main/java/org/gridsuite/directory/server/DirectoryService.java
index ba21acea..61ff1f01 100644
--- a/src/main/java/org/gridsuite/directory/server/DirectoryService.java
+++ b/src/main/java/org/gridsuite/directory/server/DirectoryService.java
@@ -24,8 +24,8 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static org.gridsuite.directory.server.DirectoryBusinessErrorCode.*;
import static java.lang.Boolean.TRUE;
-import static org.gridsuite.directory.server.DirectoryException.Type.*;
import static org.gridsuite.directory.server.dto.ElementAttributes.toElementAttributes;
import static org.gridsuite.directory.server.dto.PermissionType.*;
@@ -94,7 +94,7 @@ public ElementAttributes createElement(ElementAttributes elementAttributes, UUID
private ElementAttributes createElementWithNotif(ElementAttributes elementAttributes, UUID parentDirectoryUuid, String userId, boolean generateNewName) {
if (elementAttributes.getElementName().isBlank()) {
- throw new DirectoryException(NOT_ALLOWED);
+ throw DirectoryException.of(DIRECTORY_ELEMENT_NAME_BLANK, "Element name must not be blank");
}
assertDirectoryExist(parentDirectoryUuid);
DirectoryElementEntity elementEntity = insertElement(elementAttributes, parentDirectoryUuid, userId, generateNewName);
@@ -111,16 +111,17 @@ private ElementAttributes createElementWithNotif(ElementAttributes elementAttrib
}
public ElementAttributes duplicateElement(UUID elementId, UUID newElementId, UUID targetDirectoryId, String userId) {
- DirectoryElementEntity directoryElementEntity = directoryElementRepository.findById(elementId).orElseThrow(() -> new DirectoryException(NOT_FOUND));
+ DirectoryElementEntity directoryElementEntity = directoryElementRepository.findById(elementId)
+ .orElseThrow(() -> DirectoryException.createElementNotFound(ELEMENT, elementId));
String elementType = directoryElementEntity.getType();
UUID parentDirectoryUuid = targetDirectoryId != null ? targetDirectoryId : directoryElementEntity.getParentId();
ElementAttributes elementAttributes = ElementAttributes.builder()
- .type(elementType)
- .elementUuid(newElementId)
- .owner(userId)
- .description(directoryElementEntity.getDescription())
- .elementName(directoryElementEntity.getName())
- .build();
+ .type(elementType)
+ .elementUuid(newElementId)
+ .owner(userId)
+ .description(directoryElementEntity.getDescription())
+ .elementName(directoryElementEntity.getName())
+ .build();
assertDirectoryExist(parentDirectoryUuid);
DirectoryElementEntity elementEntity = insertElement(elementAttributes, parentDirectoryUuid, userId, true);
@@ -132,14 +133,14 @@ public ElementAttributes duplicateElement(UUID elementId, UUID newElementId, UUI
}
private void assertRootDirectoryNotExist(String rootName) {
- if (TRUE.equals(repositoryService.isRootDirectoryExist(rootName))) {
- throw new DirectoryException(NOT_ALLOWED);
+ if (repositoryService.isRootDirectoryExist(rootName)) {
+ throw DirectoryException.of(DIRECTORY_ROOT_ALREADY_EXISTS, "Root directory '%s' already exists", rootName);
}
}
private void assertDirectoryExist(UUID dirUuid) {
if (!getElement(dirUuid).getType().equals(DIRECTORY)) {
- throw new DirectoryException(NOT_DIRECTORY);
+ throw DirectoryException.of(DIRECTORY_NOT_DIRECTORY, "Element '%s' is not a directory", dirUuid);
}
}
@@ -152,14 +153,14 @@ private DirectoryElementEntity insertElement(ElementAttributes elementAttributes
Instant now = Instant.now().truncatedTo(ChronoUnit.MICROS);
DirectoryElementEntity elementEntity = new DirectoryElementEntity(elementAttributes.getElementUuid() == null ? UUID.randomUUID() : elementAttributes.getElementUuid(),
- parentDirectoryUuid,
- elementAttributes.getElementName(),
- elementAttributes.getType(),
- elementAttributes.getOwner(),
- elementAttributes.getDescription(),
- now,
- now,
- elementAttributes.getOwner());
+ parentDirectoryUuid,
+ elementAttributes.getElementName(),
+ elementAttributes.getType(),
+ elementAttributes.getOwner(),
+ elementAttributes.getDescription(),
+ now,
+ now,
+ elementAttributes.getOwner());
return tryInsertElement(elementEntity, parentDirectoryUuid, userId, generateNewName);
}
@@ -192,7 +193,7 @@ public ElementAttributes createRootDirectory(RootDirectoryAttributes rootDirecto
private ElementAttributes createRootDirectoryWithNotif(RootDirectoryAttributes rootDirectoryAttributes, String userId) {
if (rootDirectoryAttributes.getElementName().isBlank()) {
- throw new DirectoryException(NOT_ALLOWED);
+ throw DirectoryException.of(DIRECTORY_ELEMENT_NAME_BLANK, "Root directory name must not be blank");
}
assertRootDirectoryNotExist(rootDirectoryAttributes.getElementName());
@@ -202,12 +203,12 @@ private ElementAttributes createRootDirectoryWithNotif(RootDirectoryAttributes r
insertReadGlobalUsersPermission(elementUuid);
// here we know a root directory has no parent
notificationService.emitDirectoryChanged(
- elementUuid,
- elementAttributes.getElementName(),
- userId,
- null,
- true,
- NotificationType.ADD_DIRECTORY
+ elementUuid,
+ elementAttributes.getElementName(),
+ userId,
+ null,
+ true,
+ NotificationType.ADD_DIRECTORY
);
return elementAttributes;
}
@@ -226,20 +227,20 @@ public void createElementInDirectoryPath(String directoryPath, ElementAttributes
//we create the root directory if it doesn't exist
if (parentDirectoryUuid == null) {
parentDirectoryUuid = createRootDirectoryWithNotif(
- new RootDirectoryAttributes(
- s,
- userId,
- null,
- now,
- now,
- userId),
- userId).getElementUuid();
+ new RootDirectoryAttributes(
+ s,
+ userId,
+ null,
+ now,
+ now,
+ userId),
+ userId).getElementUuid();
} else {
//and then we create the rest of the path
parentDirectoryUuid = createElementWithNotif(
- toElementAttributes(UUID.randomUUID(), s, DIRECTORY, userId, null, now, now, userId),
- parentDirectoryUuid,
- userId, false).getElementUuid();
+ toElementAttributes(UUID.randomUUID(), s, DIRECTORY, userId, null, now, now, userId),
+ parentDirectoryUuid,
+ userId, false).getElementUuid();
}
} else {
parentDirectoryUuid = currentDirectoryUuid;
@@ -269,10 +270,10 @@ public List getDirectoryElements(UUID directoryUuid, List descendents = repositoryService.findAllDescendants(directoryUuid).stream().toList();
return descendents
- .stream()
- .filter(e -> types.isEmpty() || types.contains(e.getType()))
- .map(ElementAttributes::toElementAttributes)
- .toList();
+ .stream()
+ .filter(e -> types.isEmpty() || types.contains(e.getType()))
+ .map(ElementAttributes::toElementAttributes)
+ .toList();
} else {
return getAllDirectoryElementsStream(directoryUuid, types).toList();
}
@@ -280,16 +281,16 @@ public List getDirectoryElements(UUID directoryUuid, List getOnlyElementsStream(UUID directoryUuid, List types) {
return getAllDirectoryElementsStream(directoryUuid, types)
- .filter(elementAttributes -> !elementAttributes.getType().equals(DIRECTORY));
+ .filter(elementAttributes -> !elementAttributes.getType().equals(DIRECTORY));
}
private Stream getAllDirectoryElementsStream(UUID directoryUuid, List types) {
List directoryElements = repositoryService.findAllByParentId(directoryUuid);
Map subdirectoriesCountsMap = getSubDirectoriesCountsMap(types, directoryElements);
return directoryElements
- .stream()
- .filter(e -> e.getType().equals(DIRECTORY) || types.isEmpty() || types.contains(e.getType()))
- .map(e -> toElementAttributes(e, subdirectoriesCountsMap.getOrDefault(e.getId(), 0L)));
+ .stream()
+ .filter(e -> e.getType().equals(DIRECTORY) || types.isEmpty() || types.contains(e.getType()))
+ .map(e -> toElementAttributes(e, subdirectoriesCountsMap.getOrDefault(e.getId(), 0L)));
}
public List getRootDirectories(List types, String userId) {
@@ -301,8 +302,8 @@ public List getRootDirectories(List types, String use
}
Map subdirectoriesCountsMap = getSubDirectoriesCountsMap(types, directoryElements);
return directoryElements.stream()
- .map(e -> toElementAttributes(e, subdirectoriesCountsMap.getOrDefault(e.getId(), 0L)))
- .toList();
+ .map(e -> toElementAttributes(e, subdirectoriesCountsMap.getOrDefault(e.getId(), 0L)))
+ .toList();
}
private Map getSubDirectoriesCountsMap(List types, List directoryElements) {
@@ -313,8 +314,10 @@ public void updateElement(UUID elementUuid, ElementAttributes newElementAttribut
DirectoryElementEntity directoryElement = getDirectoryElementEntity(elementUuid);
if (!directoryElement.isAttributesUpdatable(newElementAttributes, userId) ||
!directoryElement.getName().equals(newElementAttributes.getElementName()) &&
- directoryHasElementOfNameAndType(directoryElement.getParentId(), newElementAttributes.getElementName(), directoryElement.getType())) {
- throw new DirectoryException(NOT_ALLOWED);
+ directoryHasElementOfNameAndType(directoryElement.getParentId(), newElementAttributes.getElementName(), directoryElement.getType())) {
+ throw DirectoryException.of(DIRECTORY_PERMISSION_DENIED,
+ "Update forbidden for element '%s': invalid permissions or duplicate name",
+ directoryElement.getId());
}
DirectoryElementEntity elementEntity = repositoryService.saveElement(directoryElement.update(newElementAttributes));
@@ -332,7 +335,7 @@ public void updateElementLastModifiedAttributes(UUID elementUuid, Instant lastMo
@Transactional
public void moveElementsDirectory(List elementsUuids, UUID newDirectoryUuid, String userId) {
if (elementsUuids.isEmpty()) {
- throw new DirectoryException(NOT_ALLOWED);
+ throw DirectoryException.of(DIRECTORY_MOVE_SELECTION_EMPTY, "Cannot move elements: no elements provided");
}
validateNewDirectory(newDirectoryUuid);
@@ -371,7 +374,9 @@ private void moveElementDirectory(DirectoryElementEntity element, UUID newDirect
private void validateElementForMove(DirectoryElementEntity element, UUID newDirectoryUuid, Set descendentsUuids) {
if (newDirectoryUuid == element.getId() || descendentsUuids.contains(newDirectoryUuid)) {
- throw new DirectoryException(MOVE_IN_DESCENDANT_NOT_ALLOWED);
+ throw DirectoryException.of(DIRECTORY_MOVE_IN_DESCENDANT_NOT_ALLOWED,
+ "Cannot move element '%s' into one of its descendants",
+ element.getId());
}
if (directoryHasElementOfNameAndType(newDirectoryUuid, element.getName(), element.getType())) {
@@ -386,10 +391,10 @@ private void updateElementParentDirectory(DirectoryElementEntity element, UUID n
private void validateNewDirectory(UUID newDirectoryUuid) {
DirectoryElementEntity newDirectory = repositoryService.getElementEntity(newDirectoryUuid)
- .orElseThrow(() -> DirectoryException.createElementNotFound(DIRECTORY, newDirectoryUuid));
+ .orElseThrow(() -> DirectoryException.createElementNotFound(DIRECTORY, newDirectoryUuid));
if (!newDirectory.getType().equals(DIRECTORY)) {
- throw new DirectoryException(NOT_DIRECTORY);
+ throw DirectoryException.of(DIRECTORY_NOT_DIRECTORY, "Target '%s' is not a directory", newDirectoryUuid);
}
}
@@ -434,9 +439,10 @@ private void deleteSubElements(UUID elementUuid, String userId) {
/**
* Method to delete multiple elements within a single repository - DIRECTORIES can't be deleted this way
- * @param elementsUuids list of elements uuids to delete
+ *
+ * @param elementsUuids list of elements uuids to delete
* @param parentDirectoryUuid expected parent uuid of each element - element with another parent UUID won't be deleted
- * @param userId user making the deletion
+ * @param userId user making the deletion
*/
public void deleteElements(List elementsUuids, UUID parentDirectoryUuid, String userId) {
// getting elements by "elementUuids", filtered if they don't belong to parentDirectoryUuid, or if they are directories
@@ -470,7 +476,8 @@ public List getPath(UUID elementUuid) {
}
public String getElementName(UUID elementUuid) {
- DirectoryElementEntity element = repositoryService.getElementEntity(elementUuid).orElseThrow(() -> new DirectoryException(NOT_FOUND));
+ DirectoryElementEntity element = repositoryService.getElementEntity(elementUuid)
+ .orElseThrow(() -> DirectoryException.createElementNotFound(ELEMENT, elementUuid));
return element.getName();
}
@@ -502,19 +509,19 @@ public List getElements(List ids, boolean strictMode, L
//if the user is not an admin we filter out elements he doesn't have the permission on
if (!roleService.isUserExploreAdmin()) {
elementEntities = elementEntities.stream().filter(directoryElementEntity ->
- hasReadPermissions(userId, List.of(directoryElementEntity.getId()))
- ).toList();
+ hasReadPermissions(userId, List.of(directoryElementEntity.getId()))
+ ).toList();
}
if (strictMode && elementEntities.size() != ids.stream().distinct().count()) {
- throw new DirectoryException(NOT_FOUND);
+ throw DirectoryException.of(DIRECTORY_ELEMENT_NOT_FOUND, "Some requested elements were not found");
}
Map subElementsCount = getSubDirectoriesCounts(elementEntities.stream().map(DirectoryElementEntity::getId).toList(), types);
return elementEntities.stream()
- .map(attribute -> toElementAttributes(attribute, subElementsCount.getOrDefault(attribute.getId(), 0L)))
- .toList();
+ .map(attribute -> toElementAttributes(attribute, subElementsCount.getOrDefault(attribute.getId(), 0L)))
+ .toList();
}
public int getCasesCount(String userId) {
@@ -545,7 +552,7 @@ private String nameCandidate(String elementName, int n) {
public String getDuplicateNameCandidate(UUID directoryUuid, String elementName, String elementType, String userId) {
if (!repositoryService.canRead(directoryUuid, userId)) {
- throw new DirectoryException(NOT_ALLOWED);
+ throw DirectoryException.of(DIRECTORY_PERMISSION_DENIED, "User '%s' cannot access directory '%s'", userId, directoryUuid);
}
var idLikes = new HashSet<>(repositoryService.getNameByTypeAndParentIdAndNameStartWith(elementType, directoryUuid, elementName));
if (!idLikes.contains(elementName)) {
@@ -569,7 +576,7 @@ public UUID getDirectoryUuidFromPath(List directoryPath) {
for (String s : directoryPath) {
UUID currentDirectoryUuid = getDirectoryUuid(s, parentDirectoryUuid);
if (currentDirectoryUuid == null) {
- throw new DirectoryException(NOT_FOUND);
+ throw DirectoryException.of(DIRECTORY_DIRECTORY_NOT_FOUND_IN_PATH, "Directory '%s' not found in path", s);
} else {
parentDirectoryUuid = currentDirectoryUuid;
}
@@ -602,13 +609,13 @@ private void notifyDirectoryHasChanged(UUID directoryUuid, String userId, String
private void notifyDirectoryHasChanged(UUID directoryUuid, String userId, String elementName, String error, boolean isDirectoryMoving) {
Objects.requireNonNull(directoryUuid);
notificationService.emitDirectoryChanged(
- directoryUuid,
- elementName,
- userId,
- error,
- repositoryService.isRootDirectory(directoryUuid),
- isDirectoryMoving,
- NotificationType.UPDATE_DIRECTORY
+ directoryUuid,
+ elementName,
+ userId,
+ error,
+ repositoryService.isRootDirectory(directoryUuid),
+ isDirectoryMoving,
+ NotificationType.UPDATE_DIRECTORY
);
}
@@ -626,13 +633,13 @@ private void notifyRootDirectoryDeleted(UUID rootDirectoryUuid, String userId, S
private void notifyRootDirectoryDeleted(UUID rootDirectoryUuid, String userId, String elementName, String error, boolean isDirectoryMoving) {
Objects.requireNonNull(rootDirectoryUuid);
notificationService.emitDirectoryChanged(
- rootDirectoryUuid,
- elementName,
- userId,
- error,
- true,
- isDirectoryMoving,
- NotificationType.DELETE_DIRECTORY
+ rootDirectoryUuid,
+ elementName,
+ userId,
+ error,
+ true,
+ isDirectoryMoving,
+ NotificationType.DELETE_DIRECTORY
);
}
@@ -640,15 +647,15 @@ private void notifyRootDirectoryDeleted(UUID rootDirectoryUuid, String userId, S
* Checks if a user has the specified permission on given elements.
* Checks parent permissions first, then target directory, then child permissions if recursive check is enabled.
*
- * @param userId User ID checking permissions for
- * @param elementUuids List of element UUIDs to check permissions on
+ * @param userId User ID checking permissions for
+ * @param elementUuids List of element UUIDs to check permissions on
* @param targetDirectoryUuid Optional target directory UUID (for move operations)
- * @param permissionType Type of permission to check (READ, WRITE, MANAGE)
- * @param recursiveCheck Whether to check permissions recursively on children
+ * @param permissionType Type of permission to check (READ, WRITE, MANAGE)
+ * @param recursiveCheck Whether to check permissions recursively on children
* @return PermissionCheckResult indicating where permission check failed, or ALLOWED if successful
*/
public PermissionCheckResult checkDirectoriesPermission(String userId, List elementUuids, UUID targetDirectoryUuid,
- PermissionType permissionType, boolean recursiveCheck) {
+ PermissionType permissionType, boolean recursiveCheck) {
return switch (permissionType) {
case READ -> checkReadPermission(userId, elementUuids);
case WRITE -> checkWritePermission(userId, elementUuids, targetDirectoryUuid, recursiveCheck);
@@ -659,14 +666,14 @@ public PermissionCheckResult checkDirectoriesPermission(String userId, List elementUuids) {
return hasReadPermissions(userId, elementUuids) ?
- PermissionCheckResult.ALLOWED :
- PermissionCheckResult.PARENT_PERMISSION_DENIED;
+ PermissionCheckResult.ALLOWED :
+ PermissionCheckResult.PARENT_PERMISSION_DENIED;
}
private PermissionCheckResult checkManagePermission(String userId, List elementUuids) {
return hasManagePermission(userId, elementUuids) ?
- PermissionCheckResult.ALLOWED :
- PermissionCheckResult.PARENT_PERMISSION_DENIED;
+ PermissionCheckResult.ALLOWED :
+ PermissionCheckResult.PARENT_PERMISSION_DENIED;
}
private PermissionCheckResult checkWritePermission(String userId, List elementUuids, UUID targetDirectoryUuid, boolean recursiveCheck) {
@@ -690,9 +697,9 @@ private PermissionCheckResult checkWritePermission(String userId, List ele
for (DirectoryElementEntity element : elements) {
if (element.getType().equals(DIRECTORY)) {
List descendantsUuids = repositoryService.findAllDescendants(element.getId())
- .stream()
- .filter(e -> e.getType().equals(DIRECTORY))
- .map(DirectoryElementEntity::getId).toList();
+ .stream()
+ .filter(e -> e.getType().equals(DIRECTORY))
+ .map(DirectoryElementEntity::getId).toList();
if (!descendantsUuids.isEmpty() && !checkPermission(userId, descendantsUuids, WRITE)) {
return PermissionCheckResult.CHILD_PERMISSION_DENIED;
}
@@ -707,8 +714,8 @@ private PermissionCheckResult checkWritePermission(String userId, List ele
public boolean hasReadPermissions(String userId, List elementUuids) {
List elements = directoryElementRepository.findAllByIdIn(elementUuids);
return elements.stream().allMatch(element ->
- //If it's a directory we check its own write permission else we check the permission on the element parent directory
- checkPermission(userId, List.of(element.getType().equals(DIRECTORY) ? element.getId() : element.getParentId()), READ)
+ //If it's a directory we check its own write permission else we check the permission on the element parent directory
+ checkPermission(userId, List.of(element.getType().equals(DIRECTORY) ? element.getId() : element.getParentId()), READ)
);
}
@@ -726,22 +733,22 @@ private boolean checkPermission(String userId, List elementUuids, Permissi
}
//Finally check group permission
return userAdminService.getUserGroups(userId)
- .stream()
- .map(UserGroupDTO::id)
- .anyMatch(groupId ->
- checkPermission(permissionRepository.findById(new PermissionId(uuid, "", groupId.toString())), permissionType)
- );
+ .stream()
+ .map(UserGroupDTO::id)
+ .anyMatch(groupId ->
+ checkPermission(permissionRepository.findById(new PermissionId(uuid, "", groupId.toString())), permissionType)
+ );
});
}
private boolean checkPermission(Optional permissionEntity, PermissionType permissionType) {
return permissionEntity
- .map(p -> switch (permissionType) {
- case READ -> Boolean.TRUE.equals(p.getRead());
- case WRITE -> Boolean.TRUE.equals(p.getWrite());
- case MANAGE -> Boolean.TRUE.equals(p.getManage());
- })
- .orElse(false);
+ .map(p -> switch (permissionType) {
+ case READ -> Boolean.TRUE.equals(p.getRead());
+ case WRITE -> Boolean.TRUE.equals(p.getWrite());
+ case MANAGE -> Boolean.TRUE.equals(p.getManage());
+ })
+ .orElse(false);
}
private boolean hasManagePermission(String userId, List elementUuids) {
@@ -788,7 +795,7 @@ private void addPermissionForGroup(UUID elementUuid, String groupId, PermissionT
public void validatePermissionsGetAccess(UUID directoryUuid, String userId) {
if (!roleService.isUserExploreAdmin() && !hasReadPermissions(userId, List.of(directoryUuid))) {
- throw new DirectoryException(NOT_ALLOWED);
+ throw DirectoryException.of(DIRECTORY_PERMISSION_DENIED, "User '%s' is not allowed to view directory '%s'", userId, directoryUuid);
}
}
@@ -797,7 +804,7 @@ public void validatePermissionsGetAccess(UUID directoryUuid, String userId) {
* Returns exactly one PermissionDTO for each permission type (READ, WRITE, MANAGE).
*
* @param directoryUuid The UUID of the directory
- * @param userId The ID of the user requesting the permissions
+ * @param userId The ID of the user requesting the permissions
* @return A list of exactly three permission DTOs (READ, WRITE, MANAGE)
* @throws DirectoryException if the user doesn't have access or the directory doesn't exist
*/
@@ -812,8 +819,8 @@ public List getDirectoryPermissions(UUID directoryUuid, String us
Map groupPermissionLevels = extractGroupPermissionLevels(permissions);
return Arrays.stream(PermissionType.values())
- .map(type -> createPermissionDto(type, allUsersPermissionLevel, groupPermissionLevels))
- .collect(Collectors.toList());
+ .map(type -> createPermissionDto(type, allUsersPermissionLevel, groupPermissionLevels))
+ .collect(Collectors.toList());
}
/**
@@ -822,15 +829,15 @@ public List getDirectoryPermissions(UUID directoryUuid, String us
* If allUsers is false, groups list will contain only groups with exactly this permission type
*/
private PermissionDTO createPermissionDto(
- PermissionType permissionType,
- PermissionType allUsersPermissionLevel,
- Map groupPermissionLevels) {
+ PermissionType permissionType,
+ PermissionType allUsersPermissionLevel,
+ Map groupPermissionLevels) {
boolean hasAllUsersPermission = hasPermissionLevel(allUsersPermissionLevel, permissionType);
List groupsWithPermission = hasAllUsersPermission
- ? Collections.emptyList()
- : getGroupsWithExactPermission(groupPermissionLevels, permissionType);
+ ? Collections.emptyList()
+ : getGroupsWithExactPermission(groupPermissionLevels, permissionType);
return new PermissionDTO(hasAllUsersPermission, groupsWithPermission, permissionType);
}
@@ -839,20 +846,20 @@ private PermissionDTO createPermissionDto(
* Gets all groups that have exactly the specified permission type
*/
private List getGroupsWithExactPermission(
- Map groupPermissionLevels,
- PermissionType exactPermissionType) {
+ Map groupPermissionLevels,
+ PermissionType exactPermissionType) {
return groupPermissionLevels.entrySet().stream()
- .filter(entry -> entry.getValue() == exactPermissionType)
- .map(entry -> {
- try {
- return UUID.fromString(entry.getKey());
- } catch (IllegalArgumentException e) {
- return null;
- }
- })
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
+ .filter(entry -> entry.getValue() == exactPermissionType)
+ .map(entry -> {
+ try {
+ return UUID.fromString(entry.getKey());
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
}
/**
@@ -865,10 +872,10 @@ private boolean hasPermissionLevel(PermissionType actualLevel, PermissionType re
return switch (requiredLevel) {
case READ -> actualLevel == PermissionType.READ ||
- actualLevel == PermissionType.WRITE ||
- actualLevel == PermissionType.MANAGE;
+ actualLevel == PermissionType.WRITE ||
+ actualLevel == PermissionType.MANAGE;
case WRITE -> actualLevel == PermissionType.WRITE ||
- actualLevel == PermissionType.MANAGE;
+ actualLevel == PermissionType.MANAGE;
case MANAGE -> actualLevel == PermissionType.MANAGE;
};
}
@@ -878,10 +885,10 @@ private boolean hasPermissionLevel(PermissionType actualLevel, PermissionType re
*/
private PermissionType extractGlobalPermissionLevel(List permissions) {
return permissions.stream()
- .filter(p -> ALL_USERS.equals(p.getUserId()))
- .findFirst()
- .map(this::determineHighestPermission)
- .orElse(null);
+ .filter(p -> ALL_USERS.equals(p.getUserId()))
+ .findFirst()
+ .map(this::determineHighestPermission)
+ .orElse(null);
}
/**
@@ -889,18 +896,18 @@ private PermissionType extractGlobalPermissionLevel(List permi
*/
private Map extractGroupPermissionLevels(List permissions) {
return permissions.stream()
- .filter(p -> !p.getUserGroupId().isEmpty())
- .collect(Collectors.toMap(
- PermissionEntity::getUserGroupId,
- this::determineHighestPermission,
- (existing, replacement) -> shouldUpdatePermission(existing, replacement) ? replacement : existing,
- HashMap::new
- ));
+ .filter(p -> !p.getUserGroupId().isEmpty())
+ .collect(Collectors.toMap(
+ PermissionEntity::getUserGroupId,
+ this::determineHighestPermission,
+ (existing, replacement) -> shouldUpdatePermission(existing, replacement) ? replacement : existing,
+ HashMap::new
+ ));
}
private void validatePermissionUpdateAccess(UUID directoryUuid, String userId) {
if (!roleService.isUserExploreAdmin() && !hasManagePermission(userId, List.of(directoryUuid))) {
- throw new DirectoryException(NOT_ALLOWED);
+ throw DirectoryException.of(DIRECTORY_PERMISSION_DENIED, "User '%s' is not allowed to update permissions on directory '%s'", userId, directoryUuid);
}
}
@@ -1032,7 +1039,7 @@ private void applyPermissionConfiguration(UUID directoryUuid, PermissionConfigur
} else {
// Apply group permissions
config.groupPermissions().forEach((groupId, permissionType) ->
- addPermissionForGroup(directoryUuid, groupId, permissionType)
+ addPermissionForGroup(directoryUuid, groupId, permissionType)
);
}
@@ -1043,7 +1050,7 @@ private void applyPermissionConfiguration(UUID directoryUuid, PermissionConfigur
*/
private void applyGroupPermissions(UUID directoryUuid, Map groupPermissions, Set targetPermissions) {
groupPermissions.entrySet().stream()
- .filter(entry -> targetPermissions.contains(entry.getValue()))
- .forEach(entry -> addPermissionForGroup(directoryUuid, entry.getKey(), entry.getValue()));
+ .filter(entry -> targetPermissions.contains(entry.getValue()))
+ .forEach(entry -> addPermissionForGroup(directoryUuid, entry.getKey(), entry.getValue()));
}
}
diff --git a/src/main/java/org/gridsuite/directory/server/PropertyServerNameProvider.java b/src/main/java/org/gridsuite/directory/server/PropertyServerNameProvider.java
new file mode 100644
index 00000000..cc21b055
--- /dev/null
+++ b/src/main/java/org/gridsuite/directory/server/PropertyServerNameProvider.java
@@ -0,0 +1,29 @@
+/**
+ * Copyright (c) 2025, RTE (http://www.rte-france.com)
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.gridsuite.directory.server;
+
+import com.powsybl.ws.commons.error.ServerNameProvider;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Mohamed Ben-rejeb {@literal }
+ */
+@Component
+public class PropertyServerNameProvider implements ServerNameProvider {
+
+ private final String name;
+
+ public PropertyServerNameProvider(@Value("${server.name:directory-server}") String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String serverName() {
+ return name;
+ }
+}
diff --git a/src/main/java/org/gridsuite/directory/server/RestResponseEntityExceptionHandler.java b/src/main/java/org/gridsuite/directory/server/RestResponseEntityExceptionHandler.java
index f10d7329..19994b19 100644
--- a/src/main/java/org/gridsuite/directory/server/RestResponseEntityExceptionHandler.java
+++ b/src/main/java/org/gridsuite/directory/server/RestResponseEntityExceptionHandler.java
@@ -6,46 +6,64 @@
*/
package org.gridsuite.directory.server;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import com.powsybl.ws.commons.error.AbstractBaseRestExceptionHandler;
+import com.powsybl.ws.commons.error.PowsyblWsProblemDetail;
+import com.powsybl.ws.commons.error.ServerNameProvider;
import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
-import org.springframework.web.bind.annotation.ExceptionHandler;
-import static org.gridsuite.directory.server.DirectoryException.Type.*;
+import java.util.Optional;
/**
* @author Abdelsalem Hedhili
+ * @author Mohamed Ben-rejeb {@literal }
*/
@ControllerAdvice
-public class RestResponseEntityExceptionHandler {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(RestResponseEntityExceptionHandler.class);
-
- @ExceptionHandler(value = {DirectoryException.class})
- protected ResponseEntity