Merge branch 'master' of github.com:Alfresco/alfresco-community-repo into fix/MNT-22293_LimitOnDeclareVersionAsRecord

This commit is contained in:
suneet-gupta
2022-11-01 16:28:23 +05:30
322 changed files with 30757 additions and 3127 deletions

View File

@@ -158,3 +158,9 @@ updates:
- "8.16"
registries:
- maven-repository-artifacts-alfresco-com-nexus-content-groups-int
- package-ecosystem: "docker"
directory: "packaging/docker-alfresco/"
schedule:
interval: "daily"
time: "22:00"
timezone: Africa/Abidjan

View File

@@ -1,7 +1,7 @@
---
dist: focal
language: java
jdk: openjdk11
jdk: openjdk17
services:
- docker
@@ -53,7 +53,7 @@ jobs:
- name: "Source Clear Scan (SCA)"
stage: test
if: branch = master OR branch =~ /release\/.*/
if: (branch = master OR branch =~ /release\/.*/) AND type != pull_request
# Run Veracode
install: skip
script: travis_wait 30 bash scripts/travis/source_clear.sh
@@ -69,8 +69,8 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:${TRANSFORMERS_TAG}
script: travis_wait 20 mvn -B test -pl repository -am -Dtest=AppContext01TestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
@@ -78,16 +78,16 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 mvn -B test -pl repository -am -Dtest=AppContext02TestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - AppContext03TestSuite"
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:${TRANSFORMERS_TAG}
script: travis_wait 20 mvn -B test -pl repository -am -Dtest=AppContext03TestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
@@ -95,8 +95,8 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:${TRANSFORMERS_TAG}
script: travis_wait 20 mvn -B test -pl repository -am -Dtest=AppContext04TestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
@@ -104,8 +104,8 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
- mkdir -p "${HOME}/tmp"
- cp repository/src/test/resources/realms/alfresco-realm.json "${HOME}/tmp"
- export HOST_IP=$(hostname -I | cut -f1 -d' ')
@@ -116,8 +116,8 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:${TRANSFORMERS_TAG}
script: travis_wait 20 mvn -B test -pl repository -am -Dtest=AppContext06TestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
@@ -125,8 +125,8 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:${TRANSFORMERS_TAG}
script: travis_wait 20 mvn -B test -pl repository -am -Dtest=AppContextExtraTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
@@ -134,8 +134,8 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:${TRANSFORMERS_TAG}
script: travis_wait 20 mvn -B test -pl repository -am -Dtest=MiscContextTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
@@ -143,8 +143,8 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 mvn -B test -pl repository -am -Dtest=SearchTestSuite -DfailIfNoTests=false -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco -Dindex.subsystem.name=solr6
- name: "Repository - MariaDB 10.2.18 tests"
@@ -152,7 +152,7 @@ jobs:
install: skip
before_script:
- docker run -d -p 3307:3306 --name mariadb -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mariadb:10.2.18 --transaction-isolation=READ-COMMITTED --max-connections=300 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 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
- name: "Repository - MariaDB 10.4 tests"
@@ -160,7 +160,7 @@ jobs:
install: skip
before_script:
- docker run -d -p 3307:3306 --name mariadb -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mariadb:10.4 --transaction-isolation=READ-COMMITTED --max-connections=300 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 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
- name: "Repository - MariaDB 10.5 tests"
@@ -168,7 +168,7 @@ jobs:
install: skip
before_script:
- docker run -d -p 3307:3306 --name mariadb -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mariadb:10.5 --transaction-isolation=READ-COMMITTED --max-connections=300 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 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
- name: "Repository - MariaDB 10.6 tests"
@@ -177,7 +177,7 @@ jobs:
install: skip
before_script:
- docker run -d -p 3307:3306 --name mariadb -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mariadb:10.6 --transaction-isolation=READ-COMMITTED --max-connections=300 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 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
- name: "Repository - MySQL 8 tests"
@@ -186,88 +186,47 @@ jobs:
install: skip
before_script:
- docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mysql:8 --transaction-isolation='READ-COMMITTED'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 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
- name: "Repository - PostgreSQL 10.9 tests"
- name: "Repository - PostgreSQL 13.7 tests"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip db\]/ AND type != pull_request) OR commit_message =~ /\[db\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:10.9 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 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
- name: "Repository - PostgreSQL 11.7 tests"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip db\]/ AND type != pull_request) OR commit_message =~ /\[db\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 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
- name: "Repository - PostgreSQL 11.12 tests"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip db\]/ AND type != pull_request) OR commit_message =~ /\[db\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.12 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 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
- name: "Repository - PostgreSQL 12.4 tests"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip db\]/ AND type != pull_request) OR commit_message =~ /\[db\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:12.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 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
- name: "Repository - PostgreSQL 12.7 tests"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip db\]/ AND type != pull_request) OR commit_message =~ /\[db\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:12.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 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
- name: "Repository - PostgreSQL 13.1 tests"
- name: "Repository - PostgreSQL 14.4 tests"
# We only run DB tests on the latest version of PostgreSQL on feature branches
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip db\]/ AND type != pull_request) OR commit_message =~ /\[db\]/
if: commit_message !~ /\[skip db\]/ OR commit_message =~ /\[db\]/ OR commit_message =~ /\[latest db\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.1 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 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
- name: "Repository - PostgreSQL 13.3 tests"
# We only run DB tests on the latest version of PostgreSQL on feature branches
if: commit_message !~ /\[skip db\]/ OR commit_message =~ /\[latest db\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 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
- name: "Repository - Messaging tests"
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 mvn -B test -pl repository -am -Dtest=CamelRoutesTest,CamelComponentsTest -DfailIfNoTests=false
- name: "Remote-api - AppContext01TestSuite"
if: commit_message !~ /\[skip repo\]/
install: travis_retry travis_wait 40 env REQUIRES_INSTALLED_ARTIFACTS=true bash scripts/travis/build.sh
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext01TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Remote-api - AppContext02TestSuite"
if: commit_message !~ /\[skip repo\]/
install: travis_retry travis_wait 40 env REQUIRES_INSTALLED_ARTIFACTS=true bash scripts/travis/build.sh
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:${TRANSFORMERS_TAG}
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext02TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
@@ -275,8 +234,8 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: travis_retry travis_wait 40 env REQUIRES_INSTALLED_ARTIFACTS=true bash scripts/travis/build.sh
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:${TRANSFORMERS_TAG}
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext03TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
@@ -284,8 +243,8 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: travis_retry travis_wait 40 env REQUIRES_INSTALLED_ARTIFACTS=true bash scripts/travis/build.sh
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:${TRANSFORMERS_TAG}
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext04TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
@@ -293,8 +252,8 @@ jobs:
if: commit_message !~ /\[skip repo\]/
install: travis_retry travis_wait 40 env REQUIRES_INSTALLED_ARTIFACTS=true bash scripts/travis/build.sh
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContextExtraTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "REST API TAS tests part1"
@@ -305,6 +264,7 @@ jobs:
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
script: travis_wait 60 mvn -B verify -f packaging/tests/tas-restapi/pom.xml -Pall-tas-tests,run-restapi-part1 -Denvironment=default -DrunBugs=false
after_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-restapi"
- name: "REST API TAS tests part2"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip tas\]/) OR commit_message =~ /\[tas\]/
@@ -313,6 +273,7 @@ jobs:
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
script: travis_wait 60 mvn -B verify -f packaging/tests/tas-restapi/pom.xml -Pall-tas-tests,run-restapi-part2 -Denvironment=default -DrunBugs=false
after_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-restapi"
- name: "REST API TAS tests part3"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip tas\]/) OR commit_message =~ /\[tas\]/
@@ -321,6 +282,7 @@ jobs:
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
script: travis_wait 60 mvn -B verify -f packaging/tests/tas-restapi/pom.xml -Pall-tas-tests,run-restapi-part3 -Denvironment=default -DrunBugs=false
after_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-restapi"
- name: "CMIS TAS tests - BROWSER binding"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip tas\]/) OR commit_message =~ /\[tas\]/
@@ -329,6 +291,7 @@ jobs:
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
script: travis_wait 40 mvn -B verify -f packaging/tests/tas-cmis/pom.xml -Pall-tas-tests,run-cmis-browser -Denvironment=default -DrunBugs=false
after_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-cmis"
- name: "CMIS TAS tests - ATOM binding"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip tas\]/) OR commit_message =~ /\[tas\]/
@@ -337,6 +300,7 @@ jobs:
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
script: travis_wait 40 mvn -B verify -f packaging/tests/tas-cmis/pom.xml -Pall-tas-tests,run-cmis-atom -Denvironment=default -DrunBugs=false
after_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-cmis"
- name: "CMIS TAS tests - WEBSERVICES binding"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip tas\]/) OR commit_message =~ /\[tas\]/
@@ -345,6 +309,7 @@ jobs:
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal+transforms.yml
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
script: travis_wait 40 mvn -B verify -f packaging/tests/tas-cmis/pom.xml -Pall-tas-tests,run-cmis-webservices -Denvironment=default -DrunBugs=false
after_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-cmis"
- name: "Email TAS tests"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip tas\]/) OR commit_message =~ /\[tas\]/
@@ -353,6 +318,7 @@ jobs:
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal.yml
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
script: travis_wait 30 mvn -B verify -f packaging/tests/tas-email/pom.xml -Pall-tas-tests -Denvironment=default -DrunBugs=false
after_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-email"
- name: "WebDAV TAS tests"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip tas\]/) OR commit_message =~ /\[tas\]/
@@ -361,6 +327,7 @@ jobs:
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal.yml
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
script: travis_wait 30 mvn -B verify -f packaging/tests/tas-webdav/pom.xml -Pall-tas-tests -Denvironment=default -DrunBugs=false
after_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-webdav"
- name: "Integration TAS tests"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip tas\]/) OR commit_message =~ /\[tas\]/
@@ -368,13 +335,15 @@ jobs:
before_script:
- ${TAS_SCRIPTS}/start-compose.sh ${TAS_ENVIRONMENT}/docker-compose-minimal.yml
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
- travis_retry travis_wait 40 mvn install -pl :alfresco-community-repo-integration-test -am -DskipTests -Pall-tas-tests
script: travis_wait 30 mvn -B verify -f packaging/tests/tas-integration/pom.xml -Pall-tas-tests -Denvironment=default -DrunBugs=false
after_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-integration"
- name: "Share Services - ShareServicesTestSuite"
if: commit_message !~ /\[skip repo\]/
install: skip
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.3 postgres -c 'max_connections=300'
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:14.4 postgres -c 'max_connections=300'
script: travis_wait 20 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
- name: "AGS Unit & Integration Tests 01 (PostgreSQL)"

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<modules>
@@ -30,16 +30,20 @@
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- Keeping illegal-access=permit for Java 11 compatibility, even though it has no effect on JDK 17 -->
<argLine>
--illegal-access=permit
--add-opens=java.base/java.lang=ALL-UNNAMED
</argLine>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<!-- Keeping illegal-access=permit for Java 11 compatibility, even though it has no effect on JDK 17 -->
<configuration>
<argLine>
--illegal-access=permit
--add-opens=java.base/java.lang=ALL-UNNAMED
</argLine>
</configuration>
</plugin>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<build>
@@ -45,7 +45,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-reload4j</artifactId>
<version>1.7.35</version>
<version>${dependency.slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@@ -0,0 +1,73 @@
/*-
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.v0;
import org.alfresco.rest.core.v0.BaseAPI;
import org.apache.http.HttpResponse;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.util.List;
/**
* Methods to make API requests using v0 API for Linking Records
*
* @author Kavit Shah
* @since 3.2
*/
@Component
public class LinksAPI extends BaseAPI {
private static final String LINK_API = "{0}doclib/action/rm-link/site/rm/documentLibrary/{1}";
/**
* Creates the Link
*
* @param user The username of the user to use.
* @param password The password of the user.
* @param expectedStatusCode The expected return status code.
* @param sourcePath The Source of link the record. This should be in the format
* "{site}/{container}/{path}", "{site}/{container}", "{store_type}/{store_id}/{id}/{path}",
* "{store_type}/{store_id}/{id}" or "{store_type}/{store_id}".
* @param nodeRefs The Node that needs to be linked.
* @return The HTTP Response.
* @throws AssertionError If the API didn't return the expected status code.
*/
public HttpResponse linkRecord(String user, String password, int expectedStatusCode, String sourcePath, List<String> nodeRefs) throws UnsupportedEncodingException {
JSONObject requestParams = new JSONObject();
requestParams.put("nodeRefs", new JSONArray(nodeRefs));
return doSlingshotPostJsonRequest(user, password, expectedStatusCode, requestParams,
MessageFormat.format(LINK_API, "{0}", sourcePath));
}
}

View File

@@ -26,6 +26,7 @@
*/
package org.alfresco.rest.v0;
import static org.apache.http.HttpStatus.SC_OK;
import static org.testng.Assert.assertTrue;
import java.io.UnsupportedEncodingException;
@@ -36,6 +37,7 @@ import java.util.List;
import org.alfresco.rest.core.v0.BaseAPI;
import org.alfresco.rest.rm.community.model.audit.AuditEntry;
import org.alfresco.rest.rm.community.util.PojoUtility;
import org.apache.http.HttpResponse;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;
@@ -58,6 +60,8 @@ public class RMAuditAPI extends BaseAPI
private static final String RM_AUDIT_API = "{0}rma/admin/rmauditlog";
private static final String RM_AUDIT_LOG_API = RM_AUDIT_API + "?{1}";
private static final String RM_AUDIT_LOG_AS_RECORD = "{0}node/{1}/rmauditlog";
/**
* Returns a list of rm audit entries .
*
@@ -84,6 +88,21 @@ public class RMAuditAPI extends BaseAPI
return PojoUtility.jsonToObject(auditEntries, AuditEntry.class);
}
/**
* Returns a list of rm audit entries .
*
* @param user The username of the user to use.
* @param password The password of the user.
* @param size Maximum number of log entries to return
* @return return All return log entries
*/
public List<AuditEntry> getRMAuditLogAll(String user, String password, final int size) {
String parameters = "size=" + size;
JSONArray auditEntries = doGetRequest(user, password,
MessageFormat.format(RM_AUDIT_LOG_API,"{0}", parameters)).getJSONObject("data").getJSONArray("entries");
return PojoUtility.jsonToObject(auditEntries, AuditEntry.class);
}
/**
* Clear the list of audit entries.
*
@@ -100,5 +119,19 @@ public class RMAuditAPI extends BaseAPI
&& getRMAuditLog(username, password, 100, null).size() == 2);
}
/**
* Logs the Audit Log as Record.
*
* @param username The username of the user to use.
* @param password The password of the user.
* @param recNodeRef The Record Node reference for which Audit log should be created as record
* @param destinationNodeRef The Folder id Node reference where the html file should be placed
* @throws AssertionError If the API call didn't create the Audit Log as Record.
*/
public HttpResponse logsAuditLogAsRecord(String username, String password, String recNodeRef, String destinationNodeRef) {
JSONObject requestParams = new JSONObject();
requestParams.put("destination", destinationNodeRef);
return doPostJsonRequest(username, password, SC_OK, requestParams, RM_AUDIT_LOG_AS_RECORD,recNodeRef);
}
}

View File

@@ -52,7 +52,7 @@ public class RecordCategoriesAPI extends BaseAPI
private static final String RM_ACTIONS_API = "{0}rma/actions/ExecutionQueue";
private static final String DISPOSITION_ACTIONS_API = "{0}node/{1}/dispositionschedule/dispositionactiondefinitions";
private static final String DISPOSITION_SCHEDULE_API = "{0}node/{1}/dispositionschedule";
private static final String NEXT_DISPOSITION_ACTIONS_API = "{0}node/{1}/nextdispositionaction";
/**
* Creates a retention schedule for the category given as parameter
@@ -191,4 +191,19 @@ public class RecordCategoriesAPI extends BaseAPI
retentionProperties.put(RETENTION_SCHEDULE.RETENTION_INSTRUCTIONS, instructions);
return retentionProperties;
}
/**
* Get the Next Disposition Action
*
* @param user
* @param password
* @param recordId
* @return the next disposition schedule action
*/
public JSONObject getNextDispositionAction(String user, String password, String recordId)
{
String nodeRef = NODE_PREFIX + recordId;
JSONObject nextDispositionAction = doGetRequest(user, password, MessageFormat.format(NEXT_DISPOSITION_ACTIONS_API, "{0}", nodeRef));
return nextDispositionAction;
}
}

View File

@@ -74,4 +74,39 @@ public class RecordFoldersAPI extends BaseAPI
return null;
}
public HttpResponse postFolderAction(String user, String password, JSONObject requestParams, String recordFolder) {
String recNodeRef = getNodeRefSpacesStore() + contentService.getNodeRef(user, password, RM_SITE_ID, recordFolder);
try {
requestParams.put("nodeRef", recNodeRef);
return doPostJsonRequest(user, password, SC_OK, requestParams, RM_ACTIONS_API);
}
catch (Exception error) {
LOGGER.error("Unable to extract response parameter", error);
}
return null;
}
public HttpResponse postRecordAction(String user, String password, JSONObject requestParams, String recordId) {
try {
requestParams.put("nodeRef", recordId);
return doPostJsonRequest(user, password, SC_OK, requestParams, RM_ACTIONS_API);
}
catch (JSONException error) {
LOGGER.error("Unable to extract response parameter", error);
}
return null;
}
public HttpResponse reOpenRecordFolder(String user, String password, String recordFolder)
{
String recNodeRef = getNodeRefSpacesStore() + contentService.getNodeRef(user, password, RM_SITE_ID, recordFolder);
JSONObject requestParams = new JSONObject();
requestParams.put("name", "openRecordFolder");
requestParams.put("nodeRef", recNodeRef);
return doPostJsonRequest(user, password, SC_OK, requestParams, RM_ACTIONS_API);
}
}

View File

@@ -360,4 +360,25 @@ public class RecordsAPI extends BaseAPI
{
return getNodeRefSpacesStore() + getItemNodeRef(username, password, recordPath + "/" + recordName);
}
/**
* Reopens the record given as parameter
*
* @param user the user declaring the document as record
* @param password the user's password
* @param recordName the record name
* @return The HTTP Response.
*/
public HttpResponse reOpenRecord(String user, String password, String recordName)
{
String recNodeRef = getNodeRefSpacesStore() + contentService.getNodeRef(user, password, RM_SITE_ID, recordName);
JSONObject requestParams = new JSONObject();
requestParams.put("name", "undeclareRecord");
requestParams.put("nodeRef", recNodeRef);
return doPostJsonRequest(user, password, SC_OK, requestParams, RM_ACTIONS_API);
}
}

View File

@@ -0,0 +1,146 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.audit;
import static java.util.Arrays.asList;
import static org.alfresco.rest.rm.community.base.TestData.*;
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.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.hamcrest.CoreMatchers.is;
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 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.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;
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;
public class AuditHoldsTest extends BaseRMRestTest {
private final String PREFIX = generateTestPrefix(AuditAddToHoldTests.class);
private final String HOLD1 = PREFIX + "hold1";
private SiteModel publicSite;
private FileModel testFile;
@Autowired
private RMAuditService rmAuditService;
@Autowired
private HoldsAPI holdsAPI;
@Autowired
private RoleService roleService;
private UserModel rmAdmin;
private RecordCategory recordCategory;
private RecordCategoryChild recordFolder1,recordFolder2;
private List<AuditEntry> auditEntries;
private String hold1NodeRef;
public static final String RECORD_FOLDER_THREE = "record-folder-three";
@BeforeClass(alwaysRun = true)
public void preconditionForAuditAddToHoldTests()
{
createRMSiteIfNotExists();
rmAdmin = roleService.createUserWithRMRole(UserRoles.ROLE_RM_ADMIN.roleId);
STEP("Create a hold");
hold1NodeRef = holdsAPI.createHoldAndGetNodeRef(rmAdmin.getUsername(), rmAdmin.getPassword(), HOLD1, HOLD_REASON,
HOLD_DESCRIPTION);
STEP("Create a collaboration site with a test file.");
publicSite = dataSite.usingAdmin().createPublicRandomSite();
testFile = dataContent.usingAdmin().usingSite(publicSite).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
STEP("Create a record category with 2 folders and 1 record");
recordCategory = createRootCategory(getRandomName("recordCategory"));
recordFolder1 = createRecordFolder(recordCategory.getId(), PREFIX + "recFolder1");
recordFolder2 = createRecordFolder(recordCategory.getId(), PREFIX + "recFolder2");
Record recordToBeAdded = createElectronicRecord(recordFolder1.getId(), PREFIX + "record");
assertStatusCode(CREATED);
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);
STEP("Delete the record folder that was held");
getRestAPIFactory().getRecordFolderAPI().deleteRecordFolder(recordFolder2.getId());
STEP("Rename the parent of the record that was held");
RecordFolder recordFolder = RecordFolder.builder().name(RECORD_FOLDER_THREE).build();
getRestAPIFactory().getRecordFolderAPI().updateRecordFolder(recordFolder, recordFolder1.getId());
}
/**
* Data provider with hold events that have links to held items
*
* @return the hold events
*/
@DataProvider (name = "holdsEvents")
public Object[][] getHoldEvents()
{
return new AuditEvents[][]
{
{ ADD_TO_HOLD },
{ REMOVE_FROM_HOLD }
};
}
@Test (dataProvider = "holdsEvents")
public void checkItemPathLink(AuditEvents event) {
auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getAdminUser(), event);
assertFalse("Audit results should not be empty",auditEntries.size()==0);
final String auditedEvent = event + " - " + testFile.getName();
assertTrue("Audit results should contain one " + auditedEvent + " event",auditEntries.stream().anyMatch(e -> e.getEvent().startsWith(event.eventDisplayName)));
STEP("Check the audit log contains only an entry for add to hold.");
assertThat(auditEntries, is(not(empty())));
}
@AfterClass(alwaysRun = true)
private void cleanup() {
dataSite.usingAdmin().deleteSite(publicSite);
deleteRecordFolder(recordFolder1.getId());
deleteRecordFolder(recordFolder2.getId());
deleteRecordCategory(recordCategory.getId());
rmAuditService.clearAuditLog();
}
}

View File

@@ -0,0 +1,244 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.audit;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.audit.AuditEntry;
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.v0.RMAuditAPI;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.Utility;
import org.alfresco.utility.model.UserModel;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.AssertJUnit;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.ASPECTS_COMPLETED_RECORD;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createRecordModel;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.fail;
public class ElectronicRecordAuditLogTest extends BaseRMRestTest {
private Optional<UserModel> rmAdmin;
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@Autowired
private RMAuditAPI auditLog;
@Autowired
private RecordsAPI recordApi;
/* electronic record details */
private static final String AUDIT_ELECTRONIC_RECORD = generateTestPrefix(ElectronicRecordAuditLogTest.class) + "electronic record";
private static final String AUDIT_COMPLETE_REOPEN_ELECTRONIC_RECORD = "Complete Reopen Electronic Record";
public static final String TITLE = "Title";
public static final String DESCRIPTION = "Description";
private RecordCategory category1;
private RecordCategoryChild recordFolder1;
private Record electronicRecord, electronicRecord2;
@BeforeClass(alwaysRun = true)
public void electronicRecordsAuditLogSetup()
{
createRMSiteIfNotExists();
rmAdmin = Optional.ofNullable(getDataUser().createRandomTestUser());
rmRolesAndActionsAPI.assignRoleToUser(
getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),
rmAdmin.get().getUsername(),
"Administrator");
auditLog.clearAuditLog(rmAdmin.get().getUsername(),rmAdmin.get().getPassword());
category1 = createRootCategory(TITLE, DESCRIPTION);
recordFolder1 = createFolder(category1.getId(),TITLE);
electronicRecord = createElectronicRecord(recordFolder1.getId(),AUDIT_ELECTRONIC_RECORD,rmAdmin.get());
}
@Test(description = "Audit log for newly filed electronic record")
@AlfrescoTest(jira="RM-4303")
public void newElectronicRecordAudit() {
List<AuditEntry> auditEntries= auditLog.getRMAuditLogAll(getAdminUser().getUsername(),getAdminUser().getPassword(),100);
// newly created record contains 2 events: "file to" and metadata update
// the order in which object creation and metadata update are listed isn't always identical due to
// both happening in the same transaction
assertTrue("File To Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("File to")));
assertTrue("Updated metadata Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Updated Metadata")));
}
@Test
(
dependsOnMethods = "newElectronicRecordAudit",
description = "Viewing electronic record audit log is itself an auditable event"
)
@AlfrescoTest(jira="RM-4303")
public void electronicRecordAuditIsEvent()
{
List<AuditEntry> auditEntries= auditLog.getRMAuditLogAll(getAdminUser().getUsername(),getAdminUser().getPassword(),100);
assertTrue("Audit View Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Audit View")));
}
@Test
(
dependsOnMethods = "electronicRecordAuditIsEvent",
description = "Rename electronic record is an edit metadata event"
)
@AlfrescoTest(jira="RM-4303")
public void renameElectronicRecord() {
auditLog.clearAuditLog(rmAdmin.get().getUsername(),rmAdmin.get().getPassword());
Record renameElectronicRecord = createRecordModel("edited " + electronicRecord.getName(), "", "");
// rename record
getRestAPIFactory().getRecordsAPI().updateRecord(renameElectronicRecord, electronicRecord.getId());
assertStatusCode(OK);
// we expect 1 new event: "metadata update"
List<AuditEntry> auditEntries= auditLog.getRMAuditLogAll(getAdminUser().getUsername(),getAdminUser().getPassword(),100);
assertTrue("Updated metadata Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Updated Metadata")));
}
@Test (
dependsOnMethods = "newElectronicRecordAudit",
description = "Complete and reopen electronic record")
@AlfrescoTest(jira="RM-4303")
public void completeAndReopenElectronicRecord() {
electronicRecord2 = createElectronicRecord(recordFolder1.getId(),AUDIT_COMPLETE_REOPEN_ELECTRONIC_RECORD);
// complete record
recordApi.completeRecord(rmAdmin.get().getUsername(),rmAdmin.get().getPassword(),
electronicRecord2.getName());
try
{
Utility.sleep(1000, 30000, () ->
{
org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
List<String> aspects = recordsAPI.getRecord(electronicRecord2.getId()).getAspectNames();
// a record must be completed
assertTrue("Record is not completed.",aspects.contains(ASPECTS_COMPLETED_RECORD));
});
}
catch (InterruptedException e)
{
fail("InterruptedException received while waiting for results.");
}
List<AuditEntry> auditEntries= auditLog.getRMAuditLogAll(getAdminUser().getUsername(),getAdminUser().getPassword(),100);
assertTrue("Complete Record Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Complete Record")));
// Reopen record
recordApi.reOpenRecord(rmAdmin.get().getUsername(),rmAdmin.get().getPassword(),
electronicRecord2.getName());
try
{
Utility.sleep(1000, 30000, () ->
{
org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
List<String> aspects = recordsAPI.getRecord(electronicRecord2.getId()).getAspectNames();
// a record mustn't be completed
assertFalse(aspects.contains(ASPECTS_COMPLETED_RECORD));
});
}
catch (InterruptedException e)
{
fail("InterruptedException received while waiting for results.");
}
auditEntries= auditLog.getRMAuditLogAll(getAdminUser().getUsername(),getAdminUser().getPassword(),100);
assertTrue("Reopen Record Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Reopen Record")));
}
@Test
(
dependsOnMethods = "completeAndReopenElectronicRecord",
description = "File electronic record's audit log as record"
)
@AlfrescoTest(jira="RM-4303")
public void fileElectronicRecordAuditLogAsRecord()
{
// audit log is stored in the same folder, refresh it so that it appears in the list
HttpResponse auditRecordHttpResponse = auditLog.logsAuditLogAsRecord(rmAdmin.get().getUsername(),rmAdmin.get().getPassword(),
getRecordNodeRef(electronicRecord2.getId()),getFolderNodeRef(recordFolder1.getId()));
JSONObject auditRecordProperties = getAuditPropertyValues(auditRecordHttpResponse);
Record auditRecord = getRestAPIFactory().getRecordsAPI().getRecord(auditRecordProperties.get("record").toString()
.replace("workspace://SpacesStore/",""));
// check audit log
AssertJUnit.assertTrue(auditRecordProperties.get("recordName").toString().endsWith(".html"));
AssertJUnit.assertTrue(auditRecord.getAspectNames().stream().noneMatch(x -> x.startsWith(ASPECTS_COMPLETED_RECORD)));
}
private String getFolderNodeRef(String folderId) {
return "workspace://SpacesStore/" + folderId;
}
private String getRecordNodeRef(String recordId) {
return "workspace/SpacesStore/" + recordId;
}
private JSONObject getAuditPropertyValues(HttpResponse httpResponse) {
HttpEntity entity = httpResponse.getEntity();
String responseString = null;
try {
responseString = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
throw new RuntimeException(e);
}
JSONObject result = new JSONObject(responseString);
return result;
}
@AfterMethod
private void closeAuditLog() {
auditLog.clearAuditLog(rmAdmin.get().getUsername(),rmAdmin.get().getPassword());
}
@AfterClass(alwaysRun = true)
private void electronicRecordAuditLogCleanup() {
deleteRecord(electronicRecord.getId());
deleteRecordFolder(recordFolder1.getId());
deleteRecordCategory(category1.getId());
dataUser.usingAdmin().deleteUser(new UserModel(rmAdmin.get().getUsername(), rmAdmin.get().getPassword()));
}
}

View File

@@ -0,0 +1,246 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.audit;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.audit.AuditEntry;
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.v0.RMAuditAPI;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.Utility;
import org.alfresco.utility.model.UserModel;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.AssertJUnit;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.ASPECTS_COMPLETED_RECORD;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createRecordModel;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.fail;
public class NonElectronicRecordAuditLogTest extends BaseRMRestTest {
private Optional<UserModel> rmAdmin;
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@Autowired
private RMAuditAPI auditLog;
@Autowired
private RecordsAPI recordApi;
private RecordCategory category1;
private RecordCategoryChild recordFolder1;
private Record nonElectronicRecord , nonElectronicRecord2;
private static final String AUDIT_NON_ELECTRONIC_RECORD = generateTestPrefix(NonElectronicRecordAuditLogTest.class) + "non electronic record";
private static final String AUDIT_COMPLETE_REOPEN_NON_ELECTRONIC_RECORD = "Complete Reopen Non-Electronic Record";
public static final String TITLE = "Title";
public static final String DESCRIPTION = "Description";
@BeforeClass(alwaysRun = true)
public void nonElectronicRecordAuditLogSetup()
{
createRMSiteIfNotExists();
rmAdmin = Optional.ofNullable(getDataUser().createRandomTestUser());
rmRolesAndActionsAPI.assignRoleToUser(
getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),
rmAdmin.get().getUsername(),
"Administrator");
auditLog.clearAuditLog(rmAdmin.get().getUsername(),rmAdmin.get().getPassword());
category1 = createRootCategory(TITLE, DESCRIPTION);
recordFolder1 = createFolder(category1.getId(),TITLE);
nonElectronicRecord = createNonElectronicRecord(recordFolder1.getId(),AUDIT_NON_ELECTRONIC_RECORD,rmAdmin.get());
}
@Test(description = "Audit log for newly filed non-electronic record")
@AlfrescoTest(jira="RM-4303")
public void newNonElectronicRecordAudit()
{
List<AuditEntry> auditEntries= auditLog.getRMAuditLogAll(getAdminUser().getUsername(),getAdminUser().getPassword(),100);
// newly created record contains 3 events: "created object", "file to" and metadata update
assertTrue("File To Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("File to")));
assertTrue("Updated metadata Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Updated Metadata")));
assertTrue("Created Object Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Created Object")));
}
@Test
(
dependsOnMethods = "newNonElectronicRecordAudit",
description = "Viewing Non electronic record audit log is itself an auditable event"
)
@AlfrescoTest(jira="RM-4303")
public void nonElectronicRecordAuditIsEvent()
{
List<AuditEntry> auditEntries= auditLog.getRMAuditLogAll(getAdminUser().getUsername(),getAdminUser().getPassword(),100);
assertTrue("Audit View Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Audit View")));
}
@Test
(
dependsOnMethods = "nonElectronicRecordAuditIsEvent",
description = "Rename electronic record is an edit metadata event"
)
@AlfrescoTest(jira="RM-4303")
public void renameNonElectronicRecord()
{
auditLog.clearAuditLog(rmAdmin.get().getUsername(),rmAdmin.get().getPassword());
Record renameNonElectronicRecord = createRecordModel("edited " + nonElectronicRecord.getName(), "", "");
// rename record
getRestAPIFactory().getRecordsAPI().updateRecord(renameNonElectronicRecord, nonElectronicRecord.getId());
assertStatusCode(OK);
// we expect 1 new event: "metadata update"
List<AuditEntry> auditEntries= auditLog.getRMAuditLogAll(getAdminUser().getUsername(),getAdminUser().getPassword(),100);
assertTrue("Updated metadata Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Updated Metadata")));
}
@Test (dependsOnMethods = "newNonElectronicRecordAudit",description = "Complete and reopen electronic record")
@AlfrescoTest(jira="RM-4303")
public void completeAndReopenNonElectronicRecord()
{
nonElectronicRecord2 = createNonElectronicRecord(recordFolder1.getId(),AUDIT_COMPLETE_REOPEN_NON_ELECTRONIC_RECORD);
// complete record
recordApi.completeRecord(rmAdmin.get().getUsername(),rmAdmin.get().getPassword(),
nonElectronicRecord2.getName());
try
{
Utility.sleep(1000, 30000, () ->
{
org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
List<String> aspects = recordsAPI.getRecord(nonElectronicRecord2.getId()).getAspectNames();
// a record must be completed
assertTrue("Record is not completed.",aspects.contains(ASPECTS_COMPLETED_RECORD));
});
}
catch (InterruptedException e)
{
fail("InterruptedException received while waiting for results.");
}
List<AuditEntry> auditEntries= auditLog.getRMAuditLogAll(getAdminUser().getUsername(),getAdminUser().getPassword(),100);
assertTrue("Complete Record Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Complete Record")));
// Reopen record
recordApi.reOpenRecord(rmAdmin.get().getUsername(),rmAdmin.get().getPassword(),
nonElectronicRecord2.getName());
try
{
Utility.sleep(1000, 30000, () ->
{
org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
List<String> aspects = recordsAPI.getRecord(nonElectronicRecord2.getId()).getAspectNames();
// a record mustn't be completed
assertFalse(aspects.contains(ASPECTS_COMPLETED_RECORD));
});
}
catch (InterruptedException e)
{
fail("InterruptedException received while waiting for results.");
}
auditEntries= auditLog.getRMAuditLogAll(getAdminUser().getUsername(),getAdminUser().getPassword(),100);
assertTrue("Reopen Record Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Reopen Record")));
}
@Test
(
dependsOnMethods = "completeAndReopenNonElectronicRecord",
description = "File electronic record's audit log as record"
)
@AlfrescoTest(jira="RM-4303")
public void fileNonElectronicRecordAuditLogAsRecord()
{
// audit log is stored in the same folder, refresh it so that it appears in the list
HttpResponse auditRecordHttpResponse = auditLog.logsAuditLogAsRecord(rmAdmin.get().getUsername(),rmAdmin.get().getPassword(),
getRecordNodeRef(nonElectronicRecord2.getId()),getFolderNodeRef(recordFolder1.getId()));
JSONObject auditRecordProperties = getAuditPropertyValues(auditRecordHttpResponse);
Record auditRecord = getRestAPIFactory().getRecordsAPI().getRecord(auditRecordProperties.get("record").toString()
.replace("workspace://SpacesStore/",""));
// check audit log
AssertJUnit.assertTrue(auditRecordProperties.get("recordName").toString().endsWith(".html"));
AssertJUnit.assertTrue(auditRecord.getAspectNames().stream().noneMatch(x -> x.startsWith(ASPECTS_COMPLETED_RECORD)));
}
private String getFolderNodeRef(String folderId) {
return "workspace://SpacesStore/" + folderId;
}
private String getRecordNodeRef(String recordId) {
return "workspace/SpacesStore/" + recordId;
}
private JSONObject getAuditPropertyValues(HttpResponse httpResponse) {
HttpEntity entity = httpResponse.getEntity();
String responseString = null;
try {
responseString = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
throw new RuntimeException(e);
}
JSONObject result = new JSONObject(responseString);
return result;
}
@AfterMethod
private void closeAuditLog() {
auditLog.clearAuditLog(rmAdmin.get().getUsername(),rmAdmin.get().getPassword());
}
@AfterClass(alwaysRun = true)
private void nonElectronicRecordAuditLogCleanup() {
deleteRecord(nonElectronicRecord.getId());
deleteRecord(nonElectronicRecord2.getId());
deleteRecordFolder(recordFolder1.getId());
deleteRecordCategory(category1.getId());
dataUser.usingAdmin().deleteUser(new UserModel(rmAdmin.get().getUsername(), rmAdmin.get().getPassword()));
}
}

