diff --git a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordsAPI.java b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordsAPI.java
index ab403834f6..6864754edb 100644
--- a/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordsAPI.java
+++ b/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/requests/gscore/api/RecordsAPI.java
@@ -139,6 +139,42 @@ public class RecordsAPI extends RMModelRequest
));
}
+ /**
+ * see {@link #completeRecord(String, String)
+ */
+ public Record completeRecord(String recordId) throws Exception
+ {
+ mandatoryString("recordId", recordId);
+
+ return completeRecord(recordId, EMPTY);
+ }
+
+ /**
+ * Complete the record recordId
+ *
+ * @param recordId The id of the record to complete
+ * @return The completed {@link Record} with the given properties
+ * @throws Exception for the following cases:
+ *
+ * - Invalid parameter: {@code recordId} is not a record
+ * - authentication fails
+ * - current user does not have permission to complete {@code recordId}
+ * - {@code recordId} does not exist or is frozen
+ * - model integrity exception: the record is already completed
+ * - model integrity exception: the record has missing meta-data
+ *
+ */
+ public Record completeRecord(String recordId, String parameters) throws Exception
+ {
+ mandatoryString("recordId", recordId);
+
+ return getRmRestWrapper().processModel(Record.class, simpleRequest(
+ POST,
+ "/records/{recordId}/complete?{parameters}",
+ recordId,
+ parameters
+ ));
+ }
/**
* Deletes a record.
*
@@ -216,7 +252,7 @@ public class RecordsAPI extends RMModelRequest
* @param recordModel The record model which holds the information
* @param recordId The identifier of a record
* @param parameters The URL parameters to add
- * @param returns The updated {@link Record}
+ * @return The updated {@link Record}
* @throws Exception for the following cases:
*
* - the update request is invalid or {@code recordId} is not a valid format or {@code recordModel} is invalid
diff --git a/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/CompleteRecordTests.java b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/CompleteRecordTests.java
new file mode 100644
index 0000000000..a46a3dd5bf
--- /dev/null
+++ b/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/CompleteRecordTests.java
@@ -0,0 +1,292 @@
+/*
+ * #%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.records;
+
+import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.IMAGE_FILE;
+import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createElectronicRecordModel;
+import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createNonElectronicRecordModel;
+import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.getFile;
+import static org.alfresco.rest.rm.community.utils.RMSiteUtil.createDOD5015RMSiteModel;
+import static org.alfresco.rest.rm.community.utils.RMSiteUtil.createStandardRMSiteModel;
+import static org.springframework.http.HttpStatus.BAD_REQUEST;
+import static org.springframework.http.HttpStatus.CREATED;
+import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
+import static org.testng.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.alfresco.rest.rm.community.base.BaseRMRestTest;
+import org.alfresco.rest.rm.community.model.record.Record;
+import org.alfresco.rest.rm.community.model.site.RMSite;
+import org.alfresco.rest.rm.community.requests.gscore.api.RMSiteAPI;
+import org.alfresco.rest.rm.community.requests.gscore.api.RecordFolderAPI;
+import org.alfresco.rest.rm.community.requests.gscore.api.RecordsAPI;
+import org.alfresco.test.AlfrescoTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * This class contains the tests for
+ * Complete Record Action REST API
+ *
+ * @author Sara Aspery
+ * @since 2.6
+ */
+public class CompleteRecordTests extends BaseRMRestTest
+{
+ private static final Boolean COMPLETE = true;
+ private static final Boolean INCOMPLETE = false;
+ private static final String PARAMETERS = "include=isCompleted";
+
+ /**
+ * Incomplete records with mandatory meta-data missing
+ */
+ @DataProvider (name = "IncompleteRecordsMandatoryMetadataMissing")
+ public Object[][] getIncompleteRecordsMandatoryMetadataMissing() throws Exception
+ {
+ //create RM site
+ createRMSite(createDOD5015RMSiteModel());
+
+ // create electronic and non-electronic records
+ return createAndVerifyRecordsInFolder();
+ }
+
+ /**
+ * Incomplete records with mandatory meta-data present
+ */
+ @DataProvider (name = "IncompleteRecordsMandatoryMetadataPresent")
+ public Object[][] getIncompleteRecordsMandatoryMetadataPresent() throws Exception
+ {
+ //create RM site
+ createRMSite(createStandardRMSiteModel());
+
+ // create electronic and non-electronic records
+ return createAndVerifyRecordsInFolder();
+ }
+
+ /**
+ * Document to be completed is not a record
+ */
+ @DataProvider (name = "Supplied node is not a record")
+ public Object[][] getNodesWhichAreNotRecords() throws Exception
+ {
+ createRMSiteIfNotExists();
+ return new String[][]
+ {
+ { createCategoryFolderInFilePlan().getId() },
+
+ };
+ }
+
+ /**
+ *
+ * Given the repository is configured to check mandatory data before completing a record
+ * And an incomplete record with its mandatory meta-data missing
+ * When I complete the record
+ * Then I receive an error indicating that I can't complete the operation,
+ * because some of the mandatory meta-data of the record is missing
+ *
+ */
+ @Test
+ (
+ dataProvider = "IncompleteRecordsMandatoryMetadataMissing",
+ description = "Cannot complete electronic and non-electronic records with mandatory metadata missing"
+ )
+ @AlfrescoTest (jira = "RM-4431")
+ public void completeRecordWithMandatoryMetadataMissing(String electronicRecordId, String nonElectronicRecordId)
+ throws Exception
+ {
+ List records = getRecordsList(electronicRecordId, nonElectronicRecordId);
+
+ for (Record record : records)
+ {
+ verifyRecordCompletionStatus(record, INCOMPLETE);
+
+ // Complete record
+ completeRecord(record);
+ assertStatusCode(UNPROCESSABLE_ENTITY);
+
+ verifyRecordCompletionStatus(record, INCOMPLETE);
+ }
+ }
+
+ /**
+ *
+ * Given the repository is configured to check mandatory data before completing a record
+ * And an incomplete record with all mandatory meta-data present
+ * When I complete the record
+ * Then the record is successfully completed
+ *
+ */
+ @Test
+ (
+ dataProvider = "IncompleteRecordsMandatoryMetadataPresent",
+ description = "Can complete electronic and non-electronic records with mandatory metadata present"
+ )
+ @AlfrescoTest (jira = "RM-4431")
+ public void completeRecordWithMandatoryMetadataPresent(String electronicRecordId, String nonElectronicRecordId)
+ throws Exception
+ {
+ List records = getRecordsList(electronicRecordId, nonElectronicRecordId);
+
+ for (Record record : records)
+ {
+ verifyRecordCompletionStatus(record, INCOMPLETE);
+
+ // Complete record
+ completeRecord(record);
+ assertStatusCode(CREATED);
+
+ verifyRecordCompletionStatus(record, COMPLETE);
+ }
+ }
+
+ /**
+ *
+ * Given a document that is not a record or any non-document node
+ * When I complete the item
+ * Then I receive an unsupported operation error
+ *
+ */
+ @Test
+ (
+ dataProvider = "Supplied node is not a record",
+ description = "Cannot complete a document that is not a record"
+ )
+ @AlfrescoTest (jira = "RM-4431")
+ public void completeNonRecord(String nonRecordId) throws Exception
+ {
+ // Get the recordsAPI
+ RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
+ recordsAPI.completeRecord(nonRecordId, PARAMETERS);
+ assertStatusCode(BAD_REQUEST);
+ }
+
+ /**
+ *
+ * Given a record that is already completed
+ * When I complete the record
+ * Then I receive an error indicating that I can't complete the operation, because the record is already complete
+ *
+ */
+ @Test
+ (
+ dataProvider = "IncompleteRecordsMandatoryMetadataPresent",
+ description = "Cannot complete a record that is already completed"
+ )
+ @AlfrescoTest (jira = "RM-4431")
+ public void completeAlreadyCompletedRecord(String electronicRecordId, String nonElectronicRecordId)
+ throws Exception
+ {
+ List records = getRecordsList(electronicRecordId, nonElectronicRecordId);
+
+ for (Record record : records)
+ {
+ verifyRecordCompletionStatus(record, INCOMPLETE);
+
+ // Complete record
+ completeRecord(record);
+ assertStatusCode(CREATED);
+
+ verifyRecordCompletionStatus(record, COMPLETE);
+
+ // Complete record
+ completeRecord(record);
+ assertStatusCode(UNPROCESSABLE_ENTITY);
+ }
+ }
+
+ /**
+ * Helper method to create an RM site and assert successful creation
+ */
+ private void createRMSite(RMSite rmSiteModel) throws Exception
+ {
+ RMSiteAPI rmSiteAPI = getRestAPIFactory().getRMSiteAPI();
+ rmSiteAPI.deleteRMSite();
+ rmSiteAPI.createRMSite(rmSiteModel);
+ assertStatusCode(CREATED);
+ }
+
+ /**
+ * Helper method to create records and and assert successful creation
+ */
+ private String[][] createAndVerifyRecordsInFolder() throws Exception
+ {
+ RecordFolderAPI recordFolderAPI = getRestAPIFactory().getRecordFolderAPI();
+
+ // create record folder
+ String recordFolderId = createCategoryFolderInFilePlan().getId();
+
+ // create electronic record in record folder
+ Record electronicRecord = recordFolderAPI.createRecord(createElectronicRecordModel(), recordFolderId,
+ getFile(IMAGE_FILE));
+ assertStatusCode(CREATED);
+
+ // create non-electronic record in record folder
+ Record nonElectronicRecord = recordFolderAPI.createRecord(createNonElectronicRecordModel(), recordFolderId);
+ assertStatusCode(CREATED);
+
+ return new String[][]
+ {
+ { electronicRecord.getId(), nonElectronicRecord.getId() },
+ };
+ }
+
+ /**
+ * Helper method to provide list of records from record Ids
+ */
+ private List getRecordsList(String electronicRecordId, String nonElectronicRecordId)
+ {
+ // Get the recordsAPI
+ RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
+
+ Record electronicRecord = recordsAPI.getRecord(electronicRecordId);
+ Record nonElectronicRecord = recordsAPI.getRecord(nonElectronicRecordId);
+
+ return Arrays.asList(electronicRecord,nonElectronicRecord);
+ }
+
+ /**
+ * Helper method to verify record is complete or incomplete
+ */
+ private void verifyRecordCompletionStatus(Record record, Boolean completionStatus)
+ {
+ RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
+ Record recordModel = recordsAPI.getRecord(record.getId(), PARAMETERS);
+ assertEquals(recordModel.getIsCompleted(), completionStatus);
+ }
+
+ /**
+ * Helper method to complete a record
+ */
+ private void completeRecord(Record record) throws Exception
+ {
+ RecordsAPI recordsAPI = getRestAPIFactory().getRecordsAPI();
+ recordsAPI.completeRecord(record.getId(), PARAMETERS);
+ }
+}
diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml
index 73adaab087..9d658f65cc 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-action-context.xml
@@ -388,8 +388,7 @@
-
-
+
diff --git a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml
index c2fd343d4b..6916b0bd36 100644
--- a/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml
+++ b/rm-community/rm-community-repo/config/alfresco/module/org_alfresco_module_rm/rm-service-context.xml
@@ -1076,6 +1076,9 @@
+
+
+
@@ -1152,6 +1155,7 @@
org.alfresco.module.org_alfresco_module_rm.record.RecordService.makeRecord=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.record.RecordService.link=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.record.RecordService.unlink=RM_ALLOW
+ org.alfresco.module.org_alfresco_module_rm.record.RecordService.complete=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.record.RecordService.*=RM_DENY
]]>
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java
index 180ef6b895..2042701e27 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/action/impl/DeclareRecordAction.java
@@ -27,33 +27,14 @@
package org.alfresco.module.org_alfresco_module_rm.action.impl;
-import static org.alfresco.module.org_alfresco_module_rm.record.RecordUtils.generateRecordIdentifier;
-
-import java.io.Serializable;
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase;
-import org.alfresco.module.org_alfresco_module_rm.record.RecordServiceImpl;
-import org.alfresco.module.org_alfresco_module_rm.util.TransactionalResourceHelper;
+import org.alfresco.module.org_alfresco_module_rm.record.RecordMissingMetadataException;
+import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
-import org.alfresco.repo.security.authentication.AuthenticationUtil;
-import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
+import org.alfresco.repo.node.integrity.IntegrityException;
import org.alfresco.service.cmr.action.Action;
-import org.alfresco.service.cmr.dictionary.AspectDefinition;
-import org.alfresco.service.cmr.dictionary.PropertyDefinition;
-import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
-import org.alfresco.service.cmr.security.OwnableService;
-import org.alfresco.service.namespace.QName;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.extensions.surf.util.I18NUtil;
+import org.alfresco.util.ParameterCheck;
/**
* Declare record action
@@ -62,36 +43,22 @@ import org.springframework.extensions.surf.util.I18NUtil;
*/
public class DeclareRecordAction extends RMActionExecuterAbstractBase
{
+ private static final String MISSING_PROPERTIES = "missingProperties";
+
/** action name */
public static final String NAME = "declareRecord";
- /** I18N */
- private static final String MSG_UNDECLARED_ONLY_RECORDS = "rm.action.undeclared-only-records";
- private static final String MSG_NO_DECLARE_MAND_PROP = "rm.action.no-declare-mand-prop";
-
- /** Logger */
- private static Log logger = LogFactory.getLog(DeclareRecordAction.class);
-
- /** check mandatory properties */
- private boolean checkMandatoryPropertiesEnabled = true;
-
- /** transactional resource helper */
- private TransactionalResourceHelper transactionalResourceHelper;
+ /** Record service */
+ private RecordService recordService;
/**
- * @param checkMandatoryPropertiesEnabled true if check mandatory properties is enabled, false otherwise
+ * Sets the record service
+ *
+ * @param recordService record service
*/
- public void setCheckMandatoryPropertiesEnabled(boolean checkMandatoryPropertiesEnabled)
+ public void setRecordService(RecordService recordService)
{
- this.checkMandatoryPropertiesEnabled = checkMandatoryPropertiesEnabled;
- }
-
- /**
- * @param transactionalResourceHelper
- */
- public void setTransactionalResourceHelper(TransactionalResourceHelper transactionalResourceHelper)
- {
- this.transactionalResourceHelper = transactionalResourceHelper;
+ this.recordService = recordService;
}
/**
@@ -100,167 +67,20 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase
@Override
protected void executeImpl(final Action action, final NodeRef actionedUponNodeRef)
{
- if (getNodeService().exists(actionedUponNodeRef) &&
- getRecordService().isRecord(actionedUponNodeRef) &&
- !getFreezeService().isFrozen(actionedUponNodeRef))
+ ParameterCheck.mandatory("actionedUponNodeRef", actionedUponNodeRef);
+ try
{
- if (!getRecordService().isDeclared(actionedUponNodeRef))
- {
- // if the record is newly created make sure the record identifier is set before completing the record
- Set newRecords = transactionalResourceHelper.getSet(RecordServiceImpl.KEY_NEW_RECORDS);
- if(newRecords.contains(actionedUponNodeRef))
- {
- generateRecordIdentifier(getNodeService(), getIdentifierService(), actionedUponNodeRef);
- }
-
- List missingProperties = new ArrayList<>(5);
- // Aspect not already defined - check mandatory properties then add
- if (!checkMandatoryPropertiesEnabled ||
- mandatoryPropertiesSet(actionedUponNodeRef, missingProperties))
- {
- getRecordService().disablePropertyEditableCheck();
- try
- {
- // Add the declared aspect
- Map declaredProps = new HashMap<>(2);
- declaredProps.put(PROP_DECLARED_AT, new Date());
- declaredProps.put(PROP_DECLARED_BY, AuthenticationUtil.getRunAsUser());
- this.getNodeService().addAspect(actionedUponNodeRef, ASPECT_DECLARED_RECORD, declaredProps);
-
- AuthenticationUtil.runAsSystem(new RunAsWork()
- {
- @Override
- public Void doWork()
- {
- // remove all owner related rights
- getOwnableService().setOwner(actionedUponNodeRef, OwnableService.NO_OWNER);
- return null;
- }
- });
- }
- finally
- {
- getRecordService().enablePropertyEditableCheck();
- }
- }
- else
- {
- logger.debug(buildMissingPropertiesErrorString(missingProperties));
- action.setParameterValue(ActionExecuterAbstractBase.PARAM_RESULT, "missingProperties");
- }
- }
+ recordService.complete(actionedUponNodeRef);
}
- else
+ catch (RecordMissingMetadataException e)
{
- if (logger.isWarnEnabled())
- {
- logger.warn(I18NUtil.getMessage(MSG_UNDECLARED_ONLY_RECORDS, actionedUponNodeRef.toString()));
- }
+ action.setParameterValue(ActionExecuterAbstractBase.PARAM_RESULT, MISSING_PROPERTIES);
}
+ catch (IntegrityException e)
+ {
+ // IntegrityException is deliberately ignored here, there should be no action taken
+ }
+
}
-
- private String buildMissingPropertiesErrorString(List missingProperties)
- {
- StringBuilder builder = new StringBuilder(255);
- builder.append(I18NUtil.getMessage(MSG_NO_DECLARE_MAND_PROP));
- builder.append(" ");
- for (String missingProperty : missingProperties)
- {
- builder.append(missingProperty).append(", ");
- }
- return builder.toString();
- }
-
- /**
- * Helper method to check whether all the mandatory properties of the node have been set
- *
- * @param nodeRef node reference
- * @return boolean true if all mandatory properties are set, false otherwise
- */
- private boolean mandatoryPropertiesSet(NodeRef nodeRef, List missingProperties)
- {
- boolean result = true;
-
- Map nodeRefProps = this.getNodeService().getProperties(nodeRef);
-
- QName nodeRefType = this.getNodeService().getType(nodeRef);
-
- TypeDefinition typeDef = this.getDictionaryService().getType(nodeRefType);
- for (PropertyDefinition propDef : typeDef.getProperties().values())
- {
- if (propDef.isMandatory() && nodeRefProps.get(propDef.getName()) == null)
- {
- logMissingProperty(propDef, missingProperties);
-
- result = false;
- break;
- }
- }
-
- if (result)
- {
- Set aspects = this.getNodeService().getAspects(nodeRef);
- for (QName aspect : aspects)
- {
- AspectDefinition aspectDef = this.getDictionaryService().getAspect(aspect);
- for (PropertyDefinition propDef : aspectDef.getProperties().values())
- {
- if (propDef.isMandatory() && nodeRefProps.get(propDef.getName()) == null)
- {
- logMissingProperty(propDef, missingProperties);
-
- result = false;
- break;
- }
- }
- }
- }
-
- // check for missing mandatory metadata from custom aspect definitions
- if (result)
- {
- QName aspect = ASPECT_RECORD;
- if (nodeRefType.equals(TYPE_NON_ELECTRONIC_DOCUMENT))
- {
- aspect = TYPE_NON_ELECTRONIC_DOCUMENT;
- }
-
- // get customAspectImpl
- String localName = aspect.toPrefixString(getNamespaceService()).replace(":", "");
- localName = MessageFormat.format("{0}CustomProperties", localName);
- QName customAspect = QName.createQName(RM_CUSTOM_URI, localName);
-
- AspectDefinition aspectDef = this.getDictionaryService().getAspect(customAspect);
-
- for (PropertyDefinition propDef : aspectDef.getProperties().values())
- {
- if (propDef.isMandatory() && nodeRefProps.get(propDef.getName()) == null)
- {
- logMissingProperty(propDef, missingProperties);
-
- result = false;
- break;
- }
- }
- }
-
- return result;
- }
-
- /**
- * Log information about missing properties.
- *
- * @param propDef property definition
- * @param missingProperties missing properties
- */
- private void logMissingProperty(PropertyDefinition propDef, List missingProperties)
- {
- if (logger.isWarnEnabled())
- {
- StringBuilder msg = new StringBuilder();
- msg.append("Mandatory property missing: ").append(propDef.getName());
- logger.warn(msg.toString());
- }
- missingProperties.add(propDef.getName().toString());
- }
+
}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordMissingMetadataException.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordMissingMetadataException.java
new file mode 100644
index 0000000000..74fbacd567
--- /dev/null
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordMissingMetadataException.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.module.org_alfresco_module_rm.record;
+
+import java.io.Serializable;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+
+/**
+ * Record missing metadata exception class
+ *
+ * @author Sara Aspery
+ * @since 2.6
+ */
+public class RecordMissingMetadataException extends AlfrescoRuntimeException
+{
+ private static final long serialVersionUID = 3437687270967611234L;
+
+ public RecordMissingMetadataException(String msgId, Throwable cause)
+ {
+ super(msgId, cause);
+ }
+
+ public RecordMissingMetadataException(String msgId, Object[] msgParams, Throwable cause)
+ {
+ super(msgId, msgParams, cause);
+ }
+
+ public RecordMissingMetadataException(String msgId, Object[] msgParams)
+ {
+ super(msgId, msgParams);
+ }
+
+ public RecordMissingMetadataException(String msgId)
+ {
+ super(msgId);
+ }
+}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java
index 52c2711314..a54119ffe9 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordService.java
@@ -201,7 +201,7 @@ public interface RecordService
/**
* Indicates whether the record is filed or not
*
- * @param nodeRef record
+ * @param record nodeRef of record
* @return boolean true if filed, false otherwise
*/
boolean isFiled(NodeRef record);
@@ -209,7 +209,7 @@ public interface RecordService
/**
* 'File' a new document that arrived in the file plan structure.
*
- * @param nodeRef record
+ * @param record noderef of record
*/
void file(NodeRef record);
@@ -278,4 +278,11 @@ public interface RecordService
* @since 2.3
*/
void unlink(NodeRef record, NodeRef recordFolder);
+
+ /**
+ * Completes a record
+ *
+ * @param nodeRef Record node reference
+ */
+ void complete(NodeRef nodeRef);
}
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java
index 85f3a01d5e..8da0e40161 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/module/org_alfresco_module_rm/record/RecordServiceImpl.java
@@ -34,6 +34,7 @@ import static org.alfresco.repo.policy.Behaviour.NotificationFrequency.TRANSACTI
import static org.alfresco.repo.policy.annotation.BehaviourKind.ASSOCIATION;
import java.io.Serializable;
+import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -62,6 +63,7 @@ import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedul
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
import org.alfresco.module.org_alfresco_module_rm.dod5015.DOD5015Model;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
+import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService;
import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService;
import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel;
@@ -81,9 +83,10 @@ import org.alfresco.module.org_alfresco_module_rm.version.RecordableVersionServi
import org.alfresco.repo.content.ContentServicePolicies;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.node.integrity.IncompleteNodeTagger;
+import org.alfresco.repo.node.integrity.IntegrityException;
+import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.ClassPolicyDelegate;
import org.alfresco.repo.policy.PolicyComponent;
-import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.annotation.Behaviour;
import org.alfresco.repo.policy.annotation.BehaviourBean;
import org.alfresco.repo.policy.annotation.BehaviourKind;
@@ -94,6 +97,7 @@ import org.alfresco.repo.security.permissions.impl.ExtendedPermissionService;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
+import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.model.FileNotFoundException;
@@ -158,6 +162,8 @@ public class RecordServiceImpl extends BaseBehaviourBean
private static final String MSG_NODE_HAS_ASPECT = "rm.service.node-has-aspect";
private static final String FINAL_VERSION = "rm.service.final-version";
private static final String FINAL_DESCRIPTION = "rm.service.final-version-description";
+ private static final String MSG_UNDECLARED_ONLY_RECORDS = "rm.action.undeclared-only-records";
+ private static final String MSG_NO_DECLARE_MAND_PROP = "rm.action.no-declare-mand-prop";
/** Always edit property array */
private static final QName[] ALWAYS_EDIT_PROPERTIES = new QName[]
@@ -168,6 +174,11 @@ public class RecordServiceImpl extends BaseBehaviourBean
/** always edit model URI's */
private List alwaysEditURIs;
+ /**
+ * check mandatory properties
+ */
+ private boolean checkMandatoryPropertiesEnabled = true;
+
/**
* @param alwaysEditURIs the alwaysEditURIs to set
*/
@@ -263,6 +274,12 @@ public class RecordServiceImpl extends BaseBehaviourBean
/** list of available record meta-data aspects and the file plan types the are applicable to */
private Map> recordMetaDataAspects;
+ /** Freeze service */
+ private FreezeService freezeService;
+
+ /** Namespace service */
+ private NamespaceService namespaceService;
+
/** policies */
private ClassPolicyDelegate beforeFileRecord;
private ClassPolicyDelegate onFileRecord;
@@ -422,6 +439,30 @@ public class RecordServiceImpl extends BaseBehaviourBean
this.incompleteNodeTagger = incompleteNodeTagger;
}
+ /**
+ * @param freezeService freeze service
+ */
+ public void setFreezeService(FreezeService freezeService)
+ {
+ this.freezeService = freezeService;
+ }
+
+ /**
+ * @param namespaceService namespace service
+ */
+ public void setNamespaceService(NamespaceService namespaceService)
+ {
+ this.namespaceService = namespaceService;
+ }
+
+ /**
+ * @param checkMandatoryPropertiesEnabled true if check mandatory properties is enabled, false otherwise
+ */
+ public void setCheckMandatoryPropertiesEnabled(boolean checkMandatoryPropertiesEnabled)
+ {
+ this.checkMandatoryPropertiesEnabled = checkMandatoryPropertiesEnabled;
+ }
+
/**
* Init method
*/
@@ -1922,4 +1963,173 @@ public class RecordServiceImpl extends BaseBehaviourBean
incompleteNodeTagger.beforeCommit(false);
}
}
-}
+
+ /**
+ * Completes a record
+ *
+ * @param nodeRef Record node reference
+ */
+ @Override
+ public void complete(NodeRef nodeRef)
+ {
+ validateForCompletion(nodeRef);
+ disablePropertyEditableCheck();
+ try
+ {
+ // Add the declared aspect
+ Map declaredProps = new HashMap<>(2);
+ declaredProps.put(PROP_DECLARED_AT, new Date());
+ declaredProps.put(PROP_DECLARED_BY, AuthenticationUtil.getRunAsUser());
+ nodeService.addAspect(nodeRef, ASPECT_DECLARED_RECORD, declaredProps);
+
+ AuthenticationUtil.runAsSystem(new RunAsWork()
+ {
+ @Override
+ public Void doWork()
+ {
+ // remove all owner related rights
+ ownableService.setOwner(nodeRef, OwnableService.NO_OWNER);
+ return null;
+ }
+ });
+ }
+ finally
+ {
+ enablePropertyEditableCheck();
+ }
+ }
+
+ /**
+ * Helper method to validate whether the node is in a state suitable for completion
+ *
+ * @param nodeRef node reference
+ * @throws Exception if node not valid for completion
+ */
+ private void validateForCompletion(NodeRef nodeRef) {
+ if (!nodeService.exists(nodeRef))
+ {
+ LOGGER.warn(I18NUtil.getMessage(MSG_UNDECLARED_ONLY_RECORDS, nodeRef.toString()));
+ throw new IntegrityException("The record does not exist.", null);
+ }
+
+ if (!isRecord(nodeRef))
+ {
+ LOGGER.warn(I18NUtil.getMessage(MSG_UNDECLARED_ONLY_RECORDS, nodeRef.toString()));
+ throw new IntegrityException("The node is not a record.", null);
+ }
+
+ if (freezeService.isFrozen(nodeRef))
+ {
+ LOGGER.warn(I18NUtil.getMessage(MSG_UNDECLARED_ONLY_RECORDS, nodeRef.toString()));
+ throw new IntegrityException("The record is frozen.", null);
+ }
+
+ if (isDeclared(nodeRef))
+ {
+ throw new IntegrityException("The record is already completed.", null);
+ }
+
+ // if the record is newly created make sure the record identifier is set before completing the record
+ Set newRecords = transactionalResourceHelper.getSet(RecordServiceImpl.KEY_NEW_RECORDS);
+ if (newRecords.contains(nodeRef))
+ {
+ generateRecordIdentifier(nodeService, identifierService, nodeRef);
+ }
+
+ // Validate that all mandatory properties, if any, are present
+ List missingProperties = new ArrayList<>(5);
+ // Aspect not already defined - check mandatory properties then add
+ if (checkMandatoryPropertiesEnabled)
+ {
+ Map nodeRefProps = nodeService.getProperties(nodeRef);
+ QName nodeRefType = nodeService.getType(nodeRef);
+
+ // check for missing mandatory metadata from type definitions
+ TypeDefinition typeDef = dictionaryService.getType(nodeRefType);
+ checkDefinitionMandatoryPropsSet(typeDef, nodeRefProps, missingProperties);
+
+ // check for missing mandatory metadata from aspect definitions
+ Set aspects = nodeService.getAspects(nodeRef);
+ for (QName aspect : aspects)
+ {
+ AspectDefinition aspectDef = dictionaryService.getAspect(aspect);
+ checkDefinitionMandatoryPropsSet(aspectDef, nodeRefProps, missingProperties);
+ }
+
+ // check for missing mandatory metadata from custom aspect definitions
+ QName customAspect = getCustomAspectImpl(nodeRefType);
+ AspectDefinition aspectDef = dictionaryService.getAspect(customAspect);
+ checkDefinitionMandatoryPropsSet(aspectDef, nodeRefProps, missingProperties);
+
+ if (!missingProperties.isEmpty())
+ {
+ LOGGER.debug(buildMissingPropertiesErrorString(missingProperties));
+ throw new RecordMissingMetadataException("The record has missing mandatory properties.");
+ }
+ }
+ }
+
+ /**
+ * Helper method to build single string containing list of missing properties
+ *
+ * @param missingProperties list of missing properties
+ * @return String of missing properties
+ */
+ private String buildMissingPropertiesErrorString(List missingProperties)
+ {
+ StringBuilder builder = new StringBuilder(255);
+ builder.append(I18NUtil.getMessage(MSG_NO_DECLARE_MAND_PROP));
+ builder.append(" ");
+ for (String missingProperty : missingProperties)
+ {
+ builder.append(missingProperty).append(", ");
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Helper method to check whether all the definition mandatory properties of the node have been set
+ *
+ * @param classDef the ClassDefinition defining the properties to be checked
+ * @param nodeRefProps the properties of the node to be checked
+ * @param missingProperties the list of mandatory properties found to be missing (currently only the first one)
+ * @return boolean true if all mandatory properties are set, false otherwise
+ */
+ private void checkDefinitionMandatoryPropsSet(final ClassDefinition classDef, final Map nodeRefProps,
+ final List missingProperties)
+ {
+ for (PropertyDefinition propDef : classDef.getProperties().values())
+ {
+ if (propDef.isMandatory() && nodeRefProps.get(propDef.getName()) == null)
+ {
+ if (LOGGER.isWarnEnabled())
+ {
+ StringBuilder msg = new StringBuilder();
+ msg.append("Mandatory property missing: ").append(propDef.getName());
+ LOGGER.warn(msg.toString());
+ }
+ missingProperties.add(propDef.getName().toString());
+ }
+ }
+ }
+
+ /**
+ * Helper method to get the custom aspect for a given nodeRef type
+ *
+ * @param nodeRefType the node type for which to return custom aspect QName
+ * @return QName custom aspect
+ */
+ private QName getCustomAspectImpl(QName nodeRefType)
+ {
+ QName aspect = ASPECT_RECORD;
+ if (nodeRefType.equals(TYPE_NON_ELECTRONIC_DOCUMENT))
+ {
+ aspect = TYPE_NON_ELECTRONIC_DOCUMENT;
+ }
+
+ // get customAspectImpl
+ String localName = aspect.toPrefixString(namespaceService).replace(":", "");
+ localName = MessageFormat.format("{0}CustomProperties", localName);
+ return QName.createQName(RM_CUSTOM_URI, localName);
+ }
+}
\ No newline at end of file
diff --git a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/RecordsEntityResource.java b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/RecordsEntityResource.java
index 4aef34e7d0..daf676ecdb 100644
--- a/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/RecordsEntityResource.java
+++ b/rm-community/rm-community-repo/source/java/org/alfresco/rm/rest/api/records/RecordsEntityResource.java
@@ -31,6 +31,7 @@ import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.c
import static org.alfresco.util.ParameterCheck.mandatory;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
+import org.alfresco.module.org_alfresco_module_rm.record.RecordMissingMetadataException;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.repo.activities.ActivityType;
import org.alfresco.repo.node.integrity.IntegrityException;
@@ -230,6 +231,31 @@ public class RecordsEntityResource implements BinaryResourceAction.Read,
return nodesModelFactory.createRecord(info, parameters, null, false);
}
+ @Operation ("complete")
+ @WebApiDescription (title = "Complete record", description = "Complete a record.")
+ public Record completeRecord(String recordId, Void body, Parameters parameters, WithResponse withResponse)
+ {
+ checkNotBlank("recordId", recordId);
+ mandatory("parameters", parameters);
+
+ // Get record
+ NodeRef record = apiUtils.validateRecord(recordId);
+
+ // Complete the record
+ try
+ {
+ recordService.complete(record);
+ }
+ catch (RecordMissingMetadataException e)
+ {
+ throw new IntegrityException("The record has missing mandatory properties.", null);
+ }
+
+ // return record state
+ FileInfo info = fileFolderService.getFileInfo(record);
+ return nodesModelFactory.createRecord(info, parameters, null, false);
+ }
+
@Override
@WebApiDescription(title = "Delete record", description="Deletes a record with id 'recordId'")
public void delete(String recordId, Parameters parameters)
diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java
index b73ecc6f78..7642f101bb 100644
--- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java
+++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/record/CompleteRecordTest.java
@@ -32,8 +32,8 @@ import java.util.HashMap;
import java.util.Map;
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionResult;
-import org.alfresco.module.org_alfresco_module_rm.action.impl.DeclareRecordAction;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementCustomModel;
+import org.alfresco.module.org_alfresco_module_rm.record.RecordServiceImpl;
import org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -55,9 +55,9 @@ public class CompleteRecordTest extends BaseRMTestCase
private static final boolean OPTIONAL_METADATA = false;
/**
- * complete record action
+ * Record service impl
*/
- private DeclareRecordAction action;
+ private RecordServiceImpl recordServiceImpl;
/**
* @see org.alfresco.module.org_alfresco_module_rm.test.util.BaseRMTestCase#initServices()
@@ -67,8 +67,8 @@ public class CompleteRecordTest extends BaseRMTestCase
{
super.initServices();
- // get the action
- action = (DeclareRecordAction) applicationContext.getBean("declareRecord");
+ // get the record service
+ recordServiceImpl = (RecordServiceImpl) applicationContext.getBean("recordService");
}
/**
@@ -80,7 +80,7 @@ public class CompleteRecordTest extends BaseRMTestCase
super.tearDownImpl();
// ensure action is returned to original state
- action.setCheckMandatoryPropertiesEnabled(true);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(true);
}
/**
@@ -100,7 +100,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given()
{
// enable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(true);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(true);
// create a record
record = utils.createRecord(rmFolder, "record.txt", "title");
@@ -140,7 +140,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given()
{
// enable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(true);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(true);
// create a record
record = utils.createRecord(rmFolder, "record.txt", "title");
@@ -183,7 +183,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given() throws Exception
{
// enable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(true);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(true);
// create the custom metadata definition (that has a mandatory property) for electronic records
defineCustomMetadata(CUSTOM_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA);
@@ -229,7 +229,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given() throws Exception
{
// enable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(true);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(true);
// define the custom metadata definition (that has a mandatory property)
defineCustomMetadata(CUSTOM_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA);
@@ -279,7 +279,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given() throws Exception
{
// enable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(true);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(true);
// create the custom metadata definition (that has a mandatory property) for non-electronic records
defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, TYPE_NON_ELECTRONIC_DOCUMENT, MANDATORY_METADATA);
@@ -326,7 +326,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given() throws Exception
{
// enable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(true);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(true);
// create the custom metadata definition (that has a mandatory property)
defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, TYPE_NON_ELECTRONIC_DOCUMENT, MANDATORY_METADATA);
@@ -375,7 +375,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given() throws Exception
{
// enable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(true);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(true);
// create the custom metadata definition (that does not have a mandatory property)
defineCustomMetadata(CUSTOM_ELECTRONIC_TEST, ASPECT_RECORD, OPTIONAL_METADATA);
@@ -422,7 +422,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given() throws Exception
{
// enable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(true);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(true);
// create the record custom metadata definition (that has a mandatory property)
defineCustomMetadata(CUSTOM_ELECTRONIC_TEST, ASPECT_RECORD, MANDATORY_METADATA);
@@ -469,7 +469,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given() throws Exception
{
// enable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(true);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(true);
// create the non-electronic record custom metadata definition (that has a mandatory property)
defineCustomMetadata(CUSTOM_NON_ELECTRONIC_TEST, TYPE_NON_ELECTRONIC_DOCUMENT, MANDATORY_METADATA);
@@ -515,7 +515,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given()
{
// disable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(false);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(false);
// create a record
record = utils.createRecord(rmFolder, "record.txt", "title");
@@ -555,7 +555,7 @@ public class CompleteRecordTest extends BaseRMTestCase
public void given()
{
// enable mandatory parameter check
- action.setCheckMandatoryPropertiesEnabled(false);
+ recordServiceImpl.setCheckMandatoryPropertiesEnabled(false);
// create a record
record = utils.createRecord(rmFolder, "record.txt", "title");
diff --git a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/rule/FilePlanRuleInheritanceTest.java b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/rule/FilePlanRuleInheritanceTest.java
index b465953eae..857310927d 100644
--- a/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/rule/FilePlanRuleInheritanceTest.java
+++ b/rm-community/rm-community-repo/test/java/org/alfresco/module/org_alfresco_module_rm/test/integration/rule/FilePlanRuleInheritanceTest.java
@@ -76,6 +76,7 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
doBehaviourDrivenTest(new BehaviourDrivenTest()
{
private NodeRef filePlan = null;
+ private Rule rule = null;
private List rules = null;
public void given()
@@ -83,24 +84,24 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
filePlan = createFilePlan();
// create a rule that applies to childre
- Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
- Rule rule = new Rule();
+ Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
+ rule = new Rule();
rule.setRuleType("inbound");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
-
+ }
+
+ public void when()
+ {
// save rule on file plan root parent folder
ruleService.saveRule(folder, rule);
}
-
- public void when()
+
+ public void then()
{
// get rules, including those inherited
rules = ruleService.getRules(filePlan, true);
- }
-
- public void then()
- {
+
// rules aren't inhreited from file plan root parent folder
assertEquals(0, rules.size());
}
@@ -121,31 +122,32 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
{
private NodeRef filePlan = null;
private List rules = null;
-
+ private Rule rule = null;
+
public void given()
{
filePlan = createFilePlan();
// create a rule that applies to childre
- Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
- Rule rule = new Rule();
+ Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
+ rule = new Rule();
rule.setRuleType("inbound");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
-
+ }
+
+ public void when()
+ {
// save rule on file plan root
ruleService.saveRule(filePlan, rule);
}
-
- public void when()
+
+ public void then()
{
// get rules, including those inherited
NodeRef unfiledRecordContainer = filePlanService.getUnfiledContainer(filePlan);
rules = ruleService.getRules(unfiledRecordContainer, true);
- }
-
- public void then()
- {
+
// rules aren't inhreited from file plan root
assertEquals(0, rules.size());
}
@@ -164,31 +166,32 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
{
private NodeRef filePlan = null;
private List rules = null;
-
+ private Rule rule = null;
+
public void given()
{
filePlan = createFilePlan();
// create a rule that applies to childre
- Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
- Rule rule = new Rule();
+ Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
+ rule = new Rule();
rule.setRuleType("inbound");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
-
+ }
+
+ public void when()
+ {
// save rule on file plan root
ruleService.saveRule(filePlan, rule);
}
-
- public void when()
+
+ public void then()
{
// get rules, including those inherited
NodeRef container = filePlanService.getHoldContainer(filePlan);
rules = ruleService.getRules(container, true);
- }
-
- public void then()
- {
+
// rules aren't inhreited from file plan root
assertEquals(0, rules.size());
}
@@ -207,31 +210,32 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
{
private NodeRef filePlan = null;
private List rules = null;
-
+ private Rule rule = null;
+
public void given()
{
filePlan = createFilePlan();
// create a rule that applies to childre
- Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
- Rule rule = new Rule();
+ Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
+ rule = new Rule();
rule.setRuleType("inbound");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
-
+ }
+
+ public void when()
+ {
// save rule on file plan root
ruleService.saveRule(filePlan, rule);
}
-
- public void when()
+
+ public void then()
{
// get rules, including those inherited
NodeRef container = filePlanService.getTransferContainer(filePlan);
rules = ruleService.getRules(container, true);
- }
-
- public void then()
- {
+
// rules aren't inhreited from file plan root
assertEquals(0, rules.size());
}
@@ -251,31 +255,32 @@ public class FilePlanRuleInheritanceTest extends BaseRMTestCase
private NodeRef filePlan = null;
private NodeRef recordCategory = null;
private List rules = null;
-
+ private Rule rule = null;
+
public void given()
{
filePlan = createFilePlan();
recordCategory = filePlanService.createRecordCategory(filePlan, GUID.generate());
// create a rule that applies to childre
- Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
- Rule rule = new Rule();
+ Action completeRecordAction = actionService.createAction(DeclareRecordAction.NAME);
+ rule = new Rule();
rule.setRuleType("inbound");
rule.setAction(completeRecordAction);
rule.applyToChildren(true);
-
+ }
+
+ public void when()
+ {
// save rule on file plan root
ruleService.saveRule(filePlan, rule);
}
-
- public void when()
+
+ public void then()
{
// get rules, including those inherited
rules = ruleService.getRules(recordCategory, true);
- }
-
- public void then()
- {
+
// rules aren't inhreited from file plan root
assertEquals(1, rules.size());
}
diff --git a/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml b/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml
index ff1ee40224..09f746fbf1 100644
--- a/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml
+++ b/rm-community/rm-community-rest-api-explorer/src/main/webapp/definitions/gs-core-api.yaml
@@ -1795,6 +1795,47 @@ paths:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
+ '/records/{recordId}/complete':
+ post:
+ tags:
+ - records
+ summary: Complete a record
+ description: |
+ Completes the record **recordId**.
+ operationId: completeRecord
+ parameters:
+ - $ref: '#/parameters/recordIdParam'
+ - $ref: '#/parameters/recordEntryIncludeParam'
+ - $ref: '#/parameters/fieldsParam'
+ consumes:
+ - application/json
+ produces:
+ - application/json
+ responses:
+ '200':
+ description: Successful response
+ schema:
+ $ref: '#/definitions/RecordEntry'
+ '400':
+ description: |
+ Invalid parameter: **recordIdParam** is not a valid format or
+ **recordIdParam** is not a record
+ '401':
+ description: Authentication failed
+ '403':
+ description: >-
+ Current user does not have permission to complete record
+ **recordIdParam**
+ '404':
+ description: |
+ **recordIdParam** does not exist
+ '422':
+ description: |
+ Model integrity exception: the action breaks system's integrity restrictions
+ default:
+ description: Unexpected error
+ schema:
+ $ref: '#/definitions/Error'
## Files
'/files/{fileId}/declare':
post: