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
187 changed files with 1855 additions and 12344 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,7 +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_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\]/
@@ -273,7 +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_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\]/
@@ -282,7 +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_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\]/
@@ -291,7 +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_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\]/
@@ -300,7 +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_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\]/
@@ -309,7 +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_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\]/
@@ -318,7 +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_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\]/
@@ -327,7 +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_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\]/
@@ -335,9 +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_failure: ${TAS_SCRIPTS}/output_logs_for_failures.sh "packaging/tests/tas-integration"
- name: "Share Services - ShareServicesTestSuite"
if: commit_message !~ /\[skip repo\]/

View File

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

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>17.137</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>17.137</version>
<version>17.102-SNAPSHOT</version>
</parent>
<build>

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

@@ -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

@@ -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);
@@ -292,7 +291,6 @@ public class DeleteRecordTests extends BaseRMRestTest
* </pre>
*/
@Test (description = "Destroying record doesn't delete the content for the associated copy")
@Ignore
@AlfrescoTest (jira = "MNT-20145")
public void destroyOfRecord()
{
@@ -325,9 +323,9 @@ public class DeleteRecordTests extends BaseRMRestTest
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());
@@ -350,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,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,430 +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.Ignore;
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
@Ignore
@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
@Ignore
@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
@Ignore
@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" })
@Ignore
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

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

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

@@ -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

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

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

@@ -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>17.137</version>
<version>17.102-SNAPSHOT</version>
</parent>
<build>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.137</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>17.137</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: encodeURIComponent(args.facetFields),
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>17.137</version>
<version>17.102-SNAPSHOT</version>
</parent>
<dependencies>

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

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>17.137</version>
<version>17.102-SNAPSHOT</version>
</parent>
<properties>
@@ -134,7 +134,7 @@
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<version>6.3.1</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>17.137</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>17.137</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-202209131110
FROM alfresco/alfresco-base-tomcat:tomcat9-jre17-rockylinux8-202205140719
# Set default docker_context.
ARG resource_path=target

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>17.137</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,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,28 +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>17.137</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>
<tas.utility.version>3.0.53</tas.utility.version>
<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 />
@@ -59,30 +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>
<version>${tas.utility.version}</version>
<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,256 +0,0 @@
package org.alfresco.cmis.dsl;
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.Set;
import java.util.stream.Collectors;
import com.google.common.collect.Streams;
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.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 returnedResults = -1;
private String currentQuery = "";
private ItemIterable<QueryResult> results;
public QueryExecutor(CmisWrapper cmisWrapper, String query)
{
this.cmisWrapper = cmisWrapper;
currentQuery = query;
}
public QueryResultAssertion assertResultsCount()
{
returnedResults = executeQuery(currentQuery).getPageNumItems();
return new QueryResultAssertion();
}
public QueryResultAssertion assertColumnIsOrdered()
{
results = executeQuery(currentQuery);
return new QueryResultAssertion();
}
public QueryResultAssertion assertColumnValuesRange()
{
results = executeQuery(currentQuery);
return new QueryResultAssertion();
}
public QueryResultAssertion assertValues()
{
results = executeQuery(currentQuery);
return new QueryResultAssertion();
}
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 equals(long expectedValue)
{
STEP(String.format("Verify that query: '%s' has %d results count returned", currentQuery, expectedValue));
Assert.assertEquals(returnedResults, 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 <= returnedResults)
Assert.fail(String.format("%s expected to have more than %d results, but found %d", showErrorMessage(), expectedValue, returnedResults));
return this;
}
public QueryResultAssertion isLowerThan(long expectedValue)
{
STEP(String.format("Verify that query: '%s' has more than %d results count returned", currentQuery, expectedValue));
if (returnedResults >= expectedValue)
Assert.fail(String.format("%s expected to have less than %d results, but found %d", showErrorMessage(), expectedValue, returnedResults));
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(Collectors.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(Collectors.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)
{
STEP(String.format("Verify that query: '%s' returns the values from %s for column %s", currentQuery, values, queryName));
Set<T> resultSet = Streams.stream(results).map(r -> (T) r.getPropertyValueByQueryName(queryName)).collect(toSet());
Assert.assertEquals(resultSet, values, "Values did not match");
return this;
}
public <T> QueryResultAssertion isReturningOrderedValues(String queryName, List<T> values)
{
STEP(String.format("Verify that query: '%s' returns the values from %s for column %s", currentQuery, values, queryName));
List<T> resultList = Streams.stream(results).map(r -> (T) r.getPropertyValueByQueryName(queryName)).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

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>17.137</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>17.137</version>
<version>17.102-SNAPSHOT</version>
</parent>
<developers>
@@ -73,7 +73,6 @@
<dependency>
<groupId>org.alfresco.tas</groupId>
<artifactId>cmis</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>

View File

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

View File

@@ -49,7 +49,6 @@ import java.util.stream.IntStream;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestActionBodyExecTemplateModel;
import org.alfresco.rest.model.RestCompositeConditionDefinitionModel;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestRuleModelsCollection;
import org.alfresco.utility.constants.UserRole;
@@ -67,6 +66,8 @@ import org.testng.annotations.Test;
@Test(groups = {TestGroup.RULES})
public class CreateRulesTests extends RestTest
{
private static final String IGNORE_ID = "id";
private static final String IGNORE_IS_SHARED = "isShared";
private UserModel user;
private SiteModel site;
private FolderModel ruleFolder;
@@ -93,10 +94,12 @@ public class CreateRulesTests extends RestTest
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = createRuleModelWithModifiedValues();
expectedRuleModel.setActions(addActionContextParams(expectedRuleModel.getActions()));
expectedRuleModel.setConditions(createEmptyConditionModel());
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().isEqualTo(expectedRuleModel, ID, IS_SHARED)
.assertThat().field(ID).isNotNull()
.assertThat().field(IS_SHARED).isNull();
rule.assertThat().isEqualTo(expectedRuleModel, IGNORE_ID, IGNORE_IS_SHARED)
.assertThat().field("id").isNotNull()
.assertThat().field("isShared").isNull();
}
/** Check creating a rule in a non-existent folder returns an error. */
@@ -381,106 +384,12 @@ public class CreateRulesTests extends RestTest
.createSingleRule(ruleModel);
final RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
expectedRuleModel.setActions(Arrays.asList(copyAction, checkOutAction, scriptAction));
expectedRuleModel.setActions(addActionContextParams(Arrays.asList(copyAction, checkOutAction, scriptAction)));
expectedRuleModel.setConditions(createEmptyConditionModel());
expectedRuleModel.setTriggers(List.of("inbound"));
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().isEqualTo(expectedRuleModel, ID, IS_SHARED)
.assertThat().field(IS_SHARED).isNull();
}
/**
* Check we get error when attempt to create a rule without any actions.
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithoutActionsShouldFail()
{
final RestRuleModel ruleModel = createRuleModelWithDefaultValues();
ruleModel.setActions(null);
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(BAD_REQUEST);
restClient.assertLastError().containsSummary("A rule must have at least one action");
}
/**
* Check we get error when attempt to create a rule with invalid action.
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithInvalidActionsShouldFail()
{
final RestRuleModel ruleModel = createRuleModelWithDefaultValues();
final RestActionBodyExecTemplateModel invalidAction = new RestActionBodyExecTemplateModel();
final String actionDefinitionId = "invalid-definition-value";
invalidAction.setActionDefinitionId(actionDefinitionId);
invalidAction.setParams(Map.of("dummy-key", "dummy-value"));
ruleModel.setActions(List.of(invalidAction));
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
restClient.assertStatusCodeIs(NOT_FOUND);
restClient.assertLastError().containsSummary(actionDefinitionId);
}
/**
* Check we can create a rule with multiple conditions
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithConditions()
{
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
ruleModel.setConditions(createVariousConditions());
RestRuleModel rule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
expectedRuleModel.setConditions(createVariousConditions());
expectedRuleModel.setTriggers(List.of("inbound"));
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().isEqualTo(expectedRuleModel, ID, IS_SHARED);
}
/**
* Check we can create a rule with empty list as conditions
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithConditions_emptyConditionList()
{
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
ruleModel.setConditions(createCompositeCondition(null));
RestRuleModel rule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
expectedRuleModel.setTriggers(List.of("inbound"));
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().isEqualTo(expectedRuleModel, ID, IS_SHARED);
}
/**
* Check we can NOT create a rule when category ID in condition is invalid, HTTP status code 400 is expected
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void createRuleWithConditions_invalidCategory()
{
STEP("Try to create a rule with non existing category in conditions.");
String fakeCategoryId = "bdba5f9f-fake-id22-803b-349bcfd06fd1";
RestCompositeConditionDefinitionModel conditions = createCompositeCondition(List.of(
createCompositeCondition(!INVERTED, List.of(
createSimpleCondition("category", "equals", fakeCategoryId)
))
));
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
ruleModel.setConditions(conditions);
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
restClient.assertStatusCodeIs(BAD_REQUEST);
restClient.assertLastError().containsSummary("Category in condition is invalid");
rule.assertThat().isEqualTo(expectedRuleModel, IGNORE_ID, IGNORE_IS_SHARED)
.assertThat().field("isShared").isNull();
}
}

View File

@@ -1,142 +0,0 @@
/*
* #%L
* Alfresco Repository
* %%
* 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 org.alfresco.rest.rules;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithModifiedValues;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.testng.Assert.assertEquals;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestRuleModelsCollection;
import org.alfresco.rest.model.RestRuleSetLinkModel;
import org.alfresco.rest.model.RestRuleSetModel;
import org.alfresco.rest.model.RestRuleSetModelsCollection;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* Tests for GET /nodes/{nodeId}/rule-sets/{ruleSetId}/rules with rule inheritance.
*/
@Test(groups = {TestGroup.RULES})
public class GetInheritedRulesTests extends RestTest
{
private UserModel user;
private SiteModel site;
@BeforeClass(alwaysRun = true)
public void dataPreparation()
{
STEP("Create a user and site");
user = dataUser.createRandomTestUser();
site = dataSite.usingUser(user).createPublicRandomSite();
}
/**
* Check we can get all the rules for the folder by providing the different rule set ids.
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void getInheritedRules()
{
STEP("Create a parent and child folder, each with inheriting rules");
FolderModel parent = dataContent.usingUser(user).usingSite(site).createFolder();
FolderModel child = dataContent.usingUser(user).usingResource(parent).createFolder();
RestRuleModel parentRule = createRuleModelWithModifiedValues();
parentRule = restClient.authenticateUser(user).withCoreAPI().usingNode(parent).usingDefaultRuleSet().createSingleRule(parentRule);
RestRuleModel childRule = createRuleModelWithModifiedValues();
childRule = restClient.authenticateUser(user).withCoreAPI().usingNode(child).usingDefaultRuleSet().createSingleRule(childRule);
STEP("Get the rules in the default rule set for the child folder");
RestRuleModelsCollection rules = restClient.authenticateUser(user).withCoreAPI().usingNode(child).usingDefaultRuleSet().getListOfRules();
rules.assertThat().entriesListContains("id", childRule.getId())
.and().entriesListCountIs(1);
STEP("Get the rules in the inherited rule set for the child folder");
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI().usingNode(child).include("inclusionType").getListOfRuleSets();
String inheritedRuleSetId = ruleSets.getEntries().stream()
.filter(ruleSet -> ruleSet.onModel().getInclusionType().equals("inherited"))
.findFirst().get().onModel().getId();
RestRuleModelsCollection inheritedRules = restClient.authenticateUser(user).withCoreAPI().usingNode(child).usingRuleSet(inheritedRuleSetId).getListOfRules();
inheritedRules.assertThat().entriesListContains("id", parentRule.getId())
.and().entriesListCountIs(1);
}
/**
* Check that we only get each rule once with linking and inheritance, and the order is correct.
* <p>
* The folder structure for this test is as follows:
* <pre>
* A --[links]-> DRuleSet
* +-B --[owns]-> BRuleSet
* +-C --[owns]-> CRuleSet
* +-D --[owns]--> DRuleSet
* </pre>
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void rulesReturnedAreUnique()
{
STEP("Create four folders with rules");
FolderModel folderA = dataContent.usingUser(user).usingSite(site).createFolder();
FolderModel folderB = dataContent.usingUser(user).usingResource(folderA).createFolder();
FolderModel folderC = dataContent.usingUser(user).usingResource(folderB).createFolder();
FolderModel folderD = dataContent.usingUser(user).usingResource(folderC).createFolder();
RestRuleModel ruleB = restClient.authenticateUser(user).withCoreAPI().usingNode(folderB).usingDefaultRuleSet().createSingleRule(createRuleModelWithModifiedValues());
RestRuleModel ruleC = restClient.authenticateUser(user).withCoreAPI().usingNode(folderC).usingDefaultRuleSet().createSingleRule(createRuleModelWithModifiedValues());
RestRuleModel ruleD = restClient.authenticateUser(user).withCoreAPI().usingNode(folderD).usingDefaultRuleSet().createSingleRule(createRuleModelWithModifiedValues());
STEP("Link folderA to ruleSetD");
RestRuleSetLinkModel linkModel = new RestRuleSetLinkModel();
linkModel.setId(folderD.getNodeRef());
restClient.authenticateUser(user).withCoreAPI().usingNode(folderA).createRuleLink(linkModel);
STEP("Get the rule sets for the folderD");
List<RestRuleSetModel> ruleSets = restClient.authenticateUser(user).withCoreAPI().usingNode(folderD).getListOfRuleSets().getEntries();
STEP("Check the rules for each rule set are as expected");
List<RestRuleModel> expectedRuleIds = List.of(ruleD, ruleB, ruleC);
IntStream.range(0, 2).forEach(index -> {
String ruleSetId = ruleSets.get(index).onModel().getId();
List<RestRuleModel> rules = restClient.authenticateUser(user)
.withCoreAPI()
.usingNode(folderD)
.usingRuleSet(ruleSetId)
.getListOfRules()
.getEntries()
.stream()
.map(RestRuleModel::onModel)
.collect(Collectors.toList());
assertEquals(rules, List.of(expectedRuleIds.get(index)), "Unexpected rules found for rule set " + ruleSetId);
});
assertEquals(ruleSets.size(), 3, "Expected three unique rule sets to be returned but got " + ruleSets);
}
}

View File

@@ -25,27 +25,16 @@
*/
package org.alfresco.rest.rules;
import static org.alfresco.rest.requests.RuleSettings.IS_INHERITANCE_ENABLED;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModel;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithDefaultValues;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithModifiedValues;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.junit.Assert.assertTrue;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
import java.util.List;
import com.google.common.collect.ImmutableMap;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestRuleSetLinkModel;
import org.alfresco.rest.model.RestRuleSetModel;
import org.alfresco.rest.model.RestRuleSetModelsCollection;
import org.alfresco.rest.model.RestRuleSettingsModel;
import org.alfresco.rest.requests.coreAPI.RestCoreAPI;
import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
@@ -62,8 +51,6 @@ public class GetRuleSetsTests extends RestTest
private UserModel user;
private SiteModel site;
private FolderModel ruleFolder;
private FolderModel inheritingChildFolder;
private FolderModel notInheritingChildFolder;
private RestRuleModel rule;
private String ruleSetId;
@@ -75,14 +62,6 @@ public class GetRuleSetsTests extends RestTest
site = dataSite.usingUser(user).createPublicRandomSite();
ruleFolder = dataContent.usingUser(user).usingSite(site).createFolder();
STEP("Create two children of the folder - one that inherits rules and one that doesn't");
inheritingChildFolder = dataContent.usingUser(user).usingResource(ruleFolder).createFolder();
notInheritingChildFolder = dataContent.usingUser(user).usingResource(ruleFolder).createFolder();
RestRuleSettingsModel doesntInherit = new RestRuleSettingsModel();
doesntInherit.setValue(false);
restClient.authenticateUser(user).withCoreAPI().usingNode(notInheritingChildFolder)
.usingIsInheritanceEnabledRuleSetting().updateSetting(doesntInherit);
STEP("Create a rule in the folder.");
RestRuleModel ruleModel = createRuleModel("ruleName");
rule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
@@ -154,7 +133,7 @@ public class GetRuleSetsTests extends RestTest
/** Check we can get the reason that a rule set is included in the list. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
public void getRuleSetsAndOwnedInclusionType()
public void getRuleSetsAndInclusionType()
{
STEP("Get the rule sets and inclusion type");
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI()
@@ -169,36 +148,6 @@ public class GetRuleSetsTests extends RestTest
ruleSets.assertThat().entriesListCountIs(1);
}
/** Check we can tell that a rule set has been inherited. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
public void getRuleSetsAndInheritedInclusionType()
{
STEP("Get the rule sets and inclusion type");
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI()
.usingNode(inheritingChildFolder)
.include("inclusionType")
.getListOfRuleSets();
restClient.assertStatusCodeIs(OK);
ruleSets.getEntries().get(0).onModel()
.assertThat().field("inclusionType").is("inherited")
.assertThat().field("id").is(ruleSetId);
ruleSets.assertThat().entriesListCountIs(1);
}
/** Check that a rule set is not inherited if inheriting is disabled. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
public void getRuleSetsWithoutInheriting()
{
STEP("Get the rule sets and inclusion type");
RestRuleSetModelsCollection ruleSets = restClient.authenticateUser(user).withCoreAPI()
.usingNode(notInheritingChildFolder)
.getListOfRuleSets();
restClient.assertStatusCodeIs(OK);
ruleSets.assertThat().entriesListCountIs(0);
}
/** Check we can get a rule set by its id. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void getRuleSetById()
@@ -208,13 +157,7 @@ public class GetRuleSetsTests extends RestTest
.getRuleSet(ruleSetId);
restClient.assertStatusCodeIs(OK);
ruleSet.assertThat().field("id").is(ruleSetId)
// Also check that the optional fields are not included by default.
.assertThat().field("owningFolder").isNull()
.assertThat().field("inheritedBy").isNull()
.assertThat().field("linkedToBy").isNull()
.assertThat().field("isInherited").isNull()
.assertThat().field("isLinkedTo").isNull();
ruleSet.assertThat().field("id").is(ruleSetId);
}
/** Check we can get a rule set using the "-default-" synonym. */
@@ -264,252 +207,4 @@ public class GetRuleSetsTests extends RestTest
ruleSet.assertThat().field("owningFolder").is(ruleFolder.getNodeRef())
.assertThat().field("id").is(ruleSetId);
}
/**
* Check we can find out the id of any folders that inherit a rule set.
* <p>
* The test checks several different situations:
* <pre>
* folder --[owns]-> rule set
* +- publicFolder --[inherits]-> rule set (user has access)
* +- privateFolder --[inherits]-> rule set (user does not have access)
* +- publicGrandchild --[inherits]-> rule set (user has access again)
* +- nonInheritingFolder (inheritance should be prevented)
* +- linkingFolder --[links]-> rule set (not inherited)
* +- descendantFolder --[inherits]-> rule set (inherited via link)
* </pre>
*/
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
public void getRuleSetAndInheritedBy()
{
STEP("Create a site owned by admin and add user as a contributor");
SiteModel siteModel = dataSite.usingAdmin().createPrivateRandomSite();
dataUser.addUserToSite(user, siteModel, UserRole.SiteContributor);
STEP("Create the folder structure");
FolderModel folder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
FolderModel publicFolder = dataContent.usingUser(user).usingResource(folder).createFolder();
FolderModel privateFolder = dataContent.usingAdmin().usingResource(folder).createFolder();
dataContent.usingAdmin().usingResource(privateFolder).setInheritPermissions(false);
// Create the grandchild with user and use admin to move it under the private folder.
FolderModel publicGrandchild = dataContent.usingUser(user).usingSite(siteModel).createFolder();
coreAPIForAdmin().usingActions().executeAction("move", publicGrandchild, ImmutableMap.of("destination-folder", "workspace://SpacesStore/" + privateFolder.getNodeRef()));
// Create the non-inheriting folder.
FolderModel nonInheritingFolder = dataContent.usingUser(user).usingResource(folder).createFolder();
RestRuleSettingsModel nonInheriting = new RestRuleSettingsModel();
nonInheriting.setKey(IS_INHERITANCE_ENABLED);
nonInheriting.setValue(false);
coreAPIForUser().usingNode(nonInheritingFolder).usingIsInheritanceEnabledRuleSetting().updateSetting(nonInheriting);
// Create a child that will link to the rule and a child of that to inherit via the link.
FolderModel linkingFolder = dataContent.usingUser(user).usingResource(nonInheritingFolder).createFolder();
FolderModel descendantFolder = dataContent.usingUser(user).usingResource(linkingFolder).createFolder();
STEP("Create an inheritable rule in the folder and get the rule set id.");
RestRuleModel ruleModel = createRuleModelWithModifiedValues();
coreAPIForUser().usingNode(folder).usingDefaultRuleSet().createSingleRule(ruleModel);
RestRuleSetModelsCollection ruleSets = coreAPIForUser().usingNode(folder).getListOfRuleSets();
String ruleSetId = ruleSets.getEntries().get(0).onModel().getId();
STEP("Create the link to the rule from the linking folder");
RestRuleSetLinkModel ruleSetLink = new RestRuleSetLinkModel();
ruleSetLink.setId(folder.getNodeRef());
coreAPIForUser().usingNode(linkingFolder).createRuleLink(ruleSetLink);
STEP("Remove the user from the site");
dataUser.removeUserFromSite(user, siteModel);
STEP("Get the rule set and inheriting folders");
RestRuleSetModel ruleSet = coreAPIForUser().usingNode(folder)
.include("inheritedBy")
.getRuleSet(ruleSetId);
restClient.assertStatusCodeIs(OK);
List<String> expectedInheritors = List.of(publicFolder.getNodeRef(), descendantFolder.getNodeRef(), publicGrandchild.getNodeRef());
ruleSet.assertThat().field("inheritedBy").is(expectedInheritors)
.assertThat().field("id").is(ruleSetId);
}
/** Check we can get the folders that link to a rule set and that this respects permissions. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES })
public void getRuleSetsAndLinkedToBy()
{
STEP("Create a site owned by admin and add user as a contributor");
SiteModel siteModel = dataSite.usingAdmin().createPrivateRandomSite();
dataUser.addUserToSite(user, siteModel, UserRole.SiteContributor);
STEP("Create the folder structure");
FolderModel ruleFolder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
FolderModel publicFolder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
FolderModel privateFolder = dataContent.usingAdmin().usingSite(siteModel).createFolder();
dataContent.usingAdmin().usingResource(privateFolder).setInheritPermissions(false);
STEP("Remove the user from the site");
dataUser.removeUserFromSite(user, siteModel);
STEP("Create a rule in the folder and link to it from the other two.");
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
coreAPIForUser().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
RestRuleSetModelsCollection ruleSets = coreAPIForAdmin().usingNode(ruleFolder).getListOfRuleSets();
String ruleSetId = ruleSets.getEntries().get(0).onModel().getId();
RestRuleSetLinkModel ruleSetLink = new RestRuleSetLinkModel();
ruleSetLink.setId(ruleFolder.getNodeRef());
coreAPIForUser().usingNode(publicFolder).createRuleLink(ruleSetLink);
coreAPIForAdmin().usingNode(privateFolder).createRuleLink(ruleSetLink);
STEP("Get the rule set and linkedToBy field");
RestRuleSetModel ruleSet = coreAPIForUser().usingNode(ruleFolder)
.include("linkedToBy")
.getRuleSet(ruleSetId);
restClient.assertStatusCodeIs(OK);
ruleSet.assertThat().field("linkedToBy").is(List.of(publicFolder.getNodeRef()))
.assertThat().field("id").is(ruleSetId);
}
/** Check that a user can see that a rule set is inherited even if they don't have permission to view the inheriting folder. */
@Test
public void getRuleSetAndIsInheritedWithoutPermission()
{
STEP("Create a site owned by admin and add user as a contributor");
SiteModel siteModel = dataSite.usingAdmin().createPrivateRandomSite();
dataUser.addUserToSite(user, siteModel, UserRole.SiteContributor);
STEP("Create a folder with a rule set and a private child folder to inherit it");
FolderModel ruleFolder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
dataContent.usingAdmin().usingResource(ruleFolder).createFolder();
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
coreAPIForUser().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
STEP("Remove the user from the site");
dataUser.removeUserFromSite(user, siteModel);
STEP("Get the rule set and isInherited field");
RestRuleSetModel ruleSet = coreAPIForUser().usingNode(ruleFolder)
.include("isInherited", "inheritedBy")
.getDefaultRuleSet();
restClient.assertStatusCodeIs(OK);
ruleSet.assertThat().field("isInherited").is(true)
.assertThat().field("inheritedBy").isEmpty();
}
/** Check that the isInherited field includes rule sets which are only inherited via links. */
@Test
public void getRuleSetAndIsInheritedViaLink()
{
STEP("Create a site and a folder with a rule");
SiteModel siteModel = dataSite.usingUser(user).createPublicRandomSite();
FolderModel ruleFolder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
coreAPIForUser().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
STEP("Create a second folder in the site that links to the rule set");
FolderModel secondFolder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
dataContent.usingUser(user).usingResource(secondFolder).createFolder();
RestRuleSetLinkModel ruleSetLink = new RestRuleSetLinkModel();
ruleSetLink.setId(ruleFolder.getNodeRef());
coreAPIForUser().usingNode(secondFolder).createRuleLink(ruleSetLink);
STEP("Get the rule set and isInherited field");
RestRuleSetModel ruleSet = coreAPIForUser().usingNode(ruleFolder)
.include("isInherited")
.getDefaultRuleSet();
restClient.assertStatusCodeIs(OK);
ruleSet.assertThat().field("isInherited").is(true);
}
/**
* Check that if a rule set is owned and linked to but not inherited then isInherited returns false.
*/
@Test
public void getRuleSetAndIsInheritedCanBeFalse()
{
STEP("Create a site and a folder with a rule");
SiteModel siteModel = dataSite.usingUser(user).createPublicRandomSite();
FolderModel ruleFolder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
coreAPIForUser().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
STEP("Create a second folder in the site that links to the rule set");
FolderModel secondFolder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
RestRuleSetLinkModel ruleSetLink = new RestRuleSetLinkModel();
ruleSetLink.setId(ruleFolder.getNodeRef());
coreAPIForUser().usingNode(secondFolder).createRuleLink(ruleSetLink);
STEP("Get the rule set and isInherited field");
RestRuleSetModel ruleSet = coreAPIForUser().usingNode(ruleFolder)
.include("isInherited")
.getDefaultRuleSet();
restClient.assertStatusCodeIs(OK);
ruleSet.assertThat().field("isInherited").is(false);
}
/** Check that a user can see that a rule set is linked to even if they don't have permission to view the linking folder. */
@Test
public void getRuleSetAndIsLinkedToWithoutPermission()
{
STEP("Create a site owned by admin and add user as a contributor");
SiteModel siteModel = dataSite.usingAdmin().createPrivateRandomSite();
dataUser.addUserToSite(user, siteModel, UserRole.SiteContributor);
STEP("Create a folder with a rule set");
FolderModel ruleFolder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
coreAPIForUser().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
STEP("Create a private folder linking to the rule set");
FolderModel linkingFolder = dataContent.usingAdmin().usingSite(siteModel).createFolder();
RestRuleSetLinkModel linkModel = new RestRuleSetLinkModel();
linkModel.setId(ruleFolder.getNodeRef());
coreAPIForAdmin().usingNode(linkingFolder).createRuleLink(linkModel);
STEP("Remove the user from the site");
dataUser.removeUserFromSite(user, siteModel);
STEP("Get the rule set and isLinkedTo field");
RestRuleSetModel ruleSet = coreAPIForUser().usingNode(ruleFolder)
.include("isLinkedTo", "linkedToBy", "owningFolder")
.getDefaultRuleSet();
restClient.assertStatusCodeIs(OK);
ruleSet.assertThat().field("isLinkedTo").is(true)
.assertThat().field("linkedToBy").isEmpty();
}
/**
* Check that if a rule set is owned and inherited but not linked to then isLinkedTo returns false.
*/
@Test
public void getRuleSetAndIsLinkedToCanBeFalse()
{
STEP("Create a site, a folder with a rule and a child folder that inherits it");
SiteModel siteModel = dataSite.usingUser(user).createPublicRandomSite();
FolderModel ruleFolder = dataContent.usingUser(user).usingSite(siteModel).createFolder();
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
coreAPIForUser().usingNode(ruleFolder).usingDefaultRuleSet().createSingleRule(ruleModel);
dataContent.usingUser(user).usingResource(ruleFolder).createFolder();
STEP("Get the rule set and isLinkedTo field");
RestRuleSetModel ruleSet = coreAPIForUser().usingNode(ruleFolder)
.include("isLinkedTo")
.getDefaultRuleSet();
restClient.assertStatusCodeIs(OK);
ruleSet.assertThat().field("isLinkedTo").is(false);
}
private RestCoreAPI coreAPIForUser()
{
return restClient.authenticateUser(user).withCoreAPI();
}
private RestCoreAPI coreAPIForAdmin()
{
return restClient.authenticateUser(dataUser.getAdminUser()).withCoreAPI();
}
}

View File

@@ -150,11 +150,11 @@ public class GetRulesTests extends RestTest
rules.getEntries().get(i).onModel()
.assertThat().field("isShared").isNotNull()
.assertThat().field("description").isNull()
.assertThat().field("isEnabled").is(false)
.assertThat().field("isInheritable").is(false)
.assertThat().field("isAsynchronous").is(false)
.assertThat().field("enabled").is(false)
.assertThat().field("cascade").is(false)
.assertThat().field("asynchronous").is(false)
.assertThat().field("errorScript").isNull()
.assertThat().field("isShared").is(false)
.assertThat().field("shared").isNull()
.assertThat().field("triggers").is("[inbound]"));
}
@@ -189,7 +189,9 @@ public class GetRulesTests extends RestTest
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = createRuleModelWithModifiedValues();
expectedRuleModel.setActions(addActionContextParams(expectedRuleModel.getActions()));
expectedRuleModel.setTriggers(List.of("update"));
expectedRuleModel.setConditions(createEmptyConditionModel());
restClient.assertStatusCodeIs(CREATED);
rule.assertThat().isEqualTo(expectedRuleModel, IGNORE_ID, IGNORE_IS_SHARED)
@@ -210,7 +212,9 @@ public class GetRulesTests extends RestTest
.createSingleRule(ruleModel);
RestRuleModel expectedRuleModel = createRuleModelWithDefaultValues();
expectedRuleModel.setActions(addActionContextParams(expectedRuleModel.getActions()));
expectedRuleModel.setTriggers(List.of("inbound"));
expectedRuleModel.setConditions(createEmptyConditionModel());
restClient.assertStatusCodeIs(CREATED);

View File

@@ -234,7 +234,7 @@ public class RuleSetLinksTests extends RestTest
STEP("Assert link result is 400");
restClient.assertStatusCodeIs(BAD_REQUEST)
.assertLastError().containsSummary(
"Unable to link to a rule set because the folder has pre-existing rules or is already linked to a rule set.");
"Unable to link to a ruleset because the folder has pre-existing rules or is already linked to a ruleset.");
}
/**

View File

@@ -26,13 +26,13 @@
package org.alfresco.rest.rules;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.rest.model.RestActionBodyExecTemplateModel;
import org.alfresco.rest.model.RestCompositeConditionDefinitionModel;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.rest.model.RestSimpleConditionDefinitionModel;
public class RulesTestsUtils
{
@@ -43,14 +43,7 @@ public class RulesTestsUtils
static final boolean RULE_ASYNC_DEFAULT = true;
static final boolean RULE_SHARED_DEFAULT = false;
static final String RULE_ERROR_SCRIPT_DEFAULT = "error-script";
static final String INBOUND = "inbound";
static final String UPDATE = "update";
static final String OUTBOUND = "outbound";
static final List<String> RULE_TRIGGERS_DEFAULT = List.of(INBOUND, UPDATE, OUTBOUND);
static final boolean INVERTED = true;
static final String AND = "and";
static final String ID = "id";
static final String IS_SHARED = "isShared";
static final List<String> ruleTriggersDefault = List.of("inbound", "update", "outbound");
/**
* Create a rule model filled with default values.
@@ -61,11 +54,11 @@ public class RulesTestsUtils
{
RestRuleModel ruleModel = createRuleModelWithDefaultValues();
ruleModel.setDescription(RULE_DESCRIPTION_DEFAULT);
ruleModel.setIsEnabled(RULE_ENABLED_DEFAULT);
ruleModel.setIsInheritable(RULE_CASCADE_DEFAULT);
ruleModel.setIsAsynchronous(RULE_ASYNC_DEFAULT);
ruleModel.setEnabled(RULE_ENABLED_DEFAULT);
ruleModel.setCascade(RULE_CASCADE_DEFAULT);
ruleModel.setAsynchronous(RULE_ASYNC_DEFAULT);
ruleModel.setIsShared(RULE_SHARED_DEFAULT);
ruleModel.setTriggers(RULE_TRIGGERS_DEFAULT);
ruleModel.setTriggers(ruleTriggersDefault);
ruleModel.setErrorScript(RULE_ERROR_SCRIPT_DEFAULT);
return ruleModel;
@@ -109,6 +102,16 @@ public class RulesTestsUtils
return restActionModel;
}
public static List<RestActionBodyExecTemplateModel> addActionContextParams(List<RestActionBodyExecTemplateModel> inputActions)
{
inputActions.forEach(inputAction -> {
final Map<String, Serializable> params = new HashMap<>((Map<String, Serializable>) inputAction.getParams());
params.put("actionContext", "rule");
inputAction.setParams(params);
});
return inputActions;
}
public static RestActionBodyExecTemplateModel createCustomActionModel(String actionDefinitionId, Map<String, Serializable> params)
{
RestActionBodyExecTemplateModel restActionModel = new RestActionBodyExecTemplateModel();
@@ -120,59 +123,8 @@ public class RulesTestsUtils
public static RestCompositeConditionDefinitionModel createEmptyConditionModel()
{
RestCompositeConditionDefinitionModel conditions = new RestCompositeConditionDefinitionModel();
conditions.setInverted(!INVERTED);
conditions.setBooleanMode(AND);
conditions.setInverted(false);
conditions.setBooleanMode("and");
return conditions;
}
public static RestCompositeConditionDefinitionModel createVariousConditions()
{
return createCompositeCondition(List.of(
createCompositeCondition(!INVERTED, List.of(
createSimpleCondition("cm:created", "less_than", "2022-09-01T12:59:00.000+02:00"),
createSimpleCondition("cm:creator", "ends", "ski"),
createSimpleCondition("size", "greater_than", "90000000"),
createSimpleCondition("mimetype", "equals", "video/3gpp"),
createSimpleCondition("encoding", "equals", "utf-8"),
createSimpleCondition("type", "equals", "cm:folder"),
createSimpleCondition("tag", "equals", "uat")
)),
createCompositeCondition(INVERTED, List.of(
createSimpleCondition("aspect", "equals", "audio:audio"),
createSimpleCondition("cm:modelVersion", "begins", "1.")
))
));
}
public static RestSimpleConditionDefinitionModel createSimpleCondition(String field, String comparator, String parameter)
{
RestSimpleConditionDefinitionModel simpleCondition = new RestSimpleConditionDefinitionModel();
simpleCondition.setField(field);
simpleCondition.setComparator(comparator);
simpleCondition.setParameter(parameter);
return simpleCondition;
}
public static RestCompositeConditionDefinitionModel createCompositeCondition(List<RestCompositeConditionDefinitionModel> compositeConditions)
{
return createCompositeCondition(AND, !INVERTED, compositeConditions, null);
}
public static RestCompositeConditionDefinitionModel createCompositeCondition(boolean inverted,
List<RestSimpleConditionDefinitionModel> simpleConditions)
{
return createCompositeCondition(AND, inverted, null, simpleConditions);
}
private static RestCompositeConditionDefinitionModel createCompositeCondition(String booleanMode, boolean inverted,
List<RestCompositeConditionDefinitionModel> compositeConditions, List<RestSimpleConditionDefinitionModel> simpleConditions)
{
RestCompositeConditionDefinitionModel compositeCondition = new RestCompositeConditionDefinitionModel();
compositeCondition.setBooleanMode(booleanMode);
compositeCondition.setInverted(inverted);
compositeCondition.setCompositeConditions(compositeConditions);
compositeCondition.setSimpleConditions(simpleConditions);
return compositeCondition;
}
}

View File

@@ -25,39 +25,19 @@
*/
package org.alfresco.rest.rules;
import static org.alfresco.rest.rules.RulesTestsUtils.ID;
import static org.alfresco.rest.rules.RulesTestsUtils.INBOUND;
import static org.alfresco.rest.rules.RulesTestsUtils.INVERTED;
import static org.alfresco.rest.rules.RulesTestsUtils.IS_SHARED;
import static org.alfresco.rest.rules.RulesTestsUtils.RULE_ASYNC_DEFAULT;
import static org.alfresco.rest.rules.RulesTestsUtils.RULE_CASCADE_DEFAULT;
import static org.alfresco.rest.rules.RulesTestsUtils.RULE_ENABLED_DEFAULT;
import static org.alfresco.rest.rules.RulesTestsUtils.createCompositeCondition;
import static org.alfresco.rest.rules.RulesTestsUtils.createCustomActionModel;
import static org.alfresco.rest.rules.RulesTestsUtils.createDefaultActionModel;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModel;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithDefaultValues;
import static org.alfresco.rest.rules.RulesTestsUtils.createRuleModelWithModifiedValues;
import static org.alfresco.rest.rules.RulesTestsUtils.createSimpleCondition;
import static org.alfresco.rest.rules.RulesTestsUtils.createVariousConditions;
import static org.alfresco.utility.constants.UserRole.SiteCollaborator;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestActionBodyExecTemplateModel;
import org.alfresco.rest.model.RestCompositeConditionDefinitionModel;
import org.alfresco.rest.model.RestRuleModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
@@ -100,9 +80,9 @@ public class UpdateRulesTests extends RestTest
.updateRule(rule.getId(), updatedRuleModel);
restClient.assertStatusCodeIs(OK);
updatedRule.assertThat().field(ID).is(rule.getId())
updatedRule.assertThat().field("id").is(rule.getId())
.assertThat().field("name").is("Updated rule name")
.assertThat().field(IS_SHARED).isNull();
.assertThat().field("isShared").isNull();
}
/** Check we get a 404 if trying to update a rule in a folder that doesn't exist. */
@@ -200,7 +180,7 @@ public class UpdateRulesTests extends RestTest
RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), updatedRuleModel);
updatedRule.assertThat().field(ID).is(rule.getId());
updatedRule.assertThat().field("id").is(rule.getId());
}
/** Check we can update a rule and get the included fields. */
@@ -212,320 +192,10 @@ public class UpdateRulesTests extends RestTest
STEP("Try to update the rule.");
RestRuleModel updatedRuleModel = createRuleModel("Updated rule name");
RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.include(IS_SHARED)
.include("isShared")
.updateRule(rule.getId(), updatedRuleModel);
updatedRule.assertThat().field(IS_SHARED).isNotNull();
}
/**
* Check we get error when attempt to update a rule to one without any actions.
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void updateRuleWithoutActionsShouldFail()
{
RestRuleModel rule = createAndSaveRule("Rule name");
STEP("Try to update the rule - set no actions.");
rule.setActions(null);
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.include(IS_SHARED)
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(BAD_REQUEST);
restClient.assertLastError().containsSummary("A rule must have at least one action");
}
/**
* Check we get error when attempt to update a rule to one with invalid action.
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void updateRuleWithInvalidActionDefinitionShouldFail()
{
RestRuleModel rule = createAndSaveRule("Rule name");
STEP("Try to update the rule - set no actions.");
final RestActionBodyExecTemplateModel invalidAction = new RestActionBodyExecTemplateModel();
final String actionDefinitionId = "invalid-definition-value";
invalidAction.setActionDefinitionId(actionDefinitionId);
invalidAction.setParams(Map.of("dummy-key", "dummy-value"));
rule.setActions(List.of(invalidAction));
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.include(IS_SHARED)
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(NOT_FOUND);
restClient.assertLastError().containsSummary(actionDefinitionId);
}
/** Check we can use the POST response to create the new rule. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateCopyRuleWithResponseFromPOST()
{
FolderModel destination = dataContent.usingUser(user).usingSite(site).createFolder();
RestActionBodyExecTemplateModel copyAction = new RestActionBodyExecTemplateModel();
copyAction.setActionDefinitionId("copy");
copyAction.setParams(ImmutableMap.of("destination-folder", destination.getNodeRef()));
RestRuleModel rule = createAndSaveRule("Rule name", List.of(copyAction));
STEP("Try to update the rule.");
rule.setName("Updated rule name");
RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.include(IS_SHARED)
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(OK);
updatedRule.assertThat().field("name").is("Updated rule name")
.assertThat().field("actions.actionDefinitionId").is(List.of("copy"))
.assertThat().field("actions.params").is(List.of(ImmutableMap.of("destination-folder", destination.getNodeRef())));
}
/** Check we can use the POST response and update rule fields. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateRuleFields()
{
final RestRuleModel rule = createAndSaveRule(createRuleModelWithModifiedValues());
STEP("Try to update the rule fields.");
rule.setName("Updated rule name");
rule.setTriggers(List.of(INBOUND));
final String updatedDescription = "Updated description";
rule.setDescription(updatedDescription);
rule.setIsEnabled(!RULE_ENABLED_DEFAULT);
rule.setIsInheritable(!RULE_CASCADE_DEFAULT);
rule.setIsAsynchronous(!RULE_ASYNC_DEFAULT);
final String updatedErrorScript = "updated-error-script";
rule.setErrorScript(updatedErrorScript);
final RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(OK);
updatedRule.assertThat().isEqualTo(rule, ID)
.assertThat().field(ID).isNotNull();
}
/** Check we can use the POST response and update rule by adding conditions. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateRuleAddConditions()
{
final RestRuleModel rule = createAndSaveRule(createRuleModelWithModifiedValues());
STEP("Try to update the rule and add conditions.");
rule.setConditions(createVariousConditions());
final RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(OK);
updatedRule.assertThat().isEqualTo(rule, ID)
.assertThat().field(ID).isNotNull();
}
/** Check we can use the POST response and update a rule rule without any conditions by adding null conditions. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateRuleAddNullConditions()
{
final RestRuleModel rule = createAndSaveRule(createRuleModelWithModifiedValues());
STEP("Try to update the rule and add null conditions.");
rule.setConditions(null);
final RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(OK);
updatedRule.assertThat().isEqualTo(rule, ID)
.assertThat().field(ID).isNotNull();
}
/** Check we can use the POST response and update rule by modifying conditions. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateRuleModifyConditions()
{
final RestRuleModel ruleModelWithInitialValues = createRuleModelWithModifiedValues();
ruleModelWithInitialValues.setConditions(createVariousConditions());
final RestRuleModel rule = createAndSaveRule(ruleModelWithInitialValues);
STEP("Try to update the rule and modify conditions.");
final RestCompositeConditionDefinitionModel compositeCondition = createCompositeCondition(
List.of(createCompositeCondition(false, List.of(createSimpleCondition("tag", "equals", "sample_tag")))));
rule.setConditions(compositeCondition);
final RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(OK);
updatedRule.assertThat().isEqualTo(rule, ID)
.assertThat().field(ID).isNotNull();
}
/** Check we can use the POST response and update rule by removing all conditions. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateRuleRemoveAllConditions()
{
final RestRuleModel ruleModelWithInitialValues = createRuleModelWithModifiedValues();
ruleModelWithInitialValues.setConditions(createVariousConditions());
final RestRuleModel rule = createAndSaveRule(ruleModelWithInitialValues);
STEP("Try to update the rule and remove all conditions.");
rule.setConditions(null);
final RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(OK);
updatedRule.assertThat().isEqualTo(rule, ID)
.assertThat().field(ID).isNotNull();
}
/** Check we get a 400 error when using the POST response and update rule by adding condition with invalid category. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateRuleWithInvalidCategoryInConditionAndFail()
{
final RestRuleModel ruleModelWithInitialValues = createRuleModelWithModifiedValues();
ruleModelWithInitialValues.setConditions(createVariousConditions());
final RestRuleModel rule = createAndSaveRule(ruleModelWithInitialValues);
STEP("Try to update the rule with invalid condition.");
final RestCompositeConditionDefinitionModel conditions = createCompositeCondition(
List.of(createCompositeCondition(!INVERTED, List.of(createSimpleCondition("category", "equals", "fake-category-id")))));
rule.setConditions(conditions);
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(BAD_REQUEST);
restClient.assertLastError().containsSummary("Category in condition is invalid");
}
/** Check we get a 400 error when using the POST response and update rule by adding condition without comparator when it is required. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateRuleWithConditionWithoutComparatorAndFail()
{
final RestRuleModel ruleModelWithInitialValues = createRuleModelWithModifiedValues();
ruleModelWithInitialValues.setConditions(createVariousConditions());
final RestRuleModel rule = createAndSaveRule(ruleModelWithInitialValues);
STEP("Try to update the rule with invalid condition (null comparator when required non-null).");
final RestCompositeConditionDefinitionModel conditions = createCompositeCondition(
List.of(createCompositeCondition(!INVERTED, List.of(createSimpleCondition("size", null, "65500")))));
rule.setConditions(conditions);
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(BAD_REQUEST);
restClient.assertLastError().containsSummary("Comparator in condition must not be blank");
}
/** Check we get a 400 error when using the POST response and update rule by adding condition without field. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateRuleWithConditionWithoutFieldAndFail()
{
final RestRuleModel ruleModelWithInitialValues = createRuleModelWithModifiedValues();
ruleModelWithInitialValues.setConditions(createVariousConditions());
final RestRuleModel rule = createAndSaveRule(ruleModelWithInitialValues);
STEP("Try to update the rule with invalid condition (null field).");
final RestCompositeConditionDefinitionModel conditions = createCompositeCondition(
List.of(createCompositeCondition(!INVERTED, List.of(createSimpleCondition(null, "greater_than", "65500")))));
rule.setConditions(conditions);
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(BAD_REQUEST);
restClient.assertLastError().containsSummary("Field in condition must not be blank");
}
/** Check we get a 400 error when using the POST response and update rule by adding condition without parameter value. */
@Test (groups = { TestGroup.REST_API, TestGroup.RULES, TestGroup.SANITY })
public void updateRuleWithConditionWithoutParamValueAndFail()
{
final RestRuleModel ruleModelWithInitialValues = createRuleModelWithModifiedValues();
ruleModelWithInitialValues.setConditions(createVariousConditions());
final RestRuleModel rule = createAndSaveRule(ruleModelWithInitialValues);
STEP("Try to update the rule with invalid condition (null parameter).");
final RestCompositeConditionDefinitionModel conditions = createCompositeCondition(
List.of(createCompositeCondition(!INVERTED, List.of(createSimpleCondition("size", "greater_than", "")))));
rule.setConditions(conditions);
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(BAD_REQUEST);
restClient.assertLastError().containsSummary("Parameter in condition must not be blank");
}
/**
* Check we can update a rule by adding several actions.
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void updateRuleAddActions()
{
final RestRuleModel rule = createAndSaveRule(createRuleModelWithModifiedValues());
STEP("Try to update the rule by adding several actions");
final Map<String, Serializable> copyParams =
Map.of("destination-folder", "dummy-folder-node", "deep-copy", true);
final RestActionBodyExecTemplateModel copyAction = createCustomActionModel("copy", copyParams);
final Map<String, Serializable> scriptParams = Map.of("script-ref", "dummy-script-node-id");
final RestActionBodyExecTemplateModel scriptAction = createCustomActionModel("script", scriptParams);
rule.setActions(Arrays.asList(copyAction, scriptAction));
final RestRuleModel updatedRule = restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(OK);
updatedRule.assertThat().isEqualTo(rule, ID)
.assertThat().field(ID).isNotNull();
}
/**
* Check we get a 400 error when attempting to update a rule by adding action with not allowed parameter.
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void updateRuleAddCheckoutActionForOutboundShouldFail()
{
final RestRuleModel rule = createAndSaveRule(createRuleModelWithModifiedValues());
STEP("Try to update the rule by adding checkout action");
final Map<String, Serializable> checkOutParams =
Map.of("destination-folder", "dummy-folder-node", "assoc-name", "cm:checkout", "assoc-type",
"cm:contains");
final RestActionBodyExecTemplateModel checkOutAction = createCustomActionModel("check-out", checkOutParams);
final Map<String, Serializable> scriptParams = Map.of("script-ref", "dummy-script-node-id");
rule.setActions(List.of(checkOutAction));
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(BAD_REQUEST);
restClient.assertLastError().containsSummary("Check out action cannot be performed for the rule type outbound!");
}
/**
* Check we get a 500 error when attempting to update a rule by adding action with parameter with non existing namespace in value.
* In near future we need to fix this kind of negative path to return a 4xx error.
*/
@Test(groups = {TestGroup.REST_API, TestGroup.RULES})
public void updateRuleAddActionWithInvalidParamShouldFail()
{
final RestRuleModel rule = createAndSaveRule(createRuleModelWithModifiedValues());
STEP("Try to update the rule by adding action with invalid parameter (non-existing namespace in value)");
final RestActionBodyExecTemplateModel action = new RestActionBodyExecTemplateModel();
action.setActionDefinitionId("add-features");
action.setParams(Map.of("aspect-name", "dummy:dummy"));
rule.setActions(List.of(action));
restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.updateRule(rule.getId(), rule);
restClient.assertStatusCodeIs(INTERNAL_SERVER_ERROR);
restClient.assertLastError().containsSummary("Namespace prefix dummy is not mapped to a namespace URI");
updatedRule.assertThat().field("isShared").isNotNull();
}
private RestRuleModel createAndSaveRule(String name)
@@ -547,17 +217,4 @@ public class UpdateRulesTests extends RestTest
return restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
}
/**
* Create a rule for folder and store it.
*
* @param ruleModel RuleModel used as create request
* @return The created rule.
*/
private RestRuleModel createAndSaveRule(final RestRuleModel ruleModel)
{
STEP("Create a rule: " + ruleModel);
return restClient.authenticateUser(user).withCoreAPI().usingNode(ruleFolder).usingDefaultRuleSet()
.createSingleRule(ruleModel);
}
}