View File

@@ -0,0 +1,120 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.audit;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.audit.AuditEntry;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
import org.alfresco.rest.v0.RMAuditAPI;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.test.AlfrescoTest;
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;
import java.util.List;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.test.util.AssertionErrors.assertTrue;
public class RecordCategoryAuditLogTest extends BaseRMRestTest {
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@Autowired
private RMAuditAPI auditLog;
private final String TEST_PREFIX = generateTestPrefix(RecordCategoryAuditLogTest.class);
private final String RM_ADMIN = TEST_PREFIX + "rm_admin";
private static final String AUDIT_CATEGORY = generateTestPrefix(RecordCategoryAuditLogTest.class) + "category";
private RecordCategory recordCategoryAudit;
@BeforeClass(alwaysRun = true)
public void recordCategoryAuditLogSetup() {
STEP("Create RM Site");
createRMSiteIfNotExists();
STEP("Create RM Admin user");
rmRolesAndActionsAPI.createUserAndAssignToRole(getAdminUser().getUsername(), getAdminUser().getPassword(), RM_ADMIN,
getAdminUser().getPassword(),
"Administrator");
}
@Test
@AlfrescoTest(jira = "RM-2768")
public void recordCategoryAudit() throws Exception {
STEP("Create root level category");
recordCategoryAudit = createRootCategory(AUDIT_CATEGORY);
List<AuditEntry> auditEntries = auditLog.getRMAuditLogAll(getAdminUser().getUsername(), getAdminUser().getPassword(), 100);
// newly created record category contains 3 events: object creation, inherited permissions set to false and metadata update
// the order in which object creation and metadata update are listed isn't always identical due to
// both happening in the same transaction
assertTrue("Created Object Event is not present.", auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Created Object")));
assertTrue("Updated metadata Event is not present.", auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Updated Metadata")));
}
@Test
(
dependsOnMethods = "recordCategoryAudit",
description = "Viewing audit log is itself an auditable event"
)
@AlfrescoTest(jira="RM-4303")
public void recordCategoryAuditIsEvent() {
List<AuditEntry> auditEntries = auditLog.getRMAuditLogAll(getAdminUser().getUsername(), getAdminUser().getPassword(), 100);
assertTrue("Audit View Event is not present.", auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Audit View")));
}
@Test
(
dependsOnMethods = "recordCategoryAuditIsEvent",
description = "Record category rename is an edit metadata event"
)
@AlfrescoTest(jira="RM-4303")
public void renameRecordCategory() {
String categoryName = "Category name " + getRandomAlphanumeric();
RecordCategory rootRecordCategory = createRootCategory(categoryName);
String newCategoryName = "Rename " + categoryName;
RecordCategory recordCategoryUpdated = RecordCategory.builder().name(newCategoryName).build();
RecordCategory renamedRecordCategory = getRestAPIFactory().getRecordCategoryAPI().updateRecordCategory(recordCategoryUpdated, rootRecordCategory.getId());
assertStatusCode(OK);
// we expect 1 new event: "metadata update"
List<AuditEntry> auditEntries = auditLog.getRMAuditLogAll(getAdminUser().getUsername(), getAdminUser().getPassword(), 100);
assertTrue("Updated metadata Event is not present.", auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Updated Metadata")));
}
@AfterClass(alwaysRun = true)
private void electronicRecordAuditLogCleanup() {
deleteRecordCategory(recordCategoryAudit.getId());
dataUser.deleteUser(new UserModel(RM_ADMIN,
getAdminUser().getPassword()));
auditLog.clearAuditLog(getAdminUser().getUsername(), getAdminUser().getPassword());
}
}

View File

@@ -0,0 +1,175 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.audit;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.audit.AuditEntry;
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.v0.RMAuditAPI;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordFoldersAPI;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.Utility;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.List;
import java.util.Optional;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.ASPECTS_COMPLETED_RECORD;
import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createRecordFolderModel;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.fail;
public class RecordFolderAuditLogTest extends BaseRMRestTest {
private Optional<UserModel> rmAdmin;
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@Autowired
private RMAuditAPI auditLog;
@Autowired
private RecordFoldersAPI recordFoldersAPI;
private RecordCategory category1;
private RecordCategoryChild recordFolder1;
public static final String TITLE = "Title";
public static final String DESCRIPTION = "Description";
@BeforeClass(alwaysRun = true)
public void recordFolderAuditLogSetup() {
createRMSiteIfNotExists();
rmAdmin = Optional.ofNullable(getDataUser().createRandomTestUser());
rmRolesAndActionsAPI.assignRoleToUser(
getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),
rmAdmin.get().getUsername(),
"Administrator");
}
@Test(description = "Audit log for empty record folder")
@AlfrescoTest(jira = "RM-4303")
public void recordFolderAudit() {
category1 = createRootCategory(TITLE, DESCRIPTION);
recordFolder1 = createFolder(category1.getId(), TITLE);
List<AuditEntry> auditEntries = auditLog.getRMAuditLogAll(getAdminUser().getUsername(), getAdminUser().getPassword(), 100);
assertTrue("Created Object Event is not present.", auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Created Object")));
assertTrue("Updated metadata Event is not present.", auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Updated Metadata")));
}
@Test
(
dependsOnMethods = "recordFolderAudit",
description = "Viewing record folder audit log is itself an auditable event"
)
@AlfrescoTest(jira = "RM-4303")
public void recordFolderAuditIsEvent() {
List<AuditEntry> auditEntries = auditLog.getRMAuditLogAll(getAdminUser().getUsername(), getAdminUser().getPassword(), 100);
assertTrue("Audit View Event is not present.", auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Audit View")));
}
@Test
(
dependsOnMethods = "recordFolderAuditIsEvent",
description = "Record folder rename is an edit metadata event"
)
@AlfrescoTest(jira = "RM-4303")
public void renameRecordFolder() {
auditLog.clearAuditLog(rmAdmin.get().getUsername(), rmAdmin.get().getPassword());
RecordFolder renameRecordFolder = createRecordFolderModel(category1.getId(), "edited");
getRestAPIFactory().getRecordFolderAPI().updateRecordFolder(renameRecordFolder, recordFolder1.getId());
assertStatusCode(OK);
// we expect 1 new event: "metadata update"
List<AuditEntry> auditEntries = auditLog.getRMAuditLogAll(getAdminUser().getUsername(), getAdminUser().getPassword(), 100);
// assertTrue("Move To Event is not present.",auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Move to")));
assertTrue("Updated metadata Event is not present.", auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Updated Metadata")));
}
@Test(dependsOnMethods = "recordFolderAudit",
description = "Close and reopen folder")
@AlfrescoTest(jira = "RM-4303")
public void closeReopenFolder() {
//close folder
recordFoldersAPI.closeRecordFolder(rmAdmin.get().getUsername(), rmAdmin.get().getPassword(),
recordFolder1.getName());
try
{
Utility.sleep(1000, 30000, () ->
{
List<AuditEntry> auditEntries = auditLog.getRMAuditLogAll(getAdminUser().getUsername(), getAdminUser().getPassword(), 100);
assertTrue("Folder Close Record Event is not present.", auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Close Record Folder")));
});
}
catch (InterruptedException e)
{
fail("InterruptedException received while waiting for results.");
}
//reopen folder
recordFoldersAPI.reOpenRecordFolder(rmAdmin.get().getUsername(), rmAdmin.get().getPassword(),
recordFolder1.getName());
try
{
Utility.sleep(1000, 30000, () ->
{
List<AuditEntry> auditEntries = auditLog.getRMAuditLogAll(getAdminUser().getUsername(), getAdminUser().getPassword(), 100);
assertTrue("Reopen Record Event is not present.", auditEntries.stream().anyMatch(x -> x.getEvent().startsWith("Open Record Folder")));
});
}
catch (InterruptedException e)
{
fail("InterruptedException received while waiting for results.");
}
}
@AfterMethod
private void closeAuditLog()
{
auditLog.clearAuditLog(rmAdmin.get().getUsername(),rmAdmin.get().getPassword());
}
@AfterClass (alwaysRun = true)
public void recordFolderAuditLogCleanup()
{
deleteRecordFolder(recordFolder1.getId());
deleteRecordCategory(category1.getId());
dataUser.usingAdmin().deleteUser(new UserModel(rmAdmin.get().getUsername(), rmAdmin.get().getPassword()));
}
}

View File

@@ -53,9 +53,12 @@ import static org.springframework.http.HttpStatus.OK;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Date;
import java.util.TimeZone;
import java.util.stream.Collectors;
import lombok.Getter;
@@ -91,6 +94,7 @@ import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.UserModel;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
@@ -121,6 +125,8 @@ public class BaseRMRestTest extends RestTest
@Getter(value = PROTECTED)
private SearchAPI searchApi;
protected static final String iso8601_DateFormat="yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
/**
* Asserts the given status code
*
@@ -161,7 +167,7 @@ public class BaseRMRestTest extends RestTest
*/
@Override
@BeforeClass (alwaysRun = true)
public void checkServerHealth() throws Exception
public void checkServerHealth()
{
// Create RM Site if not exist
createRMSiteIfNotExists();
@@ -628,8 +634,8 @@ public class BaseRMRestTest extends RestTest
* Returns search results for the given search term
*
* @param user
* @param term
* @param query language
* @param q
* @param queryLanguage language
* @return
* @throws Exception
*/
@@ -956,5 +962,34 @@ public class BaseRMRestTest extends RestTest
return false;
}
}
/**
* Helper method to get the Previous Date in the YYYY-MM-ddTHH:mm:ss.SSSXXX format
* @param previousDays number of previous days while calculating the date as output
* @return previousDate as String in the ISO 8601 Date Format
*/
protected String getIso8601Date(int previousDays) {
Date date = new Date(System.currentTimeMillis());
Date previousDate = new Date(date.getTime() - previousDays);
// Conversion
SimpleDateFormat sdf= new SimpleDateFormat(iso8601_DateFormat);;
sdf.setTimeZone(TimeZone.getDefault());
return sdf.format(previousDate);
}
/**
* Helper method to provide the Edited Disposition Date Json
* The Edited Disposition Date is modified to previous date so that CUTOFF & DESTROY Steps will be enabled
* @return JsonObject with the format {"name":"editDispositionActionAsOfDate","params":{"asOfDate":{"iso8601":"Previous Date"}}}
*/
protected JSONObject editDispositionDateJson() {
JSONObject requestParams = new JSONObject();
requestParams.put("name","editDispositionActionAsOfDate");
JSONObject params = new JSONObject();
requestParams.put("params",params);
JSONObject asOfDate = new JSONObject();
params.put("asOfDate",asOfDate);
asOfDate.put("iso8601",getIso8601Date(1));
return requestParams;
}
}

View File

@@ -72,6 +72,7 @@ import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Ignore;
import org.testng.annotations.Test;
import org.alfresco.utility.model.TestGroup;
@@ -259,6 +260,7 @@ public class DeclareAndFileDocumentAsRecordTests extends BaseRMRestTest
* And the document is not declared as a record
*/
@Test (dataProvider = "invalidDestinationPaths",groups = { TestGroup.NOT_SUPPORTED_ON_SINGLE_PIPELINE })
@Ignore
public void declareAndFileToInvalidLocationUsingActionsAPI(String containerPath, String expectedException) throws Exception
{
STEP("Declare document as record with an invalid location parameter value");

View File

@@ -61,6 +61,7 @@ import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Ignore;
import org.testng.annotations.Test;
import org.alfresco.utility.model.TestGroup;
@@ -209,6 +210,7 @@ public class FileVersionAsRecordTests extends BaseRMRestTest
* And the document is not declared as a version record
*/
@Test (dataProvider = "invalidDestinationPaths", groups = { TestGroup.NOT_SUPPORTED_ON_SINGLE_PIPELINE })
@Ignore
public void declareVersionAndFileToInvalidLocationUsingActionsAPI(String containerPath, String expectedException) throws Exception
{
STEP("Declare document as record version with an invalid location parameter value");

View File

@@ -0,0 +1,311 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.records;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
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.rules.ActionsOnRule;
import org.alfresco.rest.rm.community.model.rules.RuleDefinition;
import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainer;
import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildCollection;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.rest.v0.RulesAPI;
import org.alfresco.rest.v0.service.DispositionScheduleService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static java.util.Arrays.asList;
import static org.alfresco.rest.core.v0.BaseAPI.NODE_PREFIX;
import static org.alfresco.rest.core.v0.BaseAPI.RM_SITE_ID;
import static org.alfresco.rest.rm.community.base.TestData.DEFAULT_PASSWORD;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS;
import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.CREATED_DATE;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_READ_RECORDS;
import static org.alfresco.rest.rm.community.requests.gscore.api.FilesAPI.PARENT_ID_PARAM;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.junit.Assert.assertTrue;
import static org.springframework.http.HttpStatus.CREATED;
public class DeclareInPlaceRecordsTestLevel2 extends BaseRMRestTest {
private final String TEST_PREFIX = generateTestPrefix(DeclareInPlaceRecordsTestLevel2.class);
private final String RM_ADMIN = TEST_PREFIX + "rm_admin";
private final String RECORDS_CATEGORY_ONE = TEST_PREFIX + "category";
public static final String RECORD_FOLDER_ONE = "record-folder-one";
public static final String RECORD_CATEGORY_TWO = "record-category-two" + System.currentTimeMillis();
public static final String RECORD_FOLDER_TWO = "record-folder-two";
private final String RULE_NAME = TEST_PREFIX + "rule unfiled";
private String unfiledRecordsNodeRef;
private RecordCategory RecordCategoryOne, RecordCategoryTwo;
private RecordCategoryChild recordFolder;
private UnfiledContainer unfiledContainer;
private FolderModel testFolder;
private SiteModel testSite;
private SiteModel privateSite;
private UserModel testUser;
@Autowired
private DispositionScheduleService dispositionScheduleService;
@Autowired
private RulesAPI rulesAPI;
/**
* data prep services
*/
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@Autowired
private RecordsAPI recordsAPI;
@BeforeClass(alwaysRun = true)
public void preConditions() {
STEP("Create RM Site");
createRMSiteIfNotExists();
privateSite = dataSite.usingAdmin().createPrivateRandomSite();
}
/**
* Given that a user is the owner of a document
* And that user has been deleted
* When admin tries to declare the document as a record
* Then the document becomes an inplace record
*/
@Test
@AlfrescoTest(jira="RM-2584")
public void DeclareRecordOwnerDeleted() throws Exception {
createTestPrecondition();
// Upload document in a folder in a collaboration site
FileModel uploadedDoc = dataContent.usingSite(testSite)
.usingUser(testUser)
.usingResource(testFolder)
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
// delete the test user
dataUser.deleteUser(testUser);
// declare uploadedDocument as record
getRestAPIFactory().getFilesAPI(getDataUser().getAdminUser()).declareAsRecord(uploadedDoc.getNodeRefWithoutVersion());
assertStatusCode(CREATED);
// assert that the document is now a record
assertTrue(hasRecordAspect(uploadedDoc));
}
/**
* Given that a user is the owner of a document
* And that user declare the document as a record
* When admin files the record to a category that has a disposition schedule applied on records and a cut off step
* And admin completes the record so the pending record action is now Cut off
* Then user is still able to see the in place record in original share site location
*/
@Test
@AlfrescoTest(jira="MNT-18558")
public void inPlaceRecordVisibilityAfterFilingToCategoryWithCutOffStep() throws Exception {
// create test precondition
createTestPrecondition(RECORDS_CATEGORY_ONE);
//create a disposition schedule on Records level with a cut off step
dispositionScheduleService.createCategoryRetentionSchedule(RECORDS_CATEGORY_ONE, true);
dispositionScheduleService.addCutOffAfterPeriodStep(RECORDS_CATEGORY_ONE, "day|2", CREATED_DATE);
//create a folder in category
recordFolder = createFolder(getAdminUser(),RecordCategoryOne.getId(),RECORD_FOLDER_ONE);
// create a File to record folder rule applied on Unfiled Records container
fileToRuleAppliedOnUnfiledRecords();
//create a new test user
UserModel testUser = createSiteManager();
// upload a new document as the user and declare the document as record
FileModel uploadedDoc = dataContent.usingSite(privateSite)
.usingUser(testUser)
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
Record uploadedRecord = getRestAPIFactory().getFilesAPI(getDataUser().getAdminUser()).declareAsRecord(uploadedDoc.getNodeRefWithoutVersion());
assertStatusCode(CREATED);
//Complete the record as admin to be sure that pending action is now Cut off
recordsAPI.completeRecord(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), uploadedRecord.getName());
// As test user navigate to collaboration site documents library and check that the record is still visible
dataContent.usingAdmin().usingSite(privateSite).assertContentExist();
}
/**
* Create a user called test
* Create a collaboration site
* Add the user to the collaboration site as consumer
* Create an RM site
* In RM, create a new categories under file plan with a cut off step set after an event happens
* Under the previously created category create a folder
* Set READ-ONLY permission for test user for the folder previously created (the user does not have Read
* permissions to the category containing the folder)
* In the collaboration site create two files
* File as record the first file
* Log in with test user and check if he can still see the two files in the collaboration site
*/
@Test
@AlfrescoTest (jira = "MNT-22138")
public void filesVisibilityAfterFilingToCategoryWithCutOffAfterEventStep() throws Exception {
//create a category
RecordCategoryTwo = createRootCategory(RECORD_CATEGORY_TWO);
//create a disposition schedule on Records level with a cut off step
dispositionScheduleService.createCategoryRetentionSchedule(RECORD_CATEGORY_TWO, true);
dispositionScheduleService.addCutOffAfterPeriodStep(RECORD_CATEGORY_TWO, "day|2", CREATED_DATE);
//create a folder in category
recordFolder = createFolder(getAdminUser(),RecordCategoryTwo.getId(),RECORD_FOLDER_TWO);
//create a new test user
UserModel siteConsumer = getDataUser().createRandomTestUser();
getDataUser().addUserToSite(siteConsumer,privateSite,UserRole.SiteConsumer);
// give read permissions to test user
getRestAPIFactory().getRMUserAPI().addUserPermission(recordFolder.getId(), siteConsumer, PERMISSION_READ_RECORDS);
// create two documents
FileModel testFile = dataContent.usingSite(new SiteModel(privateSite.getTitle()))
.usingAdmin()
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
FileModel testFileNotFiled = dataContent.usingSite(new SiteModel(privateSite.getTitle()))
.usingAdmin()
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
// file one of the documents as record
getRestAPIFactory().getFilesAPI()
.usingParams(String.format("%s=%s", PARENT_ID_PARAM, recordFolder.getId()))
.declareAsRecord(testFile.getNodeRefWithoutVersion());
getRestAPIFactory().getRmRestWrapper().assertStatusCodeIs(CREATED);
// As test user navigate to collaboration site documents library and check that both of the documents are
// visible
STEP("Verify the document in collaboration site is now a record");
Assert.assertTrue(hasRecordAspect(testFile), "File should have record aspect");
Assert.assertFalse(hasRecordAspect(testFileNotFiled), "File should not have record aspect");
}
private void createTestPrecondition(String categoryName) {
// create "rm admin" user if it does not exist and assign it to RM Administrator role
rmRolesAndActionsAPI.createUserAndAssignToRole(
getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),
RM_ADMIN, DEFAULT_PASSWORD, "Administrator");
// create category
STEP("Create category");
RecordCategoryOne = createRootCategory(categoryName,"Title");
unfiledContainer = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(UNFILED_RECORDS_CONTAINER_ALIAS);
unfiledRecordsNodeRef = NODE_PREFIX + unfiledContainer.getId();
}
private void createTestPrecondition() {
STEP("Create collab_user user");
testUser = getDataUser().createRandomTestUser();
testSite = dataSite.usingAdmin().createPublicRandomSite();
// invite collab_user to Collaboration site with Contributor role
getDataUser().addUserToSite(testUser, testSite, UserRole.SiteContributor);
testFolder = dataContent.usingSite(testSite).usingUser(testUser).createFolder();
}
private void fileToRuleAppliedOnUnfiledRecords() {
unfiledRecordsRuleTeardown();
// create a rule
RuleDefinition ruleDefinition = RuleDefinition.createNewRule().title(RULE_NAME)
.description(RULE_NAME)
.createRecordPath(false)
.path("/" + RECORDS_CATEGORY_ONE + "/" + RECORD_FOLDER_ONE)
.runInBackground(true)
.actions(asList(ActionsOnRule.FILE_TO.getActionValue()));
// create a rule on unfiledRecords
rulesAPI.createRule(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), unfiledRecordsNodeRef, ruleDefinition);
}
private void unfiledRecordsRuleTeardown() {
rulesAPI.deleteAllRulesOnContainer(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), unfiledRecordsNodeRef);
}
public UserModel createSiteManager() {
UserModel siteManager = getDataUser().createRandomTestUser();
getDataUser().addUserToSite(siteManager, privateSite, UserRole.SiteManager);
return siteManager;
}
@AfterClass
public void cleanupCategory() {
unfiledRecordsRuleTeardown();
rmRolesAndActionsAPI.deleteAllItemsInContainer(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), RM_SITE_ID, RECORD_FOLDER_ONE);
rmRolesAndActionsAPI.deleteAllItemsInContainer(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), RM_SITE_ID, RecordCategoryOne.getName());
deleteRecordCategory(RecordCategoryOne.getId());
rmRolesAndActionsAPI.deleteAllItemsInContainer(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), RM_SITE_ID, RECORD_FOLDER_TWO);
rmRolesAndActionsAPI.deleteAllItemsInContainer(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), RM_SITE_ID, RecordCategoryTwo.getName());
deleteRecordCategory(RecordCategoryTwo.getId());
dataSite.usingAdmin().deleteSite(privateSite);
dataSite.usingAdmin().deleteSite(testSite);
UnfiledContainerChildCollection unfiledContainerChildCollection = getRestAPIFactory()
.getUnfiledContainersAPI().getUnfiledContainerChildren(unfiledContainer.getId());
unfiledContainerChildCollection.getEntries().forEach(unfiledChild ->
{
if (unfiledChild.getEntry().getIsRecord())
{
getRestAPIFactory().getRecordsAPI().deleteRecord(unfiledChild.getEntry().getId());
}
else
{
getRestAPIFactory().getUnfiledRecordFoldersAPI().deleteUnfiledRecordFolder(unfiledChild.getEntry().getId());
}
});
}
}

View File

@@ -75,6 +75,7 @@ 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.Ignore;
import org.testng.annotations.Test;
/**
@@ -102,7 +103,7 @@ public class DeleteRecordTests extends BaseRMRestTest
testSite = dataSite.usingAdmin().createPublicRandomSite();
recordFolder = createCategoryFolderInFilePlan();
unfiledRecordFolder = createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, getRandomName("Unfiled Folder "),
UNFILED_RECORD_FOLDER_TYPE);
UNFILED_RECORD_FOLDER_TYPE);
}
/** Data provider with electronic and non-electronic records to be deleted */
@@ -133,10 +134,10 @@ public class DeleteRecordTests extends BaseRMRestTest
* </pre>
*/
@Test
(
dataProvider = "recordsToBeDeleted",
description = "Admin user can delete records"
)
(
dataProvider = "recordsToBeDeleted",
description = "Admin user can delete records"
)
@AlfrescoTest(jira="RM-4363")
public void adminCanDeleteRecords(String recordId)
{
@@ -154,17 +155,17 @@ public class DeleteRecordTests extends BaseRMRestTest
* </pre>
*/
@Test
(
description = "User without write permissions can't delete a record"
)
(
description = "User without write permissions can't delete a record"
)
@AlfrescoTest(jira="RM-4363")
public void userWithoutWritePermissionsCantDeleteRecord()
{
// Create a non-electronic record in unfiled records
UnfiledContainerChild nonElectronicRecord = UnfiledContainerChild.builder()
.name("Record " + RandomData.getRandomAlphanumeric())
.nodeType(NON_ELECTRONIC_RECORD_TYPE)
.build();
.name("Record " + RandomData.getRandomAlphanumeric())
.nodeType(NON_ELECTRONIC_RECORD_TYPE)
.build();
UnfiledContainerChild newRecord = getRestAPIFactory().getUnfiledContainersAPI().createUnfiledContainerChild(nonElectronicRecord, UNFILED_RECORDS_CONTAINER_ALIAS);
assertStatusCode(CREATED);
@@ -187,9 +188,9 @@ public class DeleteRecordTests extends BaseRMRestTest
* </pre>
*/
@Test
(
description = "User without delete records capability can't delete a record"
)
(
description = "User without delete records capability can't delete a record"
)
@AlfrescoTest(jira="RM-4363")
public void userWithoutDeleteRecordsCapabilityCantDeleteRecord()
{
@@ -234,7 +235,7 @@ public class DeleteRecordTests extends BaseRMRestTest
STEP("Create a record in first folder and copy it into second folder.");
String recordId = getRestAPIFactory().getRecordFolderAPI()
.createRecord(createElectronicRecordModel(), recordFolder.getId(), getFile(IMAGE_FILE)).getId();
.createRecord(createElectronicRecordModel(), recordFolder.getId(), getFile(IMAGE_FILE)).getId();
String copyId = copyNode(recordId, recordFolderB.getId()).getId();
assertStatusCode(CREATED);
@@ -318,14 +319,14 @@ public class DeleteRecordTests extends BaseRMRestTest
RecordCategoryChild recFolder = createFolder(recordCategory.getId(), getRandomName("recFolder"));
RecordBodyFile recordBodyFile = RecordBodyFile.builder().targetParentId(recFolder.getId()).build();
Record recordFiled = getRestAPIFactory().getRecordsAPI().fileRecord(recordBodyFile, testFile.getNodeRefWithoutVersion());
getRestAPIFactory().getRecordsAPI().completeRecord(recordFiled.getId());
assertStatusCode(CREATED);
completeRecord(recordFiled.getId());
assertStatusCode(OK);
STEP("Execute the disposition schedule steps.");
rmRolesAndActionsAPI.executeAction(getAdminUser().getUsername(), getAdminUser().getUsername(), recordFiled.getName(),
RM_ACTIONS.CUT_OFF);
RM_ACTIONS.CUT_OFF);
rmRolesAndActionsAPI.executeAction(getAdminUser().getUsername(), getAdminUser().getUsername(), recordFiled.getName(),
RM_ACTIONS.DESTROY);
RM_ACTIONS.DESTROY);
STEP("Check that it's possible to load the copy content.");
getNodeContent(copy.getId());
@@ -348,14 +349,14 @@ public class DeleteRecordTests extends BaseRMRestTest
STEP("Declare file version as record.");
recordsAPI.declareDocumentVersionAsRecord(getAdminUser().getUsername(), getAdminUser().getPassword(), testSite.getId(),
testFile.getName());
testFile.getName());
UnfiledContainerChild unfiledContainerChild = getRestAPIFactory().getUnfiledContainersAPI()
.getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS)
.getEntries().stream()
.filter(child -> child.getEntry().getName()
.startsWith(testFile.getName().substring(0, testFile.getName().indexOf("."))))
.findFirst()
.get().getEntry();
.getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS)
.getEntries().stream()
.filter(child -> child.getEntry().getName()
.startsWith(testFile.getName().substring(0, testFile.getName().indexOf("."))))
.findFirst()
.get().getEntry();
STEP("Delete the record.");
deleteAndVerify(unfiledContainerChild.getId());

View File

@@ -0,0 +1,128 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.records;
import org.alfresco.dataprep.ContentService;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.test.AlfrescoTest;
import org.apache.commons.httpclient.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;
import static org.alfresco.rest.core.v0.BaseAPI.RM_SITE_ID;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
/**
* Tests to cover share action for records
* @author Kavit Shah
*/
public class ShareRecordsTest extends BaseRMRestTest {
/** data prep services*/
@Autowired
private RecordsAPI service;
@Autowired
private ContentService contentService;
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
/** Constants*/
private final String TEST_PREFIX = generateTestPrefix(ShareRecordsTest.class);
private final String CATEGORY = "CategoryWithSharedRecords" + TEST_PREFIX;
private final String FOLDER = "FolderWithSharedRecords" + TEST_PREFIX;
private final String ELECTRONIC_RECORD = "ELECTRONIC_RECORD" + TEST_PREFIX;
private final String NONELECTRONIC_REC = "NON_ELECTRONIC_RECORD" + TEST_PREFIX;
private RecordCategory category;
private RecordCategoryChild recordCategoryChild;
/**
* Given a record
* When admin tries to share it via API
* Then the record can't be shared
*/
@Test
@AlfrescoTest(jira = "RM-5308")
public void shareRecordViaApi()
{
//create RM Site
createRMSiteIfNotExists();
//create a category
category = createRootCategory(CATEGORY);
//create folder
recordCategoryChild = createFolder(category.getId(),FOLDER);
createNonElectronicRecord(recordCategoryChild.getId(),NONELECTRONIC_REC);
// create record to be shared
createElectronicRecord(recordCategoryChild.getId(),ELECTRONIC_RECORD);
//get the node id for the ELECTRONIC_RECORD created
String nodeRefRec1= contentService.getNodeRefByPath(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),
"/Sites/" + RM_SITE_ID + "/documentLibrary/" + CATEGORY + "/" + FOLDER + "/" + service.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), FOLDER, ELECTRONIC_RECORD));
//check record can't be shared
assertFalse("The record has been succesfully shared",
service.shareDocument(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),nodeRefRec1 ).getKey());
//check the error code when trying to share a record
assertEquals("The API response code is not " + HttpStatus.SC_INTERNAL_SERVER_ERROR, service.shareDocument(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nodeRefRec1).getValue(),
String.valueOf( HttpStatus.SC_INTERNAL_SERVER_ERROR));
//get the node id for NONELECTRONIC_REC created
String nodeRefRec2 = contentService.getNodeRefByPath(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),
"/Sites/" + RM_SITE_ID + "/documentLibrary/" + CATEGORY + "/" + FOLDER + "/" + service.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), FOLDER, NONELECTRONIC_REC));
//check record can't be shared
assertFalse("The record has been succesfully shared",
service.shareDocument(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nodeRefRec2).getKey());
//check the error code when trying to share a record
assertEquals("The API response code is not " + HttpStatus.SC_INTERNAL_SERVER_ERROR, service.shareDocument(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nodeRefRec2).getValue(),
String.valueOf(HttpStatus.SC_INTERNAL_SERVER_ERROR));
}
@AfterClass
public void cleanupCategory() {
rmRolesAndActionsAPI.deleteAllItemsInContainer(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), RM_SITE_ID, recordCategoryChild.getName());
rmRolesAndActionsAPI.deleteAllItemsInContainer(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), RM_SITE_ID, category.getName());
deleteRecordCategory(category.getId());
}
}

View File

@@ -0,0 +1,211 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.rest.core.v0.BaseAPI;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.audit.AuditEntry;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.rest.v0.service.RMAuditService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import static org.alfresco.rest.core.v0.BaseAPI.NODE_PREFIX;
import static org.alfresco.rest.core.v0.BaseAPI.RM_SITE_ID;
import static org.alfresco.rest.rm.community.model.audit.AuditEvents.DELETE_PERSON;
import static org.alfresco.rest.rm.community.model.audit.AuditEvents.LOGIN_SUCCESSFUL;
import static org.alfresco.rest.rm.community.records.SearchRecordsTests.*;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.junit.Assert.assertFalse;
import static org.testng.AssertJUnit.assertTrue;
/**
* Audit Access tests
* @author Kavit Shah
*/
public class AuditAccessTests extends BaseRMRestTest {
private Optional<UserModel> deletedUser;
private final String TEST_PREFIX = generateTestPrefix(AuditAccessTests.class);
private static final String DELETE_USER_EVENT = "Delete User";
private final String record1 = TEST_PREFIX + "RM-2967 uploaded record";
private final String classifiedRecord = TEST_PREFIX + "RM-2967 classified record";
private final String folderName = TEST_PREFIX + "RM-2967 folder";
private final String categoryName = TEST_PREFIX + "RM-2967 category";
private final String editedCategoryName = "edited " + categoryName;
private final String editedFolderName = "edited " + folderName;
private final String editedRecordName = "edited " + record1;
private final String login_successfull = "Login Successful";
private RecordCategory categoryAll;
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@Autowired
private RecordsAPI recordsAPI;
@Autowired
private RMAuditService rmAuditService;
@Test(priority = 1)
@AlfrescoTest(jira = "RM-2967")
public void deleteRMUsersShowFullAuditTest() {
createTestPrecondition();
updateCategoryMetadata();
updateFolderMetadata();
updateRecordMetadata();
// delete record category and folder with rm_admin_deleted
rmRolesAndActionsAPI.deleteAllItemsInContainer(deletedUser.get().getUsername(), deletedUser.get().getPassword(),
RM_SITE_ID, editedFolderName);
rmRolesAndActionsAPI.deleteAllItemsInContainer(deletedUser.get().getUsername(), deletedUser.get().getPassword(),
RM_SITE_ID, editedCategoryName);
// delete the user
Optional.of(deletedUser).ifPresent(x -> getDataUser().deleteUser(x.get()));
//check for RM-5235 fix
List<AuditEntry> auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getDataUser().usingAdmin().getAdminUser(),
DELETE_PERSON);
assertTrue("Delete user event not found in the audit log.", auditEntries.stream().anyMatch(
auditEntry -> auditEntry.getEvent().equals(DELETE_USER_EVENT)));
}
@Test(priority = 2)
public void filterEventsByLoginSuccessful()
{
createRMSiteIfNotExists();
List<AuditEntry> auditEntries = rmAuditService.getAuditEntriesFilteredByEvent(getDataUser().usingAdmin().getAdminUser(),
LOGIN_SUCCESSFUL);
assertFalse("Audit results should contain at least one Login Successful event",
auditEntries.isEmpty());
assertTrue("Audit results contain only Login Successful events",
auditEntries.stream()
.allMatch(e -> e.getEvent().startsWith(LOGIN_SUCCESSFUL.toString()) || e.getEvent().startsWith(login_successfull)));
}
/**
* Creates the required precondition for the test
* <p/>
* See Precondition in current class JavaDoc
*/
private void createTestPrecondition() {
createRMSiteIfNotExists();
// create "rm deleted user" user if it does not exist and assign it to RM Administrator role
createDeletedUser();
// create category and folder
categoryAll = createCategoryIfDoesNotExist(categoryName,deletedUser.get());
createRecordFolderInCategory(folderName,categoryAll,deletedUser.get());
// upload an electronic record
recordsAPI.uploadElectronicRecord(deletedUser.get().getUsername(), deletedUser.get().getPassword(), getDefaultElectronicRecordProperties(record1), folderName, CMISUtil.DocumentType.TEXT_PLAIN);
// upload another electronic record and classify it
recordsAPI.uploadElectronicRecord(deletedUser.get().getUsername(), deletedUser.get().getPassword(), getDefaultElectronicRecordProperties(classifiedRecord), folderName, CMISUtil.DocumentType.TEXT_PLAIN);
}
private void createDeletedUser() {
// create Deleted User
deletedUser = Optional.ofNullable(getDataUser().createRandomTestUser());
rmRolesAndActionsAPI.assignRoleToUser(
getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),
deletedUser.get().getUsername(),
ADMIN
);
}
private void updateCategoryMetadata() {
HashMap<BaseAPI.RMProperty, String> categoryProperties = new HashMap<>();
categoryProperties.put(BaseAPI.RMProperty.NAME, editedCategoryName);
categoryProperties.put(BaseAPI.RMProperty.TITLE, "edited " + TITLE);
categoryProperties.put(BaseAPI.RMProperty.DESCRIPTION, "edited " + DESCRIPTION);
// edit some category's properties
String categoryNodeRef = NODE_PREFIX + rmRolesAndActionsAPI.getItemNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), "/" + categoryName);
rmRolesAndActionsAPI.updateMetadata(deletedUser.get().getUsername(), deletedUser.get().getPassword(), categoryNodeRef, categoryProperties);
}
private void updateFolderMetadata() {
HashMap<BaseAPI.RMProperty, String> folderProperties = new HashMap<>();
folderProperties.put(BaseAPI.RMProperty.NAME, editedFolderName);
folderProperties.put(BaseAPI.RMProperty.TITLE, "edited " + TITLE);
folderProperties.put(BaseAPI.RMProperty.DESCRIPTION, "edited " + DESCRIPTION);
// edit some folder's properties
String folderNodeRef = NODE_PREFIX + rmRolesAndActionsAPI.getItemNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), "/" + editedCategoryName + "/" + folderName);
rmRolesAndActionsAPI.updateMetadata(deletedUser.get().getUsername(), deletedUser.get().getPassword(), folderNodeRef, folderProperties);
}
private void updateRecordMetadata() {
HashMap<BaseAPI.RMProperty, String> recordProperties = new HashMap<>();
recordProperties.put(BaseAPI.RMProperty.NAME, editedRecordName);
recordProperties.put(BaseAPI.RMProperty.TITLE, "edited " + TITLE);
recordProperties.put(BaseAPI.RMProperty.AUTHOR, "edited author");
recordProperties.put(BaseAPI.RMProperty.DESCRIPTION, "edited " + DESCRIPTION);
// edit some record's properties
String recordName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), editedFolderName, record1);
String recordNodeRef = NODE_PREFIX + rmRolesAndActionsAPI.getItemNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), "/" + editedCategoryName + "/" + editedFolderName + "/" + recordName);
rmRolesAndActionsAPI.updateMetadata(deletedUser.get().getUsername(), deletedUser.get().getPassword(), recordNodeRef, recordProperties);
}
private RecordCategory createCategoryIfDoesNotExist(String CATEGORY_ALL, UserModel deletedUser) {
return createRootCategory(deletedUser, CATEGORY_ALL);
}
private RecordCategoryChild createRecordFolderInCategory(String FOLDER_SEARCH, RecordCategory recordCategory, UserModel deletedUser) {
return createFolder(deletedUser, recordCategory.getId(), FOLDER_SEARCH);
}
private Map<BaseAPI.RMProperty, String> getDefaultElectronicRecordProperties(String recordName) {
Map<BaseAPI.RMProperty, String> defaultProperties = new HashMap<>();
defaultProperties.put(BaseAPI.RMProperty.NAME, recordName);
defaultProperties.put(BaseAPI.RMProperty.TITLE, TITLE);
defaultProperties.put(BaseAPI.RMProperty.DESCRIPTION, DESCRIPTION);
defaultProperties.put(BaseAPI.RMProperty.CONTENT, TEST_CONTENT);
return defaultProperties;
}
}

View File

@@ -0,0 +1,104 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
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.rules.ActionsOnRule;
import org.alfresco.rest.rm.community.model.rules.RuleDefinition;
import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RulesAPI;
import org.alfresco.test.AlfrescoTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.Test;
import java.util.Collections;
import static org.alfresco.rest.core.v0.BaseAPI.NODE_PREFIX;
import static org.alfresco.rest.rm.community.base.TestData.ELECTRONIC_RECORD_NAME;
import static org.alfresco.rest.rm.community.base.TestData.NONELECTRONIC_RECORD_NAME;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.*;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.*;
public class BasicRulesIntegrationTests extends BaseRMRestTest {
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
private final static String title = "Rule to complete";
private final static String description = "Rule to describe";
private final String TEST_PREFIX = generateTestPrefix(CreateCategoriesTests.class);
private final String RM_ADMIN = TEST_PREFIX + "rm_admin";
@Autowired
private RulesAPI rulesAPI;
@Test
@AlfrescoTest(jira = "RM-2794")
public void basicRulesIntegration() {
STEP("Create the RM site if doesn't exist");
createRMSiteIfNotExists();
STEP("Create RM Admin user");
rmRolesAndActionsAPI.createUserAndAssignToRole(getAdminUser().getUsername(), getAdminUser().getPassword(), RM_ADMIN,
getAdminUser().getPassword(),
"Administrator");
STEP("Create record categories and record folders");
RecordCategory Category = createRootCategory(getRandomName("recordCategory"));
String recordFolder1 = createRecordFolder(Category.getId(), getRandomName("recFolder")).getId();
//create a rule for completing a record
RuleDefinition ruleDefinition = RuleDefinition.createNewRule().title("name").description("description1")
.applyToChildren(true).title(title)
.actions(Collections.singletonList(ActionsOnRule.COMPLETE_RECORD.getActionValue()));
rulesAPI.createRule(getAdminUser().getUsername(), getAdminUser().getPassword(), NODE_PREFIX + Category.getId(), ruleDefinition);
RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI();
//create two electronic record in record folder
String electronicRecordId1 = createElectronicRecord(recordFolder1, ELECTRONIC_RECORD_NAME).getId();
String electronicRecordId2 = createElectronicRecord(recordFolder1, ELECTRONIC_RECORD_NAME).getId();
assertStatusCode(CREATED);
// Update the rules for record Category
rulesAPI.updateRule(getAdminUser().getUsername(), getAdminUser().getPassword(),
NODE_PREFIX + Category.getId(), ruleDefinition.description("description").id(description));
//Delete the root category and rules
deleteRecordCategory(Category.getId());
rulesAPI.deleteAllRulesOnContainer(getAdminUser().getUsername(), getAdminUser().getPassword(), NODE_PREFIX + Category.getId());
}
}

View File

@@ -0,0 +1,125 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.fileplan.FilePlan;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.test.AlfrescoTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
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.CoreUtil.createBodyForMoveCopy;
import static org.alfresco.rest.rm.community.utils.CoreUtil.toContentModel;
import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.junit.Assert.assertFalse;
import static org.springframework.http.HttpStatus.OK;
import static org.testng.Assert.assertEquals;
public class CreateCategoriesTests extends BaseRMRestTest {
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
private RecordCategory rootCategory;
private final String TEST_PREFIX = generateTestPrefix(CreateCategoriesTests.class);
private final String RM_ADMIN = TEST_PREFIX + "rm_admin";
private RecordCategory Category1;
private RecordCategory Category2;
private RecordCategory SubCategory1;
private RecordCategory SubCategory2;
@BeforeClass(alwaysRun = true)
public void preconditionForCreateCategoriesTests()
{
STEP("Create the RM site if doesn't exist");
createRMSiteIfNotExists();
STEP("Create RM Admin user");
rmRolesAndActionsAPI.createUserAndAssignToRole(getAdminUser().getUsername(), getAdminUser().getPassword(), RM_ADMIN,
getAdminUser().getPassword(),
"Administrator");
STEP("Create two category");
Category1 = createRootCategory(getRandomName("Category1"));
Category2= createRootCategory(getRandomName("Category2"));
STEP("Create Sub category");
RecordCategoryChild subCategory1 = createRecordCategory(Category1.getId(), getRandomName("subCategory1"));
RecordCategoryChild subCategory2 = createRecordCategory(Category2.getId(), getRandomName("subCategory2"));
}
@Test @AlfrescoTest(jira = "RM-2756")
public void createCategories() throws Exception {
FilePlan filePlan = getRestAPIFactory().getFilePlansAPI().getFilePlan(FILE_PLAN_ALIAS);
STEP("copy category 1 to File Plan.");
getRestAPIFactory().getNodeAPI(toContentModel(Category1.getId())).copy(createBodyForMoveCopy(filePlan.getId()));
STEP("copy category 1 to category 2");
getRestAPIFactory().getNodeAPI(toContentModel(Category1.getId())).copy(createBodyForMoveCopy(Category2.getId()));
String categoryName = "Category name " + getRandomAlphanumeric();
String categoryTitle = "Category title " + getRandomAlphanumeric();
// Create the root record category
RecordCategory Category1 = createRootCategory(categoryName, categoryTitle);
String newCategoryName = "Rename " + categoryName;
// Build the properties which will be updated
RecordCategory recordCategoryUpdated = Category1.builder().name(newCategoryName).build();
// Update the record category
RecordCategory renamedRecordCategory = getRestAPIFactory().getRecordCategoryAPI().updateRecordCategory(recordCategoryUpdated,Category1.getId());
// Verify the status code
assertStatusCode(OK);
// verify renamed component and editTitle component still has this parent
assertEquals(renamedRecordCategory.getParentId(), filePlan.getId());
STEP("move category 1 edited copy to File Plan");
getRestAPIFactory().getNodeAPI(toContentModel(renamedRecordCategory.getId())).move(createBodyForMoveCopy(filePlan.getId()));
assertStatusCode(OK);
// delete All the categories
deleteRecordCategory(Category1.getId());
deleteRecordCategory(Category2.getId());
}
}

View File

@@ -0,0 +1,147 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.common.ReviewPeriod;
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.RecordCategoryAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.test.AlfrescoTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.rest.rm.community.utils.CoreUtil.createBodyForMoveCopy;
import static org.alfresco.rest.rm.community.utils.CoreUtil.toContentModel;
import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.*;
public class CreateFoldersTests extends BaseRMRestTest {
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
private final String TEST_PREFIX = generateTestPrefix(CreateCategoriesTests.class);
private final String RM_ADMIN = TEST_PREFIX + "rm_admin";
private RecordCategory Category1;
private RecordCategory Category2;
private RecordCategoryChild recordCategoryChild;
@BeforeClass(alwaysRun = true)
public void preconditionForCreateFolderTests() {
STEP("Create the RM site if doesn't exist");
createRMSiteIfNotExists();
STEP("Create RM Admin user");
rmRolesAndActionsAPI.createUserAndAssignToRole(getAdminUser().getUsername(), getAdminUser().getPassword(), RM_ADMIN,
getAdminUser().getPassword(),
"Administrator");
STEP("Create two category");
Category1 = createRootCategory(getRandomName("Category1"));
Category2 = createRootCategory(getRandomName("Category2"));
// Create a record folder inside the category 1
recordCategoryChild = createRecordFolder(Category1.getId(), getRandomName("recFolder"));
}
@Test
@AlfrescoTest(jira = "RM-2757")
public void createFolders() throws Exception {
// Create record category first
String folderDescription = "The folder description is updated" + getRandomAlphanumeric();
String folderName = "The folder name is updated" + getRandomAlphanumeric();
String folderTitle = "Update title " + getRandomAlphanumeric();
String location = "Location "+ getRandomAlphanumeric();
// Create the record folder properties to update
RecordFolder recordFolder = RecordFolder.builder()
.name(folderName)
.properties(RecordFolderProperties.builder()
.title(folderTitle)
.description(folderDescription)
.vitalRecordIndicator(true)
.reviewPeriod(new ReviewPeriod("month","1"))
.location(location)
.build())
.build();
// Update the record folder
RecordFolder updatedRecordFolder = getRestAPIFactory().getRecordFolderAPI().updateRecordFolder(recordFolder, recordCategoryChild.getId());
// Check the Response Status Code
assertStatusCode(OK);
STEP("copy updated Record in category 1 and category 2");
getRestAPIFactory().getNodeAPI(toContentModel(updatedRecordFolder.getId())).copy(createBodyForMoveCopy(Category1.getId()));
//assertStatusCode(OK);
getRestAPIFactory().getNodeAPI(toContentModel(updatedRecordFolder.getId())).copy(createBodyForMoveCopy(Category2.getId()));
//assertStatusCode(OK);
// Delete the Updated folder
RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI();
String recordFolderId = updatedRecordFolder.getId();
recordFolderAPI.deleteRecordFolder(recordFolderId);
// Check the response status code
assertStatusCode(NO_CONTENT);
// Check the record folder is not found
recordFolderAPI.getRecordFolder(recordFolderId);
// Check the response status code
assertStatusCode(NOT_FOUND);
STEP("move updated Record from category 1 to category 2");
getRestAPIFactory().getNodeAPI(toContentModel(updatedRecordFolder.getId())).move(createBodyForMoveCopy(Category2.getId()));
// move category 2 to category 1
getRestAPIFactory().getNodeAPI(toContentModel(Category2.getId())).move(createBodyForMoveCopy(Category1.getId()));
// Delete the record category
RecordCategoryAPI recordCategoryAPI = getRestAPIFactory().getRecordCategoryAPI();
String recordCategoryId = Category1.getId();
recordCategoryAPI.deleteRecordCategory(recordCategoryId);
// Verify the status code
assertStatusCode(NO_CONTENT);
}
}

View File

@@ -0,0 +1,122 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import lombok.Getter;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
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.rules.ActionsOnRule;
import org.alfresco.rest.rm.community.model.rules.RuleDefinition;
import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildEntry;
import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI;
import org.alfresco.rest.v0.RulesAPI;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.data.DataContent;
import org.alfresco.utility.data.DataSite;
import org.alfresco.utility.data.DataUserAIS;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static lombok.AccessLevel.PROTECTED;
import static org.alfresco.rest.core.v0.BaseAPI.NODE_PREFIX;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.CREATED;
public class DeclareDocsAsRecordsOnUpdateRuleNewVersionTests extends BaseRMRestTest {
@Autowired
private DataSite dataSite;
private SiteModel publicSite;
private RecordCategory recordCategory;
@Autowired
private RulesAPI rulesAPI;
@Autowired
protected DataContent dataContent;
@Autowired
@Getter(value = PROTECTED)
protected DataUserAIS dataUser;
private final static String title = "Rule to convert document as record";
@BeforeClass (alwaysRun = true)
public void setUp()
{
publicSite = dataSite.usingAdmin().createPublicRandomSite();
recordCategory = createRootCategory(getRandomName("recordCategory"));
}
@Test
@AlfrescoTest(jira = "RM-1521")
public void declareDocsAsRecordsOnUpdateRuleNewVersion() {
FolderModel testFolder;
STEP("Create test collaboration site to store documents in.");
publicSite = dataSite.usingAdmin().createPublicRandomSite();
STEP("Create a record folder with a DECLARE_AS_RECORD");
RecordCategoryChild folderWithRule = createFolder(recordCategory.getId(), getRandomName("recordFolder"));
RuleDefinition ruleDefinition = RuleDefinition.createNewRule().title("name").description("description")
.applyToChildren(true)
.actions(Collections.singletonList(ActionsOnRule.DECLARE_AS_RECORD.getActionValue()));
rulesAPI.createRule(getAdminUser().getUsername(), getAdminUser().getPassword(), NODE_PREFIX + folderWithRule.getId(), ruleDefinition);
STEP("Create a document in the collaboration site");
FileModel testFile = dataContent.usingSite(publicSite)
.usingAdmin()
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
assertStatusCode(CREATED);
// verify the declared record is in Unfilled Records folder
UnfiledContainerAPI unfiledContainersAPI = getRestAPIFactory().getUnfiledContainersAPI();
List<UnfiledContainerChildEntry> matchingRecords = unfiledContainersAPI.getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS)
.getEntries()
.stream()
.filter(e -> e.getEntry().getId().equals(testFile.getNodeRefWithoutVersion()))
.collect(Collectors.toList());
//delete rm items
deleteRecordCategory(recordCategory.getId());
STEP("Delete the record.");
//delete created collaboration site
dataSite.deleteSite(publicSite);
}
}

View File

@@ -0,0 +1,121 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordFoldersAPI;
import org.alfresco.rest.v0.service.DispositionScheduleService;
import org.alfresco.test.AlfrescoTest;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.CREATED_DATE;
import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.CUT_OFF_DATE;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.rest.rm.community.utils.CoreUtil.createBodyForMoveCopy;
import static org.alfresco.rest.rm.community.utils.CoreUtil.toContentModel;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.OK;
import static org.testng.AssertJUnit.assertNotNull;
public class DestroyRecordFolderActionsTest extends BaseRMRestTest {
private RecordCategory Category1,CATEGORY_TO_MOVE;
@Autowired
private DispositionScheduleService dispositionScheduleService;
@Autowired
private RecordFoldersAPI recordFoldersAPI;
private final String TEST_PREFIX = generateTestPrefix(DestroyRecordFolderActionsTest.class);
private final String folderDisposition = TEST_PREFIX + "RM-2937 folder ghosting";
@BeforeClass(alwaysRun = true)
private void setUp(){
STEP("Create the RM site if doesn't exist");
createRMSiteIfNotExists();
STEP("Create two record category");
Category1 = createRootCategory(getRandomName("Category1"));
CATEGORY_TO_MOVE = createRootCategory(getRandomName("CATEGORY_TO_MOVE"));
//create retention schedule
dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), false);
// add cut off step
dispositionScheduleService.addCutOffAfterPeriodStep(Category1.getName(), "day|2", CREATED_DATE);
// add destroy step with ghosting
dispositionScheduleService.addDestroyWithGhostingImmediatelyAfterCutOff(Category1.getName());
}
@Test
@AlfrescoTest (jira = "RM-1621")
public void moveOnCutOffDestroyFolders() throws Exception {
//create folders
RecordCategoryChild FOLDER_DESTROY = createFolder(getAdminUser(),Category1.getId(),folderDisposition);
// edit disposition date
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),FOLDER_DESTROY.getName());
// cut off the FOLDER_DESTROY
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),FOLDER_DESTROY.getName());
// Destroy the FOLDER_DESTROY
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),FOLDER_DESTROY.getName());
//Move the FOLDER_DESTROY within the CATEGORY_TO_MOVE.");
getRestAPIFactory().getNodeAPI(toContentModel(FOLDER_DESTROY.getId())).move(createBodyForMoveCopy(CATEGORY_TO_MOVE.getId()));
assertStatusCode(OK);
}
@AfterMethod(alwaysRun = true)
private void deletePreconditions() {
deleteRecordCategory(Category1.getId());
deleteRecordCategory(CATEGORY_TO_MOVE.getId());
}
}

View File

@@ -0,0 +1,425 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.core.v0.RMEvents;
import org.alfresco.rest.model.RestNodeBodyMoveCopyModel;
import org.alfresco.rest.model.RestNodeModel;
import org.alfresco.rest.requests.Node;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.fileplan.FilePlan;
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.v0.LinksAPI;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordFoldersAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.rest.v0.service.DispositionScheduleService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.model.RepoTestModel;
import org.alfresco.utility.model.UserModel;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.AssertJUnit;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import static org.alfresco.rest.core.v0.BaseAPI.NODE_REF_WORKSPACE_SPACES_STORE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.CUT_OFF_ASPECT;
import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.*;
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;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.http.HttpStatus.NO_CONTENT;
public class DispositionScheduleLinkedRecordsTest extends BaseRMRestTest {
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@Autowired
private DispositionScheduleService dispositionScheduleService;
@Autowired
private LinksAPI linksAPI;
@Autowired
private RecordsAPI recordsAPI;
@Autowired
private RecordFoldersAPI recordFoldersAPI;
private final static String TEST_PREFIX = generateTestPrefix(DispositionScheduleLinkedRecordsTest.class);
private RecordCategory Category1,catsameLevel1,catsameLevel2;
private RecordCategoryChild CopyCatFolder,folder1,CatFolder,folder2;
private static final String categoryRM3077 = TEST_PREFIX + "RM-3077_manager_sees_me";
private static final String copyCategoryRM3077 = "Copy_of_" + categoryRM3077;
private static final String folderRM3077 = "RM-3077_folder_"+ categoryRM3077;
private static final String copyFolderRM3077 = "Copy_of_" + folderRM3077;
private final String electronicRecord = "RM-2937 electronic 2 record";
private final String folder = TEST_PREFIX + "RM-2937 folder ghosting";
private static final String categoryRecordsRM2526 = TEST_PREFIX + "RM-2526_category_records_immediately";
private static final String category2RecordsRM2526 = TEST_PREFIX + "RM-2526_category_2_records_1_day";
private static final String firstCategoryRM3060 = TEST_PREFIX + "RM-3060_category_record";
private static final String secondCategoryRM3060 = "Copy_of_" + firstCategoryRM3060;
private static final String firstFolderRM3060 = TEST_PREFIX + "RM-3060_folder";
private static final String secondFolderRM3060 = TEST_PREFIX + "RM-3060_disposition_on_Record_Level";
private static final String electronicRecordRM3060 = TEST_PREFIX + "RM-3060_electronic_1_record";
private static final String nonElectronicRecordRM3060 = TEST_PREFIX + "RM-3060_non-electronic_record";
private static final String TRANSFER_LOCATION = TEST_PREFIX + "RM-3060_transferred_records";
public static final String TRANSFER_TYPE = "rma:transferred";
private FilePlan filePlanModel;
private UserModel rmAdmin, rmManager;
@BeforeClass(alwaysRun = true)
public void setupDispositionScheduleLinkedRecordsTest() {
createRMSiteIfNotExists();
//get file plan
filePlanModel = getRestAPIFactory().getFilePlansAPI().getFilePlan(FILE_PLAN_ALIAS);
// create "rm admin" user if it does not exist and assign it to RM Administrator role
rmAdmin = getDataUser().createRandomTestUser();
rmRolesAndActionsAPI.assignRoleToUser(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),rmAdmin.getUsername(),
UserRoles.ROLE_RM_ADMIN.roleId);
// create "rm Manager" user if it does not exist and assign it to RM Administrator role
rmManager = getDataUser().createRandomTestUser();
rmRolesAndActionsAPI.assignRoleToUser(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),rmManager.getUsername(),
UserRoles.ROLE_RM_MANAGER.roleId);
}
/**
* Disposition Schedule on Record Folder with linked records test
* <p>
* Precondition:
* <p>
* Create rm_manager user that would have RM Managers role, rm_admin that would have RM Administrator role.
* Log in with admin user, create a category "manager sees me", give rm_manager read&file permission over it.
* Create a disposition schedule for it that would cut off folders after 1 day from created date. Copy the category.
* <p>
* <p/> TestRail Test C775<p/>
**/
@Test
@AlfrescoTest(jira = "RM-1622")
public void dispositionScheduleLinkedRecords() throws UnsupportedEncodingException {
STEP("Create record category");
Category1 = createRootCategory(categoryRM3077);
//create retention schedule
dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), false);
// add cut off step
dispositionScheduleService.addCutOffAfterPeriodStep(Category1.getName(), "day|2", CREATED_DATE);
//create a copy of the category recordsCategory
String CopyCategoryId = copyCategory(getAdminUser(),Category1.getId(), copyCategoryRM3077);
// create folders in both categories
CatFolder = createRecordFolder(Category1.getId(), folderRM3077);
CopyCatFolder = createRecordFolder(CopyCategoryId, copyFolderRM3077);
// create record files
String electronicRecord = "RM-2801 electronic record";
Record elRecord = createElectronicRecord(CatFolder.getId(), electronicRecord);
String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), CatFolder.getName(), electronicRecord);
String nonElectronicRecord = "RM-2801 non-electronic record";
Record nonElRecord = createNonElectronicRecord(CatFolder.getId(), nonElectronicRecord);
String nonElRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), CatFolder.getName(), nonElectronicRecord);
// link the records to copy folder, then complete them
List<String> recordLists = new ArrayList<>();
recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + elRecord.getId());
recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + nonElRecord.getId());
linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,copyCategoryRM3077 + "/" +
copyFolderRM3077, recordLists);
recordsAPI.completeRecord(rmAdmin.getUsername(), rmAdmin.getPassword(), elRecordFullName);
recordsAPI.completeRecord(rmAdmin.getUsername(), rmAdmin.getPassword(), nonElRecordFullName);
// edit disposition date
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),CatFolder.getName());
// cut off the Folder
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),CatFolder.getName());
// Verify the Content
Node electronicNode = getNode(elRecord.getId());
assertTrue("The content of " + electronicRecord + " is available",
StringUtils.isEmpty(electronicNode.getNodeContent().getResponse().getBody().asString()));
// verify the Properties
AssertJUnit.assertNull("The properties are present even after cutting off the record.", elRecord.getProperties().getTitle());
// delete precondition
deleteRecordCategory(Category1.getId());
deleteRecordCategory(CopyCategoryId);
}
/**
* Test covering RM-3060
* Check the disposition steps for a record can be executed
* When the record is linked to a folder with the same disposition schedule
* */
@Test
@AlfrescoTest (jira = "RM-3060")
public void sameDispositionScheduleLinkedRecords() throws UnsupportedEncodingException {
// create a category with retention applied on records level
RecordCategory recordCategory = getRestAPIFactory().getFilePlansAPI(rmAdmin)
.createRootRecordCategory(RecordCategory.builder().name(firstCategoryRM3060).build(),
RecordCategory.DEFAULT_FILE_PLAN_ALIAS);
dispositionScheduleService.createCategoryRetentionSchedule(firstCategoryRM3060, true);
dispositionScheduleService.addCutOffAfterPeriodStep(firstCategoryRM3060, "week|1", DATE_FILED);
dispositionScheduleService.addTransferAfterEventStep(firstCategoryRM3060, TRANSFER_LOCATION, RMEvents.CASE_CLOSED.getEventName());
dispositionScheduleService.addDestroyWithoutGhostingAfterPeriodStep(firstCategoryRM3060, "week|1", CUT_OFF_DATE);
// make a copy of the category created
String categorySecondId = copyCategory(getAdminUser(), recordCategory.getId(), secondCategoryRM3060);
// create a folder on the category firstCategoryRM3060 with a complete electronic record
RecordCategoryChild firstFolderRecordCategoryChild = createRecordFolder(recordCategory.getId(),firstFolderRM3060);
Record firstElectronicRecord = createElectronicRecord(firstFolderRecordCategoryChild.getId(),electronicRecordRM3060);
String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(),firstFolderRM3060, electronicRecordRM3060);
String elRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060);
recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), elRecordFullName);
// create a folder on the category secondCategoryRM3060 with a non electronic record
RecordCategoryChild secondFolderRecordCategoryChild = createRecordFolder(categorySecondId,secondFolderRM3060);
Record secondNonElectronicRecord = createNonElectronicRecord(secondFolderRecordCategoryChild.getId(),nonElectronicRecordRM3060);
// link the nonElectronicRecordRM3060 to firstFolderRM3060
List<String> recordLists = new ArrayList<>();
recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + secondNonElectronicRecord.getId());
linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" +
secondFolderRM3060, recordLists);
String nonElRecordFullName = recordsAPI.getRecordFullName(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), secondFolderRM3060, secondNonElectronicRecord.getName());
String nonElRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordFullName, "/" + secondCategoryRM3060 + "/" + secondFolderRM3060);
// complete records and cut them off
recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), nonElRecordFullName);
// edit the disposition date
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef);
// cut off the record
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),nonElRecordNameNodeRef);
//check the record is cut off
AssertJUnit.assertTrue("The file " + nonElectronicRecordRM3060 + " has not been successfully cut off.", getRestAPIFactory().getRecordsAPI().getRecord(secondNonElectronicRecord.getId()).getAspectNames().contains(CUT_OFF_ASPECT));
// link the electronic record to secondFolderRM3060
recordLists.clear();
recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + secondNonElectronicRecord.getId());
linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" +
secondFolderRM3060, recordLists);
// edit the disposition date and cut off the record
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),elRecordNameNodeRef);
AssertJUnit.assertTrue("The file " + electronicRecordRM3060 + " has not been successfully cut off.", getRestAPIFactory().getRecordsAPI().getRecord(firstElectronicRecord.getId()).getAspectNames().contains(CUT_OFF_ASPECT));
// open the record and complete the disposition schedule event
rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(),
getAdminUser().getPassword(), elRecordFullName, RMEvents.CASE_CLOSED, Instant.now());
rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(),
getAdminUser().getPassword(), nonElRecordFullName, RMEvents.CASE_CLOSED, Instant.now());
// transfer the files & complete transfers
HttpResponse nonElRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordFullName, "/" + secondCategoryRM3060 + "/" + secondFolderRM3060));
String nonElRecordNameTransferId = getTransferId(nonElRecordNameHttpResponse,nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),nonElRecordNameTransferId);
HttpResponse elRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060));
String elRecordNameTransferId = getTransferId(elRecordNameHttpResponse,elRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),elRecordNameTransferId);
AssertJUnit.assertTrue("The file " + electronicRecordRM3060 + " has not been successfully transferred", getRestAPIFactory().getRecordsAPI().getRecord(firstElectronicRecord.getId()).getAspectNames().contains(TRANSFER_TYPE));
AssertJUnit.assertTrue("The file " + nonElectronicRecordRM3060 + " has not been successfully transferred.", getRestAPIFactory().getRecordsAPI().getRecord(secondNonElectronicRecord.getId()).getAspectNames().contains(TRANSFER_TYPE));
// edit the disposition date for nonElectronicRecordRM3060 & electronicRecordRM3060
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef);
// destroy nonElectronicRecordRM3060 & electronicRecordRM3060 records
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),elRecordNameNodeRef);
// check the file is not displayed
assertNull("The file " + nonElectronicRecordRM3060 + " has not been successfully destroyed.", secondNonElectronicRecord.getContent());
assertNull("The file " + electronicRecordRM3060 + " has not been successfully destroyed.", firstElectronicRecord.getContent());
// delete precondition
deleteRecordCategory(recordCategory.getId());
deleteRecordCategory(categorySecondId);
}
private String copyCategory(UserModel user, String categoryId, String copyName) {
RepoTestModel repoTestModel = new RepoTestModel() {};
repoTestModel.setNodeRef(categoryId);
RestNodeModel restNodeModel;
RestNodeBodyMoveCopyModel copyDestinationInfo = new RestNodeBodyMoveCopyModel();
copyDestinationInfo.setTargetParentId(filePlanModel.getId());
copyDestinationInfo.setName(copyName);
try
{
restNodeModel = getRestAPIFactory().getNodeAPI(user, repoTestModel).copy(copyDestinationInfo);
}
catch (Exception e)
{
throw new RuntimeException("Problem copying category.", e);
}
return restNodeModel.getId();
}
private Node getNode(String recordId)
{
RepoTestModel repoTestModel = new RepoTestModel() {};
repoTestModel.setNodeRef(recordId);
return getRestAPIFactory().getNodeAPI(repoTestModel);
}
private String getTransferId(HttpResponse httpResponse,String nodeRef) {
HttpEntity entity = httpResponse.getEntity();
String responseString = null;
try {
responseString = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
throw new RuntimeException(e);
}
JSONObject result = new JSONObject(responseString);
return result
.getJSONObject("results")
.get(nodeRef)
.toString();
}
@Test
@AlfrescoTest(jira = "RM-1622")
public void sameLevelDispositionScheduleStepsPeriodsCalculation() throws Exception {
// create a category with retention applied on records level
RecordCategory catsameLevel1 = getRestAPIFactory().getFilePlansAPI(rmAdmin)
.createRootRecordCategory(RecordCategory.builder().name(firstCategoryRM3060).build(),
RecordCategory.DEFAULT_FILE_PLAN_ALIAS);
RecordCategory catsameLevel2 = getRestAPIFactory().getFilePlansAPI(rmAdmin)
.createRootRecordCategory(RecordCategory.builder().name(secondCategoryRM3060).build(),
RecordCategory.DEFAULT_FILE_PLAN_ALIAS);
// create retention schedule applied on records for category 1
dispositionScheduleService.createCategoryRetentionSchedule(firstCategoryRM3060, true);
// with retain immediately after record creation date and cut 1 day after record creation date
dispositionScheduleService.addCutOffAfterPeriodStep(firstCategoryRM3060, "day|1", DATE_FILED);
// create a folder on the category firstCategoryRM3060 with a complete electronic record
RecordCategoryChild firstFolderRecordCategoryChild = createRecordFolder(catsameLevel1.getId(),firstFolderRM3060);
Record firstElectronicRecord = createElectronicRecord(firstFolderRecordCategoryChild.getId(),electronicRecordRM3060);
String elRecordFullName = recordsAPI.getRecordFullName(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(),firstFolderRM3060, electronicRecordRM3060);
String elRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060);
recordsAPI.completeRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), elRecordFullName);
// create a folder on the category secondCategoryRM3060 with a non electronic record
RecordCategoryChild secondFolderRecordCategoryChild = createRecordFolder(catsameLevel2.getId(),secondFolderRM3060);
String elRecordNameNodeRefs = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordFullName, "/" + firstCategoryRM3060 + "/" + firstFolderRM3060);
// link it to the folder in second category through the details page
List<String> recordLists = new ArrayList<>();
recordLists.add(NODE_REF_WORKSPACE_SPACES_STORE + firstElectronicRecord.getId());
linksAPI.linkRecord(getDataUser().getAdminUser().getUsername(),
getDataUser().getAdminUser().getPassword(), HttpStatus.SC_OK,secondCategoryRM3060 + "/" +
secondFolderRM3060, recordLists);
// edit disposition date
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRefs);
}
@Test (dependsOnMethods = {"sameLevelDispositionScheduleStepsPeriodsCalculation" })
public void deleteLongestPeriodTestPrecondition() {
// Delete the RM site
getRestAPIFactory().getRMSiteAPI().deleteRMSite();
// Verify the status code
assertStatusCode(NO_CONTENT);
}
}

