Skip to content

Latest commit

 

History

History
492 lines (429 loc) · 16 KB

File metadata and controls

492 lines (429 loc) · 16 KB

Folder Management Examples

This document provides practical examples for managing folders and hierarchical structures using the Firefly ECM Library.

Table of Contents

  1. Basic Folder Operations
  2. Hierarchical Folder Structure
  3. Folder Permissions
  4. Folder Navigation
  5. Bulk Operations
  6. Advanced Scenarios

1. Basic Folder Operations

Creating Folders

@Service
public class FolderService {
    
    @Autowired
    private FolderPort folderPort;
    
    /**
     * Creates a new folder.
     */
    public Mono<Folder> createFolder(String name, String description, UUID parentId) {
        Folder folder = Folder.builder()
            .name(name)
            .description(description)
            .parentId(parentId)
            .createdAt(Instant.now())
            .build();
        
        return folderPort.createFolder(folder)
            .doOnSuccess(created -> 
                log.info("Folder created: {} (ID: {})", created.getName(), created.getId()))
            .doOnError(error -> 
                log.error("Failed to create folder: {}", name, error));
    }
    
    /**
     * Creates a folder at a specific path.
     */
    public Mono<Folder> createFolderAtPath(String path) {
        return folderPort.createFolderPath(path)
            .doOnSuccess(folder -> 
                log.info("Folder path created: {} (ID: {})", path, folder.getId()));
    }
}

Retrieving Folders

/**
 * Gets folder by ID.
 */
public Mono<Folder> getFolder(UUID folderId) {
    return folderPort.getFolder(folderId)
        .doOnNext(folder -> 
            log.debug("Retrieved folder: {} ({})", folder.getName(), folder.getPath()))
        .switchIfEmpty(Mono.error(new FolderNotFoundException("Folder not found: " + folderId)));
}

/**
 * Gets folder by path.
 */
public Mono<Folder> getFolderByPath(String path) {
    return folderPort.getFolderByPath(path)
        .doOnNext(folder -> 
            log.debug("Found folder at path: {} (ID: {})", path, folder.getId()))
        .switchIfEmpty(Mono.error(new FolderNotFoundException("Folder not found at path: " + path)));
}

Updating Folders

/**
 * Updates folder metadata.
 */
public Mono<Folder> updateFolder(UUID folderId, String newName, String newDescription) {
    return folderPort.getFolder(folderId)
        .flatMap(folder -> {
            Folder updatedFolder = folder.toBuilder()
                .name(newName != null ? newName : folder.getName())
                .description(newDescription != null ? newDescription : folder.getDescription())
                .modifiedAt(Instant.now())
                .build();
            
            return folderPort.updateFolder(updatedFolder);
        })
        .doOnSuccess(updated -> 
            log.info("Folder updated: {} (ID: {})", updated.getName(), updated.getId()));
}

Deleting Folders

/**
 * Deletes a folder (must be empty).
 */
public Mono<Void> deleteFolder(UUID folderId) {
    return folderPort.deleteFolder(folderId)
        .doOnSuccess(unused -> 
            log.info("Folder deleted: {}", folderId))
        .doOnError(error -> 
            log.error("Failed to delete folder: {}", folderId, error));
}

/**
 * Deletes a folder and all its contents recursively.
 */
public Mono<Void> deleteFolderRecursively(UUID folderId) {
    return folderPort.deleteFolderRecursively(folderId)
        .doOnSuccess(unused -> 
            log.info("Folder and contents deleted recursively: {}", folderId))
        .doOnError(error -> 
            log.error("Failed to delete folder recursively: {}", folderId, error));
}

2. Hierarchical Folder Structure

Creating Folder Hierarchies

/**
 * Creates a complete folder hierarchy.
 */
public Mono<Folder> createProjectStructure(String projectName) {
    // Create root project folder
    return createFolder(projectName, "Project root folder", null)
        .flatMap(projectFolder -> {
            // Create subfolders
            List<Mono<Folder>> subfolderCreations = Arrays.asList(
                createFolder("Documents", "Project documents", projectFolder.getId()),
                createFolder("Images", "Project images", projectFolder.getId()),
                createFolder("Archives", "Archived files", projectFolder.getId())
            );
            
            return Flux.merge(subfolderCreations)
                .then(Mono.just(projectFolder));
        })
        .doOnSuccess(project -> 
            log.info("Project structure created: {}", projectName));
}

/**
 * Creates nested folder structure using paths.
 */
public Mono<Folder> createNestedStructure() {
    List<String> paths = Arrays.asList(
        "/Company/HR/Policies",
        "/Company/HR/Employee Records",
        "/Company/Finance/Budgets",
        "/Company/Finance/Reports",
        "/Company/Legal/Contracts",
        "/Company/Legal/Compliance"
    );
    
    return Flux.fromIterable(paths)
        .flatMap(this::createFolderAtPath)
        .then(getFolderByPath("/Company"))
        .doOnSuccess(company -> 
            log.info("Company structure created with {} departments", paths.size()));
}

Navigating Folder Hierarchies

/**
 * Gets the complete folder hierarchy starting from a root folder.
 */