View File

@@ -35,11 +35,10 @@ public class GetDeploymentsSanityTests extends RestTest
restClient.assertStatusCodeIs(HttpStatus.OK);
deployments.assertThat().entriesListIsNotEmpty();
deployments.getOneRandomEntry().onModel().assertThat()
.fieldsCount().is(4).and()
.fieldsCount().is(3).and()
.field("id").isNotEmpty().and()
.field("deployedAt").isNotEmpty().and()
.field("name").isNotEmpty().and()
.field("category").isNotEmpty();
.field("name").isNotEmpty();
}
}

View File

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

View File

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

66
pom.xml
View File

@@ -2,7 +2,7 @@
<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>
<artifactId>alfresco-community-repo</artifactId>
<version>17.137</version>
<version>17.102-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Alfresco Community Repo Parent</name>
@@ -51,18 +51,18 @@
<dependency.alfresco-log-sanitizer.version>0.2</dependency.alfresco-log-sanitizer.version>
<dependency.activiti-engine.version>5.23.0</dependency.activiti-engine.version>
<dependency.activiti.version>5.23.0</dependency.activiti.version>
<dependency.alfresco-transform-service.version>2.0.0-A3</dependency.alfresco-transform-service.version>
<dependency.alfresco-transform-core.version>3.0.0-A3</dependency.alfresco-transform-core.version>
<dependency.alfresco-transform-service.version>1.5.4-A3</dependency.alfresco-transform-service.version>
<dependency.alfresco-transform-core.version>2.7.0-A1</dependency.alfresco-transform-core.version>
<dependency.alfresco-greenmail.version>6.4</dependency.alfresco-greenmail.version>
<dependency.acs-event-model.version>0.0.16</dependency.acs-event-model.version>
<dependency.spring.version>5.3.23</dependency.spring.version>
<dependency.antlr.version>3.5.3</dependency.antlr.version>
<dependency.spring.version>5.3.22</dependency.spring.version>
<dependency.antlr.version>3.5.2</dependency.antlr.version>
<dependency.jackson.version>2.13.3</dependency.jackson.version>
<dependency.jackson-databind.version>2.13.3</dependency.jackson-databind.version>
<dependency.cxf.version>3.5.3</dependency.cxf.version>
<dependency.opencmis.version>1.0.0</dependency.opencmis.version>
<dependency.webscripts.version>8.32</dependency.webscripts.version>
<dependency.webscripts.version>8.31</dependency.webscripts.version>
<dependency.bouncycastle.version>1.70</dependency.bouncycastle.version>
<dependency.mockito-core.version>4.6.1</dependency.mockito-core.version>
<dependency.assertj.version>3.23.1</dependency.assertj.version>
@@ -74,20 +74,17 @@
<dependency.httpcore.version>4.4.15</dependency.httpcore.version>
<dependency.commons-httpclient.version>3.1-HTTPCLIENT-1265</dependency.commons-httpclient.version>
<dependency.xercesImpl.version>2.12.2</dependency.xercesImpl.version>
<dependency.slf4j.version>2.0.1</dependency.slf4j.version>
<dependency.gytheio.version>0.17</dependency.gytheio.version>
<dependency.slf4j.version>1.7.36</dependency.slf4j.version>
<dependency.gytheio.version>0.16</dependency.gytheio.version>
<dependency.groovy.version>3.0.12</dependency.groovy.version>
<dependency.tika.version>2.4.1</dependency.tika.version>
<dependency.spring-security.version>5.7.3</dependency.spring-security.version>
<dependency.spring-security.version>5.7.2</dependency.spring-security.version>
<dependency.truezip.version>7.7.10</dependency.truezip.version>
<dependency.poi.version>5.2.2</dependency.poi.version>
<dependency.ooxml-schemas.version>1.4</dependency.ooxml-schemas.version>
<dependency.keycloak.version>18.0.0</dependency.keycloak.version>
<dependency.jboss.logging.version>3.5.0.Final</dependency.jboss.logging.version>
<dependency.camel.version>3.18.2</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies -->
<dependency.netty.version>4.1.79.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
<dependency.netty.qpid.version>4.1.72.Final</dependency.netty.qpid.version> <!-- must be in sync with camels transitive dependencies: native-unix-common/native-epoll/native-kqueue -->
<dependency.netty-tcnative.version>2.0.53.Final</dependency.netty-tcnative.version> <!-- must be in sync with camels transitive dependencies -->
<dependency.camel.version>3.15.0</dependency.camel.version> <!-- when bumping this version, please keep track/sync with included netty.io dependencies (can cause dependency conflicts)-->
<dependency.activemq.version>5.17.1</dependency.activemq.version>
<dependency.apache-compress.version>1.21</dependency.apache-compress.version>
<dependency.apache.taglibs.version>1.2.5</dependency.apache.taglibs.version>
@@ -110,20 +107,21 @@
<dependency.jakarta-json-path.version>2.7.0</dependency.jakarta-json-path.version>
<dependency.jakarta-rpc-api.version>1.1.4</dependency.jakarta-rpc-api.version>
<alfresco.googledrive.version>3.2.3-A2</alfresco.googledrive.version>
<alfresco.googledrive.version>3.2.2</alfresco.googledrive.version>
<alfresco.aos-module.version>1.4.1</alfresco.aos-module.version>
<alfresco.api-explorer.version>7.2.1</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
<alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version>
<license-maven-plugin.version>2.0.1.alfresco-2</license-maven-plugin.version>
<dependency.postgresql.version>42.5.0</dependency.postgresql.version>
<dependency.postgresql.version>42.4.1</dependency.postgresql.version>
<dependency.mysql.version>8.0.30</dependency.mysql.version>
<dependency.mysql-image.version>8</dependency.mysql-image.version>
<dependency.mariadb.version>2.7.4</dependency.mariadb.version>
<dependency.tas-utility.version>3.0.56</dependency.tas-utility.version>
<dependency.rest-assured.version>5.2.0</dependency.rest-assured.version>
<dependency.tas-restapi.version>1.123</dependency.tas-restapi.version>
<dependency.tas-utility.version>3.0.49</dependency.tas-utility.version>
<dependency.rest-assured.version>5.1.1</dependency.rest-assured.version>
<dependency.tas-restapi.version>1.115</dependency.tas-restapi.version>
<dependency.tas-cmis.version>1.32</dependency.tas-cmis.version>
<dependency.tas-email.version>1.9</dependency.tas-email.version>
<dependency.tas-webdav.version>1.7</dependency.tas-webdav.version>
<dependency.tas-ftp.version>1.7</dependency.tas-ftp.version>
@@ -150,7 +148,7 @@
<connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection>
<developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection>
<url>https://github.com/Alfresco/alfresco-community-repo</url>
<tag>17.137</tag>
<tag>HEAD</tag>
</scm>
<distributionManagement>
@@ -407,7 +405,7 @@
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>5.1.1</version>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
@@ -515,11 +513,6 @@
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${dependency.jackson.version}</version>
</dependency>
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.32</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
@@ -663,7 +656,7 @@
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.3</version>
<version>1.15.2</version>
</dependency>
<!-- upgrade dependency from TIKA -->
<dependency>
@@ -714,6 +707,11 @@
<artifactId>restapi</artifactId>
<version>${dependency.tas-restapi.version}</version>
</dependency>
<dependency>
<groupId>org.alfresco.tas</groupId>
<artifactId>cmis</artifactId>
<version>${dependency.tas-cmis.version}</version>
</dependency>
<dependency>
<groupId>org.alfresco.tas</groupId>
<artifactId>email</artifactId>
@@ -737,7 +735,7 @@
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.11.1</version>
<version>2.10.14</version>
</dependency>
<!-- provided dependencies -->
@@ -853,18 +851,6 @@
<artifactId>camel-mock</artifactId>
<version>${dependency.camel.version}</version>
</dependency>
<!-- Netty non-transitive dependencies declared for depending projects usage in conjunction with Camel's other transitive netty dependencies -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler-proxy</artifactId>
<version>${dependency.netty.version}</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-classes</artifactId>
<version>${dependency.netty-tcnative.version}</version>
</dependency>
<dependency>
<groupId>org.apache.taglibs</groupId>
<artifactId>taglibs-standard-spec</artifactId>
@@ -946,7 +932,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version>
<version>3.4.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View File

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

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited
* 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
@@ -36,7 +36,7 @@ import org.alfresco.repo.content.metadata.MetadataExtracter;
import org.alfresco.repo.content.metadata.MetadataExtracterRegistry;
import org.alfresco.repo.content.transform.LocalTransformServiceRegistry;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.transform.registry.TransformServiceRegistry;
import org.alfresco.transform.client.registry.TransformServiceRegistry;
import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.DeclarativeWebScript;
import org.springframework.extensions.webscripts.Status;