View File

@@ -0,0 +1,238 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
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.RecordFolderCollection;
import org.alfresco.rest.rm.community.model.user.UserRoles;
import org.alfresco.rest.v0.RecordCategoriesAPI;
import org.alfresco.rest.v0.service.RoleService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.Utility;
import org.alfresco.utility.data.DataContent;
import org.alfresco.utility.data.DataSite;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FileType;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.UserModel;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.concurrent.atomic.AtomicReference;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.rest.rm.community.utils.CoreUtil.toContentModel;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.testng.Assert.*;
public class FileAsRecordTests extends BaseRMRestTest {
private static final String CATEGORY_MANAGER = "catManager" + generateTestPrefix(FileAsRecordTests.class);
private static final String CATEGORY_ADMIN = "catAdmin" + generateTestPrefix(FileAsRecordTests.class);
private static final String FOLDER_MANAGER = "recordFolder" + generateTestPrefix(FileAsRecordTests.class);
private static final String FOLDER_ADMIN = "recordFolder" + generateTestPrefix(FileAsRecordTests.class);
private UserModel nonRMuser,rmManager;
private SiteModel testSite;
private FileModel document, documentDeclared;
private RecordCategory category_manager, category_admin;
private RecordCategoryChild folder_admin, folder_manager ;
@Autowired
private DataSite dataSite;
@Autowired
private DataContent dataContent;
@Autowired
private RoleService roleService;
@Autowired
private RecordCategoriesAPI recordCategoriesAPI;
/**
* Create preconditions:
* <pre>
* 1. RM site is created
* 2. Two users: user without RM role and a user with RM manager role
* 3. Two Record categories with one folder each
* 4. User with RM MANAGER role has Filling permission over one category
* </pre>
*/
@BeforeClass(alwaysRun = true)
public void preconditionForFileAsRecordRecordTests()
{
STEP("Create the RM site if doesn't exist");
createRMSiteIfNotExists();
STEP("Create a user");
nonRMuser = dataUser.createRandomTestUser("testUser");
STEP("Create a collaboration site");
testSite = dataSite.usingUser(nonRMuser).createPublicRandomSite();
STEP("Create a document with the user without RM role");
document = dataContent.usingSite(testSite)
.usingUser(nonRMuser)
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
STEP("Create two categories with two folders");
category_manager = createRootCategory(CATEGORY_MANAGER);
category_admin = createRootCategory(CATEGORY_ADMIN);
folder_admin = createFolder(category_admin.getId(),FOLDER_ADMIN);
folder_manager = createFolder(category_manager.getId(),FOLDER_MANAGER);
STEP("Create an rm user and give filling permission over CATEGORY_MANAGER record category");
RecordCategory recordCategory = new RecordCategory().builder()
.id(category_manager.getId())
.build();
rmManager = roleService.createCollaboratorWithRMRoleAndPermission(testSite, recordCategory,
UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
}
/**
* Given I have selected the record folder I want to file my declared record to
* When I confirm the action
* Then the dialog closes
* And the document is now shown as a record in the collaboration site
* And if I navigated to the record folder, as any user who had the right permissions, then I would see the
* record filed
*/
@Test
@AlfrescoTest(jira = "RM-6780")
public void checkFileAsRecordToRecordFolder() throws Exception {
AtomicReference<RecordFolderCollection> apiChildren = new AtomicReference<>();
STEP("Create a document with the user with RM role");
documentDeclared = dataContent.usingSite(testSite).usingUser(rmManager)
.createContent(new FileModel("checkDeclareAndFileToRecordFolder", FileType.TEXT_PLAIN));
STEP("Declare and file into a record folder the document uploaded");
getRestAPIFactory().getActionsAPI(rmManager).declareAndFile(documentDeclared,
Utility.buildPath(CATEGORY_MANAGER, FOLDER_MANAGER));
STEP("Check the file is a record within the collaboration site");
try
{
Utility.sleep(1000, 40000, () ->
{
JSONObject collaboratorSearchJson = getSearchApi().liveSearchForDocuments(rmManager.getUsername(),
rmManager.getPassword(),
documentDeclared.getName());
assertTrue("Rm Manager not able to find the document.", collaboratorSearchJson.getJSONArray("items").length() != 0);
});
}
catch (InterruptedException e)
{
fail("InterruptedException received while waiting for results.");
}
STEP("Check the record is filed into the record folder.");
// Get children from API
// List children from API
try
{
Utility.sleep(1000, 40000, () ->
{
apiChildren.set((RecordFolderCollection) getRestAPIFactory()
.getRecordFolderAPI(rmManager).getRecordFolderChildren(folder_manager.getId(), "include=properties")
.assertThat().entriesListIsNotEmpty().assertThat().entriesListIsNotEmpty());
});
}
catch (InterruptedException e)
{
fail("InterruptedException received while waiting for results.");
}
assertEquals(apiChildren.get()
.getEntries()
.get(0)
.getEntry()
.getProperties()
.getOriginalName(),documentDeclared.getName());
}
/**
* Given I have selected the "File As Record" action
* When I confirm the action without selecting a location to file to
* Then the record is declared in the unfiled folder
*/
@Test
@AlfrescoTest (jira = "RM-6780")
public void fileAsRecordToUnfiledRecordFolder() throws Exception {
STEP("Create a document with the user without RM role");
FileModel inplaceRecord = dataContent.usingSite(testSite).usingUser(rmManager)
.createContent(new FileModel("declareAndFileToIntoUnfiledRecordFolder",
FileType.TEXT_PLAIN));
STEP("Click on Declare and file without selecting a record folder");
getRestAPIFactory().getActionsAPI(rmManager).declareAndFile(inplaceRecord,"");
STEP("Check the file is declared in unfiled record folder");
Assert.assertTrue(isMatchingRecordInUnfiledRecords(inplaceRecord), "Record should be filed to Unfiled Records folder");
}
@AfterClass(alwaysRun = true)
public void cleanUpForFileAsRecordRecordTests() {
STEP("Delete the collaboration site");
dataSite.usingUser(nonRMuser).deleteSite(testSite);
STEP("Empty the trashcan.");
restClient.authenticateUser(nonRMuser).withCoreAPI().usingTrashcan().deleteNodeFromTrashcan(toContentModel(testSite.getId()));
getRestAPIFactory()
.getUnfiledContainersAPI(rmManager)
.getUnfiledContainerChildren(UNFILED_RECORDS_CONTAINER_ALIAS)
.getEntries()
.stream()
.forEach(x -> getRestAPIFactory()
.getRecordsAPI()
.deleteRecord(x.getEntry().getId()));
STEP("Cleanup Documents inside folders");
STEP("Delete folders");
getRestAPIFactory().getRecordFolderAPI().deleteRecordFolder(folder_admin.getId());
getRestAPIFactory().getRecordFolderAPI().deleteRecordFolder(folder_manager.getId());
STEP("Delete categories");
recordCategoriesAPI.deleteCategory(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), category_manager.getName());
recordCategoriesAPI.deleteCategory(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), category_admin.getName());
STEP("Delete Users");
dataUser.deleteUser(nonRMuser);
dataUser.deleteUser(rmManager);
}
}

View File

@@ -0,0 +1,138 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
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.RecordFolderCollection;
import org.alfresco.rest.rm.community.model.user.UserRoles;
import org.alfresco.rest.rm.community.records.FileUnfiledRecordsTests;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordCategoriesAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.rest.v0.service.RoleService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.Utility;
import org.alfresco.utility.data.DataContent;
import org.alfresco.utility.data.DataSite;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FileType;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.UserModel;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.concurrent.atomic.AtomicReference;
import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING;
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;
import static org.springframework.test.util.AssertionErrors.assertTrue;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
public class FileVersionAsRecordTests extends BaseRMRestTest {
private UserModel nonRMuser,rmManager;
private SiteModel testSite;
private FileModel document, documentDeclared;
private RecordCategory category_manager, category_admin;
private RecordCategoryChild folder_admin, folder_manager ;
private static final String CATEGORY_MANAGER = "catManager" + generateTestPrefix(FileAsRecordTests.class);
private static final String CATEGORY_ADMIN = "catAdmin" + generateTestPrefix(FileAsRecordTests.class);
private static final String FOLDER_MANAGER = "recordFolder" + generateTestPrefix(FileAsRecordTests.class);
private static final String FOLDER_ADMIN = "recordFolder" + generateTestPrefix(FileAsRecordTests.class);
@Autowired
private DataSite dataSite;
@Autowired
private DataContent dataContent;
@Autowired
private RoleService roleService;
@BeforeClass(alwaysRun = true)
public void preconditionForFileVersionAsRecordTests()
{
STEP("Create the RM site if doesn't exist");
createRMSiteIfNotExists();
STEP("Create a user");
nonRMuser = dataUser.createRandomTestUser("testUser");
STEP("Create a collaboration site");
testSite = dataSite.usingUser(nonRMuser).createPublicRandomSite();
STEP("Create a document with the user without RM role");
document = dataContent.usingSite(testSite)
.usingUser(nonRMuser)
.createContent(CMISUtil.DocumentType.TEXT_PLAIN);
STEP("Create two categories with two folders");
category_manager = createRootCategory(CATEGORY_MANAGER);
category_admin = createRootCategory(CATEGORY_ADMIN);
folder_admin = createFolder(category_admin.getId(),FOLDER_ADMIN);
folder_manager = createFolder(category_manager.getId(),FOLDER_MANAGER);
STEP("Create an rm user and give filling permission over CATEGORY_MANAGER record category");
RecordCategory recordCategory = new RecordCategory().builder()
.id(category_manager.getId())
.build();
rmManager = roleService.createCollaboratorWithRMRoleAndPermission(testSite, recordCategory,
UserRoles.ROLE_RM_MANAGER, PERMISSION_FILING);
}
@Test
@AlfrescoTest (jira = "APPS-1625")
public void fileVersionAsRecordToUnfiledRecordContainer() throws Exception
{
AtomicReference<RecordFolderCollection> apiChildren = new AtomicReference<>();
STEP("Create a document with the user without RM role");
FileModel inplaceRecord = dataContent.usingSite(testSite).usingUser(rmManager)
.createContent(new FileModel("declareAndFileToIntoUnfiledRecordFolder",
FileType.TEXT_PLAIN));
STEP("Click on Declare and file without selecting a record folder");
getRestAPIFactory().getActionsAPI(rmManager).declareAndFile(inplaceRecord,"");
STEP("Check the file is declared in unfiled record folder");
Assert.assertTrue(isMatchingRecordInUnfiledRecords(inplaceRecord), "Record should be filed to Unfiled Records folder");
}
}

View File

@@ -0,0 +1,108 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
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.v0.RecordFoldersAPI;
import org.alfresco.rest.v0.service.DispositionScheduleService;
import org.alfresco.test.AlfrescoTest;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.CREATED_DATE;
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;
public class FoldersDispositionScheduleTests extends BaseRMRestTest {
private RecordCategory Category1;
@Autowired
private DispositionScheduleService dispositionScheduleService;
@Autowired
private RecordFoldersAPI recordFoldersAPI;
private final String TEST_PREFIX = generateTestPrefix(FoldersDispositionScheduleTests.class);
private final String folderDisposition = TEST_PREFIX + "RM-2937 folder ghosting";
private final String electronicRecord = "RM-2937 electronic 2 record";
private final String nonElectronicRecord = "RM-2937 non-electronic record";
@BeforeClass(alwaysRun = true)
private void setUp(){
STEP("Create the RM site if doesn't exist");
createRMSiteIfNotExists();
STEP("Create record category");
Category1 = createRootCategory(getRandomName("Title"));
}
@Test
@AlfrescoTest (jira = "RM-2937")
public void foldersDispositionScheduleWithGhosting() {
//create retention schedule
dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), false);
// add cut off step
dispositionScheduleService.addCutOffAfterPeriodStep(Category1.getName(), "day|2", CREATED_DATE);
// add destroy step with ghosting
dispositionScheduleService.addDestroyWithGhostingImmediatelyAfterCutOff(Category1.getName());
//create folders
RecordCategoryChild FOLDER_DESTROY = createFolder(getAdminUser(),Category1.getId(),folderDisposition);
Record elRecord = createElectronicRecord(FOLDER_DESTROY.getId(),electronicRecord);
Record nonElRecord = createNonElectronicRecord(FOLDER_DESTROY.getId(),nonElectronicRecord);
// complete records
completeRecord(elRecord.getId());
completeRecord(nonElRecord.getId());
// edit disposition date
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),FOLDER_DESTROY.getName());
// cut off the FOLDER_DESTROY
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),FOLDER_DESTROY.getName());
// Destroy the FOLDER_DESTROY
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),FOLDER_DESTROY.getName());
}
@AfterMethod(alwaysRun = true)
private void deletePreconditions() {
deleteRecordCategory(Category1.getId());
}
}

View File

@@ -0,0 +1,116 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
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.v0.RecordFoldersAPI;
import org.alfresco.rest.v0.service.DispositionScheduleService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.Utility;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.CREATED_DATE;
import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.CUT_OFF_DATE;
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;
public class FoldersDispositionScheduleWithoutGhostRecordTests extends BaseRMRestTest {
private RecordCategory Category1;
@Autowired
private DispositionScheduleService dispositionScheduleService;
@Autowired
private RecordFoldersAPI recordFoldersAPI;
private final String TEST_PREFIX = generateTestPrefix(FoldersDispositionScheduleWithoutGhostRecordTests.class);
private final String folderDisposition = TEST_PREFIX + "RM-2937 folder ghosting";
private final String electronicRecord = "RM-2937 electronic 2 record";
private final String nonElectronicRecord = "RM-2937 non-electronic record";
@BeforeClass(alwaysRun = true)
private void setUp(){
STEP("Create the RM site if doesn't exist");
createRMSiteIfNotExists();
STEP("Create record category");
Category1 = createRootCategory(getRandomName("Title"));
}
@Test
@AlfrescoTest(jira="RM-2937")
public void foldersDispositionScheduleWithoutGhosting() {
//create retention schedule
dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), false);
// add cut off step
dispositionScheduleService.addCutOffAfterPeriodStep(Category1.getName(), "day|2", CREATED_DATE);
// add destroy step with ghosting
dispositionScheduleService.addDestroyWithoutGhostingAfterPeriodStep(Category1.getName(), "day|1", CUT_OFF_DATE);
//create folders
RecordCategoryChild FOLDER_DESTROY = createFolder(getAdminUser(),Category1.getId(),folderDisposition);
Record elRecord = createElectronicRecord(FOLDER_DESTROY.getId(),electronicRecord);
Record nonElRecord = createNonElectronicRecord(FOLDER_DESTROY.getId(),nonElectronicRecord);
// complete records
completeRecord(elRecord.getId());
completeRecord(nonElRecord.getId());
// edit disposition date
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),FOLDER_DESTROY.getName());
// cut off the FOLDER_DESTROY
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),FOLDER_DESTROY.getName());
// edit disposition date
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),FOLDER_DESTROY.getName());
Utility.waitToLoopTime(5,"Waiting for Edit Disposition to be processed");
// Destroy the FOLDER_DESTROY
recordFoldersAPI.postFolderAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),FOLDER_DESTROY.getName());
}
@AfterMethod(alwaysRun = true)
private void deletePreconditions() {
deleteRecordCategory(Category1.getId());
}
}

View File

@@ -0,0 +1,156 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.core.v0.BaseAPI;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
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.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordCategoriesAPI;
import org.alfresco.rest.v0.RecordFoldersAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.rest.v0.service.DispositionScheduleService;
import org.alfresco.test.AlfrescoTest;
import org.apache.commons.lang3.time.DateUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import static org.alfresco.rest.rm.community.base.TestData.DEFAULT_PASSWORD;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.testng.Assert.assertTrue;
public class RecordRetentionAsOfDateTest extends BaseRMRestTest {
/** data prep 6services */
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@Autowired
private RecordsAPI recordsAPI;
@Autowired
private RecordFoldersAPI recordFoldersAPI;
@Autowired
private RecordCategoriesAPI recordCategoriesAPI;
@Autowired
private DispositionScheduleService dispositionScheduleService;
private RecordCategory Category1;
private final String TEST_PREFIX = generateTestPrefix(RecordRetentionAsOfDateTest.class);
private final String RM_ADMIN = TEST_PREFIX + "rm_admin";
private final String recordsCategory = TEST_PREFIX + "RM-5733 category";
private final String folderDisposition = TEST_PREFIX + "RM-5733 folder";
private static final String YEAR_MONTH_DAY = "yyyy-MM-dd";
@Test
@AlfrescoTest (jira = "RM-5733,RM-5799")
public void checkRetentionAsOfDateForTransferStepWithRetentionAction() {
// create test precondition
createTestPrecondition(recordsCategory);
// create disposition schedule
dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), true);
// add cut off step
dispositionScheduleService.addCutOffImmediatelyStep(Category1.getName());
// add transfer step
HashMap<BaseAPI.RETENTION_SCHEDULE, String> transferStep = new HashMap<>();
transferStep.put(BaseAPI.RETENTION_SCHEDULE.RETENTION_PERIOD, "day|1");
transferStep.put(BaseAPI.RETENTION_SCHEDULE.NAME, "transfer");
transferStep.put(BaseAPI.RETENTION_SCHEDULE.RETENTION_PERIOD_PROPERTY, "rma:cutOffDate");
transferStep.put(BaseAPI.RETENTION_SCHEDULE.COMBINE_DISPOSITION_STEP_CONDITIONS, "false");
transferStep.put(BaseAPI.RETENTION_SCHEDULE.RETENTION_ELIGIBLE_FIRST_EVENT, "true");
transferStep.put(BaseAPI.RETENTION_SCHEDULE.RETENTION_GHOST, "on");
transferStep.put(BaseAPI.RETENTION_SCHEDULE.DESCRIPTION, "Transfer after 1 day");
recordCategoriesAPI.addDispositionScheduleSteps(getAdminUser().getUsername(),
getAdminUser().getPassword(), Category1.getName(), transferStep);
// create a folder and an electronic and a non-electronic record in it
RecordCategoryChild FOLDER = createFolder(getAdminUser(),Category1.getId(),folderDisposition);
String nonElectronicRecord = TEST_PREFIX + "RM-5733 non-electronic record";
Record nonElRecord = createNonElectronicRecord(FOLDER.getId(), nonElectronicRecord);
// complete records and cut them off
String nonElRecordName = recordsAPI.getRecordFullName(getAdminUser().getUsername(),
getAdminUser().getPassword(), folderDisposition, nonElectronicRecord);
// complete records and cut them off
completeRecord(nonElRecord.getId());
String nonElRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordName, "/" + Category1.getName() + "/" + folderDisposition);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),nonElRecordNameNodeRef);
JSONObject nextDispositionActionJson = recordCategoriesAPI.getNextDispositionAction(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),nonElRecord.getId());
assertTrue(getAsOfDate(nextDispositionActionJson).startsWith(getTomorrow()),
"The retention as of date is not set to tomorrow.");
}
@AfterClass(alwaysRun = true)
public void cleanUp() {
// delete category
deleteRecordCategory(Category1.getId());
}
private void createTestPrecondition(String categoryName) {
createRMSiteIfNotExists();
// create "rm admin" user if it does not exist and assign it to RM Administrator role
rmRolesAndActionsAPI.createUserAndAssignToRole(
getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),
RM_ADMIN, DEFAULT_PASSWORD, "Administrator");
// create category
STEP("Create category");
Category1 = createRootCategory(categoryName,"Title");
}
private String getAsOfDate(JSONObject nextDispositionActionJson) {
return nextDispositionActionJson.getJSONObject("data").get("asOf").toString();
}
private static String getTomorrow() {
Date today = new Date();
Date tomorrow = DateUtils.addDays(today, 1);
SimpleDateFormat dateFormat = new SimpleDateFormat(YEAR_MONTH_DAY);
return dateFormat.format(tomorrow);
}
}

View File

@@ -0,0 +1,202 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.core.v0.RMEvents;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
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.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordFoldersAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.rest.v0.service.DispositionScheduleService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.Utility;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.Test;
import java.io.IOException;
import java.time.Instant;
import static org.alfresco.rest.rm.community.base.TestData.DEFAULT_PASSWORD;
import static org.alfresco.rest.rm.community.model.recordcategory.RetentionPeriodProperty.CUT_OFF_DATE;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.report.log.Step.STEP;
/**
* Contains recordsDispositionScheduleWithoutGhosting test which checks disposition schedule cut off, transfer and destroy without maintaining metadata steps applied to records
* <p/>
* Precondition:
* <p/>
* RM site created, contains an empty category "RM-2801 disposition for records". <p/>
* RM user has RM admin role. <p/>
* A transfer location named "transferred files" is created to which RM user has access
* <p/>
* <img src="doc-files/Disposition Schedule without ghosting.png" alt="Records Disposition Schedule without ghosting" />
*
* @author Kavit Shah
*/
public class RecordsDispositionScheduleTests extends BaseRMRestTest {
/** data prep 6services */
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@Autowired
private RecordsAPI recordsAPI;
@Autowired
private RecordFoldersAPI recordFoldersAPI;
@Autowired
private DispositionScheduleService dispositionScheduleService;
private RecordCategory Category1;
private final String TEST_PREFIX = generateTestPrefix(RecordsDispositionScheduleTests.class);
private final String RM_ADMIN = TEST_PREFIX + "rm_admin";
private final String recordsCategory = TEST_PREFIX + "RM-2801 category";
private final String folderDisposition = TEST_PREFIX + "RM-2801 folder";
@Test
@AlfrescoTest(jira="RM-2801")
public void recordsDispositionScheduleWithoutGhosting() {
// create test precondition
createTestPrecondition(recordsCategory);
// create disposition schedule
dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), true);
// add cut off step
dispositionScheduleService.addCutOffImmediatelyStep(Category1.getName());
// add transfer step
dispositionScheduleService.addTransferAfterEventStep(Category1.getName(),"transferred records","all_allowances_granted_are_terminated");
// add destroy step without retaining metadata
dispositionScheduleService.addDestroyWithoutGhostingAfterPeriodStep(Category1.getName(), "day|1", CUT_OFF_DATE);
// create a folder and an electronic and a non-electronic record in it
RecordCategoryChild FOLDER_DESTROY = createFolder(getAdminUser(),Category1.getId(),folderDisposition);
String electronicRecord = "RM-2801 electronic record";
Record elRecord = createElectronicRecord(FOLDER_DESTROY.getId(), electronicRecord);
String nonElectronicRecord = "RM-2801 non-electronic record";
Record nonElRecord = createNonElectronicRecord(FOLDER_DESTROY.getId(), nonElectronicRecord);
// complete records and cut them off
String nonElRecordName = recordsAPI.getRecordFullName(getAdminUser().getUsername(),
getAdminUser().getPassword(), folderDisposition, nonElectronicRecord);
String elRecordName = recordsAPI.getRecordFullName(getAdminUser().getUsername(),
getAdminUser().getPassword(), folderDisposition, electronicRecord);
// complete records and cut them off
completeRecord(elRecord.getId());
completeRecord(nonElRecord.getId());
String nonElRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordName, "/" + Category1.getName() + "/" + folderDisposition);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),nonElRecordNameNodeRef);
String elRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordName, "/" + Category1.getName() + "/" + folderDisposition);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),elRecordNameNodeRef);
// ensure the complete event action is displayed for both events
rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(),
getAdminUser().getPassword(), nonElRecordName, RMEvents.ALL_ALLOWANCES_GRANTED_ARE_TERMINATED, Instant.now());
rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(),
getAdminUser().getPassword(), elRecordName, RMEvents.ALL_ALLOWANCES_GRANTED_ARE_TERMINATED, Instant.now());
// Create and Complete transfer
HttpResponse nonElRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordName, "/" + Category1.getName() + "/" + folderDisposition));
String nonElRecordNameTransferId = getTransferId(nonElRecordNameHttpResponse,nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),nonElRecordNameTransferId);
HttpResponse elRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordName, "/" + Category1.getName() + "/" + folderDisposition));
String elRecordNameTransferId = getTransferId(elRecordNameHttpResponse,elRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),elRecordNameTransferId);
// edit the disposition schedule date to current date
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef);
Utility.waitToLoopTime(5,"Waiting for Edit Disposition to be processed");
// destroy records
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),elRecordNameNodeRef);
// delete category
deleteRecordCategory(Category1.getId());
}
private void createTestPrecondition(String categoryName) {
createRMSiteIfNotExists();
// create "rm admin" user if it does not exist and assign it to RM Administrator role
rmRolesAndActionsAPI.createUserAndAssignToRole(
getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),
RM_ADMIN, DEFAULT_PASSWORD, "Administrator");
// create category
STEP("Create two category");
Category1 = createRootCategory(categoryName,"Title");
}
private String getTransferId(HttpResponse httpResponse,String nodeRef) {
HttpEntity entity = httpResponse.getEntity();
String responseString = null;
try {
responseString = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
throw new RuntimeException(e);
}
JSONObject result = new JSONObject(responseString);
return result
.getJSONObject("results")
.get(nodeRef)
.toString();
}
}

View File

@@ -0,0 +1,200 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.core.v0.RMEvents;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
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.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RecordFoldersAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.rest.v0.service.DispositionScheduleService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.Utility;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.Test;
import java.io.IOException;
import java.time.Instant;
import static org.alfresco.rest.rm.community.base.TestData.DEFAULT_PASSWORD;
import static org.alfresco.rest.rm.community.util.CommonTestUtils.generateTestPrefix;
import static org.alfresco.utility.report.log.Step.STEP;
/**
* Contains recordsDispositionScheduleWithGhosting test which checks disposition schedule cut off, transfer and destroy with maintaining record metadata steps applied to records
* <p/>
* Precondition:
* <p/>
* RM site created, contains an empty category "RM-2937 disposition for records with ghosting". <p/>
* RM user has RM admin role. <p/>
* A transfer location named "transferred files with ghosting" is created to which RM user has access
* <p/>
* <img src="doc-files/Disposition Schedule with ghosting.png" alt="Records Disposition Schedule with ghosting" />
*
* @author Kavit Shah
*/
public class RecordsDispositionScheduleWithGhostingTests extends BaseRMRestTest {
/** data prep 6services */
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@Autowired
private RecordsAPI recordsAPI;
@Autowired
private RecordFoldersAPI recordFoldersAPI;
@Autowired
private DispositionScheduleService dispositionScheduleService;
private RecordCategory Category1;
private final String TEST_PREFIX = generateTestPrefix(RecordsDispositionScheduleTests.class);
private final String RM_ADMIN = TEST_PREFIX + "rm_admin";
private final String recordsCategory = TEST_PREFIX + "RM-2801 category";
private final String folderDisposition = TEST_PREFIX + "RM-2801 folder";
@Test
@AlfrescoTest(jira="RM-2801")
public void recordsDispositionScheduleWithGhosting() {
// create test precondition
createTestPrecondition(recordsCategory);
// create disposition schedule
dispositionScheduleService.createCategoryRetentionSchedule(Category1.getName(), true);
// add cut off step
dispositionScheduleService.addCutOffImmediatelyStep(Category1.getName());
// add transfer step
dispositionScheduleService.addTransferAfterEventStep(Category1.getName(),"transferred records","all_allowances_granted_are_terminated");
// add destroy step without retaining metadata
dispositionScheduleService.addDestroyWithGhostingImmediatelyAfterCutOff(Category1.getName());
// create a folder and an electronic and a non-electronic record in it
RecordCategoryChild FOLDER_DESTROY = createFolder(getAdminUser(),Category1.getId(),folderDisposition);
String electronicRecord = "RM-2801 electronic record";
Record elRecord = createElectronicRecord(FOLDER_DESTROY.getId(), electronicRecord);
String nonElectronicRecord = "RM-2801 non-electronic record";
Record nonElRecord = createNonElectronicRecord(FOLDER_DESTROY.getId(), nonElectronicRecord);
// complete records and cut them off
String nonElRecordName = recordsAPI.getRecordFullName(getAdminUser().getUsername(),
getAdminUser().getPassword(), folderDisposition, nonElectronicRecord);
String elRecordName = recordsAPI.getRecordFullName(getAdminUser().getUsername(),
getAdminUser().getPassword(), folderDisposition, electronicRecord);
// complete records and cut them off
completeRecord(elRecord.getId());
completeRecord(nonElRecord.getId());
String nonElRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordName, "/" + Category1.getName() + "/" + folderDisposition);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),nonElRecordNameNodeRef);
String elRecordNameNodeRef = recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordName, "/" + Category1.getName() + "/" + folderDisposition);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","cutoff"),elRecordNameNodeRef);
// ensure the complete event action is displayed for both events
rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(),
getAdminUser().getPassword(), nonElRecordName, RMEvents.ALL_ALLOWANCES_GRANTED_ARE_TERMINATED, Instant.now());
rmRolesAndActionsAPI.completeEvent(getAdminUser().getUsername(),
getAdminUser().getPassword(), elRecordName, RMEvents.ALL_ALLOWANCES_GRANTED_ARE_TERMINATED, Instant.now());
// Create and Complete transfer
HttpResponse nonElRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), nonElRecordName, "/" + Category1.getName() + "/" + folderDisposition));
String nonElRecordNameTransferId = getTransferId(nonElRecordNameHttpResponse,nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),nonElRecordNameTransferId);
HttpResponse elRecordNameHttpResponse = recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transfer"),recordsAPI.getRecordNodeRef(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), elRecordName, "/" + Category1.getName() + "/" + folderDisposition));
String elRecordNameTransferId = getTransferId(elRecordNameHttpResponse,elRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","transferComplete"),elRecordNameTransferId);
// edit the disposition schedule date to current date
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),editDispositionDateJson(),elRecordNameNodeRef);
Utility.waitToLoopTime(5,"Waiting for Edit Disposition to be processed");
// destroy records
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),nonElRecordNameNodeRef);
recordFoldersAPI.postRecordAction(getAdminUser().getUsername(),
getAdminUser().getPassword(),new JSONObject().put("name","destroy"),elRecordNameNodeRef);
// delete category
deleteRecordCategory(Category1.getId());
}
private void createTestPrecondition(String categoryName) {
createRMSiteIfNotExists();
// create "rm admin" user if it does not exist and assign it to RM Administrator role
rmRolesAndActionsAPI.createUserAndAssignToRole(
getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(),
RM_ADMIN, DEFAULT_PASSWORD, "Administrator");
// create category
STEP("Create two category");
Category1 = createRootCategory(categoryName,"Title");
}
private String getTransferId(HttpResponse httpResponse,String nodeRef) {
HttpEntity entity = httpResponse.getEntity();
String responseString = null;
try {
responseString = EntityUtils.toString(entity, "UTF-8");
} catch (IOException e) {
throw new RuntimeException(e);
}
JSONObject result = new JSONObject(responseString);
return result
.getJSONObject("results")
.get(nodeRef)
.toString();
}
}

View File

