Compare commits

..

2 Commits

Author SHA1 Message Date
Damian.Ujma@hyland.com
cd8ef77b3e Increase sleep to 4 minutes 2022-09-06 12:27:56 +02:00
Damian.Ujma@hyland.com
8622e0e102 Init 2022-09-06 11:10:46 +02:00
631 changed files with 3688 additions and 58394 deletions

11
.github/workflows/hackathon.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
name: Hackathon
on:
push:
jobs:
sleep:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: sleep 240

View File

@@ -53,7 +53,7 @@ jobs:
- name: "Source Clear Scan (SCA)"
stage: test
if: (branch = master OR branch =~ /release\/.*/) AND type != pull_request
if: branch = master OR branch =~ /release\/.*/
# Run Veracode
install: skip
script: travis_wait 30 bash scripts/travis/source_clear.sh
@@ -264,8 +264,6 @@ 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_success: ${TAS_SCRIPTS}/output_tests_run.sh "packaging/tests/tas-restapi"
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\]/
@@ -274,8 +272,6 @@ 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_success: ${TAS_SCRIPTS}/output_tests_run.sh "packaging/tests/tas-restapi"
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\]/
@@ -284,8 +280,6 @@ 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_success: ${TAS_SCRIPTS}/output_tests_run.sh "packaging/tests/tas-restapi"
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\]/
@@ -294,8 +288,6 @@ 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_success: ${TAS_SCRIPTS}/output_tests_run.sh "packaging/tests/tas-cmis"
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\]/
@@ -304,8 +296,6 @@ 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_success: ${TAS_SCRIPTS}/output_tests_run.sh "packaging/tests/tas-cmis"
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\]/
@@ -314,8 +304,6 @@ 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_success: ${TAS_SCRIPTS}/output_tests_run.sh "packaging/tests/tas-cmis"
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\]/
@@ -324,8 +312,6 @@ 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_success: ${TAS_SCRIPTS}/output_tests_run.sh "packaging/tests/tas-email"
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\]/
@@ -334,8 +320,6 @@ 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_success: ${TAS_SCRIPTS}/output_tests_run.sh "packaging/tests/tas-webdav"
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\]/
@@ -343,10 +327,7 @@ 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_success: ${TAS_SCRIPTS}/output_tests_run.sh "packaging/tests/tas-integration"
after_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-integration"
- name: "Share Services - ShareServicesTestSuite"
if: commit_message !~ /\[skip repo\]/
@@ -396,7 +377,7 @@ jobs:
script: travis_retry travis_wait 80 mvn -B verify -Dmaven.javadoc.skip=true -Dmaven.source.skip=true -Pags -Pstart-mysql -PagsAllTestSuitePt4 -f amps/ags/pom.xml ${LOG_WARN}
- name: "AGS Community Rest API Tests"
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip ags\]/ AND commit_message !~ /\[skip tas\]/) OR (commit_message =~ /\[ags\]/ AND commit_message =~ /\[tas\]/)
if: (branch =~ /(release\/.*$|master)/ AND commit_message !~ /\[skip ags\]/) OR commit_message =~ /\[ags\]/
install: travis_retry travis_wait 40 env REQUIRES_LOCAL_IMAGES=true bash scripts/travis/build.sh
addons:
artifacts:
@@ -408,8 +389,7 @@ jobs:
before_script:
- ${TAS_SCRIPTS}/start-compose.sh ./amps/ags/rm-community/rm-community-repo/docker-compose.yml
- ${TAS_SCRIPTS}/wait-for-alfresco-start.sh "http://localhost:8080/alfresco"
- travis_wait 40 mvn -B install -pl :alfresco-governance-services-automation-community-rest-api -am -Pags -Pall-tas-tests -DskipTests
script: travis_wait 40 mvn -B test -pl :alfresco-governance-services-automation-community-rest-api -Dskip.automationtests=false -Pags -Pall-tas-tests
script: travis_wait 40 mvn -B test -pl :alfresco-governance-services-automation-community-rest-api -am -DfailIfNoTests=false -Dskip.automationtests=false -Pags
after_script: bash amps/ags/travis/scripts/getLogs.sh
- name: "Push to Nexus"

View File

@@ -7,22 +7,14 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<modules>
<module>rm-community</module>
<module>rm-automation</module>
</modules>
<profiles>
<profile>
<id>all-tas-tests</id>
<modules>
<module>rm-automation</module>
</modules>
</profile>
</profiles>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

View File

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

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
<version>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<build>
@@ -43,21 +43,14 @@
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>${dependency.log4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${dependency.log4j.version}</version>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-reload4j</artifactId>
<version>${dependency.slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.alfresco.tas</groupId>
<artifactId>restapi</artifactId>
<version>${project.version}</version>
<exclusions>
<exclusion>
<groupId>org.alfresco.tas</groupId>
@@ -69,17 +62,6 @@
<groupId>org.alfresco.tas</groupId>
<artifactId>utility</artifactId>
<version>${dependency.tas-utility.version}</version>
<!-- These exclusions can be removed once tas-utility does not rely on Reload4j anymore -->
<exclusions>
<exclusion>
<groupId>ch.qos.reload4j</groupId>
<artifactId>reload4j</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-reload4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>

View File

@@ -1,73 +0,0 @@
/*-
* #%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,7 +26,6 @@
*/
package org.alfresco.rest.v0;
import static org.apache.http.HttpStatus.SC_OK;
import static org.testng.Assert.assertTrue;
import java.io.UnsupportedEncodingException;
@@ -37,7 +36,6 @@ 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;
@@ -60,8 +58,6 @@ 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 .
*
@@ -88,21 +84,6 @@ 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.
*
@@ -119,19 +100,5 @@ 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,19 +191,4 @@ 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

@@ -97,16 +97,4 @@ public class RecordFoldersAPI extends BaseAPI
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,25 +360,4 @@ 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

@@ -1,146 +0,0 @@
/*
* #%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

@@ -1,244 +0,0 @@
/*
* #%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

@@ -1,246 +0,0 @@
/*
* #%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

@@ -1,120 +0,0 @@
/*
* #%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

@@ -1,175 +0,0 @@
/*
* #%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

@@ -72,7 +72,6 @@ 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;
@@ -260,7 +259,6 @@ 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,7 +61,6 @@ 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;
@@ -210,7 +209,6 @@ 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

@@ -1,311 +0,0 @@
/*
* #%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,7 +75,6 @@ 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;
/**
@@ -103,7 +102,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 */
@@ -134,10 +133,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)
{
@@ -155,17 +154,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);
@@ -188,9 +187,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()
{
@@ -235,7 +234,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);
@@ -319,14 +318,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());
completeRecord(recordFiled.getId());
assertStatusCode(OK);
getRestAPIFactory().getRecordsAPI().completeRecord(recordFiled.getId());
assertStatusCode(CREATED);
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());
@@ -349,14 +348,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

