mirror of
				https://github.com/Alfresco/alfresco-community-repo.git
				synced 2025-10-29 15:21:53 +00:00 
			
		
		
		
	Compare commits
	
		
			238 Commits
		
	
	
		
			23.2.0.31
			...
			feature/AC
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | cb863c646f | ||
|  | a2d1391aee | ||
|  | 818bcb09f3 | ||
|  | 72e85076ad | ||
|  | a69be867e8 | ||
|  | fa6e0ded45 | ||
|  | ed0bbb6699 | ||
|  | 8009a6d6dd | ||
|  | 785bdb72ea | ||
|  | 66aef18862 | ||
|  | 7b516f24b6 | ||
|  | 0de1aca0f6 | ||
|  | 592dc35b6d | ||
|  | 73724a8205 | ||
|  | c8c1102431 | ||
|  | c31ae1fe33 | ||
|  | 82e9f0452d | ||
|  | 377f546a9b | ||
|  | 07dcba972f | ||
|  | 8b45d19b00 | ||
|  | 0b4b1dac44 | ||
|  | f8becd3523 | ||
|  | 781b56f06d | ||
|  | 92656a68a9 | ||
|  | 99838a73e3 | ||
|  | d7f9ed1cf0 | ||
|  | 4eccb77fa8 | ||
|  | 723156a86a | ||
|  | 3f6692a63b | ||
|  | 32e72f204a | ||
|  | 07352336c5 | ||
|  | b5ce847bb1 | ||
|  | 34925b497b | ||
|  | f5868e7f45 | ||
|  | fd015de2c8 | ||
|  | ec2494f2b5 | ||
|  | afc7e73352 | ||
|  | 3519fd6a71 | ||
|  | 5e43f3d4ab | ||
|  | 337d0b8f8f | ||
|  | 3c7593265d | ||
|  | 8986d03a2f | ||
|  | 5dcecb7f19 | ||
|  | 0617a441d3 | ||
|  | d9abdc23d5 | ||
|  | f132ec0a91 | ||
|  | 1797781ce8 | ||
|  | 88c1c849ed | ||
|  | c4ac7621c2 | ||
|  | bf7432deb1 | ||
|  | 6f74667a5c | ||
|  | 502c996c9e | ||
|  | a22e7d23f0 | ||
|  | 1e1508b74e | ||
|  | da8ffa08a0 | ||
|  | 277dbd1054 | ||
|  | 43cfa0d872 | ||
|  | 1325ec5718 | ||
|  | ecde2b5b60 | ||
|  | a9f75638c6 | ||
|  | a80b996f7e | ||
|  | 05aa6b4cf6 | ||
|  | 50b1d3a52e | ||
|  | 38b763c635 | ||
|  | 762b6ce607 | ||
|  | 0d89010ae6 | ||
|  | b744f267c1 | ||
|  | e840726a7d | ||
|  | c53683df85 | ||
|  | d9e58483ff | ||
|  | 9e81472d06 | ||
|  | 3e9cdc6b77 | ||
|  | e8ac8c2602 | ||
|  | eea58f4ba3 | ||
|  | a95fa4a83d | ||
|  | 0b74d283e2 | ||
|  | 052e21e62d | ||
|  | 26f49e80e9 | ||
|  | c31158a113 | ||
|  | 2b4fc52203 | ||
|  | 6222e1dfde | ||
|  | 077c48dea9 | ||
|  | 1a0b8d8dee | ||
|  | 66d007e703 | ||
|  | 81a4c5bac0 | ||
|  | 018157c808 | ||
|  | 8db97184f2 | ||
|  | 84948e051a | ||
|  | 8c20a3271e | ||
|  | 48c7abcfbe | ||
|  | 53d77f8d71 | ||
|  | e8e747347a | ||
|  | fee244cb08 | ||
|  | 2bec2bd1c4 | ||
|  | 1cd8098f52 | ||
|  | 30afcaa033 | ||
|  | bdf1a57630 | ||
|  | 2723817832 | ||
|  | 22acb2abe7 | ||
|  | 3c616152a1 | ||
|  | 8cfdc613cb | ||
|  | f1919934b2 | ||
|  | e9da60dac3 | ||
|  | 0597b0997f | ||
|  | 6dbe30e8f7 | ||
|  | d978b1cd68 | ||
|  | dce356fe74 | ||
|  | f965165894 | ||
|  | 2ac44c24a8 | ||
|  | a0dc5a0d70 | ||
|  | af94063bbb | ||
|  | 87b91b6cae | ||
|  | 6bdcaa9b10 | ||
|  | 86070d881c | ||
|  | a82199967c | ||
|  | f09266c081 | ||
|  | 563f65825f | ||
|  | 8bedeedfd5 | ||
|  | a73cf6a71d | ||
|  | 1bdd6c022c | ||
|  | 1b553dbcaf | ||
|  | 5a3b4e1a0d | ||
|  | 1a0156b1e5 | ||
|  | 6ac9248262 | ||
|  | 70290c8f23 | ||
|  | 7d135b9356 | ||
|  | df4629b801 | ||
|  | df6f656b95 | ||
|  | f27718c43b | ||
|  | cbf0aaaaa4 | ||
|  | 2da78a94ad | ||
|  | 3325e08d57 | ||
|  | 985205e78e | ||
|  | 5fd5e75bd2 | ||
|  | 279bc15aac | ||
|  | a3283b4521 | ||
|  | e09c9118b2 | ||
|  | 330256438b | ||
|  | 2214f16e6e | ||
|  | 2237a45e76 | ||
|  | f71a003904 | ||
|  | 10d55824f8 | ||
|  | 69f8bda762 | ||
|  | d8de1ba353 | ||
|  | d4d8718f16 | ||
|  | 859492ae1e | ||
|  | 6c08a60ce5 | ||
|  | 9cdc916189 | ||
|  | 8272347b71 | ||
|  | cde7f0bba1 | ||
|  | 959678eb70 | ||
|  | cf7bb499dc | ||
|  | a915a4246c | ||
|  | 89b5fa9e90 | ||
|  | 03744ff5d5 | ||
|  | 965add646c | ||
|  | 1c93cc7ca4 | ||
|  | 0564b27ece | ||
|  | b98fdf4ca9 | ||
|  | 1aff1eab1e | ||
|  | c3b82354c1 | ||
|  | 04215f84a6 | ||
|  | e6e05c8890 | ||
|  | af69c12be3 | ||
|  | eaf07ef88e | ||
|  | c4714b19eb | ||
|  | e3407e5a53 | ||
|  | 8d978d6527 | ||
|  | de6b062f3e | ||
|  | 98231782f6 | ||
|  | b4146744d2 | ||
|  | f9f3248229 | ||
|  | 9297fa5cf4 | ||
|  | 3d1cc0924d | ||
|  | 3b574c56dc | ||
|  | f737c3ef3c | ||
|  | 5e85b8149b | ||
|  | 440e31fcdb | ||
|  | 11030f52fa | ||
|  | c537166f68 | ||
|  | 448d2c9c2f | ||
|  | dd5703a51a | ||
|  | cfc0916c84 | ||
|  | 71b69529c6 | ||
|  | 9ffe27f028 | ||
|  | 47a636997c | ||
|  | c97df2dbaa | ||
|  | 2734dcb107 | ||
|  | 197acb3a7b | ||
|  | 91019185d8 | ||
|  | fb0c64b664 | ||
|  | 5f568179bd | ||
|  | 0396bbebbc | ||
|  | 12d1ecdefa | ||
|  | ebdc62a421 | ||
|  | aea34e8149 | ||
|  | 6a5f378ce1 | ||
|  | e1de1c627d | ||
|  | 0209c8fc84 | ||
|  | bb0c3ea207 | ||
|  | 605c3a6ccd | ||
|  | d3c7342f05 | ||
|  | 0eb7abb95b | ||
|  | a7925d54a6 | ||
|  | dbd7ce1f48 | ||
|  | ecfeb77fb0 | ||
|  | 1f72faa90c | ||
|  | 925a4f4a6a | ||
|  | 4f4f7cc02f | ||
|  | ef9d724ee9 | ||
|  | 71cbb9e1ef | ||
|  | effb697261 | ||
|  | 0b753c23c2 | ||
|  | 05f0df1b2f | ||
|  | 7a8cf67c2e | ||
|  | da7f1877fd | ||
|  | 9a11075f41 | ||
|  | 4d22931dfe | ||
|  | d9fe82a2d2 | ||
|  | 60e42b4090 | ||
|  | 824886ecdd | ||
|  | e2f0ebb015 | ||
|  | 6872c3d683 | ||
|  | 4d8a9e35b7 | ||
|  | 0cea265153 | ||
|  | 1ef7b5bea9 | ||
|  | 0a9aac7332 | ||
|  | 12e1506d01 | ||
|  | 2c5a745b50 | ||
|  | dd5f2a76ab | ||
|  | e7bd28180c | ||
|  | 5bb8a621bf | ||
|  | 28ed202c78 | ||
|  | d62423f378 | ||
|  | fc7b2f9fbf | ||
|  | df93094d67 | ||
|  | e1db33ac7a | ||
|  | c319370d4e | 
							
								
								
									
										4
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
								
							| @@ -52,3 +52,7 @@ updates: | ||||
|     interval: "daily" | ||||
|     time: "22:00" | ||||
|     timezone: Africa/Abidjan | ||||
| - package-ecosystem: "github-actions" | ||||
|   directory: "/" | ||||
|   schedule: | ||||
|     interval: "monthly" | ||||
|   | ||||
							
								
								
									
										709
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										709
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -26,6 +26,12 @@ env: | ||||
|   CI_WORKSPACE: ${{ github.workspace }} | ||||
|   TAS_ENVIRONMENT: ./packaging/tests/environment | ||||
|   TAS_SCRIPTS: ../alfresco-community-repo/packaging/tests/scripts | ||||
|   AUTH0_CLIENT_ID: ${{ secrets.AUTH0_OIDC_ADMIN_CLIENT_ID }} | ||||
|   AUTH0_CLIENT_SECRET: ${{ secrets.AUTH0_OIDC_CLIENT_SECRET }} | ||||
|   AUTH0_ADMIN_PASSWORD: ${{ secrets.AUTH0_OIDC_ADMIN_PASSWORD }} | ||||
|   # Report Portal settings | ||||
|   RP_LAUNCH_PREFIX: "${{ github.workflow }} - ${{ github.job }}" | ||||
|   RP_PROJECT: alfresco-backend | ||||
|  | ||||
| jobs: | ||||
|   prepare: | ||||
| @@ -35,7 +41,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -55,7 +61,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -68,55 +74,58 @@ jobs: | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
|   veracode_sast: | ||||
|     name: "Pipeline SAST Scan" | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [prepare] | ||||
|     if: > | ||||
|       (github.ref_name == 'master' || startsWith(github.ref_name, 'release/') || github.event_name == 'pull_request') && | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/github-download-file@v5.6.0 | ||||
|         with: | ||||
|           token: ${{ secrets.BOT_GITHUB_TOKEN }} | ||||
|           repository: "Alfresco/veracode-baseline-archive" | ||||
|           file-path: "alfresco-community-repo/alfresco-community-repo-baseline.json" | ||||
|           target: "baseline.json" | ||||
|       - name: "Build" | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: | | ||||
|           bash ./scripts/ci/init.sh | ||||
|           bash ./scripts/ci/build.sh | ||||
|       - name: "Run SAST Scan" | ||||
|         uses: veracode/Veracode-pipeline-scan-action@v1.0.10 | ||||
|         with: | ||||
|           vid: ${{ secrets.VERACODE_API_ID }} | ||||
|           vkey: ${{ secrets.VERACODE_API_KEY }} | ||||
|           file: "packaging/war/target/alfresco.war" | ||||
|           fail_build: true | ||||
|           project_name: alfresco-community-repo | ||||
|           issue_details: true | ||||
|           veracode_policy_name: Alfresco Default | ||||
|           summary_output: true | ||||
|           summary_output_file: results.json | ||||
|           summary_display: true | ||||
|           baseline_file: baseline.json | ||||
|       - name: Upload scan result | ||||
|         if: success() || failure() | ||||
|         run: zip readable_output.zip results.json | ||||
|       - name: Upload Artifact | ||||
|         if: success() || failure() | ||||
|         uses: actions/upload-artifact@v3 | ||||
|         with: | ||||
|           name: Veracode Pipeline-Scan Results (Human Readable) | ||||
|           path: readable_output.zip | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
| #  SEE: ACS-6931 Currently times out after an hour. TO BE RESTORED after resolving the issue with Veracode Support. | ||||
| # | ||||
| #  veracode_sast: | ||||
| #    name: "Pipeline SAST Scan" | ||||
| #    runs-on: ubuntu-latest | ||||
| #    needs: [prepare] | ||||
| #    if: > | ||||
| #      (github.ref_name == 'master' || startsWith(github.ref_name, 'release/') || github.event_name == 'pull_request') && | ||||
| #      github.actor != 'dependabot[bot]' && | ||||
| #      !contains(github.event.head_commit.message, '[skip tests]') && | ||||
| #      !contains(github.event.head_commit.message, '[force') | ||||
| #    steps: | ||||
| #      - uses: actions/checkout@v4 | ||||
| #      - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
| #      - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2 | ||||
| #      - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| #      - uses: Alfresco/alfresco-build-tools/.github/actions/github-download-file@v5.6.0 | ||||
| #        with: | ||||
| #          token: ${{ secrets.BOT_GITHUB_TOKEN }} | ||||
| #          repository: "Alfresco/veracode-baseline-archive" | ||||
| #          file-path: "alfresco-community-repo/alfresco-community-repo-baseline.json" | ||||
| #          target: "baseline.json" | ||||
| #      - name: "Build" | ||||
| #        timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
| #        run: | | ||||
| #          bash ./scripts/ci/init.sh | ||||
| #          bash ./scripts/ci/build.sh | ||||
| #      - name: "Run SAST Scan" | ||||
| #        uses: veracode/Veracode-pipeline-scan-action@v1.0.10 | ||||
| #        with: | ||||
| #          vid: ${{ secrets.VERACODE_API_ID }} | ||||
| #          vkey: ${{ secrets.VERACODE_API_KEY }} | ||||
| #          file: "packaging/war/target/alfresco.war" | ||||
| #          fail_build: true | ||||
| #          project_name: alfresco-community-repo | ||||
| #          issue_details: true | ||||
| #          veracode_policy_name: Alfresco Default | ||||
| #          summary_output: true | ||||
| #          summary_output_file: results.json | ||||
| #          summary_display: true | ||||
| #          baseline_file: baseline.json | ||||
| #      - name: Upload scan result | ||||
| #        if: success() || failure() | ||||
| #        run: zip readable_output.zip results.json | ||||
| #      - name: Upload Artifact | ||||
| #        if: success() || failure() | ||||
| #        uses: actions/upload-artifact@v3 | ||||
| #        with: | ||||
| #          name: Veracode Pipeline-Scan Results (Human Readable) | ||||
| #          path: readable_output.zip | ||||
| #      - name: "Clean Maven cache" | ||||
| #        run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
|   pmd_scan: | ||||
|     name: "PMD Scan" | ||||
| @@ -136,24 +145,74 @@ jobs: | ||||
|           classpath-build-command: "mvn test-compile -ntp -Pags -pl \"-:alfresco-community-repo-docker\"" | ||||
|  | ||||
|   all_unit_tests_suite: | ||||
|     name: "Core, Data-Model, Repository - AllUnitTestsSuite - Build and test" | ||||
|     name: ${{ matrix.testName }} - AllUnitTestsSuite - Build and test | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [prepare] | ||||
|     if: > | ||||
|       !contains(github.event.head_commit.message, '[skip repo]') && | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         include: | ||||
|           - testName: Core | ||||
|             testModule: core | ||||
|             testAttributes: "-Dtest=AllCoreUnitTestSuite" | ||||
|           - testName: Data-Model | ||||
|             testModule: data-model | ||||
|             testAttributes: "-Dtest=AllDataModelUnitTestSuite" | ||||
|           - testName: Repository | ||||
|             testModule: repository | ||||
|             testAttributes: "-Dtest=AllUnitTestsSuite" | ||||
|           - testName: Mmt | ||||
|             testModule: mmt | ||||
|             testAttributes: "-Dtest=AllMmtUnitTestSuite" | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testModule }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           mvn -B test -pl core,data-model -am -DfailIfNoTests=false | ||||
|           mvn -B test -pl "repository,mmt" -am "-Dtest=AllUnitTestsSuite,AllMmtUnitTestSuite" -DfailIfNoTests=false | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn -B test -pl ${{ matrix.testModule }} -am ${{ matrix.testAttributes }} -DfailIfNoTests=false "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -182,7 +241,7 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_INSTALLED_ARTIFACTS: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -194,9 +253,45 @@ jobs: | ||||
|       - name: "Set transformers tag" | ||||
|         run: echo "TRANSFORMERS_TAG=$(mvn help:evaluate -Dexpression=dependency.alfresco-transform-core.version -q -DforceStdout)" >> $GITHUB_ENV | ||||
|       - name: "Set up the environment" | ||||
|         run: docker-compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d | ||||
|         run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testSuite }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         run: mvn -B test -pl remote-api -Dtest=${{ matrix.testSuite }} -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)"   | ||||
|           mvn -B test -pl remote-api -Dtest=${{ matrix.testSuite }} -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|             echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -216,18 +311,54 @@ jobs: | ||||
|       matrix: | ||||
|         version: ['10.2.18', '10.4', '10.5'] | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: Run MariaDB ${{ matrix.version }} database | ||||
|         run: docker-compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile mariadb up -d | ||||
|         run: docker compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile mariadb up -d | ||||
|         env: | ||||
|           MARIADB_VERSION: ${{ matrix.version }} | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.version }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         run: mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -243,18 +374,54 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run MariaDB 10.6 database" | ||||
|         run: docker-compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile mariadb up -d | ||||
|         run: docker compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile mariadb up -d | ||||
|         env: | ||||
|           MARIADB_VERSION: 10.6 | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         run: mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -270,18 +437,54 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run MySQL 8 database" | ||||
|         run: docker-compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile mysql up -d | ||||
|         run: docker compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile mysql up -d | ||||
|         env: | ||||
|           MYSQL_VERSION: 8 | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         run: mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=com.mysql.jdbc.Driver -Ddb.name=alfresco -Ddb.url=jdbc:mysql://localhost:3307/alfresco -Ddb.username=alfresco -Ddb.password=alfresco | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)"  | ||||
|           mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=com.mysql.jdbc.Driver -Ddb.name=alfresco -Ddb.url=jdbc:mysql://localhost:3307/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -296,18 +499,54 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run PostgreSQL 13.12 database" | ||||
|         run: docker-compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile postgres up -d | ||||
|         run: docker compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile postgres up -d | ||||
|         env: | ||||
|           POSTGRES_VERSION: 13.12 | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         run: mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)"  | ||||
|           mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -322,18 +561,54 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run PostgreSQL 14.9 database" | ||||
|         run: docker-compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile postgres up -d | ||||
|         run: docker compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile postgres up -d | ||||
|         env: | ||||
|           POSTGRES_VERSION: 14.9 | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         run: mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)"  | ||||
|           mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -348,18 +623,54 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run PostgreSQL 15.4 database" | ||||
|         run: docker-compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile postgres up -d | ||||
|         run: docker compose -f ./scripts/ci/docker-compose/docker-compose-db.yaml --profile postgres up -d | ||||
|         env: | ||||
|           POSTGRES_VERSION: 15.4 | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         run: mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)"  | ||||
|           mvn -B test -pl repository -am -Dtest=AllDBTestsTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -372,21 +683,57 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run ActiveMQ" | ||||
|         run: docker-compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile activemq up -d | ||||
|         run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile activemq up -d | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.13.1 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         run: mvn -B test -pl repository -am -Dtest=CamelRoutesTest,CamelComponentsTest -DfailIfNoTests=false | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn -B test -pl repository -am -Dtest=MessagingUnitTestSuite -DfailIfNoTests=false "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.13.1 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
|   repository_app_context_test_suites: | ||||
|     name: Repository - ${{ matrix.testSuite }} | ||||
|     name: Repository - ${{ matrix.testSuite }} ${{ matrix.idp }} | ||||
|     runs-on: ubuntu-latest | ||||
|     needs: [prepare] | ||||
|     if: > | ||||
| @@ -408,6 +755,11 @@ jobs: | ||||
|           - testSuite: AppContext05TestSuite | ||||
|             compose-profile: with-sso | ||||
|             mvn-options: '-Didentity-service.auth-server-url=http://${HOST_IP}:8999/auth -Dauthentication.chain=identity-service1:identity-service,alfrescoNtlm1:alfrescoNtlm' | ||||
|             idp: Keycloak | ||||
|           - testSuite: AppContext05TestSuite | ||||
|             compose-profile: default | ||||
|             mvn-options: '-Didentity-service.auth-server-url=https://dev-ps-alfresco.auth0.com/ -Dauthentication.chain=identity-service1:identity-service,alfrescoNtlm1:alfrescoNtlm -Didentity-service.audience=http://localhost:3000 -Didentity-service.resource=${AUTH0_CLIENT_ID} -Didentity-service.credentials.secret=${AUTH0_CLIENT_SECRET} -Didentity-service.public-client=false -Didentity-service.realm= -Didentity-service.client-id.validation.disabled=false -Dadmin.user=admin@alfresco.com -Dadmin.password=${AUTH0_ADMIN_PASSWORD} -Dauth0.enabled=true -Dauth0.admin.password=${AUTH0_ADMIN_PASSWORD} -Didentity-service.principal-attribute=nickname' | ||||
|             idp: Auth0 | ||||
|           - testSuite: AppContext06TestSuite | ||||
|             compose-profile: with-transform-core-aio | ||||
|           - testSuite: AppContextExtraTestSuite | ||||
| @@ -423,7 +775,7 @@ jobs: | ||||
|             disabledHostnameVerification: false | ||||
|             mvn-options: '-Dencryption.ssl.keystore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.keystore -Dencryption.ssl.truststore.location=${CI_WORKSPACE}/keystores/alfresco/alfresco.truststore' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -445,9 +797,45 @@ jobs: | ||||
|             echo "HOSTNAME_VERIFICATION_DISABLED=false" >> "$GITHUB_ENV" | ||||
|           fi | ||||
|       - name: "Set up the environment" | ||||
|         run: docker-compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d | ||||
|         run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.testSuite }} ${{ matrix.idp }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         run: mvn -B test -pl repository -am -Dtest=${{ matrix.testSuite }} -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco ${{ matrix.mvn-options }} | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn -B test -pl repository -am -Dtest=${{ matrix.testSuite }} -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco ${{ matrix.mvn-options }} "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -492,7 +880,7 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_LOCAL_IMAGES: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -508,16 +896,51 @@ jobs: | ||||
|       - name: "Build TAS integration tests" | ||||
|         if: ${{ matrix.test-name }} == 'Integration TAS tests' | ||||
|         run: mvn install -pl :alfresco-community-repo-integration-test -am -DskipTests -Pall-tas-tests | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} - ${{ matrix.test-name }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         id: tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: mvn -B verify -f packaging/tests/${{ matrix.pom-dir }}/pom.xml -Pall-tas-tests,${{ matrix.test-profile }} -Denvironment=default -DrunBugs=false | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn -B verify -f packaging/tests/${{ matrix.pom-dir }}/pom.xml -Pall-tas-tests,${{ matrix.test-profile }} -Denvironment=default -DrunBugs=false "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Print output after success" | ||||
|         if: ${{ always() && steps.tests.outcome == 'success' }} | ||||
|         run: ${TAS_SCRIPTS}/output_tests_run.sh "packaging/tests/${{ matrix.pom-dir }}" | ||||
|       - name: "Print output after failure" | ||||
|         if: ${{ always() && steps.tests.outcome == 'failure' }} | ||||
|         run: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/${{ matrix.pom-dir }}" | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -530,16 +953,52 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|       - name: "Init" | ||||
|         run: bash ./scripts/ci/init.sh | ||||
|       - name: "Run Postgres 15.4 database" | ||||
|         run: docker-compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile postgres up -d | ||||
|         run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile postgres up -d | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Run tests" | ||||
|         run: mvn -B test -pl :alfresco-share-services -am -Dtest=ShareServicesTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco | ||||
|         id: run-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn -B test -pl :alfresco-share-services -am -Dtest=ShareServicesTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -560,7 +1019,7 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_INSTALLED_ARTIFACTS: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -569,9 +1028,21 @@ jobs: | ||||
|         run: | | ||||
|           bash ./scripts/ci/init.sh | ||||
|           bash ./scripts/ci/build.sh | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} 0${{ matrix.part }} - (PostgreSQL) ${{ matrix.test-name }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Verify" | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: mvn --file amps/ags/pom.xml -B verify -Dmaven.javadoc.skip=true -Dmaven.source.skip=true -Pags -Pstart-postgres -PagsAllTestSuitePt${{ matrix.part }} ${{ env.LOG_WARN }} | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn --file amps/ags/pom.xml -B verify -Dmaven.javadoc.skip=true -Dmaven.source.skip=true -Pags -Pstart-postgres -PagsAllTestSuitePt${{ matrix.part }} ${{ env.LOG_WARN }} "${args[@]}" | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -592,7 +1063,7 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_INSTALLED_ARTIFACTS: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -601,9 +1072,21 @@ jobs: | ||||
|         run: | | ||||
|           bash ./scripts/ci/init.sh | ||||
|           bash ./scripts/ci/build.sh | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} 0${{ matrix.part }} - (MySQL) ${{ matrix.test-name }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Verify" | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: mvn --file amps/ags/pom.xml -B verify -Dmaven.javadoc.skip=true -Dmaven.source.skip=true -Pags -Pstart-mysql -PagsAllTestSuitePt${{ matrix.part }} ${{ env.LOG_WARN }} | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn --file amps/ags/pom.xml -B verify -Dmaven.javadoc.skip=true -Dmaven.source.skip=true -Pags -Pstart-mysql -PagsAllTestSuitePt${{ matrix.part }} ${{ env.LOG_WARN }} "${args[@]}" | ||||
|       - name: "Clean Maven cache" | ||||
|         run: bash ./scripts/ci/cleanup_cache.sh | ||||
|  | ||||
| @@ -620,7 +1103,7 @@ jobs: | ||||
|     env: | ||||
|       REQUIRES_LOCAL_IMAGES: true | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
| @@ -635,9 +1118,45 @@ jobs: | ||||
|           ${{ env.TAS_SCRIPTS }}/start-compose.sh ./amps/ags/rm-community/rm-community-repo/docker-compose.yml | ||||
|           ${{ env.TAS_SCRIPTS }}/wait-for-alfresco-start.sh "http://localhost:8080/alfresco" | ||||
|           mvn -B install -pl :alfresco-governance-services-automation-community-rest-api -am -Pags -Pall-tas-tests -DskipTests | ||||
|       - name: "Prepare Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-prepare@v5.1.0 | ||||
|         id: rp-prepare | ||||
|         with: | ||||
|           rp-launch-prefix: ${{ env.RP_LAUNCH_PREFIX }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-use-static-launch-name: true | ||||
|       - name: "Add GitHub Step Summary" | ||||
|         env: | ||||
|           RP_ENABLED: ${{ steps.rp-prepare.outputs.enabled }} | ||||
|           RP_KEY: ${{ steps.rp-prepare.outputs.key }} | ||||
|           RP_URL: ${{ steps.rp-prepare.outputs.url }} | ||||
|         run: bash scripts/ci/add_step_summary.sh | ||||
|       - name: "Test" | ||||
|         id: run-tests | ||||
|         timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }} | ||||
|         run: mvn -B test -pl :alfresco-governance-services-automation-community-rest-api -Dskip.automationtests=false -Pags -Pall-tas-tests | ||||
|         env: | ||||
|           RP_OPTS: ${{ steps.rp-prepare.outputs.mvn-opts }} | ||||
|         run: | | ||||
|           eval "args=($RP_OPTS)" | ||||
|           mvn -B test -pl :alfresco-governance-services-automation-community-rest-api -Dskip.automationtests=false -Pags -Pall-tas-tests "${args[@]}" | ||||
|         continue-on-error: true | ||||
|       - name: "Update GitHub Step Summary" | ||||
|         run: | | ||||
|           echo "#### ⏱ After Tests: $(date -u +'%Y-%m-%d %H:%M:%S%:z')" >> $GITHUB_STEP_SUMMARY | ||||
|       - name: "Summarize Report Portal" | ||||
|         uses: Alfresco/alfresco-build-tools/.github/actions/reportportal-summarize@v5.1.0 | ||||
|         id: rp-summarize | ||||
|         with: | ||||
|           tests-outcome: ${{ steps.run-tests.outcome }} | ||||
|           rp-launch-key: ${{ steps.rp-prepare.outputs.key }} | ||||
|           rp-project: ${{ env.RP_PROJECT }} | ||||
|           rp-token: ${{ secrets.REPORT_PORTAL_TOKEN }} | ||||
|       - name: "Exit on failure" | ||||
|         if: steps.run-tests.outcome != 'success' | ||||
|         run: | | ||||
|           echo "::error title=run-tests::Tests failed: re-throwing on error." | ||||
|           exit 1 | ||||
|       - name: "Configure AWS credentials" | ||||
|         if: ${{ always() }} | ||||
|         uses: aws-actions/configure-aws-credentials@v1 | ||||
| @@ -666,7 +1185,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[skip tests]') && | ||||
|       !contains(github.event.head_commit.message, '[force]') | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/free-hosted-runner-disk-space@v1.35.2       | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.35.2 | ||||
|   | ||||
							
								
								
									
										4
									
								
								.github/workflows/master_release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/master_release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -31,7 +31,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[no release]') && | ||||