View File

@@ -63,9 +63,4 @@ public interface RuleSets
* Link a rule set to a folder
*/
RuleSetLink linkToRuleSet(String folderNodeId, String linkToNodeId);
/**
* Removes the link between a rule set and a folder
*/
void unlinkRuleSet(String folderNodeId, String ruleSetId);
}

View File

@@ -1,107 +0,0 @@
/*
* #%L
* Alfresco Remote API
* %%
* 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.api.impl.mapper.rules;
import static org.alfresco.repo.action.access.ActionAccessRestriction.ACTION_CONTEXT_PARAM_NAME;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.action.CompositeActionImpl;
import org.alfresco.rest.api.impl.rules.ActionParameterConverter;
import org.alfresco.rest.api.model.mapper.RestModelMapper;
import org.alfresco.rest.api.model.rules.Action;
import org.alfresco.service.Experimental;
import org.alfresco.util.GUID;
import org.apache.commons.collections.CollectionUtils;
@Experimental
public class RestRuleActionModelMapper implements RestModelMapper<Action, org.alfresco.service.cmr.action.Action>
{
private final ActionParameterConverter parameterConverter;
public RestRuleActionModelMapper(ActionParameterConverter parameterConverter)
{
this.parameterConverter = parameterConverter;
}
/**
* Converts service POJO action to REST model action.
*
* @param actionModel - {@link org.alfresco.service.cmr.action.Action} service POJO
* @return {@link Action} REST model
*/
@Override
public Action toRestModel(org.alfresco.service.cmr.action.Action actionModel)
{
if (actionModel == null)
{
return null;
}
final Action.Builder builder = Action.builder().actionDefinitionId(actionModel.getActionDefinitionName());
if (actionModel.getParameterValues() != null)
{
final Map<String, Serializable> convertedParams = actionModel.getParameterValues()
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> parameterConverter.convertParamFromServiceModel(e.getValue())));
convertedParams.remove(ACTION_CONTEXT_PARAM_NAME);
builder.params(convertedParams);
}
return builder.create();
}
/**
* Convert the REST model objects to composite action service POJO.
*
* @param actions List of actions.
* @return The composite action service POJO.
*/
@Override
public org.alfresco.service.cmr.action.Action toServiceModel(Collection<Action> actions)
{
if (CollectionUtils.isEmpty(actions))
{
return null;
}
final org.alfresco.service.cmr.action.CompositeAction compositeAction = new CompositeActionImpl(null, GUID.generate());
actions.forEach(action -> compositeAction.addAction(toServiceAction(action)));
return compositeAction;
}
private org.alfresco.service.cmr.action.Action toServiceAction(Action action)
{
final Map<String, Serializable> convertedParams =
parameterConverter.getConvertedParams(action.getParams(), action.getActionDefinitionId());
return new ActionImpl(null, GUID.generate(), action.getActionDefinitionId(), convertedParams);
}
}

View File

@@ -1,162 +0,0 @@
/*
* #%L
* Alfresco Remote API
* %%
* 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.api.impl.mapper.rules;
import static org.alfresco.repo.action.evaluator.NoConditionEvaluator.NAME;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.alfresco.rest.api.model.mapper.RestModelMapper;
import org.alfresco.rest.api.model.rules.CompositeCondition;
import org.alfresco.rest.api.model.rules.ConditionOperator;
import org.alfresco.rest.api.model.rules.SimpleCondition;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.apache.commons.collections.CollectionUtils;
@Experimental
public class RestRuleCompositeConditionModelMapper implements RestModelMapper<CompositeCondition, ActionCondition>
{
private final RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper;
public RestRuleCompositeConditionModelMapper(
RestModelMapper<SimpleCondition, ActionCondition> simpleConditionMapper)
{
this.simpleConditionMapper = simpleConditionMapper;
}
/**
* Converts Action conditions (service POJO) list to composite condition (REST model).
*
* @param actionConditions - list of {@link ActionCondition} service POJOs
* @return {@link CompositeCondition} REST model
*/
@Override
public CompositeCondition toRestModel(final Collection<ActionCondition> actionConditions)
{
if (CollectionUtils.isEmpty(actionConditions))
{
return null;
}
final List<ActionCondition> filteredActions = actionConditions.stream()
.filter(Objects::nonNull)
.filter(c -> !NAME.equals(c.getActionConditionDefinitionName()))
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(filteredActions))
{
return null;
}
final CompositeCondition conditions = new CompositeCondition();
conditions.setCompositeConditions(new ArrayList<>());
// group action conditions by inversion flag
filteredActions.stream()
.collect(Collectors.groupingBy(ActionCondition::getInvertCondition))
// map action condition sub lists
.forEach((inverted, actionConditionsPart) -> Optional
.ofNullable(ofActionConditions(actionConditionsPart, inverted, ConditionOperator.AND))
// if composite condition present add to final list
.ifPresent(compositeCondition -> conditions.getCompositeConditions().add(compositeCondition)));
if (CollectionUtils.isEmpty(conditions.getCompositeConditions()))
{
conditions.setCompositeConditions(null);
}
return conditions;
}
@Override
public List<ActionCondition> toServiceModels(final CompositeCondition compositeCondition)
{
final List<ActionCondition> actionConditions = new ArrayList<>();
if (compositeCondition == null)
{
return actionConditions;
}
if (CollectionUtils.isNotEmpty(compositeCondition.getSimpleConditions()))
{
compositeCondition.getSimpleConditions()
.forEach(simpleCondition -> actionConditions.add(mapSimpleCondition(simpleCondition, compositeCondition.isInverted())));
}
if (CollectionUtils.isNotEmpty(compositeCondition.getCompositeConditions()))
{
compositeCondition.getCompositeConditions().forEach(condition -> actionConditions.addAll(toServiceModels(condition)));
}
return actionConditions;
}
private ActionCondition mapSimpleCondition(final SimpleCondition simpleCondition, final boolean inverted)
{
final ActionCondition actionCondition = simpleConditionMapper.toServiceModel(simpleCondition);
actionCondition.setInvertCondition(inverted);
return actionCondition;
}
private CompositeCondition ofActionConditions(final List<ActionCondition> actionConditions, final boolean inverted,
final ConditionOperator conditionOperator)
{
if (CollectionUtils.isEmpty(actionConditions))
{
return null;
}
return ofSimpleConditions(simpleConditionMapper.toRestModels(actionConditions), inverted, conditionOperator);
}
/**
* Creates a composite condition instance of simple conditions.
*
* @param simpleConditions - list of {@link SimpleCondition}
* @param inverted - determines if condition should be inverted
* @param conditionOperator - determines the operation, see {@link ConditionOperator}
* @return {@link CompositeCondition}
*/
private CompositeCondition ofSimpleConditions(final List<SimpleCondition> simpleConditions, final boolean inverted,
final ConditionOperator conditionOperator)
{
return of(simpleConditions, null, inverted, conditionOperator);
}
private CompositeCondition of(final List<SimpleCondition> simpleConditions, final List<CompositeCondition> compositeConditions,
final boolean inverted, final ConditionOperator conditionOperator)
{
if (CollectionUtils.isEmpty(simpleConditions) && CollectionUtils.isEmpty(compositeConditions))
{
return null;
}
return CompositeCondition.builder()
.inverted(inverted)
.booleanMode(conditionOperator)
.simpleConditions(simpleConditions)
.compositeConditions(compositeConditions)
.create();
}
}

