From 790e2263619d1a8d4787bf0bad79430dd0dc5275 Mon Sep 17 00:00:00 2001 From: Jelena Cule Date: Mon, 25 Sep 2017 11:11:10 +0100 Subject: [PATCH 01/15] Revert "Merge branch 'feature/RM-5416_REST_API_Tests_for_Folder_Classification_1' into 'master'" This reverts merge request !569 --- .../org/alfresco/rest/core/RMRestWrapper.java | 24 +-- .../alfresco/rest/core/RestAPIFactory.java | 10 +- .../java/org/alfresco/rest/v0/SearchAPI.java | 49 +++--- .../rm/community/base/BaseRMRestTest.java | 154 ++++-------------- 4 files changed, 69 insertions(+), 168 deletions(-) diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java index f61fe3c751..7a529e0633 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java @@ -24,7 +24,6 @@ * along with Alfresco. If not, see . * #L% */ - package org.alfresco.rest.core; import com.jayway.restassured.builder.RequestSpecBuilder; @@ -35,7 +34,6 @@ import org.alfresco.rest.model.RestHtmlResponse; import org.alfresco.rest.model.RestSiteModel; import org.alfresco.rest.model.RestSiteModelsCollection; import org.alfresco.rest.requests.coreAPI.RestCoreAPI; -import org.alfresco.rest.requests.search.SearchAPI; import org.alfresco.rest.rm.community.requests.gscore.GSCoreAPI; import org.alfresco.utility.model.StatusModel; import org.alfresco.utility.model.UserModel; @@ -147,24 +145,16 @@ public class RMRestWrapper return restWrapper.withCoreAPI(); } - /** Get the Alfresco Search API. */ - public SearchAPI withSearchAPI() - { - return restWrapper.withSearchAPI(); - } - /** - * You can handle the request sent to server by calling this method. If for example you want to sent multipart form - * data you can use: - * - *
-     * restClient.configureRequestSpec().addMultiPart("filedata", Utility.getResourceTestDataFile("restapi-resource"))
-     *             .addFormParam("renditions", "doclib").addFormParam("autoRename", true);
+     * You can handle the request sent to server by calling this method.
+     * If for example you want to sent multipart form data you can use: 
+     * restClient.configureRequestSpec()
+     *              .addMultiPart("filedata", Utility.getResourceTestDataFile("restapi-resource"))
+     *              .addFormParam("renditions", "doclib")
+     *              .addFormParam("autoRename", true);
      *
      * restClient.withCoreAPI().usingNode(ContentModel.my()).createNode();