@@ -0,0 +1,124 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2022 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.smoke;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.record.RecordContent;
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.rules.ActionsOnRule;
import org.alfresco.rest.rm.community.model.rules.RuleDefinition;
import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainer;
import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild;
import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChildProperties;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.rest.v0.RulesAPI;
import org.alfresco.test.AlfrescoTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.alfresco.rest.core.v0.BaseAPI.NODE_PREFIX;
import static org.alfresco.rest.rm.community.base.TestData.ELECTRONIC_RECORD_NAME;
import static org.alfresco.rest.rm.community.base.TestData.NONELECTRONIC_RECORD_NAME;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.*;
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;
import static org.springframework.http.HttpStatus.*;
public class UnfiledRecordsRuleTests extends BaseRMRestTest {
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
private final String TEST_PREFIX = generateTestPrefix(CreateCategoriesTests.class);
private final String RM_ADMIN = TEST_PREFIX + "rm_admin";
private RecordCategory Category2;
private RecordCategoryChild Folder2;
@Autowired
private RulesAPI rulesAPI;
@Test
@AlfrescoTest(jira = "RM-2794")
public void unfiledRecordsRule() {
STEP("Create the RM site if doesn't exist");
createRMSiteIfNotExists();
STEP("Create RM Admin user");
rmRolesAndActionsAPI.createUserAndAssignToRole(getAdminUser().getUsername(), getAdminUser().getPassword(), RM_ADMIN,
getAdminUser().getPassword(),
"Administrator");
STEP("Create record categories and record folders");
Category2 = createRootCategory(getRandomName("recordCategory"));
Folder2 = createFolder(Category2.getId(), getRandomName("recordFolder"));
STEP("Get the unfiled records container");
UnfiledContainer container = getRestAPIFactory().getUnfiledContainersAPI().getUnfiledContainer(UNFILED_RECORDS_CONTAINER_ALIAS);
// Check the response code
assertStatusCode(OK);
//create a rule
RuleDefinition ruleDefinition = RuleDefinition.createNewRule().title("name").description("description")
.applyToChildren(true)
.actions(Collections.singletonList(ActionsOnRule.FILE_TO.getActionValue()));
rulesAPI.createRule(getAdminUser().getUsername(), getAdminUser().getPassword(), NODE_PREFIX + container.getId(), ruleDefinition);
//upload an electronic record
UnfiledContainerChild electronicRecord = UnfiledContainerChild.builder()
.name(ELECTRONIC_RECORD_NAME)
.nodeType(CONTENT_TYPE)
.content(RecordContent.builder().mimeType("text/plain").build())
.build();
assertStatusCode(OK);
// create a non-electronic record
UnfiledContainerChild nonelectronicRecord = UnfiledContainerChild.builder()
.properties(UnfiledContainerChildProperties.builder()
.description(NONELECTRONIC_RECORD_NAME)
.title("Title")
.build())
.name(NONELECTRONIC_RECORD_NAME)
.nodeType(NON_ELECTRONIC_RECORD_TYPE)
.build();
assertStatusCode(OK);
//delete the record created, delete the rule from UnfilledRecord page, delete the category created
rulesAPI.deleteAllRulesOnContainer(getAdminUser().getUsername(), getAdminUser().getPassword(), NODE_PREFIX + container.getId());
deleteRecordCategory(Category2.getId());
assertStatusCode(NO_CONTENT);
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -1,3 +1,3 @@
SOLR6_TAG=2.0.3
POSTGRES_TAG=13.3
ACTIVEMQ_TAG=5.16.1
SOLR6_TAG=2.0.5
POSTGRES_TAG=14.4
ACTIVEMQ_TAG=5.17.1-jre11-rockylinux8

View File

@@ -109,6 +109,10 @@ rm.completerecord.mandatorypropertiescheck.enabled=true
#
rm.patch.v22.convertToStandardFilePlan=false
#
# Max Batch size for adding the associations between the frozen nodes and the hold
rm.patch.v35.holdNewChildAssocPatch.batchSize=1000
# Permission mapping
# these take a comma separated string of permissions from org.alfresco.service.cmr.security.PermissionService
# read maps to ReadRecords and write to FileRecords

View File

@@ -17,5 +17,6 @@
<property name="filePlanService" ref="filePlanService" />
<property name="holdService" ref="holdService" />
<property name="nodeService" ref="nodeService" />
<property name="batchSize" value="${rm.patch.v35.holdNewChildAssocPatch.batchSize}" />
</bean>
</beans>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<properties>
@@ -434,7 +434,7 @@
</run>
</image>
<image>
<name>alfresco/alfresco-activemq:${dependency.activemq.version}</name>
<name>alfresco/alfresco-activemq:${dependency.activemq.version}-jre11-rockylinux8</name>
<run>
<ports>
<port>${activemq.port1}:${activemq.port1}</port>
@@ -505,7 +505,7 @@
</run>
</image>
<image>
<name>alfresco/alfresco-activemq:${dependency.activemq.version}</name>
<name>alfresco/alfresco-activemq:${dependency.activemq.version}-jre11-rockylinux8</name>
<run>
<ports>
<port>${activemq.port1}:${activemq.port1}</port>

View File

@@ -38,9 +38,12 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
import org.alfresco.module.org_alfresco_module_rm.capability.impl.ViewRecordsCapability;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind;
@@ -76,6 +79,7 @@ import org.json.simple.JSONObject;
*
* @author Roy Wetherall
*/
@Slf4j
public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JSONConversionComponent
implements NodeServicePolicies.OnDeleteNodePolicy,
NodeServicePolicies.OnCreateNodePolicy
@@ -515,17 +519,25 @@ public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JS
AuthenticationUtil.runAsSystem((RunAsWork<Void>) () -> {
//Add details of the next incomplete event in the disposition schedule
if (dispositionService.getNextDispositionAction(nodeRef) != null)
DispositionAction nextDispositionAction = dispositionService.getNextDispositionAction(nodeRef);
if (nextDispositionAction != null)
{
for (EventCompletionDetails details : dispositionService.getNextDispositionAction(nodeRef).getEventCompletionDetails())
for (EventCompletionDetails details : nextDispositionAction.getEventCompletionDetails())
{
if (!details.isEventComplete())
{
DispositionActionDefinition dispositionActionDefinition = nextDispositionAction.getDispositionActionDefinition();
HashMap properties = (HashMap) rmNodeValues.get("properties");
properties.put("combineDispositionStepConditions", nodeService.getProperty(dispositionService.getNextDispositionAction(nodeRef).getDispositionActionDefinition().getNodeRef(), PROP_COMBINE_DISPOSITION_STEP_CONDITIONS));
properties.put("incompleteDispositionEvent", details.getEventName());
properties.put("dispositionEventCombination", nodeService.getProperty(dispositionService.getNextDispositionAction(nodeRef).getDispositionActionDefinition().getNodeRef(), PROP_DISPOSITION_EVENT_COMBINATION));
if(dispositionActionDefinition == null)
{
log.debug("Disposition action definition for disposition action "+ nextDispositionAction.getName() +" has been removed or never exist");
}
else
{
properties.put("combineDispositionStepConditions", nodeService.getProperty(dispositionActionDefinition.getNodeRef(), PROP_COMBINE_DISPOSITION_STEP_CONDITIONS));
properties.put("dispositionEventCombination", nodeService.getProperty(dispositionActionDefinition.getNodeRef(), PROP_DISPOSITION_EVENT_COMBINATION));
}
break;
}
}

View File

@@ -30,6 +30,9 @@ import static org.alfresco.model.ContentModel.ASSOC_CONTAINS;
import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel.RM_CUSTOM_URI;
import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.ASSOC_FROZEN_CONTENT;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.alfresco.model.ContentModel;
@@ -37,11 +40,14 @@ import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.hold.HoldService;
import org.alfresco.module.org_alfresco_module_rm.patch.AbstractModulePatch;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Patch to create new hold child association to link the record to the hold
@@ -52,8 +58,15 @@ import org.alfresco.service.namespace.RegexQNamePattern;
*/
public class RMv35HoldNewChildAssocPatch extends AbstractModulePatch
{
/** logger */
protected static final Logger LOGGER = LoggerFactory.getLogger(RMv35HoldNewChildAssocPatch.class);
/** A name for the associations created by this patch. */
protected static final QName PATCH_ASSOC_NAME = QName.createQName(RM_CUSTOM_URI, RMv35HoldNewChildAssocPatch.class.getSimpleName());
protected static final QName PATCH_ASSOC_NAME = QName.createQName(RM_CUSTOM_URI,
RMv35HoldNewChildAssocPatch.class.getSimpleName());
/** The batch size for processing frozen nodes. */
private int batchSize = 1000;
/**
* File plan service interface
@@ -75,7 +88,8 @@ public class RMv35HoldNewChildAssocPatch extends AbstractModulePatch
/**
* Setter for fileplanservice
*
* @param filePlanService File plan service interface
* @param filePlanService
* File plan service interface
*/
public void setFilePlanService(FilePlanService filePlanService)
{
@@ -85,7 +99,8 @@ public class RMv35HoldNewChildAssocPatch extends AbstractModulePatch
/**
* Setter for hold service
*
* @param holdService Hold service interface.
* @param holdService
* Hold service interface.
*/
public void setHoldService(HoldService holdService)
{
@@ -95,7 +110,8 @@ public class RMv35HoldNewChildAssocPatch extends AbstractModulePatch
/**
* Setter for node service
*
* @param nodeService Interface for public and internal node and store operations.
* @param nodeService
* Interface for public and internal node and store operations.
*/
public void setNodeService(NodeService nodeService)
{
@@ -112,33 +128,49 @@ public class RMv35HoldNewChildAssocPatch extends AbstractModulePatch
this.behaviourFilter = behaviourFilter;
}
/**
* Setter for maximum batch size
*
* @param maxBatchSize
* The max amount of associations to be created between the frozen nodes and the hold in a transaction
*/
public void setBatchSize(int batchSize)
{
this.batchSize = batchSize;
}
@Override
public void applyInternal()
{
behaviourFilter.disableBehaviour(ContentModel.ASPECT_AUDITABLE);
behaviourFilter.disableBehaviour(ContentModel.ASPECT_VERSIONABLE);
try
{
int patchedNodesCounter = 0;
for (NodeRef filePlan : filePlanService.getFilePlans())
{
for (NodeRef hold : holdService.getHolds(filePlan))
{
List<ChildAssociationRef> frozenAssoc = nodeService.getChildAssocs(hold, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL);
for (ChildAssociationRef ref : frozenAssoc)
LOGGER.debug("Analyzing hold {}", hold.getId());
BatchWorker batchWorker = new BatchWorker(hold);
LOGGER.debug("Hold has {} items to be analyzed", batchWorker.getWorkSize());
while (batchWorker.hasMoreResults())
{
NodeRef childNodeRef = ref.getChildRef();
// In testing we found that this was returning more than just "contains" associations.
// Possibly this is due to the code in Node2ServiceImpl.getParentAssocs not using the second parameter.
List<ChildAssociationRef> parentAssocs = nodeService.getParentAssocs(childNodeRef, ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
boolean childContainedByHold =
parentAssocs.stream().anyMatch(entry -> entry.getParentRef().equals(hold) && entry.getTypeQName().equals(ASSOC_CONTAINS));
if (!childContainedByHold)
{
nodeService.addChild(hold, childNodeRef, ASSOC_CONTAINS, PATCH_ASSOC_NAME);
}
processBatch(hold, batchWorker);
}
LOGGER.debug("Patched {} items in hold", batchWorker.getTotalPatchedNodes());
patchedNodesCounter += batchWorker.getTotalPatchedNodes();
}
}
LOGGER.debug("Patch applied to {} children across all holds", patchedNodesCounter);
}
finally
{
@@ -146,4 +178,92 @@ public class RMv35HoldNewChildAssocPatch extends AbstractModulePatch
behaviourFilter.enableBehaviour(ContentModel.ASPECT_VERSIONABLE);
}
}
private void processBatch(NodeRef hold, BatchWorker batch)
{
transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
Collection<ChildAssociationRef> childRefs = batch.getNextWork();
LOGGER.debug("Processing batch of {} children in hold", childRefs.size());
for (ChildAssociationRef child : childRefs)
{
NodeRef childNodeRef = child.getChildRef();
if (!isChildContainedByHold(hold, childNodeRef))
{
nodeService.addChild(hold, childNodeRef, ASSOC_CONTAINS, PATCH_ASSOC_NAME);
batch.countPatchedNode();
}
}
return null;
}, false, true);
}
private boolean isChildContainedByHold(NodeRef hold, NodeRef child)
{
// In testing we found that this was returning more than just "contains" associations.
// Possibly this is due to the code in Node2ServiceImpl.getParentAssocs not using the second
// parameter.
List<ChildAssociationRef> parentAssocs = nodeService.getParentAssocs(child, ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
return parentAssocs.stream()
.anyMatch(entry -> entry.getParentRef().equals(hold) && entry.getTypeQName().equals(ASSOC_CONTAINS));
}
private class BatchWorker
{
NodeRef hold;
int totalPatchedNodes = 0;
int workSize;
Iterator<ChildAssociationRef> iterator;
public BatchWorker(NodeRef hold)
{
this.hold = hold;
setupHold();
}
public boolean hasMoreResults()
{
return iterator == null ? true : iterator.hasNext();
}
public void countPatchedNode()
{
this.totalPatchedNodes += 1;
}
public int getTotalPatchedNodes()
{
return totalPatchedNodes;
}
public int getWorkSize()
{
return workSize;
}
public void setupHold()
{
// Get child assocs without preloading
List<ChildAssociationRef> holdChildren = nodeService.getChildAssocs(hold, ASSOC_FROZEN_CONTENT,
RegexQNamePattern.MATCH_ALL, Integer.MAX_VALUE, false);
this.iterator = holdChildren.listIterator();
this.workSize = holdChildren.size();
}
public Collection<ChildAssociationRef> getNextWork()
{
List<ChildAssociationRef> frozenNodes = new ArrayList<ChildAssociationRef>(batchSize);
while (iterator.hasNext() && frozenNodes.size() < batchSize)
{
frozenNodes.add(iterator.next());
}
return frozenNodes;
}
}
}

View File

@@ -156,25 +156,24 @@ public class ExtendedRuleServiceImpl extends RuleServiceImpl
* @see org.alfresco.repo.rule.RuleServiceImpl#saveRule(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.rule.Rule)
*/
@Override
public void saveRule(final NodeRef nodeRef, final Rule rule)
public Rule saveRule(final NodeRef nodeRef, final Rule rule)
{
validateWormLockRuleAction(rule);
if (filePlanService.isFilePlanComponent(nodeRef))
{
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
return AuthenticationUtil.runAsSystem(new RunAsWork<Rule>()
{
@Override
public Void doWork()
public Rule doWork()
{
ExtendedRuleServiceImpl.super.saveRule(nodeRef, rule);
return null;
return ExtendedRuleServiceImpl.super.saveRule(nodeRef, rule);
}
});
}
else
{
super.saveRule(nodeRef, rule);
return super.saveRule(nodeRef, rule);
}
}

View File

@@ -82,13 +82,7 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
public void given()
{
filePlan = createFilePlan();
// create a rule that applies to childre
Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
rule = new Rule();
rule.setRuleType("inbound");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
rule = createRuleThatAppliesToChildren();
}
public void when()
@@ -127,13 +121,7 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
public void given()
{
filePlan = createFilePlan();
// create a rule that applies to childre
Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
rule = new Rule();
rule.setRuleType("inbound");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
rule = createRuleThatAppliesToChildren();
}
public void when()
@@ -171,13 +159,7 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
public void given()
{
filePlan = createFilePlan();
// create a rule that applies to childre
Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
rule = new Rule();
rule.setRuleType("inbound");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
rule = createRuleThatAppliesToChildren();
}
public void when()
@@ -215,13 +197,7 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
public void given()
{
filePlan = createFilePlan();
// create a rule that applies to childre
Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
rule = new Rule();
rule.setRuleType("inbound");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
rule = createRuleThatAppliesToChildren();
}
public void when()
@@ -261,13 +237,7 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
{
filePlan = createFilePlan();
recordCategory = filePlanService.createRecordCategory(filePlan, GUID.generate());
// create a rule that applies to childre
Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
rule = new Rule();
rule.setRuleType("inbound");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
rule = createRuleThatAppliesToChildren();
}
public void when()
@@ -286,4 +256,15 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
}
});
}
private Rule createRuleThatAppliesToChildren()
{
Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
Rule rule = new Rule();
rule.setRuleType("inbound");
rule.setTitle("Rule name");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
return rule;
}
}

View File

@@ -4,7 +4,7 @@
# Version label
version.major=7
version.minor=3
version.minor=4
version.revision=0
version.label=

View File

@@ -34,7 +34,9 @@ import static org.alfresco.model.ContentModel.ASSOC_CONTAINS;
import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.ASSOC_FROZEN_CONTENT;
import static org.alfresco.module.org_alfresco_module_rm.patch.v35.RMv35HoldNewChildAssocPatch.PATCH_ASSOC_NAME;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -51,16 +53,21 @@ import java.util.Set;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.hold.HoldService;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.alfresco.service.transaction.TransactionService;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/**
* RM V3.5 Create new hold child association to link the record to the hold
@@ -81,6 +88,12 @@ public class RMv35HoldNewChildAssocPatchUnitTest
@Mock
private BehaviourFilter mockBehaviourFilter;
@Mock
private TransactionService mockTransactionService;
@Mock
private RetryingTransactionHelper mockRetryingTransactionHelper;
@InjectMocks
private RMv35HoldNewChildAssocPatch patch;
@@ -112,25 +125,63 @@ public class RMv35HoldNewChildAssocPatchUnitTest
/**
* Test secondary associations are created for held items so that they are "contained" in the hold.
*/
@SuppressWarnings("unchecked")
@Test
public void testAddChildDuringUpgrade()
{
when(mockFilePlanService.getFilePlans()).thenReturn(fileplans);
when(mockHoldService.getHolds(filePlanRef)).thenReturn(holds);
when(mockNodeService.getChildAssocs(holdRef, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL)).thenReturn(childAssocs);
when(mockNodeService.getChildAssocs(holdRef, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL, Integer.MAX_VALUE, false))
.thenReturn(childAssocs);
when(childAssociationRef.getChildRef()).thenReturn(heldItemRef);
// setup retrying transaction helper
Answer<Object> doInTransactionAnswer = new Answer<Object>()
{
@SuppressWarnings("rawtypes")
@Override
public Object answer(InvocationOnMock invocation) throws Throwable
{
RetryingTransactionCallback callback = (RetryingTransactionCallback) invocation.getArguments()[0];
// when(childAssociationRef.getChildRef()).thenReturn(heldItemRef);
return callback.execute();
}
};
doAnswer(doInTransactionAnswer).when(mockRetryingTransactionHelper)
.<Object> doInTransaction(any(RetryingTransactionCallback.class), anyBoolean(), anyBoolean());
when(mockTransactionService.getRetryingTransactionHelper()).thenReturn(mockRetryingTransactionHelper);
patch.applyInternal();
verify(mockNodeService, times(1)).addChild(holdRef, heldItemRef, ASSOC_CONTAINS, PATCH_ASSOC_NAME);
}
@SuppressWarnings("unchecked")
@Test
public void patchRunWithSuccessWhenNoHeldChildren()
{
when(mockFilePlanService.getFilePlans()).thenReturn(fileplans);
when(mockHoldService.getHolds(filePlanRef)).thenReturn(holds);
when(mockNodeService.getChildAssocs(holdRef, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL)).thenReturn(emptyList());
when(mockNodeService.getChildAssocs(holdRef, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL, Integer.MAX_VALUE, false))
.thenReturn(emptyList());
// setup retrying transaction helper
Answer<Object> doInTransactionAnswer = new Answer<Object>()
{
@SuppressWarnings("rawtypes")
@Override
public Object answer(InvocationOnMock invocation) throws Throwable
{
RetryingTransactionCallback callback = (RetryingTransactionCallback) invocation.getArguments()[0];
when(childAssociationRef.getChildRef()).thenReturn(heldItemRef);
return callback.execute();
}
};
doAnswer(doInTransactionAnswer).when(mockRetryingTransactionHelper)
.<Object> doInTransaction(any(RetryingTransactionCallback.class), anyBoolean(), anyBoolean());
when(mockTransactionService.getRetryingTransactionHelper()).thenReturn(mockRetryingTransactionHelper);
patch.applyInternal();

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<build>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<properties>

View File

@@ -11,7 +11,7 @@ function main()
var params =
{
type: "people",
term: args.t,
term: args.t + " [hint:useCQ]",
maxResults: (args.maxResults !== null) ? parseInt(args.maxResults, 10) : DEFAULT_MAX_RESULTS,
startIndex: (args.startIndex !== null) ? parseInt(args.startIndex, 10) : 0
};

View File

@@ -14,7 +14,7 @@ function main()
maxResults: (args.maxResults !== null) ? parseInt(args.maxResults, 10) : DEFAULT_MAX_RESULTS,
pageSize: (args.pageSize !== null) ? parseInt(args.pageSize, 10) : DEFAULT_PAGE_SIZE,
startIndex: (args.startIndex !== null) ? parseInt(args.startIndex, 10) : 0,
facetFields: args.facetFields,
facetFields: args.facetFields !== null ? args.facetFields.replace( /(<([^>]+)>)/ig, '') : null,
filters: args.filters,
encodedFilters: args.encodedFilters,
spell: (args.spellcheck !== null) ? (args.spellcheck == "true") : false

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2005 - 2020 Alfresco Software Limited.
* Copyright 2005 - 2022 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.
@@ -40,6 +40,7 @@ import org.json.JSONObject;
import org.junit.Assert;
import org.springframework.extensions.webscripts.TestWebScriptServer;
import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
import java.io.Serializable;
import java.util.ArrayList;
@@ -194,13 +195,13 @@ public class SlingshotContentGetTest extends BaseWebScriptTest
NodeRef rootFolder = createNode(companyHome, "rootFolder", ContentModel.TYPE_FOLDER);
NodeRef doc1 = createNodeWithTextContent(rootFolder, "doc1", ContentModel.TYPE_CONTENT, "doc1 file content");
NodeRef doc1 = createNodeWithTextContent(rootFolder, "doc1", ContentModel.TYPE_CONTENT, "doc1 file content", MimetypeMap.MIMETYPE_TEXT_PLAIN);
NodeRef folderX = createNode(rootFolder, "X", ContentModel.TYPE_FOLDER);
NodeRef folderY = createNode(folderX, "Y", ContentModel.TYPE_FOLDER);
NodeRef folderZ = createNode(folderY, "Z", ContentModel.TYPE_FOLDER);
NodeRef doc2 = createNodeWithTextContent(folderZ, "doc2", ContentModel.TYPE_CONTENT, "doc2 file content");
NodeRef doc2 = createNodeWithTextContent(folderZ, "doc2", ContentModel.TYPE_CONTENT, "doc2 file content", MimetypeMap.MIMETYPE_TEXT_PLAIN);
// uri with relative path at the end
String uri = URL_CONTENT_DOWNLOAD + doc1.getId() + "/X/Y/Z/doc2";
@@ -212,7 +213,50 @@ public class SlingshotContentGetTest extends BaseWebScriptTest
nodeService.deleteNode(rootFolder);
}
public NodeRef createNodeWithTextContent(NodeRef parentNode, String nodeCmName, QName nodeType, String content)
public void testForcedAttachment() throws Exception
{
Repository repositoryHelper = (Repository) getServer().getApplicationContext().getBean("repositoryHelper");
NodeRef companyHome = repositoryHelper.getCompanyHome();
NodeRef rootFolder = createNode(companyHome, "rootFolder", ContentModel.TYPE_FOLDER);
NodeRef testhtml = createNodeWithTextContent(rootFolder, "testhtml", ContentModel.TYPE_CONTENT, "testhtml content", MimetypeMap.MIMETYPE_HTML);
NodeRef testpdf = createNodeWithTextContent(rootFolder, "testpdf", ContentModel.TYPE_CONTENT, "testpdf content", MimetypeMap.MIMETYPE_PDF);
String uri = URL_CONTENT_DOWNLOAD + testhtml.getId() + "?a=false";
GetRequest req = new GetRequest(uri);
Response res = sendRequest(req, 200);
assertEquals("attachment", res.getHeader("Content-Disposition"));
assertEquals(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8", res.getContentType());
uri = URL_CONTENT_DOWNLOAD + testhtml.getId();
res = sendRequest(new GetRequest(uri), 200);
assertEquals("attachment", res.getHeader("Content-Disposition"));
assertEquals(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8", res.getContentType());
uri = URL_CONTENT_DOWNLOAD + testhtml.getId() + "?a=true";
res = sendRequest(new GetRequest(uri), 200);
assertEquals("attachment", res.getHeader("Content-Disposition"));
assertEquals(MimetypeMap.MIMETYPE_HTML + ";charset=UTF-8", res.getContentType());
uri = URL_CONTENT_DOWNLOAD + testpdf.getId() + "?a=false";
res = sendRequest(new GetRequest(uri), 200);
assertNull(res.getHeader("Content-Disposition"));
assertEquals(MimetypeMap.MIMETYPE_PDF + ";charset=UTF-8", res.getContentType());
uri = URL_CONTENT_DOWNLOAD + testpdf.getId();
res = sendRequest(new GetRequest(uri), 200);
assertNull(res.getHeader("Content-Disposition"));
assertEquals(MimetypeMap.MIMETYPE_PDF + ";charset=UTF-8", res.getContentType());
uri = URL_CONTENT_DOWNLOAD + testpdf.getId() + "?a=true";
res = sendRequest(new GetRequest(uri), 200);
assertEquals("attachment", res.getHeader("Content-Disposition"));
assertEquals(MimetypeMap.MIMETYPE_PDF + ";charset=UTF-8", res.getContentType());
nodeService.deleteNode(rootFolder);
}
public NodeRef createNodeWithTextContent(NodeRef parentNode, String nodeCmName, QName nodeType, String content, String mimetype)
{
NodeRef nodeRef = createNode(parentNode, nodeCmName, nodeType);
@@ -220,7 +264,7 @@ public class SlingshotContentGetTest extends BaseWebScriptTest
if (content != null)
{
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN);
writer.setMimetype(mimetype);
writer.setEncoding("UTF-8");
writer.putContent(content);
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<dependencies>

View File

@@ -25,6 +25,9 @@ package org.alfresco.error;
*/
public class ExceptionStackUtil
{
private static final String JAVASCRIPT_EXCEPTION = "org.mozilla.javascript.JavaScriptException";
private static final String EXCEPTION_DELIMITER = ":";
/**
* Searches through the exception stack of the given throwable to find any instance
* of the possible cause. The top-level throwable will also be tested.
@@ -38,10 +41,17 @@ public class ExceptionStackUtil
{
while (throwable != null)
{
Class<?> throwableClass = throwable.getClass();
boolean isJavaScriptException = throwableClass.getName().contains(JAVASCRIPT_EXCEPTION);
String throwableMsg = throwable.getMessage() != null ? throwable.getMessage() : "";
for (Class<?> possibleCauseClass : possibleCauses)
{
Class<?> throwableClass = throwable.getClass();
if (possibleCauseClass.isAssignableFrom(throwableClass))
String possibleCauseClassName = possibleCauseClass.getName();
if (possibleCauseClass.isAssignableFrom(throwableClass)
|| (isJavaScriptException && throwableMsg.contains(possibleCauseClassName + EXCEPTION_DELIMITER)))
{
// We have a match
return throwable;

View File

@@ -21,6 +21,7 @@ package org.alfresco.httpclient;
import java.io.IOException;
import java.util.Map;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
@@ -57,7 +58,12 @@ public class RequestHeadersHttpClient extends HttpClient
if (defaultHeaders != null)
{
defaultHeaders.forEach((k,v) -> {
method.addRequestHeader(k, v);
Header h = method.getRequestHeader(k);
boolean add = h == null || h.getValue() == null || !h.getValue().equals(v);
if (add)
{
method.addRequestHeader(k, v);
}
});
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<properties>
@@ -134,7 +134,7 @@
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<version>6.3.0</version>
<version>6.3.1</version>
</dependency>
<!-- the cxf libs were updated, see dependencyManagement section -->

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<dependencies>

View File

@@ -9,6 +9,6 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
</project>

View File

@@ -1,6 +1,6 @@
# Fetch image based on Tomcat 9.0, Java 11 and Centos 7
# Fetch image based on Tomcat 9.0, Java 17 and Rocky Linux 8
# More infos about this image: https://github.com/Alfresco/alfresco-docker-base-tomcat
FROM alfresco/alfresco-base-tomcat:tomcat9-jre11-centos7-202203091924
FROM alfresco/alfresco-base-tomcat:tomcat9-jre17-rockylinux8-202209261711
# Set default docker_context.
ARG resource_path=target
@@ -65,12 +65,12 @@ RUN sed -i -e "s_log4j.appender.File.File\=alfresco.log_log4j.appender.File.File
# fontconfig is required by Activiti worflow diagram generator
# installing pinned dependencies as well
RUN yum install -y fontconfig-2.13.0-4.3.el7 \
dejavu-fonts-common-2.33-6.el7 \
fontpackages-filesystem-1.44-8.el7 \
freetype-2.8-14.el7_9.1 \
libpng-1.5.13-8.el7 \
dejavu-sans-fonts-2.33-6.el7 && \
RUN yum install -y fontconfig-2.13.1-4.el8 \
dejavu-fonts-common-2.35-7.el8 \
fontpackages-filesystem-1.44-22.el8 \
freetype-2.9.1-4.el8_3.1 \
libpng-1.6.34-5.el8 \
dejavu-sans-fonts-2.35-7.el8 && \
yum clean all
# The standard configuration is to have all Tomcat files owned by root with group GROUPNAME and whilst owner has read/write privileges,

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -1,3 +1,3 @@
SOLR6_TAG=2.0.3
POSTGRES_TAG=13.3
ACTIVEMQ_TAG=5.16.1
SOLR6_TAG=2.0.5
POSTGRES_TAG=14.4
ACTIVEMQ_TAG=5.17.1-jre11-rockylinux8

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -0,0 +1,12 @@
#!/usr/bin/env bash
TAS_DIRECTORY=$1
cd ${TAS_DIRECTORY}
failures=$(grep 'status="FAIL"' target/surefire-reports/testng-results.xml | sed 's|^.*[ ]name="\([^"]*\)".*$|\1|g')
for failure in ${failures}
do
cat target/reports/alfresco-tas.log | sed '/STARTING Test: \['${failure}'\]/,/ENDING Test: \['${failure}'\]/!d;/ENDING Test: \['${failure}'\]/q'
done

View File

@@ -0,0 +1,495 @@
![in progress](https://img.shields.io/badge/Document_Level-In_Progress-yellow.svg?style=flat-square)
:paw_prints: Back to [TAS Master Documentation](https://gitlab.alfresco.com/tas/documentation/wikis/home)
---
## Table of Contents
* [Synopsis](#synopsis)
* [Prerequisite](#prerequisite)
* [Installation](#installation-if-you-want-to-contribute)
* [Package Presentation](#package-presentation)
* [Sample Usage](#sample-usage)
* [How to write a test](#how-to-write-a-test)
* [How to run tests?](#how-to-run-tests)
* [from IDE](#from-ide)
* [from command line](#from-command-line)
* [Perform CMIS Queries](#perform-cmis-queries)
* [Listeners](#listeners)
* [Test Results](#test-results)
* [Test Rail Integration](#test-rail-integration)
* [Configuration](#configuration)
* [How to enable Test Rail Integration?](#how-to-enable-test-rail-integration)
* [Change Log](docs/CHANGELOG.md) :glowing_star:
* [Reference](#reference)
* [Releasing](#releasing)
* [Contributors](#contributors)
* [License](#license)
## Synopsis
**TAS**( **T**est **A**utomation **S**ystem)- **CMIS** is the project that handles the automated tests related only to CMIS API integrated with Alfresco One [Alfresco CMIS API](http://docs.alfresco.com/5.1/pra/1/topics/cmis-welcome.html).
It is based on Apache Maven, compatible with major IDEs and is using also Spring capabilities for dependency injection.
As a high level overview, this project makes use of the following functionality useful in automation testing as:
* reading/defining test environment settings (e.g. alfresco server details, authentication, etc.)
* managing resource (i.e. creating files and folders)
* test data generators (for site, users, content, etc)
* helpers (i.e. randomizers, test environment information)
* test logging generated on runtime and test reporting capabilities
* test management tool integration (at this point we support integration with [Test Rail](https://alfresco.testrail.net) (v5.2.1)
* health checks (verify if server is reachable, if server is online)
* generic Internal-DSL (Domain Specific Language)
Using Nexus -Release Repository, everyone will be able to use individual interfaces in their projects by extending the automation core functionalities.
**[Back to Top ^](#table-of-contents)**
## Prerequisite
(tested on unix/non-unix distribution)
* [Java SE 1.8](http://www.oracle.com/technetwork/java/javase/downloads/index.html).
* [Maven 3.3](https://maven.apache.org/download.cgi) installed and configure according to [Windows OS](https://maven.apache.org/guides/getting-started/windows-prerequisites.html) or [Mac OS](https://maven.apache.org/install.html).
* Configure Maven to use Alfresco alfresco-internal repository following this [Guide](https://ts.alfresco.com/share/page/site/eng/wiki-page?title=Maven_Setup).
* Your favorite IDE as [Eclipse](https://eclipse.org/downloads/) or [IntelliJ](https://www.jetbrains.com/idea).
* Access to [Nexus](https://nexus.alfresco.com/nexus/) repository.
* Access to GitLab [TAS](https://gitlab.alfresco.com/tas/) repository.
* GitLab client for your operating system. (we recommend [SourceTree](https://www.sourcetreeapp.com) - use your google account for initial setup).
* Getting familiar with [Basic Git Commands](http://docs.gitlab.com/ee/gitlab-basics/basic-git-commands.html).
* Getting familiar with [Maven](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html).
* Getting familiar with [Spring](http://docs.spring.io).
* Getting familiar with [TestNG](http://testng.org/doc/index.html)
**[Back to Top ^](#table-of-contents)**
## Installation (if you want to contribute)
* Open your GitLab client and clone the repository of this project.
* You can do this also from command line (or in your terminal) adding:
```bash
$ git clone https://gitlab.alfresco.com/tas/alfresco-tas-cmis-test.git
# this clone will have the latest changes from repository. If you want to checkout a specific version released, take a look at the [Change Log](docs/CHANGELOG.md) page
$ cd alfresco-tas-cmis-test
# this command will checkout the remove v1.0.0 tagged repository and create locally a new branch v1.0.0
$ git checkout tags/v1.0.0 -b v1.0.0
```
* Install and check if all dependencies are downloaded
```bash
$ mvn clean install -DskipTests
# you should see one [INFO] BUILD SUCCESS message displayed
```
**[Back to Top ^](#table-of-contents)**
## Package Presentation
The project uses a maven layout [archetype](https://maven.apache.org/plugins-archives/maven-archetype-plugin-1.0-alpha-7/examples/simple.html):
```ruby
├── pom.xml
├── src
   ├── main
      └── java
      └── org
      └── alfresco
      └── cmis
      ├── (...)
      ├── CmisProperties.java #handles all properties from default.properties
      ├── CmisWrapper.java #wrapper around CMIS API
      └── exception
      └── (...)
   ├── test
      ├── java
         └── org
         └── alfresco
         └── cmis
         ├── CmisDemoTests.java #demo example
         └── CmisTest.java #abstract base class that should be inherited by all tests
      └── resources
      ├── alfresco-cmis-context.xml #spring configuration
      ├── default.properties #all settings related to environment, protocol
      ├── log4j.properties
      └── sanity-cmis.xml # default suite of tests
```
**[Back to Top ^](#table-of-contents)**
## Sample Usage
Following the standard layout for Maven projects, the application sources locate in src/main/java and test sources locate in src/test/java.
Application sources consist in defining the CMIS object that simulates the API calls.
The tests are based on an abstract object: CmisTest.java that handles the common behavior: checking the health status of the test server, configuration settings, getting the general properties, etc.
Please take a look at [CmisDemoTests.java](src/test/java/org/alfresco/cmis/CmisDemoTests.java) class for an example.
Common configuration settings required for this project are stored in properties file, see [default.properties](src/test/resources/default.properties).
Please analyze and update it accordingly with Alfresco test server IP, port, credentials, etc.
Example:
```java
# Alfresco HTTP Server Settings
alfresco.scheme=http
alfresco.server=<add-here-the-ip-of-your-test-server>
alfresco.port=<default-port-for-alfresco-not-share>
```
* optional update the logging level in [log4j](src/test/resources/log4j.properties) file (you can increase/decrease the deails of the [logging file](https://logging.apache.org/log4j/1.2/manual.html), setting the ```log4j.rootLogger=DEBUG``` if you want.)
* go to [running](#how-to-run-tests) section for more information on how to run this tests.
**[Back to Top ^](#table-of-contents)**
### How to write a test
* Tests are organized in java classes and located on src/test/java as per maven layout.
* One test class should contain the tests that cover one functionality as we want to have a clear separation of test scope: tests for sanity/core/full, tests that verify manage of folder/files etc.
* These are the conventions that need to follow when you write a test:
* The test class has @Test annotation with the group defined: protocols, cmis. You can add more groups like sanity, regression
```java
@Test(groups={ "sanity"}
```
* The test has @TestRail annotation in order to assure that the details and results will be submitted on TestRail. The fields for TestRail annotation will be explained on next chapter.
```java
@TestRail(section = { "cmis-api" }, executionType=ExecutionType.SANITY,
description = "Verify admin user creates folder in DocumentLibrary with CMIS")
public void adminShouldCreateFolderInSite() throws Exception
{ cmisApi.usingSite(testSite).createFolder(testFolder).assertExistsInRepo(); }
```
* Use Spring capabilities to initialize the objects(Models, Wrappers) with @Autowired
* We followed Builder pattern to develop specific DSL for simple and clear usage of protocol client in test:
```java
cmisApi.usingSite(testSite) .createFolder(testFolder) .assertExistsInRepo();
```
* To view a simple class that is using this utility, just browse on [CmisDemoTests.java](src/test/java/org/alfresco/cmis/CmisDemoTests.java)
Notice the class definition and inheritance value:
```java
public class CmisDemoTests extends CmisTest
```
* as a convention, before running your test, check if the test environment is reachable and your alfresco test server is online.
(this will stop the test if the server defined in your property file is not healthy - method available in parent class)
```java
@BeforeClass(alwaysRun = true)
public void setupCmisTest() throws Exception {
serverHealth.assertServerIsOnline();
}
```
* the test name are self explanatory:
```java
@TestRail(section = { "cmis-api" }, executionType=ExecutionType.SANITY, description = "Verify admin user creates folder in DocumentLibrary with CMIS")
public void adminShouldCreateFolderInSite() throws Exception
{
cmisApi.usingSite(testSite)
.createFolder(testFolder)
.assertExistsInRepo();
}
```
```java
@TestRail(section = { "cmis-api" }, executionType=ExecutionType.SANITY, description = "Verify admin user creates and renames folder in DocumentLibrary with CMIS")
public void adminShouldRenameFolderInSite() throws Exception
{
cmisApi.usingSite(testSite).createFolder(testFolder)
.and().rename("renamed")
.assertExistsInRepo();
}
```
**[Back to Top ^](#table-of-contents)**
### How to run tests
#### from IDE
* The project can be imported into a development environment tool (Eclipse or IntelliJ). You have the possibility to execute tests or suite of tests using [TestNG plugin](http://testng.org/doc/eclipse.html) previously installed in IDE.
From Eclipse, just right click on the testNG class (something similar to [CmisDemoTests.java](src/test/java/org/alfresco/cmis/CmisDemoTests.java)), select Run As - TestNG Test
You should see your test passed.
* In case you are using the default settings that points to localhost (127.0.0.1) and you don't have Alfresco installed on your machine, you will see one exception thrown (as expected):
```java
org.alfresco.utility.exception.ServerUnreachableException: Server {127.0.0.1} is unreachable.
```
#### from command line
* In terminal or CMD, navigate (with CD) to root folder of your project (you can use the sample project):
The tests can be executed on command line/terminal using Maven command
```bash
mvn test
```
This command with trigger the tests specified in the default testNG suite from POM file: <suiteXmlFile>src/main/resources/shared-resources/cmis-suites.xml</suiteXmlFile>
You can use -Dtest parameter to run the test/suites through command line (http://maven.apache.org/surefire/maven-surefire-plugin/examples/single-test.html).
You can also specify a different suiteXMLFile like:
```bash
mvn test -DsuiteXmlFile=src/resources/your-custom-suite.xml
```
Or even a single test:
```bash
mvn test -Dtest=org.alfresco.cmis.CmisDemoTests
```
But pay attention that you will not have enabled all the [listeners](#listeners) in this case (the Reporting listener or TestRail integration one)
### Perform CMIS Queries
(:glowing_star: please notice that at this point we assert only the results count returned by the query: we plan to extend the functionality to assert on QueryResult iterable objects also: simple modification on [QueryExecutor.java](src/main/java/org/alfresco/cmis/dsl/QueryExecutor.java)
There are a couple of ways to test the results count after performing CMIS queries, choose the one that you like the most:
a) direct queries using a simple TestNG test:
(see example [here](src/test/java/org/alfresco/cmis/search/SorlSearchSimpleQueryTests.java))
```java
public class SorlSearchSimpleQueryTests extends CmisTest
{
@Test
public void simpleQueryOnFolderDesc() throws Exception
{
// create here multiple folder as data preparation
cmisApi.authenticateUser(dataUser.getAdminUser())
.withQuery("SELECT * FROM cmis:folder ORDER BY cmis:createdBy DESC").assertResultsCount().isLowerThan(101);
}
}
```
- just extend CmisTest
- authenticate with your UserModel and perform the query. The DSL will allow you to assert the result count if is equal, lower or greater than to a particular value. You can update the methods in [QueryResultAssertion](src/main/java/org/alfresco/cmis/dsl/QueryExecutor.java) class.
b) define one set of test data (folders, files, etc. ) that you will search in all tests then execute all CMIS queris from one common XML file
- see test class [SolrSearchInFolderTests](src/test/java/org/alfresco/cmis/search/SolrSearchInFolderTests.java)
- see [XML test data](src/main/resources/shared-resources/testdata/search-in-folder.xml) used in [SolrSearchInFolderTests](src/test/java/org/alfresco/cmis/search/SolrSearchInFolderTests.java) into one DataProvider. Notice that XML file has two parameter: the query that will be executed and the expected result count returned.
c) define test data (user, sites, folder, files, aspects, comments, custom models, etc) all into one XML file with all cmis queries related.
- see example on [SolrSearchByIdTests](https://gitlab.alfresco.com/tas/alfresco-tas-cmis-test/blob/master/src/test/java/org/alfresco/cmis/search/SolrSearchByIdTests.java)
- notice the 'NODE_REF[x]'; 'NODE_REF[y]' keywords that will dynamically take the test data identified by id: x, y (you will figure it out based on examples).
**Info**: all search test queries are found [org.alfresco.cmis.search](src/test/java/org/alfresco/cmis/search) package.
**[Back to Top ^](#table-of-contents)**
## Listeners
With the help of Listeners we can modify the behaviour of TestNG framework. There are a lot of testNG listener interfaces that we can override in order to provide new functionalities.
The tas framework provides out of the box a couple of listeners that you could use. These could be enabled and added at the class level or suite level.
### a) org.alfresco.utility.report.ReportListenerAdapter
* if added at the class level:
```java
@Listeners(value=ReportListenerAdapter.class)
public class MyTestClass extends CmisTest
{
(...)
}
```
* or suite xml level
```java
<suite name="Your Suite test" parallel="classes">
<listeners>
<listener class-name="org.alfresco.utility.report.ReportListenerAdapter"></listener>
</listeners>
(...)
</suite>
```
It will automatically generate one html named "report.html" in ./target/report folder.
Please also take a look at [Test Results](#test-results) section.
### b) org.alfresco.utility.testrail.TestRailExecutorListener
It will automatically update Test Rail application with the test cases that you've automated.
Please take a look at [Test Rail Integration](#test-rail-integration) section for more details.
### c) org.alfresco.utility.report.log.LogsListener
This is a new listener that will generate further details in one XML format of the automated test steps that you will write.
Example:
```java
public void myDSLMethod1()
{
STEP("Lorem ipsum dolor sit amet");
//code for first step
STEP("consectetur adipiscing elit");
//code for the next description
}
public void myDSLMethod2()
{
STEP("sed do eiusmod tempor incididunt ut labore");
//code for first step
STEP("et dolore magna aliqua");
//code for the next description
}
```
If these methods will be executed insite a test method, all those steps will be automatically logged in the XML report generated.
Example:
```java
@Test
public void adminShouldCreateFileInSite()
{
myDSLMethod1();
myDSLMethod2()
}
```
So if "testingSomething" will be executed this is what you will see on the XML file generated. (please take a look at [Test Results](#test-results) section for defining the defaul location)
Here is one example of XML file generated with these steps:
![](docs/pics/xml-steps-report.JPG)
**[Back to Top ^](#table-of-contents)**
## Test Results
We already executed a couple of tests using command line as indicated above. Sweet! Please take a look at [sanity-cmis.xml](src/test/resources/sanity-cmis.xml) one more time.
You will see there that we have one listener added:
```java
<listener class-name="org.alfresco.utility.report.ReportListenerAdapter"></listener>
```
This will tell our framework, after we run all tests, to generate one HTML report file with graphs and metrics.
Take a look at the target/reports folder (created after running the tests) and open the report.html file.
![](docs/pics/html-report-sample.JPG)
Playing with this report, you will notice that you will be able to:
* search tests cases by name
* filter test cases by errors, labels, groups, test types, date when it was executed, protocol used, etc.
* view overall pass/fail metrics of current test suite, history of tests execution, etc.
The report path can be configured in default.properties):
```
# The location of the reports path
reports.path=your-new-location-of-reports
```
**[Back to Top ^](#table-of-contents)**
## Test Rail Integration
Alfresco is using now https://alfresco.testrail.net (v5.3.0.3601).
We aim to accelerate the delivery of automated test by minimizing the interaction with the test management tool - TestRail. In this scope we developed the following capabilities:
* creating automatically the manual tests in TestRail
* submitting the test results (with stack trace) after each execution into TestRail Test Runs
* adding the test steps for each test.
### Configuration
In order to use Test Rail Integration you will need to add a couple of information in [default.properties](src/test/resources/default.properties) file:
(the document is pretty self explanatory)
```java
# Example of configuration:
# ------------------------------------------------------
# testManagement.endPoint=https://alfresco.testrail.com/
# testManagement.username=<yourusername-that-you-connect-to-testrail>
# testManagement.apiKey=<api-key>
# testManagement.project=<id-of-your-project
# testManagement.testRun=<test-run-name>
```
!This settings are already defined in default.properties for you.
For generating a new API Key take a look at the official documentation, TestRail [APIv2](http://docs.gurock.com/testrail-api2)
* _testManagement.project= **<id-of-your-project**_ this is the ID of the project where you want to store your test cases.
If you want to use [Alfresco ONE](https://alfresco.testrail.net/index.php?/projects/overview/1) project in TestRail, open that project and notice the URL, after "/overview/**1**" link you will see the ID of the project (1 in this case)
If you want to use [TAS Project](https://alfresco.testrail.net/index.php?/projects/overview/7) you will notice the ID 7, so _"testManagement.project=7"_
* "_testManagement.testRun=<test-run-name>_" this represents the name of the Test Run from your project.
* In Test Rail, navigating to Test Runs & Results, create a new Test Run and include all/particular test cases. If this test run name is "Automation", update _testManagement.testRun= **Automation**_.
All test results will be updated only on this test run at runtime as each test is executed by TAS framework.
### How to enable Test Rail Integration?
We wanted to simplify the Test Rail integration, so we used listeners in order to enable/disable the integration of Test Rail.
* first configure your default.properties as indicated above
* now on your TestNG test, add the @TestRail annotation, so let's say you will have this test:
```java
@Test(groups="sample-tests")
public void thisAutomatedTestWillBePublishedInTestRail()
{
}
```
add now @TestRail integration with mandatory field ```section```. This means that this tests annotated, will be uploaded in TestRail:
```java
@Test(groups="sample-tests")
@TestRail(section = { "protocols", "TBD" })
public void thisAutomatedTestWillBePublishedInTestRail()
{
}
```
The section field, represents an array of strings, the hierarchy of sections that SHOULD be found on TestRail under the project you've selected in default.properties. Follow the TestRail [user-guide](http://docs.gurock.com/testrail-userguide/start) for more information regarding sections.
In our example we created in Test Rail one root section "protocols" with a child section: "TBD" (you can go further and add multiple section as you wish)
* now, lets add the listener, the TestRailExecutorListener that will handle this TC Management interaction.
This listener can be added at the class level or suite level (approach that we embrace)
Take a look at [sanity-cmis.xml](src/test/resources/sanity-cmis.xml) for further example.
```xml
<listeners>
<listener class-name="org.alfresco.utility.testrail.TestRailExecutorListener"></listener>
(...)
</listeners>
```
Right click on cmis-suites.xml file and run it, or just "mvn test" from root if this sample project.
After everything passes, go in Test Rail, open your project and navigate to "Test Cases" section. Notice that under protocols/TBD section, you will see your test case published.
If you defined also the "testManagement.testRun" correctly, you will see under Test Runs, the status of this case marked as passed.
The @TestRail annotation offers also other options like:
- "description" this is the description that will be updated in Test Rail for your test case
- "testType", the default value is set to Functional test
- "executionType", default value is set to ExecutionType.REGRESSION, but you can also use ExecutionType.SMOKE, ExecutionType.SANITY, etc
Take a look at the demo scenarios in this project for further examples.
**[Back to Top ^](#table-of-contents)**
## Reference
* For any improvements, bugs, please use Jira - [TAS](https://issues.alfresco.com/jira/browse/TAS) project.
* Setup the environment using [docker](https://gitlab.alfresco.com/tas/alfresco-docker-provisioning/blob/master/Readme.md).
* [Bamboo Test Plan](https://bamboo.alfresco.com/bamboo/browse/TAS-CMIS)
## Contributors
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other... [more](CODE_OF_CONDUCT.md)
## Releasing
Any commit done on this project should be automatically executed by [TAS Build Plan](https://bamboo.alfresco.com/bamboo/browse/TAS-TAS)
If the build passes, then you didn't broke anything.
If you want to perform a release, open [TAS-CMIS](https://bamboo.alfresco.com/bamboo/browse/TAS-CMIS) Bamboo Build.
Run the Default stage and if it passes, then manually perform the Release stage (this will auto-increment the version in pom.xml)
## License
TBD

View File

@@ -0,0 +1,20 @@
:paw_prints: Back to Utility [README](README.md).
---
# Change Log
All notable changes to this project will be documented in this file.
Each tag bellow has a corresponded version released in [Nexus](https://nexus.alfresco.com/nexus/#welcome).
Currently we are testing [CMIS v1.1](http://docs.oasis-open.org/cmis/CMIS/v1.1/CMIS-v1.1.html) with Alfresco One.
(if you need to update/polish tests please branch from the release tags)
## [[v5.2.0-1] - 2016-12-12](/tas/alfresco-tas-cmis-test/commits/v5.2.0-1)
### TBD
## [[v5.2.0-0] - 2016-12-12](/tas/alfresco-tas-cmis-test/commits/v5.2.0-0)
- works with 5.2 alfresco
- 100% Core tests for CMIS
- 100% Sanity test for CMIS
- use released v1.0.7 utility

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -1,27 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.alfresco.tas</groupId>
<artifactId>alfresco-community-repo-cmis-test</artifactId>
<name>cmis test</name>
<packaging>jar</packaging>
<artifactId>cmis</artifactId>
<name>alfresco-tas-cmis</name>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<developers>
<developer>
<name>Paul Brodner</name>
<roles>
<role>Test Automation Architect</role>
</roles>
</developer>
</developers>
<organization>
<name>Alfresco Software</name>
<url>http://www.alfresco.com/</url>
</organization>
<properties>
<maven.build.sourceVersion>11</maven.build.sourceVersion>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<chemistry-opencmis-commons-api>1.1.0</chemistry-opencmis-commons-api>
<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
<maven-release.version>2.5.3</maven-release.version>
<java.version>11</java.version>
<suiteXmlFile>${project.basedir}/src/test/resources/cmis-suite.xml</suiteXmlFile>
<cmis.binding />
<cmis.basePath />
@@ -58,10 +58,29 @@
</profiles>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>4.7.1.Final</version>
</dependency>
<!-- alfresco tester settings -->
<dependency>
<groupId>org.alfresco.tas</groupId>
<artifactId>cmis</artifactId>
<scope>test</scope>
<artifactId>utility</artifactId>
<exclusions>
<exclusion>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- open cmis settings -->
<dependency>
<groupId>org.apache.chemistry.opencmis</groupId>
<artifactId>chemistry-opencmis-commons-api</artifactId>
<version>${chemistry-opencmis-commons-api}</version>
</dependency>
</dependencies>

View File

@@ -0,0 +1,130 @@
package org.alfresco.cmis;
import org.alfresco.utility.data.AisToken;
import org.alfresco.utility.data.auth.DataAIS;
import org.alfresco.utility.model.UserModel;
import org.apache.chemistry.opencmis.commons.SessionParameter;
import org.keycloak.authorization.client.util.HttpResponseException;
import org.keycloak.representations.AccessTokenResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
import static org.alfresco.utility.report.log.Step.STEP;
@Service
public class AuthParameterProviderFactory
{
public static String STEP_PREFIX = "CMIS AuthParameterProvider:";
@Autowired
private DataAIS dataAIS;
@Autowired
private CmisProperties cmisProperties;
/**
*
* The default provider uses AIS if support for Alfresco Identity Service is enabled.
* Otherwise a provider which uses Basic authentication is returned.
*
* @return Function which takes a {@link UserModel} and returns a map of
* authentication parameters to be used with {@link CmisWrapper#authenticateUser(UserModel, Function)}
*/
public Function<UserModel, Map<String, String>> getDefaultProvider()
{
if (dataAIS.isEnabled())
{
STEP(String.format("%s Retrieved default AIS auth parameter provider.", STEP_PREFIX));
return new AisAuthParameterProvider();
}
else
{
STEP(String.format("%s Retrieved default Basic auth parameter provider.", STEP_PREFIX));
return new BasicAuthParameterProvider();
}
}
public Function<UserModel, Map<String, String>> getAISProvider()
{
return new AisAuthParameterProvider();
}
public Function<UserModel, Map<String, String>> getBasicProvider()
{
return new BasicAuthParameterProvider();
}
private class BasicAuthParameterProvider implements Function<UserModel, Map<String, String>>
{
@Override
public Map<String, String> apply(UserModel userModel)
{
STEP(String.format("%s Using Basic auth parameter provider.", STEP_PREFIX));
Map<String, String> parameters = new HashMap<>();
parameters.put(SessionParameter.USER, userModel.getUsername());
parameters.put(SessionParameter.PASSWORD, userModel.getPassword());
return parameters;
}
}
private class AisAuthParameterProvider implements Function<UserModel, Map<String, String>>
{
@Override
public Map<String, String> apply(UserModel userModel)
{
Map<String, String> parameters = new HashMap<>();
STEP(String.format("%s Using AIS auth parameter provider.", STEP_PREFIX));
AisToken aisToken = getAisAccessToken(userModel);
parameters.put(SessionParameter.AUTHENTICATION_PROVIDER_CLASS, "org.apache.chemistry.opencmis.client.bindings.spi.OAuthAuthenticationProvider");
parameters.put(SessionParameter.OAUTH_ACCESS_TOKEN, aisToken.getToken());
parameters.put(SessionParameter.OAUTH_REFRESH_TOKEN, aisToken.getRefreshToken());
parameters.put(SessionParameter.OAUTH_EXPIRATION_TIMESTAMP, String.valueOf(System.currentTimeMillis()
+ (aisToken.getExpiresIn() * 1000))); // getExpiresIn is in seconds
parameters.put(SessionParameter.OAUTH_TOKEN_ENDPOINT, cmisProperties.aisProperty().getAdapterConfig().getAuthServerUrl()
+ "/realms/alfresco/protocol/openid-connect/token");
parameters.put(SessionParameter.OAUTH_CLIENT_ID, cmisProperties.aisProperty().getAdapterConfig().getResource());
return parameters;
}
/**
* Returns a valid access token for valid user credentials in userModel.
* An invalid access token is returned for invalid user credentials,
* which can be used for tests involving non existing or unauthorized users.
* @param userModel
* @return
*/
private AisToken getAisAccessToken(UserModel userModel)
{
String badToken = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJUazFPZ2JqVlo1UEw2bmtsNWFvTUlacTZ4cW9PZzc5WGtzdnJTTUcxLUFZIn0.eyJqdGkiOiI3NTVkMGZiOS03NzI5LTQ1NzYtYWM4Ny1hZWZjZWNiZDE0ZGEiLCJleHAiOjE1NTM2MjQ1NDgsIm5iZiI6MCwiaWF0IjoxNTUzNjI0MjQ4LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0L2F1dGgvcmVhbG1zL2FsZnJlc2NvIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6Ijk4NDE0Njg4LTUwMDUtNDVmOS05YTVjLTlkMDRlODMyYTNkMiIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFsZnJlc2NvIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiNjJlN2U5YzktZmFlNS00N2RhLTk5MDItMTZjYTJhZWUwMWMwIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0KiIsImh0dHBzOi8vbG9jYWxob3N0KiJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoidXNlci12eGlrcXd3cG5jYmpzeHgifQ.PeLGCNCzj-P2m0knwUU9Vfx4dzLLQER9IdV7GyLel9LRN-3J9nh7GBDRQsyDJ0pqhObQyMg4V3wSsrsXRQ6gKhmUyDemmD-w1YMC2a2HKX6GlxsTEF_f1K_R15lIQOawNVErlWjZWORJGCvCYZOJ99SOmeOC6PGY79zLL94MMnf6dXcegePPMOKG-59eNjBkOylTipYebvM40nbbKrS5vzNHQlvUh4ALFeBoMSKGnLSjQd06Dj4SWojG0p1BrxurqDjW0zz6pQlEAm4vcWApRZ6qBLZcMH8adYix07zCDb87GOn1pmfEBWpwd3BEgC_LLu06guaCPHC9tpeIaDTHLg";
String badRefreshToken = "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJmM2YyMjhjYS1jMzg5LTQ5MGUtOGU1Zi02YWI1MmJhZDVjZGEifQ.eyJqdGkiOiIyNmExZWNhYy00Zjk0LTQwYzctYjJjNS04NTlhZmQ3NjBiYWMiLCJleHAiOjE1NTM2MjYwNDgsIm5iZiI6MCwiaWF0IjoxNTUzNjI0MjQ4LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0L2F1dGgvcmVhbG1zL2FsZnJlc2NvIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdC9hdXRoL3JlYWxtcy9hbGZyZXNjbyIsInN1YiI6Ijk4NDE0Njg4LTUwMDUtNDVmOS05YTVjLTlkMDRlODMyYTNkMiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJhbGZyZXNjbyIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6IjYyZTdlOWM5LWZhZTUtNDdkYS05OTAyLTE2Y2EyYWVlMDFjMCIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJwcm9maWxlIGVtYWlsIn0.lRBJQc7tj0rk7JBC0zpM0dDdZgDKjm9wcxP8nzLnXe4";
AisToken aisToken;
try
{
// Attempt to get an access token for userModel from AIS
aisToken = dataAIS.perform().getAccessToken(userModel);
}
catch (HttpResponseException e)
{
// Trying to authenticate with invalid user credentials so return an invalid access token
if (e.getStatusCode() == 401)
{
STEP(String.format("%s Invalid user credentials were provided %s:%s. Using invalid token for reqest.",
STEP_PREFIX, userModel.getUsername(), userModel.getPassword()));
aisToken = new AisToken(badToken, badRefreshToken, System.currentTimeMillis(), 300000);
}
else
{
throw e;
}
}
return aisToken;
}
}
}

View File

@@ -0,0 +1,64 @@
package org.alfresco.cmis;
import org.alfresco.utility.TasAisProperties;
import org.alfresco.utility.TasProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
@Configuration
@PropertySource("classpath:default.properties")
@PropertySource(value = "classpath:${environment}.properties", ignoreResourceNotFound = true)
public class CmisProperties
{
@Autowired
private TasProperties properties;
@Autowired
private TasAisProperties aisProperties;
public TasProperties envProperty()
{
return properties;
}
public TasAisProperties aisProperty()
{
return aisProperties;
}
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
{
return new PropertySourcesPlaceholderConfigurer();
}
@Value("${cmis.binding}")
private String cmisBinding;
@Value("${cmis.basePath}")
private String basePath;
public String getCmisBinding()
{
return cmisBinding;
}
public String getBasePath()
{
return basePath;
}
public void setBasePath(String basePath)
{
this.basePath = basePath;
}
public void setCmisBinding(String cmisBinding)
{
this.cmisBinding = cmisBinding;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,185 @@
package org.alfresco.cmis.dsl;
import static org.alfresco.utility.report.log.Step.STEP;
import java.util.Iterator;
import java.util.List;
import org.alfresco.cmis.CmisWrapper;
import org.alfresco.utility.LogFactory;
import org.apache.chemistry.opencmis.client.api.ItemIterable;
import org.apache.chemistry.opencmis.client.api.ObjectType;
import org.apache.chemistry.opencmis.client.api.Tree;
import org.apache.chemistry.opencmis.client.runtime.objecttype.ObjectTypeHelper;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.slf4j.Logger;
import org.testng.Assert;
/**
* DSL for preparing calls on getting the type children of a type.
*/
public class BaseObjectType
{
private CmisWrapper cmisAPI;
private String baseTypeID;
private boolean includePropertyDefinition = false;
private Logger LOG = LogFactory.getLogger();
public BaseObjectType(CmisWrapper cmisAPI, String baseTypeID)
{
this.cmisAPI = cmisAPI;
this.baseTypeID = baseTypeID;
}
public BaseObjectType withPropertyDefinitions()
{
this.includePropertyDefinition = true;
return this;
}
public BaseObjectType withoutPropertyDefinitions()
{
this.includePropertyDefinition = false;
return this;
}
/**
* Example of objectTypeID:
* "D:trx:transferReport" - see {@link ObjectTypeHelper} "D:trx:tempTransferStore"
* "D:imap:imapAttach"
*
* @param objectTypeID
*/
public PropertyDefinitionObject hasChildren(String objectTypeID)
{
return checkChildren(objectTypeID, true);
}
/**
* Example of objectTypeID:
* "D:trx:transferReport" - see {@link ObjectTypeHelper} "D:trx:tempTransferStore"
* "D:imap:imapAttach"
*
* @param objectTypeID
*/
public CmisWrapper doesNotHaveChildren(String objectTypeID)
{
checkChildren(objectTypeID, false);
return cmisAPI;
}
/**
* Example of objectTypeID:
* "D:trx:transferReport" - see {@link ObjectTypeHelper} "D:trx:tempTransferStore"
* "D:imap:imapAttach"
*
* @param objectTypeID
*/
private PropertyDefinitionObject checkChildren(String objectTypeID, boolean exist)
{
ItemIterable<ObjectType> values = cmisAPI.withCMISUtil().getTypeChildren(this.baseTypeID, includePropertyDefinition);
boolean foundChild = false;
PropertyDefinitionObject propDefinition = null;
for (Iterator<ObjectType> iterator = values.iterator(); iterator.hasNext();)
{
ObjectType type = (ObjectType) iterator.next();
LOG.info("Found child Object Type: {}", ToStringBuilder.reflectionToString(type, ToStringStyle.MULTI_LINE_STYLE));
if (type.getId().equals(objectTypeID))
{
foundChild = true;
propDefinition = new PropertyDefinitionObject(type);
break;
}
}
Assert.assertEquals(foundChild, exist,
String.format("Object Type with ID[%s] is found as children for Parent Type: [%s]", objectTypeID, this.baseTypeID));
return propDefinition;
}
public class PropertyDefinitionObject
{
ObjectType type;
public PropertyDefinitionObject(ObjectType type)
{
this.type = type;
}
public PropertyDefinitionObject propertyDefinitionIsEmpty()
{
STEP(String.format("%s Verify that property definitions map is empty.", CmisWrapper.STEP_PREFIX));
Assert.assertTrue(type.getPropertyDefinitions().isEmpty(), "Property definitions is empty.");
return this;
}
public PropertyDefinitionObject propertyDefinitionIsNotEmpty()
{
STEP(String.format("%s Verify that property definitions map is not empty.", CmisWrapper.STEP_PREFIX));
Assert.assertFalse(type.getPropertyDefinitions().isEmpty(), "Property definitions is not empty.");
return this;
}
}
private CmisWrapper checkDescendents(int depth, boolean exist, String... objectTypeIDs)
{
List<Tree<ObjectType>> values = cmisAPI.withCMISUtil().getTypeDescendants(this.baseTypeID, depth, includePropertyDefinition);
for (String objectTypeID : objectTypeIDs)
{
boolean foundChild = false;
for (Tree<ObjectType> tree : values)
{
if (tree.getItem().getId().equals(objectTypeID))
{
foundChild = true;
break;
}
}
Assert.assertEquals(foundChild, exist,
String.format("Assert %b: Descendant [%s] is found as descendant for Type: [%s]", exist, objectTypeID, this.baseTypeID));
if (foundChild)
{
STEP(String.format("%s Cmis object '%s' is found as descendant.", CmisWrapper.STEP_PREFIX, objectTypeID));
}
else
{
STEP(String.format("%s Cmis object '%s' is NOT found as descendant.", CmisWrapper.STEP_PREFIX, objectTypeID));
}
}
return cmisAPI;
}
/**
* Assert that specified descendantType is present in the depth of tree
* Depth can be -1 or >= 1
* Example of objectTypeID:
* "D:trx:transferReport" - see {@link ObjectTypeHelper} "D:trx:tempTransferStore"
* "D:imap:imapAttach"
*
* @param objectTypeID
* @param depth
* @return
*/
public CmisWrapper hasDescendantType(int depth, String... objectTypeIDs)
{
return checkDescendents(depth, true, objectTypeIDs);
}
/**
* Assert that specified descendantType is NOT present in the depth of tree
* Depth can be -1 or >= 1
* Example of objectTypeID:
* "D:trx:transferReport" - see {@link ObjectTypeHelper} "D:trx:tempTransferStore"
* "D:imap:imapAttach"
*
* @param objectTypeID
* @param depth
* @return
*/
public CmisWrapper doesNotHaveDescendantType(int depth, String... objectTypeIDs)
{
checkDescendents(depth, false, objectTypeIDs);
return cmisAPI;
}
}

View File

@@ -0,0 +1,78 @@
package org.alfresco.cmis.dsl;
import org.alfresco.cmis.CmisWrapper;
import org.alfresco.utility.Utility;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
import java.util.Map;
/**
* DSL pertaining only to check in a {@link Document}
*/
public class CheckIn
{
private CmisWrapper cmisWrapper;
private boolean version;
private Map<String, ?> properties;
private String content;
private String comment;
public CheckIn(CmisWrapper cmisWrapper)
{
this.cmisWrapper = cmisWrapper;
}
public CheckIn withMajorVersion()
{
this.version = true;
return this;
}
public CheckIn withMinorVersion()
{
this.version = false;
return this;
}
public CheckIn withContent(String content)
{
this.content = content;
return this;
}
public CheckIn withoutComment()
{
this.comment = null;
return this;
}
public CheckIn withComment(String comment)
{
this.comment = comment;
return this;
}
public CmisWrapper checkIn() throws Exception
{
return checkIn(properties);
}
public CmisWrapper checkIn(Map<String, ?> properties) throws Exception
{
ContentStream contentStream = cmisWrapper.withCMISUtil().getContentStream(content);
try
{
Document pwc = cmisWrapper.withCMISUtil().getPWCDocument();
pwc.refresh();
Utility.waitToLoopTime(2);
pwc.checkIn(version, properties, contentStream, comment);
}
catch(CmisStorageException st)
{
cmisWrapper.withCMISUtil().getPWCDocument().checkIn(version, properties, contentStream, comment);
}
return cmisWrapper;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,761 @@
package org.alfresco.cmis.dsl;
import org.alfresco.cmis.CmisWrapper;
import org.alfresco.cmis.exception.InvalidCmisObjectException;
import org.alfresco.utility.LogFactory;
import org.alfresco.utility.Utility;
import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.exception.IORuntimeException;
import org.alfresco.utility.model.ContentModel;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.GroupModel;
import org.alfresco.utility.model.UserModel;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.client.api.FileableCmisObject;
import org.apache.chemistry.opencmis.client.api.Folder;
import org.apache.chemistry.opencmis.client.api.ItemIterable;
import org.apache.chemistry.opencmis.client.api.ObjectType;
import org.apache.chemistry.opencmis.client.api.OperationContext;
import org.apache.chemistry.opencmis.client.api.Property;
import org.apache.chemistry.opencmis.client.api.QueryResult;
import org.apache.chemistry.opencmis.client.api.Rendition;
import org.apache.chemistry.opencmis.client.api.SecondaryType;
import org.apache.chemistry.opencmis.client.api.Tree;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.Ace;
import org.apache.chemistry.opencmis.commons.data.Acl;
import org.apache.chemistry.opencmis.commons.data.AclCapabilities;
import org.apache.chemistry.opencmis.commons.data.ContentStream;
import org.apache.chemistry.opencmis.commons.data.PermissionMapping;
import org.apache.chemistry.opencmis.commons.data.PropertyData;
import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
import org.apache.chemistry.opencmis.commons.enums.Action;
import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
import org.apache.chemistry.opencmis.commons.enums.BindingType;
import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
import org.apache.chemistry.opencmis.commons.exceptions.CmisVersioningException;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.testng.collections.Lists;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.alfresco.utility.report.log.Step.STEP;
/**
* DSL utility for managing CMIS objects
*/
public class CmisUtil
{
private CmisWrapper cmisAPI;
private Logger LOG = LogFactory.getLogger();
public CmisUtil(CmisWrapper cmisAPI)
{
this.cmisAPI = cmisAPI;
}
/**
* Get cmis object by object id
*
* @param objectId cmis object id
* @return CmisObject cmis object
*/
public CmisObject getCmisObjectById(String objectId)
{
LOG.debug("Get CMIS object by ID {}", objectId);
if (cmisAPI.getSession() == null)
{
throw new CmisRuntimeException("Please authenticate user, call: cmisAPI.authenticate(..)!");
}
if (objectId == null)
{
throw new InvalidCmisObjectException("Invalid content id");
}
return cmisAPI.getSession().getObject(objectId);
}
/**
* Get cmis object by object id with OperationContext
*
* @param objectId cmis object id
* @param context OperationContext
* @return CmisObject cmis object
*/
public CmisObject getCmisObjectById(String objectId, OperationContext context)
{
if (cmisAPI.getSession() == null)
{
throw new CmisRuntimeException("Please authenticate user, call: cmisAPI.authenticate(..)!");
}
if (objectId == null)
{
throw new InvalidCmisObjectException("Invalid content id");
}
return cmisAPI.getSession().getObject(objectId, context);
}
/**
* Get cmis object by path
*
* @param pathToItem String path to item
* @return CmisObject cmis object
*/
public CmisObject getCmisObject(String pathToItem)
{
if (cmisAPI.getSession() == null)
{
throw new CmisRuntimeException("Please authenticate user, call: cmisAPI.authenticate(..)!");
}
if (pathToItem == null)
{
throw new InvalidCmisObjectException("Invalid path set for content");
}
CmisObject cmisObject = cmisAPI.getSession().getObjectByPath(Utility.removeLastSlash(pathToItem));
if (cmisObject instanceof Document)
{
if (!((Document) cmisObject).getVersionLabel().contentEquals("pwc"))
{
// get last version of document
cmisObject = ((Document) cmisObject).getObjectOfLatestVersion(false);
}
else
{
// get pwc document
cmisObject = cmisAPI.getSession().getObject(((Document) cmisObject).getObjectOfLatestVersion(false).getVersionSeriesCheckedOutId());
}
}
return cmisObject;
}
/**
* Get cmis object by path with context
*
* @param pathToItem String path to item
* @param context OperationContext
* @return CmisObject cmis object
*/
public CmisObject getCmisObject(String pathToItem, OperationContext context)
{
if (cmisAPI.getSession() == null)
{
throw new CmisRuntimeException("Please authenticate user!");
}
if (pathToItem == null)
{
throw new InvalidCmisObjectException("Invalid path set for content");
}
CmisObject cmisObject = cmisAPI.getSession().getObjectByPath(Utility.removeLastSlash(pathToItem), context);
if (cmisObject instanceof Document)
{
if (!((Document) cmisObject).getVersionLabel().contentEquals("pwc"))
{
// get last version of document
cmisObject = ((Document) cmisObject).getObjectOfLatestVersion(false, context);
}
else
{
// get pwc document
cmisObject = cmisAPI.getSession().getObject(((Document) cmisObject).getObjectOfLatestVersion(false, context).getVersionSeriesCheckedOutId());
}
}
return cmisObject;
}
/**
* Get Document object for a file
*
* @param path String path to document
* @return {@link Document} object
*/
public Document getCmisDocument(final String path)
{
LOG.debug("Get CMIS Document by path {}", path);
Document d = null;
CmisObject docObj = getCmisObject(path);
if (docObj instanceof Document)
{
d = (Document) docObj;
}
else if (docObj instanceof Folder)
{
throw new InvalidCmisObjectException("Content at " + path + " is not a file");
}
return d;
}
/**
* Get Folder object for a folder
*
* @param path String path to folder
* @return {@link Folder} object
*/
public Folder getCmisFolder(final String path)
{
Folder f = null;
CmisObject folderObj = getCmisObject(path);
if (folderObj instanceof Folder)
{
f = (Folder) folderObj;
}
else if (folderObj instanceof Document)
{
throw new InvalidCmisObjectException("Content at " + path + " is not a folder");
}
return f;
}
/**
* Helper method to get the contents of a stream
*
* @param stream
* @return
* @throws IORuntimeException
*/
protected String getContentAsString(ContentStream stream)
{
LOG.debug("Get Content as String {}", stream);
InputStream inputStream = stream.getStream();
String result;
try
{
result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
}
catch (IOException e)
{
throw new IORuntimeException(e);
}
IOUtils.closeQuietly(inputStream);
return result;
}
/**
* Copy all the children of the source folder to the target folder
*
* @param sourceFolder
* @param targetFolder
*/
protected void copyChildrenFromFolder(Folder sourceFolder, Folder targetFolder)
{
for (Tree<FileableCmisObject> t : sourceFolder.getDescendants(-1))
{
CmisObject obj = t.getItem();
if (obj instanceof Document)
{
Document d = (Document) obj;
d.copy(targetFolder);
}
else if (obj instanceof Folder)
{
copyFolder((Folder) obj, targetFolder);
}
}
}
/**
* Copy folder with all children
*
* @param sourceFolder source folder
* @param targetFolder target folder
* @return CmisObject of new created folder
*/
public CmisObject copyFolder(Folder sourceFolder, Folder targetFolder)
{
Map<String, Object> folderProperties = new HashMap<String, Object>(2);
folderProperties.put(PropertyIds.NAME, sourceFolder.getName());
folderProperties.put(PropertyIds.OBJECT_TYPE_ID, sourceFolder.getBaseTypeId().value());
Folder newFolder = targetFolder.createFolder(folderProperties);
copyChildrenFromFolder(sourceFolder, newFolder);
return newFolder;
}
protected boolean isPrivateWorkingCopy()
{
boolean result;
try
{
result = getPWCDocument().isVersionSeriesPrivateWorkingCopy();
}
catch (CmisVersioningException cmisVersioningException)
{
result = false;
}
return result;
}
/**
* Returns the PWC (private working copy) ID of the document version series
*/
public Document getPWCDocument()
{
Document document = getCmisDocument(cmisAPI.getLastResource());
String pwcId = document.getVersionSeriesCheckedOutId();
if (pwcId != null)
{
return (Document) cmisAPI.getSession().getObject(pwcId);
}
else
{
throw new CmisVersioningException(String.format("Document %s is not checked out", document.getName()));
}
}
public FileModel getPWCFileModel()
{
Document document = getPWCDocument();
String[] pathTokens = cmisAPI.getLastResource().split("/");
String path = "";
for (int i = 0; i < pathTokens.length - 1; i++)
path = Utility.buildPath(path, pathTokens[i]);
path = Utility.buildPath(path, document.getName());
FileModel fileModel = new FileModel();
fileModel.setName(document.getName());
fileModel.setCmisLocation(path);
return fileModel;
}
protected Folder getFolderParent()
{
return getCmisFolder(cmisAPI.getLastResource()).getFolderParent();
}
/**
* @return List<Action> of allowable actions for the current object
*/
protected List<Action> getAllowableActions()
{
return Lists.newArrayList(getCmisObject(cmisAPI.getLastResource()).getAllowableActions().getAllowableActions());
}
/**
* Returns the requested property. If the property is not available, null is returned
*
* @param propertyId
* @return CMIS Property
*/
protected <T> Property<T> getProperty(String propertyId)
{
CmisObject cmisObject = getCmisObject(cmisAPI.getLastResource());
return cmisObject.getProperty(propertyId);
}
protected List<Rendition> getRenditions()
{
OperationContext operationContext = cmisAPI.getSession().createOperationContext();
operationContext.setRenditionFilterString("*");
CmisObject obj = cmisAPI.getSession().getObjectByPath(cmisAPI.getLastResource(), operationContext);
obj.refresh();
List<Rendition> renditions = obj.getRenditions();
int retry = 0;
while ((renditions == null || renditions.isEmpty()) && retry < Utility.retryCountSeconds)
{
Utility.waitToLoopTime(1);
obj.refresh();
renditions = obj.getRenditions();
retry++;
}
return obj.getRenditions();
}
protected List<SecondaryType> getSecondaryTypes()
{
CmisObject obj = getCmisObject(cmisAPI.getLastResource());
obj.refresh();
return obj.getSecondaryTypes();
}
/**
* Get the children from a parent folder
*
* @return Map<ContentModel, ObjectType>
*/
public Map<ContentModel, ObjectType> getChildren()
{
String folderParent = cmisAPI.getLastResource();
ItemIterable<CmisObject> children = cmisAPI.withCMISUtil().getCmisFolder(folderParent).getChildren();
Map<ContentModel, ObjectType> contents = new HashMap<ContentModel, ObjectType>();
for (CmisObject o : children)
{
ContentModel content = new ContentModel(o.getName());
content.setNodeRef(o.getId());
content.setDescription(o.getDescription());
content.setCmisLocation(Utility.buildPath(folderParent, o.getName()));
contents.put(content, o.getType());
}
return contents;
}
/**
* Gets the folder descendants starting with the current folder
*
* @param depth level of the tree that you want to go to
* - currentFolder
* -- file1.txt
* -- file2.txt
* -- folderB
* --- file3.txt
* --- file4.txt
* e.g. A depth of 1 will give you just the current folder descendants (file1.txt, file2.txt, folder1)
* e.g. A depth of -1 will return all the descendants (file1.txt, file2.txt, folder1, file3.txt and file4.txt)
*/
public List<CmisObject> getFolderDescendants(int depth)
{
return getFolderTreeCmisObjects(getCmisFolder(cmisAPI.getLastResource()).getDescendants(depth));
}
/**
* Returns a list of Cmis objects for the provided Content Models
*
* @param contentModels
*/
public List<CmisObject> getCmisObjectsFromContentModels(ContentModel... contentModels)
{
List<CmisObject> cmisObjects = new ArrayList<>();
for (ContentModel contentModel : contentModels)
cmisObjects.add(getCmisObject(contentModel.getCmisLocation()));
return cmisObjects;
}
public ContentStream getContentStream(String content)
{
String fileName = getCmisDocument(cmisAPI.getLastResource()).getName();
return cmisAPI.getDataContentService().getContentStream(fileName, content);
}
public Acl getAcls()
{
OperationContext context = cmisAPI.getSession().createOperationContext();
context.setIncludeAcls(true);
return getCmisObject(cmisAPI.getLastResource(), context).getAcl();
}
/**
* Gets only the folder descendants for the {@link #getLastResource()} folder
*
* @param depth level of the tree that you want to go to
* - currentFolder
* -- folderB
* -- folderC
* --- folderD
* e.g. A depth of 1 will give you just the current folder descendants (folderB, folderC)
* e.g. A depth of -1 will return all the descendants (folderB, folderC, folderD)
*/
public List<CmisObject> getFolderTree(int depth)
{
return getFolderTreeCmisObjects(getCmisFolder(cmisAPI.getLastResource()).getFolderTree(depth));
}
/**
* Helper method for getFolderTree and getFolderDescendants that cycles threw the folder descendants and returns List<CmisObject>
*/
private List<CmisObject> getFolderTreeCmisObjects(List<Tree<FileableCmisObject>> descendants)
{
List<CmisObject> cmisObjectList = new ArrayList<>();
for (Tree<FileableCmisObject> descendant : descendants)
{
cmisObjectList.add(descendant.getItem());
cmisObjectList.addAll(descendant.getChildren().stream().map(Tree::getItem).collect(Collectors.toList()));
}
return cmisObjectList;
}
protected List<Document> getAllDocumentVersions()
{
return getCmisDocument(cmisAPI.getLastResource()).getAllVersions();
}
public List<Document> getAllDocumentVersionsBy(OperationContext context)
{
return getCmisDocument(cmisAPI.getLastResource()).getAllVersions(context);
}
public List<Document> getCheckedOutDocumentsFromSession()
{
return com.google.common.collect.Lists.newArrayList(cmisAPI.getSession().getCheckedOutDocs());
}
public List<Document> getCheckedOutDocumentsFromSession(OperationContext context)
{
return com.google.common.collect.Lists.newArrayList(cmisAPI.getSession().getCheckedOutDocs(context));
}
public List<Document> getCheckedOutDocumentsFromFolder()
{
Folder folder = cmisAPI.withCMISUtil().getCmisFolder(cmisAPI.getLastResource());
return com.google.common.collect.Lists.newArrayList(folder.getCheckedOutDocs());
}
public List<Document> getCheckedOutDocumentsFromFolder(OperationContext context)
{
Folder folder = cmisAPI.withCMISUtil().getCmisFolder(cmisAPI.getLastResource());
return com.google.common.collect.Lists.newArrayList(folder.getCheckedOutDocs(context));
}
protected boolean isCmisObjectContainedInCmisCheckedOutDocumentsList(CmisObject cmisObject, List<Document> cmisCheckedOutDocuments)
{
for (Document cmisCheckedOutDocument : cmisCheckedOutDocuments)
if (cmisObject.getId().split(";")[0].equals(cmisCheckedOutDocument.getId().split(";")[0]))
return true;
return false;
}
public Map<String, Object> getProperties(ContentModel contentModel, String baseTypeId)
{
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(PropertyIds.OBJECT_TYPE_ID, baseTypeId);
properties.put(PropertyIds.NAME, contentModel.getName());
// WebServices binding does not have SecondaryTypes and cannot be added to Object.
// cm:title and cm:description Policies
if (cmisAPI.getSession().getBinding().getBindingType().value().equals(BindingType.WEBSERVICES.value()))
{
return properties;
}
List<Object> aspects = new ArrayList<Object>();
aspects.add("P:cm:titled");
properties.put(PropertyIds.SECONDARY_OBJECT_TYPE_IDS, aspects);
properties.put("cm:title", contentModel.getTitle());
properties.put("cm:description", contentModel.getDescription());
return properties;
}
public OperationContext setIncludeAclContext()
{
OperationContext context = cmisAPI.getSession().createOperationContext();
context.setIncludeAcls(true);
return context;
}
public List<Ace> createAce(UserModel user, UserRole role)
{
List<String> addPermission = new ArrayList<String>();
addPermission.add(role.getRoleId());
Ace addAce = cmisAPI.getSession().getObjectFactory().createAce(user.getUsername(), addPermission);
List<Ace> addAces = new ArrayList<Ace>();
addAces.add(addAce);
return addAces;
}
public List<Ace> createAce(GroupModel group, UserRole role)
{
List<String> addPermission = new ArrayList<String>();
addPermission.add(role.getRoleId());
Ace addAce = cmisAPI.getSession().getObjectFactory().createAce(group.getDisplayName(), addPermission);
List<Ace> addAces = new ArrayList<Ace>();
addAces.add(addAce);
return addAces;
}
public List<Ace> createAce(UserModel user, String... permissions)
{
List<Ace> addAces = new ArrayList<Ace>();
RepositoryInfo repositoryInfo = cmisAPI.getSession().getRepositoryInfo();
AclCapabilities aclCapabilities = repositoryInfo.getAclCapabilities();
Map<String, PermissionMapping> permissionMappings = aclCapabilities.getPermissionMapping();
for (String perm : permissions)
{
STEP(String.format("%s Add permission '%s' for user %s ", CmisWrapper.STEP_PREFIX, perm, user.getUsername()));
PermissionMapping permissionMapping = permissionMappings.get(perm);
List<String> permissionsList = permissionMapping.getPermissions();
Ace addAce = cmisAPI.getSession().getObjectFactory().createAce(user.getUsername(), permissionsList);
addAces.add(addAce);
}
return addAces;
}
public ObjectType getTypeDefinition()
{
CmisObject cmisObject = cmisAPI.withCMISUtil().getCmisObject(cmisAPI.getLastResource());
return cmisAPI.getSession().getTypeDefinition(cmisObject.getBaseTypeId().value());
}
public ItemIterable<ObjectType> getTypeChildren(String baseType, boolean includePropertyDefinitions)
{
STEP(String.format("%s Get type children for '%s' and includePropertyDefinitions set to '%s'", CmisWrapper.STEP_PREFIX, baseType,
includePropertyDefinitions));
return cmisAPI.getSession().getTypeChildren(baseType, includePropertyDefinitions);
}
public List<Tree<ObjectType>> getTypeDescendants(String baseTypeId, int depth, boolean includePropertyDefinitions)
{
STEP(String.format("%s Get type descendants for '%s' with depth set to %d and includePropertyDefinitions set to '%s'", CmisWrapper.STEP_PREFIX,
baseTypeId, depth, includePropertyDefinitions));
return cmisAPI.getSession().getTypeDescendants(baseTypeId, depth, includePropertyDefinitions);
}
public String getObjectId(String pathToObject)
{
return getCmisObject(pathToObject).getId();
}
/**
* Update property for last resource cmis object
*
* @param propertyName String property name (e.g. cmis:name)
* @param propertyValue Object property value
*/
public void updateProperties(String propertyName, Object propertyValue)
{
Map<String, Object> props = new HashMap<String, Object>();
props.put(propertyName, propertyValue);
getCmisObject(cmisAPI.getLastResource()).updateProperties(props);
}
protected boolean isFolderInList(FolderModel folderModel, List<FolderModel> folders)
{
for (FolderModel folder : folders)
{
if (folderModel.getName().equals(folder.getName()))
{
return true;
}
}
return false;
}
protected boolean isFileInList(FileModel fileModel, List<FileModel> files)
{
for (FileModel file : files)
{
if (fileModel.getName().equals(file.getName()))
{
return true;
}
}
return false;
}
protected boolean isContentInList(ContentModel contentModel, List<ContentModel> contents)
{
for (ContentModel content : contents)
{
if (content.getName().equals(content.getName()))
{
return true;
}
}
return false;
}
/**
* Get children folders from a parent folder
*
* @return List<FolderModel>
*/
public List<FolderModel> getFolders()
{
STEP(String.format("%s Get the folder children from '%s'", CmisWrapper.STEP_PREFIX, cmisAPI.getLastResource()));
Map<ContentModel, ObjectType> children = getChildren();
List<FolderModel> folders = new ArrayList<FolderModel>();
for (Map.Entry<ContentModel, ObjectType> entry : children.entrySet())
{
if (entry.getValue().getId().equals(BaseTypeId.CMIS_FOLDER.value()))
{
FolderModel folder = new FolderModel(entry.getKey().getName());
folder.setNodeRef(entry.getKey().getNodeRef());
folder.setDescription(entry.getKey().getDescription());
folder.setCmisLocation(entry.getKey().getCmisLocation());
folder.setProtocolLocation(entry.getKey().getCmisLocation());
folders.add(folder);
}
}
return folders;
}
/**
* Get children documents from a parent folder
*
* @return List<FolderModel>
*/
public List<FileModel> getFiles()
{
STEP(String.format("%s Get the file children from '%s'", CmisWrapper.STEP_PREFIX, cmisAPI.getLastResource()));
Map<ContentModel, ObjectType> children = getChildren();
List<FileModel> files = new ArrayList<FileModel>();
for (Map.Entry<ContentModel, ObjectType> entry : children.entrySet())
{
if (entry.getValue().getId().equals(BaseTypeId.CMIS_DOCUMENT.value()))
{
FileModel file = new FileModel(entry.getKey().getName());
file.setNodeRef(entry.getKey().getNodeRef());
file.setDescription(entry.getKey().getDescription());
file.setCmisLocation(entry.getKey().getCmisLocation());
file.setProtocolLocation(entry.getKey().getCmisLocation());
files.add(file);
}
}
return files;
}
/*
* Get document(set as last resource) content
*/
public String getDocumentContent()
{
Utility.waitToLoopTime(2);
Document lastVersion = getCmisDocument(cmisAPI.getLastResource());
lastVersion.refresh();
LOG.info(String.format("Get the content from %s - node: %s", lastVersion.getName(), lastVersion.getId()));
ContentStream contentStream = lastVersion.getContentStream();
String actualContent = "";
if (contentStream != null)
{
actualContent = getContentAsString(contentStream);
}
else
{
lastVersion = getCmisDocument(cmisAPI.getLastResource());
lastVersion.refresh();
LOG.info(String.format("Retry get content stream for %s node: %s", lastVersion.getName(), lastVersion.getId()));
contentStream = lastVersion.getContentStream();
if (contentStream != null)
{
actualContent = getContentAsString(contentStream);
}
}
if(actualContent.isEmpty())
{
Utility.waitToLoopTime(2);
Document retryDoc = getCmisDocument(cmisAPI.getLastResource());
retryDoc.refresh();
LOG.info(String.format("Retry get content stream for %s node: %s", retryDoc.getName(), retryDoc.getId()));
contentStream = retryDoc.getContentStream();
if (contentStream != null)
{
actualContent = getContentAsString(contentStream);
}
}
return actualContent;
}
/**
* Get user noderef
*
* @param user {@link UserModel}
*/
public String getUserNodeRef(UserModel user)
{
String objectId = "";
ItemIterable<QueryResult> results = cmisAPI.getSession().query("select cmis:objectId from cm:person where cm:userName = '" + user.getUsername() + "'",
false);
for (QueryResult qResult : results)
{
PropertyData<?> propData = qResult.getPropertyById("cmis:objectId");
objectId = (String) propData.getFirstValue();
}
return objectId;
}
}

View File

@@ -0,0 +1,137 @@
package org.alfresco.cmis.dsl;
import static org.alfresco.utility.report.log.Step.STEP;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.alfresco.cmis.CmisWrapper;
import org.apache.chemistry.opencmis.client.api.CmisObject;
import org.apache.chemistry.opencmis.client.api.Document;
import org.apache.chemistry.opencmis.client.api.OperationContext;
import org.testng.Assert;
/**
* DSL utility for verifying a document version {@link Document}
*/
public class DocumentVersioning
{
private CmisWrapper cmisWrapper;
private CmisObject cmisObject;
private boolean majorVersion;
private Object versionLabel;
private List<Object> versions;
public DocumentVersioning(CmisWrapper cmisWrapper, CmisObject cmisObject)
{
this.cmisWrapper = cmisWrapper;
this.cmisObject = cmisObject;
}
private DocumentVersioning withLatestMajorVersion()
{
this.majorVersion = true;
return this;
}
private DocumentVersioning withLatestMinorVersion()
{
this.majorVersion = false;
return this;
}
private Document getVersionOfDocument()
{
Document document = (Document) cmisObject;
if (versionLabel != null)
{
List<Document> documents = document.getAllVersions();
for (Document documentVersion : documents)
if (documentVersion.getVersionLabel().equals(versionLabel.toString()))
return documentVersion;
}
else
{
return document.getObjectOfLatestVersion(majorVersion);
}
return document;
}
private List<Object> getDocumentVersions(List<Document> documentList)
{
List<Object> versions = new ArrayList<Object>();
for (Document document : documentList)
{
versions.add(document.getVersionLabel());
}
return versions;
}
public CmisWrapper assertVersionIs(Double expectedVersion)
{
STEP(String.format("%s Verify if document '%s' has version '%s'", cmisWrapper.getProtocolName(), cmisObject.getName(), expectedVersion));
Assert.assertEquals(getVersionOfDocument().getVersionLabel(), expectedVersion.toString(), "File has version");
return cmisWrapper;
}
public CmisWrapper assertLatestMajorVersionIs(Double expectedVersion)
{
STEP(String.format("%s Verify if latest major version of document '%s' is '%s'", cmisWrapper.getProtocolName(), cmisObject.getName(), expectedVersion));
Assert.assertEquals(withLatestMajorVersion().getVersionOfDocument().getVersionLabel(), expectedVersion.toString(), "File has version");
return cmisWrapper;
}
public CmisWrapper assertLatestMinorVersionIs(Double expectedVersion)
{
STEP(String.format("%s Verify if latest minor version of document '%s' is '%s'", cmisWrapper.getProtocolName(), cmisObject.getName(), expectedVersion));
Assert.assertEquals(withLatestMinorVersion().getVersionOfDocument().getVersionLabel(), expectedVersion.toString(), "File has version");
return cmisWrapper;
}
public DocumentVersioning getAllDocumentVersions()
{
setVersions(getDocumentVersions(cmisWrapper.withCMISUtil().getAllDocumentVersions()));
return this;
}
public CmisWrapper assertHasVersions(Object... versions)
{
setVersions(getDocumentVersions(cmisWrapper.withCMISUtil().getAllDocumentVersions()));
List<Object> documentVersions = getVersions();
for (Object version : versions)
{
STEP(String.format("%s Verify if document '%s' has version '%s'", cmisWrapper.getProtocolName(), cmisObject.getName(), version));
Assert.assertTrue(documentVersions.contains(version.toString()),
String.format("Document %s does not have version %s", cmisObject.getName(), version));
}
return cmisWrapper;
}
public DocumentVersioning getAllDocumentVersionsBy(OperationContext context)
{
setVersions(getDocumentVersions(cmisWrapper.withCMISUtil().getAllDocumentVersionsBy(context)));
return this;
}
public CmisWrapper assertHasVersionsInOrder(Object... versions)
{
List<Object> documentVersions = getVersions();
List<Object> expectedVersions = Arrays.asList(versions);
STEP(String.format("%s Verify if document '%s' has versions in this order '%s'", cmisWrapper.getProtocolName(), cmisObject.getName(),
Arrays.toString(expectedVersions.toArray())));
Assert.assertTrue(documentVersions.toString().equals(expectedVersions.toString()),
String.format("Document %s does not have versions in this order %s", cmisObject.getName(), Arrays.toString(expectedVersions.toArray())));
return cmisWrapper;
}
public List<Object> getVersions()
{
return versions;
}
public void setVersions(List<Object> versions)
{
this.versions = versions;
}
}

View File

@@ -0,0 +1,39 @@
package org.alfresco.cmis.dsl;
import org.alfresco.cmis.CmisWrapper;
import org.alfresco.utility.network.Jmx;
import org.alfresco.utility.network.JmxClient;
import org.alfresco.utility.network.JmxJolokiaProxyClient;
/**
* DSL for interacting with JMX (using direct JMX call see {@link JmxClient} or {@link JmxJolokiaProxyClient}
*/
public class JmxUtil
{
@SuppressWarnings("unused")
private CmisWrapper cmisWrapper;
private Jmx jmx;
private final String jmxAuditObjectName = "Alfresco:Type=Configuration,Category=Audit,id1=default";
public JmxUtil(CmisWrapper cmisWrapper, Jmx jmx)
{
this.cmisWrapper = cmisWrapper;
this.jmx = jmx;
}
public void enableCMISAudit() throws Exception
{
if(jmx.readProperty(jmxAuditObjectName, "audit.enabled").equals(String.valueOf(false)))
{
jmx.writeProperty(jmxAuditObjectName, "audit.enabled", String.valueOf(true));
}
jmx.writeProperty(jmxAuditObjectName, "audit.cmischangelog.enabled", String.valueOf(true));
jmx.writeProperty(jmxAuditObjectName, "audit.alfresco-access.enabled", String.valueOf(true));
}
public void disableCMISAudit() throws Exception
{
jmx.writeProperty(jmxAuditObjectName, "audit.cmischangelog.enabled", String.valueOf(false));
jmx.writeProperty(jmxAuditObjectName, "audit.alfresco-access.enabled", String.valueOf(false));
}
}

View File

@@ -0,0 +1,287 @@
package org.alfresco.cmis.dsl;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.alfresco.utility.Utility.checkObjectIsInitialized;
import static org.alfresco.utility.report.log.Step.STEP;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.StreamSupport;
import org.alfresco.cmis.CmisWrapper;
import org.alfresco.utility.LogFactory;
import org.alfresco.utility.data.provider.XMLTestData;
import org.alfresco.utility.exception.TestConfigurationException;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestModel;
import org.apache.chemistry.opencmis.client.api.ItemIterable;
import org.apache.chemistry.opencmis.client.api.QueryResult;
import org.apache.chemistry.opencmis.client.api.Session;
import org.apache.chemistry.opencmis.commons.data.PropertyData;
import org.slf4j.Logger;
import org.testng.Assert;
/**
* DSL for CMIS Queries
* This will also handle execution of CMIS queries
*/
public class QueryExecutor
{
static Logger LOG = LogFactory.getLogger();
CmisWrapper cmisWrapper;
private long resultCount = -1;
private String currentQuery = "";
private List<QueryResult> results;
public QueryExecutor(CmisWrapper cmisWrapper, String query)
{
this.cmisWrapper = cmisWrapper;
currentQuery = query;
}
public QueryResultAssertion assertResultsCount()
{
resultCount = executeQuery(currentQuery).getPageNumItems();
return new QueryResultAssertion();
}
public QueryResultAssertion assertColumnIsOrdered()
{
return assertValues();
}
public QueryResultAssertion assertColumnValuesRange()
{
return assertValues();
}
public QueryResultAssertion assertValues()
{
STEP("Sending query " + currentQuery);
results = StreamSupport.stream(executeQuery(currentQuery).spliterator(), false)
.collect(toList());
resultCount = results.size();
STEP("Received results " + results.stream().map(this::resultToString).collect(toList()));
return new QueryResultAssertion();
}
/** Try to return a useful string representation of the CMIS query result. */
private String resultToString(QueryResult result)
{
if (result == null || result.getProperties() == null)
{
return "null";
}
Optional<PropertyData<?>> idProperty = result.getProperties().stream()
.filter(propertyData -> propertyData.getId().equals("cmis:objectId"))
.findFirst();
return idProperty.map(PropertyData::getValues)
.map(values -> values.stream().map(Object::toString).collect(joining(",")))
.orElse(result.getProperties().toString());
}
private ItemIterable<QueryResult> executeQuery(String query)
{
Session session = cmisWrapper.getSession();
checkObjectIsInitialized(session, "You need to authenticate first using <cmisWrapper.authenticateUser(UserModel userModel)>");
return session.query(query, false);
}
/**
* Call getNodeRef on each test data item used in test and replace that with NODE_REF keywords in your Query
*
* @param testData
* @return
*/
public QueryExecutor applyNodeRefsFrom(XMLTestData testData)
{
List<String> dataItems = extractKeywords("NODE_REF");
if (dataItems.isEmpty())
return this;
List<String> nodeRefs = new ArrayList<String>();
for (String dataItemName : dataItems)
{
currentQuery = currentQuery.replace(String.format("NODE_REF[%s]", dataItemName), "%s");
TestModel model = testData.getTestDataItemWithId(dataItemName).getModel();
if (model == null)
throw new TestConfigurationException("No TestData with ID: " + dataItemName + " found in your XML file.");
if (model instanceof SiteModel)
{
nodeRefs.add(cmisWrapper.getDataContentService().usingAdmin().usingSite((SiteModel) model).getNodeRef());
}
else if (model instanceof FolderModel)
{
nodeRefs.add(((FolderModel) model).getNodeRef());
}
else if (model instanceof FileModel)
{
nodeRefs.add(((FileModel) model).getNodeRef());
}
}
try
{
currentQuery = String.format(currentQuery, nodeRefs.toArray());
LOG.info("Injecting nodeRef IDs \n\tQuery: [{}]", currentQuery);
}
catch (Exception e)
{
throw new TestConfigurationException(
"You passed multiple keywords to your search query, please re-analyze your query search format: " + e.getMessage());
}
return this;
}
/**
* if you have in your search 'SELECT * from cmis:document where workspace://SpacesStore/NODE_REF[site1] or workspace://SpacesStore/NODE_REF[site2]'
* and pass key="NODE_REF" this method will get "site1" and "site2" as values
*
* @param key
* @return
* @throws TestConfigurationException
*/
private List<String> extractKeywords(String key) throws TestConfigurationException
{
String[] lines = currentQuery.split(key);
List<String> keywords = new ArrayList<String>();
for (int i = 0; i < lines.length; i++)
{
if (lines[i].startsWith("["))
{
String keyValue = "";
for (int j = 1; j < lines[i].length() - 1; j++)
{
String tmp = Character.toString(lines[i].charAt(j));
if (tmp.equals("]"))
break;
keyValue += tmp;
}
keywords.add(keyValue);
}
}
return keywords;
}
public class QueryResultAssertion
{
public QueryResultAssertion hasLength(long expectedValue)
{
STEP(String.format("Verify that query: '%s' has %d results count returned", currentQuery, expectedValue));
Assert.assertEquals(resultCount, expectedValue, showErrorMessage());
return this;
}
public QueryResultAssertion isGreaterThan(long expectedValue)
{
STEP(String.format("Verify that query: '%s' has more than %d results count returned", currentQuery, expectedValue));
if (expectedValue <= resultCount)
Assert.fail(String.format("%s expected to have more than %d results, but found %d", showErrorMessage(), expectedValue, resultCount));
return this;
}
public QueryResultAssertion isLowerThan(long expectedValue)
{
STEP(String.format("Verify that query: '%s' has more than %d results count returned", currentQuery, expectedValue));
if (resultCount >= expectedValue)
Assert.fail(String.format("%s expected to have less than %d results, but found %d", showErrorMessage(), expectedValue, resultCount));
return this;
}
public QueryResultAssertion isOrderedAsc(String queryName)
{
STEP(String.format("Verify that query: '%s' is returning ascending ordered values for column %s", currentQuery, queryName));
List<Object> columnValues = new ArrayList<>();
results.forEach((r) -> {
columnValues.add(r.getPropertyValueByQueryName(queryName));
});
List<Object> orderedColumnValues = columnValues.stream().sorted().collect(toList());
Assert.assertEquals(columnValues, orderedColumnValues,
String.format("%s column values expected to be in ascendent order, but found %s", queryName, columnValues.toString()));
return this;
}
public QueryResultAssertion isOrderedDesc(String queryName)
{
STEP(String.format("Verify that query: '%s' is returning descending ordered values for column %s", currentQuery, queryName));
List<Object> columnValues = new ArrayList<>();
results.forEach((r) -> {
columnValues.add(r.getPropertyValueByQueryName(queryName));
});
List<Object> reverseOrderedColumnValues = columnValues.stream().sorted(Collections.reverseOrder()).collect(toList());
Assert.assertEquals(columnValues, reverseOrderedColumnValues,
String.format("%s column values expected to be in descendent order, but found %s", queryName, columnValues.toString()));
return this;
}
public QueryResultAssertion isReturningValuesInRange(String queryName, BigDecimal minValue, BigDecimal maxValue)
{
STEP(String.format("Verify that query: '%s' is returning values for column %s in range from %.4f to %.4f", currentQuery, queryName, minValue, maxValue));
results.forEach((r) -> {
BigDecimal value = (BigDecimal) r.getPropertyValueByQueryName(queryName);
if (value.compareTo(minValue) < 0 || value.compareTo(maxValue) > 0)
{
Assert.fail(String.format("%s column values expected to be in range from %.4f to %.4f, but found %.4f", queryName, minValue, maxValue, value));
}
});
return this;
}
public <T> QueryResultAssertion isReturningValues(String queryName, Set<T> values)
{
return isReturningValues(queryName, values, false);
}
public <T> QueryResultAssertion isReturningValues(String queryName, Set<T> values, boolean multivalue)
{
STEP(String.format("Verify that query: '%s' returns the values from %s for column %s", currentQuery, values, queryName));
Function<QueryResult, Object> extractValue = (multivalue ? (r -> r.getPropertyMultivalueById(queryName)) : r -> r.getPropertyValueById(queryName));
Set<Object> resultSet = results.stream().map(extractValue).collect(toSet());
Assert.assertEquals(resultSet, values, "Values did not match - expected " + values + " got " + resultSet);
return this;
}
public <T> QueryResultAssertion isReturningOrderedValues(String queryName, List<T> values)
{
return isReturningOrderedValues(queryName, values, false);
}
public <T> QueryResultAssertion isReturningOrderedValues(String queryName, List<T> values, boolean multivalue)
{
STEP(String.format("Verify that query: '%s' returns the values from %s for column %s", currentQuery, values, queryName));
Function<QueryResult, Object> extractValue = (multivalue ? (r -> r.getPropertyMultivalueById(queryName)) : r -> r.getPropertyValueById(queryName));
List<Object> resultList = results.stream().map(extractValue).collect(toList());
// Include both lists in assertion message as TestNG does not provide this information.
Assert.assertEquals(resultList, values, "Values did not match expected " + values + " but found " + resultList);
return this;
}
private String showErrorMessage()
{
return String.format("Returned results count of Query [%s] is not the expected one:", currentQuery);
}
}
}

View File

@@ -0,0 +1,10 @@
package org.alfresco.cmis.exception;
public class InvalidCmisObjectException extends RuntimeException
{
private static final long serialVersionUID = 1L;
public InvalidCmisObjectException(String reason)
{
super(reason);
}
}

View File

@@ -0,0 +1,12 @@
package org.alfresco.cmis.exception;
public class UnrecognizedBinding extends Exception
{
private static final long serialVersionUID = 1L;
private static final String DEFAULT_MESSAGE = "Unrecognized CMIS Binding [%s]. Available binding options: BROWSER or ATOM";
public UnrecognizedBinding(String binding)
{
super(String.format(DEFAULT_MESSAGE, binding));
}
}

View File

@@ -0,0 +1,16 @@
<?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/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="org.alfresco" />
<import resource="classpath:dataprep-context.xml" />
<import resource="classpath*:alfresco-tester-context.xml" />
</beans>

View File

@@ -0,0 +1,76 @@
# dataprep related
alfresco.scheme=http
alfresco.server=localhost
alfresco.port=8081
# credentials
admin.user=admin
admin.password=admin
solrWaitTimeInSeconds=30
# in containers we cannot access directly JMX, so we will use http://jolokia.org agent
# disabling this we will use direct JMX calls to server
jmx.useJolokiaAgent=false
# Server Health section
# in ServerHealth#isServerReachable() - could also be shown.
# enable this option to view if on server there are tenants or not
serverHealth.showTenants=true
# set CMIS binding to 'browser' or 'atom'
cmis.binding=browser
cmis.basePath=/alfresco/api/-default-/public/cmis/versions/1.1/${cmis.binding}
# TEST MANAGEMENT SECTION - Test Rail
#
# (currently supporting Test Rail v5.2.1.3472 integration)
#
# Example of configuration:
# ------------------------------------------------------
# if testManagement.enabled=true we enabled TestRailExecutorListener (if used in your suite xml file)
# testManagement.updateTestExecutionResultsOnly=true (this will just update the results of a test: no step will be updated - good for performance)
# testManagement.endPoint=https://alfresco.testrail.com/
# testManagement.username=<username>
# testManagement.apiKey=<api-key>
# testManagement.project=<id-of-your-project
# testManagement.testRun=<test-run-name>
# testManagement.includeOnlyTestCasesExecuted=true #if you want to include in your run ONLY the test cases that you run, then set this value to false
# testManagement.rateLimitInSeconds=1 #is the default rate limit after what minimum time, should we upload the next request. http://docs.gurock.com/testrail-api2/introduction #Rate Limit
# testManagement.suiteId=23 (the id of the Master suite)
# ------------------------------------------------------
testManagement.enabled=false
testManagement.endPoint=
testManagement.username=
testManagement.apiKey=
testManagement.project=7
testManagement.includeOnlyTestCasesExecuted=true
testManagement.rateLimitInSeconds=1
testManagement.testRun=MyTestRunInTestRail
testManagement.suiteId=12
# The location of the reports path
reports.path=./target/reports
#
# Database Section
# You should provide here the database URL, that can be a differed server as alfresco.
# https://docs.oracle.com/javase/tutorial/jdbc/basics/connecting.html
#
# Current supported db.url:
#
# MySQL:
# db.url = jdbc:mysql://${alfresco.server}:3306/alfresco
#
# PostgreSQL:
# db.url = jdbc:postgresql://<your-DB-IP>:3306/alfresco
#
# Oracle:
# db.url = jdbc:oracle://<your-DB-IP>:3306/alfresco
#
# MariaDB:
# db.url = jdbc:mariadb://<your-DB-IP>:3306/alfresco
#
db.url = jdbc:mysql://${alfresco.server}:3306/alfresco
db.username = alfresco
db.password = alfresco

View File

@@ -0,0 +1,26 @@
# Root logger option
log4j.rootLogger=INFO, file, stdout
# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./target/reports/alfresco-tas.log
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n
# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %m%n
# TestRail particular log file
# Direct log messages to a log file
log4j.appender.testrailLog=org.apache.log4j.RollingFileAppender
log4j.appender.testrailLog.File=./target/reports/alfresco-testrail.log
log4j.appender.testrailLog.MaxBackupIndex=10
log4j.appender.testrailLog.layout=org.apache.log4j.PatternLayout
log4j.appender.testrailLog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %c{1}:%L - %m%n
log4j.category.testrail=INFO, testrailLog
log4j.additivity.testrail=false

View File

@@ -1,9 +1,21 @@
package org.alfresco.cmis.search;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.alfresco.utility.report.log.Step.STEP;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.alfresco.cmis.CmisProperties;
import org.alfresco.cmis.dsl.QueryExecutor.QueryResultAssertion;
import org.alfresco.utility.Utility;
import org.alfresco.utility.model.ContentModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -45,32 +57,76 @@ public abstract class AbstractCmisE2ETest extends AbstractE2EFunctionalTest
/**
* Repeat Elastic Query till results count returns expectedCountResults
* @param query CMIS Query to be executed
* @param expectedCountResults Number of results expected
* @param expectedResultsCount Number of results expected
* @return true when results count is equals to expectedCountResults
*/
protected boolean waitForIndexing(String query, long expectedCountResults)
protected boolean waitForIndexing(String query, long expectedResultsCount)
{
for (int searchCount = 1; searchCount <= SEARCH_MAX_ATTEMPTS; searchCount++)
try
{
waitForIndexing(query, execution -> execution.hasLength(expectedResultsCount));
return true;
}
catch (AssertionError ae)
{
STEP("Received assertion error for query '" + query + "': " + ae);
return false;
}
}
/**
* Repeat Elastic Query until we get the expected results or we hit the retry limit.
*
* @param query CMIS Query to be executed
* @param expectedResults The expected results (unordered).
*/
protected void waitForIndexing(String query, ContentModel... expectedResults)
{
Set<String> expectedNames = Arrays.stream(expectedResults).map(ContentModel::getName).collect(toSet());
waitForIndexing(query, execution -> execution.isReturningValues("cmis:name", expectedNames));
}
/**
* Repeat Elastic Query until we get the expected results in the given order or we hit the retry limit.
*
* @param query CMIS Query to be executed
* @param expectedResults The expected results (ordered).
*/
protected void waitForIndexingOrdered(String query, ContentModel... expectedResults)
{
List<String> expectedNames = Arrays.stream(expectedResults).map(ContentModel::getName).collect(toList());
waitForIndexing(query, execution -> execution.isReturningOrderedValues("cmis:name", expectedNames));
}
/**
* Repeat Elastic Query until we get the expected results or we hit the retry limit.
*
* @param query CMIS Query to be executed
* @param assertionMethod A method that will be called to check the response and which will throw an AssertionError if they aren't what we want.
*/
protected void waitForIndexing(String query, Consumer<QueryResultAssertion> assertionMethod)
{
int searchCount = 0;
while (true)
{
try
{
cmisApi.withQuery(query).assertResultsCount().equals(expectedCountResults);
return true;
assertionMethod.accept(cmisApi.withQuery(query).assertValues());
return;
}
catch (AssertionError ae)
{
LOGGER.info(String.format("WaitForIndexing in Progress: %s", ae));
searchCount++;
if (searchCount < SEARCH_MAX_ATTEMPTS)
{
LOGGER.info(String.format("WaitForIndexing in Progress: %s", ae));
Utility.waitToLoopTime(getElasticWaitTimeInSeconds(), "Wait For Indexing");
}
else
{
throw ae;
}
}
Utility.waitToLoopTime(getElasticWaitTimeInSeconds(), "Wait For Indexing");
}
return false;
}
}

View File

@@ -1,12 +1,12 @@
package org.alfresco.cmis.search;
import java.util.List;
import java.util.Set;
import org.alfresco.utility.Utility;
import org.alfresco.utility.data.provider.XMLDataConfig;
import org.alfresco.utility.data.provider.XMLTestDataProvider;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FileType;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.QueryModel;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@@ -17,6 +17,21 @@ public class SearchInFolderTests extends AbstractCmisE2ETest
private FolderModel parentFolder, subFolder1, subFolder2, subFolder3;
private FileModel subFile1, subFile2, subFile3, subFile4, subFile5;
/**
* Create test data in the following format:
* <pre>
* testSite
* +- parentFolder
* +- subFile5 (fifthFile.txt: "fifthFile content")
* +- subFolder1
* +- subFolder2
* +- subFolder3 (subFolder)
* +- subFile1 (firstFile.xls)
* +- subFile2 (.pptx)
* +- subFile3 (.txt)
* +- subFile4 (fourthFile.docx: "fourthFileTitle", "fourthFileDescription")
* </pre>
*/
@BeforeClass(alwaysRun = true)
public void createTestData() throws Exception
{
@@ -51,12 +66,164 @@ public class SearchInFolderTests extends AbstractCmisE2ETest
dataContent.deleteSite(testSite);
}
@Test(dataProviderClass = XMLTestDataProvider.class, dataProvider = "getQueriesData")
@XMLDataConfig(file = "src/test/resources/search-in-folder.xml")
public void executeCMISQuery(QueryModel query)
@Test
public void executeCMISQuery_selectFieldsFromFolder()
{
String currentQuery = String.format(query.getValue(), parentFolder.getNodeRef());
String query = "SELECT cmis:name, cmis:parentId, cmis:path, cmis:allowedChildObjectTypeIds" +
" FROM cmis:folder where IN_FOLDER('%s') AND cmis:name = 'subFolder'";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
Assert.assertTrue(waitForIndexing(currentQuery, query.getResults()), String.format("Result count not as expected for query: %s", currentQuery));
waitForIndexing(currentQuery, subFolder3);
}
@Test
public void executeCMISQuery_selectFieldsFromDocument()
{
String query = "SELECT cmis:name, cmis:objectId, cmis:lastModifiedBy, cmis:creationDate, cmis:contentStreamFileName" +
" FROM cmis:document where IN_FOLDER('%s') AND cmis:name = 'fourthFile'";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexing(currentQuery, subFile4);
}
@Test
public void executeCMISQuery_selectParentId()
{
String query = "SELECT cmis:parentId FROM cmis:folder where IN_FOLDER('%s')";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
// Expect to get the same parent for each of the three matches.
String parentId = parentFolder.getNodeRef();
List<String> expectedParentIds = List.of(parentId, parentId, parentId);
waitForIndexing(currentQuery, execution -> execution.isReturningOrderedValues("cmis:parentId", expectedParentIds));
}
@Test
public void executeCMISQuery_inFolder()
{
String query = "SELECT * FROM cmis:document where IN_FOLDER('%s')";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexing(currentQuery, subFile1, subFile2, subFile3, subFile4, subFile5);
}
@Test
public void executeCMISQuery_orderByNameAsc()
{
String query = "SELECT * FROM cmis:document where IN_FOLDER('%s') AND cmis:name NOT LIKE 'file%%' ORDER BY cmis:name ASC";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexingOrdered(currentQuery, subFile5, subFile1, subFile4);
}
@Test
public void executeCMISQuery_orderByNameDesc()
{
String query = "SELECT * FROM cmis:document where IN_FOLDER('%s') AND cmis:name NOT LIKE 'file%%' ORDER BY cmis:name DESC";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexingOrdered(currentQuery, subFile4, subFile1, subFile5);
}
@Test
public void executeCMISQuery_orderByLastModifiedAsc()
{
String query = "SELECT * FROM cmis:folder where IN_FOLDER('%s') ORDER BY cmis:lastModificationDate ASC";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexingOrdered(currentQuery, subFolder1, subFolder2, subFolder3);
}
@Test
public void executeCMISQuery_orderByLastModifiedDesc()
{
String query = "SELECT * FROM cmis:folder where IN_FOLDER('%s') ORDER BY cmis:lastModificationDate DESC";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexingOrdered(currentQuery, subFolder3, subFolder2, subFolder1);
}
@Test
public void executeCMISQuery_orderByCreatedBy()
{
String query = "SELECT * FROM cmis:document where IN_FOLDER('%s') ORDER BY cmis:createdBy DESC";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
// All the results were created by the same user, so we can't assert anything about the order.
waitForIndexing(currentQuery, subFile5, subFile1, subFile2, subFile3, subFile4);
}
@Test
public void executeCMISQuery_documentNameNotNull()
{
String query = "SELECT * FROM cmis:document where IN_FOLDER('%s') AND cmis:name IS NOT NULL";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexing(currentQuery, subFile1, subFile2, subFile3, subFile4, subFile5);
}
@Test
public void executeCMISQuery_folderNameNotNull()
{
String query = "SELECT * FROM cmis:folder where IN_FOLDER('%s') AND cmis:name IS NOT NULL";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexing(currentQuery, subFolder1, subFolder2, subFolder3);
}
@Test
public void executeCMISQuery_nameLike()
{
String query = "SELECT * FROM cmis:document where IN_FOLDER('%s') AND cmis:name LIKE 'fourthFile'";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexingOrdered(currentQuery, subFile4);
}
@Test
public void executeCMISQuery_doubleNegative()
{
String query = "SELECT * FROM cmis:folder where IN_FOLDER('%s') AND NOT(cmis:name NOT IN ('subFolder'))";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexingOrdered(currentQuery, subFolder3);
}
@Test
public void executeCMISQuery_nameInList()
{
String query = "SELECT * FROM cmis:document where IN_FOLDER('%s') AND cmis:name IN ('fourthFile', 'fifthFile.txt')";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexing(currentQuery, subFile4, subFile5);
}
@Test
public void executeCMISQuery_nameNotInList()
{
String query = "SELECT * FROM cmis:document where IN_FOLDER('%s') AND cmis:name NOT IN ('fourthFile', 'fifthFile.txt')";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexing(currentQuery, subFile1, subFile2, subFile3);
}
@Test
public void executeCMISQuery_nameDifferentFrom()
{
String query = "SELECT * FROM cmis:folder where IN_FOLDER('%s') AND cmis:name <> 'subFolder'";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
waitForIndexing(currentQuery, subFolder1, subFolder2);
}
@Test
public void executeCMISQuery_selectSecondaryObjectTypeIds()
{
String query = "SELECT cmis:secondaryObjectTypeIds FROM cmis:folder where IN_FOLDER('%s') AND cmis:name = 'subFolder'";
String currentQuery = String.format(query, parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
Set<List<String>> expectedSecondaryObjectTypeIds = Set.of(List.of("P:cm:titled", "P:sys:localized"));
waitForIndexing(currentQuery, execution -> execution.isReturningValues("cmis:secondaryObjectTypeIds", expectedSecondaryObjectTypeIds, true));
Assert.assertTrue(waitForIndexing(currentQuery, 1), String.format("Result count not as expected for query: %s", currentQuery));
}
}

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<developers>
@@ -73,6 +73,7 @@
<dependency>
<groupId>org.alfresco.tas</groupId>
<artifactId>cmis</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
@@ -106,8 +107,10 @@
<suiteXmlFiles>
<suiteXmlFile>${suiteXmlFile}</suiteXmlFile>
</suiteXmlFiles>
<!-- Keeping illegal-access=warn for Java 11 compatibility, even though it has no effect on JDK 17 -->
<argLine>
--illegal-access=warn
--add-opens=java.base/java.lang=ALL-UNNAMED
</argLine>
</configuration>
</plugin>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>17.43-SNAPSHOT</version>
<version>20.5-SNAPSHOT</version>
</parent>
<developers>
@@ -78,8 +78,10 @@
<suiteXmlFiles>
<suiteXmlFile>${suiteXmlFile}</suiteXmlFile>
</suiteXmlFiles>
<!-- Keeping illegal-access=warn for Java 11 compatibility, even though it has no effect on JDK 17 -->
<argLine>
--illegal-access=warn
--add-opens=java.base/java.lang=ALL-UNNAMED
</argLine>
</configuration>
</plugin>

View File

@@ -5,6 +5,7 @@ import java.lang.reflect.Method;
import org.alfresco.dataprep.WorkflowService;
import org.alfresco.rest.core.RestProperties;
import org.alfresco.rest.core.RestWrapper;
import org.alfresco.rest.rules.RulesTestsUtils;
import org.alfresco.utility.LogFactory;
import org.alfresco.utility.TasProperties;
import org.alfresco.utility.data.DataContent;
@@ -61,6 +62,9 @@ public abstract class RestTest extends AbstractTestNGSpringContextTests
@Autowired
protected WorkflowService workflow;
@Autowired
protected RulesTestsUtils rulesUtils;
protected SiteModel testSite;
@BeforeSuite(alwaysRun = true)

View File

@@ -2,10 +2,13 @@ package org.alfresco.rest.actions;
import static org.testng.Assert.assertFalse;
import com.google.common.collect.ImmutableMap;
import java.util.List;
import com.google.common.collect.ImmutableMap;
import org.alfresco.dataprep.CMISUtil;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestActionConstraintDataModel;
import org.alfresco.rest.model.RestActionConstraintModel;
import org.alfresco.rest.model.RestActionDefinitionModel;
import org.alfresco.rest.model.RestActionDefinitionModelsCollection;
import org.alfresco.rest.model.RestNodeModel;
@@ -148,4 +151,66 @@ public class ActionsTests extends RestTest
restActionDefinition.getDescription().equals("This will add an aspect to the matched item.");
restActionDefinition.getTitle().equals("Add aspect");
}
@TestRail(section = {TestGroup.REST_API, TestGroup.ACTIONS}, executionType = ExecutionType.SANITY,
description = "Sanity test for ACTIONS endpoint GET action-conditions/{actionConstraintName}")
@Test(groups = {TestGroup.REST_API, TestGroup.ACTIONS, TestGroup.SANITY})
public void testGetSingleActionConstraint()
{
final UserModel testUser = dataUser.createRandomTestUser();
restClient.authenticateUser(testUser);
final String compareOperationsName = "ac-compare-operations";
final RestActionConstraintModel actionConstraintCompareOperations =
restClient.withCoreAPI().usingActions().getActionConstraintByName(compareOperationsName);
restClient.assertStatusCodeIs(HttpStatus.OK);
final RestActionConstraintModel expectedComparatorConstraints = new RestActionConstraintModel();
expectedComparatorConstraints.setConstraintName(compareOperationsName);
expectedComparatorConstraints.setConstraintValues(getComparatorConstraints());
actionConstraintCompareOperations.assertThat().isEqualTo(expectedComparatorConstraints);
}
@TestRail(section = {TestGroup.REST_API, TestGroup.ACTIONS}, executionType = ExecutionType.SANITY,
description = "Sanity test for ACTIONS endpoint GET action-conditions/{actionConstraintName} - non existing constraint name")
@Test(groups = {TestGroup.REST_API, TestGroup.ACTIONS, TestGroup.SANITY})
public void testGetSingleNonExistingActionConstraint()
{
final UserModel testUser = dataUser.createRandomTestUser();
restClient.authenticateUser(testUser);
restClient.withCoreAPI().usingActions().getActionConstraintByName("dummy-name");
restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND);
}
private List<RestActionConstraintDataModel> getComparatorConstraints()
{
final RestActionConstraintDataModel equalsConstraint = new RestActionConstraintDataModel();
equalsConstraint.setValue("EQUALS");
equalsConstraint.setLabel("Equals");
final RestActionConstraintDataModel containsConstraint = new RestActionConstraintDataModel();
containsConstraint.setValue("CONTAINS");
containsConstraint.setLabel("Contains");
final RestActionConstraintDataModel beginsConstraint = new RestActionConstraintDataModel();
beginsConstraint.setValue("BEGINS");
beginsConstraint.setLabel("Begins With");
final RestActionConstraintDataModel endsConstraint = new RestActionConstraintDataModel();
endsConstraint.setValue("ENDS");
endsConstraint.setLabel("Ends With");
final RestActionConstraintDataModel greaterThanConstraint = new RestActionConstraintDataModel();
greaterThanConstraint.setValue("GREATER_THAN");
greaterThanConstraint.setLabel("Greater Than");
final RestActionConstraintDataModel greaterThanEqualConstraint = new RestActionConstraintDataModel();
greaterThanEqualConstraint.setValue("GREATER_THAN_EQUAL");
greaterThanEqualConstraint.setLabel("Greater Than Or Equal To");
final RestActionConstraintDataModel lessThanConstraint = new RestActionConstraintDataModel();
lessThanConstraint.setValue("LESS_THAN");
lessThanConstraint.setLabel("Less Than");
final RestActionConstraintDataModel lessThanEqualConstraint = new RestActionConstraintDataModel();
lessThanEqualConstraint.setValue("LESS_THAN_EQUAL");
lessThanEqualConstraint.setLabel("Less Than Or Equal To");
return List.of(equalsConstraint, containsConstraint, beginsConstraint, endsConstraint, greaterThanConstraint,
greaterThanEqualConstraint, lessThanConstraint, lessThanEqualConstraint);
}
}

View File

@@ -0,0 +1,104 @@
package org.alfresco.rest.actions.access;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.gson.Gson;
import org.alfresco.rest.actions.access.pojo.Action;
import org.alfresco.rest.actions.access.pojo.ActionCondition;
import org.alfresco.rest.actions.access.pojo.Rule;
import org.alfresco.utility.model.UserModel;
public class AccessRestrictionUtil {
public static final String MAIL_ACTION = "mail";
public static final String ERROR_MESSAGE_FIELD = "message";
public static final String ERROR_MESSAGE_ACCESS_RESTRICTED =
"Only admin or system user is allowed to define uses of or directly execute this action";
private static final String ERROR_MESSAGE_FAILED_TO_SEND_EMAIL = "Failed to send email to:";
public static Map<String, Serializable> createMailParameters(UserModel sender, UserModel recipient) {
Map<String, Serializable> parameterValues = new HashMap<>();
parameterValues.put("from", sender.getEmailAddress());
parameterValues.put("to", recipient.getEmailAddress());
parameterValues.put("subject", "Test");
parameterValues.put("text", "<html>content</html>");
return parameterValues;
}
public static Rule createRuleWithAction(String actionName, Map<String, Serializable> parameterValues) {
Rule rule = new Rule();
rule.setId("");
rule.setTitle("Test rule title");
rule.setDescription("Test rule description");
rule.setRuleType(List.of("inbound"));
rule.setDisabled(false);
rule.setApplyToChildren(false);
rule.setExecuteAsynchronously(false);
Action compositeAction = new Action();
compositeAction.setActionDefinitionName("composite-action");
ActionCondition actionCondition = new ActionCondition();
actionCondition.setConditionDefinitionName("no-condition");
actionCondition.setParameterValues(new HashMap<>());
compositeAction.setConditions(List.of(actionCondition));
Action action = createAction(actionName, parameterValues);
compositeAction.setActions(List.of(action));
rule.setAction(compositeAction);
return rule;
}
public static Action createActionWithParameters(String actionName, Map<String, Serializable> parameterValues) {
Action compositeAction = new Action();
compositeAction.setActionDefinitionName("composite-action");
ActionCondition actionCondition = new ActionCondition();
actionCondition.setConditionDefinitionName("no-condition");
actionCondition.setParameterValues(new HashMap<>());
compositeAction.setConditions(List.of(actionCondition));
Action action = createAction(actionName, parameterValues);
action.setExecuteAsynchronously(false);
compositeAction.setActions(List.of(action));
return action;
}
public static Action createAction(String actionName, Map<String, Serializable> parameterValues) {
Action action = new Action();
action.setActionDefinitionName(actionName);
action.setParameterValues(parameterValues);
return action;
}
public static String mapObjectToJSON(Object object) {
Gson gson = new Gson();
return gson.toJson(object);
}
/**
* Return error message that in fact means that the action has passed access restriction correctly,
* but due to non-configured smtp couldn't send email.
*
* @param userModel
* @return
*/
public static String getExpectedEmailSendFailureMessage(UserModel userModel) {
return ERROR_MESSAGE_FAILED_TO_SEND_EMAIL + userModel.getEmailAddress();
}
}

View File

@@ -0,0 +1,85 @@
package org.alfresco.rest.actions.access;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.ERROR_MESSAGE_ACCESS_RESTRICTED;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.ERROR_MESSAGE_FIELD;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.MAIL_ACTION;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.createMailParameters;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.getExpectedEmailSendFailureMessage;
import static org.hamcrest.Matchers.containsString;
import java.io.Serializable;
import java.util.Map;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.core.RestRequest;
import org.alfresco.rest.core.RestResponse;
import org.alfresco.rest.core.RestWrapper;
import org.alfresco.utility.model.UserModel;
import org.json.simple.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class FormProcAdminAccessRestrictionTest extends RestTest {
private static final String ACTION_FORM_PROCESSOR_ENDPOINT = "alfresco/service/api/action/%s/formprocessor";
private static final String PROPERTY_PREFIX = "prop_";
private UserModel adminUser;
private UserModel testUser;
@Autowired
protected RestWrapper restClient;
@BeforeClass(alwaysRun = true)
public void dataPreparation() throws Exception {
adminUser = dataUser.getAdminUser();
testUser = dataUser.createRandomTestUser();
}
@BeforeMethod(alwaysRun=true)
public void setup() {
restClient.configureRequestSpec()
.setBasePath("")
.addHeader("Content-Type", "application/json");
}
@Test
public void userShouldNotCreateAMailForm() {
restClient.authenticateUser(testUser);
String body = generateBody(createMailParameters(adminUser, testUser));
String endpoint = String.format(ACTION_FORM_PROCESSOR_ENDPOINT, MAIL_ACTION);
RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, body, endpoint);
RestResponse response = restClient.process(request);
response.assertThat().statusCode(HttpStatus.INTERNAL_SERVER_ERROR.value())
.assertThat().body(ERROR_MESSAGE_FIELD, containsString(ERROR_MESSAGE_ACCESS_RESTRICTED));
}
@Test
public void adminShouldCreateAMailForm() {
restClient.authenticateUser(adminUser);
String body = generateBody(createMailParameters(adminUser, testUser));
String endpoint = String.format(ACTION_FORM_PROCESSOR_ENDPOINT, MAIL_ACTION);
RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, body, endpoint);
RestResponse response = restClient.process(request);
response.assertThat().statusCode(HttpStatus.INTERNAL_SERVER_ERROR.value())
.assertThat().body(ERROR_MESSAGE_FIELD, containsString(getExpectedEmailSendFailureMessage(testUser)));
}
private String generateBody(Map<String, Serializable> mailParameters) {
JSONObject json = new JSONObject();
mailParameters.forEach((key, value) -> json.put(PROPERTY_PREFIX + key, value));
return json.toJSONString();
}
}

View File

@@ -0,0 +1,102 @@
package org.alfresco.rest.actions.access;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.actions.access.pojo.Rule;
import org.alfresco.rest.core.RestRequest;
import org.alfresco.rest.core.RestResponse;
import org.alfresco.rest.core.RestWrapper;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FileType;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.ERROR_MESSAGE_ACCESS_RESTRICTED;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.ERROR_MESSAGE_FIELD;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.MAIL_ACTION;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.createMailParameters;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.createRuleWithAction;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.mapObjectToJSON;
import static org.hamcrest.Matchers.containsString;
public class RuleAdminAccessRestrictionTest extends RestTest {
private static final String CREATE_RULE_ENDPOINT = "alfresco/service/api/node/workspace/SpacesStore/%s/ruleset/rules";
private UserModel adminUser;
private UserModel testUser;
private FolderModel testFolder;
@Autowired
protected RestWrapper restClient;
@BeforeClass(alwaysRun = true)
public void dataPreparation() throws Exception {
adminUser = dataUser.getAdminUser();
testUser = dataUser.createRandomTestUser();
testSite = dataSite.usingUser(testUser)
.createPublicRandomSite();
testFolder = dataContent.usingUser(testUser)
.usingSite(testSite)
.createFolder();
}
@BeforeMethod(alwaysRun=true)
public void setup() {
restClient.configureRequestSpec().setBasePath("");
}
@Test
public void userShouldNotBeAbleToCreateANewRule() {
restClient.authenticateUser(testUser);
Rule rule = createRuleWithAction(MAIL_ACTION, createMailParameters(adminUser, testUser));
String ruleRequestBody = mapObjectToJSON(rule);
String ruleEndpoint = String.format(CREATE_RULE_ENDPOINT, testFolder.getNodeRef());
RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, ruleRequestBody, ruleEndpoint);
RestResponse response = restClient.process(request);
response.assertThat().statusCode(HttpStatus.INTERNAL_SERVER_ERROR.value())
.assertThat().body(ERROR_MESSAGE_FIELD, containsString(ERROR_MESSAGE_ACCESS_RESTRICTED));
}
@Test
public void adminShouldBeAbleToCreateANewRule() {
restClient.authenticateUser(adminUser);
Rule rule = createRuleWithAction(MAIL_ACTION, createMailParameters(adminUser, testUser));
String ruleRequestBody = mapObjectToJSON(rule);
String ruleEndpoint = String.format(CREATE_RULE_ENDPOINT, testFolder.getNodeRef());
RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, ruleRequestBody, ruleEndpoint);
RestResponse response = restClient.process(request);
response.assertThat().statusCode(HttpStatus.OK.value());
}
@Test
public void userShouldAddAFileToFolderWithMailRule() {
restClient.authenticateUser(adminUser);
Rule rule = createRuleWithAction(MAIL_ACTION, createMailParameters(adminUser, testUser));
String ruleRequestBody = mapObjectToJSON(rule);
String ruleEndpoint = String.format(CREATE_RULE_ENDPOINT, testFolder.getNodeRef());
RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, ruleRequestBody, ruleEndpoint);
RestResponse response = restClient.process(request);
response.assertThat().statusCode(HttpStatus.OK.value());
dataContent.usingUser(testUser)
.usingSite(testSite)
.usingResource(testFolder)
.createContent(FileModel.getRandomFileModel(FileType.TEXT_PLAIN));
}
}

