From a35673d44f80a43167fdf40010eaf282c5170984 Mon Sep 17 00:00:00 2001 From: mohit-singh4 <158050587+mohit-singh4@users.noreply.github.com> Date: Fri, 8 Nov 2024 15:37:27 +0530 Subject: [PATCH] [MNT-24127] Added Endpoint To Calculate Folder Size (#2709) * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [MNT-24127] Added Endpoint to Calculate Size of the folder with Integration Test Cases [ags][tas] * [feature/MNT-24127] EndpointToCalculateFolderSize * [feature/MNT-24127] EndpointToCalculateFolderSize * [feature/MNT-24127] EndpointToCalculateFolderSize * [feature/MNT-24127] EndpointToCalculateFolderSize * [feature/MNT-24127] EndpointToCalculateFolderSize * [feature/MNT-24127] EndpointToCalculateFolderSize * [feature/MNT-24127] EndpointToCalculateFolderSize * [feature/MNT-24127] Endpoint Added To Calculate Folder Size * [feature/MNT-24127] Endpoint Added To Calculate Folder Size * [feature/MNT-24127] Endpoint Added To Calculate Folder Size * [feature/MNT-24127] Endpoint Added To Calculate Folder Size * [feature/MNT-24127] Endpoint Added To Calculate Folder Size * [feature/MNT-24127] Endpoint Added To Calculate Folder Size * Some Optimization for NodeSize Calculation. * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added Endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoint to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding performance test case * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoints implementation to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoints implementation to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoints implementation to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Added endpoints implementation to calculate folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding endpoints to calculate and retrieve folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding endpoints to calculate and retrieve folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding endpoints to calculate and retrieve folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding endpoints to calculate and retrieve folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding endpoints to calculate and retrieve folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Adding endpoints to calculate and retrieve folder size * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * Changes as per the Relationship Api Framework implementation. * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Updated endpoints flow to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments related to calculate and retrieve folder size details * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments and refactoring files * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments and refactoring files * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments and refactoring files * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments and refactoring files * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments regarding Integeration testcases * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments regarding Integeration testcases * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Addressing review comments * [feature/MNT-24127-EndpointToCalculateFolderSize] Creating Integeration test cases * [feature/MNT-24127-EndpointToCalculateFolderSize] Creating Integeration test cases * [feature/MNT-24127-EndpointToCalculateFolderSize] Creating Integeration test cases * [feature/MNT-24127-EndpointToCalculateFolderSize] Creating Integeration test cases * [feature/MNT-24127-EndpointToCalculateFolderSize] Creating Integeration test cases * [feature/MNT-24127-EndpointToCalculateFolderSize] Creating Integeration test cases * [feature/MNT-24127-EndpointToCalculateFolderSize] Some Changes in NodeSizeDetailsTests * [feature/MNT-24127-EndpointToCalculateFolderSize] Some Changes in NodeSizeDetailsTests * [feature/MNT-24127-EndpointToCalculateFolderSize] Some Changes in NodeSizeDetailsTests * [feature/MNT-24127-EndpointToCalculateFolderSize] Some Changes in NodeSizeDetailsTests * [feature/MNT-24127-EndpointToCalculateFolderSize] Checking size-details feature for solr6 subsystem * [feature/MNT-24127-EndpointToCalculateFolderSize] Checking size-details feature for solr6 subsystem --------- Co-authored-by: kshah Co-authored-by: mohit-singh4 Co-authored-by: kshah --- .secrets.baseline | 6 +- .../rest/model/RestSizeDetailsModel.java | 141 ++++ .../java/org/alfresco/rest/requests/Node.java | 214 ++++--- .../rest/nodes/NodeSizeDetailsTests.java | 72 +++ .../testdata/sampleLargeContent.txt | 600 ++++++++++++++++++ .../org/alfresco/rest/api/SizeDetails.java | 36 ++ .../rest/api/impl/SizeDetailsImpl.java | 119 ++++ .../api/nodes/NodeSizeDetailsRelation.java | 84 +++ .../alfresco/public-rest-context.xml | 23 + .../org/alfresco/AppContext02TestSuite.java | 14 +- .../org/alfresco/AppContext04TestSuite.java | 24 +- .../rest/api/impl/SizeDetailsImplTest.java | 107 ++++ .../rest/api/tests/AbstractBaseApiTest.java | 367 ++++++----- .../sizedetails/NodeSizeDetailsService.java | 41 ++ .../NodeSizeDetailsServiceImpl.java | 400 ++++++++++++ .../main/resources/alfresco/cache-context.xml | 6 + .../main/resources/alfresco/caches.properties | 9 + .../alfresco/node-services-context.xml | 38 +- .../resources/alfresco/repository.properties | 9 + 19 files changed, 2017 insertions(+), 293 deletions(-) create mode 100644 packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestSizeDetailsModel.java create mode 100644 packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/nodes/NodeSizeDetailsTests.java create mode 100644 packaging/tests/tas-restapi/src/test/resources/shared-resources/testdata/sampleLargeContent.txt create mode 100644 remote-api/src/main/java/org/alfresco/rest/api/SizeDetails.java create mode 100644 remote-api/src/main/java/org/alfresco/rest/api/impl/SizeDetailsImpl.java create mode 100644 remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeSizeDetailsRelation.java create mode 100644 remote-api/src/test/java/org/alfresco/rest/api/impl/SizeDetailsImplTest.java create mode 100644 repository/src/main/java/org/alfresco/repo/node/sizedetails/NodeSizeDetailsService.java create mode 100644 repository/src/main/java/org/alfresco/repo/node/sizedetails/NodeSizeDetailsServiceImpl.java diff --git a/.secrets.baseline b/.secrets.baseline index 4f2c57dff6..42456c42ab 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -731,7 +731,7 @@ "filename": "remote-api/src/test/java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java", "hashed_secret": "d033e22ae348aeb5660fc2140aec35850c4da997", "is_verified": false, - "line_number": 120, + "line_number": 111, "is_secret": false } ], @@ -1888,5 +1888,5 @@ } ] }, - "generated_at": "2024-10-02T10:18:47Z" -} + "generated_at": "2024-10-09T09:32:52Z" +} \ No newline at end of file diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestSizeDetailsModel.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestSizeDetailsModel.java new file mode 100644 index 0000000000..60b0d296f9 --- /dev/null +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/model/RestSizeDetailsModel.java @@ -0,0 +1,141 @@ +/*- + * #%L + * alfresco-tas-restapi + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.model; + +import java.util.Date; +import java.util.Objects; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import org.alfresco.rest.core.IRestModel; +import org.alfresco.utility.model.TestModel; + +public class RestSizeDetailsModel extends TestModel implements IRestModel +{ + @JsonProperty(value = "entry") + RestSizeDetailsModel model; + + private String id; + private Long sizeInBytes; + private Date calculatedAt; + private Integer numberOfFiles; + private String jobId; + private STATUS status; + + public enum STATUS + { + NOT_INITIATED, PENDING, IN_PROGRESS, COMPLETED, FAILED + } + + public String getId() + { + return id; + } + + public void setId(String id) + { + this.id = id; + } + + public Long getSizeInBytes() + { + return sizeInBytes; + } + + public void setSizeInBytes(Long sizeInBytes) + { + this.sizeInBytes = sizeInBytes; + } + + public Date getCalculatedAt() + { + return calculatedAt; + } + + public void setCalculatedAt(Date calculatedAt) + { + this.calculatedAt = calculatedAt; + } + + public Integer getNumberOfFiles() + { + return numberOfFiles; + } + + public void setNumberOfFiles(Integer numberOfFiles) + { + this.numberOfFiles = numberOfFiles; + } + + public String getJobId() + { + return jobId; + } + + public void setJobId(String jobId) + { + this.jobId = jobId; + } + + public STATUS getStatus() + { + return status; + } + + public void setStatus(STATUS status) + { + this.status = status; + } + + @Override + public RestSizeDetailsModel onModel() + { + return model; + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + RestSizeDetailsModel that = (RestSizeDetailsModel) o; + return Objects.equals(id, that.id) && Objects.equals(sizeInBytes, that.sizeInBytes) && Objects.equals( + calculatedAt, that.calculatedAt) && Objects.equals(numberOfFiles, that.numberOfFiles) + && Objects.equals(jobId, that.jobId) && status == that.status; + } + + @Override + public int hashCode() + { + return Objects.hash(id, sizeInBytes, calculatedAt, numberOfFiles, jobId, status); + } +} diff --git a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Node.java b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Node.java index c45164d8b3..ef1d0decda 100644 --- a/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Node.java +++ b/packaging/tests/tas-restapi/src/main/java/org/alfresco/rest/requests/Node.java @@ -2,7 +2,7 @@ * #%L * alfresco-tas-restapi * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * Copyright (C) 2005 - 2024 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -26,59 +26,34 @@ package org.alfresco.rest.requests; -import static org.alfresco.rest.core.JsonBodyGenerator.arrayToJson; -import static org.alfresco.rest.requests.RuleSettings.IS_INHERITANCE_ENABLED; import static org.springframework.http.HttpMethod.PUT; -import jakarta.json.JsonArrayBuilder; +import static org.alfresco.rest.core.JsonBodyGenerator.arrayToJson; +import static org.alfresco.rest.requests.RuleSettings.IS_INHERITANCE_ENABLED; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.List; import java.util.stream.Stream; +import jakarta.json.JsonArrayBuilder; import io.restassured.http.ContentType; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.testng.reporters.Files; + import org.alfresco.rest.core.JsonBodyGenerator; import org.alfresco.rest.core.RestRequest; import org.alfresco.rest.core.RestResponse; import org.alfresco.rest.core.RestWrapper; import org.alfresco.rest.exception.JsonToModelConversionException; -import org.alfresco.rest.model.RestActionDefinitionModelsCollection; -import org.alfresco.rest.model.RestCategoryLinkBodyModel; -import org.alfresco.rest.model.RestCategoryModel; -import org.alfresco.rest.model.RestCategoryModelsCollection; -import org.alfresco.rest.model.RestCommentModel; -import org.alfresco.rest.model.RestCommentModelsCollection; -import org.alfresco.rest.model.RestNodeAssocTargetModel; -import org.alfresco.rest.model.RestNodeAssociationModel; -import org.alfresco.rest.model.RestNodeAssociationModelCollection; -import org.alfresco.rest.model.RestNodeAssociationTypeModel; -import org.alfresco.rest.model.RestNodeBodyModel; -import org.alfresco.rest.model.RestNodeBodyMoveCopyModel; -import org.alfresco.rest.model.RestNodeChildAssocModelCollection; -import org.alfresco.rest.model.RestNodeChildAssociationModel; -import org.alfresco.rest.model.RestNodeModel; -import org.alfresco.rest.model.RestNodeModelsCollection; -import org.alfresco.rest.model.RestRatingModel; -import org.alfresco.rest.model.RestRatingModelsCollection; -import org.alfresco.rest.model.RestRenditionInfoModel; -import org.alfresco.rest.model.RestRenditionInfoModelCollection; -import org.alfresco.rest.model.RestRuleExecutionModel; -import org.alfresco.rest.model.RestRuleSetLinkModel; -import org.alfresco.rest.model.RestRuleSetModel; -import org.alfresco.rest.model.RestRuleSetModelsCollection; -import org.alfresco.rest.model.RestTagModel; -import org.alfresco.rest.model.RestTagModelsCollection; -import org.alfresco.rest.model.RestVersionModel; -import org.alfresco.rest.model.RestVersionModelsCollection; +import org.alfresco.rest.model.*; import org.alfresco.rest.model.body.RestNodeLockBodyModel; import org.alfresco.rest.model.builder.NodesBuilder; import org.alfresco.utility.Utility; import org.alfresco.utility.model.RepoTestModel; -import org.apache.commons.lang3.StringUtils; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.testng.reporters.Files; /** * Declares all Rest API under the /nodes path @@ -294,6 +269,7 @@ public class Node extends ModelRequest /** * * Get fivestar rating of a document using GET call on "nodes/{nodeId}/ratings/{ratingId}" + * * @return */ public RestRatingModel getFiveStarRating() @@ -336,7 +312,6 @@ public class Node extends ModelRequest return restWrapper.processModels(RestTagModelsCollection.class, request); } - /** * Deletes a tag for a specific content node using DELETE call on nodes/{nodeId}/tags/{tagId} * @@ -381,6 +356,7 @@ public class Node extends ModelRequest * You need to specify first the multipart call {@link RestWrapper#usingMultipartFile(java.io.File)} * * usingMultipartFile(new File("your-local-file.txt")).withCoreAPI().usingNode(ContentModel.my()).createNode(); + * * @return */ public RestNodeModel createNode() @@ -415,7 +391,8 @@ public class Node extends ModelRequest /** * Create node rendition using POST call on '/nodes/{nodeId}/renditions' * - * @param renditionId id of rendition to be created + * @param renditionId + * id of rendition to be created * @return */ public void createNodeRendition(String renditionId) @@ -428,8 +405,10 @@ public class Node extends ModelRequest /** * Create node version rendition using POST call on '/nodes/{nodeId}/versions/{versionId}/renditions' * - * @param renditionId id of rendition to be created - * @param versionId version id of node + * @param renditionId + * id of rendition to be created + * @param versionId + * version id of node * @return */ public void createNodeVersionRendition(String renditionId, String versionId) @@ -442,10 +421,10 @@ public class Node extends ModelRequest } /** - * Check if specified rendition exists and if not - * create node rendition using POST call on '/nodes/{nodeId}/renditions' + * Check if specified rendition exists and if not create node rendition using POST call on '/nodes/{nodeId}/renditions' * - * @param renditionId id of rendition to be created + * @param renditionId + * id of rendition to be created * @return */ public void createNodeRenditionIfNotExists(String renditionId) @@ -460,9 +439,10 @@ public class Node extends ModelRequest } /** - * Get node rendition using GET call on '/nodes/{nodeId}/renditions/{renditionId} + * Get node rendition using GET call on '/nodes/{nodeId}/renditions/{renditionId} * - * @param renditionId id of rendition to be retrieved + * @param renditionId + * id of rendition to be retrieved * @return */ public RestRenditionInfoModel getNodeRendition(String renditionId) @@ -474,8 +454,10 @@ public class Node extends ModelRequest /** * Get node version rendition using GET call on '/nodes/{nodeId}/versions/{versionId}renditions/{renditionId} * - * @param renditionId id of rendition to be retrieved - * @param versionId versionId of the node + * @param renditionId + * id of rendition to be retrieved + * @param versionId + * versionId of the node * @return */ public RestRenditionInfoModel getNodeVersionRendition(String renditionId, String versionId) @@ -487,8 +469,7 @@ public class Node extends ModelRequest } /** - * Get node rendition using GET call on 'nodes/{nodeId}/renditions/{renditionId} Please note that it retries to get - * the renditions response several times because on the alfresco server the rendition can take a while to be created. + * Get node rendition using GET call on 'nodes/{nodeId}/renditions/{renditionId} Please note that it retries to get the renditions response several times because on the alfresco server the rendition can take a while to be created. * * @return */ @@ -510,8 +491,7 @@ public class Node extends ModelRequest } /** - * Get node version rendition using GET call on 'nodes/{nodeId}/versions/{versionId}/renditions/{renditionId} Please note that it retries to get - * the renditions response several times because on the alfresco server the rendition can take a while to be created. + * Get node version rendition using GET call on 'nodes/{nodeId}/versions/{versionId}/renditions/{renditionId} Please note that it retries to get the renditions response several times because on the alfresco server the rendition can take a while to be created. * * @return */ @@ -533,10 +513,7 @@ public class Node extends ModelRequest } /** - * Get node rendition content using GET call on - * 'nodes/{nodeId}/renditions/{renditionId}/content Please note that it - * retries to get the renditions response several times because on the - * alfresco server the rendition can take a while to be created. + * Get node rendition content using GET call on 'nodes/{nodeId}/renditions/{renditionId}/content Please note that it retries to get the renditions response several times because on the alfresco server the rendition can take a while to be created. * * @return */ @@ -546,7 +523,7 @@ public class Node extends ModelRequest renditionId); RestResponse response = restWrapper.process(request); int retry = 0; - //Multiplied by '8' because AI rendition test cases need more time (~30 seconds) - see ACS-2158 + // Multiplied by '8' because AI rendition test cases need more time (~30 seconds) - see ACS-2158 while (!Integer.valueOf(response.getStatusCode()).equals(HttpStatus.OK.value()) && retry < (8 * Utility.retryCountSeconds)) { Utility.waitToLoopTime(1); @@ -558,10 +535,7 @@ public class Node extends ModelRequest } /** - * Get node version rendition content using GET call on - * 'nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/content Please note that it - * retries to get the renditions response several times because on the - * alfresco server the rendition can take a while to be created. + * Get node version rendition content using GET call on 'nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/content Please note that it retries to get the renditions response several times because on the alfresco server the rendition can take a while to be created. * * @return */ @@ -582,8 +556,7 @@ public class Node extends ModelRequest } /** - * Get node rendition content using GET call on - * 'nodes/{nodeId}/renditions/{renditionId}/content + * Get node rendition content using GET call on 'nodes/{nodeId}/renditions/{renditionId}/content * * @return */ @@ -595,8 +568,7 @@ public class Node extends ModelRequest } /** - * Get node version rendition content using GET call on - * 'nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/content + * Get node version rendition content using GET call on 'nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/content * * @return */ @@ -608,8 +580,8 @@ public class Node extends ModelRequest } /** - * Get rendition information for available renditions for the node using GET call on - * 'nodes/{nodeId}/renditions' + * Get rendition information for available renditions for the node using GET call on 'nodes/{nodeId}/renditions' + * * @return */ public RestRenditionInfoModelCollection getNodeRenditionsInfo() @@ -620,8 +592,8 @@ public class Node extends ModelRequest } /** - * Get rendition information for available renditions for the node version using GET call on - * 'nodes/{nodeId}/versions/{versionId}/renditions' + * Get rendition information for available renditions for the node version using GET call on 'nodes/{nodeId}/versions/{versionId}/renditions' + * * @return */ public RestRenditionInfoModelCollection getNodeVersionRenditionsInfo(String versionId) @@ -631,11 +603,11 @@ public class Node extends ModelRequest return restWrapper.processModels(RestRenditionInfoModelCollection.class, request); } - /** * Delete the rendition identified by renditionId using DELETE call on "/nodes/{nodeId}/renditions/{renditionId}" * - * @param renditionId id of rendition to delete + * @param renditionId + * id of rendition to delete */ public void deleteNodeRendition(String renditionId) { @@ -657,7 +629,8 @@ public class Node extends ModelRequest /** * Move a node to a target folder * - * @param moveBody a {@link RestNodeBodyMoveCopyModel} containing at least the target parent id + * @param moveBody + * a {@link RestNodeBodyMoveCopyModel} containing at least the target parent id * @return the moved node's new information */ public RestNodeModel move(RestNodeBodyMoveCopyModel moveBody) @@ -669,7 +642,8 @@ public class Node extends ModelRequest /** * Copy a node to a target folder * - * @param copyBody a {@link RestNodeBodyMoveCopyModel} containing at least the target parent id + * @param copyBody + * a {@link RestNodeBodyMoveCopyModel} containing at least the target parent id * @return the moved node's new information */ public RestNodeModel copy(RestNodeBodyMoveCopyModel copyBody) @@ -679,7 +653,6 @@ public class Node extends ModelRequest return restWrapper.processModel(RestNodeModel.class, request); } - /** * Lock a specific node using POST call on "nodes/{nodeId}/lock" * @@ -747,8 +720,7 @@ public class Node extends ModelRequest } /** - * Delete a target for a specific node using DELETE call on - * nodes/{nodeId}/targets/{targetId} + * Delete a target for a specific node using DELETE call on nodes/{nodeId}/targets/{targetId} * * @param target */ @@ -830,7 +802,8 @@ public class Node extends ModelRequest /** * Creates a secondary child association using POST call to: 'nodes/{nodeId}/secondary-children'. * - * @param secondaryChild - node, which should become a secondary child + * @param secondaryChild + * - node, which should become a secondary child * @return a node's parent-child association */ public RestNodeChildAssociationModel addSecondaryChild(RepoTestModel secondaryChild) @@ -841,8 +814,10 @@ public class Node extends ModelRequest /** * Creates a secondary child association using POST call to: 'nodes/{nodeId}/secondary-children'. * - * @param associationType - type of secondary parent-child relationship association - * @param secondaryChild - node, which should become a secondary child + * @param associationType + * - type of secondary parent-child relationship association + * @param secondaryChild + * - node, which should become a secondary child * @return a node's parent-child association */ public RestNodeChildAssociationModel addSecondaryChild(String associationType, RepoTestModel secondaryChild) @@ -853,7 +828,8 @@ public class Node extends ModelRequest /** * Creates a secondary child association using POST call to: 'nodes/{nodeId}/secondary-children'. * - * @param secondaryChildAssociation - node's secondary parent-child association model + * @param secondaryChildAssociation + * - node's secondary parent-child association model * @return a node's parent-child association */ public RestNodeChildAssociationModel addSecondaryChild(RestNodeChildAssociationModel secondaryChildAssociation) @@ -865,7 +841,8 @@ public class Node extends ModelRequest /** * Creates a secondary children association using POST call to: 'nodes/{nodeId}/secondary-children'. * - * @param secondaryChildren - nodes, which should become secondary children + * @param secondaryChildren + * - nodes, which should become secondary children * @return a collection of node's parent-child associations */ public RestNodeChildAssocModelCollection addSecondaryChildren(RepoTestModel... secondaryChildren) @@ -876,21 +853,24 @@ public class Node extends ModelRequest /** * Creates a secondary children association using POST call to: 'nodes/{nodeId}/secondary-children'. * - * @param associationType - type of secondary parent-child relationship association - * @param secondaryChildren - nodes, which should become secondary children + * @param associationType + * - type of secondary parent-child relationship association + * @param secondaryChildren + * - nodes, which should become secondary children * @return a collection of node's parent-child associations */ public RestNodeChildAssocModelCollection addSecondaryChildren(String associationType, RepoTestModel... secondaryChildren) { return addSecondaryChildren(Stream.of(secondaryChildren) - .map(child -> new RestNodeChildAssociationModel(child.getNodeRef(), associationType)) - .toArray(RestNodeChildAssociationModel[]::new)); + .map(child -> new RestNodeChildAssociationModel(child.getNodeRef(), associationType)) + .toArray(RestNodeChildAssociationModel[]::new)); } /** * Creates a secondary children association using POST call to: 'nodes/{nodeId}/secondary-children'. * - * @param secondaryChildrenAssociations - node's secondary parent-child association models + * @param secondaryChildrenAssociations + * - node's secondary parent-child association models * @return a collection of node's parent-child associations */ public RestNodeChildAssocModelCollection addSecondaryChildren(RestNodeChildAssociationModel... secondaryChildrenAssociations) @@ -903,7 +883,8 @@ public class Node extends ModelRequest /** * Removes secondary child association using DELETE call 'nodes/{nodeId}/secondary-children/{childId}'. * - * @param secondaryChild - node, which should NOT be a secondary child anymore + * @param secondaryChild + * - node, which should NOT be a secondary child anymore */ public void removeSecondaryChild(RepoTestModel secondaryChild) { @@ -913,8 +894,10 @@ public class Node extends ModelRequest /** * Removes secondary child association using DELETE call 'nodes/{nodeId}/secondary-children/{childId}'. * - * @param associationType - type of secondary parent-child relationship association - * @param secondaryChild - node, which should NOT be a secondary child anymore + * @param associationType + * - type of secondary parent-child relationship association + * @param secondaryChild + * - node, which should NOT be a secondary child anymore */ public void removeSecondaryChild(String associationType, RepoTestModel secondaryChild) { @@ -932,13 +915,12 @@ public class Node extends ModelRequest /** * Removes secondary child association using DELETE call 'nodes/{nodeId}/secondary-children/{childId}'. * - * @param secondaryChildAssociation - node's secondary parent-child association to remove + * @param secondaryChildAssociation + * - node's secondary parent-child association to remove */ public void removeSecondaryChild(RestNodeAssociationModel secondaryChildAssociation) { - String parameters = StringUtils.isNotEmpty(secondaryChildAssociation.getAssociation().getAssocType()) ? - "assocType=" + secondaryChildAssociation.getAssociation().getAssocType() + "&" + restWrapper.getParameters() : - restWrapper.getParameters(); + String parameters = StringUtils.isNotEmpty(secondaryChildAssociation.getAssociation().getAssocType()) ? "assocType=" + secondaryChildAssociation.getAssociation().getAssocType() + "&" + restWrapper.getParameters() : restWrapper.getParameters(); RestRequest request = RestRequest.simpleRequest(HttpMethod.DELETE, "nodes/{nodeId}/secondary-children/{childId}?{parameters}", repoModel.getNodeRef(), secondaryChildAssociation.getId(), parameters); restWrapper.processEmptyModel(request); } @@ -1013,7 +995,6 @@ public class Node extends ModelRequest deleteNode(nodeModel.getId()); } - /** * Delete a specific node using DELETE call on nodes/{nodeId} * @@ -1035,6 +1016,7 @@ public class Node extends ModelRequest /** * Get Direct Access URL for a node + * * @param postBody * @return */ @@ -1055,6 +1037,7 @@ public class Node extends ModelRequest /** * Get Direct Access URL for a specific node rendition E.g "pdf" + * * @param renditionId * @return */ @@ -1067,6 +1050,7 @@ public class Node extends ModelRequest /** * Get Direct Access URL for a specific node version. E.g "1.1" + * * @param versionId * @return */ @@ -1079,6 +1063,7 @@ public class Node extends ModelRequest /** * Get Direct Access URL for a specific node version rendition. E.g ("1.1", "pdf") + * * @param versionId * @param renditionId * @return @@ -1132,7 +1117,8 @@ public class Node extends ModelRequest /** * Get the specified rule set from a folder. * - * @param ruleSetId The id of the rule set. + * @param ruleSetId + * The id of the rule set. * @return The specified rule set. */ public RestRuleSetModel getRuleSet(String ruleSetId) @@ -1145,7 +1131,8 @@ public class Node extends ModelRequest /** * Update a rule set on this folder - for example to reorder the rules. * - * @param ruleSet The updated rule set. + * @param ruleSet + * The updated rule set. * @return The updated rule set returned by the server. */ public RestRuleSetModel updateRuleSet(RestRuleSetModel ruleSet) @@ -1188,7 +1175,8 @@ public class Node extends ModelRequest /** * Try to delete a ruleset link performing a DELETE call on "/nodes/{folderNodeId}/rule-set-links/{rulesetId}" * - * @param ruleSetId the id of the ruleset to be unlinked from the folder + * @param ruleSetId + * the id of the ruleset to be unlinked from the folder * @return */ public void unlinkRuleSet(String ruleSetId) @@ -1200,7 +1188,8 @@ public class Node extends ModelRequest /** * Trigger rules on a folder performing POST call on "/nodes/{folderNodeId}/rule-executions" * - * @param body - rules execution request + * @param body + * - rules execution request * @return execution result */ public RestRuleExecutionModel executeRules(RestRuleExecutionModel body) @@ -1223,7 +1212,8 @@ public class Node extends ModelRequest /** * Link content to category performing POST call on "/nodes/{nodeId}/category-links" * - * @param categoryLink - contains category ID + * @param categoryLink + * - contains category ID * @return linked to category */ public RestCategoryModel linkToCategory(RestCategoryLinkBodyModel categoryLink) @@ -1235,7 +1225,8 @@ public class Node extends ModelRequest /** * Link content to many categories performing POST call on "/nodes/{nodeId}/category-links" * - * @param categoryLinks - contains categories IDs + * @param categoryLinks + * - contains categories IDs * @return linked to categories */ public RestCategoryModelsCollection linkToCategories(List categoryLinks) @@ -1247,11 +1238,34 @@ public class Node extends ModelRequest /** * Unlink content from a category performing a DELETE call on "nodes/{nodeId}/category-links/{categoryId}" * - * @param categoryId the id of the category to be unlinked from content + * @param categoryId + * the id of the category to be unlinked from content */ public void unlinkFromCategory(String categoryId) { RestRequest request = RestRequest.simpleRequest(HttpMethod.DELETE, "nodes/{nodeId}/category-links/{categoryId}", repoModel.getNodeRef(), categoryId); restWrapper.processEmptyModel(request); } + + /** + * In order to retrieve folder size details using POST call on "nodes/{nodeId}/size-details" + * + * @return + */ + public RestSizeDetailsModel executeSizeDetails() + { + RestRequest request = RestRequest.simpleRequest(HttpMethod.POST, "nodes/{nodeId}/size-details", repoModel.getNodeRef()); + return restWrapper.processModel(RestSizeDetailsModel.class, request); + } + + /** + * Getting Folder size details using GET call on "nodes/{nodeId}/size-details/{jobId}" + * + * @return + */ + public RestSizeDetailsModel getSizeDetails(String jobId) + { + RestRequest request = RestRequest.simpleRequest(HttpMethod.GET, "nodes/{nodeId}/size-details/{jobId}", repoModel.getNodeRef(), jobId); + return restWrapper.processModel(RestSizeDetailsModel.class, request); + } } diff --git a/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/nodes/NodeSizeDetailsTests.java b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/nodes/NodeSizeDetailsTests.java new file mode 100644 index 0000000000..e71c65fd14 --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/java/org/alfresco/rest/nodes/NodeSizeDetailsTests.java @@ -0,0 +1,72 @@ +package org.alfresco.rest.nodes; + +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.http.HttpStatus; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import org.alfresco.dataprep.CMISUtil.DocumentType; +import org.alfresco.rest.RestTest; +import org.alfresco.utility.model.FileModel; +import org.alfresco.utility.model.FolderModel; +import org.alfresco.utility.model.SiteModel; +import org.alfresco.utility.model.TestGroup; +import org.alfresco.utility.model.UserModel; +import org.alfresco.utility.testrail.ExecutionType; +import org.alfresco.utility.testrail.annotation.TestRail; + +public class NodeSizeDetailsTests extends RestTest +{ + private UserModel user1; + private SiteModel siteModel; + private FolderModel folder; + + @BeforeClass(alwaysRun = true) + public void dataPreparation() + { + user1 = dataUser.createRandomTestUser("User-1"); + siteModel = dataSite.usingUser(user1).createPublicRandomSite(); + folder = dataContent.usingUser(user1).usingSite(siteModel).createFolder(FolderModel.getRandomFolderModel()); + } + + /** + * + * Unauthenticated user not able to execute POST /nodes/{nodeId}/size-details: 401 STATUS CODE + */ + + @TestRail(section = {TestGroup.REST_API, TestGroup.NODES}, executionType = ExecutionType.SANITY) + @Test(groups = {TestGroup.REST_API, TestGroup.NODES, TestGroup.SANITY}) + public void unauthenticatedUserIsNotAbleGetSizeDetails() + { + restClient.authenticateUser(new UserModel("random user", "random password")); + restClient.withCoreAPI().usingNode(folder).executeSizeDetails(); + restClient.assertStatusCodeIs(HttpStatus.UNAUTHORIZED); + } + + /** + * Node Id Not Exist: 404 STATUS CODE + */ + + @TestRail(section = {TestGroup.REST_API, TestGroup.NODES}, executionType = ExecutionType.SANITY) + @Test(groups = {TestGroup.REST_API, TestGroup.NODES, TestGroup.SANITY}) + public void nodeIdNotExist() + { + folder.setNodeRef(RandomStringUtils.randomAlphanumeric(20)); + restClient.authenticateUser(user1).withCoreAPI().usingNode(folder).executeSizeDetails(); + restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND); + } + + /** + * + * Value of nodeId is invalid: 422 STATUS CODE + */ + + @TestRail(section = {TestGroup.REST_API, TestGroup.NODES}, executionType = ExecutionType.SANITY) + @Test(groups = {TestGroup.REST_API, TestGroup.NODES, TestGroup.SANITY}) + public void nodeIdNotValid() + { + FileModel document = dataContent.usingSite(siteModel).usingUser(user1).createContent(DocumentType.TEXT_PLAIN); + restClient.authenticateUser(user1).withCoreAPI().usingNode(document).executeSizeDetails(); + restClient.assertStatusCodeIs(HttpStatus.UNPROCESSABLE_ENTITY); + } +} diff --git a/packaging/tests/tas-restapi/src/test/resources/shared-resources/testdata/sampleLargeContent.txt b/packaging/tests/tas-restapi/src/test/resources/shared-resources/testdata/sampleLargeContent.txt new file mode 100644 index 0000000000..c002d4a7cf --- /dev/null +++ b/packaging/tests/tas-restapi/src/test/resources/shared-resources/testdata/sampleLargeContent.txt @@ -0,0 +1,600 @@ +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. +Sample text. \ No newline at end of file diff --git a/remote-api/src/main/java/org/alfresco/rest/api/SizeDetails.java b/remote-api/src/main/java/org/alfresco/rest/api/SizeDetails.java new file mode 100644 index 0000000000..b749706566 --- /dev/null +++ b/remote-api/src/main/java/org/alfresco/rest/api/SizeDetails.java @@ -0,0 +1,36 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api; + +import org.alfresco.repo.node.sizedetails.NodeSizeDetailsServiceImpl.NodeSizeDetails; + +public interface SizeDetails +{ + NodeSizeDetails generateNodeSizeDetailsRequest(String nodeId); + + NodeSizeDetails getNodeSizeDetails(String nodeId, String jobId); + +} diff --git a/remote-api/src/main/java/org/alfresco/rest/api/impl/SizeDetailsImpl.java b/remote-api/src/main/java/org/alfresco/rest/api/impl/SizeDetailsImpl.java new file mode 100644 index 0000000000..3acc0ea87d --- /dev/null +++ b/remote-api/src/main/java/org/alfresco/rest/api/impl/SizeDetailsImpl.java @@ -0,0 +1,119 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.impl; + +import java.util.Optional; + +import org.springframework.beans.factory.InitializingBean; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.node.sizedetails.NodeSizeDetailsService; +import org.alfresco.repo.node.sizedetails.NodeSizeDetailsServiceImpl.NodeSizeDetails; +import org.alfresco.repo.node.sizedetails.NodeSizeDetailsServiceImpl.NodeSizeDetails.STATUS; +import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.SizeDetails; +import org.alfresco.rest.framework.core.exceptions.InvalidNodeTypeException; +import org.alfresco.rest.framework.core.exceptions.NotFoundException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.GUID; +import org.alfresco.util.ParameterCheck; + +public class SizeDetailsImpl implements SizeDetails, InitializingBean +{ + private final Nodes nodes; + private final NodeSizeDetailsService nodeSizeDetailsService; + + public SizeDetailsImpl(Nodes nodes, NodeSizeDetailsService nodeSizeDetailsService) + { + this.nodes = nodes; + this.nodeSizeDetailsService = nodeSizeDetailsService; + } + + /** + * generateNodeSizeDetailsRequest : providing HTTP STATUS 202 with jobId. + */ + @Override + public NodeSizeDetails generateNodeSizeDetailsRequest(String nodeId) + { + NodeRef nodeRef = nodes.validateOrLookupNode(nodeId); + validateType(nodeRef); + + Optional nodeSizeDetails = nodeSizeDetailsService.getSizeDetails(nodeId); + String actionId = nodeSizeDetails.map(NodeSizeDetails::getJobId) + .orElseGet(() -> executeSizeDetails(nodeRef)); + + return new NodeSizeDetails(actionId); + } + + /** + * getNodeSizeDetails : providing HTTP STATUS 200 with NodeSizeDetails data from cache. + */ + @Override + public NodeSizeDetails getNodeSizeDetails(final String nodeId, final String jobId) + { + NodeRef nodeRef = nodes.validateOrLookupNode(nodeId); + validateType(nodeRef); + + Optional optionalDetails = nodeSizeDetailsService.getSizeDetails(nodeId); + return optionalDetails.map(details -> { + String cachedJobId = details.getJobId(); + if (cachedJobId != null && !jobId.equalsIgnoreCase(cachedJobId)) + { + throw new NotFoundException("jobId does not exist"); + } + return details; + }) + .orElseGet(() -> new NodeSizeDetails(nodeId, STATUS.NOT_INITIATED)); + } + + /** + * Executing Asynchronously. + */ + private String executeSizeDetails(NodeRef nodeRef) + { + String jobId = GUID.generate(); + NodeSizeDetails nodeSizeDetails = new NodeSizeDetails(nodeRef.getId(), jobId, STATUS.PENDING); + nodeSizeDetailsService.putSizeDetails(nodeRef.getId(), nodeSizeDetails); + nodeSizeDetailsService.invokeSizeDetailsExecutor(nodeRef, jobId); + return jobId; + } + + private void validateType(NodeRef nodeRef) throws InvalidNodeTypeException + { + if (!nodes.isSubClass(nodeRef, ContentModel.TYPE_FOLDER, false)) + { + throw new InvalidNodeTypeException("Node id " + nodeRef.getId() + " does not represent a folder."); + } + } + + @Override + public void afterPropertiesSet() throws Exception + { + ParameterCheck.mandatory("nodes", this.nodes); + ParameterCheck.mandatory("nodeSizeDetailsServiceImpl", this.nodeSizeDetailsService); + } + +} diff --git a/remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeSizeDetailsRelation.java b/remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeSizeDetailsRelation.java new file mode 100644 index 0000000000..3a6095c1b0 --- /dev/null +++ b/remote-api/src/main/java/org/alfresco/rest/api/nodes/NodeSizeDetailsRelation.java @@ -0,0 +1,84 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.rest.api.nodes; + +import java.util.List; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.extensions.webscripts.Status; + +import org.alfresco.repo.node.sizedetails.NodeSizeDetailsServiceImpl.NodeSizeDetails; +import org.alfresco.rest.api.SizeDetails; +import org.alfresco.rest.framework.WebApiDescription; +import org.alfresco.rest.framework.WebApiParam; +import org.alfresco.rest.framework.WebApiParameters; +import org.alfresco.rest.framework.core.ResourceParameter; +import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundException; +import org.alfresco.rest.framework.resource.RelationshipResource; +import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; +import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.alfresco.util.ParameterCheck; + +@RelationshipResource(name = "size-details", entityResource = NodesEntityResource.class, title = "Node Size Details") +public class NodeSizeDetailsRelation implements RelationshipResourceAction.ReadById, + RelationshipResourceAction.Create, InitializingBean +{ + + private SizeDetails sizeDetails; + + public void setSizeDetails(SizeDetails sizeDetails) + { + this.sizeDetails = sizeDetails; + } + + @Override + public void afterPropertiesSet() + { + ParameterCheck.mandatory("sizeDetails", this.sizeDetails); + } + + @WebApiDescription(title = "Create node-size details request", successStatus = Status.STATUS_ACCEPTED) + @WebApiParam(name = "nodeSizeEntity", title = "Node Size Details Request", + description = "Request for processing Node Size.", kind = ResourceParameter.KIND.HTTP_BODY_OBJECT, + allowMultiple = false) + @Override + public List create(String nodeId, List nodeSizeEntity, Parameters parameters) + { + return List.of(sizeDetails.generateNodeSizeDetailsRequest(nodeId)); + } + + @WebApiDescription(title = "Get Node Size Details", description = "Get the Node Size Details") + @WebApiParameters({@WebApiParam(name = "nodeId", title = "The unique id of the Node being addressed", + description = "A single Node id"), + @WebApiParam(name = "jobId", title = "Job Id to get the NodeSizeDetails", description = "JobId")}) + @Override + public NodeSizeDetails readById(String nodeId, String jobId, Parameters parameters) + throws RelationshipResourceNotFoundException + { + return sizeDetails.getNodeSizeDetails(nodeId, jobId); + } +} diff --git a/remote-api/src/main/resources/alfresco/public-rest-context.xml b/remote-api/src/main/resources/alfresco/public-rest-context.xml index a438e038a8..c054b3b9de 100644 --- a/remote-api/src/main/resources/alfresco/public-rest-context.xml +++ b/remote-api/src/main/resources/alfresco/public-rest-context.xml @@ -996,6 +996,25 @@ + + + + + + + + org.alfresco.rest.api.SizeDetails + + + + + + + + + + + @@ -1770,4 +1789,8 @@ + + + + diff --git a/remote-api/src/test/java/org/alfresco/AppContext02TestSuite.java b/remote-api/src/test/java/org/alfresco/AppContext02TestSuite.java index 4756d0958b..cf3fedb8b4 100644 --- a/remote-api/src/test/java/org/alfresco/AppContext02TestSuite.java +++ b/remote-api/src/test/java/org/alfresco/AppContext02TestSuite.java @@ -4,21 +4,21 @@ * %% * Copyright (C) 2005 - 2021 Alfresco Software Limited * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: - * + * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . * #L% @@ -71,7 +71,7 @@ import org.junit.runners.Suite; org.alfresco.rest.api.tests.TestPublicApiCaching.class, org.alfresco.rest.api.tests.TestUserPreferences.class, org.alfresco.rest.api.tests.WherePredicateApiTest.class, - org.alfresco.rest.api.tests.TestRemovePermissions.class, + org.alfresco.rest.api.tests.TestRemovePermissions.class, org.alfresco.rest.api.tests.TempOutputStreamTest.class, org.alfresco.rest.api.tests.BufferedResponseTest.class, org.alfresco.rest.workflow.api.tests.DeploymentWorkflowApiTest.class, diff --git a/remote-api/src/test/java/org/alfresco/AppContext04TestSuite.java b/remote-api/src/test/java/org/alfresco/AppContext04TestSuite.java index 2ffec4fc7b..eec9f3db37 100644 --- a/remote-api/src/test/java/org/alfresco/AppContext04TestSuite.java +++ b/remote-api/src/test/java/org/alfresco/AppContext04TestSuite.java @@ -2,23 +2,23 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2021 Alfresco Software Limited + * Copyright (C) 2005 - 2024 Alfresco Software Limited * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: - * + * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . * #L% @@ -36,6 +36,8 @@ import org.alfresco.util.testing.category.NonBuildTests; @RunWith(Categories.class) @Categories.ExcludeCategory({DBTests.class, NonBuildTests.class}) @Suite.SuiteClasses({ + // [classpath:alfresco/application-context.xml, classpath:alfresco/web-scripts-application-context-test.xml, + // classpath:alfresco/web-scripts-application-context.xml] org.alfresco.repo.web.scripts.quickshare.QuickShareRestApiTest.class, org.alfresco.repo.web.scripts.admin.AdminWebScriptTest.class, org.alfresco.repo.web.scripts.audit.AuditWebScriptTest.class, @@ -47,8 +49,7 @@ import org.alfresco.util.testing.category.NonBuildTests; org.alfresco.repo.web.scripts.forms.FormRestApiJsonPost_Test.class, org.alfresco.repo.web.scripts.groups.GroupsTest.class, org.alfresco.repo.web.scripts.invitation.InvitationWebScriptTest.class, - org.alfresco.repo.web.scripts.invite.InviteServiceTest.class, - org.alfresco.repo.web.scripts.LoginTest.class, + org.alfresco.repo.web.scripts.invite.InviteServiceTest.class, org.alfresco.repo.web.scripts.LoginTest.class, org.alfresco.repo.web.scripts.search.PersonSearchTest.class, org.alfresco.repo.web.scripts.person.PersonServiceTest.class, org.alfresco.repo.web.scripts.preference.PreferenceServiceTest.class, @@ -76,9 +77,8 @@ import org.alfresco.util.testing.category.NonBuildTests; org.alfresco.repo.web.scripts.node.NodeWebScripTest.class, org.alfresco.rest.api.impl.CommentsImplUnitTest.class, org.alfresco.rest.api.impl.DownloadsImplCheckArchiveStatusUnitTest.class, - org.alfresco.rest.api.impl.FavouritesImplUnitTest.class, - org.alfresco.rest.api.impl.RestApiDirectUrlConfigUnitTest.class -}) + org.alfresco.rest.api.impl.RestApiDirectUrlConfigUnitTest.class, + org.alfresco.rest.api.impl.SizeDetailsImplTest.class}) public class AppContext04TestSuite { public AppContext04TestSuite() diff --git a/remote-api/src/test/java/org/alfresco/rest/api/impl/SizeDetailsImplTest.java b/remote-api/src/test/java/org/alfresco/rest/api/impl/SizeDetailsImplTest.java new file mode 100644 index 0000000000..7e4b49e936 --- /dev/null +++ b/remote-api/src/test/java/org/alfresco/rest/api/impl/SizeDetailsImplTest.java @@ -0,0 +1,107 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.api.impl; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.Serializable; +import java.util.Optional; +import java.util.concurrent.ThreadPoolExecutor; + +import org.junit.Before; +import org.junit.Test; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.node.sizedetails.NodeSizeDetailsServiceImpl; +import org.alfresco.repo.node.sizedetails.NodeSizeDetailsServiceImpl.NodeSizeDetails; +import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.model.Node; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.QName; + +/** + * Unit tests for {@link SizeDetailsImpl} class. + */ +public class SizeDetailsImplTest +{ + private static final String NAMESPACE = "http://www.alfresco.org/test/NodeSizeDetailsTest"; + private static final QName TYPE_FOLDER = QName.createQName(NAMESPACE, "folder"); + private SizeDetailsImpl sizeDetailsImpl; + private Nodes nodes; + private NodeSizeDetailsServiceImpl nodeSizeDetailsServiceImpl; + private NodeSizeDetails nodeSizeDetails; + + @Before + public void setUp() throws Exception + { + nodes = mock(Nodes.class); + SearchService searchService = mock(SearchService.class); + nodeSizeDetailsServiceImpl = mock(NodeSizeDetailsServiceImpl.class); + ThreadPoolExecutor threadPoolExecutor = mock(ThreadPoolExecutor.class); + SimpleCache simpleCache = mock(SimpleCache.class); + nodeSizeDetails = mock(NodeSizeDetails.class); + + nodeSizeDetailsServiceImpl.setSearchService(searchService); + nodeSizeDetailsServiceImpl.setDefaultItems(1000); + nodeSizeDetailsServiceImpl.setSimpleCache(simpleCache); + verify(nodeSizeDetailsServiceImpl).setSimpleCache(simpleCache); + nodeSizeDetailsServiceImpl.setThreadPoolExecutor(threadPoolExecutor); + sizeDetailsImpl = new SizeDetailsImpl(nodes, nodeSizeDetailsServiceImpl); + } + + @Test + public void calculateNodeSizeDetails() + { + String nodeName = "folderNode"; + String nodeId = "node-id"; + String jobId = "job-id"; + NodeRef nodeRef = new NodeRef("protocol", "identifier", nodeId); + + Node node = new Node(); + node.setIsFolder(true); + node.setNodeRef(nodeRef); + node.setName(nodeName); + node.setNodeType(TYPE_FOLDER.getLocalName()); + node.setNodeId(nodeRef.getId()); + + when(nodes.validateOrLookupNode(nodeId)).thenReturn(nodeRef); + when(nodes.isSubClass(nodeRef, ContentModel.TYPE_FOLDER, false)).thenReturn(true); + when(nodeSizeDetailsServiceImpl.getSizeDetails(nodeId)).thenReturn(Optional.ofNullable(nodeSizeDetails)); + + NodeSizeDetails requestSizeDetails = sizeDetailsImpl.generateNodeSizeDetailsRequest(nodeId); + assertNotNull("After executing POST/size-details, it will provide with 202 status code", requestSizeDetails); + + NodeSizeDetails nodeSizeDetails = sizeDetailsImpl.getNodeSizeDetails(nodeId, jobId); + assertNotNull("After executing GET/size-details, it will provide with 200 status code", nodeSizeDetails); + + } + +} diff --git a/remote-api/src/test/java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java b/remote-api/src/test/java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java index 4c233ffea9..dce7aea3f8 100644 --- a/remote-api/src/test/java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java +++ b/remote-api/src/test/java/org/alfresco/rest/api/tests/AbstractBaseApiTest.java @@ -2,50 +2,68 @@ * #%L * Alfresco Remote API * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * Copyright (C) 2005 - 2024 Alfresco Software Limited * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: - * + * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. - * + * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . * #L% */ package org.alfresco.rest.api.tests; -import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig; -import org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig; -import org.alfresco.rest.api.tests.client.PublicApiHttpClient; -import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsString; -import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsStringNonNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsString; +import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsStringNonNull; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.json.simple.JSONObject; +import org.junit.After; +import org.junit.Before; +import org.junit.experimental.categories.Category; +import org.springframework.util.ResourceUtils; + +import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig; import org.alfresco.rest.api.model.Site; import org.alfresco.rest.api.nodes.NodesEntityResource; import org.alfresco.rest.api.tests.RepoService.TestNetwork; import org.alfresco.rest.api.tests.client.HttpResponse; import org.alfresco.rest.api.tests.client.PublicApiClient; +import org.alfresco.rest.api.tests.client.PublicApiHttpClient; import org.alfresco.rest.api.tests.client.PublicApiHttpClient.BinaryPayload; import org.alfresco.rest.api.tests.client.PublicApiHttpClient.RequestBuilder; import org.alfresco.rest.api.tests.client.RequestContext; @@ -65,26 +83,10 @@ import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteVisibility; import org.alfresco.util.TempFileProvider; import org.alfresco.util.testing.category.LuceneTests; -import org.json.simple.JSONObject; -import org.junit.After; -import org.junit.Before; -import org.junit.experimental.categories.Category; -import org.springframework.util.ResourceUtils; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.HashMap; /** * Generic methods for calling the Api (originally taken and adapted from BaseCustomModelApiTest) - * + * * @author Jamal Kaabi-Mofrad * @author janv * @author gethin @@ -94,54 +96,40 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi { public static final String LAST_MODIFIED_HEADER = "Last-Modified"; public static final String IF_MODIFIED_SINCE_HEADER = "If-Modified-Since"; - - private static final String RESOURCE_PREFIX = "publicapi/upload/"; - protected static final String URL_NODES = "nodes"; protected static final String URL_DELETED_NODES = "deleted-nodes"; - protected static final String URL_RENDITIONS = "renditions"; protected static final String URL_VERSIONS = "versions"; - - private static final String URL_CHILDREN = "children"; - private static final String URL_CONTENT = "content"; - protected static final String TYPE_CM_FOLDER = "cm:folder"; protected static final String TYPE_CM_CONTENT = "cm:content"; protected static final String TYPE_CM_OBJECT = "cm:cmobject"; - protected static final String ASPECT_CM_PREFERENCES = "cm:preferences"; protected static final String ASSOC_TYPE_CM_PREFERENCE_IMAGE = "cm:preferenceImage"; - protected static final String ASSOC_TYPE_CM_CONTAINS = "cm:contains"; - // TODO improve admin-related tests, including ability to override default admin un/pw protected static final String DEFAULT_ADMIN = "admin"; protected static final String DEFAULT_ADMIN_PWD = "admin"; - + protected static final long PAUSE_TIME = 5000; // millisecond + protected static final int MAX_RETRY = 20; + private static final String RESOURCE_PREFIX = "publicapi/upload/"; + private static final String URL_CHILDREN = "children"; + private static final String URL_CONTENT = "content"; + private static final String URL_CALCULATEFOLDERSIZE = "size-details"; + private static final String REQUEST_DIRECT_ACCESS_URL = "request-direct-access-url"; // network1 with user1, user2 and a testsite1 protected static TestNetwork networkOne; - protected static String user1; // user1 from network1 protected static String user2; // user2 from network1 - // network admin (or default super admin, if not running within a tenant/network) protected static String networkAdmin = DEFAULT_ADMIN; - protected static String tSiteId; protected static String tDocLibNodeId; - - protected static List users = new ArrayList<>(); - protected static JacksonUtil jacksonUtil; protected static MutableAuthenticationService authenticationService; protected static PersonService personService; + protected final String RUNID = System.currentTimeMillis() + ""; - protected final String RUNID = System.currentTimeMillis()+""; - - private static final String REQUEST_DIRECT_ACCESS_URL = "request-direct-access-url"; - @Override @Before public void setup() throws Exception @@ -153,22 +141,22 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi // note: populateTestData/createTestData will be called (which currently creates 2 tenants, 9 users per tenant, 10 sites per tenant, ...) networkOne = getTestFixture().getRandomNetwork(); } - - //userOneN1 = networkN1.createUser(); - //userTwoN1 = networkN1.createUser(); + + // userOneN1 = networkN1.createUser(); + // userTwoN1 = networkN1.createUser(); String tenantDomain = networkOne.getId(); - - if (! TenantService.DEFAULT_DOMAIN.equals(tenantDomain)) + + if (!TenantService.DEFAULT_DOMAIN.equals(tenantDomain)) { - networkAdmin = DEFAULT_ADMIN+"@"+tenantDomain; + networkAdmin = DEFAULT_ADMIN + "@" + tenantDomain; } // to enable admin access via test calls - eg. via PublicApiClient -> AbstractTestApi -> findUserByUserName getOrCreateUser(networkAdmin, "admin", networkOne); - + setRequestContext(networkOne.getId(), networkAdmin, DEFAULT_ADMIN_PWD); - + // note: createUser currently relies on repoService user1 = createUser("user1-" + RUNID, "user1Password", networkOne); user2 = createUser("user2-" + RUNID, "user2Password", networkOne); @@ -180,7 +168,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi users.add(user2); setRequestContext(networkOne.getId(), user1, null); - + tSiteId = createSite("TestSite A - " + RUNID, SiteVisibility.PRIVATE).getId(); tDocLibNodeId = getSiteContainerNodeId(tSiteId, "documentLibrary"); @@ -197,11 +185,10 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi } setRequestContext(networkAdmin); - + for (final String username : users) { - transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() { @Override public Void execute() throws Throwable { @@ -210,7 +197,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi } }); } - + users.clear(); AuthenticationUtil.clearCurrentSecurityContext(); setRequestContext(null); @@ -233,7 +220,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi protected String getRequestArchivedRenditonContentDirectUrl(String nodeId, String renditionID) { - return URL_DELETED_NODES + "/" + nodeId + "/" + URL_RENDITIONS + "/" + renditionID + "/" + REQUEST_DIRECT_ACCESS_URL; + return URL_DELETED_NODES + "/" + nodeId + "/" + URL_RENDITIONS + "/" + renditionID + "/" + + REQUEST_DIRECT_ACCESS_URL; } protected String getRequestRenditionDirectAccessUrl(String nodeId, String renditionID) @@ -246,7 +234,6 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return URL_NODES + "/" + nodeId + "/" + URL_VERSIONS + "/" + versionId + "/" + REQUEST_DIRECT_ACCESS_URL; } - /** * The api scope. either public or private * @@ -262,10 +249,10 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - protected HttpResponse post(String url, byte[] body, Map params, Map headers, String apiName, String contentType, int expectedStatus) throws Exception + protected HttpResponse post(String url, byte[] body, Map params, Map headers, + String apiName, String contentType, int expectedStatus) throws Exception { - RequestBuilder requestBuilder = httpClient.new PostRequestBuilder() - .setBodyAsByteArray(body) + RequestBuilder requestBuilder = httpClient.new PostRequestBuilder().setBodyAsByteArray(body) .setContentType(contentType) .setRequestContext(publicApiClient.getRequestContext()) .setScope(getScope()) @@ -279,10 +266,10 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - protected HttpResponse post(String url, String body, Map params, Map headers, String apiName, int expectedStatus) throws Exception + protected HttpResponse post(String url, String body, Map params, Map headers, + String apiName, int expectedStatus) throws Exception { - RequestBuilder requestBuilder = httpClient.new PostRequestBuilder() - .setBodyAsString(body) + RequestBuilder requestBuilder = httpClient.new PostRequestBuilder().setBodyAsString(body) .setRequestContext(publicApiClient.getRequestContext()) .setScope(getScope()) .setApiName(apiName) @@ -307,7 +294,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - protected HttpResponse post(String url, String body, String queryString, String contentType, int expectedStatus) throws Exception + protected HttpResponse post(String url, String body, String queryString, String contentType, int expectedStatus) + throws Exception { if (queryString != null) { @@ -319,7 +307,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - protected HttpResponse post(String url, byte[] body, String queryString, String contentType, int expectedStatus) throws Exception + protected HttpResponse post(String url, byte[] body, String queryString, String contentType, int expectedStatus) + throws Exception { if (queryString != null) { @@ -332,9 +321,12 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi } // TODO unused queryString - fix-up usages and then remove - protected HttpResponse post(String entityCollectionName, String entityId, String relationCollectionName, byte[] body, String queryString, String contentType, int expectedStatus) throws Exception + protected HttpResponse post(String entityCollectionName, String entityId, String relationCollectionName, + byte[] body, String queryString, String contentType, int expectedStatus) + throws Exception { - HttpResponse response = publicApiClient.post(getScope(), entityCollectionName, entityId, relationCollectionName, null, body, contentType); + HttpResponse response = publicApiClient.post(getScope(), entityCollectionName, entityId, relationCollectionName, null, body, + contentType); checkStatus(expectedStatus, response.getStatusCode()); return response; @@ -345,7 +337,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return getAll(url, paging, null, expectedStatus); } - protected HttpResponse getAll(String url, PublicApiClient.Paging paging, Map otherParams, int expectedStatus) throws Exception + protected HttpResponse getAll(String url, PublicApiClient.Paging paging, Map otherParams, + int expectedStatus) throws Exception { Map params = createParams(paging, otherParams); @@ -355,7 +348,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - protected HttpResponse getAll(Class entityResource, PublicApiClient.Paging paging, Map otherParams, int expectedStatus) throws Exception + protected HttpResponse getAll(Class entityResource, PublicApiClient.Paging paging, + Map otherParams, int expectedStatus) throws Exception { HttpResponse response = publicApiClient.get(entityResource, null, null, otherParams); checkStatus(expectedStatus, response.getStatusCode()); @@ -363,16 +357,17 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - protected HttpResponse getAll(String url, PublicApiClient.Paging paging, Map otherParams, Map headers, int expectedStatus) throws Exception + protected HttpResponse getAll(String url, PublicApiClient.Paging paging, Map otherParams, + Map headers, int expectedStatus) throws Exception { return getAll(url, paging, otherParams, headers, null, expectedStatus); } - protected HttpResponse getAll(String url, PublicApiClient.Paging paging, Map otherParams, Map headers, String apiName, int expectedStatus) throws Exception + protected HttpResponse getAll(String url, PublicApiClient.Paging paging, Map otherParams, + Map headers, String apiName, int expectedStatus) throws Exception { Map params = createParams(paging, otherParams); - RequestBuilder requestBuilder = httpClient.new GetRequestBuilder() - .setRequestContext(publicApiClient.getRequestContext()) + RequestBuilder requestBuilder = httpClient.new GetRequestBuilder().setRequestContext(publicApiClient.getRequestContext()) .setScope(getScope()) .setApiName(apiName) .setEntityCollectionName(url) @@ -384,7 +379,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - + protected HttpResponse getSingle(String url, String entityId, int expectedStatus) throws Exception { return getSingle(url, entityId, null, expectedStatus); @@ -398,7 +393,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - protected HttpResponse getSingle(String url, String entityId, Map params, int expectedStatus) throws Exception + protected HttpResponse getSingle(String url, String entityId, Map params, int expectedStatus) + throws Exception { HttpResponse response = publicApiClient.get(getScope(), url, entityId, null, null, params); checkStatus(expectedStatus, response.getStatusCode()); @@ -406,7 +402,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - protected HttpResponse getSingle(Class entityResource, String entityId, Map params, int expectedStatus) throws Exception + protected HttpResponse getSingle(Class entityResource, String entityId, Map params, + int expectedStatus) throws Exception { HttpResponse response = publicApiClient.get(entityResource, entityId, null, params); checkStatus(expectedStatus, response.getStatusCode()); @@ -414,15 +411,16 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - protected HttpResponse getSingle(String url, String entityId, Map params, Map headers, int expectedStatus) throws Exception + protected HttpResponse getSingle(String url, String entityId, Map params, + Map headers, int expectedStatus) throws Exception { return getSingle(url, entityId, params, headers, null, expectedStatus); } - protected HttpResponse getSingle(String url, String entityId, Map params, Map headers, String apiName, int expectedStatus) throws Exception + protected HttpResponse getSingle(String url, String entityId, Map params, + Map headers, String apiName, int expectedStatus) throws Exception { - RequestBuilder requestBuilder = httpClient.new GetRequestBuilder() - .setRequestContext(publicApiClient.getRequestContext()) + RequestBuilder requestBuilder = httpClient.new GetRequestBuilder().setRequestContext(publicApiClient.getRequestContext()) .setScope(getScope()) .setApiName(apiName) .setEntityCollectionName(url) @@ -437,7 +435,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi } protected HttpResponse getSingleWithDelayRetry(String url, String entityId, Map params, - Map headers, int repeat, long pauseInMillisecond, int expectedStatus) throws Exception + Map headers, int repeat, long pauseInMillisecond, + int expectedStatus) throws Exception { int retryCount = 0; while (retryCount < repeat) @@ -445,7 +444,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi try { return getSingle(url, entityId, params, headers, expectedStatus); - } + } catch (AssertionError ex) { retryCount++; @@ -455,7 +454,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return null; } - protected HttpResponse put(String url, String entityId, String body, String queryString, int expectedStatus) throws Exception + protected HttpResponse put(String url, String entityId, String body, String queryString, int expectedStatus) + throws Exception { if (queryString != null) { @@ -467,8 +467,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return response; } - protected HttpResponse putBinary(String url, int version, BinaryPayload payload, String queryString, Map params, - int expectedStatus) throws Exception + protected HttpResponse putBinary(String url, int version, BinaryPayload payload, String queryString, + Map params, int expectedStatus) throws Exception { if (queryString != null) { @@ -482,7 +482,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi } protected HttpResponse putBinary(String url, BinaryPayload payload, String queryString, Map params, - int expectedStatus) throws Exception + int expectedStatus) throws Exception { return putBinary(url, 1, payload, queryString, params, expectedStatus); } @@ -492,18 +492,19 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return delete(url, entityId, null, expectedStatus); } - protected HttpResponse delete(String url, String entityId, Map params, int expectedStatus) throws Exception + protected HttpResponse delete(String url, String entityId, Map params, int expectedStatus) + throws Exception { HttpResponse response = publicApiClient.delete(getScope(), 1, url, entityId, null, null, params); checkStatus(expectedStatus, response.getStatusCode()); return response; } - - protected HttpResponse delete(String url, String entityId, Map params, Map headers, String apiName, int expectedStatus) throws Exception + + protected HttpResponse delete(String url, String entityId, Map params, Map headers, + String apiName, int expectedStatus) throws Exception { - RequestBuilder requestBuilder = httpClient.new DeleteRequestBuilder() - .setRequestContext(publicApiClient.getRequestContext()) + RequestBuilder requestBuilder = httpClient.new DeleteRequestBuilder().setRequestContext(publicApiClient.getRequestContext()) .setScope(getScope()) .setApiName(apiName) .setEntityCollectionName(url) @@ -524,7 +525,10 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi protected String createUser(String usernameIn, String password, TestNetwork network) { - return createUser(new PersonInfo(usernameIn, usernameIn, usernameIn, password, null, null, null, null, null, null, null), network); + return createUser( + new PersonInfo(usernameIn, usernameIn, usernameIn, password, null, null, null, null, null, null, + null), + network); } /** @@ -533,17 +537,16 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi protected String createUser(final PersonInfo personInfo, final TestNetwork network) { final String tenantDomain = (network != null ? network.getId() : TenantService.DEFAULT_DOMAIN); - - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { @Override public String doWork() throws Exception { - return TenantUtil.runAsTenant(new TenantUtil.TenantRunAsWork() - { + return TenantUtil.runAsTenant(new TenantUtil.TenantRunAsWork() { public String doWork() throws Exception { - String username = repoService.getPublicApiContext().createUserName(personInfo.getUsername(), tenantDomain); + String username = repoService.getPublicApiContext() + .createUserName(personInfo.getUsername(), tenantDomain); personInfo.setUsername(username); RepoService.TestPerson person = repoService.createUser(personInfo, username, network); return person.getId(); @@ -561,17 +564,17 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi { final String tenantDomain = (network != null ? network.getId() : TenantService.DEFAULT_DOMAIN); - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { @Override public String doWork() throws Exception { - return TenantUtil.runAsTenant(new TenantUtil.TenantRunAsWork() - { + return TenantUtil.runAsTenant(new TenantUtil.TenantRunAsWork() { public String doWork() throws Exception { - String username = repoService.getPublicApiContext().createUserName(usernameIn, tenantDomain); - PersonInfo personInfo = new PersonInfo(username, username, username, password, null, null, null, null, null, null, null); + String username = repoService.getPublicApiContext() + .createUserName(usernameIn, tenantDomain); + PersonInfo personInfo = new PersonInfo(username, username, username, password, null, null, null, null, null, + null, null); RepoService.TestPerson person = repoService.getOrCreateUser(personInfo, username, network); return person.getId(); } @@ -587,13 +590,11 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi { final String tenantDomain = (network != null ? network.getId() : TenantService.DEFAULT_DOMAIN); - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() - { + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { @Override public String doWork() throws Exception { - return TenantUtil.runAsTenant(new TenantUtil.TenantRunAsWork() - { + return TenantUtil.runAsTenant(new TenantUtil.TenantRunAsWork() { public String doWork() throws Exception { repoService.deleteUser(username, network); @@ -603,13 +604,15 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi } }, networkAdmin); } - + protected SiteMember addSiteMember(String siteId, String userId, final SiteRole siteRole) throws Exception { SiteMember siteMember = new SiteMember(userId, siteRole.name()); - HttpResponse response = publicApiClient.post(getScope(), "sites", siteId, "members", null, siteMember.toJSON().toString()); + HttpResponse response = publicApiClient.post(getScope(), "sites", siteId, "members", null, siteMember.toJSON() + .toString()); checkStatus(201, response.getStatusCode()); - return SiteMember.parseSiteMember(siteMember.getSiteId(), (JSONObject)response.getJsonResponse().get("entry")); + return SiteMember.parseSiteMember(siteMember.getSiteId(), (JSONObject) response.getJsonResponse() + .get("entry")); } protected Site createSite(String siteTitle, SiteVisibility siteVisibility) throws Exception @@ -617,7 +620,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return createSite(null, siteTitle, null, siteVisibility, 201); } - protected Site createSite(String siteId, String siteTitle, String siteDescription, SiteVisibility siteVisibility, int expectedStatus) throws Exception + protected Site createSite(String siteId, String siteTitle, String siteDescription, SiteVisibility siteVisibility, + int expectedStatus) throws Exception { Site site = new Site(); site.setId(siteId); @@ -633,7 +637,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi protected HttpResponse deleteSite(String siteId, boolean permanent, int expectedStatus) throws Exception { Map params = null; - if (permanent == true) + if (permanent) { params = Collections.singletonMap("permanent", "true"); } @@ -655,14 +659,14 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi protected String getSiteContainerNodeId(String siteId, String containerNameId) throws Exception { Map params = Collections.singletonMap(Nodes.PARAM_RELATIVE_PATH, "/Sites/" + siteId + "/" + containerNameId); - + HttpResponse response = publicApiClient.get(NodesEntityResource.class, Nodes.PATH_ROOT, null, params); checkStatus(200, response.getStatusCode()); Node node = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); return node.getId(); } - + protected void checkStatus(int expectedStatus, int actualStatus) { if (expectedStatus > 0 && expectedStatus != actualStatus) @@ -672,9 +676,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi } /** - * @deprecated - * * @param runAsUser + * @deprecated */ protected void setRequestContext(String runAsUser) { @@ -702,9 +705,9 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi } else if ((runAsUser != null) && runAsUser.equals(DEFAULT_ADMIN)) { - runAsUser = runAsUser+"@"+runAsNetwork; + runAsUser = runAsUser + "@" + runAsNetwork; } - + publicApiClient.setRequestContext(new RequestContext(runAsNetwork, runAsUser, password)); } @@ -736,7 +739,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi { return createFolder(parentId, folderName, null); } - + protected Folder createFolder(String parentId, String folderName, Map props) throws Exception { return createNode(parentId, folderName, TYPE_CM_FOLDER, props, Folder.class); @@ -750,17 +753,18 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi protected String createUniqueContent(String folderId) throws Exception { Document documentResp = createTextFile(folderId, "file-" + System.currentTimeMillis(), - "some text-" + System.currentTimeMillis(), "UTF-8", null); + "some text-" + System.currentTimeMillis(), "UTF-8", null); return documentResp.getId(); } - protected Node createNode(String parentId, String nodeName, String nodeType, Map props) throws Exception + protected Node createNode(String parentId, String nodeName, String nodeType, Map props) + throws Exception { return createNode(parentId, nodeName, nodeType, props, Node.class); } - protected T createNode(String parentId, String nodeName, String nodeType, Map props, Class returnType) - throws Exception + protected T createNode(String parentId, String nodeName, String nodeType, Map props, + Class returnType) throws Exception { Node n = new Node(); n.setName(nodeName); @@ -772,7 +776,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return RestApiUtil.parseRestApiEntry(response.getJsonResponse(), returnType); } - + protected void deleteNode(String nodeId) throws Exception { deleteNode(nodeId, 204); @@ -786,25 +790,27 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi protected void deleteNode(String nodeId, boolean permanent, int expectedStatus) throws Exception { Map params = null; - if (permanent == true) + if (permanent) { params = Collections.singletonMap("permanent", "true"); } - + delete(URL_NODES, nodeId, params, expectedStatus); } - protected Document createTextFile(String parentId, String fileName, String textContent) throws IOException, Exception + protected Document createTextFile(String parentId, String fileName, String textContent) throws Exception { return createTextFile(parentId, fileName, textContent, "UTF-8", null); } - protected Document createTextFile(String parentId, String fileName, String textContent, String encoding, Map props) throws IOException, Exception + protected Document createTextFile(String parentId, String fileName, String textContent, String encoding, + Map props) throws Exception { return createTextFile(parentId, fileName, textContent, encoding, props, 201); } - protected Document createTextFile(String parentId, String fileName, String textContent, String encoding, Map props, int expectedStatus) throws IOException, Exception + protected Document createTextFile(String parentId, String fileName, String textContent, String encoding, + Map props, int expectedStatus) throws Exception { if (props == null) { @@ -820,9 +826,11 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi .setProperties(props) .build(); - HttpResponse response = post(getNodeChildrenUrl(parentId), reqBody.getBody(), null, reqBody.getContentType(), expectedStatus); + HttpResponse response = post(getNodeChildrenUrl(parentId), reqBody.getBody(), null, reqBody.getContentType(), + expectedStatus); - if (response.getJsonResponse().get("error") != null) + if (response.getJsonResponse() + .get("error") != null) { return null; } @@ -834,8 +842,9 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi { return createEmptyTextFile(parentFolderId, docName, null, 201); } - - protected Document createEmptyTextFile(String parentFolderId, String docName, Map params, int expectedStatus) throws Exception + + protected Document createEmptyTextFile(String parentFolderId, String docName, Map params, + int expectedStatus) throws Exception { Document d1 = new Document(); d1.setName(docName); @@ -845,7 +854,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi d1.setContent(ci); // create empty file - HttpResponse response = post(getNodeChildrenUrl(parentFolderId), toJsonAsStringNonNull(d1), params, null, "alfresco", expectedStatus); + HttpResponse response = post(getNodeChildrenUrl(parentFolderId), toJsonAsStringNonNull(d1), params, null, "alfresco", + expectedStatus); if (expectedStatus != 201) { return null; @@ -858,7 +868,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return updateTextFile(contentId, textContent, params, 200); } - protected Document updateTextFile(String contentId, String textContent, Map params, int expectedStatus) throws Exception + protected Document updateTextFile(String contentId, String textContent, Map params, + int expectedStatus) throws Exception { ByteArrayInputStream inputStream = new ByteArrayInputStream(textContent.getBytes()); File txtFile = TempFileProvider.createTempFile(inputStream, getClass().getSimpleName(), ".txt"); @@ -874,7 +885,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi protected File getResourceFile(String fileName) throws FileNotFoundException { - URL url = NodeApiTest.class.getClassLoader().getResource(RESOURCE_PREFIX + fileName); + URL url = NodeApiTest.class.getClassLoader() + .getResource(RESOURCE_PREFIX + fileName); if (url == null) { fail("Cannot get the resource: " + fileName); @@ -907,14 +919,15 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi * @return * @throws Exception */ - protected String updateFileVersions(String userId, String contentNodeId, int cnt, - String textContentPrefix, int verCnt, - Boolean majorVersion, String currentVersionLabel) throws Exception + protected String updateFileVersions(String userId, String contentNodeId, int cnt, String textContentPrefix, + int verCnt, Boolean majorVersion, String currentVersionLabel) throws Exception { String[] parts = currentVersionLabel.split("\\."); - int majorVer = Integer.valueOf(parts[0]).intValue(); - int minorVer = Integer.valueOf(parts[1]).intValue(); + int majorVer = Integer.valueOf(parts[0]) + .intValue(); + int minorVer = Integer.valueOf(parts[1]) + .intValue(); Map params = new HashMap<>(); params.put(Nodes.PARAM_OVERWRITE, "true"); @@ -928,7 +941,6 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi majorVersion = false; } - if (majorVersion) { minorVer = 0; @@ -961,30 +973,33 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi HttpResponse response = putBinary(getNodeContentUrl(contentNodeId), payload, null, params, 200); Node nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); - assertTrue(nodeResp.getAspectNames().contains("cm:versionable")); + assertTrue(nodeResp.getAspectNames() + .contains("cm:versionable")); assertNotNull(nodeResp.getProperties()); - assertEquals(currentVersionLabel, nodeResp.getProperties().get("cm:versionLabel")); - assertEquals((majorVersion ? "MAJOR" : "MINOR"), nodeResp.getProperties().get("cm:versionType")); + assertEquals(currentVersionLabel, nodeResp.getProperties() + .get("cm:versionLabel")); + assertEquals((majorVersion ? "MAJOR" : "MINOR"), nodeResp.getProperties() + .get("cm:versionType")); // double-check - get version node info response = getSingle(getNodeVersionsUrl(contentNodeId), currentVersionLabel, null, 200); nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); - assertEquals(currentVersionLabel, nodeResp.getProperties().get("cm:versionLabel")); - assertEquals((majorVersion ? "MAJOR" : "MINOR"), nodeResp.getProperties().get("cm:versionType")); + assertEquals(currentVersionLabel, nodeResp.getProperties() + .get("cm:versionLabel")); + assertEquals((majorVersion ? "MAJOR" : "MINOR"), nodeResp.getProperties() + .get("cm:versionType")); } return currentVersionLabel; } - protected static final long PAUSE_TIME = 5000; //millisecond - protected static final int MAX_RETRY = 20; - protected Rendition waitAndGetRendition(String sourceNodeId, String versionId, String renditionId) throws Exception { return waitAndGetRendition(sourceNodeId, versionId, renditionId, MAX_RETRY, PAUSE_TIME); } - protected Rendition waitAndGetRendition(String sourceNodeId, String versionId, String renditionId, int maxRetry, long pauseTime) throws Exception + protected Rendition waitAndGetRendition(String sourceNodeId, String versionId, String renditionId, int maxRetry, + long pauseTime) throws Exception { int retryCount = 0; while (retryCount < maxRetry) @@ -992,7 +1007,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi try { HttpResponse response; - if ((versionId != null) && (! versionId.isEmpty())) + if ((versionId != null) && (!versionId.isEmpty())) { response = getSingle(getNodeVersionRenditionsUrl(sourceNodeId, versionId), renditionId, 200); } @@ -1011,7 +1026,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi // wait for 'PAUSE_TIME' and try again. retryCount++; - System.out.println("waitAndGetRendition: "+retryCount); + System.out.println("waitAndGetRendition: " + retryCount); Thread.sleep(pauseTime); } } @@ -1024,7 +1039,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi return createAndGetRendition(sourceNodeId, null, renditionId); } - protected Rendition createAndGetRendition(String sourceNodeId, String versionId, String renditionId) throws Exception + protected Rendition createAndGetRendition(String sourceNodeId, String versionId, String renditionId) + throws Exception { Rendition renditionRequest = new Rendition(); renditionRequest.setId(renditionId); @@ -1035,9 +1051,10 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi try { HttpResponse response; - if ((versionId != null) && (! versionId.isEmpty())) + if ((versionId != null) && (!versionId.isEmpty())) { - response = post(getNodeVersionRenditionsUrl(sourceNodeId, versionId), toJsonAsString(renditionRequest), 202); + response = post(getNodeVersionRenditionsUrl(sourceNodeId, versionId), + toJsonAsString(renditionRequest), 202); } else { @@ -1052,7 +1069,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi // wait for 'PAUSE_TIME' and try again. retryCount++; - System.out.println("waitAndGetRendition: "+retryCount); + System.out.println("waitAndGetRendition: " + retryCount); Thread.sleep(PAUSE_TIME); } } @@ -1072,7 +1089,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi protected String getNodeVersionRenditionIdUrl(String nodeId, String versionId, String renditionID) { - return URL_NODES + "/" + nodeId + "/" + URL_VERSIONS + "/" + versionId + "/" + URL_RENDITIONS + "/" + renditionID; + return URL_NODES + "/" + nodeId + "/" + URL_VERSIONS + "/" + versionId + "/" + URL_RENDITIONS + "/" + + renditionID; } protected String getNodeVersionsUrl(String nodeId) @@ -1120,5 +1138,14 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi RestApiDirectUrlConfig restDauConfig = (RestApiDirectUrlConfig) applicationContext.getBean("restApiDirectUrlConfig"); restDauConfig.setEnabled(false); } -} + protected String generateNodeSizeDetailsUrl(String nodeId) + { + return URL_NODES + "/" + nodeId + "/" + URL_CALCULATEFOLDERSIZE; + } + + protected String getNodeSizeDetailsUrl(String nodeId, String jobId) + { + return URL_NODES + "/" + nodeId + "/" + URL_CALCULATEFOLDERSIZE + "/" + jobId; + } +} diff --git a/repository/src/main/java/org/alfresco/repo/node/sizedetails/NodeSizeDetailsService.java b/repository/src/main/java/org/alfresco/repo/node/sizedetails/NodeSizeDetailsService.java new file mode 100644 index 0000000000..63e621f867 --- /dev/null +++ b/repository/src/main/java/org/alfresco/repo/node/sizedetails/NodeSizeDetailsService.java @@ -0,0 +1,41 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.node.sizedetails; + +import java.util.Optional; + +import org.alfresco.repo.node.sizedetails.NodeSizeDetailsServiceImpl.NodeSizeDetails; +import org.alfresco.service.cmr.repository.NodeRef; + +public interface NodeSizeDetailsService +{ + void invokeSizeDetailsExecutor(NodeRef nodeRef, String jobId); + + void putSizeDetails(String id, NodeSizeDetails nodeSizeDetails); + + Optional getSizeDetails(String id); + +} diff --git a/repository/src/main/java/org/alfresco/repo/node/sizedetails/NodeSizeDetailsServiceImpl.java b/repository/src/main/java/org/alfresco/repo/node/sizedetails/NodeSizeDetailsServiceImpl.java new file mode 100644 index 0000000000..f2eb02dbec --- /dev/null +++ b/repository/src/main/java/org/alfresco/repo/node/sizedetails/NodeSizeDetailsServiceImpl.java @@ -0,0 +1,400 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2024 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.node.sizedetails; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.ThreadPoolExecutor; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; + +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.node.sizedetails.NodeSizeDetailsServiceImpl.NodeSizeDetails.STATUS; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchParameters.FieldFacet; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.Pair; +import org.alfresco.util.ParameterCheck; + +/** + * NodeSizeDetailsServiceImpl Executing Alfresco FTS Query to find size details of Folder Node + */ +public class NodeSizeDetailsServiceImpl implements NodeSizeDetailsService, InitializingBean +{ + private static final Logger LOG = LoggerFactory.getLogger(NodeSizeDetailsServiceImpl.class); + private static final String FIELD_FACET = "content.size"; + private static final String FACET_QUERY = "{!afts}content.size:[0 TO " + Integer.MAX_VALUE + "]"; + private SearchService searchService; + private SimpleCache simpleCache; + private TransactionService transactionService; + private ThreadPoolExecutor threadPoolExecutor; + private int defaultItems; + + public void setSearchService(SearchService searchService) + { + this.searchService = searchService; + } + + @Override + public Optional getSizeDetails(String id) + { + NodeSizeDetails details = simpleCache.get(id); + return Optional.ofNullable(details) + .or(() -> { + LOG.error("No Size details found for ID: " + id); + return Optional.empty(); + }); + } + + public void setSimpleCache(SimpleCache simpleCache) + { + this.simpleCache = simpleCache; + } + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor) + { + this.threadPoolExecutor = threadPoolExecutor; + } + + public void setDefaultItems(int defaultItems) + { + this.defaultItems = defaultItems; + } + + @Override + public void invokeSizeDetailsExecutor(NodeRef nodeRef, String jobId) + { + try + { + executeSizeCalculation(nodeRef, jobId); + } + catch (Exception e) + { + LOG.error("Exception occurred while executing invokeSizeDetailsExecutor method ", e); + } + + } + + @Override + public void putSizeDetails(String id, NodeSizeDetails nodeSizeDetails) + { + simpleCache.put(id, nodeSizeDetails); + } + + private void executeSizeCalculation(NodeRef nodeRef, String jobId) + { + String authenticatedUserName = AuthenticationUtil.getFullyAuthenticatedUser(); + RetryingTransactionCallback executionCallback = () -> { + + try + { + return calculateTotalSizeFromFacet(nodeRef, jobId); + } + catch (Exception ex) + { + LOG.error("Exception occurred in executeSizeCalculation:RetryingTransactionCallback ", ex); + throw ex; + } + }; + + threadPoolExecutor.execute(() -> { + NodeSizeDetails nodeSizeDetails = new NodeSizeDetails(nodeRef.getId(), jobId, STATUS.IN_PROGRESS); + putSizeDetails(nodeRef.getId(), nodeSizeDetails); + + try + { + nodeSizeDetails = AuthenticationUtil.runAs(() -> transactionService.getRetryingTransactionHelper() + .doInTransaction(executionCallback, true), authenticatedUserName); + } + catch (Exception e) + { + LOG.error("Exception occurred in executeSizeCalculation", e); + nodeSizeDetails = new NodeSizeDetails(nodeRef.getId(), 0L, jobId, STATUS.FAILED); + } + finally + { + putSizeDetails(nodeRef.getId(), nodeSizeDetails); + } + }); + } + + private NodeSizeDetails calculateTotalSizeFromFacet(NodeRef nodeRef, String jobId) + { + long totalSizeFromFacet = 0; + int skipCount = 0; + int totalItems = defaultItems; + boolean isCalculationCompleted = false; + + try + { + ResultSet results = facetQuery(nodeRef); + int resultsSize = results.getFieldFacet(FIELD_FACET) + .size(); + + while (!isCalculationCompleted) + { + List> facetPairs = results.getFieldFacet(FIELD_FACET) + .subList(skipCount, Math.min(totalItems, resultsSize)); + totalSizeFromFacet += facetPairs.parallelStream() + .mapToLong(pair -> Long.parseLong(pair.getFirst()) * pair.getSecond()) + .sum(); + + if (resultsSize <= totalItems || resultsSize <= defaultItems) + { + isCalculationCompleted = true; + } + else + { + skipCount += defaultItems; + resultsSize -= totalItems; + totalItems += Math.min(resultsSize, defaultItems); + } + } + Date calculationDate = new Date(System.currentTimeMillis()); + NodeSizeDetails nodeSizeDetails = new NodeSizeDetails(nodeRef.getId(), totalSizeFromFacet, calculationDate, + results.getNodeRefs() + .size(), + STATUS.COMPLETED, jobId); + return nodeSizeDetails; + } + catch (Exception e) + { + LOG.error("Exception occurred while calculating total size from facet", e); + throw e; + } + } + + private ResultSet facetQuery(NodeRef nodeRef) + { + try + { + SearchParameters searchParameters = createSearchParameters(nodeRef); + ResultSet resultsWithoutFacet = searchService.query(searchParameters); + if (LOG.isDebugEnabled()) + { + LOG.debug(" After Executing facet query, no. of records found " + resultsWithoutFacet.getNumberFound()); + } + searchParameters.addFacetQuery(FACET_QUERY); + FieldFacet fieldFacet = new FieldFacet(FIELD_FACET); + fieldFacet.setLimitOrNull((int) resultsWithoutFacet.getNumberFound()); + searchParameters.addFieldFacet(fieldFacet); + resultsWithoutFacet.close(); + return searchService.query(searchParameters); + } + catch (Exception e) + { + LOG.error("Exception occurred while executing facetQuery ", e); + throw e; + } + } + + private SearchParameters createSearchParameters(NodeRef nodeRef) + { + SearchParameters searchParameters = new SearchParameters(); + searchParameters.addStore(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + searchParameters.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO); + searchParameters.setQuery("ANCESTOR:\"" + nodeRef + "\" AND TYPE:\"cm:content\""); + searchParameters.setTrackTotalHits(-1); + return searchParameters; + } + + @Override + public void afterPropertiesSet() throws Exception + { + ParameterCheck.mandatory("searchService", this.searchService); + ParameterCheck.mandatory("simpleCache", this.simpleCache); + ParameterCheck.mandatory("transactionService", this.transactionService); + ParameterCheck.mandatory("threadPoolExecutor", this.threadPoolExecutor); + } + + /** + * POJO class to hold node size details. + */ + public static class NodeSizeDetails implements Serializable + { + private static final long serialVersionUID = 1L; + private String id; + private Long sizeInBytes; + private Date calculatedAt; + private Integer numberOfFiles; + private String jobId; + private STATUS status; + + public NodeSizeDetails() + {} + + public NodeSizeDetails(String jobId) + { + this.jobId = jobId; + } + + public NodeSizeDetails(String id, STATUS status) + { + this.id = id; + this.status = status; + } + + public NodeSizeDetails(String id, String jobId, STATUS status) + { + this.id = id; + this.jobId = jobId; + this.status = status; + } + + public NodeSizeDetails(String id, Long sizeInBytes, String jobId, STATUS status) + { + this.id = id; + this.sizeInBytes = sizeInBytes; + this.jobId = jobId; + this.status = status; + } + + public NodeSizeDetails(String id, Long sizeInBytes, Date calculatedAt, Integer numberOfFiles, + STATUS currentStatus, String jobId) + { + this.id = id; + this.sizeInBytes = sizeInBytes; + this.calculatedAt = calculatedAt; + this.numberOfFiles = numberOfFiles; + this.status = currentStatus; + this.jobId = jobId; + } + + public String getId() + { + return id; + } + + public void setId(String id) + { + this.id = id; + } + + public Long getSizeInBytes() + { + return sizeInBytes; + } + + public void setSizeInBytes(Long sizeInBytes) + { + this.sizeInBytes = sizeInBytes; + } + + public Date getCalculatedAt() + { + return calculatedAt; + } + + public void setCalculatedAt(Date calculatedAt) + { + this.calculatedAt = calculatedAt; + } + + public Integer getNumberOfFiles() + { + return numberOfFiles; + } + + public void setNumberOfFiles(Integer numberOfFiles) + { + this.numberOfFiles = numberOfFiles; + } + + public String getJobId() + { + return jobId; + } + + public void setJobId(String jobId) + { + this.jobId = jobId; + } + + public STATUS getStatus() + { + return status; + } + + public void setStatus(STATUS status) + { + this.status = status; + } + + @Override + public boolean equals(Object o) + { + if (this == o) + { + return true; + } + if (o == null || getClass() != o.getClass()) + { + return false; + } + NodeSizeDetails that = (NodeSizeDetails) o; + return Objects.equals(id, that.id) && Objects.equals(sizeInBytes, that.sizeInBytes) && Objects.equals( + calculatedAt, that.calculatedAt) && Objects.equals(numberOfFiles, that.numberOfFiles) + && Objects.equals(jobId, that.jobId) && status == that.status; + } + + @Override + public int hashCode() + { + return Objects.hash(id, sizeInBytes, calculatedAt, numberOfFiles, jobId, status); + } + + @Override + public String toString() + { + return "NodeSizeDetails{" + "id='" + id + '\'' + ", sizeInBytes=" + sizeInBytes + ", calculatedAt=" + + calculatedAt + ", numberOfFiles=" + numberOfFiles + ", jobId='" + jobId + '\'' + ", status=" + + status + '}'; + } + + public enum STATUS + { + NOT_INITIATED, PENDING, IN_PROGRESS, COMPLETED, FAILED + } + + } + +} diff --git a/repository/src/main/resources/alfresco/cache-context.xml b/repository/src/main/resources/alfresco/cache-context.xml index 6cb3c7e1dc..8bd336aed3 100644 --- a/repository/src/main/resources/alfresco/cache-context.xml +++ b/repository/src/main/resources/alfresco/cache-context.xml @@ -514,4 +514,10 @@ + + + + + + diff --git a/repository/src/main/resources/alfresco/caches.properties b/repository/src/main/resources/alfresco/caches.properties index 5b009d36b7..add7db4356 100644 --- a/repository/src/main/resources/alfresco/caches.properties +++ b/repository/src/main/resources/alfresco/caches.properties @@ -710,3 +710,12 @@ cache.ldapInitialDirContextCache.backup-count=1 cache.ldapInitialDirContextCache.eviction-policy=NONE cache.ldapInitialDirContextCache.merge-policy=com.hazelcast.spi.merge.LatestUpdateMergePolicy cache.ldapInitialDirContextCache.readBackupData=false + +cache.folderSizeSharedCache.maxItems=1000 +cache.folderSizeSharedCache.timeToLiveSeconds=300 +cache.folderSizeSharedCache.maxIdleSeconds=0 +cache.folderSizeSharedCache.cluster.type=fully-distributed +cache.folderSizeSharedCache.backup-count=1 +cache.folderSizeSharedCache.eviction-policy=LRU +cache.folderSizeSharedCache.merge-policy=com.hazelcast.spi.merge.PutIfAbsentMergePolicy +cache.folderSizeSharedCache.readBackupData=false \ No newline at end of file diff --git a/repository/src/main/resources/alfresco/node-services-context.xml b/repository/src/main/resources/alfresco/node-services-context.xml index 518a674888..4cf86ccec7 100644 --- a/repository/src/main/resources/alfresco/node-services-context.xml +++ b/repository/src/main/resources/alfresco/node-services-context.xml @@ -330,6 +330,42 @@ - + + + + + defaultThreadPool + + + ${default.nodeSize.corePoolSize} + + + ${default.nodeSize.maximumPoolSize} + + + ${default.nodeSize.workQueueSize} + + + + + + + + + + + + + + + + NodeSizeDetailsServiceImpl + + + + org.alfresco.repo.node.sizedetails.NodeSizeDetailsService + + + diff --git a/repository/src/main/resources/alfresco/repository.properties b/repository/src/main/resources/alfresco/repository.properties index 2b7691cefa..f5dfa686a9 100644 --- a/repository/src/main/resources/alfresco/repository.properties +++ b/repository/src/main/resources/alfresco/repository.properties @@ -1385,3 +1385,12 @@ scripts.execution.maxMemoryUsedInBytes=-1 # Number of instructions that will trigger the observer scripts.execution.observerInstructionCount=5000 + +# Default value being used in POST/size-details endpoint to partition a huge folder into smaller chunks +# so that we can compute more efficiently and consolidate all sizes into a single unit. +default.async.folder.items=1000 + +# Default NodeSize Thread pool +default.nodeSize.corePoolSize=5 +default.nodeSize.maximumPoolSize=10 +default.nodeSize.workQueueSize=100 \ No newline at end of file