@@ -1,128 +0,0 @@
/*
* #%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

@@ -1,118 +0,0 @@
/*
* #%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.rules;
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.requests.gscore.api.RecordCategoryAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI;
import org.alfresco.rest.rm.community.smoke.CreateCategoriesTests;
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.CoreUtil.createBodyForMoveCopy;
import static org.alfresco.rest.rm.community.utils.CoreUtil.toContentModel;
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 CopyToRuleOnFoldersTest extends BaseRMRestTest {
private RecordCategory category;
private RecordCategoryChild folder1,folder2;
private final static String title = "Run in background";
private final String TEST_PREFIX = generateTestPrefix(CopyToRuleOnFoldersTest.class);
private final String RM_ADMIN = TEST_PREFIX + "rm_admin";
private final String electronicRecord = TEST_PREFIX + "record_electronic_for_copyTo";
private final String nonElectronicRecord = TEST_PREFIX + "record_non_electronic_for_copyTo";
@Autowired
private RulesAPI rulesAPI;
@Test
@AlfrescoTest(jira = "RM-2994")
public void copyToRuleOnFoldersTest()
{
RuleDefinition ruleDefinition = RuleDefinition.createNewRule().title("name").description("description1")
.runInBackground(true).title(title)
.actions(Collections.singletonList(ActionsOnRule.COPY_TO.getActionValue()));
STEP("Create the RM site if doesn't exist");
createRMSiteIfNotExists();
STEP("Create record categories and record folders");
category= createRootCategory(getRandomName("recordCategory"));
String folder1 = createCategoryFolderInFilePlan().getId();
String folder2 = createCategoryFolderInFilePlan().getId();
// create a rule on folder
rulesAPI.createRule(getAdminUser().getUsername(), getAdminUser().getPassword(), NODE_PREFIX + folder1, ruleDefinition);
// create electronic record in record folder
String electronicRecordId = createElectronicRecord(folder1, ELECTRONIC_RECORD_NAME).getId();
assertStatusCode(CREATED);
// create non-electronic record in record folder
String nonElectronicRecord = createElectronicRecord(folder1, NONELECTRONIC_RECORD_NAME).getId();
assertStatusCode(CREATED);
// Move the electronic and non-electronic records from "Category with records"> "Folder with rule"
// to "Copy Category with records" > "Folder with rule"
getRestAPIFactory().getNodeAPI(toContentModel(folder1)).copy(createBodyForMoveCopy(category.getId()));
getRestAPIFactory().getNodeAPI(toContentModel( electronicRecord)).move(createBodyForMoveCopy(folder2));
getRestAPIFactory().getNodeAPI(toContentModel( nonElectronicRecord)).move(createBodyForMoveCopy(folder2));
RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
// Delete the record category
RecordCategoryAPI recordCategoryAPI = getRestAPIFactory().getRecordCategoryAPI();
String recordCategoryId = category.getId();
recordCategoryAPI.deleteRecordCategory(recordCategoryId);
recordsAPI.deleteRecord(electronicRecord);
recordsAPI.deleteRecord(nonElectronicRecord);
assertStatusCode(NO_CONTENT);
}
}

View File

@@ -1,229 +0,0 @@
/*
* #%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.rules;
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.model.user.UserRoles;
import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI;
import org.alfresco.rest.rm.community.smoke.FileAsRecordTests;
import org.alfresco.rest.v0.RulesAPI;
import org.alfresco.rest.v0.service.RoleService;
import org.alfresco.test.AlfrescoTest;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static org.alfresco.rest.core.v0.BaseAPI.NODE_PREFIX;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
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.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.CREATED;
@AlfrescoTest (jira = "APPS-36")
public class FileAsRecordRuleTests extends BaseRMRestTest
{
private UserModel nonRMUser, rmManager;
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);
private FolderModel testFolder;
private FileModel document,inPlaceRecord;
@Autowired
private RoleService roleService;
@Autowired
private RulesAPI rulesAPI;
/**
* Create preconditions:
* 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
* 5. A collaboration folder with rule set to declare and file as record to a record folder
**/
@BeforeClass(alwaysRun = true)
public void preconditionForDeclareFileAsRecordRuleTests()
{
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 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);
STEP("Create a collaboration folder with a rule set to declare and file as record to a record folder");
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);
assertStatusCode(CREATED);
}
/**
* Given I am a user that can create a rule on a folder in a collaboration site
* When I am creating the rule
* Then I have the option of adding a "Declare and File as Record" action to the rule
* <p>
* Given I am creating a rule
* When I add the "Declare and File as Record" action to the rule
* Then I am able to select the record folder I want the declared record to be filed to
* <p>
* Given I am configuring a "Declare and File as Record" action within a rule
* And I have at least one records management role (eg RM User)
* When I am selecting the record folder location to file the declared record to
* Then I see the record folders in the file plan that I have file access to as the creator of the record
**/
@Test
public void declareAsRecordRuleAsRMUserWithFilingPermissions() {
STEP("Create a collaboration folder");
testFolder = dataContent.usingSite(testSite)
.usingUser(rmManager)
.createFolder();
STEP("Create a rule with Declare as Record action and check that user can select a record folder.");
RecordCategory recordCategory = new RecordCategory().builder()
.id(category_manager.getId()).build();
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);
assertStatusCode(CREATED);
}
/**
* Given I am configuring a "Declare and File as Record" action within a rule
* And I don't have a records management role
* When I am selecting the record folder location to file the declared record to
* Then I can see only the file plan
*/
@Test
public void declareAsRecordRuleAsNonRMUser()
{
STEP("Create a collaboration folder");
testFolder = dataContent.usingSite(testSite)
.usingUser(nonRMUser)
.createFolder();
STEP("Create a rule with Declare as Record action and check that user can select a record folder.");
RecordCategory recordCategory = new RecordCategory().builder()
.id(category_manager.getId()).build();
RuleDefinition ruleDefinition = RuleDefinition.createNewRule().title("name").description("description")
.applyToChildren(true)
.actions(Collections.singletonList(ActionsOnRule.DECLARE_AS_RECORD.getActionValue()));
rulesAPI.createRule(nonRMUser.getUsername(), nonRMUser.getPassword(), NODE_PREFIX + testFolder.getNodeRef(), ruleDefinition);
assertStatusCode(CREATED);
}
/**
* Given I have not selected a record folder location
* When the rule is triggered
* Then the file is declared as record to the UnFiled Records folder
*/
@Test
public void triggerDeclareToUnfiledRuleAsNonRMUser()
{
STEP("Create a collaboration folder with a rule set to declare and file as record without a record folder location");
RecordCategory recordCategory = new RecordCategory().builder()
.id(category_manager.getId()).build();
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);
assertStatusCode(CREATED);
STEP("Create as nonRMUser a new file into the previous folder in order to trigger the rule");
inPlaceRecord = dataContent.usingUser(nonRMUser).usingResource(testFolder).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
// Verify that 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(inPlaceRecord.getNodeRefWithoutVersion()))
.collect(Collectors.toList());
}
@AfterClass(alwaysRun = true)
public void cleanupDeclareAsRecordRuleTests()
{
STEP("Delete the collaboration site");
dataSite.usingUser(nonRMUser).deleteSite(testSite);
STEP("Delete Users");
dataUser.deleteUser(nonRMUser);
dataUser.deleteUser(rmManager);
STEP("Delete categories");
getRestAPIFactory().getFilePlansAPI().getRootRecordCategories(FILE_PLAN_ALIAS).getEntries().forEach(recordCategoryEntry ->
deleteRecordCategory(recordCategoryEntry.getEntry().getId()));
}
}

View File

@@ -1,221 +0,0 @@
/*
* #%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.rules;
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.model.user.UserRoles;
import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI;
import org.alfresco.rest.rm.community.smoke.FileAsRecordTests;
import org.alfresco.rest.v0.RulesAPI;
import org.alfresco.rest.v0.service.RoleService;
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.testng.annotations.AfterClass;
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 org.alfresco.rest.core.v0.BaseAPI.NODE_PREFIX;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
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.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.CREATED;
public class FileVersionAsRecordRuleTest extends BaseRMRestTest {
private UserModel nonRMuser, rmManager;
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);
private FolderModel testFolder;
private FileModel document,inPlaceRecord;
@Autowired
private RoleService roleService;
@Autowired
private RulesAPI rulesAPI;
@BeforeClass(alwaysRun = true)
public void createTestPrecondition()
{
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);
STEP("Create a collaboration folder with a rule set to declare and file version as record to a record folder");
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);
assertStatusCode(CREATED);
}
@Test
public void declareVersionAsRecordRuleAsRMUserWithFilingPermissions()
{
STEP("Create a collaboration folder");
testFolder = dataContent.usingSite(testSite)
.usingUser(rmManager)
.createFolder();
STEP("Create a rule with Declare as Record action and check that user can select a record folder.");
RecordCategory recordCategory = new RecordCategory().builder()
.id(category_manager.getId()).build();
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(rmManager.getUsername(), rmManager.getPassword(), NODE_PREFIX + testFolder.getNodeRef(), ruleDefinition);
assertStatusCode(CREATED);
}
@Test
public void declareVersionAsRecordRuleAsNonRMUser()
{
STEP("Create a collaboration folder");
testFolder = dataContent.usingSite(testSite)
.usingUser(nonRMuser)
.createFolder();
STEP("Create a rule with Declare as Record action and check that user can select a record folder.");
RecordCategory recordCategory = new RecordCategory().builder()
.id(category_manager.getId()).build();
RuleDefinition ruleDefinition = RuleDefinition.createNewRule().title("name").description("description")
.applyToChildren(true)
.actions(Collections.singletonList(ActionsOnRule.DECLARE_AS_RECORD.getActionValue()));
rulesAPI.createRule(nonRMuser.getUsername(), nonRMuser.getPassword(), NODE_PREFIX + testFolder.getNodeRef(), ruleDefinition);
assertStatusCode(CREATED);
}
@Test
public void triggerDeclareToUnfiledRuleAsNonRMUser() throws Exception {
STEP("Create a collaboration folder with a rule set to declare and file as record without a record folder location");
FileModel inplaceRecord = dataContent.usingSite(testSite).usingUser(nonRMuser)
.createContent(new FileModel("declareAndFileToIntoUnfiledRecordFolder",
FileType.TEXT_PLAIN));
RecordCategory recordCategory = new RecordCategory().builder()
.id(category_manager.getId()).build();
RuleDefinition ruleDefinition = RuleDefinition.createNewRule().title("name").description("description")
.applyToChildren(true)
.actions(Collections.singletonList(ActionsOnRule.DECLARE_AS_RECORD.getActionValue()));
rulesAPI.createRule(nonRMuser.getUsername(), nonRMuser.getPassword(), NODE_PREFIX + inplaceRecord.getNodeRef(), ruleDefinition);
assertStatusCode(CREATED);
STEP("Create as nonRMuser a new file into the previous folder in order to trigger the rule");
inPlaceRecord = dataContent.usingUser(nonRMuser).usingResource(testFolder).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
// 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(inplaceRecord.getNodeRefWithoutVersion()))
.collect(Collectors.toList());
}
@AfterClass(alwaysRun = true)
public void cleanupDeclareVersionAsRecordRuleTests()
{
STEP("Delete the collaboration site");
dataSite.usingUser(nonRMuser).deleteSite(testSite);
STEP("Delete Users");
dataUser.deleteUser(nonRMuser);
dataUser.deleteUser(rmManager);
STEP("Delete categories");
getRestAPIFactory().getFilePlansAPI().getRootRecordCategories(FILE_PLAN_ALIAS).getEntries().forEach(recordCategoryEntry ->
deleteRecordCategory(recordCategoryEntry.getEntry().getId()));
}
}

View File

@@ -1,237 +0,0 @@
/*
* #%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.rules;
import org.alfresco.rest.model.RestNodeModel;
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.rules.ActionsOnRule;
import org.alfresco.rest.rm.community.model.rules.ConditionsOnRule;
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.UnfiledContainerChildEntry;
import org.alfresco.rest.rm.community.model.user.UserRoles;
import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI;
import org.alfresco.rest.rm.community.requests.gscore.api.UnfiledContainerAPI;
import org.alfresco.rest.search.RestRequestQueryModel;
import org.alfresco.rest.v0.HoldsAPI;
import org.alfresco.rest.v0.RecordsAPI;
import org.alfresco.rest.v0.RulesAPI;
import org.alfresco.rest.v0.service.RoleService;
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.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import static java.lang.Integer.MAX_VALUE;
import static java.util.Arrays.asList;
import static org.alfresco.rest.core.v0.BaseAPI.NODE_PREFIX;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_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.rest.rm.community.utils.FilePlanComponentsUtil.*;
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.*;
import static org.testng.Assert.assertNotNull;
public class MoveToRuleOnFoldersTest extends BaseRMRestTest{
private RecordCategoryChild recordFolder2;
private RecordCategoryChild recordFolder1;
private String nonElectronicId;
public Record electronicRecord;
private String ruleType = ConditionsOnRule.UPDATE.getWhenConditionValue();
private UserModel rmAdmin;
public RecordCategory RecordCategoryOne;
private RecordCategoryChild recordFolder;
public static final String RECORD_FOLDER_ONE = "record-folder-one";
private final String TEST_PREFIX = generateTestPrefix(MoveToRuleOnFoldersTest.class);
private final String RECORD_CATEGORY_ONE = TEST_PREFIX + "category";
private final String recordName = "Test record";
private final String recordTitle = recordName + " title";
private final String recordDescription = recordName + " description";
private Record nonElectrinicRecordModel;
private RecordFolderAPI recordFolderAPI;
public String title,description,box,file,shelf,storageLocation,name;
@Autowired
private RulesAPI rulesAPI;
@Autowired
private HoldsAPI holdsAPI;
@Autowired
private RoleService roleService;
@Autowired
public RecordsAPI recordsAPI;
@BeforeClass(alwaysRun = true)
public void precondition()
{
//create RM site
createRMSiteIfNotExists();
rmAdmin = roleService.createUserWithRMRole(UserRoles.ROLE_RM_ADMIN.roleId);
//create root category, create folders , add electronic and non electronic records
RecordCategoryOne = createRootCategory(RECORD_CATEGORY_ONE);
recordFolder1=createRecordFolder(RecordCategoryOne.getId(), getRandomName("recFolder"));
// recordFolder1_id = createRecordFolder(RecordCategoryOne.getId(), getRandomName("recFolder")).getId();
recordFolder2 = createFolder(getAdminUser(),RecordCategoryOne.getId(),getRandomName("recFolder"));
STEP("CREATE ELECTRONIC RECORD");
recordFolderAPI = getRestAPIFactory().getRecordFolderAPI();
electronicRecord = recordFolderAPI.createRecord(createElectronicRecordModel(), recordFolder1.getId(), getFile(IMAGE_FILE));
STEP("Check the electronic record has been created");
assertStatusCode(CREATED);
STEP("Create a non-electronic record by completing some of the fields");
// Use these properties for non-electronic record to be created
title = "Title " + getRandomAlphanumeric();
description = "Description " + getRandomAlphanumeric();
box = "Box "+ getRandomAlphanumeric();
file = "File " + getRandomAlphanumeric();
shelf = "Shelf " + getRandomAlphanumeric();
storageLocation = "Storage Location " + getRandomAlphanumeric();
name = "Record " + getRandomAlphanumeric();
Random random = new Random();
Integer numberOfCopies = random.nextInt(MAX_VALUE);
Integer physicalSize = random.nextInt(MAX_VALUE);
// Set values of all available properties for the non electronic records
nonElectrinicRecordModel = createFullNonElectronicRecordModel(name, title, description, box, file, shelf, storageLocation, numberOfCopies, physicalSize);
// Create non-electronic record
nonElectronicId = recordFolderAPI.createRecord(nonElectrinicRecordModel, recordFolder1.getId()).getId();
STEP("Check the non-electronic record has been created");
assertStatusCode(CREATED);
}
@Test
public void MoveToRuleFoldersTest()
{
String CatName=RecordCategoryOne.getName();
String folder2name=recordFolder2.getName();
String recfolder2_path="/"+CatName+"/"+folder2name;
STEP("create a rule MOVE_TO for folder 1");
RuleDefinition ruleDefinition = RuleDefinition.createNewRule().title("name").description("description1")
.runInBackground(true).title(title)
.actions(Collections.singletonList(ActionsOnRule.MOVE_TO.getActionValue())).ruleType(ruleType).path(recfolder2_path);
rulesAPI.createRule(getAdminUser().getUsername(), getAdminUser().getPassword(), NODE_PREFIX +recordFolder1.getId() , ruleDefinition);
STEP("Update metadata for Non-Electronic Record");
updateRecordMetadata();
STEP("Delete ELECTRONIC AND NON-ELECTRONIC RECORDS IN FOLDER 2");
org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
recordsAPI.deleteRecord(electronicRecord.getId());
assertStatusCode(NO_CONTENT);
recordsAPI.deleteRecord(nonElectronicId);
assertStatusCode(NO_CONTENT);
STEP("RULE CREATION FOR FOLDER 1 WITHOUT RUNNING IN BACKGROUND");
RuleDefinition ruleDefinition_notinbackground = RuleDefinition.createNewRule().title("name").description("description1")
.runInBackground(false).title(title)
.actions(Collections.singletonList(ActionsOnRule.MOVE_TO.getActionValue())).ruleType(ruleType).path(recfolder2_path);
rulesAPI.createRule(getAdminUser().getUsername(), getAdminUser().getPassword(), NODE_PREFIX +recordFolder1.getId() , ruleDefinition);
STEP("CREATE ELECTRONIC AND NON-ELECTRONIC RECORDS");
electronicRecord = recordFolderAPI.createRecord(createElectronicRecordModel(), recordFolder1.getId(), getFile(IMAGE_FILE));
STEP("Check the electronic record has been created");
assertStatusCode(CREATED);
nonElectronicId = recordFolderAPI.createRecord(nonElectrinicRecordModel, recordFolder1.getId()).getId();
STEP("Check the non-electronic record has been created");
assertStatusCode(CREATED);
STEP("UPDATE METADATA");
updateRecordMetadata();
STEP("CHECK IF ELECTRONIC AND NON-ELECTRONIC RECORDS MOVED TO FOLDER2");
updateRecordMetadata();
}
@AfterClass(alwaysRun = true)
public void cleanMoveToRuleOnFoldersTest()
{
deleteRecordCategory(RecordCategoryOne.getId());
getDataUser().deleteUser(rmAdmin);
}
private String getModifiedPropertyValue(String originalValue) {
/* to be used to append to modifications */
String MODIFIED_PREFIX = "modified_";
return MODIFIED_PREFIX + originalValue;
}
private void updateRecordMetadata(){
STEP("Update metadata for Non-Electronic Record");
org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
Record nonelecrecord = recordsAPI.getRecord(nonElectronicId);
String nonelecnewName = getModifiedPropertyValue(nonElectrinicRecordModel.getName());
String nonelecnewTitle = getModifiedPropertyValue(nonElectrinicRecordModel.getProperties().getTitle());
String nonelecnewDescription = getModifiedPropertyValue(nonElectrinicRecordModel.getProperties().getDescription());
recordsAPI.updateRecord(createRecordModel(nonelecnewName, nonelecnewDescription, nonelecnewTitle),nonelecrecord.getId());
assertStatusCode(OK);
STEP("Update metadata for Electronic Record");
Record elecrecord = recordsAPI.getRecord(electronicRecord.getId());
String elecnewName = getModifiedPropertyValue(electronicRecord.getName());
String elecnewTitle = getModifiedPropertyValue(electronicRecord.getProperties().getTitle());
String elecnewDescription = getModifiedPropertyValue(electronicRecord.getProperties().getDescription());
recordsAPI.updateRecord(createRecordModel(elecnewName, elecnewDescription, elecnewTitle),elecrecord.getId());
assertStatusCode(OK);
}
}

View File

@@ -1,121 +0,0 @@
/*
* #%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

@@ -1,425 +0,0 @@
/*
* #%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

@@ -1,138 +0,0 @@
/*
* #%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

@@ -1,156 +0,0 @@
/*
* #%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,11 @@
# Root logger option
log4j.rootLogger=INFO, file
# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./target/reports/rm-automation-community-rest-api.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
log4j.logger.com.example=debug

View File

@@ -1,20 +0,0 @@
###### Root Logger #######
rootLogger.level=info
rootLogger.appenderRef.rolling.ref=RollingAppender
###### File appender definition #######
appender.rolling.type=RollingFile
appender.rolling.name=RollingAppender
appender.rolling.fileName=target/reports/rm-automation-community-rest-api.log
appender.rolling.filePattern=target/reports/rm-automation-community-rest-api.log.%i
appender.rolling.layout.type=PatternLayout
appender.rolling.layout.pattern=[%t] %d{HH:mm:ss} %-5p %c{1}:%L - %replace{%m}{[\r\n]+}{}%n
appender.rolling.policies.type=Policies
appender.rolling.policies.size.type=SizeBasedTriggeringPolicy
appender.rolling.policies.size.size=10MB
appender.rolling.strategy.type=DefaultRolloverStrategy
appender.rolling.strategy.max=10
###### Loggers definitions #######
logger.com-example.name=com.example
logger.com-example.level=debug

View File

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

View File

@@ -1,3 +1,3 @@
SOLR6_TAG=2.0.6-A4
SOLR6_TAG=2.0.3
POSTGRES_TAG=14.4
ACTIVEMQ_TAG=5.17.1-jre11-rockylinux8

View File

@@ -109,10 +109,6 @@ 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

@@ -0,0 +1,60 @@
#
# Warnings
#
log4j.logger.org.alfresco.module.org_alfresco_module_rm.caveat=warn
log4j.logger.org.alfresco.module.org_alfresco_module_rm.security.RMMethodSecurityPostProcessor=warn
#
# Module patches
#
log4j.logger.org.alfresco.module.org_alfresco_module_rm.patch=info
#
# Set to 'debug' to see details of capability failures when AccessDenied is thrown. May be
# removed to enhance performance.
#
log4j.logger.org.alfresco.module.org_alfresco_module_rm.security.RMMethodSecurityInterceptor=info
#
# RM permission debug
#
#log4j.logger.org.alfresco.module.org_alfresco_module_rm.capability.RMEntryVoter=debug
#log4j.logger.org.alfresco.module.org_alfresco_module_rm.capability.RMAfterInvocationProvider=debug
#log4j.logger.org.alfresco.module.org_alfresco_module_rm.capability.declarative=debug
#
# RM Audit service debug
#
#log4j.logger.org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService=debug
#
# Job debug
#
#log4j.logger.org.alfresco.module.org_alfresco_module_rm.job=debug
#
# Script logging level
#
log4j.logger.org.alfresco.repo.jscript.ScriptLogger=error
#
# Behaviour debug
#
log4j.logger.org.alfresco.repo.policy.annotation.AnnotatedBehaviourPostProcessor=info
log4j.logger.org.alfresco.module.org_alfresco_module_rm.behaviour.BaseBehaviourBean=info
#
# Patch debug
#
log4j.logger.org.alfresco.module.org_alfresco_module_rm.patch=info
#
# RM Audit service debug
#
#log4j.logger.org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService=debug
#
# Job debug
#
#log4j.logger.org.alfresco.module.org_alfresco_module_rm.job=debug
log4j.logger.org.alfresco.repo.web.scripts.roles.DynamicAuthoritiesGet=info
log4j.logger.org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAOImpl=info

View File

@@ -1,50 +0,0 @@
# Warnings
logger.alfresco-module-org_alfresco_module_rm-caveat.name=org.alfresco.module.org_alfresco_module_rm.caveat
logger.alfresco-module-org_alfresco_module_rm-caveat.level=warn
logger.alfresco-module-org_alfresco_module_rm-security-RMMethodSecurityPostProcessor.name=org.alfresco.module.org_alfresco_module_rm.security.RMMethodSecurityPostProcessor
logger.alfresco-module-org_alfresco_module_rm-security-RMMethodSecurityPostProcessor.level=warn
# Module patches
logger.alfresco-module-org_alfresco_module_rm-patch.name=org.alfresco.module.org_alfresco_module_rm.patch
logger.alfresco-module-org_alfresco_module_rm-patch.level=info
# Set to 'debug' to see details of capability failures when AccessDenied is thrown. May be
# removed to enhance performance.
logger.alfresco-module-org_alfresco_module_rm-security-RMMethodSecurityInterceptor.name=org.alfresco.module.org_alfresco_module_rm.security.RMMethodSecurityInterceptor
logger.alfresco-module-org_alfresco_module_rm-security-RMMethodSecurityInterceptor.level=info
# RM permission debug
#logger.alfresco-module-org_alfresco_module_rm-capability-RMEntryVoter.name=org.alfresco.module.org_alfresco_module_rm.capability.RMEntryVoter
#logger.alfresco-module-org_alfresco_module_rm-capability-RMEntryVoter.level=debug
#logger.alfresco-module-org_alfresco_module_rm-capability-RMAfterInvocationProvider.name=org.alfresco.module.org_alfresco_module_rm.capability.RMAfterInvocationProvider
#logger.alfresco-module-org_alfresco_module_rm-capability-RMAfterInvocationProvider.level=debug
#logger.alfresco-module-org_alfresco_module_rm-capability-declarative.name=org.alfresco.module.org_alfresco_module_rm.capability.declarative
#logger.alfresco-module-org_alfresco_module_rm-capability-declarative.level=debug
# RM Audit service debug
#logger.alfresco-module-org_alfresco_module_rm-audit-RecordsManagementAuditService.name=org.alfresco.module.org_alfresco_module_rm.audit.RecordsManagementAuditService
#logger.alfresco-module-org_alfresco_module_rm-audit-RecordsManagementAuditService.level=debug
# Job debug
#logger.alfresco-module-org_alfresco_module_rm-job.name=org.alfresco.module.org_alfresco_module_rm.job
#logger.alfresco-module-org_alfresco_module_rm-job.level=debug
# Script logging level
logger.alfresco-repo-jscript-ScriptLogger.name=org.alfresco.repo.jscript.ScriptLogger
logger.alfresco-repo-jscript-ScriptLogger.level=error
# Behaviour debug
logger.alfresco-repo-policy-annotation-AnnotatedBehaviourPostProcessor.name=org.alfresco.repo.policy.annotation.AnnotatedBehaviourPostProcessor
logger.alfresco-repo-policy-annotation-AnnotatedBehaviourPostProcessor.level=info
logger.alfresco-module-org_alfresco_module_rm-behaviour-BaseBehaviourBean.name=org.alfresco.module.org_alfresco_module_rm.behaviour.BaseBehaviourBean
logger.alfresco-module-org_alfresco_module_rm-behaviour-BaseBehaviourBean.level=info
logger.alfresco-repo-web-scripts-roles-DynamicAuthoritiesGet.name=org.alfresco.repo.web.scripts.roles.DynamicAuthoritiesGet
logger.alfresco-repo-web-scripts-roles-DynamicAuthoritiesGet.level=info
logger.alfresco-module-org_alfresco_module_rm-query-RecordsManagementQueryDAOImpl.name=org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAOImpl
logger.alfresco-module-org_alfresco_module_rm-query-RecordsManagementQueryDAOImpl.level=info

View File

@@ -17,6 +17,5 @@
<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

@@ -175,14 +175,6 @@
<property name="nodesModelFactory" ref="nodesModelFactory" />
</bean>
<bean class="org.alfresco.rm.rest.api.events.EventEntityResource">
<property name="recordsManagementEventService" ref="RecordsManagementEventService" />
</bean>
<bean class="org.alfresco.rm.rest.api.events.EventTypeEntityResource">
<property name="recordsManagementEventService" ref="RecordsManagementEventService" />
</bean>
<!-- extended sites bean definition -->
<bean id="rm.sites" class="org.alfresco.rm.rest.api.impl.RMSitesImpl" parent="sites">
<property name="siteSurfConfig" ref="rm.siteSurfConfig" />
@@ -217,8 +209,6 @@
<entry key="org.alfresco.service.cmr.attributes.DuplicateAttributeException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_CONFLICT}" />
<entry key="org.alfresco.module.org_alfresco_module_rm.record.RecordCreationException" value="422" />
<entry key="org.alfresco.service.cmr.model.FileExistsException" value="409" />
<entry key="org.alfresco.rest.framework.core.exceptions.EventAlreadyExistsException" value="409" />
<entry key="org.alfresco.rest.framework.core.exceptions.EntityNotFoundException" value="404" />
</map>
</property>
</bean>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<properties>

View File

@@ -30,10 +30,8 @@ package org.alfresco.module.org_alfresco_module_rm.job;
import static org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase.PARAM_NO_ERROR_CHECK;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.alfresco.error.AlfrescoRuntimeException;
@@ -49,6 +47,7 @@ import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PersonService;
import org.springframework.extensions.surf.util.I18NUtil;
/**
@@ -65,6 +64,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
/** batching properties */
private int batchSize;
public static final int DEFAULT_BATCH_SIZE = 500;
private static final String MSG_NODE_FROZEN = "rm.action.node.frozen.error-message";
/** list of disposition actions to automatically execute */
private List<String> dispositionActions;
@@ -194,7 +194,6 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
boolean hasMore = true;
int skipCount = 0;
List<NodeRef> resultNodes = new ArrayList<>();
if (batchSize < 1)
{
@@ -215,14 +214,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
// execute search
ResultSet results = searchService.query(params);
if(results != null)
{
// filtering out the hold/freezed cases from the result set
resultNodes =
results.getNodeRefs().stream().filter(node -> nodeService.getPrimaryParent(node) == null ?
!freezeService.isFrozenOrHasFrozenChildren(node) :
!freezeService.isFrozenOrHasFrozenChildren(nodeService.getPrimaryParent(node).getParentRef())).collect(Collectors.toList());
}
List<NodeRef> resultNodes = results.getNodeRefs();
hasMore = results.hasMore();
skipCount += resultNodes.size(); // increase by page size
results.close();
@@ -273,6 +265,12 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
}
Map<String, Serializable> props = Map.of(PARAM_NO_ERROR_CHECK, false);
if (freezeService.isFrozenOrHasFrozenChildren(parent.getParentRef()))
{
log.debug(I18NUtil.getMessage(MSG_NODE_FROZEN, dispAction));
continue;
}
try
{
// execute disposition action

View File

@@ -38,12 +38,9 @@ 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;
@@ -79,7 +76,6 @@ import org.json.simple.JSONObject;
*
* @author Roy Wetherall
*/
@Slf4j
public class JSONConversionComponent extends org.alfresco.repo.jscript.app.JSONConversionComponent
implements NodeServicePolicies.OnDeleteNodePolicy,
NodeServicePolicies.OnCreateNodePolicy
@@ -519,25 +515,17 @@ 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
DispositionAction nextDispositionAction = dispositionService.getNextDispositionAction(nodeRef);
if (nextDispositionAction != null)
if (dispositionService.getNextDispositionAction(nodeRef) != null)
{
for (EventCompletionDetails details : nextDispositionAction.getEventCompletionDetails())
for (EventCompletionDetails details : dispositionService.getNextDispositionAction(nodeRef).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());
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));
}
properties.put("dispositionEventCombination", nodeService.getProperty(dispositionService.getNextDispositionAction(nodeRef).getDispositionActionDefinition().getNodeRef(), PROP_DISPOSITION_EVENT_COMBINATION));
break;
}
}

View File