-     * 
- * - * This will create the node using the multipart data defined. + *
This will create the node using the multipart data defined. */ public RequestSpecBuilder configureRequestSpec() { diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java index bdbabe6eb2..710413fa33 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java @@ -24,7 +24,6 @@ * along with Alfresco. If not, see . * #L% */ - package org.alfresco.rest.core; import static lombok.AccessLevel.PROTECTED; @@ -33,7 +32,6 @@ import javax.annotation.Resource; import org.alfresco.rest.requests.Node; import org.alfresco.rest.requests.coreAPI.RestCoreAPI; -import org.alfresco.rest.requests.search.SearchAPI; import org.alfresco.rest.rm.community.requests.gscore.GSCoreAPI; import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI; import org.alfresco.rest.rm.community.requests.gscore.api.FilesAPI; @@ -67,7 +65,7 @@ import lombok.Setter; public class RestAPIFactory { @Autowired - @Getter(value = PROTECTED) + @Getter (value = PROTECTED) private DataUser dataUser; @Resource(name = "RMRestWrapper") @@ -87,12 +85,6 @@ public class RestAPIFactory return getRmRestWrapper().withCoreAPI(); } - private SearchAPI getSearchAPI(UserModel userModel) - { - getRmRestWrapper().authenticateUser(userModel != null ? userModel : getDataUser().getAdminUser()); - return getRmRestWrapper().withSearchAPI(); - } - public Node getNodeAPI(RepoTestModel model) throws Exception { return getCoreAPI(null).usingNode(model); diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java index a465fc5c90..7bcfff7d48 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java @@ -24,7 +24,6 @@ * along with Alfresco. If not, see . * #L% */ - package org.alfresco.rest.v0; import java.text.MessageFormat; @@ -51,8 +50,7 @@ import org.springframework.stereotype.Component; public class SearchAPI extends BaseAPI { /** http client factory */ - @Autowired - private AlfrescoHttpClientFactory alfrescoHttpClientFactory; + @Autowired private AlfrescoHttpClientFactory alfrescoHttpClientFactory; /** faceted search API endpoint */ private static final String FACETED_SEARCH_ENDPOINT = "{0}alfresco/s/slingshot/rmsearch/faceted/rmsearch?{1}"; @@ -61,26 +59,28 @@ public class SearchAPI extends BaseAPI private static final String RM_SEARCH_ENDPOINT = "{0}alfresco/s/slingshot/rmsearch/{1}?{2}"; /** RM document search filters */ - private static final String RM_DEFAULT_RECORD_FILTERS = "records/true,undeclared/true,vital/false,folders/false,categories/false,frozen/false,cutoff/false"; + private static final String RM_DEFAULT_RECORD_FILTERS = + "records/true,undeclared/true,vital/false,folders/false,categories/false,frozen/false,cutoff/false"; /** * Perform search request on search endpoint as a user. *

* This method is applicable only to endpoints that support HTTP GET requests and return JSON body as response. - * * @param searchEndpoint * @param searchUser * @param searchPassword * @return search results as a {@link JSONObject}, please refer to API documentation for details */ - private JSONObject doSearch(String searchEndpoint, String searchUser, String searchPassword) + private JSONObject doSearch( + String searchEndpoint, + String searchUser, + String searchPassword) { - return facetedRequest(searchUser, searchPassword, null, searchEndpoint); + return facetedRequest(searchUser, searchPassword, null, searchEndpoint); } /** * Generic rm search. - * * @param username * @param password * @param site @@ -88,36 +88,45 @@ public class SearchAPI extends BaseAPI * @param filters * @return search results (see API reference for more details), null for any errors */ - public JSONObject rmSearch(String username, String password, String site, String query, String filters) + public JSONObject rmSearch( + String username, + String password, + String site, + String query, + String filters) { List searchParameters = new ArrayList(); searchParameters.add(new BasicNameValuePair("query", query)); searchParameters.add(new BasicNameValuePair("filters", filters)); - String requestURL = MessageFormat.format(RM_SEARCH_ENDPOINT, - alfrescoHttpClientFactory.getObject().getAlfrescoUrl(), (site != null) ? site : RM_SITE_ID, - URLEncodedUtils.format(searchParameters, "UTF-8")); + String requestURL = MessageFormat.format( + RM_SEARCH_ENDPOINT, + alfrescoHttpClientFactory.getObject().getAlfrescoUrl(), + (site != null) ? site : RM_SITE_ID, + URLEncodedUtils.format(searchParameters, "UTF-8")); return doSearch(requestURL, username, password); } /** - * Search as a user for records on site "rm" matching query, using SearchAPI.RM_DEFAULT_RECORD_FILTERS
+ * Search as a user for records on site "rm" matching query, using SearchAPI.RM_DEFAULT_RECORD_FILTERS + *
* If more fine-grained control of search parameters is required, use rmSearch() directly. - * * @param username * @param password * @param query * @return list of record names */ - public List searchForRecordsAsUser(String username, String password, String query) + public List searchForRecordsAsUser( + String username, + String password, + String query) { return getItemNames(rmSearch(username, password, "rm", query, RM_DEFAULT_RECORD_FILTERS)); } /** * Generic faceted search. - * * @param username * @param password * @param parameters @@ -130,7 +139,6 @@ public class SearchAPI extends BaseAPI /** * Execute faceted search for term. - * * @param searchUser * @param searchPassword * @param searchTerm @@ -138,12 +146,14 @@ public class SearchAPI extends BaseAPI */ public JSONObject facetedSearchForTerm(String searchUser, String searchPassword, String searchTerm) { - return facetedSearch(searchUser, searchPassword, Arrays.asList(new BasicNameValuePair("term", searchTerm))); + return facetedSearch( + searchUser, + searchPassword, + Arrays.asList(new BasicNameValuePair("term", searchTerm))); } /** * Helper method to search for documents as a user using faceted search. - * * @param username to search as * @param password for username * @param term search term @@ -156,7 +166,6 @@ public class SearchAPI extends BaseAPI /** * Helper method to extract list of names from search result. - * * @param searchResult * @return list of document or record names in search result * @throws RuntimeException for malformed search response diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java index 5dee146514..38d4b2959d 100644 --- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java +++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java @@ -24,7 +24,6 @@ * along with Alfresco. If not, see . * #L% */ - package org.alfresco.rest.rm.community.base; import static lombok.AccessLevel.PROTECTED; @@ -34,14 +33,11 @@ import static org.alfresco.rest.rm.community.base.TestData.RECORD_CATEGORY_TITLE import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.ASPECTS_COMPLETED_RECORD; -import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_CONTAINER_TYPE; import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE; -import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING; -import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_USER; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createRecordCategoryChildModel; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createRecordCategoryModel; import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createTempFile; @@ -55,7 +51,6 @@ import static org.testng.Assert.assertTrue; import java.util.List; -import org.alfresco.dataprep.ContentService; import org.alfresco.rest.RestTest; import org.alfresco.rest.core.RestAPIFactory; import org.alfresco.rest.rm.community.model.fileplan.FilePlan; @@ -70,12 +65,8 @@ import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainer; import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild; import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI; import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI; -import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI; import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI; -import org.alfresco.rest.v0.RMRolesAndActionsAPI; import org.alfresco.utility.data.DataUser; -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.springframework.http.HttpStatus; @@ -94,21 +85,13 @@ import lombok.Getter; public class BaseRMRestTest extends RestTest { @Autowired - @Getter(value = PROTECTED) + @Getter (value = PROTECTED) private RestAPIFactory restAPIFactory; @Autowired - @Getter(value = PROTECTED) + @Getter (value = PROTECTED) private DataUser dataUser; - @Autowired - @Getter(value = PROTECTED) - private ContentService contentService; - - @Autowired - @Getter(value = PROTECTED) - private RMRolesAndActionsAPI rmRolesAndActionsAPI; - /** * Asserts the given status code * @@ -133,22 +116,22 @@ public class BaseRMRestTest extends RestTest @DataProvider(name = "validRootContainers") public String[][] getValidRootContainers() throws Exception { - return new String[][] { - // an arbitrary record folder - { createCategoryFolderInFilePlan().getId(), RECORD_FOLDER_TYPE }, - // unfiled records root - { UNFILED_RECORDS_CONTAINER_ALIAS, UNFILED_CONTAINER_TYPE }, - // an arbitrary unfiled records folder - { createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, - "Unfiled Folder " + getRandomAlphanumeric(), UNFILED_RECORD_FOLDER_TYPE).getId(), - UNFILED_RECORD_FOLDER_TYPE } }; + return new String[][] + { + // an arbitrary record folder + { createCategoryFolderInFilePlan().getId(), RECORD_FOLDER_TYPE}, + // unfiled records root + { UNFILED_RECORDS_CONTAINER_ALIAS, UNFILED_CONTAINER_TYPE}, + // an arbitrary unfiled records folder + { createUnfiledContainerChild(UNFILED_RECORDS_CONTAINER_ALIAS, "Unfiled Folder " + getRandomAlphanumeric(), UNFILED_RECORD_FOLDER_TYPE).getId(), UNFILED_RECORD_FOLDER_TYPE } + }; } /** * @see org.alfresco.rest.RestTest#checkServerHealth() */ @Override - @BeforeClass(alwaysRun = true) + @BeforeClass (alwaysRun = true) public void checkServerHealth() throws Exception { // Create RM Site if not exist @@ -156,7 +139,8 @@ public class BaseRMRestTest extends RestTest } /** - * Helper method to create the RM Site via the POST request if the site doesn't exist + * Helper method to create the RM Site via the POST request + * if the site doesn't exist */ public void createRMSiteIfNotExists() throws Exception { @@ -220,12 +204,10 @@ public class BaseRMRestTest extends RestTest * @return The created category * @throws Exception on unsuccessful component creation */ - public RecordCategory createRootCategory(UserModel userModel, String categoryName, String categoryTitle) - throws Exception + public RecordCategory createRootCategory(UserModel userModel, String categoryName, String categoryTitle) throws Exception { RecordCategory recordCategoryModel = createRecordCategoryModel(categoryName, categoryTitle); - return getRestAPIFactory().getFilePlansAPI(userModel).createRootRecordCategory(recordCategoryModel, - FILE_PLAN_ALIAS); + return getRestAPIFactory().getFilePlansAPI(userModel).createRootRecordCategory(recordCategoryModel, FILE_PLAN_ALIAS); } /** @@ -238,12 +220,10 @@ public class BaseRMRestTest extends RestTest * @return The created {@link RecordCategoryChild} * @throws Exception {@link RecordCategoryAPI#createRecordCategoryChild(RecordCategoryChild, String)} */ - public RecordCategoryChild createRecordCategoryChild(UserModel user, String recordCategoryId, String name, - String type) throws Exception + public RecordCategoryChild createRecordCategoryChild(UserModel user, String recordCategoryId, String name, String type) throws Exception { RecordCategoryChild recordCategoryChildModel = createRecordCategoryChildModel(name, type); - return getRestAPIFactory().getRecordCategoryAPI(user).createRecordCategoryChild(recordCategoryChildModel, - recordCategoryId); + return getRestAPIFactory().getRecordCategoryAPI(user).createRecordCategoryChild(recordCategoryChildModel, recordCategoryId); } /** @@ -255,8 +235,7 @@ public class BaseRMRestTest extends RestTest * @return The created {@link RecordCategoryChild} * @throws Exception {@link RecordCategoryAPI#createRecordCategoryChild(RecordCategoryChild, String)} */ - public RecordCategoryChild createRecordCategoryChild(String recordCategoryId, String name, String type) - throws Exception + public RecordCategoryChild createRecordCategoryChild(String recordCategoryId, String name, String type) throws Exception { return createRecordCategoryChild(getAdminUser(), recordCategoryId, name, type); } @@ -299,8 +278,7 @@ public class BaseRMRestTest extends RestTest public RecordCategoryChild createFolder(UserModel user, String recordCategoryId, String name) throws Exception { RecordCategoryChild recordFolderModel = createRecordCategoryChildModel(name, RECORD_FOLDER_TYPE); - return getRestAPIFactory().getRecordCategoryAPI(user).createRecordCategoryChild(recordFolderModel, - recordCategoryId); + return getRestAPIFactory().getRecordCategoryAPI(user).createRecordCategoryChild(recordFolderModel, recordCategoryId); } /** @@ -319,18 +297,16 @@ public class BaseRMRestTest extends RestTest /** * Helper method to create child unfiled record folder * - * @param user The user under whose privileges this structure is going to be created + *@param user The user under whose privileges this structure is going to be created * @param parentId The id of the parent folder * @param nodeType The child type * @return The created folder * @throws Exception on unsuccessful component creation */ - public UnfiledContainerChild createUnfiledRecordsFolderChild(UserModel user, String parentId, String childName, - String nodeType) throws Exception + public UnfiledContainerChild createUnfiledRecordsFolderChild(UserModel user, String parentId, String childName, String nodeType) throws Exception { UnfiledContainerChild childModel = createUnfiledContainerChildModel(childName, nodeType); - UnfiledContainerChild child = getRestAPIFactory().getUnfiledRecordFoldersAPI(user) - .createUnfiledRecordFolderChild(childModel, parentId); + UnfiledContainerChild child = getRestAPIFactory().getUnfiledRecordFoldersAPI(user).createUnfiledRecordFolderChild(childModel, parentId); assertStatusCode(CREATED); return child; @@ -344,8 +320,7 @@ public class BaseRMRestTest extends RestTest * @return The created folder * @throws Exception on unsuccessful component creation */ - public UnfiledContainerChild createUnfiledRecordsFolderChild(String parentId, String childName, String nodeType) - throws Exception + public UnfiledContainerChild createUnfiledRecordsFolderChild(String parentId, String childName, String nodeType) throws Exception { return createUnfiledRecordsFolderChild(getAdminUser(), parentId, childName, nodeType); } @@ -360,16 +335,14 @@ public class BaseRMRestTest extends RestTest * @return The created chid * @throws Exception on unsuccessful child creation */ - public UnfiledContainerChild createUnfiledContainerChild(UserModel user, String parentId, String childName, - String nodeType) throws Exception + public UnfiledContainerChild createUnfiledContainerChild(UserModel user, String parentId, String childName, String nodeType) throws Exception { UnfiledContainerChild child = null; UnfiledContainerChild childModel = createUnfiledContainerChildModel(childName, nodeType); if (FilePlanComponentType.CONTENT_TYPE.equals(nodeType)) { - child = getRestAPIFactory().getUnfiledContainersAPI(user).uploadRecord(childModel, parentId, - createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME)); + child = getRestAPIFactory().getUnfiledContainersAPI(user).uploadRecord(childModel, parentId, createTempFile(ELECTRONIC_RECORD_NAME, ELECTRONIC_RECORD_NAME)); } else { @@ -389,8 +362,7 @@ public class BaseRMRestTest extends RestTest * @return The created chid * @throws Exception on unsuccessful child creation */ - public UnfiledContainerChild createUnfiledContainerChild(String parentId, String childName, String nodeType) - throws Exception + public UnfiledContainerChild createUnfiledContainerChild(String parentId, String childName, String nodeType) throws Exception { return createUnfiledContainerChild(getAdminUser(), parentId, childName, nodeType); } @@ -405,9 +377,11 @@ public class BaseRMRestTest extends RestTest protected RecordFolder closeFolder(String folderId) throws Exception { RecordFolder recordFolderModel = RecordFolder.builder() - .properties(RecordFolderProperties.builder().isClosed(true).build()).build(); - RecordFolder updateRecordFolder = getRestAPIFactory().getRecordFolderAPI().updateRecordFolder(recordFolderModel, - folderId); + .properties(RecordFolderProperties.builder() + .isClosed(true) + .build()) + .build(); + RecordFolder updateRecordFolder = getRestAPIFactory().getRecordFolderAPI().updateRecordFolder(recordFolderModel, folderId); assertStatusCode(OK); return updateRecordFolder; @@ -494,68 +468,4 @@ public class BaseRMRestTest extends RestTest { return getFilePlanAsUser(getAdminUser(), componentId); } - - /** - * Recursively delete a folder - * - * @param siteModel - * @param folder - */ - public void deleteFolder(SiteModel siteModel, FolderModel folder) - { - contentService.deleteTree(getAdminUser().getUsername(), getAdminUser().getPassword(), siteModel.getId(), - folder.getName()); - } - - /** - * Create an electronic record - * - * @param parentId the id of the parent - * @param name the name of the record - * @return the created record - * @throws Exception - */ - public Record createElectronicRecord(String parentId, String name) throws Exception - { - RecordFolderAPI recordFolderAPI = restAPIFactory.getRecordFolderAPI(); - Record recordModel = Record.builder().name(name).nodeType(CONTENT_TYPE).build(); - return recordFolderAPI.createRecord(recordModel, parentId); - } - - /** - * Delete a record folder - * - * @param recordFolderId the id of the record folder to delete - */ - public void deleteRecordFolder(String recordFolderId) - { - RecordFolderAPI recordFolderAPI = restAPIFactory.getRecordFolderAPI(); - recordFolderAPI.deleteRecordFolder(recordFolderId); - } - - /** - * Delete a record category - * - * @param recordCategoryId the id of the record category to delete - */ - public void deleteRecordCategory(String recordCategoryId) - { - RecordCategoryAPI recordCategoryAPI = restAPIFactory.getRecordCategoryAPI(); - recordCategoryAPI.deleteRecordCategory(recordCategoryId); - } - - /** - * Assign filling permission on a record category and give the user RM_USER role - * - * @param user the user to assign the permission to - * @param categoryId the id of the category to assign permissions for - * @throws Exception - */ - public void assignFillingPermissionsOnCategory(UserModel user, String categoryId) throws Exception - { - getRestAPIFactory().getRMUserAPI().addUserPermission(categoryId, user, PERMISSION_FILING); - rmRolesAndActionsAPI.assignUserToRole(dataUser.getAdminUser().getUsername(), - dataUser.getAdminUser().getPassword(), user.getUsername(), ROLE_RM_USER); - } - } From 9e38a17a0fd57c2e636aa890d2d2b67857d0eb51 Mon Sep 17 00:00:00 2001 From: jcule Date: Mon, 25 Sep 2017 18:31:43 +0100 Subject: [PATCH 02/15] RM-5416: REST API Tests for Folder Classification --- .../org/alfresco/rest/core/RMRestWrapper.java | 7 ++ .../alfresco/rest/core/RestAPIFactory.java | 7 ++ .../rm/community/base/BaseRMRestTest.java | 79 +++++++++++++++++++ 3 files changed, 93 insertions(+) diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java index 7a529e0633..acc44a6114 100644 --- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RMRestWrapper.java @@ -34,6 +34,7 @@ import org.alfresco.rest.model.RestHtmlResponse; import org.alfresco.rest.model.RestSiteModel; import org.alfresco.rest.model.RestSiteModelsCollection; import org.alfresco.rest.requests.coreAPI.RestCoreAPI; +import org.alfresco.rest.requests.search.SearchAPI; import org.alfresco.rest.rm.community.requests.gscore.GSCoreAPI; import org.alfresco.utility.model.StatusModel; import org.alfresco.utility.model.UserModel; @@ -145,6 +146,12 @@ public class RMRestWrapper return restWrapper.withCoreAPI(); } + /** Get the Alfresco Search API. */ + public SearchAPI withSearchAPI() + { + return restWrapper.withSearchAPI(); + } + /** * You can handle the request sent to server by calling this method. * If for example you want to sent multipart form data you can use:

diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java
index 710413fa33..10b71423d9 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/RestAPIFactory.java
@@ -32,6 +32,7 @@ import javax.annotation.Resource;
 
 import org.alfresco.rest.requests.Node;
 import org.alfresco.rest.requests.coreAPI.RestCoreAPI;
+import org.alfresco.rest.requests.search.SearchAPI;
 import org.alfresco.rest.rm.community.requests.gscore.GSCoreAPI;
 import org.alfresco.rest.rm.community.requests.gscore.api.FilePlanAPI;
 import org.alfresco.rest.rm.community.requests.gscore.api.FilesAPI;
@@ -84,6 +85,12 @@ public class RestAPIFactory
         getRmRestWrapper().authenticateUser(userModel != null ? userModel : getDataUser().getAdminUser());
         return getRmRestWrapper().withCoreAPI();
     }
+    
+    private SearchAPI getSearchAPI(UserModel userModel)
+    {
+        getRmRestWrapper().authenticateUser(userModel != null ? userModel : getDataUser().getAdminUser());
+        return getRmRestWrapper().withSearchAPI();
+    }   
 
     public Node getNodeAPI(RepoTestModel model) throws Exception
     {
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
index 38d4b2959d..3d05133a04 100644
--- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
@@ -33,11 +33,14 @@ import static org.alfresco.rest.rm.community.base.TestData.RECORD_CATEGORY_TITLE
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAspects.ASPECTS_COMPLETED_RECORD;
+import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_TYPE;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_CONTAINER_TYPE;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE;
+import static org.alfresco.rest.rm.community.model.user.UserPermissions.PERMISSION_FILING;
+import static org.alfresco.rest.rm.community.model.user.UserRoles.ROLE_RM_USER;
 import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createRecordCategoryChildModel;
 import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createRecordCategoryModel;
 import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createTempFile;
@@ -51,6 +54,7 @@ import static org.testng.Assert.assertTrue;
 
 import java.util.List;
 
+import org.alfresco.dataprep.ContentService;
 import org.alfresco.rest.RestTest;
 import org.alfresco.rest.core.RestAPIFactory;
 import org.alfresco.rest.rm.community.model.fileplan.FilePlan;
@@ -65,8 +69,12 @@ import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainer;
 import org.alfresco.rest.rm.community.model.unfiledcontainer.UnfiledContainerChild;
 import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI;
 import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI;
+import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI;
 import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI;
+import org.alfresco.rest.v0.RMRolesAndActionsAPI;
 import org.alfresco.utility.data.DataUser;
+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.springframework.http.HttpStatus;
@@ -92,6 +100,14 @@ public class BaseRMRestTest extends RestTest
     @Getter (value = PROTECTED)
     private DataUser dataUser;
 
+    @Autowired
+    @Getter(value = PROTECTED)
+    private ContentService contentService;
+
+    @Autowired
+    @Getter(value = PROTECTED)
+    private RMRolesAndActionsAPI rmRolesAndActionsAPI;
+    
     /**
      * Asserts the given status code
      *
@@ -468,4 +484,67 @@ public class BaseRMRestTest extends RestTest
     {
         return getFilePlanAsUser(getAdminUser(), componentId);
     }
+    
+    /**
+     * Recursively delete a folder
+     *
+     * @param siteModel
+     * @param folder
+     */
+    public void deleteFolder(SiteModel siteModel, FolderModel folder)
+    {
+        contentService.deleteTree(getAdminUser().getUsername(), getAdminUser().getPassword(), siteModel.getId(),
+                    folder.getName());
+    }
+
+    /**
+     * Create an electronic record
+     *
+     * @param parentId the id of the parent
+     * @param name the name of the record
+     * @return the created record
+     * @throws Exception
+     */
+    public Record createElectronicRecord(String parentId, String name) throws Exception
+    {
+        RecordFolderAPI recordFolderAPI = restAPIFactory.getRecordFolderAPI();
+        Record recordModel = Record.builder().name(name).nodeType(CONTENT_TYPE).build();
+        return recordFolderAPI.createRecord(recordModel, parentId);
+    }
+
+    /**
+     * Delete a record folder
+     *
+     * @param recordFolderId the id of the record folder to delete
+     */
+    public void deleteRecordFolder(String recordFolderId)
+    {
+        RecordFolderAPI recordFolderAPI = restAPIFactory.getRecordFolderAPI();
+        recordFolderAPI.deleteRecordFolder(recordFolderId);
+    }
+
+    /**
+     * Delete a record category
+     *
+     * @param recordCategoryId the id of the record category to delete
+     */
+    public void deleteRecordCategory(String recordCategoryId)
+    {
+        RecordCategoryAPI recordCategoryAPI = restAPIFactory.getRecordCategoryAPI();
+        recordCategoryAPI.deleteRecordCategory(recordCategoryId);
+    }
+
+    /**
+     * Assign filling permission on a record category and give the user RM_USER role
+     *
+     * @param user the user to assign the permission to
+     * @param categoryId the id of the category to assign permissions for
+     * @throws Exception
+     */
+    public void assignFillingPermissionsOnCategory(UserModel user, String categoryId) throws Exception
+    {
+        getRestAPIFactory().getRMUserAPI().addUserPermission(categoryId, user, PERMISSION_FILING);
+        rmRolesAndActionsAPI.assignUserToRole(dataUser.getAdminUser().getUsername(),
+                    dataUser.getAdminUser().getPassword(), user.getUsername(), ROLE_RM_USER);
+    }
 }

From 02219969b2b1cada63effe291e5539230a27f702 Mon Sep 17 00:00:00 2001
From: Oana Nechiforescu 
Date: Tue, 26 Sep 2017 14:50:28 +0300
Subject: [PATCH 03/15] First commit just the test and new API empty trashcan
 method.

---
 .../org/alfresco/rest/v0/UserTrashcanAPI.java | 56 +++++++++++++++++++
 1 file changed, 56 insertions(+)
 create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/UserTrashcanAPI.java

diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/UserTrashcanAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/UserTrashcanAPI.java
new file mode 100644
index 0000000000..7179ea71c2
--- /dev/null
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/UserTrashcanAPI.java
@@ -0,0 +1,56 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.v0;
+
+import org.alfresco.rest.core.v0.BaseAPI;
+import org.springframework.stereotype.Component;
+
+/**
+ * Helper methods for performing actions on user trashcan
+ *
+ * @author Oana Nechiforescu
+ * @since 2.6
+ */
+@Component
+public class UserTrashcanAPI extends BaseAPI
+{
+    private static final String EMPTY_TRASHCAN = "{0}archive/workspace/SpacesStore";
+
+    /**
+     * Clears the trashcan for the current user
+     *
+     * @param username the username
+     * @param password the password
+     *
+     * @return true if the request succeeded, false if not
+     */
+    public boolean emptyTrashcan(String username, String password)
+    {
+        return doDeleteRequest(username, password, EMPTY_TRASHCAN) != null;
+    }
+
+}

From b19b5e8be1866a2a9c359a22f3e0e2d92e1a73b5 Mon Sep 17 00:00:00 2001
From: Oana Nechiforescu 
Date: Tue, 26 Sep 2017 14:51:13 +0300
Subject: [PATCH 04/15] Commit 2, test changes to use the new API method.

---
 .../src/main/java/org/alfresco/rest/v0/SearchAPI.java          | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java
index 7bcfff7d48..176b51d2d8 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java
@@ -50,7 +50,8 @@ import org.springframework.stereotype.Component;
 public class SearchAPI extends BaseAPI
 {
     /** http client factory */
-    @Autowired private AlfrescoHttpClientFactory alfrescoHttpClientFactory;
+    @Autowired
+    private AlfrescoHttpClientFactory alfrescoHttpClientFactory;
 
     /** faceted search API endpoint */
     private static final String FACETED_SEARCH_ENDPOINT = "{0}alfresco/s/slingshot/rmsearch/faceted/rmsearch?{1}";

From 052f36b0622d3a83f36352a34d57e262c7a28bc8 Mon Sep 17 00:00:00 2001
From: jcule 
Date: Wed, 27 Sep 2017 17:43:57 +0100
Subject: [PATCH 05/15] RM-5416: REST API Tests for Folder Classification -
 various improvements

---
 .../alfresco/rest/rm/community/base/BaseRMRestTest.java   | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
index 3d05133a04..0216f3ade2 100644
--- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
@@ -541,10 +541,10 @@ public class BaseRMRestTest extends RestTest
      * @param categoryId the id of the category to assign permissions for
      * @throws Exception
      */
-    public void assignFillingPermissionsOnCategory(UserModel user, String categoryId) throws Exception
+    public void assignFillingPermissionsOnCategory(UserModel user, String categoryId, String userPermission, String userRole) throws Exception
     {
-        getRestAPIFactory().getRMUserAPI().addUserPermission(categoryId, user, PERMISSION_FILING);
-        rmRolesAndActionsAPI.assignUserToRole(dataUser.getAdminUser().getUsername(),
-                    dataUser.getAdminUser().getPassword(), user.getUsername(), ROLE_RM_USER);
+        getRestAPIFactory().getRMUserAPI().addUserPermission(categoryId, user, userPermission);
+        rmRolesAndActionsAPI.assignUserToRole(getAdminUser().getUsername(),
+                    getAdminUser().getPassword(), user.getUsername(), userRole);
     }
 }

From f639b598313ccbe8ecfa3750e0aaab054cc6f714 Mon Sep 17 00:00:00 2001
From: Ana Bozianu 
Date: Thu, 28 Sep 2017 09:17:56 +0300
Subject: [PATCH 06/15] RM-5655 - moved the query in community

---
 .../query/rm-common-SqlMap.xml                |  20 +-
 .../query/rm-query-context.xml                |   2 +
 ...ChildrenWithPropertyValuesQueryParams.java |  73 +++++
 .../query/RecordsManagementQueryDAO.java      |  16 ++
 .../query/RecordsManagementQueryDAOImpl.java  |  55 +++-
 .../RecordsManagementQueryDAOImplTest.java    | 252 +++++++++++++++++-
 6 files changed, 415 insertions(+), 3 deletions(-)
 create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java

diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml
index e3f1c1a89d..d3b63f4467 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml
@@ -19,5 +19,23 @@
             prop.string_value = ?
             
     
-    
+
+   
+   
+
 
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml
index 59f1687234..dd5949efb9 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml
@@ -19,6 +19,8 @@
 	
 		
 		
+      
+      
 	
 
 
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java
new file mode 100644
index 0000000000..b0faa993e4
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java
@@ -0,0 +1,73 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.module.org_alfresco_module_rm.query;
+
+import java.util.Collection;
+
+/**
+ * Select parameter for select_CountChildrenWithPropertyValues.
+ *
+ * @author Ana Manolache
+ * @since 2.6
+ */
+public class ChildrenWithPropertyValuesQueryParams
+{
+    private Long parentId;
+    private Long propertyQnameId;
+    private Collection propertyValues;
+
+    public Long getParentId()
+    {
+        return parentId;
+    }
+
+    public void setParentId(Long parentId)
+    {
+        this.parentId = parentId;
+    }
+
+    public Long getPropertyQnameId()
+    {
+        return propertyQnameId;
+    }
+
+    public void setPropertyQnameId(Long propertyQnameId)
+    {
+        this.propertyQnameId = propertyQnameId;
+    }
+
+    public Collection getPropertyValues()
+    {
+        return propertyValues;
+    }
+
+    public void setPropertyValues(Collection propertyValues)
+    {
+        this.propertyValues = propertyValues;
+    }
+}
+
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java
index fec9ad5353..afd031cd39 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java
@@ -27,6 +27,11 @@
 
 package org.alfresco.module.org_alfresco_module_rm.query;
 
+import java.util.Collection;
+
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+
 /**
  * Records management query DAO
  * 
@@ -46,4 +51,15 @@ public interface RecordsManagementQueryDAO
      * @return int  count
      */
     int getCountRmaIdentifier(String identifierValue);
+
+    /**
+     * Returns whether a given node contains children with one of the given values for the given property
+     *
+     * @param parent         the parent to evaluate
+     * @param property       the QName of the property to evaluate
+     * @param propertyValues the list of values to look for
+     * @return true if there is at least one child with one of the values from the list set on the given property
+     * false otherwise
+     */
+    public boolean hasChildrenWithPropertyValues(NodeRef parent, QName property, Collection propertyValues);
 }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java
index 82744e7815..f62712d6ab 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java
@@ -27,11 +27,16 @@
 
 package org.alfresco.module.org_alfresco_module_rm.query;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.repo.domain.node.NodeDAO;
 import org.alfresco.repo.domain.qname.QNameDAO;
+import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.service.cmr.repository.NodeRef;
 import org.alfresco.service.namespace.QName;
 import org.alfresco.util.Pair;
 import org.mybatis.spring.SqlSessionTemplate;
@@ -45,12 +50,15 @@ import org.mybatis.spring.SqlSessionTemplate;
 public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, RecordsManagementModel
 {
     private static final String COUNT_IDENTIFIER = "alfresco.query.rm.select_CountRMIndentifier";
+    private static final String COUNT_CHILDREN_WITH_PROPERTY_VALUES = "select_CountChildrenWithPropertyValues";
     
     /** SQL session template */
     protected SqlSessionTemplate template;
     
     /** QName DAO */
     protected QNameDAO qnameDAO;
+    protected NodeDAO nodeDAO;
+    protected TenantService tenantService;
     
     /**
      * @param sqlSessionTemplate    SQL session template
@@ -67,7 +75,17 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO,
     {
         this.qnameDAO = qnameDAO;
     }
-    
+
+    public void setNodeDAO(NodeDAO nodeDAO)
+    {
+        this.nodeDAO = nodeDAO;
+    }
+
+    public void setTenantService(TenantService tenantService)
+    {
+        this.tenantService = tenantService;
+    }
+
     /**
      * @see org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO#getCountRmaIdentifier(java.lang.String)
      */
@@ -97,4 +115,39 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO,
         return result;
     }
 
+    @Override
+    public boolean hasChildrenWithPropertyValues(NodeRef parent, QName property, Collection propertyValues)
+    {
+        if(propertyValues.isEmpty())
+        {
+            return false;
+        }
+
+        ChildrenWithPropertyValuesQueryParams queryParams = new ChildrenWithPropertyValuesQueryParams();
+
+        // Set the parent node id
+        Pair nodePair = nodeDAO.getNodePair(tenantService.getName(parent));
+        if (nodePair == null)
+        {
+            throw new InvalidNodeRefException("The parent node does not exist.", parent);
+        }
+        Long parentNodeId = nodePair.getFirst();
+        queryParams.setParentId(parentNodeId);
+
+        // Set the property qname id
+        Pair pair = qnameDAO.getQName(property);
+        if (pair == null)
+        {
+            return false;
+        }
+        queryParams.setPropertyQnameId(pair.getFirst());
+
+
+        // Set the property values
+        queryParams.setPropertyValues(propertyValues);
+
+        // Perform the query
+        Long count = template.selectOne(COUNT_CHILDREN_WITH_PROPERTY_VALUES, queryParams);
+        return count > 0;
+    }
 }
diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java
index 5f29c12f7a..49a1fbe101 100644
--- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java
+++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java
@@ -27,10 +27,17 @@
 
 package org.alfresco.module.org_alfresco_module_rm.test.legacy.service;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.alfresco.model.ContentModel;
 import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
 import org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO;
 import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
 import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.GUID;
 
 /**
  * Records Management Query DAO
@@ -86,5 +93,248 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements
         });
     }
 
-  
+    /**
+     * Given a folder containing 3 files with the descriptions set
+     * When I check if the folder contains children having the description of file2 or file2
+     * Then the answer is positive
+     */
+    @org.junit.Test
+    public void testHasChildrenWithPropertyValues_someChildrenWithValues() throws Exception
+    {
+        doBehaviourDrivenTest(new BehaviourDrivenTest()
+        {
+            NodeRef parentFolder;
+            NodeRef file1;
+            NodeRef file2;
+            NodeRef file3;
+            String propValue1 = "descr1"; // set on file1
+            String propValue2 = "descr2"; // set on file2
+            String propValue3 = "descr3"; // set on file3
+            String propValue4 = "descr4"; // not set on any file
+            Boolean result;
+
+            @Override
+            public void given() throws Exception
+            {
+                setupCollaborationSiteTestDataImpl();
+                parentFolder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file1 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file2 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file3 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+
+                nodeService.setProperty(file1, PROP_DESCRIPTION, propValue1);
+                nodeService.setProperty(file2, PROP_DESCRIPTION, propValue2);
+                nodeService.setProperty(file3, PROP_DESCRIPTION, propValue3);
+            }
+
+            @Override
+            public void when() throws Exception
+            {
+                result = queryDAO.hasChildrenWithPropertyValues(parentFolder, PROP_DESCRIPTION, Arrays.asList(propValue1, propValue2, propValue4));
+            }
+
+            @Override
+            public void then() throws Exception
+            {
+                assertTrue(result);
+            }
+
+            @Override
+            public void after() throws Exception
+            {
+                if (parentFolder != null && nodeService.exists(parentFolder))
+                {
+                    nodeService.deleteNode(parentFolder);
+                }
+            }
+        });
+    }
+
+    /**
+     * Given a folder containing 3 files with the descriptions unset
+     * When I check if the folder contains children having certain descriptions
+     * Then the answer is negative
+     */
+    @org.junit.Test
+    public void testHasChildrenWithPropertyValues_propertyNotSetOnChildren() throws Exception
+    {
+        doBehaviourDrivenTest(new BehaviourDrivenTest()
+        {
+            NodeRef parentFolder;
+            NodeRef file1;
+            NodeRef file2;
+            NodeRef file3;
+            Boolean result;
+
+            @Override
+            public void given() throws Exception
+            {
+                setupCollaborationSiteTestDataImpl();
+                parentFolder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file1 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file2 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file3 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+            }
+
+            @Override
+            public void when() throws Exception
+            {
+                result = queryDAO.hasChildrenWithPropertyValues(parentFolder, PROP_DESCRIPTION, Arrays.asList("descr1", "descr2", "descr3"));
+            }
+
+            @Override
+            public void then() throws Exception
+            {
+                assertFalse(result);
+            }
+
+            @Override
+            public void after() throws Exception
+            {
+                if (parentFolder != null && nodeService.exists(parentFolder))
+                {
+                    nodeService.deleteNode(parentFolder);
+                }
+            }
+        });
+    }
+
+    /**
+     * Given a folder with no children but the property set on itself
+     * When I check if the folder contains children having certain descriptions
+     * Then the answer is negative
+     */
+    @org.junit.Test
+    public void testHasChildrenWithPropertyValues_noChildren() throws Exception
+    {
+        doBehaviourDrivenTest(new BehaviourDrivenTest()
+        {
+            NodeRef folder;
+            String propValue = "descr";
+            Boolean result;
+
+            @Override
+            public void given() throws Exception
+            {
+                setupCollaborationSiteTestDataImpl();
+                folder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                nodeService.setProperty(folder, PROP_DESCRIPTION, propValue);
+            }
+
+            @Override
+            public void when() throws Exception
+            {
+                result = queryDAO.hasChildrenWithPropertyValues(folder, PROP_DESCRIPTION, Arrays.asList("descr"));
+            }
+
+            @Override
+            public void then() throws Exception
+            {
+                assertFalse(result);
+            }
+
+            @Override
+            public void after() throws Exception
+            {
+                if (folder != null && nodeService.exists(folder))
+                {
+                    nodeService.deleteNode(folder);
+                }
+            }
+        });
+    }
+
+    /**
+     * Given a folder with children and an unused property
+     * When I check if the folder contains children having the unused property
+     * Then the answer is negative
+     */
+    @org.junit.Test
+    public void testHasChildrenWithPropertyValues_propertyNotUsed() throws Exception
+    {
+        doBehaviourDrivenTest(new BehaviourDrivenTest()
+        {
+            NodeRef parentFolder;
+            QName property;
+            Boolean result;
+
+            @Override
+            public void given() throws Exception
+            {
+                setupCollaborationSiteTestDataImpl();
+                parentFolder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                property = QName.createQName(URI, "customProp-" + GUID.generate());
+            }
+
+            @Override
+            public void when() throws Exception
+            {
+                result = queryDAO.hasChildrenWithPropertyValues(folder, property, Arrays.asList("descr"));
+            }
+
+            @Override
+            public void then() throws Exception
+            {
+                assertFalse(result);
+            }
+
+            @Override
+            public void after() throws Exception
+            {
+                if (folder != null && nodeService.exists(folder))
+                {
+                    nodeService.deleteNode(folder);
+                }
+            }
+        });
+    }
+
+    /**
+     * Given any folder and any property
+     * When I pass an empty array to the hasChildrenWithPropertyValues method
+     * Then the answer is negative
+     */
+    @org.junit.Test
+    public void testHasChildrenWithPropertyValues_emptyArray() throws Exception
+    {
+        doBehaviourDrivenTest(new BehaviourDrivenTest()
+        {
+            NodeRef parentFolder;
+            NodeRef file1;
+            Boolean result;
+
+            @Override
+            public void given() throws Exception
+            {
+                setupCollaborationSiteTestDataImpl();
+                parentFolder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file1 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+
+                nodeService.setProperty(file1, PROP_DESCRIPTION, "descr1");
+            }
+
+            @Override
+            public void when() throws Exception
+            {
+                result = queryDAO.hasChildrenWithPropertyValues(folder, PROP_DESCRIPTION, new ArrayList());
+            }
+
+            @Override
+            public void then() throws Exception
+            {
+                assertFalse(result);
+            }
+
+            @Override
+            public void after() throws Exception
+            {
+                if (folder != null && nodeService.exists(folder))
+                {
+                    nodeService.deleteNode(folder);
+                }
+            }
+        });
+    }
 }

