diff --git a/rm-automation/pom.xml b/rm-automation/pom.xml index 1e6d3439db..d98b337c2a 100644 --- a/rm-automation/pom.xml +++ b/rm-automation/pom.xml @@ -127,6 +127,87 @@ + + + + + apply-rm-community + + + + maven-dependency-plugin + + + fetch-amps + process-test-resources + + copy + + + + + org.alfresco + alfresco-rm-community-share + ${project.version} + amp + + + org.alfresco + alfresco-rm-community-repo + ${project.version} + amp + + + ${project.build.directory}/amps + true + + + + + + org.alfresco.maven.plugin + alfresco-maven-plugin + true + + + install-community-repo-amp + + install + + process-test-resources + + true + + ${project.build.directory}/amps/alfresco-rm-community-repo-${project.version}.amp + + ${project.build.directory}/alf-installation/tomcat/webapps/alfresco.war + + + + + install-community-share-amp + + install + + process-test-resources + + true + + ${project.build.directory}/amps/alfresco-rm-community-share-${project.version}.amp + + ${project.build.directory}/alf-installation/tomcat/webapps/share.war + + + + + + + + + + apply-rm-enterprise + + maven-dependency-plugin 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 new file mode 100644 index 0000000000..18e717bba0 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/core/v0/BaseAPI.java @@ -0,0 +1,616 @@ +/* + * #%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.core.v0; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.dataprep.AlfrescoHttpClient; +import org.alfresco.dataprep.AlfrescoHttpClientFactory; +import org.alfresco.dataprep.ContentService; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.ParseException; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.entity.StringEntity; +import org.apache.http.util.EntityUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * The base API class containing common methods for making v0 API requests + * + * @author Kristijan Conkas + * @since 2.5 + */ +public abstract class BaseAPI +{ + // logger + private static final Logger LOGGER = LoggerFactory.getLogger(BaseAPI.class); + + /** exception key in JSON response body */ + private static final String EXCEPTION_KEY = "exception"; + protected static final String FILE_PLAN_PATH = "Sites/rm/documentLibrary"; + protected static final String NODE_REF_WORKSPACE_SPACES_STORE = "workspace://SpacesStore/"; + protected 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"; + protected static final String RM_SITE_ID = "rm"; + + @Autowired + private AlfrescoHttpClientFactory alfrescoHttpClientFactory; + + @Autowired + private ContentService contentService; + + /** + * Helper method to extract list of properties values from result. + * + * @param result the response + * @return list of specified property values in result + * @throws RuntimeException for malformed response + */ + protected List getPropertyValues(JSONObject result, String propertyName) + { + ArrayList results = new ArrayList(); + try + { + JSONArray items = result.getJSONArray("items"); + + for (int i = 0; i < items.length(); i++) + { + results.add(items.getJSONObject(i).getString(propertyName)); + } + } + catch (JSONException error) + { + throw new RuntimeException("Unable to parse result", error); + } + + return results; + } + + /** + * Helper method to extract property values from request result and put them in map as a list that corresponds to a unique property value. + * + * @param requestResult the request response + * @return a map containing information about multiple properties values that correspond to a unique one + * @throws RuntimeException for malformed response + */ + protected Map> getPropertyValuesByUniquePropertyValue(JSONObject requestResult, String uniqueProperty, List otherProperties) + { + Map> valuesByUniqueProperty = new HashMap<>(); + try + { + JSONArray items = requestResult.getJSONArray("items"); + + for (int i = 0; i < items.length(); i++) + { + List otherPropertiesValues = new ArrayList<>(); + + for (int j = 0; j < otherProperties.size(); j++) + { + otherPropertiesValues.add(items.getJSONObject(i).get(otherProperties.get(j)).toString()); + } + valuesByUniqueProperty.put(items.getJSONObject(i).getString(uniqueProperty), otherPropertiesValues); + } + } + catch (JSONException error) + { + throw new RuntimeException("Unable to parse result", error); + } + + return valuesByUniqueProperty; + } + + /** + * Retrieves the nodeRef of an item (category, folder or record) with the given path + * + * @param username the username + * @param password the password + * @param path the path to the container eg. in case of a category it would be the category name, + * in case of a folder it would be /categoryName/folderName + * when trying to get File Plan, the path would be "" + * @return the container nodeRef + */ + public String getItemNodeRef(String username, String password, String path) + { + return contentService.getNodeRefByPath(username, password, FILE_PLAN_PATH + path); + } + + /** + * Retrieve a Cmis object by its path + * + * @param username the user's username + * @param password its password + * @param path the object path + * @return the object in case it exists, null if its does not exist + */ + protected CmisObject getObjectByPath(String username, String password, String path) + { + CmisObject object; + try + { + object = contentService.getCMISSession(username, password).getObjectByPath(path); + } catch (CmisObjectNotFoundException notFoundError) + { + return null; + } + return object; + } + + /** + * Generic faceted request. + * + * @param username the username + * @param password the password + * @param parameters if the request has parameters + * @return result object (see API reference for more details), null for any errors + */ + protected JSONObject facetedRequest(String username, String password, List parameters, String requestURI) + { + String requestURL; + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + + if (parameters == null || parameters.isEmpty()) + { + requestURL = MessageFormat.format( + requestURI, + client.getAlfrescoUrl()); + } + else + { + requestURL = MessageFormat.format( + requestURI, + client.getAlfrescoUrl(), + URLEncodedUtils.format(parameters, "UTF-8")); + } + client.close(); + return doGetRequest(username, password, requestURL); + } + + /** + * Helper method for GET requests + * @param adminUser user with administrative privileges + * @param adminPassword password for adminUser + * @param urlTemplate request URL template + * @param urlTemplateParams zero or more parameters used with urlTemplate + */ + protected JSONObject doGetRequest(String adminUser, + String adminPassword, + String urlTemplate, + String ... urlTemplateParams) + { + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + String requestUrl = MessageFormat.format( + urlTemplate, + client.getApiUrl(), + urlTemplateParams); + client.close(); + + try + { + return doRequest(HttpGet.class, requestUrl, adminUser, adminPassword, null); + } + catch (InstantiationException | IllegalAccessException error) + { + throw new IllegalArgumentException("doGetRequest failed", error); + } + } + + /** + * Helper method for Delete requests + * @param adminUser user with administrative privileges + * @param adminPassword password for adminUser + * @param urlTemplate request URL template + * @param urlTemplateParams zero or more parameters used with urlTemplate + */ + protected JSONObject doDeleteRequest(String adminUser, + String adminPassword, + String urlTemplate, + String ... urlTemplateParams) + { + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + String requestUrl = MessageFormat.format( + urlTemplate, + client.getApiUrl(), + urlTemplateParams); + client.close(); + + try + { + return doRequest(HttpDelete.class, requestUrl, adminUser, adminPassword, null); + } + catch (InstantiationException | IllegalAccessException error) + { + throw new IllegalArgumentException("doDeleteRequest failed", error); + } + } + + /** + * Helper method for PUT requests + * @param adminUser user with administrative privileges + * @param adminPassword password for adminUser + * @param requestParams zero or more endpoint specific request parameters + * @param urlTemplate request URL template + * @param urlTemplateParams zero or more parameters used with urlTemplate + */ + protected JSONObject doPutRequest(String adminUser, + String adminPassword, + JSONObject requestParams, + String urlTemplate, + String ... urlTemplateParams) + { + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + String requestUrl = MessageFormat.format( + urlTemplate, + client.getApiUrl(), + urlTemplateParams); + client.close(); + + try + { + return doRequest(HttpPut.class, requestUrl, adminUser, adminPassword, requestParams); + } + catch (InstantiationException | IllegalAccessException error) + { + throw new IllegalArgumentException("doPutRequest failed", error); + } + } + + /** + * Helper method for POST requests + * @param adminUser user with administrative privileges + * @param adminPassword password for adminUser + * @param requestParams zero or more endpoint specific request parameters + * @param urlTemplate request URL template + * @param urlTemplateParams zero or more parameters used with urlTemplate + */ + protected JSONObject doPostRequest(String adminUser, + String adminPassword, + JSONObject requestParams, + String urlTemplate, + String ... urlTemplateParams) + { + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + String requestUrl = MessageFormat.format( + urlTemplate, + client.getApiUrl(), + urlTemplateParams); + client.close(); + + try + { + return doRequest(HttpPost.class, requestUrl, adminUser, adminPassword, requestParams); + } + catch (InstantiationException | IllegalAccessException error) + { + throw new IllegalArgumentException("doPostRequest failed", error); + } + } + + /** + * Helper method for POST requests + * + * @param adminUser user with administrative privileges + * @param adminPassword password for adminUser + * @param requestParams zero or more endpoint specific request parameters + * @param urlTemplate request URL template + * @param urlTemplateParams zero or more parameters used with urlTemplate + */ + protected boolean doPostJsonRequest(String adminUser, + String adminPassword, + JSONObject requestParams, + String urlTemplate, + String... urlTemplateParams) + { + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + String requestUrl = MessageFormat.format( + urlTemplate, + client.getApiUrl(), + urlTemplateParams); + client.close(); + + try + { + return doRequestJson(HttpPost.class, requestUrl, adminUser, adminPassword, requestParams); + } + catch (InstantiationException | IllegalAccessException error) + { + throw new IllegalArgumentException("doPostRequest failed", error); + } + } + + /** + * Helper method for handling generic HTTP requests + * @param requestType request type (a subclass of {@link HttpRequestBase}) + * @param requestUrl URL the request is to be sent to + * @param adminUser user with administrative privileges + * @param adminPassword password for adminUser + * @param requestParams endpoint specific request parameters + * @return response body + * @throws IllegalAccessException for invalid requestType + * @throws InstantiationException for invalid requestType + */ + private JSONObject doRequest( + Class requestType, + String requestUrl, + String adminUser, + String adminPassword, + JSONObject requestParams) throws InstantiationException, IllegalAccessException + { + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + T request = requestType.newInstance(); + + HttpResponse response = null; + JSONObject responseBody = null; + JSONObject returnValues = null; + + try + { + request.setURI(new URI(requestUrl)); + + if (requestParams != null && request instanceof HttpEntityEnclosingRequestBase) + { + ((HttpEntityEnclosingRequestBase) request).setEntity(new StringEntity(requestParams.toString())); + } + response = client.execute(adminUser, adminPassword, request); + + try + { + responseBody = new JSONObject(EntityUtils.toString(response.getEntity())); + } + catch (ParseException | IOException | JSONException error) + { + LOGGER.error("Parsing message body failed", error); + } + + switch (response.getStatusLine().getStatusCode()) + { + case HttpStatus.SC_OK: + case HttpStatus.SC_CREATED: + // request successful + if (responseBody != null) + { + returnValues = responseBody; + } + break; + + case HttpStatus.SC_INTERNAL_SERVER_ERROR: + case HttpStatus.SC_BAD_REQUEST: + if (responseBody != null && responseBody.has(EXCEPTION_KEY)) + { + LOGGER.error("Request failed: " + responseBody.getString(EXCEPTION_KEY)); + } + break; + + default: + LOGGER.error("Request returned unexpected HTTP status " + response.getStatusLine().getStatusCode()); + break; + } + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + catch (UnsupportedEncodingException | URISyntaxException error1) + { + LOGGER.error("Unable to construct request", error1); + } + finally + { + if (request != null) + { + request.releaseConnection(); + } + client.close(); + } + + return returnValues; + } + + private boolean doRequestJson( + Class requestType, + String requestUrl, + String adminUser, + String adminPassword, + JSONObject requestParams) throws InstantiationException, IllegalAccessException + { + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + T request = requestType.newInstance(); + + try + { + request.setURI(new URI(requestUrl)); + request.setHeader("Content-Type", "application/json"); + + if (requestParams != null && request instanceof HttpEntityEnclosingRequestBase) + { + ((HttpEntityEnclosingRequestBase) request).setEntity(new StringEntity(requestParams.toString())); + } + + return client.execute(adminUser, adminPassword, request).getStatusLine().getStatusCode() == HttpStatus.SC_OK; + } + catch (UnsupportedEncodingException | URISyntaxException error1) + { + LOGGER.error("Unable to construct request", error1); + } + finally + { + if (request != null) + { + request.releaseConnection(); + } + client.close(); + } + + return false; + } + + /** + * Used to set RM items properties + * including records, categories and folders + */ + public enum RMProperty + { + NAME, + TITLE, + CONTENT, + DESCRIPTION, + AUTHOR, + PHYSICAL_SIZE, + NUMBER_OF_COPIES, + STORAGE_LOCATION, + SHELF, + BOX, + FILE, + } + + public enum RETENTION_SCHEDULE + { + NAME, + DESCRIPTION, + RETENTION_AUTHORITY, + RETENTION_INSTRUCTIONS, + RETENTION_PERIOD, + RETENTION_LOCATION, + RETENTION_PERIOD_PROPERTY, + RETENTION_GHOST, + RETENTION_ELIGIBLE_FIRST_EVENT, + RETENTION_EVENTS, + + } + + /** + * Used to execute rm actions on a node + */ + public enum RM_ACTIONS + { + EDIT_DISPOSITION_DATE("editDispositionActionAsOfDate"), + CUT_OFF("cutoff"), + UNDO_CUT_OFF("undoCutoff"), + TRANSFER("transfer"), + DESTROY("destroy"); + String action; + + private RM_ACTIONS(String action) + { + this.action = action; + } + + public String getAction() + { + return action; + } + } + + public enum PermissionType + { + SET_READ, + REMOVE_READ, + SET_READ_AND_FILE, + REMOVE_READ_AND_FILE, + } + + /** + * Util to return the property value from a map + * + * @param properties the map containing properties + * @param property to get value for + * @return the property value + */ + public > String getPropertyValue(Map properties, Enum property) + { + String value = properties.get(property); + if (value == null) + { + return ""; + } + return value; + } + + /** + * Retrieves the property value and decides if that gets to be added to the request + * + * @param requestParams the request parameters + * @param propertyRequestValue the property name in the request, eg. "prop_cm_name" + * @param itemProperties map of item's properties values + * @param property the property in the property map to check value for + * @return the json object used in request with the property with its value added if that is not null or empty + */ + protected > JSONObject addPropertyToRequest(JSONObject requestParams, String propertyRequestValue, Map itemProperties, Enum property) throws JSONException + { + String propertyValue = getPropertyValue(itemProperties, property); + + if (!propertyValue.equals("")) + { + requestParams.put(propertyRequestValue, propertyValue); + } + return requestParams; + } + + /** + * Deletes the category, folder or record given as parameter + * + * @param username the username with whom the delete is performed + * @param password the user's password + * @param itemPath the path to the item eg. in case of a category it would be the "/" + category name, + * in case of a folder or subCategory it would be /categoryName/folderName or /categoryName/subCategoryName/ + * in case of a record /categoryName/folderName/recordName + * @return true if the deletion has been successful + */ + protected boolean deleteItem(String username, String password, String itemPath) + { + CmisObject container = getObjectByPath(username, password, FILE_PLAN_PATH + itemPath); + if (container != null) + { + container.delete(); + } + return getObjectByPath(username, password, itemPath) == null; + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RMRolesAndActionsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RMRolesAndActionsAPI.java new file mode 100644 index 0000000000..6d33a69c12 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RMRolesAndActionsAPI.java @@ -0,0 +1,353 @@ +/* + * #%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 static org.alfresco.dataprep.AlfrescoHttpClient.MIME_TYPE_JSON; + +import java.io.IOException; +import java.text.MessageFormat; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.Map; + +import org.alfresco.dataprep.AlfrescoHttpClient; +import org.alfresco.dataprep.AlfrescoHttpClientFactory; +import org.alfresco.dataprep.ContentService; +import org.alfresco.dataprep.UserService; +import org.alfresco.rest.core.v0.BaseAPI; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.apache.commons.httpclient.HttpStatus; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.message.BasicHeader; +import org.apache.http.protocol.HTTP; +import org.apache.http.util.EntityUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Component; + +/** + * Methods to make API requests using v0 API on RM items (move, update and other actions) including adding users to RM roles + * + * @author Oana Nechiforescu + * @since 2.5 + */ +@Component +public class RMRolesAndActionsAPI extends BaseAPI +{ + private static final String RM_ROLES_AUTHORITIES = "{0}rm/roles/{1}/authorities/{2}?alf_ticket={3}"; + + // logger + private static final Logger LOGGER = LoggerFactory.getLogger(RMRolesAndActionsAPI.class); + private static final String MOVE_ACTIONS_API = "action/rm-move-to/site/rm/documentLibrary/{0}"; + private static final String CREATE_HOLDS_API = "{0}type/rma:hold/formprocessor"; + + /** http client factory */ + @Autowired + private AlfrescoHttpClientFactory alfrescoHttpClientFactory; + + private ApplicationContext applicationContext; + + /** user service */ + @Autowired + private UserService userService; + + @Autowired + private ContentService contentService; + + /** + * create user and assign to records management role + */ + public void createUserAndAssignToRole( + String adminUser, + String adminPassword, + String userName, + String password, + String email, + String role, + String firstName, + String lastName) + { + if (!userService.userExists(adminUser, adminPassword, userName)) + { + userService.create(adminUser, adminPassword, userName, password, email, firstName, lastName); + } + assignUserToRole(adminUser, adminPassword, userName, role); + } + + /** + * assign user to records management role + */ + public boolean assignUserToRole(String adminUser, String adminPassword, String userName, String role) + { + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + String reqURL = MessageFormat.format( + RM_ROLES_AUTHORITIES, + client.getApiUrl(), + role, + userName, + client.getAlfTicket(adminUser, adminPassword)); + + HttpPost request = null; + HttpResponse response = null; + try + { + request = new HttpPost(reqURL); + response = client.execute(adminUser, adminPassword, request); + switch (response.getStatusLine().getStatusCode()) + { + case HttpStatus.SC_OK: + return true; + case HttpStatus.SC_CONFLICT: + break; + default: + break; + } + } + finally + { + if (request != null) + { + request.releaseConnection(); + } + client.close(); + } + + return false; + } + + /** + * Move action + * + * @param user the user to move the contentPath + * @param password the user's password + * @param contentPath path to the content to be moved + * @param destinationPath destination path + * @return true if the action completed successfully + */ + public boolean moveTo(String user, String password, String contentPath, String destinationPath) + { + String contentNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + getItemNodeRef(user, password, contentPath); + AlfrescoHttpClient client = alfrescoHttpClientFactory.getObject(); + String url = MessageFormat.format(client.getAlfrescoUrl() + "alfresco/s/slingshot/doclib/" + MOVE_ACTIONS_API, destinationPath); + HttpPost request = new HttpPost(url); + + try + { + JSONObject body = new JSONObject(); + body.put("nodeRefs", new JSONArray(Arrays.asList(contentNodeRef))); + StringEntity se = new StringEntity(body.toString(), AlfrescoHttpClient.UTF_8_ENCODING); + se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, MIME_TYPE_JSON)); + request.setEntity(se); + + HttpResponse response = client.execute(user, password, request); + switch (response.getStatusLine().getStatusCode()) + { + case HttpStatus.SC_OK: + JSONObject json = new JSONObject(EntityUtils.toString(response.getEntity())); + return (Boolean) json.get("overallSuccess"); + case HttpStatus.SC_NOT_FOUND: + LOGGER.info("The provided paths couldn't be found " + response.toString()); + default: + LOGGER.error("Unable to move: " + response.toString()); + } + } + catch (JSONException e) + { + e.printStackTrace(); + } + catch (IOException e) + { + e.printStackTrace(); + } + finally + { + if (request != null) + { + request.releaseConnection(); + } + client.close(); + } + + return false; + } + + /** + * Perform an action on the record folder + * + * @param user the user executing the action + * @param password the user's password + * @param contentName the content name + * @return true if the action completed successfully + */ + public boolean executeAction(String user, String password, String contentName, RM_ACTIONS rm_action) + { + return executeAction(user, password, contentName, rm_action, null); + } + + /** + * Perform an action on the record folder + * + * @param user the user closing the folder + * @param password the user's password + * @param contentName the record folder name + * @param date the date to be updated + * @return true if the action completed successfully + */ + public boolean executeAction(String user, String password, String contentName, RM_ACTIONS action, ZonedDateTime date) + { + String recNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + contentService.getNodeRef(user, password, RM_SITE_ID, contentName); + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("name", action.getAction()); + requestParams.put("nodeRef", recNodeRef); + if (date != null) + { + String thisMoment = date.format(DateTimeFormatter.ISO_INSTANT); + requestParams.put("params", new JSONObject() + .put("asOfDate", new JSONObject() + .put("iso8601", thisMoment) + ) + ); + } + return doPostJsonRequest(user, password, requestParams, RM_ACTIONS_API); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Deletes every item in the given container + * + * @param username the user's username + * @param password its password + * @param siteId the site id in which the container is located + * @param containerName the container to look for items into + * @return true if the deletion has been successful + */ + public boolean deleteAllItemsInContainer(String username, String password, String siteId, String containerName) + { + for (CmisObject item : contentService.getFolderObject(contentService.getCMISSession(username, password), siteId, containerName).getChildren()) + { + item.delete(); + } + return !(contentService.getFolderObject(contentService.getCMISSession(username, password), siteId, containerName).getChildren().getHasMoreItems()); + } + + /** + * Deletes hold + * + * @param username user's username + * @param password its password + * @param holdName the hold name + * @return true if the delete is successful + */ + public boolean deleteHold(String username, String password, String holdName) + { + return deleteItem(username, password, "/Holds/" + holdName); + } + + + /** + * Util method to create a hold + * + * @param user the user creating the category + * @param password the user's password + * @param holdName the hold name + * @param reason hold reason + * @param description hold description + * @return true if the hold creation has been successful + */ + public boolean createHold(String user, String password, String holdName, String reason, String description) + { + // if the hold already exists don't try to create it again + String holdsContainerPath = FILE_PLAN_PATH + "/Holds"; + + CmisObject hold = getObjectByPath(user, password, holdsContainerPath + "/" + holdName); + if (hold != null) + { + return true; + } + // retrieve the Holds container nodeRef + String parentNodeRef = getItemNodeRef(user, password, "/Holds"); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("alf_destination", NODE_REF_WORKSPACE_SPACES_STORE + parentNodeRef); + requestParams.put("prop_cm_name", holdName); + requestParams.put("prop_cm_description", description); + requestParams.put("prop_rma_holdReason", reason); + + boolean requestSucceeded = doPostJsonRequest(user, password, requestParams, CREATE_HOLDS_API); + return requestSucceeded && getObjectByPath(user, password, holdsContainerPath + "/" + holdName) != null; + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Updates metadata, can be used on records, folders and categories + * + * @param username the user updating the item + * @param password the user's password + * @param itemNodeRef the item noderef + * @return true if the update of the item properties has been successful + */ + public boolean updateMetadata(String username, String password, String itemNodeRef, Map properties) + { + try + { + JSONObject requestParams = new JSONObject(); + addPropertyToRequest(requestParams, "prop_cm_name", properties, RMProperty.NAME); + addPropertyToRequest(requestParams, "prop_cm_title", properties, RMProperty.TITLE); + addPropertyToRequest(requestParams, "prop_cm_description", properties, RMProperty.DESCRIPTION); + addPropertyToRequest(requestParams, "prop_cm_author", properties, RMProperty.AUTHOR); + + return doPostJsonRequest(username, password, requestParams, MessageFormat.format(UPDATE_METADATA_API, "{0}", itemNodeRef)); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordCategoriesAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordCategoriesAPI.java new file mode 100644 index 0000000000..855b633cd0 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordCategoriesAPI.java @@ -0,0 +1,191 @@ +/* + * #%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 java.util.HashMap; +import java.util.Map; + +import org.alfresco.rest.core.v0.BaseAPI; +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 record categories + * + * @author Oana Nechiforescu + * @since 2.5 + */ +@Component +public class RecordCategoriesAPI extends BaseAPI +{ + private static final Logger LOGGER = LoggerFactory.getLogger(RecordCategoriesAPI.class); + private static final String RM_ACTIONS_API = "{0}rma/actions/ExecutionQueue"; + private static final String DISPOSITION_ACTIONS_API = "{0}node/{1}/dispositionschedule/dispositionactiondefinitions"; + + /** + * Creates a retention schedule for the category given as parameter + * + * @param user the user creating the disposition schedule + * @param password the user's password + * @param categoryName the category name to create the retention schedule for + * @return true if the creation completed successfully + */ + public boolean createRetentionSchedule(String user, String password, String categoryName) + { + String catNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + getItemNodeRef(user, password, "/" + categoryName); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("name", "createDispositionSchedule"); + requestParams.put("nodeRef", catNodeRef); + + return doPostJsonRequest(user, password, requestParams, RM_ACTIONS_API); + } catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Sets retention schedule authority and instructions, also if it is applied to records or folders + * + * @param user the user creating the disposition schedule + * @param password the user's password + * @param retentionNodeRef the retention nodeRef + * @return true if the creation completed successfully + */ + public boolean setRetentionScheduleGeneralFields(String user, String password, String retentionNodeRef, Map retentionProperties, Boolean appliedToRecords) + { + String dispRetentionNodeRef = NODE_PREFIX + retentionNodeRef; + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("prop_rma_dispositionAuthority", getPropertyValue(retentionProperties, RETENTION_SCHEDULE.RETENTION_AUTHORITY)); + requestParams.put("prop_rma_dispositionInstructions", getPropertyValue(retentionProperties, RETENTION_SCHEDULE.RETENTION_INSTRUCTIONS)); + requestParams.put("prop_rma_recordLevelDisposition", appliedToRecords.toString()); + return doPostJsonRequest(user, password, requestParams, MessageFormat.format(UPDATE_METADATA_API, "{0}", dispRetentionNodeRef)); + } catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Creates a retention schedule steps for the category given as parameter + * + * @param user the user creating the disposition schedule + * @param password the user's password + * @param categoryName the category name to create the retention schedule for + * @return true if the creation completed successfully + */ + public boolean addDispositionScheduleSteps(String user, String password, String categoryName, Map properties) + { + String catNodeRef = NODE_PREFIX + getItemNodeRef(user, password, "/" + categoryName); + { + try + { + JSONObject requestParams = new JSONObject(); + addPropertyToRequest(requestParams, "name", properties, RETENTION_SCHEDULE.NAME); + addPropertyToRequest(requestParams, "description", properties, RETENTION_SCHEDULE.DESCRIPTION); + addPropertyToRequest(requestParams, "period", properties, RETENTION_SCHEDULE.RETENTION_PERIOD); + addPropertyToRequest(requestParams, "ghostOnDestroy", properties, RETENTION_SCHEDULE.RETENTION_GHOST); + addPropertyToRequest(requestParams, "periodProperty", properties, RETENTION_SCHEDULE.RETENTION_PERIOD_PROPERTY); + addPropertyToRequest(requestParams, "events", properties, RETENTION_SCHEDULE.RETENTION_EVENTS); + addPropertyToRequest(requestParams, "eligibleOnFirstCompleteEvent", properties, RETENTION_SCHEDULE.RETENTION_ELIGIBLE_FIRST_EVENT); + + return doPostJsonRequest(user, password, requestParams, MessageFormat.format(DISPOSITION_ACTIONS_API, "{0}", catNodeRef)); + } catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + } + + /** + * Delete a category + * + * @param username user's username + * @param password its password + * @param categoryName the name of the category + * @return true if the delete is successful + */ + public boolean deleteCategory(String username, String password, String categoryName) + { + return deleteItem(username, password, "/" + categoryName); + } + + /** + * Delete a sub-category + * + * @param username user's username + * @param password its password + * @param categoryName the name of the sub-category + * @return true if the delete is successful + */ + public boolean deleteSubCategory(String username, String password, String categoryName, String subCategoryName) + { + return deleteItem(username, password, "/" + categoryName + "/" + subCategoryName); + } + + /** + * Delete a folder inside a container in RM site + * + * @param username user's username + * @param password its password + * @param folderName folder name + * @param containerName the name of the category or container sin which the folder is + * @return true if the delete is successful + */ + public boolean deleteFolderInContainer(String username, String password, String folderName, String containerName) + { + return deleteItem(username, password, "/" + containerName + "/" + folderName); + } + + /** + * Returns a map of retention properties + * + * @param authority retention authority + * @param instructions retention authority + * @return the map + */ + public Map getRetentionProperties(String authority, String instructions) + { + Map retentionProperties = new HashMap<>(); + retentionProperties.put(RETENTION_SCHEDULE.RETENTION_AUTHORITY, authority); + retentionProperties.put(RETENTION_SCHEDULE.RETENTION_INSTRUCTIONS, instructions); + return retentionProperties; + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordFoldersAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordFoldersAPI.java new file mode 100644 index 0000000000..48ac2e0a82 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordFoldersAPI.java @@ -0,0 +1,78 @@ +/* + * #%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.dataprep.ContentService; +import org.alfresco.rest.core.v0.BaseAPI; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Methods to make API requests using v0 API on records folders + * + * @author Oana Nechiforescu + * @since 2.5 + */ +@Component +public class RecordFoldersAPI extends BaseAPI +{ + private static final Logger LOGGER = LoggerFactory.getLogger(RecordFoldersAPI.class); + + @Autowired + private ContentService contentService; + + /** + * Close the record folder + * + * @param user the user closing the folder + * @param password the user's password + * @param recordFolder the record folder name + * @return true if the action completed successfully + */ + public boolean closeRecordFolder(String user, String password, String recordFolder) + { + String recNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + contentService.getNodeRef(user, password, RM_SITE_ID, recordFolder); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("name", "closeRecordFolder"); + requestParams.put("nodeRef", recNodeRef); + + return doPostJsonRequest(user, password, requestParams, RM_ACTIONS_API); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } +} diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordsAPI.java new file mode 100644 index 0000000000..126829a099 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/RecordsAPI.java @@ -0,0 +1,291 @@ +/* + * #%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.util.Map; + +import org.alfresco.dataprep.CMISUtil.DocumentType; +import org.alfresco.dataprep.ContentService; +import org.alfresco.rest.core.v0.BaseAPI; +import org.apache.chemistry.opencmis.client.api.CmisObject; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Methods to make API requests using v0 API on records + * + * @author Oana Nechiforescu + * @since 2.5 + */ +@Component +public class RecordsAPI extends BaseAPI +{ + // logger + private static final Logger LOGGER = LoggerFactory.getLogger(RecordsAPI.class); + + private static final String CREATE_NON_ELECTRONIC_RECORD_API = "{0}type/rma:nonElectronicDocument/formprocessor"; + + @Autowired + private ContentService contentService; + + + /** + * Declare documents as records + * + * @param user the user declaring the document as record + * @param password the user's password + * @param siteID the site id in which the document exists + * @param documentName the document name + * @return true if the action was successful + */ + public boolean declareDocumentAsRecord(String user, String password, String siteID, String documentName) + { + String docNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + contentService.getNodeRef(user, password, siteID, documentName); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("actionedUponNode", docNodeRef); + requestParams.put("actionDefinitionName", "create-record"); + + return doPostJsonRequest(user, password, requestParams, ACTIONS_API); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Completes the record given as parameter + * + * @param user the user declaring the document as record + * @param password the user's password + * @param recordName the record name + * @return true if the action completed successfully + */ + public boolean completeRecord(String user, String password, String recordName) + { + String recNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + contentService.getNodeRef(user, password, RM_SITE_ID, recordName); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("name", "declareRecord"); + requestParams.put("nodeRef", recNodeRef); + + return doPostJsonRequest(user, password, requestParams, RM_ACTIONS_API); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + + /** + * Declare document version as record + * + * @param user the user declaring the document version as record + * @param password the user's password + * @param siteID the site id in which the document exists + * @param documentName the document name + * @return true if the action was successful + */ + public boolean declareDocumentVersionAsRecord(String user, String password, String siteID, String documentName) + { + String docNodeRef = NODE_REF_WORKSPACE_SPACES_STORE + contentService.getNodeRef(user, password, siteID, documentName); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("actionedUponNode", docNodeRef); + requestParams.put("actionDefinitionName", "declare-as-version-record"); + + return doPostJsonRequest(user, password, requestParams, ACTIONS_API); + } + catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Creates a non-electronic record + * + * @param username the username + * @param password the password + * @param properties a map of record properties and their values + * @param categoryName the category that contains the record, in the case in which the container would be Unfiled records use UNFILED_RECORDS_BREADCRUMB as value + * @param folderName the folder inside which the record exists, in the case in which the folder name is "", the record will be created directly in the specified container + * this case is useful when trying to create a record directly in Unfiled Records + * @return true if the creation of the record has been successful + * eg. of usage for Unfiled records with folder : createNonElectronicRecord(getAdminName(), getAdminPassword(), properties, UNFILED_RECORDS_BREADCRUMB, "unfiled records folder"); + * eg. of usage for creating record directly in Unfiled Records : createNonElectronicRecord(getAdminName(), getAdminPassword(), properties, UNFILED_RECORDS_BREADCRUMB, ""); + */ + public > boolean createNonElectronicRecord(String username, String password, Map properties, String categoryName, String folderName) + { + String recordName = properties.get(RMProperty.NAME); + if (getRecord(username, password, folderName, recordName) != null) + { + return true; + } + String recordPath = "/" + categoryName; + if (!folderName.equals("")) + { + recordPath = recordPath + "/" + folderName; + } + // if the record already exists don't try to create it again + CmisObject record = getObjectByPath(username, password, FILE_PLAN_PATH + recordPath + "/" + recordName); + + if (record != null) + { + return true; + } + // non-electronic properties + String recordTitle = getPropertyValue(properties, RMProperty.TITLE); + String description = getPropertyValue(properties, RMProperty.DESCRIPTION); + String physicalSize = getPropertyValue(properties, RMProperty.PHYSICAL_SIZE); + String numberOfCopies = getPropertyValue(properties, RMProperty.NUMBER_OF_COPIES); + String shelf = getPropertyValue(properties, RMProperty.SHELF); + String storage = getPropertyValue(properties, RMProperty.STORAGE_LOCATION); + String box = getPropertyValue(properties, RMProperty.BOX); + String file = getPropertyValue(properties, RMProperty.FILE); + + // retrieve the container nodeRef + String parentNodeRef = getItemNodeRef(username, password, recordPath); + + try + { + JSONObject requestParams = new JSONObject(); + requestParams.put("alf_destination", NODE_REF_WORKSPACE_SPACES_STORE + parentNodeRef); + requestParams.put("prop_cm_name", recordName); + requestParams.put("prop_cm_title", recordTitle); + requestParams.put("prop_cm_description", description); + requestParams.put("prop_rma_physicalSize", physicalSize); + requestParams.put("prop_rma_numberOfCopies", numberOfCopies); + requestParams.put("prop_rma_storageLocation", storage); + requestParams.put("prop_rma_shelf", shelf); + requestParams.put("prop_rma_box", box); + requestParams.put("prop_rma_file", file); + + return doPostJsonRequest(username, password, requestParams, CREATE_NON_ELECTRONIC_RECORD_API); + } catch (JSONException error) + { + LOGGER.error("Unable to extract response parameter", error); + } + return false; + } + + /** + * Uploads an electronic record + * + * @param username the username + * @param password the password + * @param properties a map of record properties and their values + * @param folderName the folder inside which the record will be created, it needs to have a unique name + * @return true if the creation of the record has been successful + * eg. of usage for creating record directly in Unfiled Records : uploadElectronicRecord(getAdminName(), getAdminPassword(), recordPropertiesStringMap, UNFILED_RECORDS_BREADCRUMB, DocumentType.HTML) + * the folder name in which the record gets created has to have a unique name, as this method doesn't check other containers than the folder name + */ + public boolean uploadElectronicRecord(String username, String password, Map properties, String folderName, DocumentType documentType) + { + String recordName = getPropertyValue(properties, RMProperty.NAME); + String recordContent = getPropertyValue(properties, RMProperty.CONTENT); + return (getRecord(username, password, folderName, recordName) != null) || (contentService.createDocumentInFolder(username, password, RM_SITE_ID, folderName, documentType, recordName, recordContent) != null); + } + + /** + * Delete a record from the given path + * + * @param username user's username + * @param password its password + * @param recordName the record name + * @param categoryName the name of the category in which the folder is, in case of unfiled record, this will have UNFILED_RECORDS_BREADCRUMB as container + * @param folderName folder name, in case in which trying to delete a record in Unfiled records directly, this will be "" + * @return true if the delete is successful + * eg. of usage in the case in which the record is inside a folder in Unfiled Records : deleteRecord(getAdminName(), getAdminPassword(), "f1 (2016-1472716888713)", UNFILED_RECORDS_BREADCRUMB, "unfiled records folder"); + * eg. of usage in the case in which the record is created directly in Unfiled Records : deleteRecord(getAdminName(), getAdminPassword(), "f1 (2016-1472716888713)", UNFILED_RECORDS_BREADCRUMB, ""); + */ + public boolean deleteRecord(String username, String password, String recordName, String categoryName, String folderName) + { + String recordPath = "/" + categoryName; + if (!folderName.equals("")) + { + recordPath = recordPath + "/" + folderName; + } + return deleteItem(username, password, recordPath + "/" + recordName); + } + + /** + * Retrieves the record object in case it exists + * + * @param username the user's username + * @param password its password + * @param folderName the folder in which the record is supposed to exist + * @param recordName the String with which the record name starts + * @return the record object in case it exists, null otherwise + */ + private CmisObject getRecord(String username, String password, String folderName, String recordName) + { + for (CmisObject record : contentService.getFolderObject(contentService.getCMISSession(username, password), RM_SITE_ID, folderName).getChildren()) + { + if (record.getName().startsWith(recordName)) + { + return record; + } + } + return null; + } + + /** + * Retrieves record full name for given partial name + * + * @param username the user's username + * @param password its password + * @param folderName the folder in which the record is supposed to exist + * @param recordPartialName the String with which the record name starts + * @return the record name in case it exists, empty String otherwise + */ + public String getRecordFullName(String username, String password, String folderName, String recordPartialName) + { + CmisObject record = getRecord(username, password, folderName, recordPartialName); + if (record != null) + { + return record.getName(); + } + return ""; + } + +} 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 new file mode 100644 index 0000000000..7bcfff7d48 --- /dev/null +++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/SearchAPI.java @@ -0,0 +1,177 @@ +/* + * #%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 java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.alfresco.dataprep.AlfrescoHttpClientFactory; +import org.alfresco.rest.core.v0.BaseAPI; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.message.BasicNameValuePair; +import org.json.JSONObject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Helper methods for performing search using various Alfresco search APIs. + * + * @author Kristijan Conkas + * @since 2.5 + */ +@Component +public class SearchAPI extends BaseAPI +{ + /** http client factory */ + @Autowired private AlfrescoHttpClientFactory alfrescoHttpClientFactory; + + /** faceted search API endpoint */ + private static final String FACETED_SEARCH_ENDPOINT = "{0}alfresco/s/slingshot/rmsearch/faceted/rmsearch?{1}"; + + /** RM search URL template */ + 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"; + + /** + * 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) + { + return facetedRequest(searchUser, searchPassword, null, searchEndpoint); + } + + /** + * Generic rm search. + * @param username + * @param password + * @param site + * @param query + * @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) + { + 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")); + + return doSearch(requestURL, username, password); + } + + /** + * 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) + { + return getItemNames(rmSearch(username, password, "rm", query, RM_DEFAULT_RECORD_FILTERS)); + } + + /** + * Generic faceted search. + * @param username + * @param password + * @param parameters + * @return search results (see API reference for more details), null for any errors + */ + public JSONObject facetedSearch(String username, String password, List parameters) + { + return facetedRequest(username, password, parameters, FACETED_SEARCH_ENDPOINT); + } + + /** + * Execute faceted search for term. + * @param searchUser + * @param searchPassword + * @param searchTerm + * @return search results (see API reference for more details) + */ + public JSONObject facetedSearchForTerm(String searchUser, String searchPassword, String 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 + * @return list of document names found + */ + public List searchForDocumentsAsUser(String username, String password, String term) + { + return getItemNames(facetedSearchForTerm(username, password, term)); + } + + /** + * 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 + */ + private List getItemNames(JSONObject searchResult) + { + return getPropertyValues(searchResult, "name"); + } +}