Skip to content

Commit

Permalink
#27540 Refactor file listing commands for modularity
Browse files Browse the repository at this point in the history
Moved common functionality of FilesTree and FilesLs into new AbstractFilesListingCommand for code reuse and simplified their implementations appropriately. The changes aim to improve maintainability and code clarity by avoiding repetition.
  • Loading branch information
jgambarios committed Feb 13, 2024
1 parent 62ecfeb commit 073ebd3
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 235 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.dotcms.cli.command.files;

import com.dotcms.api.LanguageAPI;
import com.dotcms.api.client.files.traversal.RemoteTraversalService;
import com.dotcms.api.traversal.TreeNode;
import com.dotcms.cli.common.ConsoleLoadingAnimation;
import com.dotcms.model.language.Language;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.microprofile.context.ManagedExecutor;
import picocli.CommandLine;

/**
* This abstract class is used for implementing files listing commands. It provides common
* functionality for listing the contents of a remote directory.
*/
public abstract class AbstractFilesListingCommand extends AbstractFilesCommand {

@CommandLine.Mixin
FilesListingMixin filesMixin;

@Inject
RemoteTraversalService remoteTraversalService;

@CommandLine.Spec
CommandLine.Model.CommandSpec spec;

@Inject
ManagedExecutor executor;

/**
* Executes the listing of a remote folder at the specified depth.
*
* @param depth the depth of the folder traversal
* @return an exit code indicating the success of the operation
* @throws ExecutionException if an exception occurs during execution
* @throws InterruptedException if the execution is interrupted
*/
protected Integer listing(final Integer depth) throws ExecutionException, InterruptedException {

// Checking for unmatched arguments
output.throwIfUnmatchedArguments(spec.commandLine());

var includeFolderPatterns = parsePatternOption(filesMixin.includeFolderPatternsOption);
var includeAssetPatterns = parsePatternOption(filesMixin.includeAssetPatternsOption);
var excludeFolderPatterns = parsePatternOption(filesMixin.excludeFolderPatternsOption);
var excludeAssetPatterns = parsePatternOption(filesMixin.excludeAssetPatternsOption);

CompletableFuture<Pair<List<Exception>, TreeNode>> folderTraversalFuture = executor.supplyAsync(
() ->
// Service to handle the traversal of the folder
remoteTraversalService.traverseRemoteFolder(
filesMixin.folderPath,
depth,
true,
includeFolderPatterns,
includeAssetPatterns,
excludeFolderPatterns,
excludeAssetPatterns
)
);

// ConsoleLoadingAnimation instance to handle the waiting "animation"
ConsoleLoadingAnimation consoleLoadingAnimation = new ConsoleLoadingAnimation(
output,
folderTraversalFuture
);

CompletableFuture<Void> animationFuture = executor.runAsync(
consoleLoadingAnimation
);

// Waits for the completion of both the folder traversal and console loading animation tasks.
// This line blocks the current thread until both CompletableFuture instances
// (folderTraversalFuture and animationFuture) have completed.
CompletableFuture.allOf(folderTraversalFuture, animationFuture).join();
final var result = folderTraversalFuture.get();

if (result == null) {
output.error(String.format(
"Error occurred while pulling folder info: [%s].", filesMixin.folderPath));
return CommandLine.ExitCode.SOFTWARE;
}

// We need to retrieve the languages
final LanguageAPI languageAPI = clientFactory.getClient(LanguageAPI.class);
final List<Language> languages = languageAPI.list().entity();

// Display the result
StringBuilder sb = new StringBuilder();
TreePrinter.getInstance()
.filteredFormat(sb, result.getRight(), !filesMixin.excludeEmptyFolders, languages);

output.info(sb.toString());

return CommandLine.ExitCode.OK;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.dotcms.cli.command.files;

import picocli.CommandLine;
import picocli.CommandLine.Parameters;

/**
* Mixin class that provides options for listing the contents of a remote dotCMS directory
*/
public class FilesListingMixin {

@Parameters(index = "0", arity = "1", paramLabel = "path",
description = "dotCMS path to the directory to list the contents of "
+ "- Format: //{site}/{folder}")
String folderPath;

@CommandLine.Option(names = {"-ee", "--excludeEmptyFolders"}, defaultValue = "false",
description =
"When this option is enabled, the tree display will exclude folders that do "
+ "not contain any assets, as well as folders that have no children with assets. "
+ "This can be useful for users who want to focus on the folder structure that "
+ "contains assets, making the output more concise and easier to navigate. By "
+ "default, this option is disabled, and all folders, including empty ones, "
+ "will be displayed in the tree.")
boolean excludeEmptyFolders;

@CommandLine.Option(names = {"-ef", "--excludeFolder"},
paramLabel = "patterns",
description = "Exclude directories matching the given glob patterns. Multiple "
+ "patterns can be specified, separated by commas.")
String excludeFolderPatternsOption;

@CommandLine.Option(names = {"-ea", "--excludeAsset"},
paramLabel = "patterns",
description = "Exclude assets matching the given glob patterns. Multiple "
+ "patterns can be specified, separated by commas.")
String excludeAssetPatternsOption;

@CommandLine.Option(names = {"-if", "--includeFolder"},
paramLabel = "patterns",
description = "Include directories matching the given glob patterns. Multiple "
+ "patterns can be specified, separated by commas.")
String includeFolderPatternsOption;

@CommandLine.Option(names = {"-ia", "--includeAsset"},
paramLabel = "patterns",
description = "Include assets matching the given glob patterns. Multiple "
+ "patterns can be specified, separated by commas.")
String includeAssetPatternsOption;

}
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
package com.dotcms.cli.command.files;

import com.dotcms.api.LanguageAPI;
import com.dotcms.api.client.files.traversal.RemoteTraversalService;
import com.dotcms.api.traversal.TreeNode;
import com.dotcms.cli.command.DotCommand;
import com.dotcms.cli.common.ConsoleLoadingAnimation;
import com.dotcms.cli.common.OutputOptionMixin;
import com.dotcms.model.language.Language;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import javax.enterprise.context.control.ActivateRequestContext;
import javax.inject.Inject;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.microprofile.context.ManagedExecutor;
import picocli.CommandLine;
import picocli.CommandLine.Parameters;

/**
* Command to lists the files and directories in the specified directory.
Expand All @@ -29,117 +18,13 @@
"" // empty string here so we can have a new line
}
)
public class FilesLs extends AbstractFilesCommand implements Callable<Integer>, DotCommand {
public class FilesLs extends AbstractFilesListingCommand implements Callable<Integer>, DotCommand {

static final String NAME = "ls";

@Parameters(index = "0", arity = "1", paramLabel = "path",
description = "dotCMS path to the directory to list the contents of "
+ "- Format: //{site}/{folder}")
String folderPath;

@CommandLine.Option(names = {"-ee", "--excludeEmptyFolders"}, defaultValue = "false",
description =
"When this option is enabled, the tree display will exclude folders that do "
+ "not contain any assets, as well as folders that have no children with assets. "
+ "This can be useful for users who want to focus on the folder structure that "
+ "contains assets, making the output more concise and easier to navigate. By "
+ "default, this option is disabled, and all folders, including empty ones, "
+ "will be displayed in the tree.")
boolean excludeEmptyFolders;

@CommandLine.Option(names = {"-ef", "--excludeFolder"},
paramLabel = "patterns",
description = "Exclude directories matching the given glob patterns. Multiple "
+ "patterns can be specified, separated by commas.")
String excludeFolderPatternsOption;

@CommandLine.Option(names = {"-ea", "--excludeAsset"},
paramLabel = "patterns",
description = "Exclude assets matching the given glob patterns. Multiple "
+ "patterns can be specified, separated by commas.")
String excludeAssetPatternsOption;

@CommandLine.Option(names = {"-if", "--includeFolder"},
paramLabel = "patterns",
description = "Include directories matching the given glob patterns. Multiple "
+ "patterns can be specified, separated by commas.")
String includeFolderPatternsOption;

@CommandLine.Option(names = {"-ia", "--includeAsset"},
paramLabel = "patterns",
description = "Include assets matching the given glob patterns. Multiple "
+ "patterns can be specified, separated by commas.")
String includeAssetPatternsOption;

@Inject
RemoteTraversalService remoteTraversalService;

@CommandLine.Spec
CommandLine.Model.CommandSpec spec;

@Inject
ManagedExecutor executor;

@Override
public Integer call() throws Exception {

// Checking for unmatched arguments
output.throwIfUnmatchedArguments(spec.commandLine());

var includeFolderPatterns = parsePatternOption(includeFolderPatternsOption);
var includeAssetPatterns = parsePatternOption(includeAssetPatternsOption);
var excludeFolderPatterns = parsePatternOption(excludeFolderPatternsOption);
var excludeAssetPatterns = parsePatternOption(excludeAssetPatternsOption);

CompletableFuture<Pair<List<Exception>, TreeNode>> folderTraversalFuture = executor.supplyAsync(
() -> {
// Service to handle the traversal of the folder
return remoteTraversalService.traverseRemoteFolder(
folderPath,
0,
true,
includeFolderPatterns,
includeAssetPatterns,
excludeFolderPatterns,
excludeAssetPatterns
);
});

// ConsoleLoadingAnimation instance to handle the waiting "animation"
ConsoleLoadingAnimation consoleLoadingAnimation = new ConsoleLoadingAnimation(
output,
folderTraversalFuture
);

CompletableFuture<Void> animationFuture = executor.runAsync(
consoleLoadingAnimation
);

// Waits for the completion of both the folder traversal and console loading animation tasks.
// This line blocks the current thread until both CompletableFuture instances
// (folderTraversalFuture and animationFuture) have completed.
CompletableFuture.allOf(folderTraversalFuture, animationFuture).join();
final var result = folderTraversalFuture.get();

if (result == null) {
output.error(String.format(
"Error occurred while pulling folder info: [%s].", folderPath));
return CommandLine.ExitCode.SOFTWARE;
}

// We need to retrieve the languages
final LanguageAPI languageAPI = clientFactory.getClient(LanguageAPI.class);
final List<Language> languages = languageAPI.list().entity();

// Display the result
StringBuilder sb = new StringBuilder();
TreePrinter.getInstance().filteredFormat(sb, result.getRight(), !excludeEmptyFolders, languages);

output.info(sb.toString());


return CommandLine.ExitCode.OK;
return listing(0);
}

@Override
Expand All @@ -152,5 +37,4 @@ public OutputOptionMixin getOutput() {
return output;
}


}
Loading

0 comments on commit 073ebd3

Please sign in to comment.