From 6b54bd082f7f0cbb3c26ada31f66b84d57909889 Mon Sep 17 00:00:00 2001
From: Rodica Sutu 
Date: Mon, 25 Sep 2017 22:45:32 +0300
Subject: [PATCH 07/15] create relationships with v0 API requests

---
 .../org/alfresco/rest/core/v0/BaseAPI.java    |  27 +++-
 .../rest/v0/CustomDefinitionsAPI.java         | 137 ++++++++++++++++++
 2 files changed, 163 insertions(+), 1 deletion(-)
 create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java

diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
index 9e6e90063e..19a5db90ac 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
@@ -87,7 +87,7 @@ public abstract class BaseAPI
     @Autowired
     private ContentService contentService;
 
-    private static final String NODE_REF_WORKSPACE_SPACES_STORE = "workspace://SpacesStore/";
+    protected static final String NODE_REF_WORKSPACE_SPACES_STORE = "workspace://SpacesStore/";
     private static final String FILE_PLAN_PATH = "/Sites/rm/documentLibrary";
 
     /**
@@ -640,4 +640,29 @@ public abstract class BaseAPI
     {
         return FILE_PLAN_PATH;
     }
+
+    /**
+     * Used to set RM items properties
+     * including records, categories and folders
+     */
+    public enum CUSTOM_DEFINITIONS
+    {
+        ATTACHMENT("Attachment"),
+        MESSAGE("Message"),
+        NEXT_VERSION("Next Version"),
+        RENDITION("Rendition");
+        String definition;
+
+        private CUSTOM_DEFINITIONS(String definition)
+        {
+            this.definition = definition;
+        }
+
+        public String getDefinition()
+        {
+            return definition;
+        }
+
+    }
+
 }
diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
new file mode 100644
index 0000000000..6a29ae5b09
--- /dev/null
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
@@ -0,0 +1,137 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.v0;
+
+import java.text.MessageFormat;
+
+import org.alfresco.rest.core.v0.BaseAPI;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * Methods to make API requests using v0 API on Records Management Custom Model Reference Definitions
+ *
+ * @author Rodica Sutu
+ * @since 2.6
+ */
+@Component
+public class CustomDefinitionsAPI extends BaseAPI
+{
+    /**
+     * custom references endpoint
+     */
+    private static final String CUSTOM_REFERENCE_API_ENDPOINT = "{0}rma/admin/customreferencedefinitions";
+
+    /**
+     * create reference endpoint
+     */
+    private static final String CREATE_RELATIONSHIP_API_ENDPOINT = "{0}node/{1}/customreferences";
+
+    // logger
+    private static final Logger LOGGER = LoggerFactory.getLogger(CustomDefinitionsAPI.class);
+
+    /**
+     * Helper method to get custom references definitions
+     *
+     * @param adminUser        user with administrative privileges
+     * @param adminPassword    password for adminUser
+     * @param customDefinition custom reference definition name
+     */
+
+    public String getCustomReferencesId(String adminUser, String adminPassword, String customDefinition)
+    {
+
+        JSONObject getResponse = doGetRequest(adminUser, adminPassword, CUSTOM_REFERENCE_API_ENDPOINT);
+        if (getResponse != null)
+            try
+            {
+                JSONArray customDefinitions = getResponse.getJSONObject("data").getJSONArray("customReferences");
+                for (int i = 0; i < customDefinitions.length(); i++)
+                {
+                    JSONObject item = customDefinitions.getJSONObject(i);
+                    if (customDefinition.equalsIgnoreCase(item.has("source") ? item.getString("source") : null) ||
+                            customDefinition.equalsIgnoreCase(item.has("target") ? item.getString("target") : null) ||
+                            customDefinition.equalsIgnoreCase(item.has("label") ? item.getString("label") : null)
+                            )
+                    {
+                        return item.getString("refId");
+                    }
+                }
+
+            }
+            catch (JSONException error)
+            {
+                LOGGER.error("Unable to get the refId for the custom reference definition " + customDefinition);
+            }
+        return null;
+    }
+
+    /**
+     * Helper method to add custom reference instance to the specified record node
+     *
+     * @param adminUser     user with administrative privileges
+     * @param adminPassword password for adminUser
+     * @param recordNodeIdFrom node ref to set a custom reference
+     * @param recordNodeIdto        node ref of the to record
+     * @return true if creating relationship was successful,
+     * falseotherwise
+     */
+    public boolean createRelationship(
+            String adminUser,
+            String adminPassword,
+            String recordNodeIdFrom,
+            String recordNodeIdto,
+            CUSTOM_DEFINITIONS relationshipType)
+    {
+        try
+        {
+            //create the request body
+            JSONObject requestParams = new JSONObject();
+            requestParams.put("recordNodeIdto", NODE_REF_WORKSPACE_SPACES_STORE + recordNodeIdto);
+            requestParams.put("refId", getCustomReferencesId(adminUser, adminPassword, relationshipType
+                    .getDefinition()));
+            //send the API request to create the relationship
+            JSONObject setRelationshipStatus = doPostRequest(adminUser, adminPassword, requestParams,
+                    MessageFormat.format(CREATE_RELATIONSHIP_API_ENDPOINT, "{0}", NODE_PREFIX + recordNodeIdFrom));
+            //check the response
+            if (setRelationshipStatus != null)
+            {
+                return setRelationshipStatus.getBoolean("success");
+            }
+        }
+        catch (JSONException error)
+        {
+            LOGGER.error("Unable to extract response parameter", error);
+        }
+        return false;
+    }
+
+}