public Mono<FolderTree> getFolderHierarchy(UUID rootFolderId) {
    return folderPort.getFolder(rootFolderId)
        .flatMap(rootFolder -> {
            return buildFolderTree(rootFolder);
        });
}

private Mono<FolderTree> buildFolderTree(Folder folder) {
    return folderPort.getSubfolders(folder.getId())
        .flatMap(this::buildFolderTree)
        .collectList()
        .map(children -> FolderTree.builder()
            .folder(folder)
            .children(children)
            .build());
}

/**
 * Gets folder breadcrumb path.
 */
public Mono<List<Folder>> getFolderBreadcrumb(UUID folderId) {
    return folderPort.getFolder(folderId)
        .expand(folder -> {
            if (folder.getParentId() != null) {
                return folderPort.getFolder(folder.getParentId());
            } else {
                return Mono.empty();
            }
        })
        .collectList()
        .map(folders -> {
            Collections.reverse(folders); // Root first
            return folders;
        });
}

3. Folder Permissions

Setting Folder Permissions

@Autowired
private FolderPermissionPort folderPermissionPort;

/**
 * Sets permissions for a folder.
 */
public Mono<FolderPermission> setFolderPermissions(UUID folderId, Long userId, 
                                                  Set<PermissionType> permissions) {
    FolderPermission folderPermission = FolderPermission.builder()
        .folderId(folderId)
        .userId(userId)
        .permissions(permissions)
        .grantedAt(Instant.now())
        .build();
    
    return folderPermissionPort.grantPermission(folderPermission)
        .doOnSuccess(granted -> 
            log.info("Permissions granted for folder {} to user {}: {}", 
                    folderId, userId, permissions));
}

/**
 * Inherits permissions from parent folder.
 */
public Mono<Void> inheritParentPermissions(UUID folderId) {
    return folderPort.getFolder(folderId)
        .filter(folder -> folder.getParentId() != null)
        .flatMap(folder -> folderPermissionPort.inheritPermissions(folderId, folder.getParentId()))
        .doOnSuccess(unused -> 
            log.info("Permissions inherited for folder: {}", folderId));
}

Checking Folder Access

/**
 * Checks if user has access to folder.
 */
public Mono<Boolean> hasAccess(UUID folderId, Long userId, PermissionType permission) {
    return folderPermissionPort.hasPermission(folderId, userId, permission)
        .doOnNext(hasAccess -> 
            log.debug("Access check for folder {} user {} permission {}: {}", 
                     folderId, userId, permission, hasAccess));
}

/**
 * Gets effective permissions for a user on a folder.
 */
public Mono<Set<PermissionType>> getEffectivePermissions(UUID folderId, Long userId) {
    return folderPermissionPort.getEffectivePermissions(folderId, userId)
        .doOnNext(permissions -> 
            log.debug("Effective permissions for folder {} user {}: {}", 
                     folderId, userId, permissions));
}

4. Folder Navigation

Listing Folder Contents

/**
 * Gets all contents of a folder (documents and subfolders).
 */
public Mono<FolderContents> getFolderContents(UUID folderId) {
    Mono<List<Document>> documents = documentPort.findDocumentsByFolder(folderId)
        .collectList();
    
    Mono<List<Folder>> subfolders = folderPort.getSubfolders(folderId)
        .collectList();
    
    return Mono.zip(documents, subfolders)
        .map(tuple -> FolderContents.builder()
            .documents(tuple.getT1())
            .subfolders(tuple.getT2())
            .build())
        .doOnNext(contents -> 
            log.debug("Folder {} contains {} documents and {} subfolders", 
                     folderId, contents.getDocuments().size(), contents.getSubfolders().size()));
}

/**
 * Gets paginated folder contents.
 */
public Mono<PagedFolderContents> getFolderContentsPaged(UUID folderId, int page, int size) {
    return folderPort.getFolderContentsPaged(folderId, page, size)
        .doOnNext(contents -> 
            log.debug("Page {} of folder {} contents: {} items", 
                     page, folderId, contents.getContent().size()));
}

Searching Within Folders

/**
 * Searches for documents within a folder and its subfolders.
 */
public Flux<Document> searchInFolder(UUID folderId, String query, boolean includeSubfolders) {
    if (includeSubfolders) {
        return folderPort.getFolder(folderId)
            .flatMapMany(folder -> documentPort.searchDocuments(
                DocumentSearchCriteria.builder()
                    .query(query)
                    .folderPath(folder.getPath())
                    .includeSubfolders(true)
                    .build()))
            .doOnNext(document -> 
                log.debug("Found document in folder search: {} ({})", 
                         document.getName(), document.getId()));
    } else {
        return documentPort.findDocumentsByFolder(folderId)
            .filter(document -> document.getName().toLowerCase().contains(query.toLowerCase()))
            .doOnNext(document -> 
                log.debug("Found document in folder: {} ({})", 
                         document.getName(), document.getId()));
    }
}

5. Bulk Operations

Moving Multiple Items

/**
 * Moves multiple documents to a target folder.
 */