@@ -30,9 +30,6 @@ 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;
@@ -40,14 +37,11 @@ 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
@@ -58,15 +52,8 @@ import org.slf4j.LoggerFactory;
*/
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());
/** The batch size for processing frozen nodes. */
private int batchSize = 1000;
protected static final QName PATCH_ASSOC_NAME = QName.createQName(RM_CUSTOM_URI, RMv35HoldNewChildAssocPatch.class.getSimpleName());
/**
* File plan service interface
@@ -88,8 +75,7 @@ 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)
{
@@ -99,8 +85,7 @@ public class RMv35HoldNewChildAssocPatch extends AbstractModulePatch
/**
* Setter for hold service
*
* @param holdService
* Hold service interface.
* @param holdService Hold service interface.
*/
public void setHoldService(HoldService holdService)
{
@@ -110,8 +95,7 @@ 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)
{
@@ -128,49 +112,33 @@ 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))
{
LOGGER.debug("Analyzing hold {}", hold.getId());
BatchWorker batchWorker = new BatchWorker(hold);
LOGGER.debug("Hold has {} items to be analyzed", batchWorker.getWorkSize());
while (batchWorker.hasMoreResults())
List<ChildAssociationRef> frozenAssoc = nodeService.getChildAssocs(hold, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL);
for (ChildAssociationRef ref : frozenAssoc)
{
processBatch(hold, batchWorker);
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);
}
}
LOGGER.debug("Patched {} items in hold", batchWorker.getTotalPatchedNodes());
patchedNodesCounter += batchWorker.getTotalPatchedNodes();
}
}
LOGGER.debug("Patch applied to {} children across all holds", patchedNodesCounter);
}
finally
{
@@ -178,92 +146,4 @@ 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

@@ -1,222 +0,0 @@
/*
* #%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.rm.rest.api.events;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent;
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.core.ResourceParameter;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.EventAlreadyExistsException;
import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.model.EventBody;
import org.alfresco.rm.rest.api.model.EventInfo;
import org.alfresco.util.GUID;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static org.alfresco.util.ParameterCheck.mandatory;
/**
* Event entity resource
*/
@EntityResource(name = "events", title = "Events")
public class EventEntityResource implements EntityResourceAction.Read<EventInfo>,
EntityResourceAction.ReadById<EventInfo>,
EntityResourceAction.Update<EventBody>,
EntityResourceAction.Create<EventBody> {
private RecordsManagementEventService recordsManagementEventService;
/**
* Set the records management event service
*
* @param rmEventService
*/
public void setRecordsManagementEventService(RecordsManagementEventService rmEventService)
{
this.recordsManagementEventService = rmEventService;
}
@Override
@WebApiDescription(title = "Return a single event identified by 'eventId'")
@WebApiParam(name = "eventId", title = "The event id", kind = ResourceParameter.KIND.URL_PATH)
public EventInfo readById(String eventId, Parameters parameters) throws EntityNotFoundException
{
mandatory("eventId", eventId);
RecordsManagementEvent event = null;
if (eventExists(eventId))
{
// Get the event
event = recordsManagementEventService.getEvent(eventId);
}
return EventInfo.fromRecordsManagementEvent(event);
}
@Override
@WebApiDescription(title = "Return a list of events")
public CollectionWithPagingInfo<EventInfo> readAll(Parameters params)
{
Paging paging = params.getPaging();
List<EventInfo> eventInfoList = recordsManagementEventService.getEvents().stream()
.map(EventInfo::fromRecordsManagementEvent)
.collect(Collectors.toList());
int totalCount = eventInfoList.size();
boolean hasMoreItems = paging.getSkipCount() + paging.getMaxItems() < totalCount;
return CollectionWithPagingInfo.asPaged(paging, eventInfoList.stream()
.skip(paging.getSkipCount())
.limit(paging.getMaxItems())
.collect(Collectors.toList()), hasMoreItems, totalCount);
}
@Override
@WebApiDescription(title = "Create a new event")
public List<EventBody> create(List<EventBody> eventBodyList, Parameters parameters)
{
//TODO: 403 User not allowed to update event error still needs to be implemented
mandatory("eventBodyList", eventBodyList);
for (EventBody eventBody : eventBodyList) {
mandatory("eventName", eventBody.getName());
mandatory("eventType", eventBody.getType());
}
List<EventBody> responseEventBodyList = new ArrayList<>();
for (EventBody eventBody : eventBodyList) {
String eventId = GUID.generate();
String eventName = eventBody.getName();
String eventType = eventBody.getType();
if(canCreateEvent(eventId, eventName)) {
RecordsManagementEvent event = recordsManagementEventService.addEvent(eventType, eventId, eventName);
responseEventBodyList.add(EventBody.fromRecordsManagementEvent(event));
}
}
return responseEventBodyList;
}
@Override
@WebApiDescription(title = "Update a single event identified by 'eventId'")
@WebApiParam(name = "eventId", title = "The event id", kind = ResourceParameter.KIND.URL_PATH)
public EventBody update(String eventId, EventBody eventBody, Parameters parameters)
{
//TODO: 403 User not allowed to update event error still needs to be implemented
mandatory("eventId", eventId);
mandatory("eventName", eventBody.getName());
mandatory("eventType", eventBody.getType());
RecordsManagementEvent event = null;
if (canEditEvent(eventBody.getName(), eventId, eventBody.getType()))
{
// Create event
event = recordsManagementEventService.addEvent(eventBody.getType(), eventId, eventBody.getName());
}
else
{
// Get event
event = recordsManagementEventService.getEvent(eventId);
}
return EventBody.fromRecordsManagementEvent(event);
}
/**
* Helper method for checking if an event exists or not. Throws an
* error if the event does not exist.
*
* @param eventId The id of the event
*/
private boolean eventExists(String eventId)
{
boolean eventExists = true;
// Check the event exists
if (!recordsManagementEventService.existsEvent(eventId))
{
throw new EntityNotFoundException(eventId);
}
return eventExists;
}
/**
* Helper method for checking if an event can be created or not. Throws an
* error if the event already exists.
*
* @param eventId The id of the event
* @param eventName The name of the event
*/
private boolean canCreateEvent(String eventId, String eventName)
{
boolean canCreateEvent = true;
if (!recordsManagementEventService.canCreateEvent(eventName, eventId))
{
throw new EventAlreadyExistsException("framework.exception.CreateEventAlreadyExists", new Object[] {eventName});
}
return canCreateEvent;
}
/**
* Helper method for checking if an event can be edited or not. Throws an
* error if an event with the same display label already exists.
*
* @param eventName The name of the event
* @param eventId The id of the event
* @param eventType The type of the event
* @return true if the event can be edited, false otherwise
*/
private boolean canEditEvent(String eventName, String eventId, String eventType)
{
boolean canEditEvent = false;
if (eventExists(eventId)) {
try
{
canEditEvent = recordsManagementEventService.canEditEvent(eventName, eventId, eventType);
}
catch (AlfrescoRuntimeException are)
{
throw new EventAlreadyExistsException("framework.exception.UpdateEventAlreadyExists", new Object[] {eventName});
}
}
return canEditEvent;
}
}

View File

@@ -1,76 +0,0 @@
/*
* #%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.rm.rest.api.events;
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.model.EventTypeInfo;
import java.util.List;
import java.util.stream.Collectors;
/**
* Event Type entity resource
*/
@EntityResource(name = "event-types", title = "Event Types")
public class EventTypeEntityResource implements EntityResourceAction.Read<EventTypeInfo> {
private RecordsManagementEventService recordsManagementEventService;
/**
* Set the records management event service
* @param rmEventService
*/
public void setRecordsManagementEventService(RecordsManagementEventService rmEventService)
{
this.recordsManagementEventService = rmEventService;
}
@Override
@WebApiDescription(title = "Return a list of different event types")
public CollectionWithPagingInfo<EventTypeInfo> readAll(Parameters params)
{
// TODO: AssociationName and ActionOnAssociatedNode properties still need to be assigned.
Paging paging = params.getPaging();
List<EventTypeInfo> eventTypeInfoList = recordsManagementEventService.getEventTypes().stream()
.map(EventTypeInfo::fromRecordsManagementEventType)
.collect(Collectors.toList());
int totalCount = eventTypeInfoList.size();
boolean hasMoreItems = paging.getSkipCount() + paging.getMaxItems() < totalCount;
return CollectionWithPagingInfo.asPaged(paging, eventTypeInfoList.stream()
.skip(paging.getSkipCount())
.limit(paging.getMaxItems())
.collect(Collectors.toList()), hasMoreItems, totalCount);
}
}

View File

@@ -1,34 +0,0 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
/**
* Package info that defines the Information Governance Events REST API
*/
@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
package org.alfresco.rm.rest.api.events;
import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.WebApi;

View File

@@ -1,71 +0,0 @@
/*
* #%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.rm.rest.api.model;
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent;
/**
* The EventBody model to be exposed through REST API.
*/
public class EventBody {
private String name;
private String type;
public static EventBody fromRecordsManagementEvent(RecordsManagementEvent event)
{
EventBody eventBody = new EventBody();
if(event != null) {
eventBody.setName(event.getDisplayLabel());
eventBody.setType(event.getType());
}
return eventBody;
}
public EventBody() {}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
}

View File

@@ -1,82 +0,0 @@
/*
* #%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.rm.rest.api.model;
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent;
/**
* The EventInfo model to be exposed through REST API.
*/
public class EventInfo {
private String id;
private String name;
private String type;
public static EventInfo fromRecordsManagementEvent(RecordsManagementEvent event)
{
EventInfo eventInfo = new EventInfo();
if (event != null) {
eventInfo.setName(event.getDisplayLabel());
eventInfo.setId(event.getName());
eventInfo.setType(event.getType());
}
return eventInfo;
}
public EventInfo() {}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
}

View File

@@ -1,105 +0,0 @@
/*
* #%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.rm.rest.api.model;
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventType;
/**
* The EventTypeInfo model to be exposed through REST API.
*/
public class EventTypeInfo {
private String id;
private String name;
private boolean isAutomatic;
private String associationName;
private String actionOnAssociatedNode;
public static EventTypeInfo fromRecordsManagementEventType(RecordsManagementEventType eventType)
{
EventTypeInfo eventTypeInfo = new EventTypeInfo();
if (eventType != null) {
eventTypeInfo.setName(eventType.getDisplayLabel());
eventTypeInfo.setId(eventType.getName());
eventTypeInfo.setAutomatic(eventType.isAutomaticEvent());
}
return eventTypeInfo;
}
public EventTypeInfo() {}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public boolean isAutomatic()
{
return isAutomatic;
}
public void setAutomatic(boolean automatic)
{
isAutomatic = automatic;
}
public String getAssociationName()
{
return associationName;
}
public void setAssociationName(String associationName)
{
this.associationName = associationName;
}
public String getActionOnAssociatedNode()
{
return actionOnAssociatedNode;
}
public void setActionOnAssociatedNode(String actionOnAssociatedNode)
{
this.actionOnAssociatedNode = actionOnAssociatedNode;
}
}

View File

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

View File

@@ -170,7 +170,7 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
executer.executeImpl();
// then
verify(mockedNodeService, times(2)).getPrimaryParent(any(NodeRef.class));
// ensure the query is executed and closed
verifyQueryTimes(2);
@@ -206,7 +206,7 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
executer.executeImpl();
// then
verify(mockedNodeService, times(1)).getPrimaryParent(any(NodeRef.class));
// ensure the query is executed and closed
verifyQueryTimes(1);
@@ -262,11 +262,11 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
// ensure each node is process correctly
// node1
verify(mockedNodeService, times(1)).getProperty(node1, RecordsManagementModel.PROP_DISPOSITION_ACTION);
verify(mockedNodeService, times(3)).getPrimaryParent(node1);
verify(mockedNodeService, times(1)).getPrimaryParent(node1);
verify(mockedRecordsManagementActionService, times(1)).executeRecordsManagementAction(eq(parent), eq(CUTOFF), anyMap());
// node2
verify(mockedNodeService, times(1)).getProperty(node2, RecordsManagementModel.PROP_DISPOSITION_ACTION);
verify(mockedNodeService, times(3)).getPrimaryParent(node2);
verify(mockedNodeService, times(1)).getPrimaryParent(node2);
verify(mockedRecordsManagementActionService, times(1)).executeRecordsManagementAction(eq(parent), eq(RETAIN), anyMap());
// ensure no more interactions
@@ -329,7 +329,7 @@ public class DispositionLifecycleJobExecuterUnitTest extends BaseUnitTest
// call the service
executer.executeImpl();
// check the loop iterated through all the elements
// check the loop iterated trough all the elements
verify(mockedNodeService).exists(node1);
verify(mockedNodeService).exists(node2);
verify(mockedNodeService).exists(node3);

View File

@@ -34,9 +34,7 @@ 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;
@@ -53,21 +51,16 @@ 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
@@ -88,12 +81,6 @@ public class RMv35HoldNewChildAssocPatchUnitTest
@Mock
private BehaviourFilter mockBehaviourFilter;
@Mock
private TransactionService mockTransactionService;
@Mock
private RetryingTransactionHelper mockRetryingTransactionHelper;
@InjectMocks
private RMv35HoldNewChildAssocPatch patch;
@@ -125,63 +112,25 @@ 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, Integer.MAX_VALUE, false))
.thenReturn(childAssocs);
when(mockNodeService.getChildAssocs(holdRef, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL)).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, 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);
when(mockNodeService.getChildAssocs(holdRef, ASSOC_FROZEN_CONTENT, RegexQNamePattern.MATCH_ALL)).thenReturn(emptyList());
patch.applyInternal();