From da50858cfc8a38ef30d4d588c203d5dc6637b33d Mon Sep 17 00:00:00 2001
From: Rodica Sutu 
Date: Tue, 26 Sep 2017 09:51:15 +0300
Subject: [PATCH 08/15] fix the java docs

---
 .../src/main/java/org/alfresco/rest/core/v0/BaseAPI.java     | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
index 19a5db90ac..125fba650f 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
@@ -642,8 +642,7 @@ public abstract class BaseAPI
     }
 
     /**
-     * Used to set RM items properties
-     * including records, categories and folders
+     * List of existing records management custom references.
      */
     public enum CUSTOM_DEFINITIONS
     {
@@ -662,7 +661,5 @@ public abstract class BaseAPI
         {
             return definition;
         }
-
     }
-
 }

From 30959017ee4cd644267348ed7354b398b7e53024 Mon Sep 17 00:00:00 2001
From: Rodica Sutu 
Date: Tue, 26 Sep 2017 16:41:16 +0300
Subject: [PATCH 09/15] fix the request body sent when creating relationships

---
 .../main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
index 6a29ae5b09..257ac05bb7 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
@@ -115,7 +115,7 @@ public class CustomDefinitionsAPI extends BaseAPI
         {
             //create the request body
             JSONObject requestParams = new JSONObject();
-            requestParams.put("recordNodeIdto", NODE_REF_WORKSPACE_SPACES_STORE + recordNodeIdto);
+            requestParams.put("toNode", NODE_REF_WORKSPACE_SPACES_STORE + recordNodeIdto);
             requestParams.put("refId", getCustomReferencesId(adminUser, adminPassword, relationshipType
                     .getDefinition()));
             //send the API request to create the relationship

From 9e5f53bd09a76df1c3ffa9b3e0ea194cf3aafead Mon Sep 17 00:00:00 2001
From: Rodica Sutu 
Date: Wed, 27 Sep 2017 09:17:54 +0300
Subject: [PATCH 10/15] review comments changes

---
 .../org/alfresco/rest/core/v0/BaseAPI.java    | 22 -------
 .../model/custom/CustomDefinitions.java       | 63 +++++++++++++++++++
 .../rest/v0/CustomDefinitionsAPI.java         | 50 ++++++++++-----
 3 files changed, 98 insertions(+), 37 deletions(-)
 create mode 100644 rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/custom/CustomDefinitions.java

diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
index 125fba650f..c4844156aa 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
@@ -640,26 +640,4 @@ public abstract class BaseAPI
     {
         return FILE_PLAN_PATH;
     }
-
-    /**
-     * List of existing records management custom references.
-     */
-    public enum CUSTOM_DEFINITIONS
-    {
-        ATTACHMENT("Attachment"),
-        MESSAGE("Message"),
-        NEXT_VERSION("Next Version"),
-        RENDITION("Rendition");
-        String definition;
-
-        private CUSTOM_DEFINITIONS(String definition)
-        {
-            this.definition = definition;
-        }
-
-        public String getDefinition()
-        {
-            return definition;
-        }
-    }
 }
diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/custom/CustomDefinitions.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/custom/CustomDefinitions.java
new file mode 100644
index 0000000000..5e94f25d91
--- /dev/null
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/custom/CustomDefinitions.java
@@ -0,0 +1,63 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.rest.rm.community.model.custom;
+
+/**
+ * List of existing records management custom references.
+ *
+ * @author Rodica Sutu
+ * @since 2.6
+ */
+public enum CustomDefinitions
+{
+    ATTACHMENT("Attachment"),
+    MESSAGE("Message"),
+    NEXT_VERSION("Next Version"),
+    RENDITION("Rendition");
+    /**
+     * The name of custom reference.
+     */
+    private String definition;
+
+    /**
+     * Private constructor.
+     */
+    CustomDefinitions(String definition)
+    {
+        this.definition = definition;
+    }
+
+    /**
+     * Get the name of the custom reference.
+     *
+     * @return The value of custom reference.
+     */
+    public String getDefinition()
+    {
+        return definition;
+    }
+}
diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
index 257ac05bb7..fd2ab72a1b 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
@@ -29,6 +29,7 @@ package org.alfresco.rest.v0;
 import java.text.MessageFormat;
 
 import org.alfresco.rest.core.v0.BaseAPI;
+import org.alfresco.rest.rm.community.model.custom.CustomDefinitions;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -55,35 +56,52 @@ public class CustomDefinitionsAPI extends BaseAPI
      */
     private static final String CREATE_RELATIONSHIP_API_ENDPOINT = "{0}node/{1}/customreferences";
 
-    // logger
+    /**
+     * logger
+     */
     private static final Logger LOGGER = LoggerFactory.getLogger(CustomDefinitionsAPI.class);
 
     /**
-     * Helper method to get custom references definitions
+     * Helper method to get the reference id for a custom reference
      *
      * @param adminUser        user with administrative privileges
      * @param adminPassword    password for adminUser
      * @param customDefinition custom reference definition name
+     * @return  reference id   if the customDefinition is found
+     *           null  otherwise
+     *
      */
-
-    public String getCustomReferencesId(String adminUser, String adminPassword, String customDefinition)
+    public String getCustomReferenceId(String adminUser, String adminPassword, String customDefinition)
     {
 
         JSONObject getResponse = doGetRequest(adminUser, adminPassword, CUSTOM_REFERENCE_API_ENDPOINT);
         if (getResponse != null)
+        {
             try
             {
                 JSONArray customDefinitions = getResponse.getJSONObject("data").getJSONArray("customReferences");
                 for (int i = 0; i < customDefinitions.length(); i++)
                 {
                     JSONObject item = customDefinitions.getJSONObject(i);
-                    if (customDefinition.equalsIgnoreCase(item.has("source") ? item.getString("source") : null) ||
-                            customDefinition.equalsIgnoreCase(item.has("target") ? item.getString("target") : null) ||
-                            customDefinition.equalsIgnoreCase(item.has("label") ? item.getString("label") : null)
-                            )
+                    boolean hasSource = customDefinition.equalsIgnoreCase(
+                            item.has("source") ? item.getString("source") : null
+                                                                         );
+
+                    boolean hasTarget = customDefinition.equalsIgnoreCase(
+                            item.has("target") ? item.getString("target") : null
+                                                                         );
+
+                    boolean hasLabel = customDefinition.equalsIgnoreCase(
+                            item.has("label") ? item.getString("label") : null
+                                                                        );
+                    if ( hasSource || hasTarget || hasLabel)
                     {
                         return item.getString("refId");
                     }
+                    else
+                    {
+                        return null;
+                    }
                 }
 
             }
@@ -91,6 +109,7 @@ public class CustomDefinitionsAPI extends BaseAPI
             {
                 LOGGER.error("Unable to get the refId for the custom reference definition " + customDefinition);
             }
+        }
         return null;
     }
 
@@ -100,23 +119,24 @@ public class CustomDefinitionsAPI extends BaseAPI
      * @param adminUser     user with administrative privileges
      * @param adminPassword password for adminUser
      * @param recordNodeIdFrom node ref to set a custom reference