View File

@@ -0,0 +1,76 @@
package org.alfresco.rest.actions.access;
import org.alfresco.rest.actions.access.pojo.Action;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.core.RestRequest;
import org.alfresco.rest.core.RestResponse;
import org.alfresco.rest.core.RestWrapper;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.ERROR_MESSAGE_ACCESS_RESTRICTED;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.ERROR_MESSAGE_FIELD;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.MAIL_ACTION;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.createActionWithParameters;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.createMailParameters;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.getExpectedEmailSendFailureMessage;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.mapObjectToJSON;
import static org.hamcrest.Matchers.containsString;
public class V0AdminAccessRestrictionTest extends RestTest {
private static final String ACTION_QUEUE_ENDPOINT = "alfresco/service/api/actionQueue?async=false";
private UserModel adminUser;
private UserModel testUser;
@Autowired
protected RestWrapper restClient;
@BeforeClass(alwaysRun = true)
public void dataPreparation() throws Exception {
adminUser = dataUser.getAdminUser();
testUser = dataUser.createRandomTestUser();
testSite = dataSite.usingUser(testUser)
.createPublicRandomSite();
}
@BeforeMethod(alwaysRun=true)
public void setup() {
restClient.configureRequestSpec().setBasePath("");
}
@Test
public void userShouldNotExecuteMailActionQueue() {
restClient.authenticateUser(testUser);
Action action = createActionWithParameters(MAIL_ACTION, createMailParameters(adminUser, testUser));
String actionRequestBody = mapObjectToJSON(action);
RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, actionRequestBody, ACTION_QUEUE_ENDPOINT);
RestResponse response = restClient.process(request);
response.assertThat().statusCode(HttpStatus.INTERNAL_SERVER_ERROR.value())
.assertThat().body(ERROR_MESSAGE_FIELD, containsString(ERROR_MESSAGE_ACCESS_RESTRICTED));
}
@Test
public void adminShouldExecuteMailActionQueue() {
restClient.authenticateUser(adminUser);
Action action = createActionWithParameters(MAIL_ACTION, createMailParameters(adminUser, testUser));
String actionRequestBody = mapObjectToJSON(action);
RestRequest request = RestRequest.requestWithBody(HttpMethod.POST, actionRequestBody, ACTION_QUEUE_ENDPOINT);
RestResponse response = restClient.process(request);
response.assertThat().statusCode(HttpStatus.INTERNAL_SERVER_ERROR.value())
.assertThat().body(ERROR_MESSAGE_FIELD, containsString(getExpectedEmailSendFailureMessage(testUser)));
}
}

