From d9a8c5c1f08e4e9c19ad1171f8ec8da41c5b3f78 Mon Sep 17 00:00:00 2001 From: Fabrizzio Araya <37148755+fabrizzio-dotCMS@users.noreply.github.com> Date: Mon, 11 Mar 2024 09:01:15 -0600 Subject: [PATCH] bug(CLI) fix files pull with limited perms Refs: #27386 (#27897) * #27386 fix files pull with limited perms * #27386 adjusting test --- .../task/RemoteFolderTraversalTask.java | 24 +++++++++++++++--- .../api/client/pull/file/FilePullHandler.java | 22 ++++++++-------- .../api/client/files/PullServiceIT.java | 25 +++++++++++++------ 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/tools/dotcms-cli/cli/src/main/java/com/dotcms/api/client/files/traversal/task/RemoteFolderTraversalTask.java b/tools/dotcms-cli/cli/src/main/java/com/dotcms/api/client/files/traversal/task/RemoteFolderTraversalTask.java index c2ea39dc2848..e36bf6c1ddf2 100644 --- a/tools/dotcms-cli/cli/src/main/java/com/dotcms/api/client/files/traversal/task/RemoteFolderTraversalTask.java +++ b/tools/dotcms-cli/cli/src/main/java/com/dotcms/api/client/files/traversal/task/RemoteFolderTraversalTask.java @@ -191,10 +191,8 @@ private CompletableFuture processSubFolders( task::compute, executor ).thenCompose(Function.identity()); futures.add(future); - } catch (TraversalTaskException e) { - throw e; } catch (Exception e) { - throw new TraversalTaskException(e.getMessage(), e); + handleException(errors, e); } } else { @@ -335,4 +333,24 @@ private FolderView retrieveFolderInformation(final String siteName, final String } } + /** + * Handles an exception that occurred during the execution of a traversal task. + * + * @param errors The list of exceptions to which the error should be added. + * @param e The exception that occurred. + */ + private void handleException(List errors, Exception e) { + + if (traversalTaskParams.failFast()) { + + if (e instanceof TraversalTaskException) { + throw (TraversalTaskException) e; + } + + throw new TraversalTaskException(e.getMessage(), e); + } else { + errors.add(e); + } + } + } diff --git a/tools/dotcms-cli/cli/src/main/java/com/dotcms/api/client/pull/file/FilePullHandler.java b/tools/dotcms-cli/cli/src/main/java/com/dotcms/api/client/pull/file/FilePullHandler.java index 5f8abb6f3bb1..05d5faeed2f8 100644 --- a/tools/dotcms-cli/cli/src/main/java/com/dotcms/api/client/pull/file/FilePullHandler.java +++ b/tools/dotcms-cli/cli/src/main/java/com/dotcms/api/client/pull/file/FilePullHandler.java @@ -97,19 +97,17 @@ public int pull(List contents, PullOptions pullOptions, OutputOptionMixin output) throws ExecutionException, InterruptedException { - //Now we're going to partition the list of results into two lists: - //The first one will contain the results that have exceptions and the second one will contain the results that don't have exceptions - final Map> partitioned = contents.stream() - .collect(Collectors.partitioningBy(pojo -> !pojo.exceptions().isEmpty())); - //Inform the user about any errors that could have occurred during the traversal process - final List failed = partitioned.get(true); + //Collect all exceptions from the returned contents + final List allExceptions = contents.stream().map(FileTraverseResult::exceptions) + .flatMap(List::stream).collect(Collectors.toList()); + + //Any failed TreeNode will not be present + //So no need to separate the results + //Save the error code for the traversal process. This will be used to determine the exit code of the command if greater than 0 (zero) - int errorCode = handleExceptions(failed.stream().map(FileTraverseResult::exceptions).flatMap(List::stream).collect(Collectors.toList()), output); + int errorCode = handleExceptions(allExceptions, output); - //The second list will contain the results that don't have exceptions - //We assume that the traversal process was successful for these results - final List success = partitioned.get(false); boolean preserve = false; boolean includeEmptyFolders = false; @@ -121,9 +119,9 @@ public int pull(List contents, getOrDefault(INCLUDE_EMPTY_FOLDERS, false); } - output.info(startPullingHeader(success)); + output.info(startPullingHeader(contents)); - for (final var content : success) { + for (final var content : contents) { var errors = pullTree( content, diff --git a/tools/dotcms-cli/cli/src/test/java/com/dotcms/api/client/files/PullServiceIT.java b/tools/dotcms-cli/cli/src/test/java/com/dotcms/api/client/files/PullServiceIT.java index 95c758df23eb..de807a7887b3 100644 --- a/tools/dotcms-cli/cli/src/test/java/com/dotcms/api/client/files/PullServiceIT.java +++ b/tools/dotcms-cli/cli/src/test/java/com/dotcms/api/client/files/PullServiceIT.java @@ -836,6 +836,17 @@ void Test_Handle_Early_Thrown_Exception_Expect_SOFTWARE_ExitCode() throws IOExce final var testSiteName = String.format("non-existent-site-%s", UUID.randomUUID()); final var folderPath = String.format("//%s", testSiteName); OutputOptionMixin outputOptions = new MockOutputOptionMixin(); + FileFetcher mockedProvider = Mockito.mock(FileFetcher.class); + //But here is where the real test lies. We are going to return a List of FileTraverseResult with multiple exceptions + //They all should be logged and the first one should be the one that triggers the exit code + when(mockedProvider.fetchByKey(anyString(), anyBoolean(),any())).thenReturn( + FileTraverseResult.builder().tree(new TreeNode(FolderView.builder().host(testSiteName).path("/test").name("name").build())).exceptions( + List.of( + new UnauthorizedException(), + new TraversalTaskException("Unexpected Error Traversing folders ") + )).build() + ); + var tempFolder = filesTestHelper.createTempFolder(); var workspace = workspaceManager.getOrCreate(tempFolder); try { @@ -849,7 +860,7 @@ void Test_Handle_Early_Thrown_Exception_Expect_SOFTWARE_ExitCode() throws IOExce maxRetryAttempts(0). build(), outputOptions, - fileProvider, + mockedProvider, filePullHandler ); } catch (Exception e) { @@ -879,17 +890,15 @@ void Test_Handle_Multiple_Thrown_Exceptions() throws IOException { MockOutputOptionMixin outputOptions = new MockOutputOptionMixin(); FileFetcher mockedProvider = Mockito.mock(FileFetcher.class); - - //But here is where the real test lies. We are going to return a List of FileTraverseResult with multiple exceptions //They all should be logged and the first one should be the one that triggers the exit code when(mockedProvider.fetch(anyBoolean(), any())).thenReturn( List.of( - FileTraverseResult.builder().tree(Optional.empty()).exceptions(List.of(new UnauthorizedException())).build(), - FileTraverseResult.builder().tree(Optional.empty()).exceptions(List.of(new TraversalTaskException("Unexpected Error Traversing folders "))).build(), - FileTraverseResult.builder().tree( - new TreeNode(FolderView.builder().host(testSiteName).path("/test").name("name").build()) - ).exceptions(List.of()).build() + FileTraverseResult.builder().tree(new TreeNode(FolderView.builder().host(testSiteName).path("/test").name("name").build())).exceptions( + List.of( + new UnauthorizedException(), + new TraversalTaskException("Unexpected Error Traversing folders ") + )).build() )); //We're going to use PullHandler but this time we do not specify the contentKey so the fetch method gets called