-     * @param recordNodeIdto        node ref of the to record
-     * @return true if creating relationship was successful,
-     * falseotherwise
+     * @param recordNodeIdTo        node ref of the to record
+     * @param   relationshipType    relation type to be created
+     * @return  true   if creating relationship was successful,
+     *          false  otherwise
      */
     public boolean createRelationship(
             String adminUser,
             String adminPassword,
             String recordNodeIdFrom,
-            String recordNodeIdto,
-            CUSTOM_DEFINITIONS relationshipType)
+            String recordNodeIdTo,
+            CustomDefinitions relationshipType)
     {
         try
         {
             //create the request body
             JSONObject requestParams = new JSONObject();
-            requestParams.put("toNode", NODE_REF_WORKSPACE_SPACES_STORE + recordNodeIdto);
-            requestParams.put("refId", getCustomReferencesId(adminUser, adminPassword, relationshipType
+            requestParams.put("toNode", NODE_REF_WORKSPACE_SPACES_STORE + recordNodeIdTo);
+            requestParams.put("refId", getCustomReferenceId(adminUser, adminPassword, relationshipType
                     .getDefinition()));
             //send the API request to create the relationship
             JSONObject setRelationshipStatus = doPostRequest(adminUser, adminPassword, requestParams,

From 9e18aa7f0c7d8b25d10996d1f3e456a4b090623b Mon Sep 17 00:00:00 2001
From: Rodica Sutu 
Date: Wed, 27 Sep 2017 15:59:29 +0300
Subject: [PATCH 11/15] fix the failure introduced with the last commit

---
 .../main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java  | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
index fd2ab72a1b..0529374f83 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/CustomDefinitionsAPI.java
@@ -98,10 +98,6 @@ public class CustomDefinitionsAPI extends BaseAPI
                     {
                         return item.getString("refId");
                     }
-                    else
-                    {
-                        return null;
-                    }
                 }
 
             }

From 24a6888adb4f9199accfdae59f486bcc64454d62 Mon Sep 17 00:00:00 2001
From: Ana Bozianu 
Date: Thu, 28 Sep 2017 11:32:44 +0300
Subject: [PATCH 12/15] RM-5655 - fixed unit tests and review formatting
 changes

---
 .../org_alfresco_module_rm/query/rm-query-context.xml     | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml
index dd5949efb9..5bb4a8b094 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml
@@ -16,11 +16,11 @@
         
     
 
-	
-		
-		
+   
+      
+      
       
       
-	
+   
 
 

From 47ae2830c5fccb0749be12182d9cf517462c7aab Mon Sep 17 00:00:00 2001
From: Rodica Sutu 
Date: Fri, 29 Sep 2017 09:10:42 +0300
Subject: [PATCH 13/15] refactoring tests to be more independent

---
 .../org/alfresco/rest/core/v0/BaseAPI.java    |  2 +-
 .../rm/community/base/BaseRMRestTest.java     | 74 ++++++++++++++++++-
 2 files changed, 72 insertions(+), 4 deletions(-)

diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
index c4844156aa..3bd7855468 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java
@@ -74,7 +74,7 @@ public abstract class BaseAPI
 
     /** exception key in JSON response body */
     private static final String EXCEPTION_KEY = "exception";
-    protected static final String NODE_PREFIX = "workspace/SpacesStore/";
+    public static final String NODE_PREFIX = "workspace/SpacesStore/";
     protected static final String UPDATE_METADATA_API = "{0}node/{1}/formprocessor";
     protected static final String ACTIONS_API = "{0}actionQueue";
     protected static final String RM_ACTIONS_API = "{0}rma/actions/ExecutionQueue";
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
index 0216f3ade2..124df6907a 100644
--- a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/base/BaseRMRestTest.java
@@ -52,6 +52,7 @@ import static org.springframework.http.HttpStatus.OK;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.alfresco.dataprep.ContentService;
@@ -71,6 +72,9 @@ import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI;
 import org.alfresco.rest.rm.community.requests.gscore.api.RecordCategoryAPI;
 import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI;
 import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI;
+import org.alfresco.rest.search.RestRequestQueryModel;
+import org.alfresco.rest.search.SearchNodeModel;
+import org.alfresco.rest.search.SearchRequest;
 import org.alfresco.rest.v0.RMRolesAndActionsAPI;
 import org.alfresco.utility.data.DataUser;
 import org.alfresco.utility.model.FolderModel;
@@ -507,11 +511,24 @@ public class BaseRMRestTest extends RestTest
      */
     public Record createElectronicRecord(String parentId, String name) throws Exception
     {
-        RecordFolderAPI recordFolderAPI = restAPIFactory.getRecordFolderAPI();
+       return createElectronicRecord(parentId, name ,null);
+    }
+
+
+    /**
+     * Create an electronic record
+     *
+     * @param parentId the id of the parent
+     * @param name     the name of the record
+     * @return the created record
+     * @throws Exception
+     */
+    public Record createElectronicRecord(String parentId, String name, UserModel user) throws Exception
+    {
+        RecordFolderAPI recordFolderAPI = restAPIFactory.getRecordFolderAPI(user);
         Record recordModel = Record.builder().name(name).nodeType(CONTENT_TYPE).build();
         return recordFolderAPI.createRecord(recordModel, parentId);
     }
-
     /**
      * Delete a record folder
      *
@@ -541,10 +558,61 @@ public class BaseRMRestTest extends RestTest
      * @param categoryId the id of the category to assign permissions for
      * @throws Exception
      */
-    public void assignFillingPermissionsOnCategory(UserModel user, String categoryId, String userPermission, String userRole) throws Exception
+    public void assignFillingPermissionsOnCategory(UserModel user, String categoryId,
+                                                   String userPermission, String userRole) throws Exception
     {
         getRestAPIFactory().getRMUserAPI().addUserPermission(categoryId, user, userPermission);
         rmRolesAndActionsAPI.assignUserToRole(getAdminUser().getUsername(),
                     getAdminUser().getPassword(), user.getUsername(), userRole);
     }
+
+    /**
+     * Returns search results for the given search term
+     *
+     * @param user
+     * @param term
+     * @return
+     * @throws Exception
+     */
+    public List searchForContentAsUser(UserModel user, String term) throws Exception
+    {
+        getRestAPIFactory().getRmRestWrapper().authenticateUser(user);
+        RestRequestQueryModel queryReq = new RestRequestQueryModel();
+        SearchRequest query = new SearchRequest(queryReq);
+        queryReq.setQuery("cm:name:*" + term + "*");
+
+        List names = new ArrayList<>();
+        // wait for solr indexing
+        int counter = 0;
+        int waitInMilliSeconds = 6000;
+        while (counter < 3)
+        {
+            synchronized (this)
+            {
+                try
+                {
+                    this.wait(waitInMilliSeconds);
+                } catch (InterruptedException e)
+                {
+                }
+            }
+
+            List searchResults = getRestAPIFactory().getRmRestWrapper().withSearchAPI().search(query)
+                                                                     .getEntries();
+            if ((searchResults != null && !searchResults.isEmpty()))
+            {
+                searchResults.forEach(childNode ->
+                {
+                    names.add(childNode.onModel().getName());
+                });
+                break;
+            } else
+            {
+                counter++;
+            }
+            // double wait time to not overdo solr search
+            waitInMilliSeconds = (waitInMilliSeconds * 2);
+        }
+        return names;
+    }
 }

From 10e639220ceff958510cae7562acbe60584c8ca7 Mon Sep 17 00:00:00 2001
From: Rodica Sutu 
Date: Fri, 29 Sep 2017 12:31:14 +0300
Subject: [PATCH 14/15] improve the tests for record folder classification to
 be independent

---
 .../model/recordcategory/RecordCategoryProperties.java         | 3 +++
 .../community/model/recordfolder/RecordFolderProperties.java   | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryProperties.java
index cbf9c1e4d3..ea75965d50 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryProperties.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordcategory/RecordCategoryProperties.java
@@ -34,6 +34,8 @@ import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanCo
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TITLE;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VITAL_RECORD_INDICATOR;
 
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 
@@ -59,6 +61,7 @@ import lombok.NoArgsConstructor;
 @EqualsAndHashCode(callSuper = true)
 @NoArgsConstructor
 @AllArgsConstructor
+@JsonIgnoreProperties (ignoreUnknown = true)
 public class RecordCategoryProperties extends TestModel
 {
     /*************************/
diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderProperties.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderProperties.java
index 824cf348a7..65b98ab3d9 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderProperties.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/recordfolder/RecordFolderProperties.java
@@ -40,6 +40,8 @@ import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanCo
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TITLE;
 import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VITAL_RECORD_INDICATOR;
 
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 
@@ -65,6 +67,7 @@ import lombok.NoArgsConstructor;
 @EqualsAndHashCode(callSuper = true)
 @NoArgsConstructor
 @AllArgsConstructor
+@JsonIgnoreProperties (ignoreUnknown = true)
 public class RecordFolderProperties extends TestModel
 {
     /*************************/

From 41311ef40bf71431a8a6e6992995e479f1ee2931 Mon Sep 17 00:00:00 2001
From: Ana Bozianu 
Date: Fri, 29 Sep 2017 11:39:00 +0100
Subject: [PATCH 15/15] Feature/rm 5655 classification beyond highest child

---
 .../query/rm-common-SqlMap.xml                |  20 +-
 .../query/rm-query-context.xml                |  10 +-
 ...ChildrenWithPropertyValuesQueryParams.java |  73 +++++
 .../query/RecordsManagementQueryDAO.java      |  16 ++
 .../query/RecordsManagementQueryDAOImpl.java  |  55 +++-
 .../RecordsManagementQueryDAOImplTest.java    | 252 +++++++++++++++++-
 6 files changed, 419 insertions(+), 7 deletions(-)
 create mode 100644 rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java

diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml
index e3f1c1a89d..d3b63f4467 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-common-SqlMap.xml
@@ -19,5 +19,23 @@
             prop.string_value = ?
             
     
-    
+
+   
+   
+
 
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml
index 59f1687234..5bb4a8b094 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/query/rm-query-context.xml
@@ -16,9 +16,11 @@
         
     
 
-	
-		
-		
-	
+   
+      
+      
+      
+      
+   
 
 
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java
new file mode 100644
index 0000000000..b0faa993e4
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/ChildrenWithPropertyValuesQueryParams.java
@@ -0,0 +1,73 @@
+/*
+ * #%L
+ * Alfresco Records Management Module
+ * %%
+ * Copyright (C) 2005 - 2017 Alfresco Software Limited
+ * %%
+ * This file is part of the Alfresco software.
+ * -
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail.  Otherwise, the software is
+ * provided under the following open source license terms:
+ * -
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * -
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ * -
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ * #L%
+ */
+package org.alfresco.module.org_alfresco_module_rm.query;
+
+import java.util.Collection;
+
+/**
+ * Select parameter for select_CountChildrenWithPropertyValues.
+ *
+ * @author Ana Manolache
+ * @since 2.6
+ */
+public class ChildrenWithPropertyValuesQueryParams
+{
+    private Long parentId;
+    private Long propertyQnameId;
+    private Collection propertyValues;
+
+    public Long getParentId()
+    {
+        return parentId;
+    }
+
+    public void setParentId(Long parentId)
+    {
+        this.parentId = parentId;
+    }
+
+    public Long getPropertyQnameId()
+    {
+        return propertyQnameId;
+    }
+
+    public void setPropertyQnameId(Long propertyQnameId)
+    {
+        this.propertyQnameId = propertyQnameId;
+    }
+
+    public Collection getPropertyValues()
+    {
+        return propertyValues;
+    }
+
+    public void setPropertyValues(Collection propertyValues)
+    {
+        this.propertyValues = propertyValues;
+    }
+}
+
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java
index fec9ad5353..afd031cd39 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAO.java
@@ -27,6 +27,11 @@
 
 package org.alfresco.module.org_alfresco_module_rm.query;
 
+import java.util.Collection;
+
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+
 /**
  * Records management query DAO
  * 
@@ -46,4 +51,15 @@ public interface RecordsManagementQueryDAO
      * @return int  count
      */
     int getCountRmaIdentifier(String identifierValue);
+
+    /**
+     * Returns whether a given node contains children with one of the given values for the given property
+     *
+     * @param parent         the parent to evaluate
+     * @param property       the QName of the property to evaluate
+     * @param propertyValues the list of values to look for
+     * @return true if there is at least one child with one of the values from the list set on the given property
+     * false otherwise
+     */
+    public boolean hasChildrenWithPropertyValues(NodeRef parent, QName property, Collection propertyValues);
 }
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java
index 82744e7815..f62712d6ab 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/query/RecordsManagementQueryDAOImpl.java
@@ -27,11 +27,16 @@
 
 package org.alfresco.module.org_alfresco_module_rm.query;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
 import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.repo.domain.node.NodeDAO;
 import org.alfresco.repo.domain.qname.QNameDAO;
+import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.service.cmr.repository.NodeRef;
 import org.alfresco.service.namespace.QName;
 import org.alfresco.util.Pair;
 import org.mybatis.spring.SqlSessionTemplate;
@@ -45,12 +50,15 @@ import org.mybatis.spring.SqlSessionTemplate;
 public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO, RecordsManagementModel
 {
     private static final String COUNT_IDENTIFIER = "alfresco.query.rm.select_CountRMIndentifier";
+    private static final String COUNT_CHILDREN_WITH_PROPERTY_VALUES = "select_CountChildrenWithPropertyValues";
     
     /** SQL session template */
     protected SqlSessionTemplate template;
     
     /** QName DAO */
     protected QNameDAO qnameDAO;
+    protected NodeDAO nodeDAO;
+    protected TenantService tenantService;
     
     /**
      * @param sqlSessionTemplate    SQL session template
@@ -67,7 +75,17 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO,
     {
         this.qnameDAO = qnameDAO;
     }
-    
+
+    public void setNodeDAO(NodeDAO nodeDAO)
+    {
+        this.nodeDAO = nodeDAO;
+    }
+
+    public void setTenantService(TenantService tenantService)
+    {
+        this.tenantService = tenantService;
+    }
+
     /**
      * @see org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO#getCountRmaIdentifier(java.lang.String)
      */
@@ -97,4 +115,39 @@ public class RecordsManagementQueryDAOImpl implements RecordsManagementQueryDAO,
         return result;
     }
 
+    @Override
+    public boolean hasChildrenWithPropertyValues(NodeRef parent, QName property, Collection propertyValues)
+    {
+        if(propertyValues.isEmpty())
+        {
+            return false;
+        }
+
+        ChildrenWithPropertyValuesQueryParams queryParams = new ChildrenWithPropertyValuesQueryParams();
+
+        // Set the parent node id
+        Pair nodePair = nodeDAO.getNodePair(tenantService.getName(parent));
+        if (nodePair == null)
+        {
+            throw new InvalidNodeRefException("The parent node does not exist.", parent);
+        }
+        Long parentNodeId = nodePair.getFirst();
+        queryParams.setParentId(parentNodeId);
+
+        // Set the property qname id
+        Pair pair = qnameDAO.getQName(property);
+        if (pair == null)
+        {
+            return false;
+        }
+        queryParams.setPropertyQnameId(pair.getFirst());
+
+
+        // Set the property values
+        queryParams.setPropertyValues(propertyValues);
+
+        // Perform the query
+        Long count = template.selectOne(COUNT_CHILDREN_WITH_PROPERTY_VALUES, queryParams);
+        return count > 0;
+    }
 }
diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java
index 5f29c12f7a..49a1fbe101 100644
--- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java
+++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/legacy/service/RecordsManagementQueryDAOImplTest.java
@@ -27,10 +27,17 @@
 
 package org.alfresco.module.org_alfresco_module_rm.test.legacy.service;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.alfresco.model.ContentModel;
 import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
 import org.alfresco.module.org_alfresco_module_rm.query.RecordsManagementQueryDAO;
 import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
 import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.GUID;
 
 /**
  * Records Management Query DAO
@@ -86,5 +93,248 @@ public class RecordsManagementQueryDAOImplTest extends BaseRMTestCase implements
         });
     }
 
-  
+    /**
+     * Given a folder containing 3 files with the descriptions set
+     * When I check if the folder contains children having the description of file2 or file2
+     * Then the answer is positive
+     */
+    @org.junit.Test
+    public void testHasChildrenWithPropertyValues_someChildrenWithValues() throws Exception
+    {
+        doBehaviourDrivenTest(new BehaviourDrivenTest()
+        {
+            NodeRef parentFolder;
+            NodeRef file1;
+            NodeRef file2;
+            NodeRef file3;
+            String propValue1 = "descr1"; // set on file1
+            String propValue2 = "descr2"; // set on file2
+            String propValue3 = "descr3"; // set on file3
+            String propValue4 = "descr4"; // not set on any file
+            Boolean result;
+
+            @Override
+            public void given() throws Exception
+            {
+                setupCollaborationSiteTestDataImpl();
+                parentFolder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file1 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file2 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file3 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+
+                nodeService.setProperty(file1, PROP_DESCRIPTION, propValue1);
+                nodeService.setProperty(file2, PROP_DESCRIPTION, propValue2);
+                nodeService.setProperty(file3, PROP_DESCRIPTION, propValue3);
+            }
+
+            @Override
+            public void when() throws Exception
+            {
+                result = queryDAO.hasChildrenWithPropertyValues(parentFolder, PROP_DESCRIPTION, Arrays.asList(propValue1, propValue2, propValue4));
+            }
+
+            @Override
+            public void then() throws Exception
+            {
+                assertTrue(result);
+            }
+
+            @Override
+            public void after() throws Exception
+            {
+                if (parentFolder != null && nodeService.exists(parentFolder))
+                {
+                    nodeService.deleteNode(parentFolder);
+                }
+            }
+        });
+    }
+
+    /**
+     * Given a folder containing 3 files with the descriptions unset
+     * When I check if the folder contains children having certain descriptions
+     * Then the answer is negative
+     */
+    @org.junit.Test
+    public void testHasChildrenWithPropertyValues_propertyNotSetOnChildren() throws Exception
+    {
+        doBehaviourDrivenTest(new BehaviourDrivenTest()
+        {
+            NodeRef parentFolder;
+            NodeRef file1;
+            NodeRef file2;
+            NodeRef file3;
+            Boolean result;
+
+            @Override
+            public void given() throws Exception
+            {
+                setupCollaborationSiteTestDataImpl();
+                parentFolder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file1 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file2 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file3 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+            }
+
+            @Override
+            public void when() throws Exception
+            {
+                result = queryDAO.hasChildrenWithPropertyValues(parentFolder, PROP_DESCRIPTION, Arrays.asList("descr1", "descr2", "descr3"));
+            }
+
+            @Override
+            public void then() throws Exception
+            {
+                assertFalse(result);
+            }
+
+            @Override
+            public void after() throws Exception
+            {
+                if (parentFolder != null && nodeService.exists(parentFolder))
+                {
+                    nodeService.deleteNode(parentFolder);
+                }
+            }
+        });
+    }
+
+    /**
+     * Given a folder with no children but the property set on itself
+     * When I check if the folder contains children having certain descriptions
+     * Then the answer is negative
+     */
+    @org.junit.Test
+    public void testHasChildrenWithPropertyValues_noChildren() throws Exception
+    {
+        doBehaviourDrivenTest(new BehaviourDrivenTest()
+        {
+            NodeRef folder;
+            String propValue = "descr";
+            Boolean result;
+
+            @Override
+            public void given() throws Exception
+            {
+                setupCollaborationSiteTestDataImpl();
+                folder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                nodeService.setProperty(folder, PROP_DESCRIPTION, propValue);
+            }
+
+            @Override
+            public void when() throws Exception
+            {
+                result = queryDAO.hasChildrenWithPropertyValues(folder, PROP_DESCRIPTION, Arrays.asList("descr"));
+            }
+
+            @Override
+            public void then() throws Exception
+            {
+                assertFalse(result);
+            }
+
+            @Override
+            public void after() throws Exception
+            {
+                if (folder != null && nodeService.exists(folder))
+                {
+                    nodeService.deleteNode(folder);
+                }
+            }
+        });
+    }
+
+    /**
+     * Given a folder with children and an unused property
+     * When I check if the folder contains children having the unused property
+     * Then the answer is negative
+     */
+    @org.junit.Test
+    public void testHasChildrenWithPropertyValues_propertyNotUsed() throws Exception
+    {
+        doBehaviourDrivenTest(new BehaviourDrivenTest()
+        {
+            NodeRef parentFolder;
+            QName property;
+            Boolean result;
+
+            @Override
+            public void given() throws Exception
+            {
+                setupCollaborationSiteTestDataImpl();
+                parentFolder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                property = QName.createQName(URI, "customProp-" + GUID.generate());
+            }
+
+            @Override
+            public void when() throws Exception
+            {
+                result = queryDAO.hasChildrenWithPropertyValues(folder, property, Arrays.asList("descr"));
+            }
+
+            @Override
+            public void then() throws Exception
+            {
+                assertFalse(result);
+            }
+
+            @Override
+            public void after() throws Exception
+            {
+                if (folder != null && nodeService.exists(folder))
+                {
+                    nodeService.deleteNode(folder);
+                }
+            }
+        });
+    }
+
+    /**
+     * Given any folder and any property
+     * When I pass an empty array to the hasChildrenWithPropertyValues method
+     * Then the answer is negative
+     */
+    @org.junit.Test
+    public void testHasChildrenWithPropertyValues_emptyArray() throws Exception
+    {
+        doBehaviourDrivenTest(new BehaviourDrivenTest()
+        {
+            NodeRef parentFolder;
+            NodeRef file1;
+            Boolean result;
+
+            @Override
+            public void given() throws Exception
+            {
+                setupCollaborationSiteTestDataImpl();
+                parentFolder = fileFolderService.create(documentLibrary, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+                file1 = fileFolderService.create(parentFolder, GUID.generate(), ContentModel.TYPE_FOLDER).getNodeRef();
+
+                nodeService.setProperty(file1, PROP_DESCRIPTION, "descr1");
+            }
+
+            @Override
+            public void when() throws Exception
+            {
+                result = queryDAO.hasChildrenWithPropertyValues(folder, PROP_DESCRIPTION, new ArrayList());
+            }
+
+            @Override
+            public void then() throws Exception
+            {
+                assertFalse(result);
+            }
+
+            @Override
+            public void after() throws Exception
+            {
+                if (folder != null && nodeService.exists(folder))
+                {
+                    nodeService.deleteNode(folder);
+                }
+            }
+        });
+    }
 }