public Mono<Void> moveDocumentsToFolder(List<UUID> documentIds, UUID targetFolderId) {
    return Flux.fromIterable(documentIds)
        .flatMap(documentId -> 
            documentPort.getDocument(documentId)
                .flatMap(document -> {
                    Document movedDocument = document.toBuilder()
                        .folderId(targetFolderId)
                        .modifiedAt(Instant.now())
                        .build();
                    return documentPort.updateDocument(movedDocument);
                }))
        .then()
        .doOnSuccess(unused -> 
            log.info("Moved {} documents to folder {}", documentIds.size(), targetFolderId));
}

/**
 * Copies folder structure to another location.
 */
public Mono<Folder> copyFolderStructure(UUID sourceFolderId, UUID targetParentId) {
    return folderPort.getFolder(sourceFolderId)
        .flatMap(sourceFolder -> {
            // Create copy of the folder
            Folder copiedFolder = sourceFolder.toBuilder()
                .id(null) // New ID will be generated
                .parentId(targetParentId)
                .name(sourceFolder.getName() + " (Copy)")
                .createdAt(Instant.now())
                .build();
            
            return folderPort.createFolder(copiedFolder)
                .flatMap(newFolder -> {
                    // Copy subfolders recursively
                    return folderPort.getSubfolders(sourceFolderId)
                        .flatMap(subfolder -> copyFolderStructure(subfolder.getId(), newFolder.getId()))
                        .then(Mono.just(newFolder));
                });
        })
        .doOnSuccess(copied -> 
            log.info("Folder structure copied: {} -> {}", sourceFolderId, copied.getId()));
}

6. Advanced Scenarios

Folder Templates

/**
 * Creates a folder from a predefined template.
 */
public Mono<Folder> createFromTemplate(String templateName, String folderName, UUID parentId) {
    return getTemplate(templateName)
        .flatMap(template -> createFolderFromTemplate(template, folderName, parentId))
        .doOnSuccess(folder -> 
            log.info("Folder created from template '{}': {} ({})", 
                    templateName, folderName, folder.getId()));
}

private Mono<FolderTemplate> getTemplate(String templateName) {
    // Load template from configuration or database
    Map<String, FolderTemplate> templates = Map.of(
        "project", FolderTemplate.builder()
            .name("Project Template")
            .structure(Arrays.asList(
                "Documents/Requirements",
                "Documents/Design",
                "Documents/Testing",
                "Resources/Images",
                "Resources/Assets",
                "Archive"
            ))
            .build()
    );
    
    return Mono.justOrEmpty(templates.get(templateName));
}

private Mono<Folder> createFolderFromTemplate(FolderTemplate template, String folderName, UUID parentId) {
    return createFolder(folderName, "Created from template: " + template.getName(), parentId)
        .flatMap(rootFolder -> {
            return Flux.fromIterable(template.getStructure())
                .flatMap(path -> createFolderAtPath(rootFolder.getPath() + "/" + path))
                .then(Mono.just(rootFolder));
        });
}

Folder Statistics

/**
 * Gets comprehensive folder statistics.
 */
public Mono<FolderStatistics> getFolderStatistics(UUID folderId) {
    Mono<Long> documentCount = documentPort.findDocumentsByFolder(folderId).count();
    Mono<Long> subfolderCount = folderPort.getSubfolders(folderId).count();
    Mono<Long> totalSize = documentPort.findDocumentsByFolder(folderId)
        .map(Document::getSize)
        .reduce(0L, Long::sum);
    
    return Mono.zip(documentCount, subfolderCount, totalSize)
        .map(tuple -> FolderStatistics.builder()
            .folderId(folderId)
            .documentCount(tuple.getT1())
            .subfolderCount(tuple.getT2())
            .totalSize(tuple.getT3())
            .calculatedAt(Instant.now())
            .build())
        .doOnNext(stats -> 
            log.debug("Folder {} statistics: {} docs, {} folders, {} bytes", 
                     folderId, stats.getDocumentCount(), stats.getSubfolderCount(), stats.getTotalSize()));
}

Folder Cleanup

/**
 * Cleans up empty folders in a hierarchy.
 */
public Mono<Integer> cleanupEmptyFolders(UUID rootFolderId) {
    return folderPort.getSubfolders(rootFolderId)
        .flatMap(this::cleanupEmptyFolders) // Recursive cleanup
        .reduce(0, Integer::sum)
        .flatMap(cleanedSubfolders -> {
            // Check if this folder is now empty
            return getFolderContents(rootFolderId)
                .flatMap(contents -> {
                    if (contents.getDocuments().isEmpty() && contents.getSubfolders().isEmpty()) {
                        return folderPort.deleteFolder(rootFolderId)
                            .then(Mono.just(cleanedSubfolders + 1));
                    } else {
                        return Mono.just(cleanedSubfolders);
                    }
                });
        })
        .doOnSuccess(count -> 
            log.info("Cleaned up {} empty folders", count));
}

These examples demonstrate comprehensive folder management capabilities using the Firefly ECM Library's folder ports and domain models. All examples are based on the actual port interfaces and follow reactive programming best practices.