Skip to content

Commit

Permalink
Move directory (#162)
Browse files Browse the repository at this point in the history
Signed-off-by: Seddik Yengui <seddik.yengui@rte-france.com>
Co-authored-by: Slimane AMAR <amarsli@gm0winl104.bureau.si.interne>
  • Loading branch information
YenguiSeddik and Slimane AMAR authored Oct 25, 2024
1 parent 1d56f71 commit d98a00a
Show file tree
Hide file tree
Showing 11 changed files with 315 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ public ResponseEntity<Void> updateElement(@PathVariable("elementUuid") UUID elem
@PutMapping(value = "/elements", params = "targetDirectoryUuid")
@Operation(summary = "Move elements within directory tree")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Elements was successfully updated"),
@ApiResponse(responseCode = "200", description = "Elements was successfully moved"),
@ApiResponse(responseCode = "404", description = "The elements or the targeted directory was not found"),
@ApiResponse(responseCode = "403", description = "Not authorized execute this update")
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public enum Type {
NOT_DIRECTORY,
IS_DIRECTORY,
UNKNOWN_NOTIFICATION,
NAME_ALREADY_EXISTS
NAME_ALREADY_EXISTS,
MOVE_IN_DESCENDANT_NOT_ALLOWED,
}
}
65 changes: 49 additions & 16 deletions src/main/java/org/gridsuite/directory/server/DirectoryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static org.gridsuite.directory.server.DirectoryException.Type.*;
Expand Down Expand Up @@ -327,26 +328,43 @@ public void moveElementsDirectory(List<UUID> elementsUuids, UUID newDirectoryUui

validateNewDirectory(newDirectoryUuid);

elementsUuids.forEach(elementUuid -> moveElementDirectory(elementUuid, newDirectoryUuid, userId));
elementsUuids.forEach(elementUuid -> moveElementDirectory(getDirectoryElementEntity(elementUuid), newDirectoryUuid, userId));
}

private void moveElementDirectory(UUID elementUuid, UUID newDirectoryUuid, String userId) {
DirectoryElementEntity element = repositoryService.getElementEntity(elementUuid)
.orElseThrow(() -> DirectoryException.createElementNotFound(ELEMENT, elementUuid));
private void moveElementDirectory(DirectoryElementEntity element, UUID newDirectoryUuid, String userId) {
if (Objects.equals(element.getParentId(), newDirectoryUuid)) { // Same directory ?
return;
}

DirectoryElementEntity oldDirectory = element.getParentId() == null ? null : repositoryService.getElementEntity(element.getParentId()).orElseThrow();
boolean isDirectory = DIRECTORY.equals(element.getType());
List<DirectoryElementEntity> descendents = isDirectory ? repositoryService.findAllDescendants(element.getId()).stream().toList() : List.of();

validateElementForMove(element, newDirectoryUuid, userId);
// validate move elements
validateElementForMove(element, newDirectoryUuid, userId, descendents.stream().map(DirectoryElementEntity::getId).collect(Collectors.toSet()));

DirectoryElementEntity oldDirectory = repositoryService.getElementEntity(element.getParentId()).orElseThrow();
// we update the parent of the moving element
updateElementParentDirectory(element, newDirectoryUuid);
notifyDirectoryHasChanged(element.getParentId() == null ? element.getId() : element.getParentId(), userId, element.getName());
notifyDirectoryHasChanged(oldDirectory.getId(), userId, element.getName());

// reindex descendents
repositoryService.reindexElements(descendents);

// if it has a parent, we notify it.
// otherwise, which means it is a root, we send a notification that a root has been deleted (in this case, it moved under a new directory)
if (oldDirectory != null) {
notifyDirectoryHasChanged(oldDirectory.getId(), userId, element.getName(), isDirectory);
} else {
notifyRootDirectoryDeleted(element.getId(), userId, element.getName(), isDirectory);
}
notifyDirectoryHasChanged(newDirectoryUuid, userId, element.getName(), isDirectory);

}

private void validateElementForMove(DirectoryElementEntity element, UUID newDirectoryUuid, String userId) {
if (element.getType().equals(DIRECTORY)) {
throw new DirectoryException(IS_DIRECTORY);
private void validateElementForMove(DirectoryElementEntity element, UUID newDirectoryUuid, String userId, Set<UUID> descendentsUuids) {
if (newDirectoryUuid == element.getId() || descendentsUuids.contains(newDirectoryUuid)) {
throw new DirectoryException(MOVE_IN_DESCENDANT_NOT_ALLOWED);
}

if (!isDirectoryElementUpdatable(toElementAttributes(element), userId) ||
directoryHasElementOfNameAndType(newDirectoryUuid, element.getName(), element.getType())) {
throw new DirectoryException(NOT_ALLOWED);
Expand Down Expand Up @@ -455,7 +473,7 @@ public void deleteElements(List<UUID> elementsUuids, UUID parentDirectoryUuid, S
* @return ElementAttributes of element and all it's parents up to root directory
*/
public List<ElementAttributes> getPath(UUID elementUuid) {
List<ElementAttributes> path = repositoryService.findElementHierarchy(elementUuid).stream().map(ElementAttributes::toElementAttributes).toList();
List<ElementAttributes> path = repositoryService.getPath(elementUuid).stream().map(ElementAttributes::toElementAttributes).toList();
if (path.isEmpty()) {
throw DirectoryException.createElementNotFound(ELEMENT, elementUuid);
}
Expand Down Expand Up @@ -580,40 +598,55 @@ public UUID getDirectoryUuidFromPath(List<String> directoryPath) {
// then we send a notification on this directory
private void notifyDirectoryHasChanged(UUID directoryUuid, String userId) {
Objects.requireNonNull(directoryUuid);
notifyDirectoryHasChanged(directoryUuid, userId, null, null);
notifyDirectoryHasChanged(directoryUuid, userId, null, null, false);
}

private void notifyDirectoryHasChanged(UUID directoryUuid, String userId, String elementName) {
notifyDirectoryHasChanged(directoryUuid, userId, elementName, false);
}

private void notifyDirectoryHasChanged(UUID directoryUuid, String userId, String elementName, boolean isDirectoryMoving) {
Objects.requireNonNull(directoryUuid);
notifyDirectoryHasChanged(directoryUuid, userId, elementName, null);
notifyDirectoryHasChanged(directoryUuid, userId, elementName, null, isDirectoryMoving);
}

private void notifyDirectoryHasChanged(UUID directoryUuid, String userId, String elementName, String error) {
notifyDirectoryHasChanged(directoryUuid, userId, elementName, error, false);
}

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
);
}

// Root directories don't have parent directories. Then if on is deleted, we must send a specific notification
private void notifyRootDirectoryDeleted(UUID rootDirectoryUuid, String userId, String elementName) {
Objects.requireNonNull(rootDirectoryUuid);
notifyRootDirectoryDeleted(rootDirectoryUuid, userId, elementName, null);
notifyRootDirectoryDeleted(rootDirectoryUuid, userId, elementName, false);
}

private void notifyRootDirectoryDeleted(UUID rootDirectoryUuid, String userId, String elementName, boolean isDirectoryMoving) {
Objects.requireNonNull(rootDirectoryUuid);
notifyRootDirectoryDeleted(rootDirectoryUuid, userId, elementName, null, isDirectoryMoving);
}

private void notifyRootDirectoryDeleted(UUID rootDirectoryUuid, String userId, String elementName, String error) {
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
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class NotificationService {
public static final String HEADER_NOTIFICATION_TYPE = "notificationType";
public static final String HEADER_ELEMENT_NAME = "elementName";
public static final String HEADER_ELEMENT_UUID = "elementUuid";
public static final String HEADER_IS_DIRECTORY_MOVING = "isDirectoryMoving";
public static final String UPDATE_TYPE_ELEMENT_DELETE = "deleteElement";
private static final String CATEGORY_BROKER_OUTPUT = DirectoryService.class.getName() + ".output-broker-messages";
private static final Logger MESSAGE_OUTPUT_LOGGER = LoggerFactory.getLogger(CATEGORY_BROKER_OUTPUT);
Expand All @@ -48,6 +49,10 @@ private void sendUpdateMessage(Message<String> message) {
}

public void emitDirectoryChanged(UUID directoryUuid, String elementName, String userId, String error, boolean isRoot, NotificationType notificationType) {
emitDirectoryChanged(directoryUuid, elementName, userId, error, isRoot, false, notificationType);
}

public void emitDirectoryChanged(UUID directoryUuid, String elementName, String userId, String error, boolean isRoot, boolean isDirectoryMoving, NotificationType notificationType) {
MessageBuilder<String> messageBuilder = MessageBuilder.withPayload("")
.setHeader(HEADER_USER_ID, userId)
.setHeader(HEADER_DIRECTORY_UUID, directoryUuid)
Expand All @@ -56,6 +61,7 @@ public void emitDirectoryChanged(UUID directoryUuid, String elementName, String
.setHeader(HEADER_IS_PUBLIC_DIRECTORY, true) // null may only come from borked REST request
.setHeader(HEADER_NOTIFICATION_TYPE, notificationType)
.setHeader(HEADER_UPDATE_TYPE, UPDATE_TYPE_DIRECTORIES)
.setHeader(HEADER_IS_DIRECTORY_MOVING, isDirectoryMoving)
.setHeader(HEADER_ERROR, error);
sendUpdateMessage(messageBuilder.build());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ protected ResponseEntity<Object> handleException(RuntimeException exception) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(UNKNOWN_NOTIFICATION);
case NAME_ALREADY_EXISTS:
return ResponseEntity.status(HttpStatus.CONFLICT).body(directoryException.getMessage());
case MOVE_IN_DESCENDANT_NOT_ALLOWED:
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(MOVE_IN_DESCENDANT_NOT_ALLOWED);
default:
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,20 +88,15 @@ interface SubDirectoryCount {
List<DirectoryElementEntity> findElementHierarchy(@Param("elementId") UUID elementId);

@Query(nativeQuery = true, value =
"WITH RECURSIVE DescendantHierarchy (element_id, parent_element_id) AS (" +
" SELECT" +
" id AS element_id, parent_id AS parent_element_id" +
" FROM element where id = :elementId" +
"WITH RECURSIVE DescendantHierarchy (element_id, parent_element_id, depth) AS (" +
" SELECT id AS element_id, parent_id AS parent_element_id, 0 AS depth" +
" FROM element WHERE id = :elementId" +
" UNION ALL" +
" select e.id AS element_id, e.parent_id AS parent_element_id" +
" SELECT e.id AS element_id, e.parent_id AS parent_element_id, dh.depth + 1" +
" FROM element e" +
" INNER JOIN" +
" DescendantHierarchy dh" +
" ON dh.element_id = e.parent_id" +
" WHERE e.type = 'DIRECTORY' )" +
"SELECT * FROM element e" +
"JOIN" +
" DescendantHierarchy dh" +
" ON e.id = dh.element_id")
" INNER JOIN DescendantHierarchy dh ON dh.element_id = e.parent_id)" +
"SELECT * FROM element e " +
"WHERE e.id IN (SELECT dh.element_id FROM DescendantHierarchy dh) AND e.id != :elementId"
)
List<DirectoryElementEntity> findAllDescendants(@Param("elementId") UUID elementId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.*;

import static org.gridsuite.directory.server.DirectoryService.DIRECTORY;

Expand Down Expand Up @@ -59,19 +57,23 @@ public boolean isElementExists(UUID parentDirectoryUuid, String elementName, Str
return !directoryElementRepository.findByNameAndParentIdAndType(elementName, parentDirectoryUuid, type).isEmpty();
}

public void saveElementsInfos(@NonNull List<DirectoryElementInfos> directoryElementInfos) {
private void saveElementsInfos(List<DirectoryElementEntity> directoryElements) {
Map<UUID, List<DirectoryElementEntity>> pathsCache = new HashMap<>();
List<DirectoryElementInfos> directoryElementInfos = directoryElements.stream()
.map(directoryElementEntity -> directoryElementEntity.toDirectoryElementInfos(getPath(directoryElementEntity.getParentId(), pathsCache)))
.toList();
Lists.partition(directoryElementInfos, partitionSize)
.parallelStream()
.forEach(directoryElementInfosRepository::saveAll);
}

private DirectoryElementEntity saveElementsInfo(DirectoryElementEntity elementEntity) {
directoryElementInfosRepository.save(elementEntity.toDirectoryElementInfos(findElementHierarchy(elementEntity.getParentId())));
private DirectoryElementEntity saveElementInfos(DirectoryElementEntity elementEntity) {
directoryElementInfosRepository.save(elementEntity.toDirectoryElementInfos(getPath(elementEntity.getParentId())));
return elementEntity;
}

public DirectoryElementEntity saveElement(DirectoryElementEntity elementEntity) {
return saveElementsInfo(directoryElementRepository.save(elementEntity));
return saveElementInfos(directoryElementRepository.save(elementEntity));
}

public void deleteElement(UUID elementUuid) {
Expand All @@ -89,9 +91,11 @@ public boolean canRead(UUID id, String userId) {
}

public void reindexElements() {
saveElementsInfos(directoryElementRepository.findAll().stream()
.map(directoryElementEntity -> directoryElementEntity.toDirectoryElementInfos(findElementHierarchy(directoryElementEntity.getParentId())))
.toList());
reindexElements(directoryElementRepository.findAll());
}

public void reindexElements(@NonNull List<DirectoryElementEntity> elementEntities) {
saveElementsInfos(elementEntities);
}

public UUID getParentUuid(UUID elementUuid) {
Expand Down Expand Up @@ -129,9 +133,16 @@ public List<String> getNameByTypeAndParentIdAndNameStartWith(String type, UUID p
return directoryElementRepository.getNameByTypeAndParentIdAndNameStartWith(type, parentId, name);
}

public List<DirectoryElementEntity> findElementHierarchy(UUID elementId) {
public List<DirectoryElementEntity> getPath(UUID elementId) {
return getPath(elementId, new HashMap<>());
}

public List<DirectoryElementEntity> getPath(UUID elementId, Map<UUID, List<DirectoryElementEntity>> pathsCache) {
// Test null for root directories
return elementId == null ? List.of() : directoryElementRepository.findElementHierarchy(elementId);
return elementId == null ? List.of() : pathsCache.computeIfAbsent(elementId, directoryElementRepository::findElementHierarchy);
}

public List<DirectoryElementEntity> findAllDescendants(@NonNull UUID elementId) {
return directoryElementRepository.findAllDescendants(elementId);
}
}
Loading

0 comments on commit d98a00a

Please sign in to comment.