View File

@@ -1,164 +0,0 @@
/*
* #%L
* Alfresco Remote API
* %%
* 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.api.impl.mapper.rules;
import java.io.Serializable;
import java.util.Map;
import java.util.stream.Collectors;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.action.executer.ScriptActionExecuter;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.impl.rules.ActionParameterConverter;
import org.alfresco.rest.api.model.mapper.RestModelMapper;
import org.alfresco.rest.api.model.rules.Action;
import org.alfresco.rest.api.model.rules.CompositeCondition;
import org.alfresco.rest.api.model.rules.Rule;
import org.alfresco.rest.api.model.rules.RuleTrigger;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.GUID;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@Experimental
public class RestRuleModelMapper implements RestModelMapper<Rule, org.alfresco.service.cmr.rule.Rule>
{
private static Log log = LogFactory.getLog(RestRuleModelMapper.class);
private final RestModelMapper<CompositeCondition, ActionCondition> compositeConditionMapper;
private final RestModelMapper<Action, org.alfresco.service.cmr.action.Action> actionMapper;
private final Nodes nodes;
private final ActionParameterConverter actionParameterConverter;
public RestRuleModelMapper(
RestModelMapper<CompositeCondition, ActionCondition> compositeConditionMapper,
RestModelMapper<Action, org.alfresco.service.cmr.action.Action> actionMapper,
Nodes nodes,
ActionParameterConverter actionParameterConverter)
{
this.compositeConditionMapper = compositeConditionMapper;
this.actionMapper = actionMapper;
this.nodes = nodes;
this.actionParameterConverter = actionParameterConverter;
}
/**
* Converts service POJO rule to REST model rule.
*
* @param serviceRule - {@link org.alfresco.service.cmr.rule.Rule} service POJO
* @return {@link Rule} REST model
*/
@Override
public Rule toRestModel(org.alfresco.service.cmr.rule.Rule serviceRule)
{
if (serviceRule == null)
{
return null;
}
final Rule.Builder builder = Rule.builder()
.name(serviceRule.getTitle())
.description(serviceRule.getDescription())
.isEnabled(!serviceRule.getRuleDisabled())
.isInheritable(serviceRule.isAppliedToChildren())
.isAsynchronous(serviceRule.getExecuteAsynchronously());
if (serviceRule.getNodeRef() != null)
{
builder.id(serviceRule.getNodeRef().getId());
}
if (CollectionUtils.isNotEmpty(serviceRule.getRuleTypes()))
{
builder.triggers(serviceRule.getRuleTypes().stream().map(RuleTrigger::of).collect(Collectors.toList()));
}
if (serviceRule.getAction() != null)
{
builder.conditions(compositeConditionMapper.toRestModel(serviceRule.getAction().getActionConditions()));
if (serviceRule.getAction().getCompensatingAction() != null &&
serviceRule.getAction().getCompensatingAction().getParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF) != null)
{
String errorScript = actionParameterConverter.convertParamFromServiceModel(
serviceRule.getAction().getCompensatingAction().getParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF)).toString();
builder.errorScript(errorScript);
}
if (serviceRule.getAction() instanceof CompositeAction && ((CompositeAction) serviceRule.getAction()).getActions() != null)
{
builder.actions(
((CompositeAction) serviceRule.getAction()).getActions().stream()
.map(actionMapper::toRestModel)
.collect(Collectors.toList()));
} else {
log.warn("Rule Action should be of 'CompositeAction' type but found: " + serviceRule.getAction().getClass());
}
}
return builder.create();
}
/**
* Convert the REST model object to the equivalent service POJO.
*
* @param restRuleModel {@link Rule} REST model.
* @return The rule service POJO.
*/
@Override
public org.alfresco.service.cmr.rule.Rule toServiceModel(Rule restRuleModel)
{
final org.alfresco.service.cmr.rule.Rule serviceRule = new org.alfresco.service.cmr.rule.Rule();
final NodeRef nodeRef = (restRuleModel.getId() != null) ? nodes.validateOrLookupNode(restRuleModel.getId(), null) : null;
serviceRule.setNodeRef(nodeRef);
serviceRule.setTitle(restRuleModel.getName());
serviceRule.setDescription(restRuleModel.getDescription());
serviceRule.setRuleDisabled(!restRuleModel.getIsEnabled());
serviceRule.applyToChildren(restRuleModel.getIsInheritable());
serviceRule.setExecuteAsynchronously(restRuleModel.getIsAsynchronous());
serviceRule.setRuleTypes(restRuleModel.getTriggers());
serviceRule.setAction(actionMapper.toServiceModel(restRuleModel.getActions()));
if (restRuleModel.getErrorScript() != null)
{
final org.alfresco.service.cmr.action.Action compensatingAction =
new ActionImpl(null, GUID.generate(), ScriptActionExecuter.NAME);
final Map<String, Serializable> scriptParam = actionParameterConverter
.getConvertedParams(Map.of(ScriptActionExecuter.PARAM_SCRIPTREF, restRuleModel.getErrorScript()),
compensatingAction.getActionDefinitionName());
compensatingAction.setParameterValues(scriptParam);
serviceRule.getAction().setCompensatingAction(compensatingAction);
}
if (restRuleModel.getConditions() != null)
{
compositeConditionMapper.toServiceModels(restRuleModel.getConditions())
.forEach(condition -> serviceRule.getAction().addActionCondition(condition));
}
return serviceRule;
}
}

View File