View File

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

View File

@@ -38,9 +38,6 @@ tags:
description: Retrieve and manage unfiled records containers
- name: unfiled-record-folders
description: Retrieve and manage unfiled record folders
- name: events
description: Retrieve and manage retention events
paths:
## GS sites
'/gs-sites':
@@ -2094,172 +2091,7 @@ paths:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
'/events':
get:
tags:
- events
summary: List all available retention events
description: |
Gets the list of events that can be used by retention steps
operationId: getAllEvents
produces:
- application/json
parameters:
- $ref: '#/parameters/skipCountParam'
- $ref: '#/parameters/maxItemsParam'
responses:
'200':
description: Successful response
schema:
$ref: '#/definitions/EventPaging'
'400':
description: |
Invalid parameter: value of **maxItems** or **skipCount** is invalid
'401':
description: Authentication failed
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
post:
tags:
- events
summary: Create a new retention event
description: |
Creates a new event that can be used by retention schedules.
operationId: createEvent
parameters:
- in: body
name: eventBodyCreate
description: The new event.
required: true
schema:
$ref: '#/definitions/EventBody'
consumes:
- application/json
produces:
- application/json
responses:
'201':
description: Successful response
schema:
$ref: '#/definitions/EventEntry'
'400':
description: |
Invalid parameter: **name** or **type** is invalid
'401':
description: Authentication failed
'403':
description: Current user does not have permission to create event
'409':
description: Cannot create event. An event with the name **name** already exists
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
'/events/{eventId}':
get:
tags:
- events
summary: Return event for given eventId
description: |
Gets information about the retention event with id **eventId**.
operationId: getEvent
produces:
- application/json
parameters:
- $ref: '#/parameters/eventIdParam'
responses:
'200':
description: Successful response
schema:
$ref: '#/definitions/EventEntry'
'400':
description: |
Invalid parameter: **eventId** is invalid
'401':
description: Authentication failed
'404':
description: "**eventId** does not exist"
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
put:
tags:
- events
summary: Update event for given eventId
operationId: updateEvent
description: |
Updates retention event with id **eventId**.
produces:
- application/json
parameters:
- $ref: '#/parameters/eventIdParam'
- in: body
name: eventBodyUpdate
description: The event information to update.
required: true
schema:
$ref: '#/definitions/EventBody'
responses:
'200':
description: Successful response
schema:
$ref: '#/definitions/EventEntry'
'400':
description: |
Invalid parameter: The update request is invalid or **eventId** is not a valid format or **eventBodyUpdate** is invalid
'401':
description: Authentication failed
'403':
description: Current user does not have permission to update events
'404':
description: "**eventId** does not exist"
'409':
description: Cannot update event. An event with the name **name** already exists
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
'/event-types':
get:
tags:
- events
summary: List all the retention event types
description: |
Gets a list of all the retention event types.
operationId: getAllEventTypes
produces:
- application/json
parameters:
- $ref: '#/parameters/skipCountParam'
- $ref: '#/parameters/maxItemsParam'
responses:
'200':
description: Successful response
schema:
$ref: '#/definitions/EventTypePaging'
'400':
description: |
Invalid parameter: value of **maxItems** or **skipCount** is invalid
'401':
description: Authentication failed
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
parameters:
## event
eventIdParam:
name: eventId
in: path
description: The identifier of an event.
required: true
type: string
## File plans
filePlanEntryIncludeParam:
name: include
@@ -3928,91 +3760,3 @@ definitions:
- SiteCollaborator
- SiteContributor
- SiteManager
EventPaging:
type: object
properties:
list:
type: object
properties:
pagination:
$ref: '#/definitions/Pagination'
entries:
type: array
items:
$ref: '#/definitions/EventEntry'
EventEntry:
type: object
required:
- entry
properties:
entry:
$ref: '#/definitions/Event'
Event:
type: object
required:
- id
- name
- type
properties:
id:
type: string
description: this is the id of the event
name:
type: string
description: This is the unique display label of the event
type:
type: string
description: this is event type
EventBody:
type: object
required:
- name
properties:
name:
type: string
description: This is the unique display label of the event
type:
type: string
description: this is event type
default: Simple
EventTypePaging:
type: object
properties:
list:
type: object
properties:
pagination:
$ref: '#/definitions/Pagination'
entries:
type: array
items:
$ref: '#/definitions/EventTypeEntry'
EventTypeEntry:
type: object
required:
- entry
properties:
entry:
$ref: '#/definitions/EventType'
EventType:
type: object
required:
- id
- name
properties:
id:
type: string
description: this is the event type id
name:
type: string
description: this is event type name
isAutomatic:
type: boolean
description: Whether events of this type need completing manually or can be completed automatically
default: true
associationName:
type: string
description: The association used to determine whether automatic events of this type are complete
actionOnAssociatedNode:
type: string
description: If an association name is set for this event type then it is possible to require an action to be completed on the associated node

View File

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

View File

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

View File