View File

@@ -0,0 +1,63 @@
package org.alfresco.rest.actions.access;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.ERROR_MESSAGE_ACCESS_RESTRICTED;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.MAIL_ACTION;
import static org.alfresco.rest.actions.access.AccessRestrictionUtil.createMailParameters;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.core.RestWrapper;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class V1AdminAccessRestrictionTest extends RestTest {
private UserModel adminUser;
private UserModel testUser;
private FolderModel testFolder;
@Autowired
protected RestWrapper restClient;
@BeforeClass(alwaysRun = true)
public void dataPreparation() throws Exception {
adminUser = dataUser.getAdminUser();
testUser = dataUser.createRandomTestUser();
testSite = dataSite.usingUser(testUser)
.createPublicRandomSite();
testFolder = dataContent.usingUser(testUser)
.usingSite(testSite)
.createFolder();
}
@Test
public void userShouldNotExecuteMailAction() throws Exception {
restClient.authenticateUser(testUser)
.withCoreAPI()
.usingActions()
.executeAction(MAIL_ACTION, testFolder, createMailParameters(adminUser, testUser));
restClient.onResponse()
.assertThat().statusCode(HttpStatus.FORBIDDEN.value())
.assertThat().body("entry.id", nullValue());
restClient.assertLastError().containsSummary(ERROR_MESSAGE_ACCESS_RESTRICTED);
}
@Test
public void adminShouldExecuteMailAction() throws Exception {
restClient.authenticateUser(adminUser)
.withCoreAPI()
.usingActions()
.executeAction(MAIL_ACTION, testFolder, createMailParameters(adminUser, testUser));
restClient.onResponse()
.assertThat().statusCode(HttpStatus.ACCEPTED.value())
.assertThat().body("entry.id", notNullValue());
}
}