@@ -1,233 +0,0 @@
/*
* #%L
* Alfresco Remote API
* %%
* 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.api.impl.mapper.rules;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import com.rometools.utils.Strings;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionConditionImpl;
import org.alfresco.repo.action.evaluator.CompareMimeTypeEvaluator;
import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator;
import org.alfresco.repo.action.evaluator.HasAspectEvaluator;
import org.alfresco.repo.action.evaluator.HasTagEvaluator;
import org.alfresco.repo.action.evaluator.InCategoryEvaluator;
import org.alfresco.repo.action.evaluator.IsSubTypeEvaluator;
import org.alfresco.repo.action.evaluator.NoConditionEvaluator;
import org.alfresco.repo.action.evaluator.compare.ComparePropertyValueOperation;
import org.alfresco.repo.action.evaluator.compare.ContentPropertyName;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.mapper.RestModelMapper;
import org.alfresco.rest.api.model.rules.SimpleCondition;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.collections.MapUtils;
@Experimental
public class RestRuleSimpleConditionModelMapper implements RestModelMapper<SimpleCondition, ActionCondition>
{
static final String CATEGORY_INVALID_MSG = "Category in condition is invalid";
static final String PARAM_CATEGORY = "category";
static final String PARAM_MIMETYPE = "mimetype";
static final String FIELD_NOT_NULL = "Field in condition must not be blank";
static final String PARAMETER_NOT_NULL = "Parameter in condition must not be blank";
static final String COMPARATOR_NOT_NULL = "Comparator in condition must not be blank";
private final NamespaceService namespaceService;
private final Nodes nodes;
public RestRuleSimpleConditionModelMapper(NamespaceService namespaceService, Nodes nodes)
{
this.namespaceService = namespaceService;
this.nodes = nodes;
}
@Override
public SimpleCondition toRestModel(ActionCondition actionCondition)
{
if (actionCondition == null || actionCondition.getActionConditionDefinitionName() == null ||
MapUtils.isEmpty(actionCondition.getParameterValues()))
{
return null;
}
switch (actionCondition.getActionConditionDefinitionName())
{
case ComparePropertyValueEvaluator.NAME:
return createComparePropertyValueCondition(actionCondition, namespaceService);
case CompareMimeTypeEvaluator.NAME:
return createCompareMimeTypeCondition(actionCondition);
case HasAspectEvaluator.NAME:
return createHasAspectCondition(actionCondition, namespaceService);
case HasTagEvaluator.NAME:
return createHasTagCondition(actionCondition);
case InCategoryEvaluator.NAME:
return createInCategoryCondition(actionCondition);
case IsSubTypeEvaluator.NAME:
return createIsSubtypeCondition(actionCondition, namespaceService);
case NoConditionEvaluator.NAME:
default:
return null;
}
}
@Override
public ActionCondition toServiceModel(SimpleCondition restModel)
{
final String field = restModel.getField();
checkStringNotBlank(field, FIELD_NOT_NULL);
final Map<String, Serializable> parameterValues = new HashMap<>();
String conditionDefinitionId;
final String parameter = restModel.getParameter();
checkStringNotBlank(parameter, PARAMETER_NOT_NULL);
switch (field)
{
case HasAspectEvaluator.PARAM_ASPECT:
conditionDefinitionId = HasAspectEvaluator.NAME;
parameterValues.put(HasAspectEvaluator.PARAM_ASPECT, QName.createQName(parameter, namespaceService));
break;
case HasTagEvaluator.PARAM_TAG:
conditionDefinitionId = HasTagEvaluator.NAME;
parameterValues.put(HasTagEvaluator.PARAM_TAG, parameter);
break;
case PARAM_CATEGORY:
conditionDefinitionId = InCategoryEvaluator.NAME;
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_ASPECT, ContentModel.ASPECT_GEN_CLASSIFIABLE);
try
{
parameterValues.put(InCategoryEvaluator.PARAM_CATEGORY_VALUE, nodes.validateOrLookupNode(parameter, null));
} catch (EntityNotFoundException e) {
throw new InvalidArgumentException(CATEGORY_INVALID_MSG);
}
break;
case IsSubTypeEvaluator.PARAM_TYPE:
conditionDefinitionId = IsSubTypeEvaluator.NAME;
parameterValues.put(IsSubTypeEvaluator.PARAM_TYPE, QName.createQName(parameter, namespaceService));
break;
case PARAM_MIMETYPE:
conditionDefinitionId = CompareMimeTypeEvaluator.NAME;
parameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, ContentModel.TYPE_CONTENT);
parameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, parameter);
break;
default:
conditionDefinitionId = ComparePropertyValueEvaluator.NAME;
try
{
// if size or encoding create content property evaluator
ContentPropertyName.valueOf(field.toUpperCase());
parameterValues.put(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY, field.toUpperCase());
parameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, ContentModel.TYPE_CONTENT);
}
catch (IllegalArgumentException ignore)
{
// else create common property evaluator
parameterValues.put(ComparePropertyValueEvaluator.PARAM_PROPERTY, QName.createQName(field, namespaceService));
}
checkStringNotBlank(restModel.getComparator(), COMPARATOR_NOT_NULL);
parameterValues.put(ComparePropertyValueEvaluator.PARAM_OPERATION, restModel.getComparator().toUpperCase());
parameterValues.put(ComparePropertyValueEvaluator.PARAM_VALUE, parameter);
break;
}
return new ActionConditionImpl(UUID.randomUUID().toString(), conditionDefinitionId, parameterValues);
}
private void checkStringNotBlank(final String string, final String message) {
if (Strings.isBlank(string))
{
throw new InvalidArgumentException(message);
}
}
private static SimpleCondition createComparePropertyValueCondition(final ActionCondition actionCondition, final NamespaceService namespaceService)
{
final SimpleCondition.Builder builder = SimpleCondition.builder();
if (actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY) != null)
{
builder.field(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY).toString().toLowerCase());
} else {
builder.field(((QName) actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_PROPERTY)).toPrefixString(namespaceService));
}
return builder
.comparator(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_OPERATION).toString().toLowerCase())
.parameter(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_VALUE).toString())
.create();
}
private static SimpleCondition createCompareMimeTypeCondition(final ActionCondition actionCondition)
{
return SimpleCondition.builder()
.field(PARAM_MIMETYPE)
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_VALUE).toString())
.create();
}
private static SimpleCondition createHasAspectCondition(final ActionCondition actionCondition, final NamespaceService namespaceService)
{
return SimpleCondition.builder()
.field(HasAspectEvaluator.PARAM_ASPECT)
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(((QName) actionCondition.getParameterValues().get(HasAspectEvaluator.PARAM_ASPECT)).toPrefixString(namespaceService))
.create();
}
private static SimpleCondition createHasTagCondition(final ActionCondition actionCondition)
{
return SimpleCondition.builder()
.field(HasTagEvaluator.PARAM_TAG)
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(actionCondition.getParameterValues().get(HasTagEvaluator.PARAM_TAG).toString())
.create();
}
private static SimpleCondition createInCategoryCondition(final ActionCondition actionCondition)
{
return SimpleCondition.builder()
.field(PARAM_CATEGORY)
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(((NodeRef) actionCondition.getParameterValues().get(InCategoryEvaluator.PARAM_CATEGORY_VALUE)).getId())
.create();
}
private static SimpleCondition createIsSubtypeCondition(final ActionCondition actionCondition, final NamespaceService namespaceService)
{
return SimpleCondition.builder()
.field(IsSubTypeEvaluator.PARAM_TYPE)
.comparator(ComparePropertyValueOperation.EQUALS.toString().toLowerCase())
.parameter(((QName) actionCondition.getParameterValues().get(IsSubTypeEvaluator.PARAM_TYPE)).toPrefixString(namespaceService))
.create();
}
}

View File

@@ -48,7 +48,6 @@ import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.json.JSONArray;
import org.json.JSONException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@Experimental
public class ActionParameterConverter
@@ -65,18 +64,12 @@ public class ActionParameterConverter
this.namespaceService = namespaceService;
}
public Map<String, Serializable> getConvertedParams(Map<String, Serializable> params, String name)
Map<String, Serializable> getConvertedParams(Map<String, Serializable> params, String name)
{
final Map<String, Serializable> parameters = new HashMap<>(params.size());
final ParameterizedItemDefinition definition;
try
final ParameterizedItemDefinition definition = actionService.getActionDefinition(name);
if (definition == null)
{
definition = actionService.getActionDefinition(name);
if (definition == null)
{
throw new NotFoundException(NotFoundException.DEFAULT_MESSAGE_ID, new String[]{name});
}
} catch (NoSuchBeanDefinitionException e) {
throw new NotFoundException(NotFoundException.DEFAULT_MESSAGE_ID, new String[]{name});
}

View File

@@ -180,12 +180,6 @@ public class NodeValidator
}
}
/**
* Verifies if rule set node or folder node's default rule set is shared
* @param ruleSetNodeRef
* @param folderNodeRef
* @return
*/
public boolean isRuleSetNotNullAndShared(final NodeRef ruleSetNodeRef, final NodeRef folderNodeRef)
{
if (ruleSetNodeRef == null && folderNodeRef != null)

View File

@@ -27,12 +27,8 @@ package org.alfresco.rest.api.impl.rules;
import java.util.List;
import org.alfresco.rest.api.model.mapper.RestModelMapper;
import org.alfresco.rest.api.model.rules.CompositeCondition;
import org.alfresco.rest.api.model.rules.Rule;
import org.alfresco.rest.api.model.rules.SimpleCondition;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.RuleService;
@@ -43,11 +39,10 @@ public class RuleLoader
public static final String IS_SHARED = "isShared";
private RuleService ruleService;
private NodeValidator nodeValidator;
private RestModelMapper<Rule, org.alfresco.service.cmr.rule.Rule> ruleMapper;
public Rule loadRule(org.alfresco.service.cmr.rule.Rule ruleModel, List<String> includes)
{
final Rule rule = ruleMapper.toRestModel(ruleModel);
Rule rule = Rule.from(ruleModel);
if (includes != null && includes.contains(IS_SHARED))
{
NodeRef ruleSet = ruleService.getRuleSetNode(ruleModel.getNodeRef());
@@ -66,10 +61,4 @@ public class RuleLoader
{
this.nodeValidator = nodeValidator;
}
public void setRuleMapper(
RestModelMapper<Rule, org.alfresco.service.cmr.rule.Rule> ruleMapper)
{
this.ruleMapper = ruleMapper;
}
}

View File

@@ -31,13 +31,11 @@ import static org.alfresco.rest.api.model.rules.InclusionType.OWNED;
import java.util.List;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.rest.api.model.rules.RuleSet;
import org.alfresco.service.Experimental;
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.cmr.rule.RuleService;
/** Responsible for converting a NodeRef into a {@link RuleSet} object. */
@Experimental
@@ -45,13 +43,7 @@ public class RuleSetLoader
{
protected static final String OWNING_FOLDER = "owningFolder";
protected static final String INCLUSION_TYPE = "inclusionType";
protected static final String INHERITED_BY = "inheritedBy";
protected static final String LINKED_TO_BY = "linkedToBy";
protected static final String IS_INHERITED = "isInherited";
protected static final String IS_LINKED_TO = "isLinkedTo";
private static final int MAX_INHERITED_BY_SIZE = 100;
private NodeService nodeService;
private RuleService ruleService;
/**
* Load a rule set for the given node ref.
@@ -87,65 +79,12 @@ public class RuleSetLoader
ruleSet.setInclusionType(linked ? LINKED : INHERITED);
}
}
if (includes.contains(INHERITED_BY))
{
ruleSet.setInheritedBy(loadInheritedBy(ruleSetNodeRef));
}
if (includes.contains(LINKED_TO_BY))
{
ruleSet.setLinkedToBy(loadLinkedToBy(ruleSetNodeRef));
}
if (includes.contains(IS_INHERITED))
{
ruleSet.setIsInherited(loadIsInherited(ruleSetNodeRef));
}
if (includes.contains(IS_LINKED_TO))
{
ruleSet.setIsLinkedTo(loadIsLinkedTo(ruleSetNodeRef, parentRef));
}
}
return ruleSet;
}
private List<NodeRef> loadInheritedBy(NodeRef ruleSetNodeRef)
{
return ruleService.getFoldersInheritingRuleSet(ruleSetNodeRef, MAX_INHERITED_BY_SIZE);
}
private List<NodeRef> loadLinkedToBy(NodeRef ruleSetNodeRef)
{
return ruleService.getFoldersLinkingToRuleSet(ruleSetNodeRef);
}
private boolean loadIsInherited(NodeRef ruleSetNodeRef)
{
return AuthenticationUtil.runAsSystem(() -> !ruleService.getFoldersInheritingRuleSet(ruleSetNodeRef, 1).isEmpty());
}
/**
* Check if any parents of the rule set node are not the owning folder.
*
* @param ruleSetNodeRef The rule set node.
* @param parentRef The owning folder.
* @return True if another folder links to the rule set.
*/
private Boolean loadIsLinkedTo(NodeRef ruleSetNodeRef, NodeRef parentRef)
{
return AuthenticationUtil.runAsSystem(() ->
nodeService.getParentAssocs(ruleSetNodeRef)
.stream()
.map(ChildAssociationRef::getParentRef)
.anyMatch(folder -> !folder.equals(parentRef))
);
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setRuleService(RuleService ruleService)
{
this.ruleService = ruleService;
}
}

View File

@@ -29,7 +29,7 @@ package org.alfresco.rest.api.impl.rules;
import static java.util.stream.Collectors.toList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import org.alfresco.repo.rule.RuleModel;
import org.alfresco.repo.rule.RuntimeRuleService;
@@ -41,7 +41,6 @@ import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.ListPage;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.repository.AspectMissingException;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.rule.RuleService;
@@ -60,13 +59,10 @@ public class RuleSetsImpl implements RuleSets
{
NodeRef folderNode = validator.validateFolderNode(folderNodeId, false);
List<RuleSet> ruleSets = ruleService.getNodesSupplyingRuleSets(folderNode)
.stream()
.map(ruleService::getRuleSetNode)
.filter(Objects::nonNull)
.map(nodeRef -> ruleSetLoader.loadRuleSet(nodeRef, folderNode, includes))
.distinct()
.collect(toList());
NodeRef ruleSetNode = ruleService.getRuleSetNode(folderNode);
List<RuleSet> ruleSets = Optional.ofNullable(ruleSetNode)
.map(nodeRef -> ruleSetLoader.loadRuleSet(nodeRef, folderNode, includes))
.stream().collect(toList());
return ListPage.of(ruleSets, paging);
}
@@ -97,7 +93,7 @@ public class RuleSetsImpl implements RuleSets
//The folder shouldn't have any pre-existing rules
if (ruleService.hasRules(folderNodeRef)) {
throw new InvalidArgumentException("Unable to link to a rule set because the folder has pre-existing rules or is already linked to a rule set.");
throw new InvalidArgumentException("Unable to link to a ruleset because the folder has pre-existing rules or is already linked to a ruleset.");
}
// Create the destination folder as a secondary child of the first
@@ -111,22 +107,6 @@ public class RuleSetsImpl implements RuleSets
return ruleSetLink;
}
@Override
public void unlinkRuleSet(String folderNodeId, String ruleSetId)
{
final NodeRef folderNodeRef = validator.validateFolderNode(folderNodeId,true);
final NodeRef ruleSetNodeRef = validator.validateRuleSetNode(ruleSetId, folderNodeRef);
//The folder should be linked to a rule set
if (!ruleService.isLinkedToRuleNode(folderNodeRef))
{
throw new InvalidArgumentException("The folder is not linked to a rule set.");
}
//The following line also handles the deletion of the parent-child association that gets created during linking
nodeService.removeAspect(folderNodeRef,RuleModel.ASPECT_RULES);
}
public void setRuleSetLoader(RuleSetLoader ruleSetLoader)
{
this.ruleSetLoader = ruleSetLoader;

View File

@@ -32,23 +32,15 @@ import java.util.stream.Collectors;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.Rules;
import org.alfresco.rest.api.model.mapper.RestModelMapper;
import org.alfresco.rest.api.model.rules.CompositeCondition;
import org.alfresco.rest.api.model.rules.Rule;
import org.alfresco.rest.api.model.rules.RuleSet;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.api.model.rules.SimpleCondition;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.ListPage;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.rule.RuleService;
import org.alfresco.util.collections.CollectionUtils;
import org.alfresco.service.namespace.NamespaceService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,13 +48,13 @@ import org.slf4j.LoggerFactory;
public class RulesImpl implements Rules
{
private static final Logger LOGGER = LoggerFactory.getLogger(RulesImpl.class);
private static final String MUST_HAVE_AT_LEAST_ONE_ACTION = "A rule must have at least one action";
private Nodes nodes;
private RuleService ruleService;
private NodeValidator validator;
private RuleLoader ruleLoader;
private ActionParameterConverter actionParameterConverter;
private ActionPermissionValidator actionPermissionValidator;
private RestModelMapper<Rule, org.alfresco.service.cmr.rule.Rule> ruleMapper;
@Override
public CollectionWithPagingInfo<Rule> getRules(final String folderNodeId,
@@ -71,11 +63,10 @@ public class RulesImpl implements Rules
final Paging paging)
{
final NodeRef folderNodeRef = validator.validateFolderNode(folderNodeId, false);
NodeRef ruleSetNode = validator.validateRuleSetNode(ruleSetId, folderNodeRef);
NodeRef owningFolder = ruleService.getOwningNodeRef(ruleSetNode);
validator.validateRuleSetNode(ruleSetId, folderNodeRef);
final List<Rule> rules = ruleService.getRules(owningFolder, false).stream()
.map(ruleModel -> ruleLoader.loadRule(ruleModel, includes))
final List<Rule> rules = ruleService.getRules(folderNodeRef).stream()
.map(ruleModel -> loadRuleAndConvertActionParams(ruleModel, includes))
.collect(Collectors.toList());
return ListPage.of(rules, paging);
@@ -88,7 +79,7 @@ public class RulesImpl implements Rules
final NodeRef ruleSetNodeRef = validator.validateRuleSetNode(ruleSetId, folderNodeRef);
final NodeRef ruleNodeRef = validator.validateRuleNode(ruleId, ruleSetNodeRef);
return ruleLoader.loadRule(ruleService.getRule(ruleNodeRef), includes);
return loadRuleAndConvertActionParams(ruleService.getRule(ruleNodeRef), includes);
}
@Override
@@ -104,7 +95,7 @@ public class RulesImpl implements Rules
return rules.stream()
.map(this::mapToServiceModelAndValidateActions)
.map(rule -> ruleService.saveRule(folderNodeRef, rule))
.map(rule -> ruleLoader.loadRule(rule, includes))
.map(rule -> loadRuleAndConvertActionParams(rule, includes))
.collect(Collectors.toList());
}
@@ -132,15 +123,33 @@ public class RulesImpl implements Rules
private org.alfresco.service.cmr.rule.Rule mapToServiceModelAndValidateActions(Rule rule)
{
if (CollectionUtils.isEmpty(rule.getActions()))
{
throw new InvalidArgumentException(MUST_HAVE_AT_LEAST_ONE_ACTION);
}
final org.alfresco.service.cmr.rule.Rule serviceModelRule = ruleMapper.toServiceModel(rule);
final org.alfresco.service.cmr.rule.Rule serviceModelRule = rule.toServiceModel(nodes);
final CompositeAction compositeAction = (CompositeAction) serviceModelRule.getAction();
compositeAction.getActions().forEach(action -> action.setParameterValues(
actionParameterConverter.getConvertedParams(action.getParameterValues(), action.getActionDefinitionName())));
return actionPermissionValidator.validateRulePermissions(serviceModelRule);
}
private Rule loadRuleAndConvertActionParams(org.alfresco.service.cmr.rule.Rule ruleModel, List<String> includes)
{
final Rule rule = ruleLoader.loadRule(ruleModel, includes);
rule.getActions()
.forEach(a -> a.setParams(a.getParams().entrySet()
.stream()
.collect(Collectors
.toMap(Map.Entry::getKey, e -> actionParameterConverter.convertParamFromServiceModel(e.getValue())))
)
);
return rule;
}
public void setNodes(Nodes nodes)
{
this.nodes = nodes;
}
public void setRuleService(RuleService ruleService)
{
this.ruleService = ruleService;
@@ -156,14 +165,13 @@ public class RulesImpl implements Rules
this.ruleLoader = ruleLoader;
}
public void setActionParameterConverter(ActionParameterConverter actionParameterConverter)
{
this.actionParameterConverter = actionParameterConverter;
}
public void setActionPermissionValidator(ActionPermissionValidator actionPermissionValidator)
{
this.actionPermissionValidator = actionPermissionValidator;
}
public void setRuleMapper(
RestModelMapper<Rule, org.alfresco.service.cmr.rule.Rule> ruleMapper)
{
this.ruleMapper = ruleMapper;
}
}

View File

@@ -284,8 +284,7 @@ public class RepositoryInfo
.setMaxDocs(licenseDescriptor.getMaxDocs())
.setMaxUsers(licenseDescriptor.getMaxUsers())
.setClusterEnabled(licenseDescriptor.isClusterEnabled())
.setCryptodocEnabled(licenseDescriptor.isCryptodocEnabled())
.setCustomEmbeddedWorkflowEnabled(licenseDescriptor.isCustomEmbeddedWorkflowEnabled());
.setCryptodocEnabled(licenseDescriptor.isCryptodocEnabled());
}
public Date getIssuedAt()
@@ -344,7 +343,6 @@ public class RepositoryInfo
private Long maxDocs;
private boolean isClusterEnabled;
private boolean isCryptodocEnabled;
private boolean isCustomEmbeddedWorkflowEnabled;
public LicenseEntitlement()
{
@@ -394,17 +392,6 @@ public class RepositoryInfo
return this;
}
public boolean getIsCustomEmbeddedWorkflowEnabled()
{
return isCustomEmbeddedWorkflowEnabled;
}
public LicenseEntitlement setCustomEmbeddedWorkflowEnabled(boolean customEmbeddedWorkflowEnabled)
{
isCustomEmbeddedWorkflowEnabled = customEmbeddedWorkflowEnabled;
return this;
}
@Override
public String toString()
{
@@ -413,7 +400,6 @@ public class RepositoryInfo
.append(", maxDocs=").append(maxDocs)
.append(", isClusterEnabled=").append(isClusterEnabled)
.append(", isCryptodocEnabled=").append(isCryptodocEnabled)
.append(", isCustomEmbeddedWorkflowEnabled=").append(isCustomEmbeddedWorkflowEnabled)
.append(']');
return sb.toString();
}

View File

@@ -1,70 +0,0 @@
/*
* #%L
* Alfresco Remote API
* %%
* 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.api.model.mapper;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.alfresco.service.Experimental;
import org.apache.commons.lang3.NotImplementedException;
@Experimental
public interface RestModelMapper<R, S>
{
default R toRestModel(S serviceModel) {
throw new NotImplementedException();
}
default S toServiceModel(R restModel) {
throw new NotImplementedException();
}
default R toRestModel(Collection<S> serviceModels) {
throw new NotImplementedException();
}
default S toServiceModel(Collection<R> restModels) {
throw new NotImplementedException();
}
default List<R> toRestModels(Collection<S> serviceModels) {
return serviceModels.stream()
.filter(Objects::nonNull)
.map(this::toRestModel)
.collect(Collectors.toList());
}
default List<S> toServiceModels(Collection<R> restModels) {
return restModels.stream()
.filter(Objects::nonNull)
.map(this::toServiceModel)
.collect(Collectors.toList());
}
default List<R> toRestModels(S serviceModel) {
throw new NotImplementedException();
}
default List<S> toServiceModels(R restModel) {
throw new NotImplementedException();
}
}

View File

@@ -27,10 +27,16 @@
package org.alfresco.rest.api.model.rules;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.action.CompositeActionImpl;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.GUID;
@Experimental
public class Action
@@ -38,6 +44,56 @@ public class Action
private String actionDefinitionId;
private Map<String, Serializable> params;
/**
* Converts service POJO action to REST model action.
*
* @param actionModel - {@link org.alfresco.service.cmr.action.Action} service POJO
* @return {@link Action} REST model
*/
public static Action from(final org.alfresco.service.cmr.action.Action actionModel)
{
if (actionModel == null)
{
return null;
}
final Action.Builder builder = builder().actionDefinitionId(actionModel.getActionDefinitionName());
if (actionModel.getParameterValues() != null)
{
builder.params(new HashMap<>(actionModel.getParameterValues()));
}
return builder.create();
}
/**
* Convert the REST model object to the equivalent service POJO.
*
* @param nodeRef The node reference.
* @return The action service POJO.
*/
public org.alfresco.service.cmr.action.Action toServiceModel(final NodeRef nodeRef)
{
return new ActionImpl(nodeRef, GUID.generate(), this.actionDefinitionId, params);
}
/**
* Convert the REST model objects to composite action service POJO.
*
* @param actions List of actions.
* @return The composite action service POJO.
*/
public static org.alfresco.service.cmr.action.Action toCompositeAction(final List<Action> actions) {
if (actions == null)
{
return null;
}
final org.alfresco.service.cmr.action.CompositeAction compositeAction = new CompositeActionImpl(null, GUID.generate());
actions.forEach(action -> compositeAction.addAction(action.toServiceModel(null)));
return compositeAction;
}
public String getActionDefinitionId()
{
return actionDefinitionId;

View File

@@ -26,10 +26,15 @@
package org.alfresco.rest.api.model.rules;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.apache.commons.collections.CollectionUtils;
@Experimental
public class CompositeCondition
@@ -39,6 +44,74 @@ public class CompositeCondition
private List<CompositeCondition> compositeConditions;
private List<SimpleCondition> simpleConditions;
/**
* Converts Action conditions (service POJO) list to composite condition (REST model).
*
* @param actionConditions - list of {@link ActionCondition} service POJOs
* @return {@link CompositeCondition} REST model
*/
public static CompositeCondition from(final List<ActionCondition> actionConditions)
{
if (actionConditions == null)
{
return null;
}
final CompositeCondition conditions = new CompositeCondition();
conditions.compositeConditions = new ArrayList<>();
// group action conditions by inversion flag
actionConditions.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(ActionCondition::getInvertCondition))
// map action condition sub lists
.forEach((inverted, actionConditionsPart) -> Optional.ofNullable(CompositeCondition.ofActionConditions(actionConditionsPart, inverted, ConditionOperator.AND))
// if composite condition present add to final list
.ifPresent(compositeCondition -> conditions.compositeConditions.add(compositeCondition)));
if (conditions.compositeConditions.isEmpty()) {
conditions.compositeConditions = null;
}
return conditions;
}
private static CompositeCondition ofActionConditions(final List<ActionCondition> actionConditions, final boolean inverted, final ConditionOperator conditionOperator)
{
if (actionConditions == null)
{
return null;
}
return ofSimpleConditions(SimpleCondition.listOf(actionConditions), inverted, conditionOperator);
}
/**
* Creates a composite condition instance of simple conditions.
*
* @param simpleConditions - list of {@link SimpleCondition}
* @param inverted - determines if condition should be inverted
* @param conditionOperator - determines the operation, see {@link ConditionOperator}
* @return {@link CompositeCondition}
*/
public static CompositeCondition ofSimpleConditions(final List<SimpleCondition> simpleConditions, final boolean inverted, final ConditionOperator conditionOperator)
{
return of(simpleConditions, null, inverted, conditionOperator);
}
private static CompositeCondition of(final List<SimpleCondition> simpleConditions, final List<CompositeCondition> compositeConditions,
final boolean inverted, final ConditionOperator conditionOperator)
{
if (CollectionUtils.isEmpty(simpleConditions) && CollectionUtils.isEmpty(compositeConditions))
{
return null;
}
return builder()
.inverted(inverted)
.booleanMode(conditionOperator)
.simpleConditions(simpleConditions)
.compositeConditions(compositeConditions)
.create();
}
public boolean isInverted()
{
return inverted;
@@ -58,14 +131,6 @@ public class CompositeCondition
return booleanMode.name().toLowerCase();
}
public void setBooleanMode(String booleanMode)
{
if (booleanMode != null)
{
this.booleanMode = ConditionOperator.valueOf(booleanMode.toUpperCase());
}
}
public void setBooleanMode(ConditionOperator booleanMode)
{
this.booleanMode = booleanMode;

View File

@@ -30,8 +30,14 @@ import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import org.alfresco.repo.action.ActionImpl;
import org.alfresco.repo.action.executer.ScriptActionExecuter;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.framework.resource.UniqueId;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.CompositeAction;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.GUID;
@Experimental
public class Rule
@@ -39,15 +45,89 @@ public class Rule
private String id;
private String name;
private String description;
private boolean isEnabled;
private boolean isInheritable;
private boolean isAsynchronous;
private boolean enabled;
private boolean cascade;
private boolean asynchronous;
private Boolean isShared;
private String errorScript;
private List<RuleTrigger> triggers = List.of(RuleTrigger.INBOUND);
private CompositeCondition conditions;
private List<Action> actions;
/**
* Converts service POJO rule to REST model rule.
*
* @param ruleModel - {@link org.alfresco.service.cmr.rule.Rule} service POJO
* @return {@link Rule} REST model
*/
public static Rule from(final org.alfresco.service.cmr.rule.Rule ruleModel)
{
if (ruleModel == null)
{
return null;
}
final Rule.Builder builder = builder()
.name(ruleModel.getTitle())
.description(ruleModel.getDescription())
.enabled(!ruleModel.getRuleDisabled())
.cascade(ruleModel.isAppliedToChildren())
.asynchronous(ruleModel.getExecuteAsynchronously());
if (ruleModel.getNodeRef() != null) {
builder.id(ruleModel.getNodeRef().getId());
}
if (ruleModel.getRuleTypes() != null)
{
builder.triggers(ruleModel.getRuleTypes().stream().map(RuleTrigger::of).collect(Collectors.toList()));
}
if (ruleModel.getAction() != null)
{
builder.conditions(CompositeCondition.from(ruleModel.getAction().getActionConditions()));
if (ruleModel.getAction().getCompensatingAction() != null && ruleModel.getAction().getCompensatingAction().getParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF) != null)
{
builder.errorScript(ruleModel.getAction().getCompensatingAction().getParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF).toString());
}
if (ruleModel.getAction() instanceof CompositeAction && ((CompositeAction) ruleModel.getAction()).getActions() != null)
{
builder.actions(((CompositeAction) ruleModel.getAction()).getActions().stream().map(Action::from).collect(Collectors.toList()));
}
}
return builder.create();
}
/**
* Convert the REST model object to the equivalent service POJO.
*
* @param nodes The nodes API.
* @return The rule service POJO.
*/
public org.alfresco.service.cmr.rule.Rule toServiceModel(Nodes nodes)
{
final org.alfresco.service.cmr.rule.Rule ruleModel = new org.alfresco.service.cmr.rule.Rule();
final NodeRef nodeRef = (id != null) ? nodes.validateOrLookupNode(id, null) : null;
ruleModel.setNodeRef(nodeRef);
ruleModel.setTitle(name);
ruleModel.setDescription(description);
ruleModel.setRuleDisabled(!enabled);
ruleModel.applyToChildren(cascade);
ruleModel.setExecuteAsynchronously(asynchronous);
if (triggers != null)
{
ruleModel.setRuleTypes(triggers.stream().map(RuleTrigger::getValue).collect(Collectors.toList()));
}
ruleModel.setAction(Action.toCompositeAction(actions));
if (errorScript != null)
{
final org.alfresco.service.cmr.action.Action compensatingAction = new ActionImpl(null, GUID.generate(), ScriptActionExecuter.NAME);
compensatingAction.setParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF, errorScript);
ruleModel.getAction().setCompensatingAction(compensatingAction);
}
return ruleModel;
}
@UniqueId
public String getId()
{
@@ -79,34 +159,34 @@ public class Rule
this.description = description;
}
public boolean getIsEnabled()
public boolean isEnabled()
{
return isEnabled;
return enabled;
}
public void setIsEnabled(boolean isEnabled)
public void setEnabled(boolean enabled)
{
this.isEnabled = isEnabled;
this.enabled = enabled;
}
public boolean getIsInheritable()
public boolean isCascade()
{
return isInheritable;
return cascade;
}
public void setIsInheritable(boolean isInheritable)
public void setCascade(boolean cascade)
{
this.isInheritable = isInheritable;
this.cascade = cascade;
}
public boolean getIsAsynchronous()
public boolean isAsynchronous()
{
return isAsynchronous;
return asynchronous;
}
public void setIsAsynchronous(boolean isAsynchronous)
public void setAsynchronous(boolean asynchronous)
{
this.isAsynchronous = isAsynchronous;
this.asynchronous = asynchronous;
}
public String getErrorScript()
@@ -174,8 +254,8 @@ public class Rule
@Override
public String toString()
{
return "Rule{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", description='" + description + '\'' + ", isEnabled=" + isEnabled + ", isInheritable=" + isInheritable
+ ", isAsynchronous=" + isAsynchronous + ", isShared=" + isShared + ", errorScript='" + errorScript + '\'' + ", triggers=" + triggers + ", conditions=" + conditions
return "Rule{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", description='" + description + '\'' + ", enabled=" + enabled + ", cascade=" + cascade
+ ", asynchronous=" + asynchronous + ", isShared=" + isShared + ", errorScript='" + errorScript + '\'' + ", triggers=" + triggers + ", conditions=" + conditions
+ ", actions=" + actions + '}';
}
@@ -187,9 +267,9 @@ public class Rule
if (o == null || getClass() != o.getClass())
return false;
Rule rule = (Rule) o;
return isEnabled == rule.isEnabled
&& isInheritable == rule.isInheritable
&& isAsynchronous == rule.isAsynchronous
return enabled == rule.enabled
&& cascade == rule.cascade
&& asynchronous == rule.asynchronous
&& Objects.equals(isShared, rule.isShared)
&& Objects.equals(id, rule.id)
&& Objects.equals(name, rule.name)
@@ -203,7 +283,7 @@ public class Rule
@Override
public int hashCode()
{
return Objects.hash(id, name, description, isEnabled, isInheritable, isAsynchronous, isShared, errorScript, triggers, conditions, actions);
return Objects.hash(id, name, description, enabled, cascade, asynchronous, isShared, errorScript, triggers, conditions, actions);
}
public static Builder builder()
@@ -217,9 +297,9 @@ public class Rule
private String id;
private String name;
private String description;
private boolean isEnabled;
private boolean isInheritable;
private boolean isAsynchronous;
private boolean enabled;
private boolean cascade;
private boolean asynchronous;
private Boolean isShared;
private String errorScript;
private List<RuleTrigger> triggers = List.of(RuleTrigger.INBOUND);
@@ -244,21 +324,21 @@ public class Rule
return this;
}
public Builder isEnabled(boolean isEnabled)
public Builder enabled(boolean enabled)
{
this.isEnabled = isEnabled;
this.enabled = enabled;
return this;
}
public Builder isInheritable(boolean isInheritable)
public Builder cascade(boolean cascade)
{
this.isInheritable = isInheritable;
this.cascade = cascade;
return this;
}
public Builder isAsynchronous(boolean isAsynchronous)
public Builder asynchronous(boolean asynchronous)
{
this.isAsynchronous = isAsynchronous;
this.asynchronous = asynchronous;
return this;
}
@@ -298,9 +378,9 @@ public class Rule
rule.setId(id);
rule.setName(name);
rule.setDescription(description);
rule.setIsEnabled(isEnabled);
rule.setIsInheritable(isInheritable);
rule.setIsAsynchronous(isAsynchronous);
rule.setEnabled(enabled);
rule.setCascade(cascade);
rule.setAsynchronous(asynchronous);
rule.setIsShared(isShared);
rule.setErrorScript(errorScript);
rule.setRuleTriggers(triggers);