@@ -11,7 +11,7 @@ function main()
var params =
{
type: "people",
term: args.t + " [hint:useCQ]",
term: args.t,
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 !== null ? args.facetFields.replace( /(<([^>]+)>)/ig, '') : null,
facetFields: args.facetFields,
filters: args.filters,
encodedFilters: args.encodedFilters,
spell: (args.spellcheck !== null) ? (args.spellcheck == "true") : false

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<dependencies>
@@ -54,14 +54,9 @@
<classifier>asl</classifier>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${dependency.log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${dependency.log4j.version}</version>
<groupId>ch.qos.reload4j</groupId>
<artifactId>reload4j</artifactId>
<version>1.2.18.3</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
@@ -118,8 +113,8 @@
<!-- Test only dependencies, as popped up while running mvn test -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-reload4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>

View File

@@ -25,9 +25,6 @@ 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.
@@ -41,17 +38,10 @@ 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)
{
String possibleCauseClassName = possibleCauseClass.getName();
if (possibleCauseClass.isAssignableFrom(throwableClass)
|| (isJavaScriptException && throwableMsg.contains(possibleCauseClassName + EXCEPTION_DELIMITER)))
Class<?> throwableClass = throwable.getClass();
if (possibleCauseClass.isAssignableFrom(throwableClass))
{
// We have a match
return throwable;

View File

@@ -21,7 +21,6 @@ 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;
@@ -58,12 +57,7 @@ public class RequestHeadersHttpClient extends HttpClient
if (defaultHeaders != null)
{
defaultHeaders.forEach((k,v) -> {
Header h = method.getRequestHeader(k);
boolean add = h == null || h.getValue() == null || !h.getValue().equals(v);
if (add)
{
method.addRequestHeader(k, v);
}
method.addRequestHeader(k, v);
});
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2022 Alfresco Software Limited.
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -23,7 +23,7 @@ import org.apache.commons.logging.LogFactory;
/**
* A stand in for the org.apache.logging.log4j.ThreadContext class that avoids introducing runtime dependencies against the otherwise
* A stand in for the org.apache.log4j.NDC class that avoids introducing runtime dependencies against the otherwise
* optional log4j.
*
* @author dward
@@ -32,7 +32,7 @@ public class NDC
{
private static Log logger = LogFactory.getLog(NDC.class);
/** Log4J2 delegate for NDC */
/** Log4J delegate for NDC */
private static NDCDelegate ndcDelegate;
static

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2005-2022 Alfresco Software Limited.
* Copyright (C) 2005-2010 Alfresco Software Limited.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -25,19 +25,19 @@
package org.alfresco.util.log.log4j;
import org.alfresco.util.log.NDCDelegate;
import org.apache.logging.log4j.ThreadContext;
import org.apache.log4j.NDC;
/**
* A stand in for the org.apache.logging.log4j.ThreadContext class that avoids introducing runtime dependencies against the otherwise
* A stand in for the org.apache.log4j.NDC class that avoids introducing runtime dependencies against the otherwise
* optional log4j.
*
* @author dward
*/
public class Log4JNDC implements NDCDelegate
{
// Force resolution of the log4j2 ThreadContext class by the classloader (thus forcing an error if unavailable)
// Force resolution of the log4j NDC class by the classloader (thus forcing an error if unavailable)
@SuppressWarnings("unused")
private static final Class<?> NDC_REF = ThreadContext.class;
private static final Class<?> NDC_REF = NDC.class;
/**
* Push new diagnostic context information for the current thread.
@@ -47,7 +47,7 @@ public class Log4JNDC implements NDCDelegate
*/
public void push(String message)
{
ThreadContext.push(message);
NDC.push(message);
}
/**
@@ -55,6 +55,6 @@ public class Log4JNDC implements NDCDelegate
*/
public void remove()
{
ThreadContext.clearAll();
NDC.remove();
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<properties>
@@ -134,7 +134,7 @@
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<version>6.4.0</version>
<version>6.3.0</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>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<dependencies>

View File

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

View File

@@ -1,6 +1,6 @@
# 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-jre17-rockylinux8-202209261711
FROM alfresco/alfresco-base-tomcat:tomcat9-jre17-rockylinux8-202205140719
# Set default docker_context.
ARG resource_path=target
@@ -42,6 +42,10 @@ RUN sed -i "s/shared.loader=/shared.loader=\${catalina.base}\/shared\/classes/"
RUN mkdir -p ${TOMCAT_DIR}/amps
#RUN echo -e '\n\
#log4j.logger.org.alfresco.repo.content.transform.TransformerDebug=debug\n\
#' >> ${TOMCAT_DIR}/shared/classes/alfresco/extension/custom-log4j.propertiesRUN mkdir -p ${TOMCAT_DIR}/amps
# Copy the amps from build context to the appropriate location for your application server
COPY ${resource_path}/amps ${TOMCAT_DIR}/amps
@@ -51,14 +55,13 @@ RUN java -jar ${TOMCAT_DIR}/alfresco-mmt/alfresco-mmt*.jar install \
${TOMCAT_DIR}/webapps/alfresco -directory -nobackup
# Move the log file
RUN sed -i -e "s_appender.rolling.fileName\=alfresco.log_appender.rolling.fileName\=${TOMCAT_DIR}/logs\/alfresco.log_" \
${TOMCAT_DIR}/webapps/alfresco/WEB-INF/classes/log4j2.properties && \
sed -i -e "s_appender.rolling.filePattern=alfresco.log.%d{yyyy-MM-dd}_appender.rolling.filePattern\=${TOMCAT_DIR}/logs\/alfresco.log.%d{yyyy-MM-dd}_" \
${TOMCAT_DIR}/webapps/alfresco/WEB-INF/classes/log4j2.properties && \
RUN sed -i -e "s_log4j.appender.File.File\=alfresco.log_log4j.appender.File.File\=${TOMCAT_DIR}/logs\/alfresco.log_" \
${TOMCAT_DIR}/webapps/alfresco/WEB-INF/classes/log4j.properties && \
# Add catalina.policy to ROOT.war and alfresco.war
# Grant all security permissions to alfresco webapp because of numerous permissions required in order to work properly.
# Grant only deployXmlPermission to ROOT webapp.
sed -i -e "\$a\grant\ codeBase\ \"file:\$\{catalina.base\}\/webapps\/alfresco\/-\" \{\n\ permission\ java.security.AllPermission\;\n\};\ngrant\ codeBase\ \"file:\$\{catalina.base\}\/webapps\/_vti_bin\/-\" \{\n\ permission\ java.security.AllPermission\;\n\};\ngrant\ codeBase\ \"file:\$\{catalina.base\}\/webapps\/ROOT\/-\" \{\n\ permission org.apache.catalina.security.DeployXmlPermission \"ROOT\";\n\};" ${TOMCAT_DIR}/conf/catalina.policy
sed -i -e "\$a\grant\ codeBase\ \"file:\$\{catalina.base\}\/webapps\/alfresco\/-\" \{\n\ permission\ java.security.AllPermission\;\n\};\ngrant\ codeBase\ \"file:\$\{catalina.base\}\/webapps\/ROOT\/-\" \{\n\ permission org.apache.catalina.security.DeployXmlPermission \"ROOT\";\n\};" ${TOMCAT_DIR}/conf/catalina.policy
# fontconfig is required by Activiti worflow diagram generator
# installing pinned dependencies as well

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<properties>
@@ -78,6 +78,7 @@
</artifactItems>
</configuration>
</execution>
<execution>
<id>copy-amps</id>
<phase>process-resources</phase>

View File

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

View File

@@ -1,3 +1,3 @@
SOLR6_TAG=2.0.6-A4
SOLR6_TAG=2.0.3
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>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<modules>

View File

@@ -1,12 +0,0 @@
#!/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

@@ -1,7 +0,0 @@
#!/usr/bin/env bash
TAS_DIRECTORY=$1
cd ${TAS_DIRECTORY}
cat target/reports/alfresco-tas.log | grep "*** STARTING"

View File

@@ -1,21 +1,9 @@
#!/usr/bin/env bash
set -x
export DOCKER_COMPOSE_PATH=$1
export DOCKER_COMPOSES=""
export CLEAN_UP=""
export CLEAN_UP="$2"
for var in "$@"
do
if [ "$var" == "no-clean-up" ]
then
export CLEAN_UP="$var"
else
export DOCKER_COMPOSES+="--file $var "
fi
done
if [ -z "$DOCKER_COMPOSES" ]
if [ -z "$DOCKER_COMPOSE_PATH" ]
then
echo "Please provide path to docker-compose.yml: \"${0##*/} /path/to/docker-compose.yml\""
exit 1
@@ -27,8 +15,8 @@ fi
# The second parameter can be used to avoid doing a clean up if we are doing a restart test.
if [ "$CLEAN_UP" != "no-clean-up" ]
then
docker-compose ${DOCKER_COMPOSES} --project-directory $(dirname "${DOCKER_COMPOSE_PATH}") kill
docker-compose ${DOCKER_COMPOSES} --project-directory $(dirname "${DOCKER_COMPOSE_PATH}") rm -f
docker-compose --file "${DOCKER_COMPOSE_PATH}" kill
docker-compose --file "${DOCKER_COMPOSE_PATH}" rm -f
export GENERATED_IMAGES=$(docker images | grep '^environment_' | awk '{ print $3 }')
if [ -n "$GENERATED_IMAGES" ]
@@ -43,7 +31,7 @@ export TRANSFORMERS_TAG=$(mvn help:evaluate -Dexpression=dependency.alfresco-tra
export TRANSFORM_ROUTER_TAG=$(mvn help:evaluate -Dexpression=dependency.alfresco-transform-service.version -q -DforceStdout)
# .env files are picked up from project directory correctly on docker-compose 1.23.0+
docker-compose ${DOCKER_COMPOSES} --project-directory $(dirname "${DOCKER_COMPOSE_PATH}") up -d
docker-compose --file "${DOCKER_COMPOSE_PATH}" --project-directory $(dirname "${DOCKER_COMPOSE_PATH}") up -d
if [ $? -eq 0 ]
then

View File

@@ -1,495 +0,0 @@
![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

@@ -1,20 +0,0 @@
: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.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View File

@@ -1,27 +1,27 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<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>cmis</artifactId>
<name>alfresco-tas-cmis</name>
<artifactId>alfresco-community-repo-cmis-test</artifactId>
<name>cmis test</name>
<packaging>jar</packaging>
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<organization>
<name>Alfresco Software</name>
<url>http://www.alfresco.com/</url>
</organization>
<developers>
<developer>
<name>Paul Brodner</name>
<roles>
<role>Test Automation Architect</role>
</roles>
</developer>
</developers>
<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,29 +58,10 @@
</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>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>
<artifactId>cmis</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

View File

@@ -1,130 +0,0 @@
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

@@ -1,64 +0,0 @@
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;
}
}

View File

@@ -1,185 +0,0 @@
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

@@ -1,78 +0,0 @@
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;
}
}

View File

@@ -1,761 +0,0 @@
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

@@ -1,137 +0,0 @@
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

@@ -1,39 +0,0 @@
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

@@ -1,287 +0,0 @@
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

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

View File

@@ -1,12 +0,0 @@
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

@@ -1,16 +0,0 @@
<?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

@@ -1,76 +0,0 @@
# 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

@@ -1,26 +0,0 @@
# 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,21 +1,9 @@
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;
@@ -57,76 +45,32 @@ public abstract class AbstractCmisE2ETest extends AbstractE2EFunctionalTest
/**
* Repeat Elastic Query till results count returns expectedCountResults
* @param query CMIS Query to be executed
* @param expectedResultsCount Number of results expected
* @param expectedCountResults Number of results expected
* @return true when results count is equals to expectedCountResults
*/
protected boolean waitForIndexing(String query, long expectedResultsCount)
protected boolean waitForIndexing(String query, long expectedCountResults)
{
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)
for (int searchCount = 1; searchCount <= SEARCH_MAX_ATTEMPTS; searchCount++)
{
try
{
assertionMethod.accept(cmisApi.withQuery(query).assertValues());
return;
cmisApi.withQuery(query).assertResultsCount().equals(expectedCountResults);
return true;
}
catch (AssertionError 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;
}
LOGGER.info(String.format("WaitForIndexing in Progress: %s", 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,21 +17,6 @@ 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
{
@@ -66,164 +51,12 @@ public class SearchInFolderTests extends AbstractCmisE2ETest
dataContent.deleteSite(testSite);
}
@Test
public void executeCMISQuery_selectFieldsFromFolder()
@Test(dataProviderClass = XMLTestDataProvider.class, dataProvider = "getQueriesData")
@XMLDataConfig(file = "src/test/resources/search-in-folder.xml")
public void executeCMISQuery(QueryModel query)
{
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());
String currentQuery = String.format(query.getValue(), parentFolder.getNodeRef());
cmisApi.authenticateUser(testUser);
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));
Assert.assertTrue(waitForIndexing(currentQuery, query.getResults()), 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>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>20.36-SNAPSHOT</version>
<version>17.102-SNAPSHOT</version>
</parent>
<developers>
@@ -66,7 +66,6 @@
<dependency>
<groupId>org.alfresco.tas</groupId>
<artifactId>restapi</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
@@ -74,7 +73,6 @@
<dependency>
<groupId>org.alfresco.tas</groupId>
<artifactId>cmis</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>

View File

@@ -1,564 +0,0 @@
![in progress](https://img.shields.io/badge/Document_Level-In_Progress-yellow.svg?style=flat-square)
Back to [TAS Master Documentation](https://git.alfresco.com/tas/alfresco-tas-utility/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 generate models or check coverage](#how-to-generate-models-or-check-coverage)
* [How to run tests?](#how-to-run-tests)
* [from IDE](#from-ide)
* [from command line](#from-command-line)
* [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)
* [Reference](#reference)
* [Change Log](docs/CHANGELOG.md) 🌟
* [Contributors](#contributors)
* [Releasing](#releasing)
* [License](#license)
## Synopsis
**TAS**( **T**est **A**utomation **S**ystem)- **RESTAPI** is the project that handles the automated tests related only to [Alfresco REST API](http://docs.alfresco.com/5.1/pra/1/topics/pra-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 destribution)
* [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 [InteliJ](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://git.alfresco.com/tas/alfresco-tas-restapi-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-tester
# this command will checkout the remove v2.0.0 tagged repository and create locally a new branch v2.0.0
$ git checkout tags/v2.0.0 -b v2.0.0
```
* Install and check if all dependencies are downloaded
```bash
> cd alfresco-tas-restapi-test
> 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
├── report.html
├── src
   ├── main
      └── java
      └── org
      └── alfresco
      └── rest
      ├── core
         ├── assertion
  ├── IModelAssertion.java
        ├── IModelsCollectionAssertion.java
        ├── (...)
        └── PaginationAssertionVerbs.java
         ├── swagger
      ├── Generator.java
      ├── RestModelProperty.java
      ├── (...)
      └── SwaggerYamlParser.java
         ├── IRestModel.java
         ├── IRestModelsCollection.java
         ├── (...)
         ├── RestResponse.java
         └── RestWrapper.java
      ├── exception
         ├── EmptyJsonResponseException.java
      ├── EmptyRestModelCollectionException.java
      └── JsonToModelConversionException.java
      ├── model
  ├── builder  
        └── NodesBuilder.java
      ├── RestActivityModel.java
      ├── RestActivityModelsCollection.java
      ├── (...)
      └── RestVariableModelsCollection.java
├── requests
├── authAPI  
        └── RestAuthAPI.java
      ├── coreAPI
└── RestCoreAPI.java
      ├── workflowAPI
└── RestWorkflowAPI.java
├── Deployments.java
      ├── (...)
      └── Tenant.java
   ├── test
      ├── java
         └── org
         └── alfresco
         └── rest
         ├── auth
└── AuthTests.java
├── comments
├── AddCommentCoreTests.java
├── (...)
         ├── (...)
├── workflow
├── deployments
├── DeleteDeploymentCoreFullTests.java
├── (...)
├── processDefinitions
├── GetProcessDefinitionCoreTests.java
├── (...)
├── (...)
         ├── FunctionalCasesTests.java
         └── RestTest.java
      └── resources
      ├── alfresco-restapi-context.xml
      ├── default.properties
      └── log4j.properties
```
**[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 REST API object that simulates the calls: get, post, put, delete.
The tests are based on an abstract object: Rest.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 [RestDemoTests.java](src/test/java/org/alfresco/rest/demo/RestDemoTests.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
* Please also take a look at the [Rest-API desing and implementation](https://ts.alfresco.com/share/page/site/eng/document-details?nodeRef=workspace://SpacesStore/9f7823e7-0597-4435-9fd1-6ec8a4791259) guidelines.
* 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: rest-api. You can add more groups like sanity, regression
```java
@Test(groups={ TestGroup.COMMENTS}
```
* 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={TestGroup.REST_API, TestGroup.PEOPLE}, executionType= ExecutionType.SANITY,
description = "Verify admin user gets person with Rest API and response is not empty")
public void adminShouldRetrievePerson() throws Exception
{
peopleAPI.getPerson(userModel.getUsername())
.assertResponseIsNotEmpty();
restClient.assertStatusCodeIs(HttpStatus.OK.toString());
}
```
* Use Spring capabilities to initialize the objects(Models, Wrappers) with @Autowired
* To view a simple class that is using this utility, just browse on [RestDemoTests.java]((src/test/java/org/alfresco/rest/demo/RestDemoTests.java)
Notice the class definition and inheritance value:
```java
public class RestDemoTest extends RestTest
```
* 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 setupRestTest() throws Exception{
serverHealth.assertServerIsOnline();
}
```
* the test name are self explanatory:
```java
@TestRail(section={TestGroup.REST_API, TestGroup.SITES}, executionType= ExecutionType.SANITY,
description = "Verify admin user gets sites with Rest API and status code is 200")
public void adminShouldRetrieveSites() throws JsonToModelConversionException, Exception
{
siteAPI.getSites();
restClient()
.assertStatusCodeIs(HttpStatus.OK.toString());
}
```
* Asserting on imbricated keys:
Let's say your response is something like:
```json
{"entry": {
"createdAt": "2017-08-01T12:01:24.979+0000",
"edited": false,
"modifiedBy": {
"firstName": "Administrator"
}
}}
```
if you want to assert that firstName is "Administrator" you can do that using the following DSL:
```java
restClient.onResponse().assertThat().body("entry.modifiedBy.firstName", org.hamcrest.Matchers.is("Administrator"));
```
(notice that i'm using Hamcrest Matcher to finalize this assertion)
**[Back to Top ^](#table-of-contents)**
### How to generate models or check coverage
There are some simple generators that could parse [Swagger YAML](http://docs.alfresco.com/community/concepts/alfresco-sdk-tutorials-using-rest-api-explorer.html) files and provide some usefull information to you like:
a) Show on screen the actual coverage of TAS vs requests that exists in each YAML file - defined in pom.xml)
```bash
mvn exec:java -Dcoverage
```
Any missing request are saved under "missing-requests```<yaml-file-name>```.txt" file for further analysis
All current implementation are saved under "implemented-requests```<yaml-file-name>```.txt"
![](docs/pics/coverage.png)
b) Generate all missing models
```bash
mvn exec:java -Dmodels
```
This command will read all definitions of models from swagger YAML file predefined in pom.xml file.
It will compare with this file pattern: 'Rest```<model-definition-name>```Model.java', file created under ```/src/main/java/org/alfresco/rest/model``` ignoring the ones that are specified in [ignore-models](src/main/java/org/alfresco/rest/model/ignore-models) file.
![](docs/pics/models-all.png)
At this time you you will be prompted to select (based on displayed ID) what models you want to generate (separate each one by comma).
(you can also skip the generation of models from select yaml file or generate all missing models)
c) Generate specific models
Maybe you want to generate/regenerate just one model or multiple ones.
```bash
mvn exec:java -Dmodels=Error,SiteModel
```
This command will parse the Swagger YAML file, and will generate the definition of only those specified models, even if those models exist locally (in this case you will be prompted to override that file or not)
_NOTE_: there are some fields that are marked as ```required``` in swagger file. We also generate those fields with annotation ```@JsonProperty(required = true)```
All models are generated based on a [freemarker](http://freemarker.org) template found [here](src/main/resources/rest-model.ftl).
### 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.
* 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/restapi-acs-community-suite.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.rest.RestDemoTest
```
But pay attention that you will not have enabled all the [listeners](#listeners) in this case (the Reporting listener or TestRail integration one)
**[Back to Top ^](#table-of-contents)**
## Listeners
With the help of Listeners we can modify the behavior 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 RestTest
{
(...)
}
```
* 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 [rest-suites.xml](src/main/resources/shared-resources/restapi-acs-community-suite.xml) one more time.
You will see there that we have one listener added:
```java
<listener class-name="org.alfresco.utility.report.HtmlReportListener"></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.enabled=<true/false>
# 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.includeOnlyTestCasesExecuted=<true/false>
# testManagement.rateLimitInSeconds= 1
# testManagement.testRun=<test-run-name>
# testManagement.suiteId=<suite-id>
```
!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= TestGroup.REST_API, TestGroup.FULL,)
@TestRail(section = {TestGroup.REST_API, TestGroup.PROCESSES })
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 "restAPI" with a child section: "processes" (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 [restapi-acs-community-suite.xml](src/main/resources/shared-resources/restapi-acs-community-suite.xml) for further example.
```xml
<listeners>
<listener class-name="org.alfresco.utility.report.HtmlReportListener"></listener>
(...)
</listeners>
```
Right click on sanity-suite.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 restApi/processes 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-RESTAPI)
## 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-RestAPI](https://bamboo.alfresco.com/bamboo/browse/TAS-RESTAPI) 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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

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