View File

@@ -0,0 +1,58 @@
package org.alfresco.rest.actions.access.pojo;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
public class Action {
private String actionDefinitionName;
private String actionedUponNode;
private List<ActionCondition> conditions;
private List<Action> actions;
private Map<String, Serializable> parameterValues;
private boolean executeAsynchronously;
public void setExecuteAsynchronously(boolean executeAsynchronously) {
this.executeAsynchronously = executeAsynchronously;
}
public String getActionDefinitionName() {
return actionDefinitionName;
}
public void setActionDefinitionName(String actionDefinitionName) {
this.actionDefinitionName = actionDefinitionName;
}
public String getActionedUponNode() {
return actionedUponNode;
}
public void setActionedUponNode(String actionedUponNode) {
this.actionedUponNode = actionedUponNode;
}
public List<ActionCondition> getConditions() {
return conditions;
}
public void setConditions(List<ActionCondition> conditions) {
this.conditions = conditions;
}
public List<Action> getActions() {
return actions;
}
public void setActions(List<Action> actions) {
this.actions = actions;
}
public Map<String, Serializable> getParameterValues() {
return parameterValues;
}
public void setParameterValues(Map<String, Serializable> parameterValues) {
this.parameterValues = parameterValues;
}
}

View File

@@ -0,0 +1,24 @@
package org.alfresco.rest.actions.access.pojo;
import java.util.Map;
public class ActionCondition {
private String conditionDefinitionName;
private Map<String, String> parameterValues;
public String getConditionDefinitionName() {
return conditionDefinitionName;
}
public void setConditionDefinitionName(String conditionDefinitionName) {
this.conditionDefinitionName = conditionDefinitionName;
}
public Map<String, String> getParameterValues() {
return parameterValues;
}
public void setParameterValues(Map<String, String> parameterValues) {
this.parameterValues = parameterValues;
}
}

Some files were not shown because too many files have changed in this diff Show More