View File

@@ -26,7 +26,6 @@
package org.alfresco.rest.api.model.rules;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
@@ -41,10 +40,6 @@ public class RuleSet
private String id;
private NodeRef owningFolder;
private InclusionType inclusionType;
private List<NodeRef> inheritedBy;
private List<NodeRef> linkedToBy;
private Boolean isInherited;
private Boolean isLinkedTo;
public static RuleSet of(String id)
{
@@ -91,66 +86,6 @@ public class RuleSet
this.inclusionType = inclusionType;
}
public List<NodeRef> getInheritedBy()
{
return inheritedBy;
}
public void setInheritedBy(List<NodeRef> inheritedBy)
{
this.inheritedBy = inheritedBy;
}
public List<NodeRef> getLinkedToBy()
{
return linkedToBy;
}
public void setLinkedToBy(List<NodeRef> linkedToBy)
{
this.linkedToBy = linkedToBy;
}
/**
* Set a flag indicating that the rule set is inherited by a folder.
*
* @param inherited The flag.
*/
public void setIsInherited(Boolean inherited)
{
isInherited = inherited;
}
/**
* Find if the rule set is inherited by a folder.
*
* @return The value of the flag.
*/
public Boolean getIsInherited()
{
return isInherited;
}
/**
* Set a flag indicating that the rule set is linked to by a folder.
*
* @param isLinkedTo The flag.
*/
public void setIsLinkedTo(Boolean isLinkedTo)
{
this.isLinkedTo = isLinkedTo;
}
/**
* Find if the rule set is linked to by a folder.
*
* @return The value of the flag.
*/
public Boolean getIsLinkedTo()
{
return isLinkedTo;
}
@Override
public String toString()
{
@@ -159,10 +94,6 @@ public class RuleSet
.add("id='" + id + "'")
.add("owningFolder='" + owningFolder + "'")
.add("inclusionType='" + inclusionType + "'")
.add("inheritedBy='" + inheritedBy + "'")
.add("linkedToBy='" + linkedToBy + "'")
.add("isInherited='" + isInherited + "'")
.add("isLinkedTo='" + isLinkedTo + "'")
.toString()
+ '}';
}
@@ -177,17 +108,13 @@ public class RuleSet
RuleSet ruleSet = (RuleSet) o;
return Objects.equals(id, ruleSet.id)
&& Objects.equals(owningFolder, ruleSet.owningFolder)
&& inclusionType == ruleSet.inclusionType
&& Objects.equals(inheritedBy, ruleSet.inheritedBy)
&& Objects.equals(linkedToBy, ruleSet.linkedToBy)
&& Objects.equals(isInherited, ruleSet.isInherited)
&& Objects.equals(isLinkedTo, ruleSet.isLinkedTo);
&& inclusionType == ruleSet.inclusionType;
}
@Override
public int hashCode()
{
return Objects.hash(id, owningFolder, inclusionType, inheritedBy, linkedToBy, isInherited, isLinkedTo);
return Objects.hash(id, owningFolder, inclusionType);
}
public static Builder builder()
@@ -200,10 +127,6 @@ public class RuleSet
private String id;
private NodeRef owningFolder;
private InclusionType inclusionType;
private List<NodeRef> inheritedBy;
private List<NodeRef> linkedToBy;
private Boolean isInherited;
private Boolean isLinkedTo;
public Builder id(String id)
{
@@ -223,40 +146,12 @@ public class RuleSet
return this;
}
public Builder inheritedBy(List<NodeRef> inheritedBy)
{
this.inheritedBy = inheritedBy;
return this;
}
public Builder linkedToBy(List<NodeRef> linkedToBy)
{
this.linkedToBy = linkedToBy;
return this;
}
public Builder isInherited(Boolean isInherited)
{
this.isInherited = isInherited;
return this;
}
public Builder isLinkedTo(Boolean isLinkedTo)
{
this.isLinkedTo = isLinkedTo;
return this;
}
public RuleSet create()
{
final RuleSet ruleSet = new RuleSet();
ruleSet.setId(id);
ruleSet.setOwningFolder(owningFolder);
ruleSet.setInclusionType(inclusionType);
ruleSet.setInheritedBy(inheritedBy);
ruleSet.setLinkedToBy(linkedToBy);
ruleSet.setIsInherited(isInherited);
ruleSet.setIsLinkedTo(isLinkedTo);
return ruleSet;
}
}

View File

@@ -30,7 +30,7 @@ public class RuleSetLink
{
/**
* This id is referring to the node id of either the rule set or the folder that contains the rule set.
* This id is referring to the node id of the linked-to-folder which contains the ruleset(s)
*/
private String id;

View File

@@ -26,43 +26,88 @@
package org.alfresco.rest.api.model.rules;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ActionConditionImpl;
import org.alfresco.repo.action.evaluator.CompareMimeTypeEvaluator;
import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator;
import org.alfresco.repo.action.evaluator.HasAspectEvaluator;
import org.alfresco.repo.action.evaluator.HasChildEvaluator;
import org.alfresco.repo.action.evaluator.HasTagEvaluator;
import org.alfresco.repo.action.evaluator.HasVersionHistoryEvaluator;
import org.alfresco.repo.action.evaluator.InCategoryEvaluator;
import org.alfresco.repo.action.evaluator.IsSubTypeEvaluator;
import org.alfresco.repo.action.evaluator.NoConditionEvaluator;
import org.alfresco.repo.action.evaluator.compare.ComparePropertyValueOperation;
import org.alfresco.repo.action.evaluator.compare.ContentPropertyName;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.mapper.RestModelMapper;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.service.Experimental;
import org.alfresco.service.cmr.action.ActionCondition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.collections.CollectionUtils;
@Experimental
public class SimpleCondition
{
private static final String COMPARATOR_EQUALS = "equals";
private String field;
private String comparator;
private String parameter;
/**
* Converts list of service POJO action conditions to list of REST model simple conditions.
*
* @param actionConditions - list of {@link ActionCondition} service POJOs
* @return list of {@link SimpleCondition} REST models
*/
public static List<SimpleCondition> listOf(final List<ActionCondition> actionConditions)
{
if (CollectionUtils.isEmpty(actionConditions))
{
return null;
}
return actionConditions.stream()
.map(SimpleCondition::from)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* Creates simple condition REST model instance from service POJO action condition.
*
* @param actionCondition - {@link ActionCondition} service POJO
* @return {@link SimpleCondition} REST model
*/
public static SimpleCondition from(final ActionCondition actionCondition)
{
if (actionCondition == null || actionCondition.getActionConditionDefinitionName() == null || actionCondition.getParameterValues() == null)
{
return null;
}
switch (actionCondition.getActionConditionDefinitionName())
{
case ComparePropertyValueEvaluator.NAME:
return createComparePropertyValueCondition(actionCondition);
case CompareMimeTypeEvaluator.NAME:
return createCompareMimeTypeCondition(actionCondition);
case HasAspectEvaluator.NAME:
return createHasAspectCondition(actionCondition);
case HasChildEvaluator.NAME:
return createHasChildCondition(actionCondition);
case HasTagEvaluator.NAME:
return createHasTagCondition(actionCondition);
case HasVersionHistoryEvaluator.NAME:
return createHasVersionHistoryCondition(actionCondition);
case InCategoryEvaluator.NAME:
return createInCategoryCondition(actionCondition);
case IsSubTypeEvaluator.NAME:
return createIsSubtypeCondition(actionCondition);
case NoConditionEvaluator.NAME:
default:
return null;
}
}
public String getField()
{
return field;
@@ -116,6 +161,82 @@ public class SimpleCondition
return Objects.hash(field, comparator, parameter);
}
private static SimpleCondition createComparePropertyValueCondition(final ActionCondition actionCondition) {
final SimpleCondition.Builder builder = builder();
if (actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY) != null)
{
builder.field(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_CONTENT_PROPERTY).toString().toLowerCase());
} else {
builder.field(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_PROPERTY).toString().toLowerCase());
}
return builder
.comparator(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_OPERATION).toString().toLowerCase())
.parameter(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_VALUE).toString())
.create();
}
private static SimpleCondition createCompareMimeTypeCondition(final ActionCondition actionCondition) {
return builder()
.field(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_PROPERTY).toString().toLowerCase())
.comparator(COMPARATOR_EQUALS)
.parameter(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_VALUE).toString())
.create();
}
private static SimpleCondition createHasAspectCondition(final ActionCondition actionCondition) {
return builder()
.field(HasAspectEvaluator.PARAM_ASPECT)
.comparator(COMPARATOR_EQUALS)
.parameter(actionCondition.getParameterValues().get(HasAspectEvaluator.PARAM_ASPECT).toString())
.create();
}
private static SimpleCondition createHasChildCondition(final ActionCondition actionCondition) {
final SimpleCondition.Builder builder = builder();
if (actionCondition.getParameterValues().get(HasChildEvaluator.PARAM_ASSOC_TYPE) != null)
{
builder.field(actionCondition.getParameterValues().get(HasChildEvaluator.PARAM_ASSOC_TYPE).toString().toLowerCase());
} else {
builder.field(actionCondition.getParameterValues().get(HasChildEvaluator.PARAM_ASSOC_NAME).toString().toLowerCase());
}
return builder
.comparator(COMPARATOR_EQUALS)
.parameter(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_VALUE).toString())
.create();
}
private static SimpleCondition createHasTagCondition(final ActionCondition actionCondition) {
return builder()
.field(HasTagEvaluator.PARAM_TAG)
.comparator(COMPARATOR_EQUALS)
.parameter(actionCondition.getParameterValues().get(HasTagEvaluator.PARAM_TAG).toString())
.create();
}
private static SimpleCondition createHasVersionHistoryCondition(final ActionCondition actionCondition) {
return builder()
.field(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_PROPERTY).toString().toLowerCase())
.comparator(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_OPERATION).toString().toLowerCase())
.parameter(actionCondition.getParameterValues().get(ComparePropertyValueEvaluator.PARAM_VALUE).toString())
.create();
}
private static SimpleCondition createInCategoryCondition(final ActionCondition actionCondition) {
return builder()
.field(actionCondition.getParameterValues().get(InCategoryEvaluator.PARAM_CATEGORY_ASPECT).toString().toLowerCase())
.comparator(COMPARATOR_EQUALS)
.parameter(actionCondition.getParameterValues().get(InCategoryEvaluator.PARAM_CATEGORY_VALUE).toString())
.create();
}
private static SimpleCondition createIsSubtypeCondition(final ActionCondition actionCondition) {
return builder()
.field(IsSubTypeEvaluator.PARAM_TYPE)
.comparator(COMPARATOR_EQUALS)
.parameter(actionCondition.getParameterValues().get(IsSubTypeEvaluator.PARAM_TYPE).toString())
.create();
}
public static Builder builder()
{
return new Builder();

View File

@@ -35,7 +35,6 @@ import org.alfresco.rest.api.model.rules.RuleSetLink;
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.RelationshipResourceNotFoundException;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.Parameters;
@@ -43,9 +42,8 @@ import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.InitializingBean;
@RelationshipResource(name = "rule-set-links", entityResource = NodesEntityResource.class, title = "Rule set links")
public class NodeRuleSetLinksRelation implements InitializingBean, RelationshipResourceAction.Create<RuleSetLink>,
RelationshipResourceAction.Delete
@RelationshipResource(name = "rule-set-links", entityResource = NodesEntityResource.class, title = "Linking to a rule set")
public class NodeRuleSetLinksRelation implements InitializingBean, RelationshipResourceAction.Create<RuleSetLink>
{
private final RuleSets ruleSets;
@@ -69,24 +67,6 @@ public class NodeRuleSetLinksRelation implements InitializingBean, RelationshipR
.collect(Collectors.toList());
}
/**
* Remove link between a rule set and a folder for given rule set's and folder's node IDs.
* <p>
* - DELETE /nodes/{folderNodeId}/rule-set-links/{ruleSetId}
*
* @param folderNodeId - folder node ID
* @param ruleSetNodeId - rule set node ID (associated with folder node)
* @throws RelationshipResourceNotFoundException in case resource was not found
*/
@WebApiDescription(title = "Remove link between a rule set and a folder node",
description = "Submits a request to unlink a rule set from a folder",
successStatus = HttpServletResponse.SC_NO_CONTENT)
@Override
public void delete(String folderNodeId, String ruleSetNodeId, Parameters parameters)
{
ruleSets.unlinkRuleSet(folderNodeId, ruleSetNodeId);
}
public NodeRuleSetLinksRelation(RuleSets ruleSets)
{
this.ruleSets = ruleSets;

View File

@@ -65,7 +65,7 @@ public class NodeRulesRelation implements RelationshipResourceAction.Read<Rule>,
/**
* List folder rules for given folder node's and rule set's IDs as a page.
* <p>
*
* - GET /nodes/{folderNodeId}/rule-sets/{ruleSetId}/rules
*
* @param folderNodeId - entity resource context for this relationship
@@ -87,7 +87,7 @@ public class NodeRulesRelation implements RelationshipResourceAction.Read<Rule>,
/**
* Get single folder rule for given node's, rule set's and rule's IDs.
* <p>
*
* - GET /nodes/{folderNodeId}/rule-sets/{ruleSetId}/rules/{ruleId}
*
* @param folderNodeId - entity resource context for this relationship
@@ -158,7 +158,7 @@ public class NodeRulesRelation implements RelationshipResourceAction.Read<Rule>,
/**
* Delete single folder rule for given node's, rule set's and rule's IDs.
* <p>
*
* - DELETE /nodes/{folderNodeId}/rule-sets/{ruleSetId}/rules/{ruleId}
*
* @param folderNodeId - entity resource context for this relationship

View File

@@ -863,7 +863,6 @@
<bean id="ruleSetLoader" class="org.alfresco.rest.api.impl.rules.RuleSetLoader">
<property name="nodeService" ref="NodeService" />
<property name="ruleService" ref="RuleService" />
</bean>
<bean id="ruleSets" class="org.alfresco.rest.api.impl.rules.RuleSetsImpl">
@@ -887,7 +886,6 @@
<bean id="ruleLoader" class="org.alfresco.rest.api.impl.rules.RuleLoader">
<property name="ruleService" ref="RuleService" />
<property name="nodeValidator" ref="nodeValidator" />
<property name="ruleMapper" ref="ruleMapper"/>
</bean>
<bean class="org.alfresco.rest.api.nodes.NodeRuleSetsRelation">
@@ -905,11 +903,12 @@
</bean>
<bean id="rules" class="org.alfresco.rest.api.impl.rules.RulesImpl">
<property name="nodes" ref="Nodes" />
<property name="validator" ref="nodeValidator"/>
<property name="ruleService" ref="RuleService" />
<property name="ruleLoader" ref="ruleLoader"/>
<property name="actionParameterConverter" ref="actionParameterConverter"/>
<property name="actionPermissionValidator" ref="actionPermissionValidator"/>
<property name="ruleMapper" ref="ruleMapper"/>
</bean>
<bean id="Rules" class="org.springframework.aop.framework.ProxyFactoryBean">
@@ -945,25 +944,6 @@
<property name="ruleSettings" ref="RuleSettings" />
</bean>
<bean id="simpleConditionMapper" class="org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapper">
<constructor-arg name="namespaceService" ref="NamespaceService"/>
<constructor-arg name="nodes" ref="Nodes"/>
</bean>
<bean id="compositeConditionMapper" class="org.alfresco.rest.api.impl.mapper.rules.RestRuleCompositeConditionModelMapper">
<constructor-arg name="simpleConditionMapper" ref="simpleConditionMapper"/>
</bean>
<bean id="actionMapper" class="org.alfresco.rest.api.impl.mapper.rules.RestRuleActionModelMapper">
<constructor-arg name="parameterConverter" ref="actionParameterConverter"/>
</bean>
<bean id="ruleMapper" class="org.alfresco.rest.api.impl.mapper.rules.RestRuleModelMapper">
<constructor-arg name="compositeConditionMapper" ref="compositeConditionMapper"/>
<constructor-arg name="actionMapper" ref="actionMapper"/>
<constructor-arg name="nodes" ref="Nodes"/>
<constructor-arg name="actionParameterConverter" ref="actionParameterConverter"/>
</bean>
<bean id="publicapi.mimeTypePropertyLookup" class="org.alfresco.rest.api.lookups.MimeTypePropertyLookup">
<property name="serviceRegistry" ref="ServiceRegistry"/>
<property name="supported">

View File

@@ -309,7 +309,7 @@ function sortByName(a, b)
function findUsers(searchTerm, maxResults, results)
{
var personRefs = people.getPeople(searchTerm+ " [hint:useCQ]", maxResults, "lastName", true);
var personRefs = people.getPeople(searchTerm, maxResults, "lastName", true);
// create person object for each result
for each(var personRef in personRefs)

View File

@@ -55,14 +55,10 @@ public class PreferenceServiceTest extends BaseWebScriptTest
private PersonService personService;
private static final String USER_ONE = "PreferenceTestOne" + System.currentTimeMillis();
private static final String USER_TWO = "PreferenceTestTwo" + System.currentTimeMillis();
private static final String USER_BAD = "PreferenceTestBad" + System.currentTimeMillis();
private static final String URL = "/api/people/" + USER_ONE + "/preferences";
private static final String URL2 = "/api/people/" + USER_TWO + "/preferences";
private static final int NUM_THREADS = 3;
private static Integer THREADS_FINISHED = 0;
private static final String URL = "/api/people/" + USER_ONE + "/preferences";;
@Override
protected void setUp() throws Exception
@@ -79,7 +75,6 @@ public class PreferenceServiceTest extends BaseWebScriptTest
// Create users
createUser(USER_ONE);
createUser(USER_TWO);
createUser(USER_BAD);
// Do tests as user one
@@ -108,6 +103,7 @@ public class PreferenceServiceTest extends BaseWebScriptTest
{
super.tearDown();
this.authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName());
}
public void testPreferences() throws Exception
@@ -197,64 +193,6 @@ public class PreferenceServiceTest extends BaseWebScriptTest
sendRequest(new GetRequest("/api/people/noExistUser/preferences"), 404);
}
public void testPreferencesMNT21901() throws Exception
{
String[] body = {
"{\"org\":{\"alfresco\":{\"share\":{\"forum\":{\"summary\":{\"dashlet\":{\"component-1-5\":{\"topics\":\"mine\"}}}}}}}}",
"{\"org\":{\"alfresco\":{\"share\":{\"forum\":{\"summary\":{\"dashlet\":{\"component-2-5\":{\"topics\":\"mine\"}}}}}}}}",
"{\"org\":{\"alfresco\":{\"share\":{\"forum\":{\"summary\":{\"dashlet\":{\"component-2-5\":{\"history\":\"1\"}}}}}}}}"
};
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < NUM_THREADS; i++)
{
UpdatePreferencesThread t = new UpdatePreferencesThread(i, body[i]);
threads[i] = t;
t.start();
}
for (int i = 0; i < threads.length; i++)
{
threads[i].join();
}
authenticationComponent.setCurrentUser(USER_ONE);
if (THREADS_FINISHED != NUM_THREADS)
{
fail("An error has occurred when updating preferences in concurrency context");
}
}
private class UpdatePreferencesThread extends Thread
{
private String body;
UpdatePreferencesThread(int threadNum, String body)
{
super(UpdatePreferencesThread.class.getSimpleName() + "-" + threadNum);
this.body = body;
}
@Override
public void run()
{
authenticationComponent.setCurrentUser(USER_TWO);
try
{
Response resp = sendRequest(new PostRequest(URL2, body, "application/json"), 200);
assertEquals(0, resp.getContentLength());
THREADS_FINISHED++;
}
catch (Exception e)
{
// Intentionally empty
}
}
}
private JSONObject getPreferenceObj() throws JSONException
{
JSONObject jsonObject = new JSONObject();
@@ -270,4 +208,5 @@ public class PreferenceServiceTest extends BaseWebScriptTest
assertEquals(10, jsonObject.get("numberValue"));
assertEquals(BigDecimal.valueOf(3.142), jsonObject.get("numberValue2"));
}
}

View File

@@ -26,16 +26,13 @@
package org.alfresco.rest.api;
import org.alfresco.rest.api.impl.mapper.rules.RestRuleActionModelMapperTest;
import org.alfresco.rest.api.impl.mapper.rules.RestRuleCompositeConditionModelMapperTest;
import org.alfresco.rest.api.impl.mapper.rules.RestRuleModelMapperTest;
import org.alfresco.rest.api.impl.mapper.rules.RestRuleSimpleConditionModelMapperTest;
import org.alfresco.rest.api.impl.rules.ActionParameterConverterTest;
import org.alfresco.rest.api.impl.rules.ActionPermissionValidatorTest;
import org.alfresco.rest.api.impl.rules.NodeValidatorTest;
import org.alfresco.rest.api.impl.rules.RuleLoaderTest;
import org.alfresco.rest.api.impl.rules.RuleSetsImplTest;
import org.alfresco.rest.api.model.rules.ActionTest;
import org.alfresco.rest.api.model.rules.CompositeConditionTest;
import org.alfresco.rest.api.impl.rules.RulesImplTest;
import org.alfresco.rest.api.model.rules.RuleTest;
import org.alfresco.rest.api.model.rules.SimpleConditionTest;
import org.alfresco.rest.api.nodes.NodeRulesRelationTest;
import org.alfresco.service.Experimental;
import org.junit.runner.RunWith;
@@ -48,13 +45,10 @@ import org.junit.runners.Suite;
RulesImplTest.class,
RuleSetsImplTest.class,
NodeValidatorTest.class,
RuleLoaderTest.class,
ActionParameterConverterTest.class,
ActionPermissionValidatorTest.class,
RestRuleSimpleConditionModelMapperTest.class,
RestRuleCompositeConditionModelMapperTest.class,
RestRuleActionModelMapperTest.class,
RestRuleModelMapperTest.class
RuleTest.class,
ActionTest.class,
SimpleConditionTest.class,
CompositeConditionTest.class
})
public class RulesUnitTests
{

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