|       github.event_name != 'pull_request' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
| @@ -60,7 +60,7 @@ jobs: | ||||
|       !contains(github.event.head_commit.message, '[no downstream]') && | ||||
|       github.event_name != 'pull_request' | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/checkout@v4 | ||||
|         with: | ||||
|           persist-credentials: false | ||||
|       - uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.35.2 | ||||
|   | ||||
| @@ -71,7 +71,7 @@ the _alfresco-internal_ repository: | ||||
|    </snapshots> | ||||
| </repository> | ||||
| ``` | ||||
|   | ||||
|  | ||||
|  | ||||
| For additional instructions you can check the official Maven documentation: | ||||
| * [setting up repositories](https://maven.apache.org/guides/mini/guide-multiple-repositories.html) | ||||
| @@ -227,7 +227,7 @@ If only the Community or Enterprise images need to be built than the same comman | ||||
|  | ||||
| The Docker images of the Repo can be started independently from Share running the following command in the rm-repo-enterprise or rm-repo-community folder which contains the Docker-compose.yml file: | ||||
| ``` | ||||
| docker-compose up | ||||
| docker compose up | ||||
| ``` | ||||
| > Be aware of the fact that the Share images can not be started independently from Repo | ||||
|  | ||||
| @@ -237,5 +237,5 @@ e.g. In order to start an instance of rm-enterprise-repo and rm-enterprise-share | ||||
|  | ||||
| If you have a license for jRebel then this can be used from the rm-community-share or rm-enterprise-share directories with: | ||||
| ``` | ||||
| docker-compose -f docker-compose.yml -f jrebel-docker-compose.yml --project-name agsdev up --build --force-recreate | ||||
| docker compose -f docker-compose.yml -f jrebel-docker-compose.yml --project-name agsdev up --build --force-recreate | ||||
| ``` | ||||
|   | ||||
| @@ -4,9 +4,9 @@ set -x | ||||
| # Display running containers | ||||
| docker ps | ||||
|  | ||||
| alfrescoContainerId=$(docker ps -a | grep '_alfresco_' | awk '{print $1}') | ||||
| shareContainerId=$(docker ps -a | grep '_share_' | awk '{print $1}') | ||||
| solrContainerId=$(docker ps -a | grep '_search_' | awk '{print $1}') | ||||
| alfrescoContainerId=$(docker ps -a | grep '\-alfresco\-' | awk '{print $1}') | ||||
| shareContainerId=$(docker ps -a | grep '\-share\-' | awk '{print $1}') | ||||
| solrContainerId=$(docker ps -a | grep '\-search\-' | awk '{print $1}') | ||||
|  | ||||
| docker logs $alfrescoContainerId > alfresco.log | ||||
| if [ -n "$shareContainerId" ]; then | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|       <version>23.2.0.31</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>23.2.0.31</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-automation-community-repo</artifactId> | ||||
|       <version>23.2.0.31</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <build> | ||||
| @@ -74,6 +74,22 @@ | ||||
|          <artifactId>alfresco-testng</artifactId> | ||||
|          <version>1.1</version> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>com.epam.reportportal</groupId> | ||||
|          <artifactId>agent-java-testng</artifactId> | ||||
|          <scope>test</scope> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>com.squareup.okhttp3</groupId> | ||||
|          <artifactId>okhttp</artifactId> | ||||
|          <scope>test</scope> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>org.awaitility</groupId> | ||||
|          <artifactId>awaitility</artifactId> | ||||
|          <version>${dependency.awaitility.version}</version> | ||||
|          <scope>test</scope> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>org.apache.commons</groupId> | ||||
|          <artifactId>commons-collections4</artifactId> | ||||
| @@ -96,11 +112,11 @@ | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>org.bouncycastle</groupId> | ||||
|          <artifactId>bcprov-jdk15to18</artifactId> | ||||
|          <artifactId>bcprov-jdk18on</artifactId> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>org.bouncycastle</groupId> | ||||
|          <artifactId>bcpkix-jdk15to18</artifactId> | ||||
|          <artifactId>bcpkix-jdk18on</artifactId> | ||||
|       </dependency> | ||||
|    </dependencies> | ||||
| </project> | ||||
|   | ||||
| @@ -39,6 +39,7 @@ import org.alfresco.rest.rm.community.requests.gscore.GSCoreAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.ActionsExecutionAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.FilesAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.HoldsAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.RMUserAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; | ||||
| @@ -243,4 +244,14 @@ public class RestAPIFactory | ||||
|     { | ||||
|         return getGSCoreAPI(null).usingActionsExecutionsAPI(); | ||||
|     } | ||||
|  | ||||
|     public HoldsAPI getHoldsAPI() | ||||
|     { | ||||
|         return getGSCoreAPI(null).usingHoldsAPI(); | ||||
|     } | ||||
|  | ||||
|     public HoldsAPI getHoldsAPI(UserModel userModel) | ||||
|     { | ||||
|         return getGSCoreAPI(userModel).usingHoldsAPI(); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -61,7 +61,6 @@ public class FilePlanComponentFields | ||||
|     public static final String PROPERTIES_RECORD_SEARCH_DISPOSITION_EVENTS = "rma:recordSearchDispositionEvents"; | ||||
|     public static final String PROPERTIES_DECLASSIFICATION_REVIEW_COMPLETED_BY = "rma:declassificationReviewCompletedBy"; | ||||
|     public static final String PROPERTIES_DECLASSIFICATION_REVIEW_COMPLETED_AT = "rma:declassificationReviewCompletedAt"; | ||||
|      | ||||
|  | ||||
|     /** File plan properties */ | ||||
|     public static final String PROPERTIES_COMPONENT_ID = "st:componentId"; | ||||
|   | ||||
| @@ -0,0 +1,83 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import java.util.Objects; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @Builder | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class Hold extends TestModel | ||||
| { | ||||
|     @JsonProperty(required = true) | ||||
|     private String id; | ||||
|  | ||||
|     @JsonProperty(required = true) | ||||
|     private String name; | ||||
|  | ||||
|     @JsonProperty(required = true) | ||||
|     private String description; | ||||
|  | ||||
|     @JsonProperty(required = true) | ||||
|     private String reason; | ||||
|  | ||||
|     @Override | ||||
|     public boolean equals(Object o) | ||||
|     { | ||||
|         if (this == o) | ||||
|         { | ||||
|             return true; | ||||
|         } | ||||
|         if (o == null || getClass() != o.getClass()) | ||||
|         { | ||||
|             return false; | ||||
|         } | ||||
|         Hold hold = (Hold) o; | ||||
|         return Objects.equals(id, hold.id) && Objects.equals(name, hold.name) | ||||
|             && Objects.equals(description, hold.description) && Objects.equals(reason, hold.reason); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int hashCode() | ||||
|     { | ||||
|         return Objects.hash(id, name, description, reason); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,60 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.rest.search.RestRequestQueryModel; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold bulk request | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @Builder | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldBulkOperation extends TestModel | ||||
| { | ||||
|     public enum HoldBulkOperationType | ||||
|     { | ||||
|         ADD | ||||
|     } | ||||
|  | ||||
|     @JsonProperty(required = true) | ||||
|     private RestRequestQueryModel query; | ||||
|     @JsonProperty(required = true) | ||||
|     private HoldBulkOperationType op; | ||||
| } | ||||
| @@ -0,0 +1,50 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold bulk request entry | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @Builder | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldBulkOperationEntry | ||||
| { | ||||
|     private String bulkStatusId; | ||||
|  | ||||
|     private long totalItems; | ||||
| } | ||||
| @@ -0,0 +1,68 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold bulk request | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @Builder | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldBulkStatus extends TestModel | ||||
| { | ||||
|     private String bulkStatusId; | ||||
|  | ||||
|     private String startTime; | ||||
|  | ||||
|     private String endTime; | ||||
|  | ||||
|     private long processedItems; | ||||
|  | ||||
|     private long errorsCount; | ||||
|  | ||||
|     private long totalItems; | ||||
|  | ||||
|     private String lastError; | ||||
|  | ||||
|     private Status status; | ||||
|  | ||||
|     public enum Status | ||||
|     { | ||||
|         PENDING, | ||||
|         IN_PROGRESS, | ||||
|         DONE | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,38 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import org.alfresco.rest.core.RestModels; | ||||
|  | ||||
| /** | ||||
|  * Handle collection of {@link HoldBulkStatusEntry} | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public class HoldBulkStatusCollection extends RestModels<HoldBulkStatusEntry, HoldBulkStatusCollection> | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.rest.core.RestModels; | ||||
|  | ||||
| @Builder | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldBulkStatusEntry extends RestModels<HoldBulkStatus, HoldBulkStatusEntry> | ||||
| { | ||||
|     private HoldBulkStatus entry; | ||||
| } | ||||
| @@ -0,0 +1,52 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold child | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @Builder | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldChild extends TestModel | ||||
| { | ||||
|     @JsonProperty(required = true) | ||||
|     private String id; | ||||
| } | ||||
| @@ -0,0 +1,38 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import org.alfresco.rest.core.RestModels; | ||||
|  | ||||
| /** | ||||
|  * Handle collection of {@link HoldChildEntry} | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public class HoldChildCollection extends RestModels<HoldChildEntry, HoldChildCollection> | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,52 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.rest.core.RestModels; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold child entry | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @Builder | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldChildEntry extends RestModels<Hold, HoldChildEntry> | ||||
| { | ||||
|     @JsonProperty | ||||
|     private HoldChild entry; | ||||
| } | ||||
| @@ -0,0 +1,38 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import org.alfresco.rest.core.RestModels; | ||||
|  | ||||
| /** | ||||
|  * Handle collection of {@link HoldEntry} | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public class HoldCollection extends RestModels<HoldEntry, HoldCollection> | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,52 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold deletion reason | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @Builder | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldDeletionReason extends TestModel | ||||
| { | ||||
|     @JsonProperty | ||||
|     private String reason; | ||||
| } | ||||
| @@ -0,0 +1,52 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.rest.core.RestModels; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold child entry | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @Builder | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| public class HoldDeletionReasonEntry extends RestModels<HoldDeletionReason, HoldDeletionReasonEntry> | ||||
| { | ||||
|     @JsonProperty | ||||
|     private HoldDeletionReason entry; | ||||
| } | ||||
| @@ -26,31 +26,27 @@ | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.EqualsAndHashCode; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
| import org.alfresco.rest.core.RestModels; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold entry | ||||
|  * | ||||
|  * @author Rodica Sutu | ||||
|  * @since 3.2 | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @Builder | ||||
| @Data | ||||
| @EqualsAndHashCode(callSuper = true) | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| @JsonIgnoreProperties (ignoreUnknown = true) | ||||
| public class HoldEntry extends TestModel | ||||
| public class HoldEntry extends RestModels<Hold, HoldEntry> | ||||
| { | ||||
|     @JsonProperty (required = true) | ||||
|     private String name; | ||||
|  | ||||
|     @JsonProperty (required = true) | ||||
|     private String nodeRef; | ||||
|     @JsonProperty | ||||
|     private Hold entry; | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,56 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.model.hold.v0; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import lombok.AllArgsConstructor; | ||||
| import lombok.Builder; | ||||
| import lombok.Data; | ||||
| import lombok.NoArgsConstructor; | ||||
| import org.alfresco.utility.model.TestModel; | ||||
|  | ||||
| /** | ||||
|  * POJO for hold entry | ||||
|  * | ||||
|  * @author Rodica Sutu | ||||
|  * @since 3.2 | ||||
|  */ | ||||
| @Builder | ||||
| @Data | ||||
| @NoArgsConstructor | ||||
| @AllArgsConstructor | ||||
| @JsonIgnoreProperties (ignoreUnknown = true) | ||||
| public class HoldEntry extends TestModel | ||||
| { | ||||
|     @JsonProperty (required = true) | ||||
|     private String name; | ||||
|  | ||||
|     @JsonProperty (required = true) | ||||
|     private String nodeRef; | ||||
| } | ||||
| @@ -37,6 +37,7 @@ import org.alfresco.rest.rm.community.requests.gscore.api.ActionsExecutionAPI; | ||||
| import org.alfresco.rest.rm.community.requests.RMModelRequest; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.FilesAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.HoldsAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.RMUserAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; | ||||
| @@ -190,4 +191,6 @@ public class GSCoreAPI extends RMModelRequest | ||||
|     { | ||||
|         return new ActionsExecutionAPI(getRmRestWrapper()); | ||||
|     } | ||||
|  | ||||
|     public HoldsAPI usingHoldsAPI() { return new HoldsAPI(getRmRestWrapper()); } | ||||
| } | ||||
|   | ||||
| @@ -38,6 +38,8 @@ import static org.springframework.http.HttpMethod.PUT; | ||||
|  | ||||
| import org.alfresco.rest.core.RMRestWrapper; | ||||
| import org.alfresco.rest.rm.community.model.fileplan.FilePlan; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldCollection; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryCollection; | ||||
| import org.alfresco.rest.rm.community.requests.RMModelRequest; | ||||
| @@ -213,4 +215,74 @@ public class FilePlanAPI extends RMModelRequest | ||||
|                 parameters)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a hold. | ||||
|      * | ||||
|      * @param holdModel The hold model | ||||
|      * @param filePlanId The identifier of a file plan | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The created {@link Hold} | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>{@code filePlanId} is not a valid format or {@code filePlanId} is invalid</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to add children to {@code filePlanId}</li> | ||||
|      *  <li>{@code filePlanIds} does not exist</li> | ||||
|      *  <li>new name clashes with an existing node in the current parent container</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public Hold createHold(Hold holdModel, String filePlanId, String parameters) | ||||
|     { | ||||
|         mandatoryString("filePlanId", filePlanId); | ||||
|         mandatoryObject("holdModel", holdModel); | ||||
|  | ||||
|         return getRmRestWrapper().processModel(Hold.class, requestWithBody( | ||||
|             POST, | ||||
|             toJson(holdModel), | ||||
|             "file-plans/{filePlanId}/holds", | ||||
|             filePlanId, | ||||
|             parameters | ||||
|                                                                           )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #createHold(Hold, String, String)} | ||||
|      */ | ||||
|     public Hold createHold(Hold holdModel, String filePlanId) | ||||
|     { | ||||
|         return createHold(holdModel, filePlanId, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the holds of a file plan. | ||||
|      * | ||||
|      * @param filePlanId The identifier of a file plan | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The {@link HoldCollection} for the given {@code filePlanId} | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to read {@code filePlanId}</li> | ||||
|      *  <li>{@code filePlanId} does not exist</li> | ||||
|      *</ul> | ||||
|      */ | ||||
|     public HoldCollection getHolds(String filePlanId, String parameters) | ||||
|     { | ||||
|         mandatoryString("filePlanId", filePlanId); | ||||
|  | ||||
|         return getRmRestWrapper().processModels(HoldCollection.class, simpleRequest( | ||||
|             GET, | ||||
|             "file-plans/{filePlanId}/holds?{parameters}", | ||||
|             filePlanId, | ||||
|             parameters | ||||
|                                                                                    )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #getHolds(String, String)} | ||||
|      */ | ||||
|     public HoldCollection getHolds(String filePlanId) | ||||
|     { | ||||
|         return getHolds(filePlanId, EMPTY); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,403 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.requests.gscore.api; | ||||
|  | ||||
| import static org.alfresco.rest.core.RestRequest.requestWithBody; | ||||
| import static org.alfresco.rest.core.RestRequest.simpleRequest; | ||||
| import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject; | ||||
| import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryString; | ||||
| import static org.alfresco.rest.rm.community.util.PojoUtility.toJson; | ||||
| import static org.apache.commons.lang3.StringUtils.EMPTY; | ||||
| import static org.springframework.http.HttpMethod.DELETE; | ||||
| import static org.springframework.http.HttpMethod.GET; | ||||
| import static org.springframework.http.HttpMethod.POST; | ||||
| import static org.springframework.http.HttpMethod.PUT; | ||||
|  | ||||
| import org.alfresco.rest.core.RMRestWrapper; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkOperation; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkOperationEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatus; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatusCollection; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChild; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChildCollection; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldDeletionReason; | ||||
| import org.alfresco.rest.rm.community.requests.RMModelRequest; | ||||
|  | ||||
| /** | ||||
|  * Holds REST API Wrapper | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public class HoldsAPI extends RMModelRequest | ||||
| { | ||||
|  | ||||
|     /** | ||||
|      * @param rmRestWrapper | ||||
|      */ | ||||
|     public HoldsAPI(RMRestWrapper rmRestWrapper) | ||||
|     { | ||||
|         super(rmRestWrapper); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets a hold. | ||||
|      * | ||||
|      * @param holdId The identifier of a hold | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The {@link Hold} for the given {@code holdId} | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>{@code holdId} is not a valid format</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to read {@code holdId}</li> | ||||
|      *  <li>{@code holdId} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public Hold getHold(String holdId, String parameters) | ||||
|     { | ||||
|         mandatoryString("holdId", holdId); | ||||
|  | ||||
|         return getRmRestWrapper().processModel(Hold.class, simpleRequest( | ||||
|             GET, | ||||
|             "holds/{holdId}?{parameters}", | ||||
|             holdId, | ||||
|             parameters | ||||
|                                                                         )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #getHold(String, String)} | ||||
|      */ | ||||
|     public Hold getHold(String holdId) | ||||
|     { | ||||
|         mandatoryString("holdId", holdId); | ||||
|  | ||||
|         return getHold(holdId, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Updates a hold. | ||||
|      * | ||||
|      * @param holdModel     The hold model which holds the information | ||||
|      * @param holdId        The identifier of the hold | ||||
|      * @param parameters          The URL parameters to add | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>the update request is invalid or {@code holdId} is not a valid format or {@code holdModel} is invalid</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to update {@code holdId}</li> | ||||
|      *  <li>{@code holdId} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public Hold updateHold(Hold holdModel, String holdId, String parameters) | ||||
|     { | ||||
|         mandatoryObject("holdModel", holdModel); | ||||
|         mandatoryString("holdId", holdId); | ||||
|  | ||||
|         return getRmRestWrapper().processModel(Hold.class, requestWithBody( | ||||
|             PUT, | ||||
|             toJson(holdModel), | ||||
|             "holds/{holdId}?{parameters}", | ||||
|             holdId, | ||||
|             parameters | ||||
|                                                                             )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #updateHold(Hold, String, String)} | ||||
|      */ | ||||
|     public Hold updateHold(Hold holdModel, String holdId) | ||||
|     { | ||||
|         mandatoryObject("holdModel", holdModel); | ||||
|         mandatoryString("holdId", holdId); | ||||
|  | ||||
|         return updateHold(holdModel, holdId, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Deletes a hold. | ||||
|      * | ||||
|      * @param holdId The identifier of a hold | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>{@code holdId} is not a valid format</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to delete {@code holdId}</li> | ||||
|      *  <li>{@code holdId} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public void deleteHold(String holdId) | ||||
|     { | ||||
|         mandatoryString("holdId", holdId); | ||||
|  | ||||
|         getRmRestWrapper().processEmptyModel(simpleRequest( | ||||
|             DELETE, | ||||
|             "holds/{holdId}", | ||||
|             holdId | ||||
|                                                           )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Deletes a hold and stores a reason for deletion in the audit log. | ||||
|      * | ||||
|      * @param reason        The reason for hold deletion | ||||
|      * @param holdId        The identifier of a hold | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>{@code holdId} is not a valid format or {@code reason} is invalid</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to delete {@code holdId}</li> | ||||
|      *  <li>{@code holdId} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public HoldDeletionReason deleteHoldWithReason(HoldDeletionReason reason, String holdId) | ||||
|     { | ||||
|         mandatoryObject("reason", reason); | ||||
|         mandatoryString("holdId", holdId); | ||||
|  | ||||
|         return getRmRestWrapper().processModel(HoldDeletionReason.class, requestWithBody( | ||||
|             POST, | ||||
|             toJson(reason), | ||||
|             "holds/{holdId}/delete", | ||||
|             holdId | ||||
|                                                           )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds the relationship between a child and a parent hold. | ||||
|      * | ||||
|      * @param holdChild The hold child model | ||||
|      * @param holdId The identifier of a hold | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The created {@link Hold} | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>{@code holdId} is not a valid format or {@code holdId} is invalid</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to add children to {@code holdId}</li> | ||||
|      *  <li>{@code holdId} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public HoldChild addChildToHold(HoldChild holdChild, String holdId, String parameters) | ||||
|     { | ||||
|         mandatoryObject("holdId", holdId); | ||||
|  | ||||
|         return getRmRestWrapper().processModel(HoldChild.class, requestWithBody( | ||||
|             POST, | ||||
|             toJson(holdChild), | ||||
|             "holds/{holdId}/children", | ||||
|             holdId, | ||||
|             parameters)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #addChildToHold(HoldChild, String, String)} | ||||
|      */ | ||||
|     public HoldChild addChildToHold(HoldChild holdChild, String holdId) | ||||
|     { | ||||
|         return addChildToHold(holdChild, holdId, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the children of a hold. | ||||
|      * | ||||
|      * @param holdId The identifier of a hold | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The {@link HoldChildCollection} for the given {@code holdId} | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to read {@code holdId}</li> | ||||
|      *  <li>{@code holdId} does not exist</li> | ||||
|      *</ul> | ||||
|      */ | ||||
|     public HoldChildCollection getChildren(String holdId, String parameters) | ||||
|     { | ||||
|         mandatoryString("holdId", holdId); | ||||
|  | ||||
|         return getRmRestWrapper().processModels(HoldChildCollection.class, simpleRequest( | ||||
|             GET, | ||||
|             "holds/{holdId}/children", | ||||
|             holdId, | ||||
|             parameters | ||||
|                                                                                    )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #getChildren(String, String)} | ||||
|      */ | ||||
|     public HoldChildCollection getChildren(String holdId) | ||||
|     { | ||||
|         return getChildren(holdId, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Deletes the relationship between a child and a parent hold. | ||||
|      * | ||||
|      * @param holdChildId The identifier of hold child | ||||
|      * @param holdId The identifier of a hold | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>{@code holdId} or {@code holdChildId} is invalid</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to delete children from {@code holdId}</li> | ||||
|      *  <li>{@code holdId} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public void deleteHoldChild(String holdId, String holdChildId, String parameters) | ||||
|     { | ||||
|         mandatoryString("holdId", holdId); | ||||
|         mandatoryString("holdChildId", holdChildId); | ||||
|  | ||||
|         getRmRestWrapper().processEmptyModel(simpleRequest( | ||||
|             DELETE, | ||||
|             "holds/{holdId}/children/{holdChildId}", | ||||
|             holdId, | ||||
|             holdChildId, | ||||
|             parameters | ||||
|                                                           )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #deleteHoldChild(String, String, String)} | ||||
|      */ | ||||
|     public void deleteHoldChild(String holdId, String holdChildId) | ||||
|     { | ||||
|         deleteHoldChild(holdId, holdChildId, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Starts a bulk process for a hold. | ||||
|      * | ||||
|      * @param holdBulkOperation The bulk operation details | ||||
|      * @param hold The identifier of a hold | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The {@link HoldBulkOperationEntry} for the started bulk process | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>{@code hold} or {@code holdBulkOperation} is invalid</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to start a bulk process for {@code hold}</li> | ||||
|      *  <li>{@code hold} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public HoldBulkOperationEntry startBulkProcess(HoldBulkOperation holdBulkOperation, String hold, String parameters) | ||||
|     { | ||||
|         mandatoryObject("holdBulkOperation", holdBulkOperation); | ||||
|         mandatoryString("hold", hold); | ||||
|  | ||||
|         return getRmRestWrapper().processModel(HoldBulkOperationEntry.class, requestWithBody( | ||||
|             POST, | ||||
|             toJson(holdBulkOperation), | ||||
|             "holds/{hold}/bulk", | ||||
|             hold, | ||||
|             parameters | ||||
|                                                                                           )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #startBulkProcess(HoldBulkOperation, String, String)} | ||||
|      */ | ||||
|     public HoldBulkOperationEntry startBulkProcess(HoldBulkOperation holdBulkOperation, String hold) | ||||
|     { | ||||
|         return startBulkProcess(holdBulkOperation, hold, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the status of a bulk process for a hold. | ||||
|      * | ||||
|      * @param holdId The identifier of a hold | ||||
|      * @param holdBulkStatusId The identifier of a bulk status operation | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The {@link HoldBulkStatus} for the given {@code holdId} and {@code holdBulkStatusId} | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *  <li>{@code holdId} or {@code holdBulkStatusId} is invalid</li> | ||||
|      *  <li>authentication fails</li> | ||||
|      *  <li>current user does not have permission to get the bulk status for {@code holdId}</li> | ||||
|      *  <li>{@code holdId} or {@code holdBulkStatusId} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public HoldBulkStatus getBulkStatus(String holdId, String holdBulkStatusId, String parameters) | ||||
|     { | ||||
|         mandatoryString("holdId", holdId); | ||||
|         mandatoryString("holdBulkStatusId", holdBulkStatusId); | ||||
|  | ||||
|         return getRmRestWrapper().processModel(HoldBulkStatus.class, simpleRequest( | ||||
|             GET, | ||||
|             "holds/{holdId}/bulk-statuses/{holdBulkStatusId}", | ||||
|             holdId, | ||||
|             holdBulkStatusId, | ||||
|             parameters | ||||
|                                                                                    )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #getBulkStatus(String, String, String)} | ||||
|      */ | ||||
|     public HoldBulkStatus getBulkStatus(String holdId, String holdBulkStatusId) | ||||
|     { | ||||
|         return getBulkStatus(holdId, holdBulkStatusId, EMPTY); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the statuses of all bulk processes for a hold. | ||||
|      * | ||||
|      * @param holdId The identifier of a hold | ||||
|      * @param parameters The URL parameters to add | ||||
|      * @return The {@link HoldBulkStatusCollection} for the given {@code holdId} | ||||
|      * @throws RuntimeException for the following cases: | ||||
|      * <ul> | ||||
|      *     <li>{@code holdId} is invalid</li> | ||||
|      *     <li>authentication fails</li> | ||||
|      *     <li>current user does not have permission to get the bulk statuses for {@code holdId}</li> | ||||
|      *     <li>{@code holdId} does not exist</li> | ||||
|      * </ul> | ||||
|      */ | ||||
|     public HoldBulkStatusCollection getBulkStatuses(String holdId, String parameters) | ||||
|     { | ||||
|         mandatoryString("holdId", holdId); | ||||
|  | ||||
|         return getRmRestWrapper().processModels(HoldBulkStatusCollection.class, simpleRequest( | ||||
|             GET, | ||||
|             "holds/{holdId}/bulk-statuses", | ||||
|             holdId, | ||||
|             parameters | ||||
|                                                                                              )); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * See {@link #getBulkStatuses(String, String)} | ||||
|      */ | ||||
|     public HoldBulkStatusCollection getBulkStatuses(String holdId) | ||||
|     { | ||||
|         return getBulkStatuses(holdId, EMPTY); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -36,7 +36,7 @@ import java.util.stream.Collectors; | ||||
|  | ||||
| import org.alfresco.rest.core.v0.APIUtils; | ||||
| import org.alfresco.rest.core.v0.BaseAPI; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.v0.HoldEntry; | ||||
| import org.alfresco.rest.rm.community.util.PojoUtility; | ||||
| import org.alfresco.utility.model.UserModel; | ||||
| import org.apache.http.HttpResponse; | ||||
|   | ||||
| @@ -31,18 +31,18 @@ import static java.util.Arrays.asList; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON; | ||||
| import static org.alfresco.rest.rm.community.model.audit.AuditEvents.ADD_TO_HOLD; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; | ||||
| import static org.alfresco.rest.rm.community.utils.RMSiteUtil.FILE_PLAN_PATH; | ||||
| import static org.alfresco.utility.Utility.buildPath; | ||||
| import static org.alfresco.utility.Utility.removeLastSlash; | ||||
| import static org.alfresco.utility.data.RandomData.getRandomName; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR; | ||||
| import static org.springframework.http.HttpStatus.FORBIDDEN; | ||||
| import static org.testng.AssertJUnit.assertEquals; | ||||
| import static org.testng.AssertJUnit.assertTrue; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | ||||
| import com.google.common.collect.ImmutableMap; | ||||
| @@ -50,12 +50,13 @@ import com.google.common.collect.ImmutableMap; | ||||
| import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.audit.AuditEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChild; | ||||
| import org.alfresco.rest.rm.community.model.record.Record; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; | ||||
| import org.alfresco.rest.rm.community.model.user.UserPermissions; | ||||
| import org.alfresco.rest.rm.community.model.user.UserRoles; | ||||
| import org.alfresco.rest.v0.HoldsAPI; | ||||
| import org.alfresco.rest.v0.service.RMAuditService; | ||||
| import org.alfresco.rest.v0.service.RoleService; | ||||
| import org.alfresco.test.AlfrescoTest; | ||||
| @@ -85,8 +86,6 @@ public class AuditAddToHoldTests extends BaseRMRestTest | ||||
|     @Autowired | ||||
|     private RMAuditService rmAuditService; | ||||
|     @Autowired | ||||
|     private HoldsAPI holdsAPI; | ||||
|     @Autowired | ||||
|     private RoleService roleService; | ||||
|  | ||||
|     private UserModel rmAdmin, rmManagerNoReadOnHold, rmManagerNoReadOnNode; | ||||
| @@ -94,17 +93,22 @@ public class AuditAddToHoldTests extends BaseRMRestTest | ||||
|     private RecordCategory recordCategory; | ||||
|     private RecordCategoryChild recordFolder; | ||||
|     private List<AuditEntry> auditEntries; | ||||
|     private final List<String> holdsList = asList(HOLD1, HOLD2); | ||||
|     private List<String> holdsListRef = new ArrayList<>(); | ||||
|     private String hold1NodeRef; | ||||
|     private String hold2NodeRef; | ||||
|  | ||||
|     @BeforeClass (alwaysRun = true) | ||||
|     public void preconditionForAuditAddToHoldTests() | ||||
|     { | ||||
|         STEP("Create 2 holds."); | ||||
|         hold1NodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), | ||||
|                 getAdminUser().getPassword(), HOLD1, HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         String hold2NodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         hold1NodeRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(HOLD1).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|             .getId(); | ||||
|         hold2NodeRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(HOLD2).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|             .getId(); | ||||
|         holdsListRef = asList(hold1NodeRef, hold2NodeRef); | ||||
|  | ||||
|         STEP("Create a new record category with a record folder."); | ||||
| @@ -169,7 +173,8 @@ public class AuditAddToHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Add node to hold."); | ||||
|         holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), nodeId, HOLD1); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(nodeId).build(), hold1NodeRef); | ||||
|  | ||||
|  | ||||
|         STEP("Check the audit log contains the entry for the add to hold event."); | ||||
|         rmAuditService.checkAuditLogForEvent(getAdminUser(), ADD_TO_HOLD, rmAdmin, nodeName, nodePath, | ||||
| @@ -191,9 +196,8 @@ public class AuditAddToHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Try to add the record to a hold by an user with no rights."); | ||||
|         holdsAPI.addItemsToHolds(rmManagerNoReadOnHold.getUsername(), rmManagerNoReadOnHold.getPassword(), | ||||
|                 SC_INTERNAL_SERVER_ERROR, Collections.singletonList(recordToBeAdded.getId()), | ||||
|                 Collections.singletonList(hold1NodeRef)); | ||||
|         getRestAPIFactory().getHoldsAPI(rmManagerNoReadOnHold).addChildToHold(HoldChild.builder().id(recordToBeAdded.getId()).build(), hold1NodeRef); | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|  | ||||
|         STEP("Check the audit log doesn't contain the entry for the unsuccessful add to hold."); | ||||
|         assertTrue("The list of events should not contain Add to Hold entry ", | ||||
| @@ -215,7 +219,7 @@ public class AuditAddToHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Add record folder to hold."); | ||||
|         holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), notEmptyRecFolder.getId(), HOLD1); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(notEmptyRecFolder.getId()).build(), hold1NodeRef); | ||||
|  | ||||
|         auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), ADD_TO_HOLD); | ||||
|  | ||||
| @@ -239,8 +243,9 @@ public class AuditAddToHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Add record to multiple holds."); | ||||
|         holdsAPI.addItemsToHolds(rmAdmin.getUsername(), rmAdmin.getPassword(), | ||||
|                 Collections.singletonList(recordToBeAdded.getId()), holdsList); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(recordToBeAdded.getId()).build(), hold1NodeRef); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(recordToBeAdded.getId()).build(), hold2NodeRef); | ||||
|  | ||||
|  | ||||
|         auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), ADD_TO_HOLD); | ||||
|  | ||||
| @@ -268,7 +273,7 @@ public class AuditAddToHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Add file to hold."); | ||||
|         holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), contentToBeAdded.getNodeRefWithoutVersion(), HOLD1); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(contentToBeAdded.getNodeRefWithoutVersion()).build(), hold1NodeRef); | ||||
|  | ||||
|         STEP("Check that an user with no Read permissions can't see the entry for the add to hold event."); | ||||
|         assertTrue("The list of events should not contain Add to Hold entry ", | ||||
| @@ -289,7 +294,7 @@ public class AuditAddToHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Add file to hold."); | ||||
|         holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), contentToBeAdded.getNodeRefWithoutVersion(), HOLD1); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(contentToBeAdded.getNodeRefWithoutVersion()).build(), hold1NodeRef); | ||||
|  | ||||
|         auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(rmManagerNoReadOnHold, ADD_TO_HOLD); | ||||
|  | ||||
| @@ -304,7 +309,8 @@ public class AuditAddToHoldTests extends BaseRMRestTest | ||||
|     @AfterClass (alwaysRun = true) | ||||
|     public void cleanUpAuditAddToHoldTests() | ||||
|     { | ||||
|         holdsListRef.forEach(holdRef -> holdsAPI.deleteHold(getAdminUser(), holdRef)); | ||||
|         holdsListRef.forEach(holdRef -> getRestAPIFactory().getHoldsAPI(getAdminUser()).deleteHold(holdRef)); | ||||
|  | ||||
|         dataSite.usingAdmin().deleteSite(privateSite); | ||||
|         asList(rmAdmin, rmManagerNoReadOnHold, rmManagerNoReadOnNode).forEach(user -> getDataUser().usingAdmin().deleteUser(user)); | ||||
|         deleteRecordCategory(recordCategory.getId()); | ||||
|   | ||||
| @@ -31,9 +31,10 @@ import static java.util.Arrays.asList; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON; | ||||
| import static org.alfresco.rest.rm.community.model.audit.AuditEvents.CREATE_HOLD; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR; | ||||
| import static org.springframework.http.HttpStatus.CONFLICT; | ||||
| import static org.testng.AssertJUnit.assertEquals; | ||||
| import static org.testng.AssertJUnit.assertTrue; | ||||
|  | ||||
| @@ -44,8 +45,8 @@ import com.google.common.collect.ImmutableMap; | ||||
|  | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.audit.AuditEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.user.UserRoles; | ||||
| import org.alfresco.rest.v0.HoldsAPI; | ||||
| import org.alfresco.rest.v0.service.RMAuditService; | ||||
| import org.alfresco.rest.v0.service.RoleService; | ||||
| import org.alfresco.test.AlfrescoTest; | ||||
| @@ -73,8 +74,6 @@ public class AuditCreateHoldTests extends BaseRMRestTest | ||||
|     @Autowired | ||||
|     private RMAuditService rmAuditService; | ||||
|     @Autowired | ||||
|     private HoldsAPI holdsAPI; | ||||
|     @Autowired | ||||
|     private RoleService roleService; | ||||
|  | ||||
|     private UserModel rmAdmin, rmManager; | ||||
| @@ -102,8 +101,10 @@ public class AuditCreateHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Create a new hold."); | ||||
|         String hold1NodeRef = holdsAPI.createHoldAndGetNodeRef(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD1, | ||||
|                 HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         String hold1NodeRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(HOLD1).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|             .getId(); | ||||
|         holdsListRef.add(hold1NodeRef); | ||||
|         STEP("Check the audit log contains the entry for the created hold with the hold details."); | ||||
|         rmAuditService.checkAuditLogForEvent(getAdminUser(), CREATE_HOLD, rmAdmin, HOLD1, | ||||
| @@ -120,13 +121,18 @@ public class AuditCreateHoldTests extends BaseRMRestTest | ||||
|     public void createHoldEventIsNotAuditedForExistingHold() | ||||
|     { | ||||
|         STEP("Create a new hold."); | ||||
|         String hold2NodeRef = holdsAPI.createHoldAndGetNodeRef(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         String hold2NodeRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(HOLD2).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|             .getId(); | ||||
|         holdsListRef.add(hold2NodeRef); | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Try to create again the same hold and expect action to fail."); | ||||
|         holdsAPI.createHold(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION, | ||||
|                 SC_INTERNAL_SERVER_ERROR); | ||||
|         getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(HOLD2).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS); | ||||
|         assertStatusCode(CONFLICT); | ||||
|  | ||||
|         STEP("Check the audit log doesn't contain the entry for the second create hold event."); | ||||
|         assertTrue("The list of events should not contain Create Hold entry ", | ||||
| @@ -145,13 +151,17 @@ public class AuditCreateHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Create a new hold."); | ||||
|         holdsAPI.createHold(rmAdmin.getUsername(), rmAdmin.getPassword(), holdName, HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         String nodeRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(holdName).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS).getId(); | ||||
|  | ||||
|         STEP("Get the list of audit entries for the create hold event."); | ||||
|         List<AuditEntry> auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), CREATE_HOLD); | ||||
|  | ||||
|         STEP("Delete the created hold."); | ||||
|         holdsAPI.deleteHold(rmAdmin.getUsername(), rmAdmin.getPassword(), holdName); | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(rmAdmin) | ||||
|             .deleteHold(nodeRef); | ||||
|  | ||||
|         STEP("Get again the list of audit entries for the create hold event."); | ||||
|         List<AuditEntry> auditEntriesAfterDelete = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), CREATE_HOLD); | ||||
| @@ -171,8 +181,10 @@ public class AuditCreateHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Create a new hold."); | ||||
|         String hold3NodeRef = holdsAPI.createHoldAndGetNodeRef(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD3, | ||||
|                 HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         String hold3NodeRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(HOLD3).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS).getId(); | ||||
|  | ||||
|         holdsListRef.add(hold3NodeRef); | ||||
|  | ||||
|         STEP("Check that an user with no Read permissions over the hold can't see the entry for the create hold event"); | ||||
| @@ -183,7 +195,7 @@ public class AuditCreateHoldTests extends BaseRMRestTest | ||||
|     @AfterClass (alwaysRun = true) | ||||
|     public void cleanUpAuditCreateHoldTests() | ||||
|     { | ||||
|         holdsListRef.forEach(holdRef -> holdsAPI.deleteHold(getAdminUser(), holdRef)); | ||||
|         holdsListRef.forEach(holdRef -> getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHold(holdRef)); | ||||
|         asList(rmAdmin, rmManager).forEach(user -> getDataUser().usingAdmin().deleteUser(user)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -31,18 +31,20 @@ import static java.util.Arrays.asList; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON; | ||||
| import static org.alfresco.rest.rm.community.model.audit.AuditEvents.DELETE_HOLD; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR; | ||||
| import static org.springframework.http.HttpStatus.FORBIDDEN; | ||||
| import static org.testng.AssertJUnit.assertTrue; | ||||
|  | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | ||||
| import com.google.common.collect.ImmutableMap; | ||||
|  | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldDeletionReason; | ||||
| import org.alfresco.rest.rm.community.model.user.UserRoles; | ||||
| import org.alfresco.rest.v0.HoldsAPI; | ||||
| import org.alfresco.rest.v0.service.RMAuditService; | ||||
| import org.alfresco.rest.v0.service.RoleService; | ||||
| import org.alfresco.test.AlfrescoTest; | ||||
| @@ -62,14 +64,13 @@ import org.testng.annotations.Test; | ||||
| public class AuditDeleteHoldTests extends BaseRMRestTest | ||||
| { | ||||
|     private final String PREFIX = generateTestPrefix(AuditDeleteHoldTests.class); | ||||
|     private final String HOLD = PREFIX + "holdToBeDeleted"; | ||||
|     private final String HOLD2 = PREFIX + "deleteHold"; | ||||
|     private final String hold = PREFIX + "holdToBeDeleted"; | ||||
|     private final String hold2 = PREFIX + "deleteHold"; | ||||
|     private final String hold3 = PREFIX + "deleteHoldWithReason"; | ||||
|  | ||||
|     @Autowired | ||||
|     private RMAuditService rmAuditService; | ||||
|     @Autowired | ||||
|     private HoldsAPI holdsAPI; | ||||
|     @Autowired | ||||
|     private RoleService roleService; | ||||
|  | ||||
|     private UserModel rmAdmin, rmManager; | ||||
| @@ -79,8 +80,10 @@ public class AuditDeleteHoldTests extends BaseRMRestTest | ||||
|     public void preconditionForAuditDeleteHoldTests() | ||||
|     { | ||||
|         STEP("Create a new hold."); | ||||
|         holdNodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD, | ||||
|                 HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         holdNodeRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(hold).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|             .getId(); | ||||
|  | ||||
|         STEP("Create 2 users with different permissions for the created hold."); | ||||
|         rmAdmin = roleService.createUserWithRMRole(UserRoles.ROLE_RM_ADMIN.roleId); | ||||
| @@ -99,17 +102,51 @@ public class AuditDeleteHoldTests extends BaseRMRestTest | ||||
|     public void deleteHoldEventIsAudited() | ||||
|     { | ||||
|         STEP("Create a new hold."); | ||||
|         String holdRef = holdsAPI.createHoldAndGetNodeRef(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD2, | ||||
|                 HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         String holdRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(hold2).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|             .getId(); | ||||
|  | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Delete the created hold."); | ||||
|         holdsAPI.deleteHold(rmAdmin, holdRef); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHold(holdRef); | ||||
|  | ||||
|         STEP("Check the audit log contains the entry for the deleted hold with the hold details."); | ||||
|         rmAuditService.checkAuditLogForEvent(getAdminUser(), DELETE_HOLD, rmAdmin, HOLD2, | ||||
|                 Collections.singletonList(ImmutableMap.of("new", "", "previous", HOLD2, "name", "Hold Name"))); | ||||
|         rmAuditService.checkAuditLogForEvent(getAdminUser(), DELETE_HOLD, rmAdmin, hold2, | ||||
|                 List.of(ImmutableMap.of("new", "", "previous", hold2, "name", "Hold Name"), | ||||
|                     ImmutableMap.of("new", "", "previous", "", "name", "Hold deletion reason"))); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a hold is deleted with a reason | ||||
|      * When I view the audit log | ||||
|      * Then an entry has been created in the audit log which contains the following: | ||||
|      *      name of the hold | ||||
|      *      hold deletion reason | ||||
|      *      user who deleted the hold | ||||
|      *      date the delete occurred | ||||
|      */ | ||||
|     @Test | ||||
|     public void deleteHoldWithReasonEventIsAudited() | ||||
|     { | ||||
|         STEP("Create a new hold."); | ||||
|         String holdRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(hold3).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|             .getId(); | ||||
|  | ||||
|         String deletionReason = "Test reason"; | ||||
|  | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Delete the created hold with a reason."); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHoldWithReason(HoldDeletionReason.builder().reason(deletionReason).build(), holdRef); | ||||
|  | ||||
|         STEP("Check the audit log contains the entry for the deleted hold with the hold details."); | ||||
|         rmAuditService.checkAuditLogForEvent(getAdminUser(), DELETE_HOLD, rmAdmin, hold3, | ||||
|             List.of(ImmutableMap.of("new", "", "previous", hold3, "name", "Hold Name"), | ||||
|                 ImmutableMap.of("new", "", "previous", deletionReason, "name", "Hold deletion reason"))); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -123,7 +160,8 @@ public class AuditDeleteHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Try to delete a hold by an user with no Read permissions over the hold."); | ||||
|         holdsAPI.deleteHold(rmManager.getUsername(), rmManager.getPassword(), holdNodeRef, SC_INTERNAL_SERVER_ERROR); | ||||
|         getRestAPIFactory().getHoldsAPI(rmManager).deleteHold(holdNodeRef); | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|  | ||||
|         STEP("Check the audit log doesn't contain the entry for the unsuccessful delete hold."); | ||||
|         assertTrue("The list of events should not contain Delete Hold entry ", | ||||
| @@ -133,7 +171,7 @@ public class AuditDeleteHoldTests extends BaseRMRestTest | ||||
|     @AfterClass (alwaysRun = true) | ||||
|     public void cleanUpAuditDeleteHoldTests() | ||||
|     { | ||||
|         holdsAPI.deleteHold(getAdminUser(), holdNodeRef); | ||||
|         getRestAPIFactory().getHoldsAPI(rmManager).deleteHold(holdNodeRef); | ||||
|         asList(rmAdmin, rmManager).forEach(user -> getDataUser().usingAdmin().deleteUser(user)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -25,10 +25,14 @@ | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.audit; | ||||
|  | ||||
| import static java.util.Arrays.asList; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.*; | ||||
|  | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON; | ||||
| import static org.alfresco.rest.rm.community.model.audit.AuditEvents.ADD_TO_HOLD; | ||||
| import static org.alfresco.rest.rm.community.model.audit.AuditEvents.REMOVE_FROM_HOLD; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; | ||||
| import static org.alfresco.utility.data.RandomData.getRandomName; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| @@ -37,20 +41,22 @@ import static org.hamcrest.MatcherAssert.assertThat; | ||||
| import static org.hamcrest.Matchers.empty; | ||||
| import static org.hamcrest.core.IsNot.not; | ||||
| import static org.springframework.http.HttpStatus.CREATED; | ||||
| import static org.testng.AssertJUnit.*; | ||||
| import static org.testng.AssertJUnit.assertFalse; | ||||
| import static org.testng.AssertJUnit.assertTrue; | ||||
|  | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | ||||
| import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.audit.AuditEntry; | ||||
| import org.alfresco.rest.rm.community.model.audit.AuditEvents; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChild; | ||||
| import org.alfresco.rest.rm.community.model.record.Record; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; | ||||
| import org.alfresco.rest.rm.community.model.recordfolder.RecordFolder; | ||||
| import org.alfresco.rest.rm.community.model.user.UserRoles; | ||||
| import org.alfresco.rest.v0.HoldsAPI; | ||||
| import org.alfresco.rest.v0.service.RMAuditService; | ||||
| import org.alfresco.rest.v0.service.RoleService; | ||||
| import org.alfresco.utility.model.FileModel; | ||||
| @@ -69,8 +75,6 @@ public class AuditHoldsTest extends BaseRMRestTest { | ||||
|     @Autowired | ||||
|     private RMAuditService rmAuditService; | ||||
|     @Autowired | ||||
|     private HoldsAPI holdsAPI; | ||||
|     @Autowired | ||||
|     private RoleService roleService; | ||||
|     private UserModel rmAdmin; | ||||
|     private RecordCategory recordCategory; | ||||
| @@ -85,8 +89,11 @@ public class AuditHoldsTest extends BaseRMRestTest { | ||||
|         rmAdmin = roleService.createUserWithRMRole(UserRoles.ROLE_RM_ADMIN.roleId); | ||||
|  | ||||
|         STEP("Create a hold"); | ||||
|         hold1NodeRef = holdsAPI.createHoldAndGetNodeRef(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD1, HOLD_REASON, | ||||
|             HOLD_DESCRIPTION); | ||||
|  | ||||
|         hold1NodeRef = getRestAPIFactory() | ||||
|                 .getFilePlansAPI(rmAdmin) | ||||
|                 .createHold(Hold.builder().name(HOLD1).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|                 .getId(); | ||||
|  | ||||
|         STEP("Create a collaboration site with a test file."); | ||||
|         publicSite = dataSite.usingAdmin().createPublicRandomSite(); | ||||
| @@ -101,9 +108,11 @@ public class AuditHoldsTest extends BaseRMRestTest { | ||||
|  | ||||
|         STEP("Add some items to the hold, then remove them from the hold"); | ||||
|         final List<String> itemsList = asList(testFile.getNodeRefWithoutVersion(), recordToBeAdded.getId(), recordFolder2.getId()); | ||||
|         final List<String> holdsList = Collections.singletonList(HOLD1); | ||||
|         holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), recordToBeAdded.getId(), HOLD1); | ||||
|         holdsAPI.removeItemsFromHolds(rmAdmin.getUsername(), rmAdmin.getPassword(), itemsList, holdsList); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(recordToBeAdded.getId()).build(), hold1NodeRef); | ||||
|         for(String childId : itemsList) | ||||
|         { | ||||
|             getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHoldChild(hold1NodeRef, childId); | ||||
|         } | ||||
|  | ||||
|         STEP("Delete the record folder that was held"); | ||||
|         getRestAPIFactory().getRecordFolderAPI().deleteRecordFolder(recordFolder2.getId()); | ||||
|   | ||||
| @@ -31,18 +31,18 @@ import static java.util.Arrays.asList; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON; | ||||
| import static org.alfresco.rest.rm.community.model.audit.AuditEvents.REMOVE_FROM_HOLD; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; | ||||
| import static org.alfresco.rest.rm.community.utils.RMSiteUtil.FILE_PLAN_PATH; | ||||
| import static org.alfresco.utility.Utility.buildPath; | ||||
| import static org.alfresco.utility.Utility.removeLastSlash; | ||||
| import static org.alfresco.utility.data.RandomData.getRandomName; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR; | ||||
| import static org.springframework.http.HttpStatus.FORBIDDEN; | ||||
| import static org.testng.AssertJUnit.assertEquals; | ||||
| import static org.testng.AssertJUnit.assertTrue; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | ||||
| import com.google.common.collect.ImmutableMap; | ||||
| @@ -50,12 +50,13 @@ import com.google.common.collect.ImmutableMap; | ||||
| import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.audit.AuditEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChild; | ||||
| import org.alfresco.rest.rm.community.model.record.Record; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; | ||||
| import org.alfresco.rest.rm.community.model.user.UserPermissions; | ||||
| import org.alfresco.rest.rm.community.model.user.UserRoles; | ||||
| import org.alfresco.rest.v0.HoldsAPI; | ||||
| import org.alfresco.rest.v0.service.RMAuditService; | ||||
| import org.alfresco.rest.v0.service.RoleService; | ||||
| import org.alfresco.test.AlfrescoTest; | ||||
| @@ -86,8 +87,6 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|     @Autowired | ||||
|     private RMAuditService rmAuditService; | ||||
|     @Autowired | ||||
|     private HoldsAPI holdsAPI; | ||||
|     @Autowired | ||||
|     private RoleService roleService; | ||||
|  | ||||
|     private UserModel rmAdmin, rmManagerNoReadOnHold, rmManagerNoReadOnNode; | ||||
| @@ -96,10 +95,11 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|     private RecordCategoryChild recordFolder, heldRecordFolder; | ||||
|     private Record heldRecord; | ||||
|     private List<AuditEntry> auditEntries; | ||||
|     private final List<String> holdsList = asList(HOLD1, HOLD2, HOLD3); | ||||
|     private List<String> holdsListRef = new ArrayList<>(); | ||||
|     private FileModel heldContent; | ||||
|     private String hold1NodeRef; | ||||
|     private String hold2NodeRef; | ||||
|     private String hold3NodeRef; | ||||
|  | ||||
|     @BeforeClass (alwaysRun = true) | ||||
|     public void preconditionForAuditRemoveFromHoldTests() | ||||
| @@ -111,10 +111,18 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|         privateSite = dataSite.usingUser(rmAdmin).createPrivateRandomSite(); | ||||
|  | ||||
|         STEP("Create new holds."); | ||||
|         hold1NodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(), | ||||
|                 HOLD1, HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         String hold2NodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD2, HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         String hold3NodeRef = holdsAPI.createHoldAndGetNodeRef(getAdminUser().getUsername(), getAdminUser().getPassword(), HOLD3, HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         hold1NodeRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(HOLD1).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|             .getId(); | ||||
|         hold2NodeRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(HOLD2).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|             .getId(); | ||||
|         hold3NodeRef = getRestAPIFactory() | ||||
|             .getFilePlansAPI(rmAdmin) | ||||
|             .createHold(Hold.builder().name(HOLD3).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), FILE_PLAN_ALIAS) | ||||
|             .getId(); | ||||
|         holdsListRef = asList(hold1NodeRef, hold2NodeRef, hold3NodeRef); | ||||
|  | ||||
|         STEP("Create a new record category with a record folder."); | ||||
| @@ -127,9 +135,12 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|         heldRecordFolder = createRecordFolder(recordCategory.getId(), PREFIX + "heldRecFolder"); | ||||
|         heldRecord = createElectronicRecord(recordFolder.getId(), PREFIX + "record"); | ||||
|  | ||||
|         holdsAPI.addItemsToHolds(getAdminUser().getUsername(), getAdminUser().getPassword(), | ||||
|                 asList(heldContent.getNodeRefWithoutVersion(), heldRecordFolder.getId(), heldRecord.getId()), | ||||
|                 holdsList); | ||||
|         holdsListRef.forEach(holdRef -> | ||||
|         { | ||||
|             getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(heldContent.getNodeRefWithoutVersion()).build(), holdRef); | ||||
|             getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(heldRecordFolder.getId()).build(), holdRef); | ||||
|             getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(heldRecord.getId()).build(), holdRef); | ||||
|         }); | ||||
|  | ||||
|         STEP("Create users without rights to remove content from a hold."); | ||||
|         rmManagerNoReadOnHold = roleService.createUserWithSiteRoleRMRoleAndPermission(privateSite, | ||||
| @@ -179,7 +190,7 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Remove node from hold."); | ||||
|         holdsAPI.removeItemFromHold(rmAdmin.getUsername(), rmAdmin.getPassword(), nodeId, HOLD3); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHoldChild(hold3NodeRef, nodeId); | ||||
|  | ||||
|         STEP("Check the audit log contains the entry for the remove from hold event."); | ||||
|         rmAuditService.checkAuditLogForEvent(getAdminUser(), REMOVE_FROM_HOLD, rmAdmin, nodeName, nodePath, | ||||
| @@ -198,9 +209,8 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Try to remove the record from a hold by an user with no rights."); | ||||
|         holdsAPI.removeItemsFromHolds(rmManagerNoReadOnHold.getUsername(), rmManagerNoReadOnHold.getPassword(), | ||||
|                 SC_INTERNAL_SERVER_ERROR, Collections.singletonList(heldRecord.getId()), | ||||
|                 Collections.singletonList(hold1NodeRef)); | ||||
|         getRestAPIFactory().getHoldsAPI(rmManagerNoReadOnHold).deleteHoldChild(hold1NodeRef, heldRecord.getId()); | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|  | ||||
|         STEP("Check the audit log doesn't contain the entry for the unsuccessful remove from hold."); | ||||
|         assertTrue("The list of events should not contain remove from hold entry ", | ||||
| @@ -220,12 +230,12 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|         Record record = createElectronicRecord(notEmptyRecFolder.getId(), PREFIX + "record"); | ||||
|  | ||||
|         STEP("Add the record folder to a hold."); | ||||
|         holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), notEmptyRecFolder.getId(), HOLD1); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(notEmptyRecFolder.getId()).build(), hold1NodeRef); | ||||
|  | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Remove record folder from hold."); | ||||
|         holdsAPI.removeItemFromHold(rmAdmin.getUsername(), rmAdmin.getPassword(), notEmptyRecFolder.getId(), HOLD1); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHoldChild(hold1NodeRef, notEmptyRecFolder.getId()); | ||||
|  | ||||
|         STEP("Get the list of audit entries for the remove from hold event."); | ||||
|         auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), REMOVE_FROM_HOLD); | ||||
| @@ -247,8 +257,8 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Remove record folder from multiple holds."); | ||||
|         holdsAPI.removeItemsFromHolds(rmAdmin.getUsername(), rmAdmin.getPassword(), | ||||
|                 Collections.singletonList(heldRecordFolder.getId()), asList(HOLD1, HOLD2)); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHoldChild(hold1NodeRef, heldRecordFolder.getId()); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHoldChild(hold2NodeRef, heldRecordFolder.getId()); | ||||
|  | ||||
|         STEP("Get the list of audit entries for the remove from hold event."); | ||||
|         auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), REMOVE_FROM_HOLD); | ||||
| @@ -275,12 +285,12 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|         STEP("Add content to a hold."); | ||||
|         FileModel heldFile = dataContent.usingAdmin().usingSite(privateSite) | ||||
|                                            .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|         holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), heldFile.getNodeRefWithoutVersion(), HOLD1); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(heldFile.getNodeRefWithoutVersion()).build(), hold1NodeRef); | ||||
|  | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Remove held content from the hold."); | ||||
|         holdsAPI.removeItemFromHold(rmAdmin.getUsername(), rmAdmin.getPassword(), heldFile.getNodeRefWithoutVersion(), HOLD1); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHoldChild(hold1NodeRef, heldFile.getNodeRefWithoutVersion()); | ||||
|  | ||||
|         STEP("Check that an user with no Read permissions can't see the entry for the remove from hold event."); | ||||
|         assertTrue("The list of events should not contain Remove from Hold entry ", | ||||
| @@ -298,12 +308,12 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|         STEP("Add content to a hold."); | ||||
|         FileModel heldFile = dataContent.usingAdmin().usingSite(privateSite) | ||||
|                                         .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|         holdsAPI.addItemToHold(rmAdmin.getUsername(), rmAdmin.getPassword(), heldFile.getNodeRefWithoutVersion(), HOLD1); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).addChildToHold(HoldChild.builder().id(heldFile.getNodeRefWithoutVersion()).build(), hold1NodeRef); | ||||
|  | ||||
|         rmAuditService.clearAuditLog(); | ||||
|  | ||||
|         STEP("Remove held content from the hold."); | ||||
|         holdsAPI.removeItemFromHold(rmAdmin.getUsername(), rmAdmin.getPassword(), heldFile.getNodeRefWithoutVersion(), HOLD1); | ||||
|         getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHoldChild(hold1NodeRef, heldFile.getNodeRefWithoutVersion()); | ||||
|  | ||||
|         auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(rmManagerNoReadOnHold, REMOVE_FROM_HOLD); | ||||
|  | ||||
| @@ -318,7 +328,7 @@ public class AuditRemoveFromHoldTests extends BaseRMRestTest | ||||
|     @AfterClass (alwaysRun = true) | ||||
|     public void cleanUpAuditRemoveFromHoldTests() | ||||
|     { | ||||
|         holdsListRef.forEach(holdRef -> holdsAPI.deleteHold(getAdminUser(), holdRef)); | ||||
|         holdsListRef.forEach(holdRef -> getRestAPIFactory().getHoldsAPI(rmAdmin).deleteHold(holdRef)); | ||||
|         dataSite.usingAdmin().deleteSite(privateSite); | ||||
|         asList(rmAdmin, rmManagerNoReadOnHold, rmManagerNoReadOnNode).forEach(user -> getDataUser().usingAdmin().deleteUser(user)); | ||||
|         deleteRecordCategory(recordCategory.getId()); | ||||
|   | ||||
| @@ -60,12 +60,15 @@ import static org.testng.Assert.fail; | ||||
| import static org.testng.AssertJUnit.assertEquals; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.NoSuchElementException; | ||||
|  | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.base.DataProviderClass; | ||||
| import org.alfresco.rest.rm.community.model.fileplan.FilePlan; | ||||
| import org.alfresco.rest.rm.community.model.fileplan.FilePlanProperties; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldCollection; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryCollection; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryProperties; | ||||
| @@ -514,5 +517,97 @@ public class FilePlanTests extends BaseRMRestTest | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * <pre> | ||||
|      * Given that a file plan exists | ||||
|      * When I ask the API to create a hold | ||||
|      * Then it is created | ||||
|      * </pre> | ||||
|      */ | ||||
|     @Test | ||||
|     public void createHolds() | ||||
|     { | ||||
|         String holdName = "Hold" + getRandomAlphanumeric(); | ||||
|         String holdDescription = "Description" + getRandomAlphanumeric(); | ||||
|         String holdReason = "Reason" + getRandomAlphanumeric(); | ||||
|  | ||||
|         // Create the hold | ||||
|         Hold hold = Hold.builder() | ||||
|             .name(holdName) | ||||
|             .description(holdDescription) | ||||
|             .reason(holdReason) | ||||
|             .build(); | ||||
|         Hold createdHold = getRestAPIFactory().getFilePlansAPI() | ||||
|             .createHold(hold, FILE_PLAN_ALIAS); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(CREATED); | ||||
|  | ||||
|         assertEquals(createdHold.getName(), holdName); | ||||
|         assertEquals(createdHold.getDescription(), holdDescription); | ||||
|         assertEquals(createdHold.getReason(), holdReason); | ||||
|         assertNotNull(createdHold.getId()); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Test | ||||
|     public void listHolds() | ||||
|     { | ||||
|         // Delete all holds | ||||
|         getRestAPIFactory().getFilePlansAPI().getHolds(FILE_PLAN_ALIAS).getEntries().forEach(holdEntry -> | ||||
|             getRestAPIFactory().getHoldsAPI().deleteHold(holdEntry.getEntry().getId())); | ||||
|  | ||||
|         // Add holds | ||||
|         List<Hold> filePlanHolds = new ArrayList<>(); | ||||
|         for (int i = 0; i < NUMBER_OF_CHILDREN; i++) | ||||
|         { | ||||
|             String holdName = "Hold name " + getRandomAlphanumeric(); | ||||
|             String holdDescription = "Hold Description " + getRandomAlphanumeric(); | ||||
|             String holdReason = "Reason " + getRandomAlphanumeric(); | ||||
|             // Create a hold | ||||
|             Hold hold = Hold.builder() | ||||
|                 .name(holdName) | ||||
|                 .description(holdDescription) | ||||
|                 .reason(holdReason) | ||||
|                 .build(); | ||||
|             Hold createdHold = getRestAPIFactory().getFilePlansAPI() | ||||
|                 .createHold(hold, FILE_PLAN_ALIAS); | ||||
|             assertNotNull(createdHold.getId()); | ||||
|             filePlanHolds.add(createdHold); | ||||
|         } | ||||
|  | ||||
|         // Get holds of a file plan | ||||
|         HoldCollection holdCollection = getRestAPIFactory().getFilePlansAPI() | ||||
|             .getHolds(FILE_PLAN_ALIAS); | ||||
|  | ||||
|         // Check status code | ||||
|         assertStatusCode(OK); | ||||
|  | ||||
|         // Check holds against created list | ||||
|         holdCollection.getEntries().forEach(c -> | ||||
|             { | ||||
|                 Hold hold = c.getEntry(); | ||||
|                 String holdId = hold.getId(); | ||||
|                 assertNotNull(holdId); | ||||
|                 logger.info("Checking hold " + holdId); | ||||
|  | ||||
|                 try | ||||
|                 { | ||||
|                     // Find this hold in created holds list | ||||
|                     Hold createdHold = filePlanHolds.stream() | ||||
|                         .filter(child -> child.getId().equals(holdId)) | ||||
|                         .findFirst() | ||||
|                         .orElseThrow(); | ||||
|  | ||||
|                     assertEquals(createdHold.getName(), hold.getName()); | ||||
|                     assertEquals(createdHold.getDescription(), hold.getDescription()); | ||||
|                     assertEquals(createdHold.getReason(), hold.getReason()); | ||||
|                 } | ||||
|                 catch (NoSuchElementException e) | ||||
|                 { | ||||
|                     fail("No child element for " + hold); | ||||
|                 } | ||||
|             } | ||||
|                                                    ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,532 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.hold; | ||||
|  | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING; | ||||
| import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_READ_RECORDS; | ||||
| import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.awaitility.Awaitility.await; | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertNotNull; | ||||
| import static org.junit.Assert.assertTrue; | ||||
| import static org.springframework.http.HttpStatus.ACCEPTED; | ||||
| import static org.springframework.http.HttpStatus.BAD_REQUEST; | ||||
| import static org.springframework.http.HttpStatus.FORBIDDEN; | ||||
| import static org.springframework.http.HttpStatus.NOT_FOUND; | ||||
| import static org.springframework.http.HttpStatus.UNAUTHORIZED; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.TimeUnit; | ||||
|  | ||||
| import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.dataprep.ContentActions; | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkOperation; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkOperation.HoldBulkOperationType; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkOperationEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatus; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatus.Status; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatusCollection; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldBulkStatusEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChild; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChildEntry; | ||||
| import org.alfresco.rest.rm.community.model.user.UserRoles; | ||||
| import org.alfresco.rest.search.RestRequestQueryModel; | ||||
| import org.alfresco.rest.search.SearchRequest; | ||||
| import org.alfresco.rest.v0.service.RoleService; | ||||
| import org.alfresco.utility.constants.UserRole; | ||||
| import org.alfresco.utility.model.FileModel; | ||||
| import org.alfresco.utility.model.FolderModel; | ||||
| import org.alfresco.utility.model.UserModel; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.testng.annotations.AfterClass; | ||||
| import org.testng.annotations.BeforeClass; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| /** | ||||
|  * API tests for adding items to holds via the bulk process | ||||
|  */ | ||||
| public class AddToHoldsBulkV1Tests extends BaseRMRestTest | ||||
| { | ||||
|     private static final String ACCESS_DENIED_ERROR_MESSAGE = "Access Denied.  You do not have the appropriate " + | ||||
|         "permissions to perform this operation."; | ||||
|     private static final int NUMBER_OF_FILES = 5; | ||||
|     private final List<FileModel> addedFiles = new ArrayList<>(); | ||||
|     private final List<UserModel> users = new ArrayList<>(); | ||||
|     private final List<Hold> holds = new ArrayList<>(); | ||||
|     private Hold hold; | ||||
|     private Hold hold2; | ||||
|     private Hold hold3; | ||||
|     private FolderModel rootFolder; | ||||
|     private HoldBulkOperation holdBulkOperation; | ||||
|     @Autowired | ||||
|     private RoleService roleService; | ||||
|     @Autowired | ||||
|     private ContentActions contentActions; | ||||
|  | ||||
|     @BeforeClass(alwaysRun = true) | ||||
|     public void preconditionForAddContentToHold() | ||||
|     { | ||||
|         STEP("Create a hold."); | ||||
|         hold = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold( | ||||
|             Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION) | ||||
|                 .reason(HOLD_REASON).build(), FILE_PLAN_ALIAS); | ||||
|         holds.add(hold); | ||||
|  | ||||
|         STEP("Create test files."); | ||||
|         testSite = dataSite.usingAdmin().createPublicRandomSite(); | ||||
|  | ||||
|         rootFolder = dataContent.usingAdmin().usingSite(testSite).createFolder(); | ||||
|         FolderModel folder1 = dataContent.usingAdmin().usingResource(rootFolder).createFolder(); | ||||
|         FolderModel folder2 = dataContent.usingAdmin().usingResource(folder1).createFolder(); | ||||
|  | ||||
|         // Add files to subfolders in the site | ||||
|         for (int i = 0; i < NUMBER_OF_FILES; i++) | ||||
|         { | ||||
|             FileModel documentHeld = dataContent.usingAdmin() | ||||
|                 .usingResource(i % 2 == 0 ? folder1 : folder2) | ||||
|                 .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|             addedFiles.add(documentHeld); | ||||
|         } | ||||
|  | ||||
|         RestRequestQueryModel queryReq = getContentFromSiteQuery(testSite.getId()); | ||||
|         SearchRequest searchRequest = new SearchRequest(); | ||||
|         searchRequest.setQuery(queryReq); | ||||
|  | ||||
|         STEP("Wait until all files are searchable."); | ||||
|         await().atMost(30, TimeUnit.SECONDS) | ||||
|             .until(() -> getRestAPIFactory().getSearchAPI(null).search(searchRequest).getPagination() | ||||
|                 .getTotalItems() == NUMBER_OF_FILES); | ||||
|  | ||||
|         holdBulkOperation = HoldBulkOperation.builder() | ||||
|             .query(queryReq) | ||||
|             .op(HoldBulkOperationType.ADD).build(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then the content is added to the hold and the status of the bulk operation is DONE | ||||
|      */ | ||||
|     @Test | ||||
|     public void addContentFromTestSiteToHoldUsingBulkAPI() | ||||
|     { | ||||
|         UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING); | ||||
|         users.add(userAddHoldPermission); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .startBulkProcess(holdBulkOperation, hold.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(ACCEPTED); | ||||
|         assertEquals(NUMBER_OF_FILES, bulkOperationEntry.getTotalItems()); | ||||
|  | ||||
|         STEP("Wait until all files are added to the hold."); | ||||
|         await().atMost(20, TimeUnit.SECONDS).until( | ||||
|             () -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold.getId()).getEntries().size() | ||||
|                 == NUMBER_OF_FILES); | ||||
|         List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getChildren(hold.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map( | ||||
|                 HoldChild::getId).toList(); | ||||
|         assertEquals(addedFiles.stream().map(FileModel::getNodeRefWithoutVersion).sorted().toList(), | ||||
|             holdChildrenNodeRefs.stream().sorted().toList()); | ||||
|  | ||||
|         STEP("Check the bulk status."); | ||||
|         HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId()); | ||||
|         assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null); | ||||
|  | ||||
|         STEP("Check the bulk statuses."); | ||||
|         HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatuses(hold.getId()); | ||||
|         assertEquals(Arrays.asList(holdBulkStatus), holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a folder and all subfolders to a hold using the bulk API | ||||
|      * Then the content is added to the hold and the status of the bulk operation is DONE | ||||
|      */ | ||||
|     @Test | ||||
|     public void addContentFromFolderAndAllSubfoldersToHoldUsingBulkAPI() | ||||
|     { | ||||
|         hold3 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold( | ||||
|             Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION) | ||||
|                 .reason(HOLD_REASON).build(), FILE_PLAN_ALIAS); | ||||
|         holds.add(hold3); | ||||
|  | ||||
|         UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole.SiteCollaborator, hold3.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING); | ||||
|         users.add(userAddHoldPermission); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         // Get content from folder and all subfolders of the root folder | ||||
|         HoldBulkOperation bulkOperation = HoldBulkOperation.builder() | ||||
|             .query(getContentFromFolderAndAllSubfoldersQuery(rootFolder.getNodeRefWithoutVersion())) | ||||
|             .op(HoldBulkOperationType.ADD).build(); | ||||
|         HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .startBulkProcess(bulkOperation, hold3.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(ACCEPTED); | ||||
|         assertEquals(NUMBER_OF_FILES, bulkOperationEntry.getTotalItems()); | ||||
|  | ||||
|         STEP("Wait until all files are added to the hold."); | ||||
|         await().atMost(20, TimeUnit.SECONDS).until( | ||||
|             () -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold3.getId()).getEntries().size() | ||||
|                 == NUMBER_OF_FILES); | ||||
|         List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getChildren(hold3.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map( | ||||
|                 HoldChild::getId).toList(); | ||||
|         assertEquals(addedFiles.stream().map(FileModel::getNodeRefWithoutVersion).sorted().toList(), | ||||
|             holdChildrenNodeRefs.stream().sorted().toList()); | ||||
|  | ||||
|         STEP("Check the bulk status."); | ||||
|         HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatus(hold3.getId(), bulkOperationEntry.getBulkStatusId()); | ||||
|         assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 0, null); | ||||
|  | ||||
|         STEP("Check the bulk statuses."); | ||||
|         HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatuses(hold3.getId()); | ||||
|         assertEquals(Arrays.asList(holdBulkStatus), holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user without the add to hold capability | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then the user receives access denied error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessWithUserWithoutAddToHoldCapability() | ||||
|     { | ||||
|         UserModel userWithoutAddToHoldCapability = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole | ||||
|                 .SiteCollaborator, | ||||
|             hold.getId(), UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING); | ||||
|         users.add(userWithoutAddToHoldCapability); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         getRestAPIFactory().getHoldsAPI(userWithoutAddToHoldCapability) | ||||
|             .startBulkProcess(holdBulkOperation, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response status code and the error message."); | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|         getRestAPIFactory().getRmRestWrapper().assertLastError().containsSummary(ACCESS_DENIED_ERROR_MESSAGE); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user without the filing permission on a hold | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then the user receives access denied error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessWithUserWithoutFilingPermissionOnAHold() | ||||
|     { | ||||
|         // User without filing permission on a hold | ||||
|         UserModel userWithoutPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole.SiteCollaborator, hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_READ_RECORDS); | ||||
|         users.add(userWithoutPermission); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         getRestAPIFactory().getHoldsAPI(userWithoutPermission) | ||||
|             .startBulkProcess(holdBulkOperation, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response status code and the error message."); | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|         getRestAPIFactory().getRmRestWrapper().assertLastError().containsSummary(ACCESS_DENIED_ERROR_MESSAGE); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user without the write permission on all the content | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then all processed items are marked as errors and the last error message contains access denied error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessWithUserWithoutWritePermissionOnTheContent() | ||||
|     { | ||||
|         // User without write permission on the content | ||||
|         UserModel userWithoutPermission = roleService.createUserWithSiteRoleRMRoleAndPermission( | ||||
|             testSite, UserRole.SiteConsumer, | ||||
|             hold.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING); | ||||
|         users.add(userWithoutPermission); | ||||
|  | ||||
|         // Wait until permissions are reverted | ||||
|         SearchRequest searchRequest = new SearchRequest(); | ||||
|         searchRequest.setQuery(holdBulkOperation.getQuery()); | ||||
|         await().atMost(30, TimeUnit.SECONDS) | ||||
|             .until(() -> getRestAPIFactory().getSearchAPI(userWithoutPermission).search(searchRequest).getPagination() | ||||
|                 .getTotalItems() == NUMBER_OF_FILES); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI( | ||||
|             userWithoutPermission).startBulkProcess(holdBulkOperation, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response."); | ||||
|         assertStatusCode(ACCEPTED); | ||||
|  | ||||
|         await().atMost(20, TimeUnit.SECONDS).until(() -> | ||||
|             getRestAPIFactory().getHoldsAPI(userWithoutPermission) | ||||
|                 .getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId()).getStatus() == Status.DONE); | ||||
|  | ||||
|         HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userWithoutPermission) | ||||
|             .getBulkStatus(hold.getId(), bulkOperationEntry.getBulkStatusId()); | ||||
|         assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, NUMBER_OF_FILES, ACCESS_DENIED_ERROR_MESSAGE); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user without the write permission on one file | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then all processed items are added to the hold except the one that the user does not have write permission | ||||
|      * And the status of the bulk operation is DONE, contains the error message and the number of errors is 1 | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessWithUserWithoutWritePermissionOnOneFile() | ||||
|     { | ||||
|         hold2 = getRestAPIFactory().getFilePlansAPI(getAdminUser()).createHold( | ||||
|             Hold.builder().name("HOLD" + generateTestPrefix(AddToHoldsV1Tests.class)).description(HOLD_DESCRIPTION) | ||||
|                 .reason(HOLD_REASON).build(), FILE_PLAN_ALIAS); | ||||
|         holds.add(hold2); | ||||
|  | ||||
|         UserModel userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole.SiteCollaborator, hold2.getId(), UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING); | ||||
|         users.add(userAddHoldPermission); | ||||
|  | ||||
|         contentActions.setPermissionForUser(getAdminUser().getUsername(), getAdminUser().getPassword(), | ||||
|             testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(), | ||||
|             UserRole.SiteConsumer.getRoleId(), false); | ||||
|  | ||||
|         STEP("Add content from the site to the hold using the bulk API."); | ||||
|         HoldBulkOperationEntry bulkOperationEntry = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .startBulkProcess(holdBulkOperation, hold2.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(ACCEPTED); | ||||
|         assertEquals(NUMBER_OF_FILES, bulkOperationEntry.getTotalItems()); | ||||
|  | ||||
|         STEP("Wait until all files are added to the hold."); | ||||
|         await().atMost(30, TimeUnit.SECONDS).until( | ||||
|             () -> getRestAPIFactory().getHoldsAPI(getAdminUser()).getChildren(hold2.getId()).getEntries().size() | ||||
|                 == NUMBER_OF_FILES - 1); | ||||
|         await().atMost(30, TimeUnit.SECONDS).until( | ||||
|             () -> getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|                 .getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId()).getProcessedItems() == NUMBER_OF_FILES); | ||||
|         List<String> holdChildrenNodeRefs = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getChildren(hold2.getId()).getEntries().stream().map(HoldChildEntry::getEntry).map( | ||||
|                 HoldChild::getId).toList(); | ||||
|         assertEquals(addedFiles.stream().skip(1).map(FileModel::getNodeRefWithoutVersion).sorted().toList(), | ||||
|             holdChildrenNodeRefs.stream().sorted().toList()); | ||||
|  | ||||
|         STEP("Check the bulk status."); | ||||
|         HoldBulkStatus holdBulkStatus = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatus(hold2.getId(), bulkOperationEntry.getBulkStatusId()); | ||||
|         assertBulkProcessStatus(holdBulkStatus, NUMBER_OF_FILES, 1, ACCESS_DENIED_ERROR_MESSAGE); | ||||
|  | ||||
|         STEP("Check the bulk statuses."); | ||||
|         HoldBulkStatusCollection holdBulkStatusCollection = getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .getBulkStatuses(hold2.getId()); | ||||
|         assertEquals(Arrays.asList(holdBulkStatus), holdBulkStatusCollection.getEntries().stream().map(HoldBulkStatusEntry::getEntry).toList()); | ||||
|  | ||||
|         // Revert the permissions | ||||
|         contentActions.setPermissionForUser(getAdminUser().getUsername(), getAdminUser().getPassword(), | ||||
|             testSite.getId(), addedFiles.get(0).getName(), userAddHoldPermission.getUsername(), | ||||
|             UserRole.SiteCollaborator.getRoleId(), true); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given an unauthenticated user | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * Then the user receives unauthorized error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessAsUnauthenticatedUser() | ||||
|     { | ||||
|         STEP("Start bulk process as unauthenticated user"); | ||||
|         getRestAPIFactory().getHoldsAPI(new UserModel(getAdminUser().getUsername(), "wrongPassword")) | ||||
|             .startBulkProcess(holdBulkOperation, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(UNAUTHORIZED); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * And the hold does not exist | ||||
|      * Then the user receives not found error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testBulkProcessForNonExistentHold() | ||||
|     { | ||||
|         STEP("Start bulk process for non existent hold"); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).startBulkProcess(holdBulkOperation, "nonExistentHoldId"); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * and the bulk operation is invalid | ||||
|      * Then the user receives bad request error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testGetBulkStatusesForInvalidOperation() | ||||
|     { | ||||
|         STEP("Start bulk process for non existent hold"); | ||||
|  | ||||
|         HoldBulkOperation invalidHoldBulkOperation = HoldBulkOperation.builder().op(null) | ||||
|             .query(holdBulkOperation.getQuery()).build(); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).startBulkProcess(invalidHoldBulkOperation, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(BAD_REQUEST); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * And the hold does not exist | ||||
|      * Then the user receives not found error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testGetBulkStatusForNonExistentHold() | ||||
|     { | ||||
|         STEP("Start bulk process for non existent hold"); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).getBulkStatus("nonExistentHoldId", "nonExistenBulkStatusId"); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * And the bulk status does not exist | ||||
|      * Then the user receives not found error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testGetBulkStatusForNonExistentBulkStatus() | ||||
|     { | ||||
|         STEP("Start bulk process for non bulk status"); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).getBulkStatus(hold.getId(), "nonExistenBulkStatusId"); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from a site to a hold using the bulk API | ||||
|      * And the hold does not exist | ||||
|      * Then the user receives not found error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testGetBulkStatusesForNonExistentHold() | ||||
|     { | ||||
|         STEP("Start bulk process for non existent hold"); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).getBulkStatuses("nonExistentHoldId"); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a user with the add to hold capability and hold filing permission | ||||
|      * When the user adds content from all sites to a hold using the bulk API to exceed the limit (30 items) | ||||
|      * Then the user receives bad request error | ||||
|      */ | ||||
|     @Test | ||||
|     public void testExceedingBulkOperationLimit() | ||||
|     { | ||||
|         RestRequestQueryModel queryReq = new RestRequestQueryModel(); | ||||
|         queryReq.setQuery("TYPE:content"); | ||||
|         queryReq.setLanguage("afts"); | ||||
|  | ||||
|         HoldBulkOperation exceedLimitOp = HoldBulkOperation.builder() | ||||
|             .query(queryReq) | ||||
|             .op(HoldBulkOperationType.ADD).build(); | ||||
|  | ||||
|         STEP("Start bulk process to exceed the limit"); | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).startBulkProcess(exceedLimitOp, hold.getId()); | ||||
|  | ||||
|         STEP("Verify the response status code."); | ||||
|         assertStatusCode(BAD_REQUEST); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private void assertBulkProcessStatus(HoldBulkStatus holdBulkStatus, long expectedProcessedItems, | ||||
|         int expectedErrorsCount, String expectedErrorMessage) | ||||
|     { | ||||
|         assertEquals(Status.DONE, holdBulkStatus.getStatus()); | ||||
|         assertEquals(expectedProcessedItems, holdBulkStatus.getTotalItems()); | ||||
|         assertEquals(expectedProcessedItems, holdBulkStatus.getProcessedItems()); | ||||
|         assertEquals(expectedErrorsCount, holdBulkStatus.getErrorsCount()); | ||||
|         assertNotNull(holdBulkStatus.getStartTime()); | ||||
|         assertNotNull(holdBulkStatus.getEndTime()); | ||||
|  | ||||
|         if (expectedErrorMessage != null) | ||||
|         { | ||||
|             assertTrue(holdBulkStatus.getLastError().contains(expectedErrorMessage)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private RestRequestQueryModel getContentFromSiteQuery(String siteId) | ||||
|     { | ||||
|         RestRequestQueryModel queryReq = new RestRequestQueryModel(); | ||||
|         queryReq.setQuery("SITE:\"" + siteId + "\" and TYPE:content"); | ||||
|         queryReq.setLanguage("afts"); | ||||
|         return queryReq; | ||||
|     } | ||||
|  | ||||
|     private RestRequestQueryModel getContentFromFolderAndAllSubfoldersQuery(String folderId) | ||||
|     { | ||||
|         RestRequestQueryModel queryReq = new RestRequestQueryModel(); | ||||
|         queryReq.setQuery("ANCESTOR:\"workspace://SpacesStore/" + folderId + "\" and TYPE:content"); | ||||
|         queryReq.setLanguage("afts"); | ||||
|         return queryReq; | ||||
|     } | ||||
|  | ||||
|     @AfterClass(alwaysRun = true) | ||||
|     public void cleanupAddToHoldsBulkV1Tests() | ||||
|     { | ||||
|         dataSite.usingAdmin().deleteSite(testSite); | ||||
|         users.forEach(user -> getDataUser().usingAdmin().deleteUser(user)); | ||||
|         holds.forEach(hold -> getRestAPIFactory().getHoldsAPI(getAdminUser()).deleteHold(hold.getId())); | ||||
|     } | ||||
| } | ||||
| @@ -60,7 +60,7 @@ import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.dataprep.ContentActions; | ||||
| import org.alfresco.rest.model.RestNodeModel; | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.v0.HoldEntry; | ||||
| import org.alfresco.rest.rm.community.model.record.Record; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; | ||||
|   | ||||
| @@ -0,0 +1,386 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.hold; | ||||
|  | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.FROZEN_ASPECT; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; | ||||
| import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING; | ||||
| import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_READ_RECORDS; | ||||
| import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_MANAGER; | ||||
| import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; | ||||
| import static org.alfresco.rest.rm.community.utils.CoreUtil.toContentModel; | ||||
| import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.IMAGE_FILE; | ||||
| import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicRecordModel; | ||||
| import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createNonElectronicRecordModel; | ||||
| import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.getFile; | ||||
| import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.apache.commons.httpclient.HttpStatus.SC_BAD_REQUEST; | ||||
| import static org.springframework.http.HttpStatus.CREATED; | ||||
| import static org.springframework.http.HttpStatus.FORBIDDEN; | ||||
| import static org.testng.Assert.assertEquals; | ||||
| import static org.testng.Assert.assertTrue; | ||||
| import static org.testng.AssertJUnit.assertFalse; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.dataprep.ContentActions; | ||||
| import org.alfresco.rest.model.RestNodeAssociationModelCollection; | ||||
| import org.alfresco.rest.model.RestNodeModel; | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChild; | ||||
| import org.alfresco.rest.rm.community.model.record.Record; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; | ||||
| import org.alfresco.rest.rm.community.model.user.UserRoles; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; | ||||
| import org.alfresco.rest.v0.service.RoleService; | ||||
| import org.alfresco.utility.constants.UserRole; | ||||
| import org.alfresco.utility.model.FileModel; | ||||
| import org.alfresco.utility.model.SiteModel; | ||||
| import org.alfresco.utility.model.UserModel; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.springframework.http.HttpStatus; | ||||
| import org.testng.annotations.AfterClass; | ||||
| import org.testng.annotations.BeforeClass; | ||||
| import org.testng.annotations.DataProvider; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| /** | ||||
|  * V1 API tests for adding content/record folder/records to holds | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public class AddToHoldsV1Tests extends BaseRMRestTest | ||||
| { | ||||
|     private static final String ACCESS_DENIED_ERROR_MESSAGE = "Access Denied.  You do not have the appropriate " + | ||||
|         "permissions to perform this operation."; | ||||
|     private static final String INVALID_TYPE_ERROR_MESSAGE = "Only records, record folders or content can be added to a hold."; | ||||
|     private static final String LOCKED_FILE_ERROR_MESSAGE = "Locked content can't be added to a hold."; | ||||
|  | ||||
|     private static final String HOLD = "HOLD" + generateTestPrefix(AddToHoldsV1Tests.class); | ||||
|     private String holdNodeRef; | ||||
|     private SiteModel testSite; | ||||
|     private FileModel documentHeld; | ||||
|     private FileModel contentToAddToHold; | ||||
|     private FileModel contentAddToHoldNoPermission; | ||||
|     private Hold hold; | ||||
|  | ||||
|     private UserModel userAddHoldPermission; | ||||
|     private final List<UserModel> users = new ArrayList<>(); | ||||
|     private final List<String> nodesToBeClean = new ArrayList<>(); | ||||
|  | ||||
|     @Autowired | ||||
|     private RoleService roleService; | ||||
|     @Autowired | ||||
|     private ContentActions contentActions; | ||||
|  | ||||
|     @BeforeClass(alwaysRun = true) | ||||
|     public void preconditionForAddContentToHold() | ||||
|     { | ||||
|         STEP("Create a hold."); | ||||
|         hold = createHold(FILE_PLAN_ALIAS, | ||||
|             Hold.builder().name(HOLD).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), getAdminUser()); | ||||
|         holdNodeRef = hold.getId(); | ||||
|         STEP("Create test files."); | ||||
|         testSite = dataSite.usingAdmin().createPublicRandomSite(); | ||||
|         documentHeld = dataContent.usingAdmin().usingSite(testSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|         contentToAddToHold = dataContent.usingAdmin().usingSite(testSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|         contentAddToHoldNoPermission = dataContent.usingAdmin().usingSite(testSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|  | ||||
|         STEP("Add the content to the hold."); | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(getAdminUser()) | ||||
|             .addChildToHold(HoldChild.builder().id(documentHeld.getNodeRefWithoutVersion()).build(), hold.getId()); | ||||
|  | ||||
|         STEP("Create users"); | ||||
|         userAddHoldPermission = roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, | ||||
|             UserRole.SiteCollaborator, holdNodeRef, UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING); | ||||
|         users.add(userAddHoldPermission); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a hold that contains at least one active content | ||||
|      * When I use the existing REST API to retrieve the contents of the hold | ||||
|      * Then I should see all the active content on hold | ||||
|      */ | ||||
|     @Test | ||||
|     public void retrieveTheContentOfTheHoldUsingV1API() | ||||
|     { | ||||
|         STEP("Retrieve the list of children from the hold and collect the entries that have the name of the active " + | ||||
|             "content held"); | ||||
|         List<String> documentNames = restClient.authenticateUser(getAdminUser()).withCoreAPI() | ||||
|             .usingNode(toContentModel(holdNodeRef)) | ||||
|             .listChildren().getEntries().stream() | ||||
|             .map(RestNodeModel::onModel) | ||||
|             .map(RestNodeModel::getName) | ||||
|             .filter(documentName -> documentName.equals(documentHeld.getName())) | ||||
|             .toList(); | ||||
|  | ||||
|         STEP("Check the list of active content"); | ||||
|         assertEquals(documentNames, Set.of(documentHeld.getName())); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a hold that contains at least one active content | ||||
|      * When I use the existing REST API to retrieve the holds the content is added | ||||
|      * Then the hold where the content held is returned | ||||
|      */ | ||||
|     @Test | ||||
|     public void retrieveTheHoldWhereTheContentIsAdded() | ||||
|     { | ||||
|         RestNodeAssociationModelCollection holdsEntries = getRestAPIFactory() | ||||
|             .getNodeAPI(documentHeld).usingParams("where=(assocType='rma:frozenContent')").getParents(); | ||||
|         Hold retrievedHold = getRestAPIFactory().getHoldsAPI(getAdminUser()) | ||||
|             .getHold(holdsEntries.getEntries().get(0).getModel().getId()); | ||||
|         assertEquals(retrievedHold, hold, "Holds are not equal"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Valid nodes to be added to hold | ||||
|      */ | ||||
|     @DataProvider(name = "validNodesForAddToHold") | ||||
|     public Object[][] getValidNodesForAddToHold() | ||||
|     { | ||||
|         //create electronic and nonElectronic record in record folder | ||||
|         RecordCategoryChild recordFolder = createCategoryFolderInFilePlan(); | ||||
|         RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); | ||||
|         nodesToBeClean.add(recordFolder.getParentId()); | ||||
|         Record electronicRecord = recordFolderAPI.createRecord(createElectronicRecordModel(), recordFolder.getId(), | ||||
|             getFile | ||||
|                 (IMAGE_FILE)); | ||||
|         assertStatusCode(CREATED); | ||||
|  | ||||
|         Record nonElectronicRecord = recordFolderAPI.createRecord(createNonElectronicRecordModel(), | ||||
|             recordFolder.getId()); | ||||
|         assertStatusCode(CREATED); | ||||
|         getRestAPIFactory().getRMUserAPI().addUserPermission(recordFolder.getId(), userAddHoldPermission, | ||||
|             PERMISSION_FILING); | ||||
|  | ||||
|         RecordCategoryChild folderToHold = createCategoryFolderInFilePlan(); | ||||
|         getRestAPIFactory().getRMUserAPI().addUserPermission(folderToHold.getId(), userAddHoldPermission, | ||||
|             PERMISSION_FILING); | ||||
|         nodesToBeClean.add(folderToHold.getParentId()); | ||||
|  | ||||
|         return new String[][] | ||||
|             {       // record folder | ||||
|                 { folderToHold.getId() }, | ||||
|                 //electronic record | ||||
|                 { electronicRecord.getId() }, | ||||
|                 // non electronic record | ||||
|                 { nonElectronicRecord.getId() }, | ||||
|                 // document from collaboration site | ||||
|                 { contentToAddToHold.getNodeRefWithoutVersion() }, | ||||
|             }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given record folder/record/document not on hold | ||||
|      * And a hold | ||||
|      * And file permission on the hold | ||||
|      * And the appropriate capability to add to hold | ||||
|      * When I use the existing REST API to add the node to the hold | ||||
|      * Then the record folder/record/document is added to the hold | ||||
|      * And the item is frozen | ||||
|      * | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     @Test(dataProvider = "validNodesForAddToHold") | ||||
|     public void addValidNodesToHoldWithAllowedUser(String nodeId) throws Exception | ||||
|     { | ||||
|         STEP("Add node to hold with user with permission."); | ||||
|         getRestAPIFactory().getHoldsAPI(userAddHoldPermission) | ||||
|             .addChildToHold(HoldChild.builder().id(nodeId).build(), hold.getId()); | ||||
|  | ||||
|         STEP("Check the node is frozen."); | ||||
|         assertTrue(hasAspect(nodeId, FROZEN_ASPECT)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Data provider with user without correct permission to add to hold and the node ref to be added to hold | ||||
|      * | ||||
|      * @return object with user model and the node ref to be added to hold | ||||
|      */ | ||||
|     @DataProvider(name = "userWithoutPermissionForAddToHold") | ||||
|     public Object[][] getUserWithoutPermissionForAddToHold() | ||||
|     { | ||||
|         //create record folder | ||||
|         RecordCategoryChild recordFolder = createCategoryFolderInFilePlan(); | ||||
|         //create a rm manager and grant read permission over the record folder created | ||||
|         UserModel user = roleService.createUserWithRMRoleAndRMNodePermission(ROLE_RM_MANAGER.roleId, | ||||
|             recordFolder.getId(), | ||||
|             PERMISSION_READ_RECORDS); | ||||
|         getRestAPIFactory().getRMUserAPI().addUserPermission(holdNodeRef, user, PERMISSION_FILING); | ||||
|         nodesToBeClean.add(recordFolder.getParentId()); | ||||
|         return new Object[][] | ||||
|             {       // user without write permission on the content | ||||
|                 { | ||||
|                     roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, UserRole.SiteConsumer, | ||||
|                         holdNodeRef, UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING), | ||||
|                     contentAddToHoldNoPermission.getNodeRefWithoutVersion() | ||||
|                 }, | ||||
|                 // user with write permission on the content and without filling permission on a hold | ||||
|                 { | ||||
|                     roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, UserRole | ||||
|                             .SiteCollaborator, | ||||
|                         holdNodeRef, UserRoles.ROLE_RM_MANAGER, PERMISSION_READ_RECORDS), | ||||
|                     contentAddToHoldNoPermission.getNodeRefWithoutVersion() | ||||
|                 }, | ||||
|                 // user with write permission on the content, filling permission on a hold without add to | ||||
|                 // hold capability | ||||
|                 { | ||||
|                     roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, UserRole | ||||
|                             .SiteCollaborator, | ||||
|                         holdNodeRef, UserRoles.ROLE_RM_POWER_USER, PERMISSION_READ_RECORDS), | ||||
|                     contentAddToHoldNoPermission.getNodeRefWithoutVersion() | ||||
|                 }, | ||||
|                 //user without write permission on RM  record folder | ||||
|                 { | ||||
|                     user, recordFolder.getId() | ||||
|                 }, | ||||
|  | ||||
|             }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a node not on hold | ||||
|      * And a hold | ||||
|      * And user without right permission to add to hold | ||||
|      * When I use the existing REST API to add the node to the hold | ||||
|      * Then the node is not added to the hold | ||||
|      * And the node is not frozen | ||||
|      * | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     @Test(dataProvider = "userWithoutPermissionForAddToHold") | ||||
|     public void addContentToHoldWithUserWithoutHoldPermission(UserModel userModel, String nodeToBeAddedToHold) | ||||
|         throws Exception | ||||
|     { | ||||
|         users.add(userModel); | ||||
|         STEP("Add the node to the hold with user without permission."); | ||||
|  | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(userModel) | ||||
|             .addChildToHold(HoldChild.builder().id(nodeToBeAddedToHold).build(), holdNodeRef); | ||||
|  | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|         getRestAPIFactory().getRmRestWrapper().assertLastError().containsSummary(ACCESS_DENIED_ERROR_MESSAGE); | ||||
|  | ||||
|         STEP("Check the node is not frozen."); | ||||
|         assertFalse(hasAspect(nodeToBeAddedToHold, FROZEN_ASPECT)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Data provider with invalid node types that can be added to a hold | ||||
|      */ | ||||
|     @DataProvider(name = "invalidNodesForAddToHold") | ||||
|     public Object[][] getInvalidNodesForAddToHold() | ||||
|     { | ||||
|         //create locked file | ||||
|         FileModel contentLocked = dataContent.usingAdmin().usingSite(testSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|  | ||||
|         contentActions.checkOut(getAdminUser().getUsername(), getAdminUser().getPassword(), | ||||
|             testSite.getId(), contentLocked.getName()); | ||||
|         RecordCategory category = createRootCategory(getRandomAlphanumeric()); | ||||
|         nodesToBeClean.add(category.getId()); | ||||
|         return new Object[][] | ||||
|             {       // file plan node id | ||||
|                 { getFilePlan(FILE_PLAN_ALIAS).getId(), SC_BAD_REQUEST, INVALID_TYPE_ERROR_MESSAGE }, | ||||
|                 //transfer container | ||||
|                 { getTransferContainer(TRANSFERS_ALIAS).getId(), SC_BAD_REQUEST, INVALID_TYPE_ERROR_MESSAGE }, | ||||
|                 // a record category | ||||
|                 { category.getId(), SC_BAD_REQUEST, INVALID_TYPE_ERROR_MESSAGE }, | ||||
|                 // unfiled records root | ||||
|                 { getUnfiledContainer(UNFILED_RECORDS_CONTAINER_ALIAS).getId(), SC_BAD_REQUEST, | ||||
|                     INVALID_TYPE_ERROR_MESSAGE }, | ||||
|                 // an arbitrary unfiled records folder | ||||
|                 { createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, "Unfiled Folder " + | ||||
|                     getRandomAlphanumeric(), UNFILED_RECORD_FOLDER_TYPE).getId(), SC_BAD_REQUEST, | ||||
|                     INVALID_TYPE_ERROR_MESSAGE }, | ||||
|                 //folder, | ||||
|                 { dataContent.usingAdmin().usingSite(testSite).createFolder().getNodeRef(), SC_BAD_REQUEST, | ||||
|                     INVALID_TYPE_ERROR_MESSAGE }, | ||||
|                 //document locked | ||||
|                 { contentLocked.getNodeRefWithoutVersion(), SC_BAD_REQUEST, LOCKED_FILE_ERROR_MESSAGE } | ||||
|             }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a node that is not a document/record/ record folder ( a valid node type to be added to hold) | ||||
|      * And a hold | ||||
|      * And user without right permission to add to hold | ||||
|      * When I use the existing REST API to add the node to the hold | ||||
|      * Then the node is not added to the hold | ||||
|      * And the node is not frozen | ||||
|      * | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     @Test(dataProvider = "invalidNodesForAddToHold") | ||||
|     public void addInvalidNodesToHold(String itemNodeRef, int responseCode, String errorMessage) throws Exception | ||||
|     { | ||||
|         STEP("Add the node to the hold "); | ||||
|  | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(getAdminUser()) | ||||
|             .addChildToHold(HoldChild.builder().id(itemNodeRef).build(), holdNodeRef); | ||||
|  | ||||
|         assertStatusCode(HttpStatus.valueOf(responseCode)); | ||||
|         getRestAPIFactory().getRmRestWrapper().assertLastError().containsSummary(errorMessage); | ||||
|  | ||||
|         STEP("Check node is not frozen."); | ||||
|         assertFalse(hasAspect(itemNodeRef, FROZEN_ASPECT)); | ||||
|     } | ||||
|  | ||||
|     private Hold createHold(String parentId, Hold hold, UserModel user) | ||||
|     { | ||||
|         FilePlanAPI filePlanAPI = getRestAPIFactory().getFilePlansAPI(user); | ||||
|         return filePlanAPI.createHold(hold, parentId); | ||||
|     } | ||||
|  | ||||
|     @AfterClass(alwaysRun = true) | ||||
|     public void cleanUpAddContentToHold() | ||||
|     { | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).deleteHold(holdNodeRef); | ||||
|         dataSite.usingAdmin().deleteSite(testSite); | ||||
|         users.forEach(user -> getDataUser().usingAdmin().deleteUser(user)); | ||||
|         nodesToBeClean.forEach(this::deleteRecordCategory); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,186 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.hold; | ||||
|  | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric; | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertNotNull; | ||||
| import static org.springframework.http.HttpStatus.NOT_FOUND; | ||||
| import static org.springframework.http.HttpStatus.NO_CONTENT; | ||||
| import static org.springframework.http.HttpStatus.OK; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldDeletionReason; | ||||
| import org.testng.annotations.AfterClass; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| /** | ||||
|  * This class contains the tests for the Holds CRUD V1 API | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public class HoldsTests extends BaseRMRestTest | ||||
| { | ||||
|  | ||||
|     private final List<String> nodeRefs = new ArrayList<>(); | ||||
|  | ||||
|     @Test | ||||
|     public void testGetHold() | ||||
|     { | ||||
|         String holdName = "Hold" + getRandomAlphanumeric(); | ||||
|         String holdDescription = "Description" + getRandomAlphanumeric(); | ||||
|         String holdReason = "Reason" + getRandomAlphanumeric(); | ||||
|  | ||||
|         // Create the hold | ||||
|         Hold hold = Hold.builder() | ||||
|             .name(holdName) | ||||
|             .description(holdDescription) | ||||
|             .reason(holdReason) | ||||
|             .build(); | ||||
|         Hold createdHold = getRestAPIFactory().getFilePlansAPI() | ||||
|             .createHold(hold, FILE_PLAN_ALIAS); | ||||
|  | ||||
|         // Get the hold | ||||
|         Hold receivedHold = getRestAPIFactory().getHoldsAPI().getHold(createdHold.getId()); | ||||
|         nodeRefs.add(receivedHold.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(OK); | ||||
|  | ||||
|         assertEquals(receivedHold.getName(), holdName); | ||||
|         assertEquals(receivedHold.getDescription(), holdDescription); | ||||
|         assertEquals(receivedHold.getReason(), holdReason); | ||||
|         assertNotNull(receivedHold.getId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateHold() | ||||
|     { | ||||
|         String holdName = "Hold" + getRandomAlphanumeric(); | ||||
|         String holdDescription = "Description" + getRandomAlphanumeric(); | ||||
|         String holdReason = "Reason" + getRandomAlphanumeric(); | ||||
|  | ||||
|         // Create the hold | ||||
|         Hold hold = Hold.builder() | ||||
|             .name(holdName) | ||||
|             .description(holdDescription) | ||||
|             .reason(holdReason) | ||||
|             .build(); | ||||
|         Hold createdHold = getRestAPIFactory().getFilePlansAPI() | ||||
|             .createHold(hold, FILE_PLAN_ALIAS); | ||||
|         nodeRefs.add(createdHold.getId()); | ||||
|  | ||||
|         Hold holdModel = Hold.builder() | ||||
|             .name("Updated" + holdName) | ||||
|             .description("Updated" + holdDescription) | ||||
|             .reason("Updated" + holdReason) | ||||
|             .build(); | ||||
|  | ||||
|         // Update the hold | ||||
|         Hold updatedHold = getRestAPIFactory().getHoldsAPI().updateHold(holdModel, createdHold.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(OK); | ||||
|  | ||||
|         assertEquals(updatedHold.getName(), "Updated" + holdName); | ||||
|         assertEquals(updatedHold.getDescription(), "Updated" + holdDescription); | ||||
|         assertEquals(updatedHold.getReason(), "Updated" + holdReason); | ||||
|         assertNotNull(updatedHold.getId()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteHold() | ||||
|     { | ||||
|         String holdName = "Hold" + getRandomAlphanumeric(); | ||||
|         String holdDescription = "Description" + getRandomAlphanumeric(); | ||||
|         String holdReason = "Reason" + getRandomAlphanumeric(); | ||||
|  | ||||
|         // Create the hold | ||||
|         Hold hold = Hold.builder() | ||||
|             .name(holdName) | ||||
|             .description(holdDescription) | ||||
|             .reason(holdReason) | ||||
|             .build(); | ||||
|         Hold createdHold = getRestAPIFactory().getFilePlansAPI() | ||||
|             .createHold(hold, FILE_PLAN_ALIAS); | ||||
|         nodeRefs.add(createdHold.getId()); | ||||
|  | ||||
|         // Delete the hold | ||||
|         getRestAPIFactory().getHoldsAPI().deleteHold(createdHold.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(NO_CONTENT); | ||||
|  | ||||
|         // Try to get the hold | ||||
|         getRestAPIFactory().getHoldsAPI().getHold(createdHold.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testDeleteHoldWithReason() | ||||
|     { | ||||
|         String holdName = "Hold" + getRandomAlphanumeric(); | ||||
|         String holdDescription = "Description" + getRandomAlphanumeric(); | ||||
|         String holdReason = "Reason" + getRandomAlphanumeric(); | ||||
|  | ||||
|         // Create the hold | ||||
|         Hold hold = Hold.builder() | ||||
|             .name(holdName) | ||||
|             .description(holdDescription) | ||||
|             .reason(holdReason) | ||||
|             .build(); | ||||
|         Hold createdHold = getRestAPIFactory().getFilePlansAPI() | ||||
|             .createHold(hold, FILE_PLAN_ALIAS); | ||||
|         nodeRefs.add(createdHold.getId()); | ||||
|  | ||||
|         // Delete the hold with the reason | ||||
|         getRestAPIFactory().getHoldsAPI() | ||||
|             .deleteHoldWithReason(HoldDeletionReason.builder().reason("Example reason").build(), createdHold.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(OK); | ||||
|  | ||||
|         // Try to get the hold | ||||
|         getRestAPIFactory().getHoldsAPI().getHold(createdHold.getId()); | ||||
|  | ||||
|         // Verify the status code | ||||
|         assertStatusCode(NOT_FOUND); | ||||
|     } | ||||
|  | ||||
|     @AfterClass(alwaysRun = true) | ||||
|     public void cleanUpHoldsTests() | ||||
|     { | ||||
|         nodeRefs.forEach(nodeRef -> getRestAPIFactory().getHoldsAPI(getAdminUser()).deleteHold(nodeRef)); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,337 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.hold; | ||||
|  | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.ASPECTS_VITAL_RECORD; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.ASPECTS_VITAL_RECORD_DEFINITION; | ||||
| import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; | ||||
| import static org.alfresco.rest.rm.community.utils.CoreUtil.createBodyForMoveCopy; | ||||
| import static org.alfresco.utility.data.RandomData.getRandomName; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR; | ||||
| import static org.springframework.http.HttpStatus.CREATED; | ||||
| import static org.springframework.http.HttpStatus.FORBIDDEN; | ||||
| import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; | ||||
| import static org.springframework.http.HttpStatus.OK; | ||||
| import static org.testng.Assert.assertNotNull; | ||||
| import static org.testng.Assert.assertTrue; | ||||
| import static org.testng.AssertJUnit.assertFalse; | ||||
|  | ||||
| import java.io.File; | ||||
|  | ||||
| import jakarta.json.Json; | ||||
| import jakarta.json.JsonObject; | ||||
| import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.rest.core.JsonBodyGenerator; | ||||
| import org.alfresco.rest.core.v0.BaseAPI.RM_ACTIONS; | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.common.ReviewPeriod; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChild; | ||||
| import org.alfresco.rest.rm.community.model.record.Record; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; | ||||
| import org.alfresco.rest.rm.community.model.recordfolder.RecordFolder; | ||||
| import org.alfresco.rest.rm.community.model.recordfolder.RecordFolderProperties; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; | ||||
| import org.alfresco.rest.v0.RMRolesAndActionsAPI; | ||||
| import org.alfresco.rest.v0.service.DispositionScheduleService; | ||||
| import org.alfresco.utility.Utility; | ||||
| import org.alfresco.utility.model.FileModel; | ||||
| import org.alfresco.utility.model.FolderModel; | ||||
| import org.alfresco.utility.model.UserModel; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.testng.annotations.AfterClass; | ||||
| import org.testng.annotations.BeforeClass; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| /** | ||||
|  * V1 API tests to check actions on frozen content | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public class PreventActionsOnFrozenContentV1Tests extends BaseRMRestTest | ||||
| { | ||||
|     private static String holdNodeRef; | ||||
|     private static FileModel contentHeld; | ||||
|     private static File updatedFile; | ||||
|     private static FolderModel folderModel; | ||||
|     private static RecordCategoryChild recordFolder; | ||||
|     private static Record recordFrozen; | ||||
|     private static Record recordNotHeld; | ||||
|     private static RecordCategory categoryWithRS; | ||||
|  | ||||
|     private Hold hold; | ||||
|  | ||||
|     @Autowired | ||||
|     private DispositionScheduleService dispositionScheduleService; | ||||
|  | ||||
|     @Autowired | ||||
|     private RMRolesAndActionsAPI rmRolesAndActionsAPI; | ||||
|  | ||||
|     @BeforeClass(alwaysRun = true) | ||||
|     public void preconditionForPreventActionsOnFrozenContent() | ||||
|     { | ||||
|         String holdOne = "HOLD" + generateTestPrefix(PreventActionsOnFrozenContentV1Tests.class); | ||||
|  | ||||
|         STEP("Create a hold."); | ||||
|         hold = createHold(FILE_PLAN_ALIAS, | ||||
|             Hold.builder().name(holdOne).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), getAdminUser()); | ||||
|         holdNodeRef = hold.getId(); | ||||
|  | ||||
|         STEP("Create a test file."); | ||||
|         testSite = dataSite.usingAdmin().createPublicRandomSite(); | ||||
|         contentHeld = dataContent.usingAdmin().usingSite(testSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|  | ||||
|         STEP("Add the file to the hold."); | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(getAdminUser()) | ||||
|             .addChildToHold(HoldChild.builder().id(contentHeld.getNodeRefWithoutVersion()).build(), hold.getId()); | ||||
|  | ||||
|         STEP("Get a file resource."); | ||||
|         updatedFile = Utility.getResourceTestDataFile("SampleTextFile_10kb.txt"); | ||||
|  | ||||
|         STEP("Create a folder withing the test site ."); | ||||
|         folderModel = dataContent.usingAdmin().usingSite(testSite) | ||||
|             .createFolder(); | ||||
|  | ||||
|         STEP("Create a record folder with some records"); | ||||
|         recordFolder = createCategoryFolderInFilePlan(); | ||||
|         recordFrozen = createElectronicRecord(recordFolder.getId(), getRandomName("elRecordFrozen")); | ||||
|         recordNotHeld = createElectronicRecord(recordFolder.getId(), getRandomName("elRecordNotHeld")); | ||||
|         assertStatusCode(CREATED); | ||||
|  | ||||
|         STEP("Add the record to the hold."); | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(getAdminUser()) | ||||
|             .addChildToHold(HoldChild.builder().id(recordFrozen.getId()).build(), hold.getId()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given active content on hold | ||||
|      * When I try to edit the properties | ||||
|      * Or perform an action that edits the properties | ||||
|      * Then I am not successful | ||||
|      */ | ||||
|     @Test | ||||
|     public void editPropertiesForContentHeld() throws Exception | ||||
|     { | ||||
|         STEP("Update name property of the held content"); | ||||
|         JsonObject nameUpdated = Json.createObjectBuilder().add("name", "HeldNameUpdated").build(); | ||||
|         restClient.authenticateUser(getAdminUser()).withCoreAPI().usingNode(contentHeld) | ||||
|             .updateNode(nameUpdated.toString()); | ||||
|  | ||||
|         STEP("Check the request failed."); | ||||
|         restClient.assertStatusCodeIs(FORBIDDEN); | ||||
|         restClient.assertLastError().containsSummary("Frozen content can't be updated."); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Given active content on hold | ||||
|      * When I try to update the content | ||||
|      * Then I am not successful | ||||
|      */ | ||||
|     @Test | ||||
|     public void updateContentForFrozenFile() throws Exception | ||||
|     { | ||||
|         STEP("Update content of the held file"); | ||||
|         restClient.authenticateUser(getAdminUser()).withCoreAPI().usingNode(contentHeld).updateNodeContent(updatedFile); | ||||
|  | ||||
|         STEP("Check the request failed."); | ||||
|         restClient.assertStatusCodeIs(INTERNAL_SERVER_ERROR); | ||||
|         restClient.assertLastError().containsSummary("Frozen content can't be updated."); | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|      * Given active content on hold | ||||
|      * When I try to delete the content | ||||
|      * Then I am not successful | ||||
|      */ | ||||
|     @Test | ||||
|     public void deleteFrozenFile() throws Exception | ||||
|     { | ||||
|         STEP("Delete frozen file"); | ||||
|         restClient.authenticateUser(getAdminUser()).withCoreAPI().usingNode(contentHeld) | ||||
|             .deleteNode(contentHeld.getNodeRefWithoutVersion()); | ||||
|  | ||||
|         STEP("Check the request failed."); | ||||
|         restClient.assertStatusCodeIs(FORBIDDEN); | ||||
|         restClient.assertLastError().containsSummary("Frozen content can't be deleted."); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given active content on hold | ||||
|      * When I try to copy the content | ||||
|      * Then I am not successful | ||||
|      */ | ||||
|     @Test | ||||
|     public void copyFrozenFile() | ||||
|     { | ||||
|         STEP("Copy frozen file"); | ||||
|         String postBody = JsonBodyGenerator.keyValueJson("targetParentId", folderModel.getNodeRef()); | ||||
|         getRestAPIFactory().getNodeAPI(contentHeld).copyNode(postBody); | ||||
|  | ||||
|         STEP("Check the request failed."); | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|         getRestAPIFactory().getRmRestWrapper().assertLastError().containsSummary("Permission was denied"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given active content on hold | ||||
|      * When I try to move the content | ||||
|      * Then I am not successful | ||||
|      */ | ||||
|     @Test | ||||
|     public void moveFrozenFile() throws Exception | ||||
|     { | ||||
|         STEP("Move frozen file"); | ||||
|         getRestAPIFactory().getNodeAPI(contentHeld).move(createBodyForMoveCopy(folderModel.getNodeRef())); | ||||
|  | ||||
|         STEP("Check the request failed."); | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|         getRestAPIFactory().getRmRestWrapper().assertLastError().containsSummary("Frozen content can't be moved."); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a record folder with a frozen record and another record not held | ||||
|      * When I update the record folder and make the records as vital | ||||
|      * Then I am successful and the records not held are marked as vital | ||||
|      * And the frozen nodes have the vital record search properties updated | ||||
|      */ | ||||
|     @Test | ||||
|     public void updateRecordFolderVitalProperties() | ||||
|     { | ||||
|         STEP("Update the vital record properties for the record folder"); | ||||
|         // Create the record folder properties to update | ||||
|         RecordFolder recordFolderToUpdate = RecordFolder.builder() | ||||
|             .properties(RecordFolderProperties.builder() | ||||
|                 .vitalRecordIndicator(true) | ||||
|                 .reviewPeriod(new ReviewPeriod("month", "1")) | ||||
|                 .build()) | ||||
|             .build(); | ||||
|  | ||||
|         // Update the record folder | ||||
|         RecordFolder updatedRecordFolder = getRestAPIFactory().getRecordFolderAPI().updateRecordFolder | ||||
|             (recordFolderToUpdate, | ||||
|                 recordFolder.getId()); | ||||
|         assertStatusCode(OK); | ||||
|         assertTrue(updatedRecordFolder.getAspectNames().contains(ASPECTS_VITAL_RECORD_DEFINITION)); | ||||
|  | ||||
|         STEP("Check the frozen record was not marked as vital"); | ||||
|         recordFrozen = getRestAPIFactory().getRecordsAPI().getRecord(recordFrozen.getId()); | ||||
|         assertFalse(recordFrozen.getAspectNames().contains(ASPECTS_VITAL_RECORD)); | ||||
|         assertTrue(recordFrozen.getProperties().getRecordSearchVitalRecordReviewPeriod().contains("month")); | ||||
|         assertTrue(recordFrozen.getProperties().getRecordSearchVitalRecordReviewPeriodExpression().contains("1")); | ||||
|  | ||||
|         STEP("Check the record not held was marked as vital"); | ||||
|         recordNotHeld = getRestAPIFactory().getRecordsAPI().getRecord(recordNotHeld.getId()); | ||||
|         assertTrue(recordNotHeld.getAspectNames().contains(ASPECTS_VITAL_RECORD)); | ||||
|         assertNotNull(recordNotHeld.getProperties().getReviewAsOf()); | ||||
|         assertTrue(recordNotHeld.getProperties().getRecordSearchVitalRecordReviewPeriod().contains("month")); | ||||
|         assertTrue(recordNotHeld.getProperties().getRecordSearchVitalRecordReviewPeriodExpression().contains("1")); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a record folder with a frozen record and another record not held | ||||
|      * When I add a disposition schedule | ||||
|      * Then I am successful | ||||
|      * And the record search disposition schedule properties are updated | ||||
|      */ | ||||
|     @Test | ||||
|     public void createDispositionScheduleOnCategoryWithHeldChildren() | ||||
|     { | ||||
|         STEP("Create a retention schedule on the category with frozen children"); | ||||
|         RecordCategory categoryWithRS = getRestAPIFactory().getRecordCategoryAPI() | ||||
|             .getRecordCategory(recordFolder.getParentId()); | ||||
|         dispositionScheduleService.createCategoryRetentionSchedule(categoryWithRS.getName(), false); | ||||
|         dispositionScheduleService.addCutOffImmediatelyStep(categoryWithRS.getName()); | ||||
|         dispositionScheduleService.addDestroyWithGhostingImmediatelyAfterCutOff(categoryWithRS.getName()); | ||||
|  | ||||
|         STEP("Check the record folder has a disposition schedule"); | ||||
|         RecordFolder folderWithRS = getRestAPIFactory().getRecordFolderAPI().getRecordFolder(recordFolder.getId()); | ||||
|         assertNotNull(folderWithRS.getProperties().getRecordSearchDispositionAuthority()); | ||||
|         assertNotNull(folderWithRS.getProperties().getRecordSearchDispositionInstructions()); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given a record category with a disposition schedule applied to records | ||||
|      * And the disposition schedule has a retain step  immediately and destroy step immediately | ||||
|      * And a complete record added to one hold | ||||
|      * When I execute the retain action | ||||
|      * Then the action is executed | ||||
|      * And the record search disposition schedule properties are updated | ||||
|      */ | ||||
|     @Test | ||||
|     public void retainActionOnFrozenHeldRecords() | ||||
|     { | ||||
|         STEP("Add a category with a disposition schedule."); | ||||
|         categoryWithRS = createRootCategory(getRandomName("CategoryWithRS")); | ||||
|         dispositionScheduleService.createCategoryRetentionSchedule(categoryWithRS.getName(), true); | ||||
|         dispositionScheduleService.addRetainAfterPeriodStep(categoryWithRS.getName(), "immediately"); | ||||
|         dispositionScheduleService.addDestroyWithGhostingImmediatelyAfterCutOff(categoryWithRS.getName()); | ||||
|  | ||||
|         STEP("Create record folder with a record."); | ||||
|         RecordCategoryChild folder = createFolder(categoryWithRS.getId(), getRandomName("RecFolder")); | ||||
|         Record record = createElectronicRecord(folder.getId(), getRandomName("elRecord")); | ||||
|         completeRecord(record.getId()); | ||||
|  | ||||
|         STEP("Add the record to the hold"); | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(getAdminUser()) | ||||
|             .addChildToHold(HoldChild.builder().id(record.getId()).build(), hold.getId()); | ||||
|  | ||||
|         STEP("Execute the retain action"); | ||||
|         rmRolesAndActionsAPI.executeAction(getAdminUser().getUsername(), getAdminUser().getPassword(), record.getName(), | ||||
|             RM_ACTIONS.END_RETENTION, null, SC_INTERNAL_SERVER_ERROR); | ||||
|  | ||||
|         STEP("Check the record search disposition properties"); | ||||
|         Record recordUpdated = getRestAPIFactory().getRecordsAPI().getRecord(record.getId()); | ||||
|         assertTrue(recordUpdated.getProperties().getRecordSearchDispositionActionName() | ||||
|             .contains(RM_ACTIONS.END_RETENTION.getAction())); | ||||
|         assertTrue(recordUpdated.getProperties().getRecordSearchDispositionPeriod().contains("immediately")); | ||||
|     } | ||||
|  | ||||
|     private Hold createHold(String parentId, Hold hold, UserModel user) | ||||
|     { | ||||
|         FilePlanAPI filePlanAPI = getRestAPIFactory().getFilePlansAPI(user); | ||||
|         return filePlanAPI.createHold(hold, parentId); | ||||
|     } | ||||
|  | ||||
|     @AfterClass(alwaysRun = true) | ||||
|     public void cleanUpPreventActionsOnFrozenContent() | ||||
|     { | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()).deleteHold(holdNodeRef); | ||||
|         dataSite.usingAdmin().deleteSite(testSite); | ||||
|         deleteRecordCategory(recordFolder.getParentId()); | ||||
|         deleteRecordCategory(categoryWithRS.getId()); | ||||
|     } | ||||
| } | ||||
| @@ -52,7 +52,7 @@ import java.util.Set; | ||||
|  | ||||
| import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldEntry; | ||||
| import org.alfresco.rest.rm.community.model.hold.v0.HoldEntry; | ||||
| import org.alfresco.rest.rm.community.model.record.Record; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; | ||||
| import org.alfresco.rest.rm.community.model.user.UserRoles; | ||||
|   | ||||
| @@ -0,0 +1,374 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.hold; | ||||
|  | ||||
| import static java.util.Arrays.asList; | ||||
|  | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_DESCRIPTION; | ||||
| import static org.alfresco.rest.rm.community.base.TestData.HOLD_REASON; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; | ||||
| import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.FROZEN_ASPECT; | ||||
| import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING; | ||||
| import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_READ_RECORDS; | ||||
| import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_MANAGER; | ||||
| import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix; | ||||
| import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.IMAGE_FILE; | ||||
| import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicRecordModel; | ||||
| import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createNonElectronicRecordModel; | ||||
| import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.getFile; | ||||
| import static org.alfresco.utility.report.log.Step.STEP; | ||||
| import static org.springframework.http.HttpStatus.CREATED; | ||||
| import static org.springframework.http.HttpStatus.FORBIDDEN; | ||||
| import static org.testng.Assert.assertFalse; | ||||
| import static org.testng.Assert.assertTrue; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| import java.util.stream.Stream; | ||||
|  | ||||
| import org.alfresco.dataprep.CMISUtil; | ||||
| import org.alfresco.rest.model.RestNodeAssociationModelCollection; | ||||
| import org.alfresco.rest.rm.community.base.BaseRMRestTest; | ||||
| import org.alfresco.rest.rm.community.model.hold.Hold; | ||||
| import org.alfresco.rest.rm.community.model.hold.HoldChild; | ||||
| import org.alfresco.rest.rm.community.model.record.Record; | ||||
| import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; | ||||
| import org.alfresco.rest.rm.community.model.user.UserRoles; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; | ||||
| import org.alfresco.rest.rm.community.utils.CoreUtil; | ||||
| import org.alfresco.rest.v0.service.RoleService; | ||||
| import org.alfresco.utility.constants.UserRole; | ||||
| import org.alfresco.utility.model.FileModel; | ||||
| import org.alfresco.utility.model.SiteModel; | ||||
| import org.alfresco.utility.model.UserModel; | ||||
| import org.springframework.beans.factory.annotation.Autowired; | ||||
| import org.testng.annotations.AfterClass; | ||||
| import org.testng.annotations.BeforeClass; | ||||
| import org.testng.annotations.DataProvider; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| /** | ||||
|  * V1 API tests for removing content/record folder/record from holds | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public class RemoveFromHoldsV1Tests extends BaseRMRestTest | ||||
| { | ||||
|     private static final String HOLD_ONE = "HOLD_ONE" + generateTestPrefix(RemoveFromHoldsV1Tests.class); | ||||
|     private static final String HOLD_TWO = "HOLD_TWO" + generateTestPrefix(RemoveFromHoldsV1Tests.class); | ||||
|     private static final String ACCESS_DENIED_ERROR_MESSAGE = "Access Denied.  You do not have the appropriate " + | ||||
|         "permissions to perform this operation."; | ||||
|  | ||||
|     private SiteModel testSite; | ||||
|     private SiteModel privateSite; | ||||
|     private String holdNodeRefOne; | ||||
|     private FileModel contentHeld; | ||||
|     private FileModel contentAddToManyHolds; | ||||
|     private List<String> holdsListRef = new ArrayList<>(); | ||||
|     private final Set<UserModel> usersToBeClean = new HashSet<>(); | ||||
|     private final Set<String> nodesToBeClean = new HashSet<>(); | ||||
|  | ||||
|     @Autowired | ||||
|     private RoleService roleService; | ||||
|  | ||||
|     @BeforeClass(alwaysRun = true) | ||||
|     public void preconditionForRemoveContentFromHold() | ||||
|     { | ||||
|         STEP("Create two holds."); | ||||
|  | ||||
|         holdNodeRefOne = createHold(FILE_PLAN_ALIAS, | ||||
|             Hold.builder().name(HOLD_ONE).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), | ||||
|             getAdminUser()).getId(); | ||||
|         String holdNodeRefTwo = createHold(FILE_PLAN_ALIAS, | ||||
|             Hold.builder().name(HOLD_TWO).description(HOLD_DESCRIPTION).reason(HOLD_REASON).build(), | ||||
|             getAdminUser()).getId(); | ||||
|         holdsListRef = asList(holdNodeRefOne, holdNodeRefTwo); | ||||
|  | ||||
|         STEP("Create test files."); | ||||
|         testSite = dataSite.usingAdmin().createPublicRandomSite(); | ||||
|         privateSite = dataSite.usingAdmin().createPrivateRandomSite(); | ||||
|         contentHeld = dataContent.usingAdmin().usingSite(testSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|         contentAddToManyHolds = dataContent.usingSite(testSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|  | ||||
|         STEP("Add content to the holds."); | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(getAdminUser()) | ||||
|             .addChildToHold(HoldChild.builder().id(contentHeld.getNodeRefWithoutVersion()).build(), holdNodeRefOne); | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(getAdminUser()) | ||||
|             .addChildToHold(HoldChild.builder().id(contentAddToManyHolds.getNodeRefWithoutVersion()).build(), | ||||
|                 holdNodeRefOne); | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(getAdminUser()) | ||||
|             .addChildToHold(HoldChild.builder().id(contentAddToManyHolds.getNodeRefWithoutVersion()).build(), | ||||
|                 holdNodeRefTwo); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Valid nodes to be removed from hold | ||||
|      */ | ||||
|     @DataProvider(name = "validNodesToRemoveFromHold") | ||||
|     public Object[][] getValidNodesToRemoveFromHold() | ||||
|     { | ||||
|         //create electronic and nonElectronic record in record folder | ||||
|         RecordCategoryChild recordFolder = createCategoryFolderInFilePlan(); | ||||
|         RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI(); | ||||
|         nodesToBeClean.add(recordFolder.getParentId()); | ||||
|         Record electronicRecord = recordFolderAPI.createRecord(createElectronicRecordModel(), recordFolder.getId(), | ||||
|             getFile | ||||
|                 (IMAGE_FILE)); | ||||
|         assertStatusCode(CREATED); | ||||
|         Record nonElectronicRecord = recordFolderAPI.createRecord(createNonElectronicRecordModel(), | ||||
|             recordFolder.getId()); | ||||
|         assertStatusCode(CREATED); | ||||
|  | ||||
|         RecordCategoryChild folderToHeld = createCategoryFolderInFilePlan(); | ||||
|         nodesToBeClean.add(folderToHeld.getParentId()); | ||||
|         Stream.of(electronicRecord.getId(), nonElectronicRecord.getId(), folderToHeld.getId()) | ||||
|             .forEach(id -> getRestAPIFactory() | ||||
|                 .getHoldsAPI(getAdminUser()) | ||||
|                 .addChildToHold(HoldChild.builder().id(id).build(), holdNodeRefOne)); | ||||
|  | ||||
|         return new String[][] | ||||
|             {       // record folder | ||||
|                 { folderToHeld.getId() }, | ||||
|                 //electronic record | ||||
|                 { electronicRecord.getId() }, | ||||
|                 // non electronic record | ||||
|                 { nonElectronicRecord.getId() }, | ||||
|                 // document from collaboration site | ||||
|                 { contentHeld.getNodeRefWithoutVersion() }, | ||||
|             }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given content/record folder/record that is held | ||||
|      * And the corresponding hold | ||||
|      * When I use the existing REST API to remove the node from the hold | ||||
|      * Then the node is removed from the hold | ||||
|      * And is no longer frozen | ||||
|      */ | ||||
|     @Test(dataProvider = "validNodesToRemoveFromHold") | ||||
|     public void removeContentFromHold(String nodeId) throws Exception | ||||
|     { | ||||
|         STEP("Remove node from hold"); | ||||
|         getRestAPIFactory() | ||||
|             .getHoldsAPI(getAdminUser()).deleteHoldChild(holdNodeRefOne, nodeId); | ||||
|  | ||||
|         STEP("Check the node is not held"); | ||||
|         assertFalse(hasAspect(nodeId, FROZEN_ASPECT)); | ||||
|  | ||||
|         STEP("Check node is not in any hold"); | ||||
|         RestNodeAssociationModelCollection holdsEntries = getRestAPIFactory() | ||||
|             .getNodeAPI(CoreUtil.toContentModel(nodeId)).usingParams("where=(assocType='rma:frozenContent')") | ||||
|             .getParents(); | ||||
|         assertTrue(holdsEntries.getEntries().isEmpty(), "Content held is still added to a hold."); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given active content that is held on many holds | ||||
|      * When I use the existing REST API to remove the active content from one hold | ||||
|      * Then the active content is removed from the specific hold | ||||
|      * And is frozen | ||||
|      * And in the other holds | ||||
|      */ | ||||
|     @Test | ||||
|     public void removeContentAddedToManyHolds() throws Exception | ||||
|     { | ||||
|         STEP("Remove content from hold. "); | ||||
|  | ||||
|         getRestAPIFactory().getHoldsAPI(getAdminUser()) | ||||
|             .deleteHoldChild(holdNodeRefOne, contentAddToManyHolds.getNodeRefWithoutVersion()); | ||||
|  | ||||
|         STEP("Check the content is held. "); | ||||
|         assertTrue(hasAspect(contentAddToManyHolds.getNodeRefWithoutVersion(), FROZEN_ASPECT)); | ||||
|  | ||||
|         STEP("Check node is in hold HOLD_TWO. "); | ||||
|  | ||||
|         RestNodeAssociationModelCollection holdsEntries = getRestAPIFactory() | ||||
|             .getNodeAPI(CoreUtil.toContentModel(contentAddToManyHolds.getNodeRefWithoutVersion())) | ||||
|             .usingParams("where=(assocType='rma:frozenContent')").getParents(); | ||||
|         assertFalse(holdsEntries.getEntries().isEmpty(), "Content held is not held after removing from one hold."); | ||||
|         assertTrue(holdsEntries.getEntries().stream() | ||||
|                 .anyMatch(restNodeModel -> restNodeModel.getModel().getName().equals(HOLD_TWO)), | ||||
|             "Content held is not held after removing from one hold."); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Data provider with user without right permission or capability to remove from hold a specific node | ||||
|      * | ||||
|      * @return user model and the node ref to be removed from hold | ||||
|      */ | ||||
|     @DataProvider(name = "userWithoutPermissionForRemoveFromHold") | ||||
|     public Object[][] getUserWithoutPermissionForAddToHold() | ||||
|     { | ||||
|         //create record folder | ||||
|         RecordCategoryChild recordFolder = createCategoryFolderInFilePlan(); | ||||
|         nodesToBeClean.add(recordFolder.getParentId()); | ||||
|         UserModel user = roleService.createUserWithRMRole(ROLE_RM_MANAGER.roleId); | ||||
|         getRestAPIFactory().getRMUserAPI().addUserPermission(holdNodeRefOne, user, PERMISSION_FILING); | ||||
|         //create files that will be removed from hold | ||||
|         FileModel contentNoHoldPerm = dataContent.usingAdmin().usingSite(testSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|         FileModel contentNoHoldCap = dataContent.usingAdmin().usingSite(testSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|         FileModel privateFile = dataContent.usingAdmin().usingSite(privateSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|         //add files to hold | ||||
|         asList(recordFolder.getId(), contentNoHoldCap.getNodeRefWithoutVersion(), | ||||
|             contentNoHoldPerm.getNodeRefWithoutVersion(), privateFile.getNodeRefWithoutVersion()) | ||||
|             .forEach(id -> getRestAPIFactory() | ||||
|                 .getHoldsAPI(getAdminUser()) | ||||
|                 .addChildToHold(HoldChild.builder().id(id).build(), holdNodeRefOne)); | ||||
|  | ||||
|         return new Object[][] | ||||
|             { | ||||
|                 // user with read permission on the content, with remove from hold capability and without | ||||
|                 // filling permission on a hold | ||||
|                 { | ||||
|                     roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, UserRole.SiteCollaborator, | ||||
|                         holdNodeRefOne, UserRoles.ROLE_RM_MANAGER, PERMISSION_READ_RECORDS), | ||||
|                     contentNoHoldPerm.getNodeRefWithoutVersion() | ||||
|                 }, | ||||
|                 // user with write permission on the content, filling permission on a hold without remove from | ||||
|                 // hold capability | ||||
|                 { | ||||
|                     roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, UserRole | ||||
|                             .SiteCollaborator, | ||||
|                         holdNodeRefOne, UserRoles.ROLE_RM_POWER_USER, PERMISSION_FILING), | ||||
|                     contentNoHoldCap.getNodeRefWithoutVersion() | ||||
|                 }, | ||||
|                 //user without read permission on RM  record folder | ||||
|                 { | ||||
|                     user, recordFolder.getId() | ||||
|                 }, | ||||
|                 //user without read permission over the content from the private site | ||||
|                 { | ||||
|                     user, privateFile.getNodeRefWithoutVersion() | ||||
|                 } | ||||
|             }; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Given node on hold in a single hold location | ||||
|      * And the user does not have sufficient permissions or capabilities to remove the node from the hold | ||||
|      * When the user tries to remove the node from the hold | ||||
|      * Then it's unsuccessful | ||||
|      * | ||||
|      * @throws Exception | ||||
|      */ | ||||
|     @Test(dataProvider = "userWithoutPermissionForRemoveFromHold") | ||||
|     public void removeFromHoldWithUserWithoutPermission(UserModel userModel, String nodeIdToBeRemoved) throws Exception | ||||
|     { | ||||
|         STEP("Update the list of users to be deleted after running the tests"); | ||||
|         usersToBeClean.add(userModel); | ||||
|  | ||||
|         STEP("Remove node from hold with user without right permission or capability"); | ||||
|         getRestAPIFactory().getHoldsAPI(userModel).deleteHoldChild(holdNodeRefOne, nodeIdToBeRemoved); | ||||
|  | ||||
|         assertStatusCode(FORBIDDEN); | ||||
|         getRestAPIFactory().getRmRestWrapper().assertLastError().containsSummary(ACCESS_DENIED_ERROR_MESSAGE); | ||||
|  | ||||
|         STEP("Check node is frozen."); | ||||
|         assertTrue(hasAspect(nodeIdToBeRemoved, FROZEN_ASPECT)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Data provider with user with right permission or capability to remove from hold a specific node | ||||
|      * | ||||
|      * @return user model and the node ref to be removed from hold | ||||
|      */ | ||||
|     @DataProvider(name = "userWithPermissionForRemoveFromHold") | ||||
|     public Object[][] getUserWithPermissionForAddToHold() | ||||
|     { | ||||
|         //create record folder | ||||
|         RecordCategoryChild recordFolder = createCategoryFolderInFilePlan(); | ||||
|         nodesToBeClean.add(recordFolder.getParentId()); | ||||
|         UserModel user = roleService.createUserWithRMRoleAndRMNodePermission(ROLE_RM_MANAGER.roleId, | ||||
|             recordFolder.getId(), | ||||
|             PERMISSION_READ_RECORDS); | ||||
|         getRestAPIFactory().getRMUserAPI().addUserPermission(holdNodeRefOne, user, PERMISSION_FILING); | ||||
|         //create file that will be removed from hold | ||||
|         FileModel contentPermission = dataContent.usingAdmin().usingSite(testSite) | ||||
|             .createContent(CMISUtil.DocumentType.TEXT_PLAIN); | ||||
|  | ||||
|         //add files to hold | ||||
|         asList(recordFolder.getId(), contentPermission.getNodeRefWithoutVersion()) | ||||
|             .forEach(id -> getRestAPIFactory() | ||||
|                 .getHoldsAPI(getAdminUser()) | ||||
|                 .addChildToHold(HoldChild.builder().id(id).build(), holdNodeRefOne)); | ||||
|  | ||||
|         return new Object[][] | ||||
|             { | ||||
|                 // user with write permission on the content | ||||
|                 { | ||||
|                     roleService.createUserWithSiteRoleRMRoleAndPermission(testSite, UserRole.SiteConsumer, | ||||
|                         holdNodeRefOne, UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING), | ||||
|                     contentPermission.getNodeRefWithoutVersion() | ||||
|                 }, | ||||
|                 //user with read permission on RM  record folder | ||||
|                 { | ||||
|                     user, recordFolder.getId() | ||||
|                 }, | ||||
|  | ||||
|             }; | ||||
|     } | ||||
|  | ||||
|     @Test(dataProvider = "userWithPermissionForRemoveFromHold") | ||||
|     public void removeFromHoldWithUserWithPermission(UserModel userModel, String nodeIdToBeRemoved) throws Exception | ||||
|     { | ||||
|         STEP("Update the list of users to be deleted after running the tests"); | ||||
|         usersToBeClean.add(userModel); | ||||
|  | ||||
|         STEP("Remove node from hold with user with right permission and capability"); | ||||
|         getRestAPIFactory().getHoldsAPI(userModel).deleteHoldChild(holdNodeRefOne, nodeIdToBeRemoved); | ||||
|  | ||||
|         STEP("Check node is not frozen."); | ||||
|         assertFalse(hasAspect(nodeIdToBeRemoved, FROZEN_ASPECT)); | ||||
|     } | ||||
|  | ||||
|     private Hold createHold(String parentId, Hold hold, UserModel user) | ||||
|     { | ||||
|         FilePlanAPI filePlanAPI = getRestAPIFactory().getFilePlansAPI(user); | ||||
|         return filePlanAPI.createHold(hold, parentId); | ||||
|     } | ||||
|  | ||||
|     @AfterClass(alwaysRun = true) | ||||
|     public void cleanUpRemoveContentFromHold() | ||||
|     { | ||||
|         holdsListRef.forEach(holdRef -> getRestAPIFactory().getHoldsAPI(getAdminUser()).deleteHold(holdRef)); | ||||
|         dataSite.usingAdmin().deleteSite(testSite); | ||||
|         dataSite.usingAdmin().deleteSite(privateSite); | ||||
|         usersToBeClean.forEach(user -> getDataUser().usingAdmin().deleteUser(user)); | ||||
|         nodesToBeClean.forEach(this::deleteRecordCategory); | ||||
|     } | ||||
| } | ||||
| @@ -62,6 +62,7 @@ import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI; | ||||
| import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledRecordFolderAPI; | ||||
| import org.alfresco.rest.rm.community.utils.AlfrescoRetryAnalyzer; | ||||
| import org.alfresco.rest.v0.service.RoleService; | ||||
| import org.alfresco.test.AlfrescoTest; | ||||
| import org.alfresco.utility.model.UserModel; | ||||
| @@ -289,10 +290,11 @@ public class UpdateRecordsTests extends BaseRMRestTest | ||||
|      * </pre> | ||||
|      */ | ||||
|     @Test | ||||
|     ( | ||||
|         dataProvider = "completeRecords", | ||||
|         description = "Complete records can't be updated" | ||||
|     ) | ||||
|         ( | ||||
|             dataProvider = "completeRecords", | ||||
|             description = "Complete records can't be updated", | ||||
|             retryAnalyzer = AlfrescoRetryAnalyzer.class | ||||
|         ) | ||||
|     @AlfrescoTest(jira="RM-4362") | ||||
|     @Bug (id = "APPS-132") | ||||
|     public void completeRecordsCantBeUpdated(String electronicRecordId, String nonElectronicRecordId) | ||||
|   | ||||
| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rest.rm.community.utils; | ||||
|  | ||||
| import org.testng.ITestResult; | ||||
| import org.testng.util.RetryAnalyzerCount; | ||||
|  | ||||
| public class AlfrescoRetryAnalyzer extends RetryAnalyzerCount | ||||
| { | ||||
|     public AlfrescoRetryAnalyzer() { | ||||
|         super(); | ||||
|         setCount(3); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean retryMethod(ITestResult result) | ||||
|     { | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1 @@ | ||||
| com.epam.reportportal.testng.ReportPortalTestNGListener | ||||
| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-parent</artifactId> | ||||
|       <version>23.2.0.31</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <modules> | ||||
|   | ||||
| @@ -1,3 +1,3 @@ | ||||
| SOLR6_TAG=2.0.8.1 | ||||
| SOLR6_TAG=2.0.10 | ||||
| POSTGRES_TAG=15.4 | ||||
| ACTIVEMQ_TAG=5.18.3-jre17-rockylinux8 | ||||
|   | ||||
| @@ -139,3 +139,21 @@ content.metadata.async.extract.6.enabled=false | ||||
|  | ||||
| # Max number of entries returned in Record search view | ||||
| rm.recordSearch.maxItems=500 | ||||
|  | ||||
| # | ||||
| # Hold bulk | ||||
| # | ||||
| # The number of worker threads. | ||||
| rm.hold.bulk.threadCount=2 | ||||
| # The maximum number of total items to process in a single bulk operation. | ||||
| rm.hold.bulk.maxItems=1000 | ||||
| # The number of entries to be fetched from the Search Service as a next set of work object to process. | ||||
| rm.hold.bulk.batchSize=100 | ||||
| # The number of entries to process before reporting progress. | ||||
| rm.hold.bulk.logging.interval=100 | ||||
| # The number of entries we process at a time in a transaction. | ||||
| rm.hold.bulk.itemsPerTransaction=1 | ||||
|  | ||||
| cache.bulkHoldStatusCache.cluster.type=fully-distributed | ||||
| cache.bulkHoldRegistryCache.cluster.type=fully-distributed | ||||
|  | ||||
|   | ||||
| @@ -538,6 +538,11 @@ | ||||
|                <type>d:text</type> | ||||
|                <mandatory>true</mandatory> | ||||
|             </property> | ||||
|             <property name="rma:holdDeletionReason"> | ||||
|                <title>Hold Deletion Reason</title> | ||||
|                <type>d:text</type> | ||||
|                <mandatory>false</mandatory> | ||||
|             </property> | ||||
|          </properties> | ||||
|  | ||||
|          <associations> | ||||
|   | ||||
| @@ -89,6 +89,9 @@ | ||||
|    <!-- Import RM Audit --> | ||||
|    <import resource="classpath:alfresco/module/org_alfresco_module_rm/rm-audit-context.xml"/> | ||||
|  | ||||
|    <!-- Import RM Bulk --> | ||||
|    <import resource="classpath:alfresco/module/org_alfresco_module_rm/rm-bulk-context.xml"/> | ||||
|  | ||||
|    <!--  Import RM query context --> | ||||
|    <import resource="classpath:alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml" /> | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,51 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <beans xmlns="http://www.springframework.org/schema/beans" | ||||
|        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||||
|        xmlns:context="http://www.springframework.org/schema/context" | ||||
|        xsi:schemaLocation="http://www.springframework.org/schema/beans | ||||
|                         http://www.springframework.org/schema/beans/spring-beans.xsd | ||||
|                         http://www.springframework.org/schema/context | ||||
|                         http://www.springframework.org/schema/context/spring-context.xsd"> | ||||
|  | ||||
|    <bean id="holdBulkService" | ||||
|          class="org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkServiceImpl"> | ||||
|       <property name="serviceRegistry" ref="ServiceRegistry" /> | ||||
|       <property name="transactionService" ref="transactionService" /> | ||||
|       <property name="searchMapper" ref="searchapiSearchMapper" /> | ||||
|       <property name="bulkMonitor" ref="holdBulkMonitor" /> | ||||
|       <property name="holdService" ref="HoldService" /> | ||||
|       <property name="capabilityService" ref="CapabilityService" /> | ||||
|       <property name="permissionService" ref="PermissionService" /> | ||||
|       <property name="nodeService" ref="NodeService" /> | ||||
|       <property name="threadCount"> | ||||
|          <value>${rm.hold.bulk.threadCount}</value> | ||||
|       </property> | ||||
|       <property name="batchSize"> | ||||
|          <value>${rm.hold.bulk.batchSize}</value> | ||||
|       </property> | ||||
|       <property name="maxItems"> | ||||
|          <value>${rm.hold.bulk.maxItems}</value> | ||||
|       </property> | ||||
|       <property name="loggingInterval"> | ||||
|          <value>${rm.hold.bulk.logging.interval}</value> | ||||
|       </property> | ||||
|       <property name="itemsPerTransaction"> | ||||
|          <value>${rm.hold.bulk.itemsPerTransaction}</value> | ||||
|       </property> | ||||
|    </bean> | ||||
|  | ||||
|    <bean id="holdBulkMonitor" class="org.alfresco.module.org_alfresco_module_rm.bulk.hold.DefaultHoldBulkMonitor"> | ||||
|       <property name="holdProgressCache" ref="holdProgressCache" /> | ||||
|       <property name="holdProcessRegistry" ref="holdProcessRegistry" /> | ||||
|    </bean> | ||||
|  | ||||
|  | ||||
|    <bean name="holdProgressCache" factory-bean="cacheFactory" factory-method="createCache"> | ||||
|       <constructor-arg value="cache.bulkHoldStatusCache" /> | ||||
|    </bean> | ||||
|  | ||||
|    <bean name="holdProcessRegistry" factory-bean="cacheFactory" factory-method="createCache"> | ||||
|       <constructor-arg value="cache.bulkHoldRegistryCache" /> | ||||
|    </bean> | ||||
|  | ||||
| </beans> | ||||
| @@ -69,7 +69,39 @@ | ||||
|        <property name="transactionService" ref="transactionService" /> | ||||
|     </bean> | ||||
|  | ||||
|     <bean class="org.alfresco.rm.rest.api.unfiledcontainers.UnfiledContainerEntityResource"> | ||||
|    <bean class="org.alfresco.rm.rest.api.fileplans.FilePlanHoldsRelation"> | ||||
|       <property name="apiUtils" ref="apiUtils" /> | ||||
|       <property name="nodesModelFactory" ref="nodesModelFactory" /> | ||||
|       <property name="holdService" ref="HoldService" /> | ||||
|       <property name="fileFolderService" ref="FileFolderService" /> | ||||
|       <property name="transactionService" ref="transactionService" /> | ||||
|    </bean> | ||||
|  | ||||
|    <bean class="org.alfresco.rm.rest.api.holds.HoldsEntityResource" > | ||||
|       <property name="holdService" ref="HoldService" /> | ||||
|       <property name="apiUtils" ref="apiUtils" /> | ||||
|       <property name="nodesModelFactory" ref="nodesModelFactory" /> | ||||
|       <property name="fileFolderService" ref="FileFolderService" /> | ||||
|       <property name="transactionService" ref="transactionService" /> | ||||
|       <property name="holdBulkService" ref="holdBulkService" /> | ||||
|    </bean> | ||||
|  | ||||
|    <bean class="org.alfresco.rm.rest.api.holds.HoldsBulkStatusesRelation" > | ||||
|       <property name="holdBulkMonitor" ref="holdBulkMonitor" /> | ||||
|       <property name="apiUtils" ref="apiUtils" /> | ||||
|       <property name="permissionService" ref="PermissionService" /> | ||||
|    </bean> | ||||
|  | ||||
|    <bean class="org.alfresco.rm.rest.api.holds.HoldsChildrenRelation"> | ||||
|       <property name="holdService" ref="HoldService" /> | ||||
|       <property name="apiUtils" ref="apiUtils" /> | ||||
|       <property name="nodesModelFactory" ref="nodesModelFactory" /> | ||||
|       <property name="fileFolderService" ref="FileFolderService" /> | ||||
|       <property name="transactionService" ref="transactionService" /> | ||||
|       <property name="permissionService" ref="PermissionService" /> | ||||
|    </bean> | ||||
|  | ||||
|    <bean class="org.alfresco.rm.rest.api.unfiledcontainers.UnfiledContainerEntityResource"> | ||||
|        <property name="apiUtils" ref="apiUtils" /> | ||||
|        <property name="fileFolderService" ref="FileFolderService" /> | ||||
|        <property name="nodesModelFactory" ref="nodesModelFactory" /> | ||||
|   | ||||
| @@ -1614,6 +1614,8 @@ | ||||
|             org.alfresco.module.org_alfresco_module_rm.hold.HoldService.createHold=RM_CAP.0.rma:filePlanComponent.CreateHold | ||||
|             org.alfresco.module.org_alfresco_module_rm.hold.HoldService.getHoldReason=RM.Read.0 | ||||
|             org.alfresco.module.org_alfresco_module_rm.hold.HoldService.setHoldReason=RM_CAP.0.rma:filePlanComponent.EditHold | ||||
|             org.alfresco.module.org_alfresco_module_rm.hold.HoldService.setHoldDeletionReason=RM_CAP.0.rma:filePlanComponent.EditHold | ||||
|             org.alfresco.module.org_alfresco_module_rm.hold.HoldService.updateHold=RM_CAP.0.rma:filePlanComponent.EditHold | ||||
|             org.alfresco.module.org_alfresco_module_rm.hold.HoldService.deleteHold=RM_CAP.0.rma:filePlanComponent.DeleteHold | ||||
|             org.alfresco.module.org_alfresco_module_rm.hold.HoldService.addToHold=RM_CAP.0.rma:filePlanComponent.AddToHold | ||||
|             org.alfresco.module.org_alfresco_module_rm.hold.HoldService.addToHolds=RM_ALLOW | ||||
|   | ||||
| @@ -41,6 +41,8 @@ services: | ||||
|                 -Daos.baseUrlOverwrite=http://localhost:8080/alfresco/aos | ||||
|                 -Dmessaging.broker.url=\"failover:(tcp://activemq:61616)?timeout=3000&jms.useCompression=true\" | ||||
|                 -DlocalTransform.core-aio.url=http://transform-core-aio:8090/ | ||||
|                 -Drm.hold.bulk.maxItems=5 | ||||
|                 -Drm.hold.bulk.batchSize=2 | ||||
|                 " | ||||
|         ports: | ||||
|             - 8080:8080 | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|       <version>23.2.0.31</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <properties> | ||||
| @@ -84,6 +84,11 @@ | ||||
|          <artifactId>junit</artifactId> | ||||
|          <scope>test</scope> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>com.epam.reportportal</groupId> | ||||
|          <artifactId>agent-java-testng</artifactId> | ||||
|          <scope>test</scope> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>org.postgresql</groupId> | ||||
|          <artifactId>postgresql</artifactId> | ||||
|   | ||||
| @@ -27,6 +27,7 @@ | ||||
|  | ||||
| package org.alfresco.module.org_alfresco_module_rm.audit.event; | ||||
|  | ||||
| import static org.alfresco.module.org_alfresco_module_rm.audit.event.HoldUtils.HOLD_DELETION_REASON; | ||||
| import static org.alfresco.repo.policy.Behaviour.NotificationFrequency.EVERY_EVENT; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| @@ -77,6 +78,8 @@ public class DeleteHoldAuditEvent extends AuditEvent implements NodeServicePolic | ||||
|     public void beforeDeleteNode(NodeRef holdNodeRef) | ||||
|     { | ||||
|         Map<QName, Serializable> auditProperties = HoldUtils.makePropertiesMap(holdNodeRef, nodeService); | ||||
|         auditProperties.put(HOLD_DELETION_REASON, nodeService.getProperty(holdNodeRef, PROP_HOLD_DELETION_REASON)); | ||||
|  | ||||
|         recordsManagementAuditService.auditEvent(holdNodeRef, getName(), auditProperties, null, true, false); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -47,6 +47,7 @@ class HoldUtils | ||||
| { | ||||
|     /** A QName to display for the hold name. */ | ||||
|     public static final QName HOLD_NAME = QName.createQName(RecordsManagementModel.RM_URI, "Hold Name"); | ||||
|     public static final QName HOLD_DELETION_REASON = QName.createQName(RecordsManagementModel.RM_URI, "Hold deletion reason"); | ||||
|     /** A QName to display for the hold node ref. */ | ||||
|     public static final QName HOLD_NODEREF = QName.createQName(RecordsManagementModel.RM_URI, "Hold NodeRef"); | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,249 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk; | ||||
|  | ||||
| import java.util.UUID; | ||||
|  | ||||
| import org.alfresco.repo.batch.BatchProcessWorkProvider; | ||||
| import org.alfresco.repo.batch.BatchProcessor; | ||||
| import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker; | ||||
| import org.alfresco.rest.api.search.impl.SearchMapper; | ||||
| import org.alfresco.rest.api.search.model.Query; | ||||
| import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; | ||||
| import org.alfresco.service.ServiceRegistry; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.search.ResultSet; | ||||
| import org.alfresco.service.cmr.search.SearchParameters; | ||||
| import org.alfresco.service.cmr.search.SearchService; | ||||
| import org.alfresco.service.transaction.TransactionService; | ||||
| import org.apache.commons.logging.Log; | ||||
| import org.apache.commons.logging.LogFactory; | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
|  | ||||
| /** | ||||
|  * A base class for executing bulk operations on nodes based on search query results | ||||
|  */ | ||||
| public abstract class BulkBaseService<T> implements InitializingBean | ||||
| { | ||||
|     private static final Log LOG = LogFactory.getLog(BulkBaseService.class); | ||||
|  | ||||
|     protected ServiceRegistry serviceRegistry; | ||||
|     protected SearchService searchService; | ||||
|     protected TransactionService transactionService; | ||||
|     protected SearchMapper searchMapper; | ||||
|     protected BulkMonitor<T> bulkMonitor; | ||||
|  | ||||
|     protected int threadCount; | ||||
|     protected int batchSize; | ||||
|     protected int itemsPerTransaction; | ||||
|     protected int maxItems; | ||||
|     protected int loggingInterval; | ||||
|  | ||||
|     @Override | ||||
|     public void afterPropertiesSet() throws Exception | ||||
|     { | ||||
|         this.searchService = serviceRegistry.getSearchService(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute bulk operation on node based on the search query results | ||||
|      * | ||||
|      * @param nodeRef       node reference | ||||
|      * @param bulkOperation bulk operation | ||||
|      * @return bulk status | ||||
|      */ | ||||
|     public T execute(NodeRef nodeRef, BulkOperation bulkOperation) | ||||
|     { | ||||
|         checkPermissions(nodeRef, bulkOperation); | ||||
|  | ||||
|         ResultSet resultSet = getTotalItems(bulkOperation.searchQuery(), maxItems); | ||||
|         if (maxItems < resultSet.getNumberFound() || resultSet.hasMore()) | ||||
|         { | ||||
|             throw new InvalidArgumentException("Too many items to process. Please refine your query."); | ||||
|         } | ||||
|         long totalItems = resultSet.getNumberFound(); | ||||
|         // Generate a random process id | ||||
|         String processId = UUID.randomUUID().toString(); | ||||
|  | ||||
|         T initBulkStatus = getInitBulkStatus(processId, totalItems); | ||||
|         bulkMonitor.updateBulkStatus(initBulkStatus); | ||||
|         bulkMonitor.registerProcess(nodeRef, processId); | ||||
|  | ||||
|         BatchProcessWorker<NodeRef> batchProcessWorker = getWorkerProvider(nodeRef, bulkOperation); | ||||
|         BulkStatusUpdater bulkStatusUpdater = getBulkStatusUpdater(); | ||||
|  | ||||
|         BatchProcessor<NodeRef> batchProcessor = new BatchProcessor<>( | ||||
|             processId, | ||||
|             transactionService.getRetryingTransactionHelper(), | ||||
|             getWorkProvider(bulkOperation, totalItems, bulkStatusUpdater), | ||||
|             threadCount, | ||||
|             itemsPerTransaction, | ||||
|             bulkStatusUpdater, | ||||
|             LOG, | ||||
|             loggingInterval); | ||||
|  | ||||
|         runAsyncBatchProcessor(batchProcessor, batchProcessWorker, bulkStatusUpdater); | ||||
|         return initBulkStatus; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Run batch processor | ||||
|      */ | ||||
|     protected void runAsyncBatchProcessor(BatchProcessor<NodeRef> batchProcessor, | ||||
|         BatchProcessWorker<NodeRef> batchProcessWorker, BulkStatusUpdater bulkStatusUpdater) | ||||
|     { | ||||
|         Runnable backgroundLogic = () -> { | ||||
|             try | ||||
|             { | ||||
|                 if (LOG.isDebugEnabled()) | ||||
|                 { | ||||
|                     LOG.debug("Started processing batch with name: " + batchProcessor.getProcessName()); | ||||
|                 } | ||||
|                 batchProcessor.processLong(batchProcessWorker, true); | ||||
|                 if (LOG.isDebugEnabled()) | ||||
|                 { | ||||
|                     LOG.debug("Processing batch with name: " + batchProcessor.getProcessName() + " completed"); | ||||
|                 } | ||||
|             } | ||||
|             catch (Exception exception) | ||||
|             { | ||||
|                 LOG.error("Error processing batch with name: " + batchProcessor.getProcessName(), exception); | ||||
|             } | ||||
|             finally | ||||
|             { | ||||
|                 bulkStatusUpdater.update(); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         Thread backgroundThread = new Thread(backgroundLogic, "BulkBaseService-BackgroundThread"); | ||||
|         backgroundThread.start(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get initial bulk status | ||||
|      * | ||||
|      * @param processId  process id | ||||
|      * @param totalItems total items | ||||
|      * @return bulk status | ||||
|      */ | ||||
|     protected abstract T getInitBulkStatus(String processId, long totalItems); | ||||
|  | ||||
|     /** | ||||
|      * Get bulk status updater | ||||
|      * | ||||
|      * @return bulk status updater | ||||
|      */ | ||||
|     protected abstract BulkStatusUpdater getBulkStatusUpdater(); | ||||
|  | ||||
|     /** | ||||
|      * Get work provider | ||||
|      * | ||||
|      * @param bulkOperation     bulk operation | ||||
|      * @param totalItems        total items | ||||
|      * @param bulkStatusUpdater bulk status updater | ||||
|      * @return work provider | ||||
|      */ | ||||
|     protected abstract BatchProcessWorkProvider<NodeRef> getWorkProvider(BulkOperation bulkOperation, long totalItems, | ||||
|         BulkStatusUpdater bulkStatusUpdater); | ||||
|  | ||||
|     /** | ||||
|      * Get worker provider | ||||
|      * | ||||
|      * @param nodeRef       node reference | ||||
|      * @param bulkOperation bulk operation | ||||
|      * @return worker provider | ||||
|      */ | ||||
|     protected abstract BatchProcessWorker<NodeRef> getWorkerProvider(NodeRef nodeRef, BulkOperation bulkOperation); | ||||
|  | ||||
|     /** | ||||
|      * Check permissions | ||||
|      * | ||||
|      * @param nodeRef       node reference | ||||
|      * @param bulkOperation bulk operation | ||||
|      */ | ||||
|     protected abstract void checkPermissions(NodeRef nodeRef, BulkOperation bulkOperation); | ||||
|  | ||||
|     protected ResultSet getTotalItems(Query searchQuery, int skipCount) | ||||
|     { | ||||
|         SearchParameters searchParams = new SearchParameters(); | ||||
|         searchMapper.setDefaults(searchParams); | ||||
|         searchMapper.fromQuery(searchParams, searchQuery); | ||||
|         searchParams.setSkipCount(skipCount); | ||||
|         searchParams.setMaxItems(1); | ||||
|         return searchService.query(searchParams); | ||||
|     } | ||||
|  | ||||
|     public void setServiceRegistry(ServiceRegistry serviceRegistry) | ||||
|     { | ||||
|         this.serviceRegistry = serviceRegistry; | ||||
|     } | ||||
|  | ||||
|     public void setSearchService(SearchService searchService) | ||||
|     { | ||||
|         this.searchService = searchService; | ||||
|     } | ||||
|  | ||||
|     public void setTransactionService(TransactionService transactionService) | ||||
|     { | ||||
|         this.transactionService = transactionService; | ||||
|     } | ||||
|  | ||||
|     public void setSearchMapper(SearchMapper searchMapper) | ||||
|     { | ||||
|         this.searchMapper = searchMapper; | ||||
|     } | ||||
|  | ||||
|     public void setBulkMonitor(BulkMonitor<T> bulkMonitor) | ||||
|     { | ||||
|         this.bulkMonitor = bulkMonitor; | ||||
|     } | ||||
|  | ||||
|     public void setThreadCount(int threadCount) | ||||
|     { | ||||
|         this.threadCount = threadCount; | ||||
|     } | ||||
|  | ||||
|     public void setBatchSize(int batchSize) | ||||
|     { | ||||
|         this.batchSize = batchSize; | ||||
|     } | ||||
|  | ||||
|     public void setMaxItems(int maxItems) | ||||
|     { | ||||
|         this.maxItems = maxItems; | ||||
|     } | ||||
|  | ||||
|     public void setLoggingInterval(int loggingInterval) | ||||
|     { | ||||
|         this.loggingInterval = loggingInterval; | ||||
|     } | ||||
|  | ||||
|     public void setItemsPerTransaction(int itemsPerTransaction) | ||||
|     { | ||||
|         this.itemsPerTransaction = itemsPerTransaction; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,58 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk; | ||||
|  | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
|  | ||||
| /** | ||||
|  * An interface for monitoring the progress of a bulk operation | ||||
|  */ | ||||
| public interface BulkMonitor<T> | ||||
| { | ||||
|     /** | ||||
|      * Update the bulk status | ||||
|      * | ||||
|      * @param bulkStatus the bulk status | ||||
|      */ | ||||
|     void updateBulkStatus(T bulkStatus); | ||||
|  | ||||
|     /** | ||||
|      * Register a process | ||||
|      * | ||||
|      * @param nodeRef   the node reference | ||||
|      * @param processId the process id | ||||
|      */ | ||||
|     void registerProcess(NodeRef nodeRef, String processId); | ||||
|  | ||||
|     /** | ||||
|      * Get the bulk status | ||||
|      * | ||||
|      * @param bulkStatusId the bulk status id | ||||
|      * @return the bulk status | ||||
|      */ | ||||
|     T getBulkStatus(String bulkStatusId); | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk; | ||||
|  | ||||
| import org.alfresco.rest.api.search.model.Query; | ||||
|  | ||||
| /** | ||||
|  * An immutable POJO to represent a bulk operation | ||||
|  */ | ||||
| public record BulkOperation(Query searchQuery, String operationType) | ||||
| { | ||||
|     public BulkOperation | ||||
|     { | ||||
|         if (operationType == null || searchQuery == null) | ||||
|         { | ||||
|             throw new IllegalArgumentException("Operation type and search query must not be null"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,40 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk; | ||||
|  | ||||
| import org.springframework.context.ApplicationEventPublisher; | ||||
|  | ||||
| /** | ||||
|  * An interface for updating the status of a bulk operation | ||||
|  */ | ||||
| public interface BulkStatusUpdater extends ApplicationEventPublisher | ||||
| { | ||||
|     /** | ||||
|      * Update the bulk status | ||||
|      */ | ||||
|     void update(); | ||||
| } | ||||
| @@ -0,0 +1,109 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.Comparator; | ||||
| import java.util.List; | ||||
| import java.util.Objects; | ||||
| import java.util.Optional; | ||||
|  | ||||
| import org.alfresco.repo.cache.SimpleCache; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.springframework.context.ApplicationEvent; | ||||
| import org.springframework.extensions.surf.util.AbstractLifecycleBean; | ||||
|  | ||||
| /** | ||||
|  * Default hold bulk monitor implementation | ||||
|  */ | ||||
| public class DefaultHoldBulkMonitor extends AbstractLifecycleBean implements HoldBulkMonitor | ||||
| { | ||||
|     protected SimpleCache<String, HoldBulkStatus> holdProgressCache; | ||||
|     protected SimpleCache<String, List<HoldBulkProcessDetails>> holdProcessRegistry; | ||||
|  | ||||
|     @Override | ||||
|     public void updateBulkStatus(HoldBulkStatus holdBulkStatus) | ||||
|     { | ||||
|         holdProgressCache.put(holdBulkStatus.bulkStatusId(), holdBulkStatus); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void registerProcess(NodeRef holdRef, String processId) | ||||
|     { | ||||
|         List<HoldBulkProcessDetails> processIds = Optional.ofNullable(holdProcessRegistry.get(holdRef.getId())) | ||||
|             .orElse(new ArrayList<>()); | ||||
|         processIds.add(new HoldBulkProcessDetails(processId, null)); | ||||
|         holdProcessRegistry.put(holdRef.getId(), processIds); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public HoldBulkStatus getBulkStatus(String bulkStatusId) | ||||
|     { | ||||
|         return holdProgressCache.get(bulkStatusId); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public List<HoldBulkStatus> getBulkStatusesForHold(String holdId) | ||||
|     { | ||||
|         return Optional.ofNullable(holdProcessRegistry.get(holdId)) | ||||
|             .map(bulkProcessDetailsList -> bulkProcessDetailsList.stream() | ||||
|                 .map(HoldBulkProcessDetails::bulkStatusId) | ||||
|                 .map(this::getBulkStatus) | ||||
|                 .filter(Objects::nonNull) | ||||
|                 .sorted(Comparator.comparing(HoldBulkStatus::endTime, Comparator.nullsLast(Comparator.naturalOrder())) | ||||
|                     .thenComparing(HoldBulkStatus::startTime, Comparator.nullsLast(Comparator.naturalOrder())) | ||||
|                     .reversed()) | ||||
|                 .toList()) | ||||
|             .orElse(Collections.emptyList()); | ||||
|     } | ||||
|  | ||||
|     public void setHoldProgressCache( | ||||
|         SimpleCache<String, HoldBulkStatus> holdProgressCache) | ||||
|     { | ||||
|         this.holdProgressCache = holdProgressCache; | ||||
|     } | ||||
|  | ||||
|     public void setHoldProcessRegistry( | ||||
|         SimpleCache<String, List<HoldBulkProcessDetails>> holdProcessRegistry) | ||||
|     { | ||||
|         this.holdProcessRegistry = holdProcessRegistry; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onBootstrap(ApplicationEvent applicationEvent) | ||||
|     { | ||||
|         // NOOP | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onShutdown(ApplicationEvent applicationEvent) | ||||
|     { | ||||
|         // NOOP | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkMonitor; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
|  | ||||
| /** | ||||
|  * An interface for monitoring the progress of a bulk hold operation | ||||
|  */ | ||||
| public interface HoldBulkMonitor extends BulkMonitor<HoldBulkStatus> | ||||
| { | ||||
|     /** | ||||
|      * Get the bulk statuses for a hold | ||||
|      * | ||||
|      * @param holdId the hold id | ||||
|      * @return the bulk statuses | ||||
|      */ | ||||
|     List<HoldBulkStatus> getBulkStatusesForHold(String holdId); | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import java.io.Serializable; | ||||
|  | ||||
| /** | ||||
|  * A simple immutable POJO to hold the details of a bulk hold process | ||||
|  */ | ||||
| public record HoldBulkProcessDetails(String bulkStatusId, String creatorInstance) implements Serializable | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,45 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
|  | ||||
| /** | ||||
|  * Interface defining a hold bulk service. | ||||
|  */ | ||||
| public interface HoldBulkService | ||||
| { | ||||
|     /** | ||||
|      * Initiates a bulk operation on a hold. | ||||
|      * | ||||
|      * @param holdRef       The hold reference | ||||
|      * @param bulkOperation The bulk operation | ||||
|      */ | ||||
|     HoldBulkStatus execute(NodeRef holdRef, BulkOperation bulkOperation); | ||||
| } | ||||
| @@ -0,0 +1,258 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import static org.alfresco.model.ContentModel.PROP_NAME; | ||||
| import static org.alfresco.rm.rest.api.model.HoldBulkOperationType.ADD; | ||||
|  | ||||
| import java.util.Collection; | ||||
| import java.util.Collections; | ||||
| import java.util.Locale; | ||||
| import java.util.concurrent.atomic.AtomicInteger; | ||||
|  | ||||
| import org.alfresco.model.ContentModel; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkBaseService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkStatusUpdater; | ||||
| import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; | ||||
| import org.alfresco.module.org_alfresco_module_rm.hold.HoldService; | ||||
| import org.alfresco.repo.batch.BatchProcessWorkProvider; | ||||
| import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker; | ||||
| import org.alfresco.repo.security.authentication.AuthenticationUtil; | ||||
| import org.alfresco.repo.security.permissions.AccessDeniedException; | ||||
| import org.alfresco.rest.api.search.model.Query; | ||||
| import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkOperationType; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.NodeService; | ||||
| import org.alfresco.service.cmr.search.ResultSet; | ||||
| import org.alfresco.service.cmr.search.SearchParameters; | ||||
| import org.alfresco.service.cmr.security.AccessStatus; | ||||
| import org.alfresco.service.cmr.security.PermissionService; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.springframework.extensions.surf.util.I18NUtil; | ||||
|  | ||||
| /** | ||||
|  * Implementation of the {@link HoldBulkService} interface. | ||||
|  */ | ||||
| @SuppressWarnings("PMD.PreserveStackTrace") | ||||
| public class HoldBulkServiceImpl extends BulkBaseService<HoldBulkStatus> implements HoldBulkService | ||||
| { | ||||
|     private static final Logger LOGGER = LoggerFactory.getLogger(HoldBulkServiceImpl.class); | ||||
|  | ||||
|     private HoldService holdService; | ||||
|     private static final String MSG_ERR_ACCESS_DENIED = "permissions.err_access_denied"; | ||||
|  | ||||
|     private CapabilityService capabilityService; | ||||
|     private PermissionService permissionService; | ||||
|     private NodeService nodeService; | ||||
|  | ||||
|     @Override | ||||
|     protected HoldBulkStatus getInitBulkStatus(String processId, long totalItems) | ||||
|     { | ||||
|         return new HoldBulkStatus(processId, null, null, 0, 0, totalItems, null); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected BulkStatusUpdater getBulkStatusUpdater() | ||||
|     { | ||||
|         return new HoldBulkStatusUpdater((HoldBulkMonitor) bulkMonitor); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected BatchProcessWorkProvider<NodeRef> getWorkProvider(BulkOperation bulkOperation, long totalItems, | ||||
|         BulkStatusUpdater bulkStatusUpdater) | ||||
|     { | ||||
|         return new AddToHoldWorkerProvider(new AtomicInteger(0), bulkOperation, totalItems, bulkStatusUpdater); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected BatchProcessWorker<NodeRef> getWorkerProvider(NodeRef nodeRef, BulkOperation bulkOperation) | ||||
|     { | ||||
|         try | ||||
|         { | ||||
|             HoldBulkOperationType holdBulkOperationType = HoldBulkOperationType.valueOf(bulkOperation.operationType() | ||||
|                 .toUpperCase(Locale.ENGLISH)); | ||||
|             return switch (holdBulkOperationType) | ||||
|             { | ||||
|                 case ADD -> new AddToHoldWorkerBatch(nodeRef); | ||||
|             }; | ||||
|         } | ||||
|         catch (IllegalArgumentException e) | ||||
|         { | ||||
|             String errorMsg = "Unsupported action type when starting the bulk process: "; | ||||
|             if (LOGGER.isDebugEnabled()) | ||||
|             { | ||||
|                 LOGGER.debug("{} {}", errorMsg, bulkOperation.operationType(), e); | ||||
|             } | ||||
|             throw new InvalidArgumentException(errorMsg + bulkOperation.operationType()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void checkPermissions(NodeRef holdRef, BulkOperation bulkOperation) | ||||
|     { | ||||
|         if (!holdService.isHold(holdRef)) | ||||
|         { | ||||
|             final String holdName = (String) nodeService.getProperty(holdRef, PROP_NAME); | ||||
|             throw new InvalidArgumentException(I18NUtil.getMessage("rm.hold.not-hold", holdName), null); | ||||
|         } | ||||
|         if (ADD.name().equals(bulkOperation.operationType()) && (!AccessStatus.ALLOWED.equals( | ||||
|             capabilityService.getCapabilityAccessState(holdRef, RMPermissionModel.ADD_TO_HOLD)) || | ||||
|             permissionService.hasPermission(holdRef, RMPermissionModel.FILING) == AccessStatus.DENIED)) | ||||
|         { | ||||
|             throw new AccessDeniedException(I18NUtil.getMessage(MSG_ERR_ACCESS_DENIED)); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     private class AddToHoldWorkerBatch implements BatchProcessWorker<NodeRef> | ||||
|     { | ||||
|         private final NodeRef holdRef; | ||||
|         private final String currentUser; | ||||
|  | ||||
|         public AddToHoldWorkerBatch(NodeRef holdRef) | ||||
|         { | ||||
|             this.holdRef = holdRef; | ||||
|             currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public String getIdentifier(NodeRef entry) | ||||
|         { | ||||
|             return entry.getId(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void beforeProcess() | ||||
|         { | ||||
|             AuthenticationUtil.pushAuthentication(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void process(NodeRef entry) throws Throwable | ||||
|         { | ||||
|             AuthenticationUtil.setFullyAuthenticatedUser(currentUser); | ||||
|             holdService.addToHold(holdRef, entry); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void afterProcess() | ||||
|         { | ||||
|             AuthenticationUtil.popAuthentication(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private class AddToHoldWorkerProvider implements BatchProcessWorkProvider<NodeRef> | ||||
|     { | ||||
|         private final AtomicInteger currentNodeNumber; | ||||
|         private final Query searchQuery; | ||||
|         private final String currentUser; | ||||
|         private final long totalItems; | ||||
|         private final BulkStatusUpdater bulkStatusUpdater; | ||||
|  | ||||
|         public AddToHoldWorkerProvider(AtomicInteger currentNodeNumber, BulkOperation bulkOperation, long totalItems, | ||||
|             BulkStatusUpdater bulkStatusUpdater) | ||||
|         { | ||||
|             this.currentNodeNumber = currentNodeNumber; | ||||
|             this.searchQuery = bulkOperation.searchQuery(); | ||||
|             this.totalItems = totalItems; | ||||
|             this.bulkStatusUpdater = bulkStatusUpdater; | ||||
|             currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int getTotalEstimatedWorkSize() | ||||
|         { | ||||
|             return (int) totalItems; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public long getTotalEstimatedWorkSizeLong() | ||||
|         { | ||||
|             return totalItems; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public Collection<NodeRef> getNextWork() | ||||
|         { | ||||
|             AuthenticationUtil.pushAuthentication(); | ||||
|             AuthenticationUtil.setFullyAuthenticatedUser(currentUser); | ||||
|             SearchParameters searchParams = getNextPageParameters(); | ||||
|             ResultSet result = searchService.query(searchParams); | ||||
|             if (result.getNodeRefs().isEmpty()) | ||||
|             { | ||||
|                 return Collections.emptyList(); | ||||
|             } | ||||
|             AuthenticationUtil.popAuthentication(); | ||||
|             if (LOGGER.isDebugEnabled()) | ||||
|             { | ||||
|                 LOGGER.debug("Processing the next work for the batch processor, skipCount={}, size={}", | ||||
|                     searchParams.getSkipCount(), result.getNumberFound()); | ||||
|             } | ||||
|             currentNodeNumber.addAndGet(batchSize); | ||||
|             bulkStatusUpdater.update(); | ||||
|             return result.getNodeRefs(); | ||||
|         } | ||||
|  | ||||
|         private SearchParameters getNextPageParameters() | ||||
|         { | ||||
|             SearchParameters searchParams = new SearchParameters(); | ||||
|             searchMapper.setDefaults(searchParams); | ||||
|             searchMapper.fromQuery(searchParams, searchQuery); | ||||
|             searchParams.setSkipCount(currentNodeNumber.get()); | ||||
|             searchParams.setMaxItems(batchSize); | ||||
|             searchParams.setLimit(batchSize); | ||||
|             searchParams.addSort("@" + ContentModel.PROP_CREATED, true); | ||||
|             return searchParams; | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public void setHoldService(HoldService holdService) | ||||
|     { | ||||
|         this.holdService = holdService; | ||||
|     } | ||||
|  | ||||
|     public void setCapabilityService(CapabilityService capabilityService) | ||||
|     { | ||||
|         this.capabilityService = capabilityService; | ||||
|     } | ||||
|  | ||||
|     public void setPermissionService(PermissionService permissionService) | ||||
|     { | ||||
|         this.permissionService = permissionService; | ||||
|     } | ||||
|  | ||||
|     public void setNodeService(NodeService nodeService) | ||||
|     { | ||||
|         this.nodeService = nodeService; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk.hold; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkStatusUpdater; | ||||
| import org.alfresco.repo.batch.BatchMonitor; | ||||
| import org.alfresco.repo.batch.BatchMonitorEvent; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
|  | ||||
| /** | ||||
|  * An implementation of {@link BulkStatusUpdater} for the hold bulk operation | ||||
|  */ | ||||
| public class HoldBulkStatusUpdater implements BulkStatusUpdater | ||||
| { | ||||
|     private final Runnable task; | ||||
|     private BatchMonitor batchMonitor; | ||||
|  | ||||
|     public HoldBulkStatusUpdater(HoldBulkMonitor holdBulkMonitor) | ||||
|     { | ||||
|         this.task = () -> holdBulkMonitor.updateBulkStatus( | ||||
|             new HoldBulkStatus(batchMonitor.getProcessName(), batchMonitor.getStartTime(), | ||||
|                 batchMonitor.getEndTime(), | ||||
|                 batchMonitor.getSuccessfullyProcessedEntriesLong() + batchMonitor.getTotalErrorsLong(), | ||||
|                 batchMonitor.getTotalErrorsLong(), batchMonitor.getTotalResultsLong(), | ||||
|                 batchMonitor.getLastError())); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void update() | ||||
|     { | ||||
|         if (task != null && batchMonitor != null) | ||||
|         { | ||||
|             task.run(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void publishEvent(Object event) | ||||
|     { | ||||
|         if (event instanceof BatchMonitorEvent batchMonitorEvent) | ||||
|         { | ||||
|             batchMonitor = batchMonitorEvent.getBatchMonitor(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -219,7 +219,7 @@ public class FilePlanServiceImpl extends ServiceBaseImpl | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return rmContainerCacheManager.get(storeRef); | ||||
|             return new HashSet<>(rmContainerCacheManager.get(storeRef)); | ||||
|         } | ||||
|  | ||||
|         return results; | ||||
|   | ||||
| @@ -111,6 +111,24 @@ public interface HoldService | ||||
|      */ | ||||
|     void setHoldReason(NodeRef hold, String reason); | ||||
|  | ||||
|     /** | ||||
|      * Sets the reason for the hold deletion | ||||
|      * | ||||
|      * @param hold The {@link NodeRef} of the hold | ||||
|      * @param reason {@link String} The reason for the hold | ||||
|      */ | ||||
|     void setHoldDeletionReason(NodeRef hold, String reason); | ||||
|  | ||||
|     /** | ||||
|      * Updates a hold with the given name, reason and description | ||||
|      * | ||||
|      * @param hold The {@link NodeRef} of the hold | ||||
|      * @param name {@link String} The name of the hold | ||||
|      * @param reason {@link String} The reason of the hold | ||||
|      * @param description {@link String} The description of the hold | ||||
|      */ | ||||
|     void updateHold(NodeRef hold, String name, String reason, String description); | ||||
|  | ||||
|     /** | ||||
|      * Deletes the hold | ||||
|      * | ||||
|   | ||||
| @@ -29,6 +29,7 @@ package org.alfresco.module.org_alfresco_module_rm.hold; | ||||
|  | ||||
| import static org.alfresco.model.ContentModel.ASPECT_LOCKABLE; | ||||
| import static org.alfresco.model.ContentModel.ASSOC_CONTAINS; | ||||
| import static org.alfresco.model.ContentModel.PROP_DESCRIPTION; | ||||
| import static org.alfresco.model.ContentModel.PROP_NAME; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| @@ -458,11 +459,11 @@ public class HoldServiceImpl extends ServiceBaseImpl | ||||
|  | ||||
|         // create map of properties | ||||
|         Map<QName, Serializable> properties = new HashMap<>(3); | ||||
|         properties.put(ContentModel.PROP_NAME, name); | ||||
|         properties.put(PROP_NAME, name); | ||||
|         properties.put(PROP_HOLD_REASON, reason); | ||||
|         if (description != null && !description.isEmpty()) | ||||
|         { | ||||
|             properties.put(ContentModel.PROP_DESCRIPTION, description); | ||||
|             properties.put(PROP_DESCRIPTION, description); | ||||
|         } | ||||
|  | ||||
|         // create assoc name | ||||
| @@ -512,6 +513,39 @@ public class HoldServiceImpl extends ServiceBaseImpl | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.hold.HoldService#setHoldDeletionReason(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) | ||||
|      */ | ||||
|     @Override | ||||
|     public void setHoldDeletionReason(NodeRef hold, String reason) | ||||
|     { | ||||
|         ParameterCheck.mandatory("hold", hold); | ||||
|         ParameterCheck.mandatory("reason", reason); | ||||
|  | ||||
|         if (nodeService.exists(hold) && isHold(hold)) | ||||
|         { | ||||
|             nodeService.setProperty(hold, PROP_HOLD_DELETION_REASON, reason); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.hold.HoldService#updateHold(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String, java.lang.String) (org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String, java.lang.String) | ||||
|      */ | ||||
|     @Override | ||||
|     public void updateHold(NodeRef hold, String name, String reason, String description) | ||||
|     { | ||||
|         ParameterCheck.mandatory("hold", hold); | ||||
|         ParameterCheck.mandatory("name", name); | ||||
|         ParameterCheck.mandatory("reason", reason); | ||||
|  | ||||
|         if (nodeService.exists(hold) && isHold(hold)) | ||||
|         { | ||||
|             nodeService.setProperty(hold, PROP_NAME, name); | ||||
|             nodeService.setProperty(hold, PROP_HOLD_REASON, reason); | ||||
|             nodeService.setProperty(hold, PROP_DESCRIPTION, description); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @see org.alfresco.module.org_alfresco_module_rm.hold.HoldService#deleteHold(org.alfresco.service.cmr.repository.NodeRef) | ||||
|      */ | ||||
| @@ -563,7 +597,7 @@ public class HoldServiceImpl extends ServiceBaseImpl | ||||
|  | ||||
|                 if (permissionService.hasPermission(nodeRef, permission) == AccessStatus.DENIED) | ||||
|                 { | ||||
|                     heldNames.add((String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME)); | ||||
|                     heldNames.add((String) nodeService.getProperty(nodeRef, PROP_NAME)); | ||||
|                 } | ||||
|             } | ||||
|             catch (AccessDeniedException ade) | ||||
| @@ -630,7 +664,7 @@ public class HoldServiceImpl extends ServiceBaseImpl | ||||
|         { | ||||
|             if (!isHold(hold)) | ||||
|             { | ||||
|                 final String holdName = (String) nodeService.getProperty(hold, ContentModel.PROP_NAME); | ||||
|                 final String holdName = (String) nodeService.getProperty(hold, PROP_NAME); | ||||
|                 throw new IntegrityException(I18NUtil.getMessage("rm.hold.not-hold", holdName), null); | ||||
|             } | ||||
|  | ||||
| @@ -688,7 +722,7 @@ public class HoldServiceImpl extends ServiceBaseImpl | ||||
|     { | ||||
|         if (!isRecordFolder(nodeRef) && !instanceOf(nodeRef, ContentModel.TYPE_CONTENT)) | ||||
|         { | ||||
|             final String nodeName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); | ||||
|             final String nodeName = (String) nodeService.getProperty(nodeRef, PROP_NAME); | ||||
|             throw new IntegrityException(I18NUtil.getMessage("rm.hold.add-to-hold-invalid-type", nodeName), null); | ||||
|         } | ||||
|  | ||||
| @@ -795,7 +829,7 @@ public class HoldServiceImpl extends ServiceBaseImpl | ||||
|             { | ||||
|                 if (!isHold(hold)) | ||||
|                 { | ||||
|                     final String holdName = (String) nodeService.getProperty(hold, ContentModel.PROP_NAME); | ||||
|                     final String holdName = (String) nodeService.getProperty(hold, PROP_NAME); | ||||
|                     throw new IntegrityException(I18NUtil.getMessage("rm.hold.not-hold", holdName), null); | ||||
|                 } | ||||
|  | ||||
|   | ||||
| @@ -35,6 +35,7 @@ import org.alfresco.service.namespace.QName; | ||||
|  * | ||||
|  * @author Roy Wetherall | ||||
|  */ | ||||
| @SuppressWarnings("PMD.ConstantsInInterface") | ||||
| @AlfrescoPublicApi | ||||
| public interface RecordsManagementModel extends RecordsManagementCustomModel | ||||
| { | ||||
| @@ -200,6 +201,7 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel | ||||
|     // Hold type | ||||
|     QName TYPE_HOLD = QName.createQName(RM_URI, "hold"); | ||||
|     QName PROP_HOLD_REASON = QName.createQName(RM_URI, "holdReason"); | ||||
|     QName PROP_HOLD_DELETION_REASON = QName.createQName(RM_URI, "holdDeletionReason"); | ||||
|     //since 3.2 | ||||
|     @Deprecated | ||||
|     QName ASSOC_FROZEN_RECORDS = QName.createQName(RM_URI, "frozenRecords"); | ||||
|   | ||||
| @@ -0,0 +1,155 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.fileplans; | ||||
|  | ||||
| import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank; | ||||
| import static org.alfresco.util.ParameterCheck.mandatory; | ||||
|  | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.hold.HoldService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; | ||||
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; | ||||
| import org.alfresco.rest.framework.WebApiDescription; | ||||
| import org.alfresco.rest.framework.resource.RelationshipResource; | ||||
| import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; | ||||
| import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory; | ||||
| import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils; | ||||
| import org.alfresco.rm.rest.api.model.HoldModel; | ||||
| import org.alfresco.service.cmr.model.FileFolderService; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.transaction.TransactionService; | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
|  | ||||
| /** | ||||
|  * File plan holds relation | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @RelationshipResource(name = "holds", entityResource = FilePlanEntityResource.class, title = "Holds in a file plan") | ||||
| public class FilePlanHoldsRelation implements | ||||
|     RelationshipResourceAction.Create<HoldModel>, | ||||
|     RelationshipResourceAction.Read<HoldModel>, | ||||
|     InitializingBean | ||||
| { | ||||
|     private FilePlanComponentsApiUtils apiUtils; | ||||
|     private ApiNodesModelFactory nodesModelFactory; | ||||
|     private HoldService holdService; | ||||
|     private FileFolderService fileFolderService; | ||||
|     private TransactionService transactionService; | ||||
|  | ||||
|     @Override | ||||
|     public void afterPropertiesSet() throws Exception | ||||
|     { | ||||
|         mandatory("apiUtils", this.apiUtils); | ||||
|         mandatory("nodesModelFactory", this.nodesModelFactory); | ||||
|         mandatory("holdService", this.holdService); | ||||
|         mandatory("fileFolderService", this.fileFolderService); | ||||
|         mandatory("transactionService", this.transactionService); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @WebApiDescription(title = "Return a paged list of holds for the file plan identified by 'filePlanId'") | ||||
|     public CollectionWithPagingInfo<HoldModel> readAll(String filePlanId, Parameters parameters) | ||||
|     { | ||||
|         checkNotBlank("filePlanId", filePlanId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(filePlanId, RecordsManagementModel.TYPE_FILE_PLAN); | ||||
|         List<NodeRef> holds = holdService.getHolds(parentNodeRef); | ||||
|  | ||||
|         List<HoldModel> page = holds.stream() | ||||
|             .map(hold -> fileFolderService.getFileInfo(hold)) | ||||
|             .map(nodesModelFactory::createHoldModel) | ||||
|             .skip(parameters.getPaging().getSkipCount()) | ||||
|             .limit(parameters.getPaging().getMaxItems()) | ||||
|             .collect(Collectors.toCollection(LinkedList::new)); | ||||
|  | ||||
|         int totalItems = holds.size(); | ||||
|         boolean hasMore = parameters.getPaging().getSkipCount() + parameters.getPaging().getMaxItems() < totalItems; | ||||
|         return CollectionWithPagingInfo.asPaged(parameters.getPaging(), page, hasMore, totalItems); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @WebApiDescription(title = "Create one (or more) holds in a file plan identified by 'filePlanId'") | ||||
|     public List<HoldModel> create(String filePlanId, List<HoldModel> holds, Parameters parameters) | ||||
|     { | ||||
|         checkNotBlank("filePlanId", filePlanId); | ||||
|         mandatory("holds", holds); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(filePlanId, RecordsManagementModel.TYPE_FILE_PLAN); | ||||
|  | ||||
|         RetryingTransactionCallback<List<NodeRef>> callback = () -> { | ||||
|             List<NodeRef> createdNodes = new LinkedList<>(); | ||||
|             for (HoldModel nodeInfo : holds) | ||||
|             { | ||||
|                 NodeRef newNodeRef = holdService.createHold(parentNodeRef, nodeInfo.name(), nodeInfo.reason(), | ||||
|                     nodeInfo.description()); | ||||
|                 createdNodes.add(newNodeRef); | ||||
|             } | ||||
|             return createdNodes; | ||||
|         }; | ||||
|  | ||||
|         List<NodeRef> createdNodes = transactionService.getRetryingTransactionHelper() | ||||
|             .doInTransaction(callback, false, true); | ||||
|  | ||||
|         return createdNodes.stream() | ||||
|             .map(hold -> fileFolderService.getFileInfo(hold)) | ||||
|             .map(nodesModelFactory::createHoldModel) | ||||
|             .collect(Collectors.toCollection(LinkedList::new)); | ||||
|     } | ||||
|  | ||||
|     public void setApiUtils(FilePlanComponentsApiUtils apiUtils) | ||||
|     { | ||||
|         this.apiUtils = apiUtils; | ||||
|     } | ||||
|  | ||||
|     public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory) | ||||
|     { | ||||
|         this.nodesModelFactory = nodesModelFactory; | ||||
|     } | ||||
|  | ||||
|     public void setHoldService(HoldService holdService) | ||||
|     { | ||||
|         this.holdService = holdService; | ||||
|     } | ||||
|  | ||||
|     public void setFileFolderService(FileFolderService fileFolderService) | ||||
|     { | ||||
|         this.fileFolderService = fileFolderService; | ||||
|     } | ||||
|  | ||||
|     public void setTransactionService(TransactionService transactionService) | ||||
|     { | ||||
|         this.transactionService = transactionService; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,120 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.holds; | ||||
|  | ||||
| import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank; | ||||
| import static org.alfresco.util.ParameterCheck.mandatory; | ||||
|  | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.Optional; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkMonitor; | ||||
| import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; | ||||
| import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; | ||||
| import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; | ||||
| 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.CollectionWithPagingInfo; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.security.AccessStatus; | ||||
| import org.alfresco.service.cmr.security.PermissionService; | ||||
| import org.springframework.extensions.surf.util.I18NUtil; | ||||
|  | ||||
| @RelationshipResource(name = "bulk-statuses", entityResource = HoldsEntityResource.class, title = "Bulk statuses of a hold") | ||||
| public class HoldsBulkStatusesRelation | ||||
|     implements RelationshipResourceAction.Read<HoldBulkStatus>, RelationshipResourceAction.ReadById<HoldBulkStatus> | ||||
| { | ||||
|     private HoldBulkMonitor holdBulkMonitor; | ||||
|     private FilePlanComponentsApiUtils apiUtils; | ||||
|     private PermissionService permissionService; | ||||
|  | ||||
|     @Override | ||||
|     public CollectionWithPagingInfo<HoldBulkStatus> readAll(String holdId, Parameters parameters) | ||||
|     { | ||||
|         // validate parameters | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef holdRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|  | ||||
|         checkReadPermissions(holdRef); | ||||
|  | ||||
|         List<HoldBulkStatus> statuses = holdBulkMonitor.getBulkStatusesForHold(holdId); | ||||
|         List<HoldBulkStatus> page = statuses.stream() | ||||
|             .skip(parameters.getPaging().getSkipCount()) | ||||
|             .limit(parameters.getPaging().getMaxItems()) | ||||
|             .collect(Collectors.toCollection(LinkedList::new)); | ||||
|  | ||||
|         int totalItems = statuses.size(); | ||||
|         boolean hasMore = parameters.getPaging().getSkipCount() + parameters.getPaging().getMaxItems() < totalItems; | ||||
|         return CollectionWithPagingInfo.asPaged(parameters.getPaging(), page, hasMore, totalItems); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public HoldBulkStatus readById(String holdId, String bulkStatusId, Parameters parameters) | ||||
|         throws RelationshipResourceNotFoundException | ||||
|     { | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         checkNotBlank("bulkStatusId", bulkStatusId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef holdRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|  | ||||
|         checkReadPermissions(holdRef); | ||||
|  | ||||
|         return Optional.ofNullable(holdBulkMonitor.getBulkStatus(bulkStatusId)).orElseThrow(() -> new EntityNotFoundException(bulkStatusId)); | ||||
|     } | ||||
|  | ||||
|     private void checkReadPermissions(NodeRef holdRef) | ||||
|     { | ||||
|         if (permissionService.hasReadPermission(holdRef) == AccessStatus.DENIED) | ||||
|         { | ||||
|             throw new PermissionDeniedException(I18NUtil.getMessage("permissions.err_access_denied")); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void setHoldBulkMonitor(HoldBulkMonitor holdBulkMonitor) | ||||
|     { | ||||
|         this.holdBulkMonitor = holdBulkMonitor; | ||||
|     } | ||||
|  | ||||
|     public void setApiUtils(FilePlanComponentsApiUtils apiUtils) | ||||
|     { | ||||
|         this.apiUtils = apiUtils; | ||||
|     } | ||||
|  | ||||
|     public void setPermissionService(PermissionService permissionService) | ||||
|     { | ||||
|         this.permissionService = permissionService; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,207 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.holds; | ||||
|  | ||||
| import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank; | ||||
| import static org.alfresco.util.ParameterCheck.mandatory; | ||||
|  | ||||
| import java.util.LinkedList; | ||||
| import java.util.List; | ||||
| import java.util.stream.Collectors; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.hold.HoldService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; | ||||
| import org.alfresco.repo.node.integrity.IntegrityException; | ||||
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; | ||||
| import org.alfresco.rest.framework.WebApiDescription; | ||||
| import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; | ||||
| import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException; | ||||
| import org.alfresco.rest.framework.resource.RelationshipResource; | ||||
| import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; | ||||
| import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory; | ||||
| import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils; | ||||
| import org.alfresco.rm.rest.api.model.HoldChild; | ||||
| import org.alfresco.service.cmr.model.FileFolderService; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.cmr.repository.StoreRef; | ||||
| import org.alfresco.service.cmr.security.AccessStatus; | ||||
| import org.alfresco.service.cmr.security.PermissionService; | ||||
| import org.alfresco.service.transaction.TransactionService; | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
| import org.springframework.extensions.surf.util.I18NUtil; | ||||
|  | ||||
| /** | ||||
|  * Hold children relation | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @RelationshipResource(name = "children", entityResource = HoldsEntityResource.class, title = "Children of a hold") | ||||
| public class HoldsChildrenRelation implements | ||||
|     RelationshipResourceAction.Create<HoldChild>, | ||||
|     RelationshipResourceAction.Read<HoldChild>, | ||||
|     RelationshipResourceAction.Delete, | ||||
|     InitializingBean | ||||
| { | ||||
|     private HoldService holdService; | ||||
|     private FilePlanComponentsApiUtils apiUtils; | ||||
|     private ApiNodesModelFactory nodesModelFactory; | ||||
|     private TransactionService transactionService; | ||||
|     private FileFolderService fileFolderService; | ||||
|     private PermissionService permissionService; | ||||
|  | ||||
|     @Override | ||||
|     public void afterPropertiesSet() throws Exception | ||||
|     { | ||||
|         mandatory("holdService", holdService); | ||||
|         mandatory("apiUtils", apiUtils); | ||||
|         mandatory("nodesModelFactory", nodesModelFactory); | ||||
|         mandatory("transactionService", transactionService); | ||||
|         mandatory("fileFolderService", fileFolderService); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @WebApiDescription(title = "Add one (or more) children as children of a hold identified by 'holdId'") | ||||
|     public List<HoldChild> create(String holdId, List<HoldChild> children, Parameters parameters) | ||||
|     { | ||||
|         // validate parameters | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         mandatory("children", children); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|  | ||||
|         RetryingTransactionCallback<List<NodeRef>> callback = () -> { | ||||
|             List<NodeRef> createdNodes = children.stream() | ||||
|                 .map(holdChild -> new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, holdChild.id())) | ||||
|                 .collect(Collectors.toList()); | ||||
|             try | ||||
|             { | ||||
|                 holdService.addToHold(parentNodeRef, createdNodes); | ||||
|             } | ||||
|             catch (IntegrityException exception) | ||||
|             { | ||||
|                 // Throw 400 Bad Request when a node with id 'holdId' is not a hold or a child cannot be added to a hold | ||||
|                 throw new InvalidArgumentException(exception.getMsgId()).initCause(exception); | ||||
|             } | ||||
|             return createdNodes; | ||||
|         }; | ||||
|  | ||||
|         List<NodeRef> nodeInfos = transactionService.getRetryingTransactionHelper() | ||||
|             .doInTransaction(callback, false, true); | ||||
|  | ||||
|         return nodeInfos.stream() | ||||
|             .map(nodeRef -> new HoldChild(nodeRef.getId())) | ||||
|             .collect(Collectors.toCollection(LinkedList::new)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @WebApiDescription(title = "Return a paged list of hold children for the hold identified by 'holdId'") | ||||
|     public CollectionWithPagingInfo<HoldChild> readAll(String holdId, Parameters parameters) | ||||
|     { | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|         List<NodeRef> children = holdService.getHeld(parentNodeRef); | ||||
|  | ||||
|         List<HoldChild> page = children.stream() | ||||
|             .map(NodeRef::getId) | ||||
|             .map(HoldChild::new) | ||||
|             .skip(parameters.getPaging().getSkipCount()) | ||||
|             .limit(parameters.getPaging().getMaxItems()) | ||||
|             .collect(Collectors.toCollection(LinkedList::new)); | ||||
|  | ||||
|         int totalItems = children.size(); | ||||
|         boolean hasMore = parameters.getPaging().getSkipCount() + parameters.getPaging().getMaxItems() < totalItems; | ||||
|         return CollectionWithPagingInfo.asPaged(parameters.getPaging(), page, hasMore, totalItems); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @WebApiDescription(title = "Remove a child from a hold", description = "Remove a child with id 'childId' from a hold with id 'holdId'") | ||||
|     public void delete(String holdId, String childId, Parameters parameters) | ||||
|     { | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         checkNotBlank("childId", childId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|         NodeRef childRef = apiUtils.lookupByPlaceholder(childId); | ||||
|  | ||||
|         if (permissionService.hasReadPermission(childRef) == AccessStatus.DENIED) | ||||
|         { | ||||
|             throw new PermissionDeniedException(I18NUtil.getMessage("permissions.err_access_denied")); | ||||
|         } | ||||
|  | ||||
|         RetryingTransactionCallback<List<NodeRef>> callback = () -> { | ||||
|             try | ||||
|             { | ||||
|                 holdService.removeFromHold(nodeRef, childRef); | ||||
|             } | ||||
|             catch (IntegrityException exception) | ||||
|             { | ||||
|                 // Throw 400 Bad Request when a node with id 'holdId' is not a hold | ||||
|                 throw new InvalidArgumentException(exception.getMsgId()).initCause(exception); | ||||
|             } | ||||
|             return null; | ||||
|         }; | ||||
|  | ||||
|         transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true); | ||||
|     } | ||||
|  | ||||
|     public void setHoldService(HoldService holdService) | ||||
|     { | ||||
|         this.holdService = holdService; | ||||
|     } | ||||
|  | ||||
|     public void setApiUtils(FilePlanComponentsApiUtils apiUtils) | ||||
|     { | ||||
|         this.apiUtils = apiUtils; | ||||
|     } | ||||
|  | ||||
|     public void setTransactionService(TransactionService transactionService) | ||||
|     { | ||||
|         this.transactionService = transactionService; | ||||
|     } | ||||
|  | ||||
|     public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory) | ||||
|     { | ||||
|         this.nodesModelFactory = nodesModelFactory; | ||||
|     } | ||||
|  | ||||
|     public void setFileFolderService(FileFolderService fileFolderService) | ||||
|     { | ||||
|         this.fileFolderService = fileFolderService; | ||||
|     } | ||||
|  | ||||
|     public void setPermissionService(PermissionService permissionService) | ||||
|     { | ||||
|         this.permissionService = permissionService; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,212 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.holds; | ||||
|  | ||||
| import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank; | ||||
| import static org.alfresco.util.ParameterCheck.mandatory; | ||||
|  | ||||
| import jakarta.servlet.http.HttpServletResponse; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.BulkOperation; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.hold.HoldService; | ||||
| import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; | ||||
| import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; | ||||
| import org.alfresco.rest.framework.Operation; | ||||
| import org.alfresco.rest.framework.WebApiDescription; | ||||
| import org.alfresco.rest.framework.WebApiParam; | ||||
| import org.alfresco.rest.framework.resource.EntityResource; | ||||
| import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.rest.framework.webscripts.WithResponse; | ||||
| import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory; | ||||
| import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkOperation; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkOperationEntry; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.rm.rest.api.model.HoldDeletionReason; | ||||
| import org.alfresco.rm.rest.api.model.HoldModel; | ||||
| import org.alfresco.service.cmr.model.FileFolderService; | ||||
| import org.alfresco.service.cmr.model.FileInfo; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.alfresco.service.transaction.TransactionService; | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.springframework.beans.factory.InitializingBean; | ||||
|  | ||||
| /** | ||||
|  * Hold entity resource | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @EntityResource(name = "holds", title = "Holds") | ||||
| public class HoldsEntityResource implements | ||||
|     EntityResourceAction.ReadById<HoldModel>, | ||||
|     EntityResourceAction.Update<HoldModel>, | ||||
|     EntityResourceAction.Delete, | ||||
|     InitializingBean | ||||
| { | ||||
|     private FilePlanComponentsApiUtils apiUtils; | ||||
|     private FileFolderService fileFolderService; | ||||
|     private ApiNodesModelFactory nodesModelFactory; | ||||
|     private HoldService holdService; | ||||
|     private TransactionService transactionService; | ||||
|     private HoldBulkService holdBulkService; | ||||
|  | ||||
|     @Override | ||||
|     public void afterPropertiesSet() throws Exception | ||||
|     { | ||||
|         mandatory("nodesModelFactory", nodesModelFactory); | ||||
|         mandatory("apiUtils", apiUtils); | ||||
|         mandatory("fileFolderService", fileFolderService); | ||||
|         mandatory("holdService", holdService); | ||||
|         mandatory("transactionService", transactionService); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @WebApiDescription(title = "Get hold information", description = "Get information for a hold with id 'holdId'") | ||||
|     @WebApiParam(name = "holdId", title = "The hold id") | ||||
|     public HoldModel readById(String holdId, Parameters parameters) | ||||
|     { | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef hold = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|         FileInfo info = fileFolderService.getFileInfo(hold); | ||||
|         return nodesModelFactory.createHoldModel(info); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @WebApiDescription(title = "Update a hold", description = "Updates a hold with id 'holdId'") | ||||
|     public HoldModel update(String holdId, HoldModel holdModel, Parameters parameters) | ||||
|     { | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         mandatory("holdModel", holdModel); | ||||
|         mandatory("holdModel.name", holdModel.name()); | ||||
|         mandatory("holdModel.reason", holdModel.reason()); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|  | ||||
|         RetryingTransactionCallback<Void> callback = () -> { | ||||
|             holdService.updateHold(nodeRef, holdModel.name(), holdModel.reason(), holdModel.description()); | ||||
|             return null; | ||||
|         }; | ||||
|         transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true); | ||||
|  | ||||
|         RetryingTransactionCallback<FileInfo> readCallback = () -> fileFolderService.getFileInfo(nodeRef); | ||||
|         FileInfo info = transactionService.getRetryingTransactionHelper().doInTransaction(readCallback, false, true); | ||||
|  | ||||
|         return nodesModelFactory.createHoldModel(info); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @WebApiDescription(title = "Delete hold", description = "Deletes a hold with id 'holdId'") | ||||
|     public void delete(String holdId, Parameters parameters) | ||||
|     { | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef hold = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|         RetryingTransactionCallback<Void> callback = () -> { | ||||
|             holdService.deleteHold(hold); | ||||
|             return null; | ||||
|         }; | ||||
|         transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true); | ||||
|     } | ||||
|  | ||||
|     @Operation("delete") | ||||
|     @WebApiDescription(title = "Delete hold with a reason", | ||||
|         successStatus = HttpServletResponse.SC_OK) | ||||
|     public HoldDeletionReason deleteHoldWithReason(String holdId, HoldDeletionReason reason, Parameters parameters, | ||||
|         WithResponse withResponse) | ||||
|     { | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         mandatory("reason", reason); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef hold = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|         String deletionReason = reason.reason(); | ||||
|  | ||||
|         RetryingTransactionCallback<Void> callback = () -> { | ||||
|             if (StringUtils.isNotBlank(deletionReason)) | ||||
|             { | ||||
|                 holdService.setHoldDeletionReason(hold, deletionReason); | ||||
|             } | ||||
|             holdService.deleteHold(hold); | ||||
|             return null; | ||||
|         }; | ||||
|         transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true); | ||||
|  | ||||
|         return reason; | ||||
|     } | ||||
|  | ||||
|     @Operation("bulk") | ||||
|     @WebApiDescription(title = "Start the hold bulk operation", | ||||
|         successStatus = HttpServletResponse.SC_ACCEPTED) | ||||
|     public HoldBulkOperationEntry bulk(String holdId, HoldBulkOperation holdBulkOperation, Parameters parameters, | ||||
|         WithResponse withResponse) | ||||
|     { | ||||
|         // validate parameters | ||||
|         checkNotBlank("holdId", holdId); | ||||
|         mandatory("parameters", parameters); | ||||
|  | ||||
|         NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD); | ||||
|  | ||||
|         HoldBulkStatus holdBulkStatus = holdBulkService.execute(parentNodeRef, | ||||
|             new BulkOperation(holdBulkOperation.query(), holdBulkOperation.op().name())); | ||||
|         return new HoldBulkOperationEntry(holdBulkStatus.bulkStatusId(), holdBulkStatus.totalItems()); | ||||
|     } | ||||
|  | ||||
|     public void setApiUtils(FilePlanComponentsApiUtils apiUtils) | ||||
|     { | ||||
|         this.apiUtils = apiUtils; | ||||
|     } | ||||
|  | ||||
|     public void setFileFolderService(FileFolderService fileFolderService) | ||||
|     { | ||||
|         this.fileFolderService = fileFolderService; | ||||
|     } | ||||
|  | ||||
|     public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory) | ||||
|     { | ||||
|         this.nodesModelFactory = nodesModelFactory; | ||||
|     } | ||||
|  | ||||
|     public void setHoldService(HoldService holdService) | ||||
|     { | ||||
|         this.holdService = holdService; | ||||
|     } | ||||
|  | ||||
|     public void setTransactionService(TransactionService transactionService) | ||||
|     { | ||||
|         this.transactionService = transactionService; | ||||
|     } | ||||
|  | ||||
|     public void setHoldBulkService(HoldBulkService holdBulkService) | ||||
|     { | ||||
|         this.holdBulkService = holdBulkService; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Package info that defines the Information Governance Holds REST API | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| @WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1) | ||||
| package org.alfresco.rm.rest.api.holds; | ||||
| import org.alfresco.rest.framework.Api; | ||||
| import org.alfresco.rest.framework.WebApi; | ||||
| @@ -47,6 +47,7 @@ import org.alfresco.rest.api.model.UserInfo; | ||||
| import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter; | ||||
| import org.alfresco.rest.framework.resource.parameters.Parameters; | ||||
| import org.alfresco.rm.rest.api.model.FilePlan; | ||||
| import org.alfresco.rm.rest.api.model.HoldModel; | ||||
| import org.alfresco.rm.rest.api.model.RMNode; | ||||
| import org.alfresco.rm.rest.api.model.Record; | ||||
| import org.alfresco.rm.rest.api.model.RecordCategory; | ||||
| @@ -637,6 +638,21 @@ public class ApiNodesModelFactory | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Creates an object of type HoldModel | ||||
|      * | ||||
|      * @param info info of the hold | ||||
|      * @return HoldModel object | ||||
|      */ | ||||
|     public HoldModel createHoldModel(FileInfo info) | ||||
|     { | ||||
|         return new HoldModel(info.getNodeRef().getId(), | ||||
|             (String) info.getProperties().get(ContentModel.PROP_NAME), | ||||
|             (String) info.getProperties().get(ContentModel.PROP_DESCRIPTION), | ||||
|             (String)  info.getProperties().get(RecordsManagementModel.PROP_HOLD_REASON)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates an object of type FilePlan | ||||
|      * | ||||
|   | ||||
| @@ -0,0 +1,33 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||||
|  | ||||
| import org.alfresco.rest.api.search.model.Query; | ||||
|  | ||||
| public record HoldBulkOperation(@JsonProperty(required = true) Query query, @JsonProperty(required = true) HoldBulkOperationType op) {} | ||||
| @@ -0,0 +1,29 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| public record HoldBulkOperationEntry(String bulkStatusId, long totalItems){} | ||||
| @@ -0,0 +1,38 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| /** | ||||
|  * This enum represents the types of bulk operations that can be performed on holds | ||||
|  */ | ||||
| public enum HoldBulkOperationType | ||||
| { | ||||
|     /** | ||||
|      * The ADD operation represents adding items to a hold in bulk. | ||||
|      */ | ||||
|     ADD | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.util.Date; | ||||
|  | ||||
| public record HoldBulkStatus(String bulkStatusId, Date startTime, Date endTime, long processedItems, long errorsCount, | ||||
|                              long totalItems, String lastError) implements Serializable | ||||
| { | ||||
|     public enum Status | ||||
|     { | ||||
|         PENDING("PENDING"), | ||||
|         IN_PROGRESS("IN PROGRESS"), | ||||
|         DONE("DONE"); | ||||
|  | ||||
|         private final String value; | ||||
|  | ||||
|         Status(String value) | ||||
|         { | ||||
|             this.value = value; | ||||
|         } | ||||
|  | ||||
|         public String getValue() | ||||
|         { | ||||
|             return value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public String getStatus() | ||||
|     { | ||||
|         if (startTime == null && endTime == null) | ||||
|         { | ||||
|             return Status.PENDING.getValue(); | ||||
|         } | ||||
|         else if (startTime != null && endTime == null) | ||||
|         { | ||||
|             return Status.IN_PROGRESS.getValue(); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             return Status.DONE.getValue(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| /** | ||||
|  * Hold Child POJO for use in the v1 REST API. | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public record HoldChild(String id) | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| /** | ||||
|  * Hold Deletion Reason POJO for use in the v1 REST API. | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public record HoldDeletionReason(String reason) | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| /*- | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.rm.rest.api.model; | ||||
|  | ||||
| /** | ||||
|  * Hold POJO for use in the v1 REST API. | ||||
|  * | ||||
|  * @author Damian Ujma | ||||
|  */ | ||||
| public record HoldModel(String id, String name, String description, String reason) | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1 @@ | ||||
| com.epam.reportportal.testng.ReportPortalTestNGListener | ||||
| @@ -4,7 +4,7 @@ | ||||
|  | ||||
| # Version label | ||||
| version.major=23 | ||||
| version.minor=2 | ||||
| version.minor=3 | ||||
| version.revision=0 | ||||
| version.label= | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,119 @@ | ||||
| /* | ||||
|  * #%L | ||||
|  * Alfresco Records Management Module | ||||
|  * %% | ||||
|  * 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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco.module.org_alfresco_module_rm.bulk; | ||||
|  | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.mockito.Mockito.when; | ||||
|  | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.Date; | ||||
| import java.util.List; | ||||
|  | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.hold.DefaultHoldBulkMonitor; | ||||
| import org.alfresco.module.org_alfresco_module_rm.bulk.hold.HoldBulkProcessDetails; | ||||
| import org.alfresco.repo.cache.SimpleCache; | ||||
| import org.alfresco.rm.rest.api.model.HoldBulkStatus; | ||||
| import org.alfresco.service.cmr.repository.NodeRef; | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
| import org.mockito.Mock; | ||||
| import org.mockito.Mockito; | ||||
| import org.mockito.MockitoAnnotations; | ||||
|  | ||||
| public class DefaultHoldBulkMonitorUnitTest | ||||
| { | ||||
|  | ||||
|     @Mock | ||||
|     private SimpleCache<String, HoldBulkStatus> holdProgressCache; | ||||
|  | ||||
|     @Mock | ||||
|     private SimpleCache<String, List<HoldBulkProcessDetails>> holdProcessRegistry; | ||||
|  | ||||
|     private DefaultHoldBulkMonitor holdBulkMonitor; | ||||
|  | ||||
|     @Before | ||||
|     public void setUp() | ||||
|     { | ||||
|         MockitoAnnotations.openMocks(this); | ||||
|         holdBulkMonitor = new DefaultHoldBulkMonitor(); | ||||
|         holdBulkMonitor.setHoldProgressCache(holdProgressCache); | ||||
|         holdBulkMonitor.setHoldProcessRegistry(holdProcessRegistry); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testUpdateBulkStatus() | ||||
|     { | ||||
|         HoldBulkStatus status = new HoldBulkStatus("bulkStatusId", null, null, 0L, 0L, 0L, null); | ||||
|  | ||||
|         holdBulkMonitor.updateBulkStatus(status); | ||||
|  | ||||
|         Mockito.verify(holdProgressCache).put("bulkStatusId", status); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testRegisterProcess() | ||||
|     { | ||||
|         NodeRef holdRef = new NodeRef("workspace://SpacesStore/holdId"); | ||||
|         String processId = "processId"; | ||||
|         when(holdProcessRegistry.get(holdRef.getId())).thenReturn(null); | ||||
|  | ||||
|         holdBulkMonitor.registerProcess(holdRef, processId); | ||||
|  | ||||
|         Mockito.verify(holdProcessRegistry) | ||||
|             .put(holdRef.getId(), Arrays.asList(new HoldBulkProcessDetails(processId, null))); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetBulkStatusesForHoldReturnsEmptyListWhenNoProcesses() | ||||
|     { | ||||
|         when(holdProcessRegistry.get("holdId")).thenReturn(null); | ||||
|         assertEquals(Collections.emptyList(), holdBulkMonitor.getBulkStatusesForHold("holdId")); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void testGetBulkStatusesForHoldReturnsSortedStatuses() | ||||
|     { | ||||
|         HoldBulkStatus status1 = new HoldBulkStatus(null, new Date(1000), new Date(2000), 0L, 0L, 0L, null); | ||||
|         HoldBulkStatus status2 = new HoldBulkStatus(null, new Date(3000), null, 0L, 0L, 0L, null); | ||||
|         HoldBulkStatus status3 = new HoldBulkStatus(null, new Date(4000), null, 0L, 0L, 0L, null); | ||||
|         HoldBulkStatus status4 = new HoldBulkStatus(null, new Date(500), new Date(800), 0L, 0L, 0L, null); | ||||
|         HoldBulkStatus status5 = new HoldBulkStatus(null, null, null, 0L, 0L, 0L, null); | ||||
|  | ||||
|         when(holdProcessRegistry.get("holdId")).thenReturn( | ||||
|             Arrays.asList("process1", "process2", "process3", "process4", "process5") | ||||
|                 .stream().map(bulkStatusId -> new HoldBulkProcessDetails(bulkStatusId, null)).toList()); | ||||
|         when(holdProgressCache.get("process1")).thenReturn(status1); | ||||
|         when(holdProgressCache.get("process2")).thenReturn(status2); | ||||
|         when(holdProgressCache.get("process3")).thenReturn(status3); | ||||
|         when(holdProgressCache.get("process4")).thenReturn(status4); | ||||
|         when(holdProgressCache.get("process5")).thenReturn(status5); | ||||
|  | ||||
|         assertEquals(Arrays.asList(status5, status3, status2, status1, status4), | ||||
|             holdBulkMonitor.getBulkStatusesForHold("holdId")); | ||||
|     } | ||||
| } | ||||
| @@ -87,6 +87,7 @@ public class HoldServiceImplUnitTest extends BaseUnitTest | ||||
|     /** test values */ | ||||
|     private static final String HOLD_NAME = "holdname"; | ||||
|     private static final String HOLD_REASON = "holdreason"; | ||||
|     private static final String HOLD_DELETION_REASON = "holddeletionreason"; | ||||
|     private static final String HOLD_DESCRIPTION = "holddescription"; | ||||
|     private static final String GENERIC_ERROR_MSG = "any error message text"; | ||||
|  | ||||
| @@ -173,7 +174,7 @@ public class HoldServiceImplUnitTest extends BaseUnitTest | ||||
|     } | ||||
|  | ||||
|     @Test (expected=AlfrescoRuntimeException.class) | ||||
|     public void getHold() | ||||
|     public void testGetHold() | ||||
|     { | ||||
|         // setup node service interactions | ||||
|         when(mockedNodeService.getChildByName(eq(holdContainer), eq(ContentModel.ASSOC_CONTAINS), anyString())).thenReturn(null) | ||||
| @@ -194,19 +195,19 @@ public class HoldServiceImplUnitTest extends BaseUnitTest | ||||
|     } | ||||
|  | ||||
|     @Test (expected=RuntimeException.class) | ||||
|     public void getHeldNotAHold() | ||||
|     public void testGetHeldNotAHold() | ||||
|     { | ||||
|         holdService.getHeld(recordFolder); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void getHeldNoResults() | ||||
|     public void testGetHeldNoResults() | ||||
|     { | ||||
|         assertTrue(holdService.getHeld(hold).isEmpty()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void getHeldWithResults() | ||||
|     public void testGetHeldWithResults() | ||||
|     { | ||||
|         // setup record folder in hold | ||||
|         List<ChildAssociationRef> holds = new ArrayList<>(2); | ||||
| @@ -259,7 +260,7 @@ public class HoldServiceImplUnitTest extends BaseUnitTest | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void getHoldReason() | ||||
|     public void testGetHoldReason() | ||||
|     { | ||||
|         // setup node service interactions | ||||
|         when(mockedNodeService.exists(hold)) | ||||
| @@ -306,6 +307,80 @@ public class HoldServiceImplUnitTest extends BaseUnitTest | ||||
|         verify(mockedNodeService).setProperty(hold, PROP_HOLD_REASON, HOLD_REASON); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void setHoldDeletionReasonForNodeDoesNotExist() | ||||
|     { | ||||
|         // setup node service interactions | ||||
|         when(mockedNodeService.exists(hold)) | ||||
|             .thenReturn(false); | ||||
|  | ||||
|         // node does not exist | ||||
|         holdService.setHoldDeletionReason(hold, HOLD_DELETION_REASON); | ||||
|         verify(mockedNodeService, never()).setProperty(hold, PROP_HOLD_DELETION_REASON, HOLD_DELETION_REASON); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void setHoldDeletionReasonForNodeIsNotAHold() | ||||
|     { | ||||
|         // setup node service interactions | ||||
|         when(mockedNodeService.exists(hold)) | ||||
|             .thenReturn(true); | ||||
|  | ||||
|         // node isn't a hold | ||||
|         holdService.setHoldDeletionReason(recordFolder, HOLD_DELETION_REASON); | ||||
|         verify(mockedNodeService, never()).setProperty(hold, PROP_HOLD_DELETION_REASON, HOLD_DELETION_REASON); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void setHoldDeletionReason() | ||||
|     { | ||||
|         // setup node service interactions | ||||
|         when(mockedNodeService.exists(hold)) | ||||
|             .thenReturn(true); | ||||
|  | ||||
|         // set hold deletion reason | ||||
|         holdService.setHoldDeletionReason(hold, HOLD_DELETION_REASON); | ||||
|         verify(mockedNodeService).setProperty(hold, PROP_HOLD_DELETION_REASON, HOLD_DELETION_REASON); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void updateHoldThatDoesNotExist() | ||||
|     { | ||||
|         // setup node service interactions | ||||
|         when(mockedNodeService.exists(hold)) | ||||
|             .thenReturn(false); | ||||
|  | ||||
|         // node does not exist | ||||
|         holdService.updateHold(hold, HOLD_NAME, HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         verify(mockedNodeService, never()).setProperty(any(NodeRef.class), any(QName.class), any(String.class)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void updateHoldThatIsNotAHold() | ||||
|     { | ||||
|         // setup node service interactions | ||||
|         when(mockedNodeService.exists(hold)) | ||||
|             .thenReturn(true); | ||||
|  | ||||
|         // node isn't a hold | ||||
|         holdService.updateHold(recordFolder, HOLD_NAME, HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         verify(mockedNodeService, never()).setProperty(any(NodeRef.class), any(QName.class), any(String.class)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     public void updateHold() | ||||
|     { | ||||
|         // setup node service interactions | ||||
|         when(mockedNodeService.exists(hold)) | ||||
|             .thenReturn(true); | ||||
|  | ||||
|         // update hold | ||||
|         holdService.updateHold(hold, HOLD_NAME, HOLD_REASON, HOLD_DESCRIPTION); | ||||
|         verify(mockedNodeService).setProperty(hold, ContentModel.PROP_NAME, HOLD_NAME); | ||||
|         verify(mockedNodeService).setProperty(hold, ContentModel.PROP_DESCRIPTION, HOLD_DESCRIPTION); | ||||
|         verify(mockedNodeService).setProperty(hold, PROP_HOLD_REASON, HOLD_REASON); | ||||
|     } | ||||
|  | ||||
|     @Test (expected=AlfrescoRuntimeException.class) | ||||
|     public void deleteHoldNotAHold() | ||||
|     { | ||||
|   | ||||
| @@ -0,0 +1 @@ | ||||
| com.epam.reportportal.testng.ReportPortalTestNGListener | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-governance-services-community-repo-parent</artifactId> | ||||
|         <version>23.2.0.31</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <build> | ||||
|   | ||||
| @@ -38,6 +38,8 @@ tags: | ||||
|     description: Retrieve and manage unfiled records containers | ||||
|   - name: unfiled-record-folders | ||||
|     description: Retrieve and manage unfiled record folders | ||||
|   - name: holds | ||||
|     description: Retrieve and manage holds | ||||
|  | ||||
| paths: | ||||
|   ## GS sites | ||||
| @@ -418,6 +420,124 @@ paths: | ||||
|           description: New name clashes with an existing node in the current parent container | ||||
|         '422': | ||||
|           description: Model integrity exception, including node name with invalid characters | ||||
|   '/file-plans/{filePlanId}/holds': | ||||
|     get: | ||||
|       tags: | ||||
|         - file-plans | ||||
|       summary: Get all holds in a file plan | ||||
|       description: | | ||||
|         Returns a list of holds. | ||||
|       operationId: getHolds | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/filePlanIdWithAliasParam' | ||||
|         - $ref: '#/parameters/skipCountParam' | ||||
|         - $ref: '#/parameters/maxItemsParam' | ||||
|       consumes: | ||||
|         - application/json | ||||
|       produces: | ||||
|         - application/json | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldPaging' | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to read **filePlanId** | ||||
|         '404': | ||||
|           description: "**filePlanId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|     post: | ||||
|       tags: | ||||
|         - file-plans | ||||
|       summary: Create holds for a file plan | ||||
|       description: | | ||||
|         Creates a new hold. | ||||
|  | ||||
|         You must specify at least a **name** and a **reason** property. | ||||
|  | ||||
|         **Note:** You can create more than one hold by specifying a list of holds in the JSON body. | ||||
|         For example, the following JSON body creates two holds: | ||||
|         ```JSON | ||||
|         [ | ||||
|         { | ||||
|           "name":"Hold1", | ||||
|           "description": "Description1", | ||||
|           "reason": "Reason1" | ||||
|         }, | ||||
|         { | ||||
|           "name":"Hold2", | ||||
|           "description": "Description2", | ||||
|           "reason": "Reason2" | ||||
|         } | ||||
|         ] | ||||
|         ``` | ||||
|  | ||||
|         If you specify a list as input, then a paginated list rather than an entry is returned in the response body. For example: | ||||
|  | ||||
|         ```JSON | ||||
|         { | ||||
|           "list": { | ||||
|             "pagination": { | ||||
|               "count": 2, | ||||
|               "hasMoreItems": false, | ||||
|               "totalItems": 2, | ||||
|               "skipCount": 0, | ||||
|               "maxItems": 100 | ||||
|             }, | ||||
|             "entries": [ | ||||
|               { | ||||
|                 "entry": { | ||||
|                   ... | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "entry": { | ||||
|                   ... | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|           } | ||||
|         } | ||||
|         ``` | ||||
|       operationId: addHold | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/filePlanIdWithAliasParam' | ||||
|         - in: body | ||||
|           name: nodeBodyCreate | ||||
|           description: The node information to create. | ||||
|           required: true | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldCreateBodyModel' | ||||
|       consumes: | ||||
|         - application/json | ||||
|       produces: | ||||
|         - application/json | ||||
|       responses: | ||||
|         '201': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldModelEntry' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **filePlanId** is not a valid format or **HoldCreateBodyModel** is not valid | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to create a hold | ||||
|         '404': | ||||
|           description: | | ||||
|             **filePlanId** does not exist | ||||
|         '409': | ||||
|           description: A hold with the name **name** already exists | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   ## Unfiled records containers | ||||
|   '/unfiled-containers/{unfiledContainerId}': | ||||
|     get: | ||||
| @@ -2092,6 +2212,395 @@ paths: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   ## Holds | ||||
|   '/holds/{holdId}': | ||||
|     get: | ||||
|       tags: | ||||
|         - holds | ||||
|       summary: Get a hold | ||||
|       description: | | ||||
|         Gets information for hold with id **holdId**. | ||||
|       operationId: getHold | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|       produces: | ||||
|         - application/json | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldModelEntry' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdId** is not a valid format | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to read **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|     put: | ||||
|       tags: | ||||
|         - holds | ||||
|       summary: Update a hold | ||||
|       description: | | ||||
|         Updates the hold with id **holdId**. For example, you can rename a hold: | ||||
|         ```JSON | ||||
|         { | ||||
|           "name":"My new name", | ||||
|           "description":"Existing description", | ||||
|           "reason":"Existing reason" | ||||
|         } | ||||
|         ``` | ||||
|       operationId: updateHold | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - in: body | ||||
|           name: holdBodyUpdate | ||||
|           description: The hold information to update. | ||||
|           required: true | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldCreateBodyModel' | ||||
|       produces: | ||||
|         - application/json | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldModelEntry' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: the update request is invalid or **holdId** is not a valid format or **holdBodyUpdate** is invalid | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to update **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** does not exist" | ||||
|         '409': | ||||
|           description: Updated name clashes with an existing node in the current parent folder | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|     delete: | ||||
|       tags: | ||||
|         - holds | ||||
|       summary: Delete a hold | ||||
|       description: | | ||||
|         Deletes the hold with id **holdId**. | ||||
|       operationId: deleteHold | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|       produces: | ||||
|         - application/json | ||||
|       responses: | ||||
|         '204': | ||||
|           description: Successful response | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdId** is not a valid format | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to delete **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/holds/{holdId}/bulk-statuses': | ||||
|     get: | ||||
|       tags: | ||||
|         - holds | ||||
|       operationId: listBulkStatuses | ||||
|       summary: Get bulk statuses | ||||
|       description: | | ||||
|         Gets bulk statuses for hold with id **holdId**. | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - $ref: '#/parameters/skipCountParam' | ||||
|         - $ref: '#/parameters/maxItemsParam' | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldBulkStatusPaging' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdId** is not a valid format | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to read **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/holds/{holdId}/bulk-statuses/{bulkStatusId}': | ||||
|     get: | ||||
|       tags: | ||||
|         - holds | ||||
|       operationId: getBulkStatus | ||||
|       summary: Get a bulk status | ||||
|       description: | | ||||
|         Gets a bulk status specified by **bulkStatusId** for **holdId**. | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - $ref: '#/parameters/bulkStatusId' | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldBulkStatus' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdId** or **bulkStatusId** is not a valid format | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to read **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** or **bulkStatusId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/holds/{holdId}/bulk': | ||||
|     post: | ||||
|       tags: | ||||
|         - holds | ||||
|       operationId: startHoldBulkProcess | ||||
|       summary: Start the hold bulk process | ||||
|       description: | | ||||
|         Start the asynchronous bulk process for a hold with id **holdId** based on search query results. | ||||
|          | ||||
|         ```JSON | ||||
|         For example, the following JSON body starts the bulk process to add search query results | ||||
|         as children of a hold. | ||||
|          | ||||
|         { | ||||
|           "query": { | ||||
|             "query": "SITE:swsdp and TYPE:content", | ||||
|             "language": "afts" | ||||
|           }, | ||||
|           "op": "ADD" | ||||
|         } | ||||
|         ``` | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - in: body | ||||
|           name: holdBulkOperation | ||||
|           description: Bulk operation. | ||||
|           required: true | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldBulkOperation' | ||||
|       responses: | ||||
|         '202': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldBulkOperationEntry' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdId** is not a valid format or **HoldBulkOperation** is not valid | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to start the bulk process for **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/holds/{holdId}/delete': | ||||
|     post: | ||||
|       tags: | ||||
|         - holds | ||||
|       summary: Delete a hold with a reason | ||||
|       description: | | ||||
|         Deletes the hold with id **holdId** and stores a reason for deletion in the audit log. | ||||
|          | ||||
|         A **reason** must be specified in the request body. | ||||
|       operationId: deleteHoldWithReason | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - in: body | ||||
|           name: holdDeletionReason | ||||
|           description: Reason for deletion. | ||||
|           required: true | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldDeletionReason' | ||||
|       produces: | ||||
|         - application/json | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldDeletionReasonEntry' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdId** is not a valid format | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to update or delete **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/holds/{holdId}/children': | ||||
|     post: | ||||
|       tags: | ||||
|         - holds | ||||
|       summary: Add children to a hold | ||||
|       description: | | ||||
|         Add a child of a hold with id **holdId**. | ||||
|  | ||||
|         You must specify the child **id**. | ||||
|          | ||||
|         The API returns a 201 Created if the child is already a child of the hold.  | ||||
|  | ||||
|         **Note:** You can add more than one child by specifying a list of children in the JSON body. | ||||
|         For example, the following JSON body adds two children: | ||||
|         ```JSON | ||||
|         [ | ||||
|           { | ||||
|             "id":"a7c10f46-b85b-4de5-af1c-930056b736a7" | ||||
|           }, | ||||
|           { | ||||
|             "id":"e0d79b71-be2b-4ce7-a846-a7c50cba20fb" | ||||
|           } | ||||
|         ] | ||||
|         ``` | ||||
|  | ||||
|         If you specify a list as input, then a paginated list rather than an entry is returned in the response body. For example: | ||||
|  | ||||
|         ```JSON | ||||
|         { | ||||
|           "list": { | ||||
|             "pagination": { | ||||
|               "count": 2, | ||||
|               "hasMoreItems": false, | ||||
|               "totalItems": 2, | ||||
|               "skipCount": 0, | ||||
|               "maxItems": 100 | ||||
|             }, | ||||
|             "entries": [ | ||||
|               { | ||||
|                 "entry": { | ||||
|                   ... | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "entry": { | ||||
|                   ... | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|           } | ||||
|         } | ||||
|         ``` | ||||
|       operationId: addChildToHold | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - in: body | ||||
|           name: nodeId | ||||
|           description: The node id. | ||||
|           required: true | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldChild' | ||||
|       consumes: | ||||
|         - application/json | ||||
|       produces: | ||||
|         - application/json | ||||
|       responses: | ||||
|         '201': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldChildEntry' | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdId** is not a valid format or **HoldChild** is not valid | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to add items to the hold | ||||
|         '404': | ||||
|           description: | | ||||
|             **holdId** does not exist | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|     get: | ||||
|       tags: | ||||
|         - holds | ||||
|       summary: Get children of a hold | ||||
|       description: | | ||||
|         Returns a list of children of a hold with id **holdId**. | ||||
|       operationId: getHoldChildren | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - $ref: '#/parameters/skipCountParam' | ||||
|         - $ref: '#/parameters/maxItemsParam' | ||||
|       consumes: | ||||
|         - application/json | ||||
|       produces: | ||||
|         - application/json | ||||
|       responses: | ||||
|         '200': | ||||
|           description: Successful response | ||||
|           schema: | ||||
|             $ref: '#/definitions/HoldChildPaging' | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to read **holdId** | ||||
|         '404': | ||||
|           description: "**holdId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|   '/holds/{holdId}/children/{holdChildId}': | ||||
|     delete: | ||||
|       tags: | ||||
|         - holds | ||||
|       summary: Delete a child of a hold | ||||
|       description: | | ||||
|         Deletes the relationship between a child with id **holdChildId** and a parent hold with id **holdId**. | ||||
|       operationId: removeHoldChild | ||||
|       parameters: | ||||
|         - $ref: '#/parameters/holdIdParam' | ||||
|         - $ref: '#/parameters/holdChildIdParam' | ||||
|       produces: | ||||
|         - application/json | ||||
|       responses: | ||||
|         '204': | ||||
|           description: Successful response | ||||
|         '400': | ||||
|           description: | | ||||
|             Invalid parameter: **holdChildId** or **holdId** is not a valid format | ||||
|         '401': | ||||
|           description: Authentication failed | ||||
|         '403': | ||||
|           description: Current user does not have permission to delete **holdChildId** | ||||
|         '404': | ||||
|           description: " **holdChildId** or **holdId** does not exist" | ||||
|         default: | ||||
|           description: Unexpected error | ||||
|           schema: | ||||
|             $ref: '#/definitions/Error' | ||||
|  | ||||
| parameters: | ||||
|   ## File plans | ||||
| @@ -2175,7 +2684,7 @@ parameters: | ||||
|     description: Also include **source** (in addition to **entries**) with folder information on the parent node – the specified parent **unfiledContainerId** | ||||
|     required: false | ||||
|     type: boolean | ||||
| ## Unfiled record folders | ||||
|   ## Unfiled record folders | ||||
|   unfiledRecordFolderIdParam: | ||||
|     name: unfiledRecordFolderId | ||||
|     in: path | ||||
| @@ -2446,6 +2955,25 @@ parameters: | ||||
|     items: | ||||
|       type: string | ||||
|     collectionFormat: csv | ||||
|   # Holds | ||||
|   holdIdParam: | ||||
|     name: holdId | ||||
|     in: path | ||||
|     description: The identifier of a hold. | ||||
|     required: true | ||||
|     type: string | ||||
|   holdChildIdParam: | ||||
|     name: holdChildId | ||||
|     in: path | ||||
|     description: The identifier of a child of a hold. | ||||
|     required: true | ||||
|     type: string | ||||
|   bulkStatusId: | ||||
|     name: bulkStatusId | ||||
|     in: path | ||||
|     description: The identifier of a bulk process. | ||||
|     required: true | ||||
|     type: string | ||||
|   ## Record | ||||
|   recordIdParam: | ||||
|     name: recordId | ||||
| @@ -3519,6 +4047,174 @@ definitions: | ||||
|         properties: | ||||
|           association: | ||||
|             $ref: '#/definitions/ChildAssociationInfo' | ||||
|   ## Holds | ||||
|   HoldModelEntry: | ||||
|     type: object | ||||
|     required: | ||||
|       - entry | ||||
|     properties: | ||||
|       entry: | ||||
|         $ref: '#/definitions/HoldModel' | ||||
|   HoldModel: | ||||
|     type: object | ||||
|     properties: | ||||
|       id: | ||||
|         type: string | ||||
|       name: | ||||
|         type: string | ||||
|       description: | ||||
|         type: string | ||||
|       reason: | ||||
|         type: string | ||||
|   HoldCreateBodyModel: | ||||
|     type: object | ||||
|     required: | ||||
|       - name | ||||
|       - reason | ||||
|     properties: | ||||
|       name: | ||||
|         type: string | ||||
|       description: | ||||
|         type: string | ||||
|       reason: | ||||
|         type: string | ||||
|   HoldPaging: | ||||
|     type: object | ||||
|     properties: | ||||
|       list: | ||||
|         type: object | ||||
|         properties: | ||||
|           pagination: | ||||
|             $ref: '#/definitions/Pagination' | ||||
|           entries: | ||||
|             type: array | ||||
|             items: | ||||
|               $ref: '#/definitions/HoldModelEntry' | ||||
|   HoldChild: | ||||
|     type: object | ||||
|     required: | ||||
|       - id | ||||
|     properties: | ||||
|       id: | ||||
|         type: string | ||||
|   HoldChildEntry: | ||||
|     type: object | ||||
|     required: | ||||
|       - entry | ||||
|     properties: | ||||
|       entry: | ||||
|         $ref: '#/definitions/HoldChild' | ||||
|   HoldChildPaging: | ||||
|     type: object | ||||
|     properties: | ||||
|       list: | ||||
|         type: object | ||||
|         properties: | ||||
|           pagination: | ||||
|             $ref: '#/definitions/Pagination' | ||||
|           entries: | ||||
|             type: array | ||||
|             items: | ||||
|               $ref: '#/definitions/HoldChildEntry' | ||||
|   HoldDeletionReasonEntry: | ||||
|     type: object | ||||
|     required: | ||||
|       - entry | ||||
|     properties: | ||||
|       entry: | ||||
|         $ref: '#/definitions/HoldDeletionReason' | ||||
|   HoldDeletionReason: | ||||
|     type: object | ||||
|     required: | ||||
|       - reason | ||||
|     properties: | ||||
|       reason: | ||||
|         type: string | ||||
|   SearchRequestQuery: | ||||
|     type: object | ||||
|     required: | ||||
|       - query | ||||
|     properties: | ||||
|       language: | ||||
|         description: The query language in which the query is written. | ||||
|         type: string | ||||
|         default: afts | ||||
|         enum: | ||||
|           - afts | ||||
|           - lucene | ||||
|           - cmis | ||||
|       userQuery: | ||||
|         description: The search request typed in by the user | ||||
|         type: string | ||||
|       query: | ||||
|         description: The query which may have been generated in some way from the userQuery | ||||
|         type: string | ||||
|   HoldBulkOperation: | ||||
|     type: object | ||||
|     properties: | ||||
|       query: | ||||
|         $ref: '#/definitions/SearchRequestQuery' | ||||
|       op: | ||||
|         description: The operation type. | ||||
|         type: string | ||||
|         default: ADD | ||||
|         enum: | ||||
|           - ADD | ||||
|   HoldBulkOperationEntry: | ||||
|     type: object | ||||
|     properties: | ||||
|       bulkStatusId: | ||||
|         type: string | ||||
|       totalItems: | ||||
|         type: integer | ||||
|         format: int64 | ||||
|   HoldBulkStatus: | ||||
|     type: object | ||||
|     properties: | ||||
|       bulkStatusId: | ||||
|         type: string | ||||
|       startTime: | ||||
|         type: string | ||||
|         format: date-time | ||||
|       endTime: | ||||
|         type: string | ||||
|         format: date-time | ||||
|       processedItems: | ||||
|         type: integer | ||||
|         format: int64 | ||||
|       errorsCount: | ||||
|         type: integer | ||||
|         format: int64 | ||||
|       totalItems: | ||||
|         type: integer | ||||
|         format: int64 | ||||
|       lastError: | ||||
|         type: string | ||||
|       status: | ||||
|         type: string | ||||
|         enum: | ||||
|           - PENDING | ||||
|           - IN PROGRESS | ||||
|           - DONE | ||||
|   HoldBulkStatusEntry: | ||||
|     type: object | ||||
|     required: | ||||
|       - entry | ||||
|     properties: | ||||
|       entry: | ||||
|         $ref: '#/definitions/HoldBulkStatus' | ||||
|   HoldBulkStatusPaging: | ||||
|     type: object | ||||
|     properties: | ||||
|       list: | ||||
|         type: object | ||||
|         properties: | ||||
|           pagination: | ||||
|             $ref: '#/definitions/Pagination' | ||||
|           entries: | ||||
|             type: array | ||||
|             items: | ||||
|               $ref: '#/definitions/HoldBulkStatusEntry' | ||||
|   ## | ||||
|   RequestBodyFile: | ||||
|     type: object | ||||
|   | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.2.0.31</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <modules> | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo-amps</artifactId> | ||||
|         <version>23.2.0.31</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
| @@ -70,6 +70,11 @@ | ||||
|             <artifactId>junit</artifactId> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>com.epam.reportportal</groupId> | ||||
|             <artifactId>agent-java-testng</artifactId> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.postgresql</groupId> | ||||
|             <artifactId>postgresql</artifactId> | ||||
|   | ||||
| @@ -0,0 +1 @@ | ||||
| com.epam.reportportal.testng.ReportPortalTestNGListener | ||||
							
								
								
									
										14
									
								
								core/pom.xml
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								core/pom.xml
									
									
									
									
									
								
							| @@ -7,7 +7,7 @@ | ||||
|    <parent> | ||||
|       <groupId>org.alfresco</groupId> | ||||
|       <artifactId>alfresco-community-repo</artifactId> | ||||
|       <version>23.2.0.31</version> | ||||
|       <version>23.3.0.38-SNAPSHOT</version> | ||||
|    </parent> | ||||
|  | ||||
|    <dependencies> | ||||
| @@ -126,6 +126,11 @@ | ||||
|          <artifactId>junit</artifactId> | ||||
|          <scope>test</scope> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>com.epam.reportportal</groupId> | ||||
|          <artifactId>agent-java-testng</artifactId> | ||||
|          <scope>test</scope> | ||||
|       </dependency> | ||||
|       <dependency> | ||||
|          <groupId>org.mockito</groupId> | ||||
|          <artifactId>mockito-core</artifactId> | ||||
| @@ -158,6 +163,13 @@ | ||||
|                </execution> | ||||
|             </executions> | ||||
|          </plugin> | ||||
|          <plugin> | ||||
|             <groupId>org.apache.maven.plugins</groupId> | ||||
|             <artifactId>maven-surefire-plugin</artifactId> | ||||
|             <configuration> | ||||
|                <skipTests>${skipCoreTests}</skipTests> | ||||
|             </configuration> | ||||
|          </plugin> | ||||
|       </plugins> | ||||
|    </build> | ||||
|  | ||||
|   | ||||
							
								
								
									
										59
									
								
								core/src/test/java/AllCoreUnitTestSuite.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								core/src/test/java/AllCoreUnitTestSuite.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| /* | ||||
|  * #%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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco; | ||||
|  | ||||
| import org.junit.experimental.categories.Categories; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.junit.runners.Suite; | ||||
|  | ||||
| @RunWith(Categories.class) | ||||
| @Suite.SuiteClasses({ | ||||
|         org.alfresco.config.SystemPropertiesSetterBeanTest.class, | ||||
|         org.alfresco.encryption.AlfrescoKeyStoreTest.class, | ||||
|         org.alfresco.encryption.EncryptingOutputStreamTest.class, | ||||
|         org.alfresco.error.AlfrescoRuntimeExceptionTest.class, | ||||
|         org.alfresco.query.CannedQueryTest.class, | ||||
|         org.alfresco.util.BridgeTableTest.class, | ||||
|         org.alfresco.util.CachingDateFormatTest.class, | ||||
|         org.alfresco.util.DynamicallySizedThreadPoolExecutorTest.class, | ||||
|         org.alfresco.util.EqualsHelperTest.class, | ||||
|         org.alfresco.util.GuidTest.class, | ||||
|         org.alfresco.util.ISO8601DateFormatTest.class, | ||||
|         org.alfresco.util.LogAdapterTest.class, | ||||
|         org.alfresco.util.LogTeeTest.class, | ||||
|         org.alfresco.util.PathMapperTest.class, | ||||
|         org.alfresco.util.TempFileProviderTest.class, | ||||
|         org.alfresco.util.VersionNumberTest.class, | ||||
|         org.alfresco.util.collections.CollectionUtilsTest.class, | ||||
|         org.alfresco.util.exec.ExecParameterTokenizerTest.class, | ||||
|         org.alfresco.util.exec.RuntimeExecBeansTest.class, | ||||
|         org.alfresco.util.exec.RuntimeExecTest.class, | ||||
|         org.alfresco.util.random.NormalDistributionHelperTest.class, | ||||
|         org.alfresco.util.shard.ExplicitShardingPolicyTest.class, | ||||
|         org.alfresco.util.transaction.SpringAwareUserTransactionTest.class | ||||
| }) | ||||
| public class AllCoreUnitTestSuite { | ||||
| } | ||||
| @@ -0,0 +1 @@ | ||||
| com.epam.reportportal.testng.ReportPortalTestNGListener | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.2.0.31</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <properties> | ||||
| @@ -193,14 +193,6 @@ | ||||
|             <artifactId>tika-parsers-standard-package</artifactId> | ||||
|             <version>${dependency.tika.version}</version> | ||||
|             <exclusions> | ||||
|                 <exclusion> | ||||
|                     <groupId>org.bouncycastle</groupId> | ||||
|                     <artifactId>bcprov-jdk15on</artifactId> | ||||
|                 </exclusion> | ||||
|                 <exclusion> | ||||
|                     <groupId>org.bouncycastle</groupId> | ||||
|                     <artifactId>bcmail-jdk15on</artifactId> | ||||
|                 </exclusion> | ||||
|                 <exclusion> | ||||
|                     <groupId>asm</groupId> | ||||
|                     <artifactId>asm</artifactId> | ||||
| @@ -224,10 +216,6 @@ | ||||
|                 </exclusion> | ||||
|             </exclusions> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.bouncycastle</groupId> | ||||
|             <artifactId>bcmail-jdk15to18</artifactId> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>com.fasterxml.jackson.core</groupId> | ||||
|             <artifactId>jackson-core</artifactId> | ||||
| @@ -247,6 +235,11 @@ | ||||
|             <artifactId>junit</artifactId> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>com.epam.reportportal</groupId> | ||||
|             <artifactId>agent-java-testng</artifactId> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.antlr</groupId> | ||||
|             <artifactId>gunit</artifactId> | ||||
|   | ||||
| @@ -0,0 +1,64 @@ | ||||
| /* | ||||
|  * #%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 <http://www.gnu.org/licenses/>. | ||||
|  * #L% | ||||
|  */ | ||||
| package org.alfresco; | ||||
|  | ||||
| import org.junit.experimental.categories.Categories; | ||||
| import org.junit.runner.RunWith; | ||||
| import org.junit.runners.Suite; | ||||
|  | ||||
| @RunWith(Categories.class) | ||||
| @Suite.SuiteClasses({ | ||||
|         org.alfresco.opencmis.dictionary.CMISAbstractDictionaryServiceTest.class, | ||||
|         org.alfresco.repo.content.MimetypeMapTest.class, | ||||
|         org.alfresco.repo.content.encoding.CharsetFinderTest.class, | ||||
|         org.alfresco.repo.dictionary.AbstractModelTest.class, | ||||
|         org.alfresco.repo.dictionary.DictionaryComponentTest.class, | ||||
|         org.alfresco.repo.dictionary.DictionaryDAOTest.class, | ||||
|         org.alfresco.repo.dictionary.DiffModelTest.class, | ||||
|         org.alfresco.repo.dictionary.constraint.ConstraintsTest.class, | ||||
|         org.alfresco.repo.index.ShardMethodEnumTest.class, | ||||
|         org.alfresco.repo.search.impl.parsers.CMISTest.class, | ||||
|         org.alfresco.repo.search.impl.parsers.CMIS_FTSTest.class, | ||||
|         org.alfresco.repo.search.impl.parsers.FTSTest.class, | ||||
|         org.alfresco.repo.security.authentication.InMemoryTicketComponentTest.class, | ||||
|         org.alfresco.service.cmr.repository.MLTextTest.class, | ||||
|         org.alfresco.service.cmr.repository.NodeRefTest.class, | ||||
|         org.alfresco.service.cmr.repository.PathTest.class, | ||||
|         org.alfresco.service.cmr.repository.PeriodTest.class, | ||||
|         org.alfresco.service.cmr.repository.datatype.DefaultTypeConverterTest.class, | ||||
|         org.alfresco.service.cmr.search.StatsProcessorTest.class, | ||||
|         org.alfresco.service.namespace.DynamicNameSpaceResolverTest.class, | ||||
|         org.alfresco.service.namespace.QNamePatternTest.class, | ||||
|         org.alfresco.service.namespace.QNameTest.class, | ||||
|         org.alfresco.util.ConfigFileFinderTest.class, | ||||
|         org.alfresco.util.ConfigSchedulerTest.class, | ||||
|         org.alfresco.util.ISO9075Test.class, | ||||
|         org.alfresco.util.NumericEncodingTest.class, | ||||
|         org.alfresco.util.SearchDateConversionTest.class, | ||||
|         org.alfresco.util.SearchLanguageConversionTest.class | ||||
| }) | ||||
| public class AllDataModelUnitTestSuite { | ||||
| } | ||||
| @@ -0,0 +1 @@ | ||||
| com.epam.reportportal.testng.ReportPortalTestNGListener | ||||
| @@ -7,7 +7,7 @@ | ||||
|     <parent> | ||||
|         <groupId>org.alfresco</groupId> | ||||
|         <artifactId>alfresco-community-repo</artifactId> | ||||
|         <version>23.2.0.31</version> | ||||
|         <version>23.3.0.38-SNAPSHOT</version> | ||||
|     </parent> | ||||
|  | ||||
|     <dependencies> | ||||
| @@ -29,7 +29,7 @@ | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.bouncycastle</groupId> | ||||
|             <artifactId>bcprov-jdk15to18</artifactId> | ||||
|             <artifactId>bcprov-jdk18on</artifactId> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>de.schlichtherle.truezip</groupId> | ||||
| @@ -46,6 +46,11 @@ | ||||
|             <artifactId>junit</artifactId> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>com.epam.reportportal</groupId> | ||||
|             <artifactId>agent-java-testng</artifactId> | ||||
|             <scope>test</scope> | ||||
|         </dependency> | ||||
|         <dependency> | ||||
|             <groupId>org.mockito</groupId> | ||||
|             <artifactId>mockito-core</artifactId> | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user