[APPS-2838][APPS-2839] POST and GET API implementation for the Retention Schedule Steps (#2721)

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

* [APPS-2838][APPS-2839] Fixed Junit Testcases

* [APPS-2838][APPS-2839] Retention Schedule Step POST and GET API

---------

Co-authored-by: Sathish Kumar <ST28@ford.com>
Co-authored-by: suneet-gupta <suneet.gupta@hyland.com>
This commit is contained in:
SathishK-T
2024-06-28 17:26:17 +05:30
committed by GitHub
parent 8e15dba3eb
commit 92fbaf29d4
18 changed files with 1196 additions and 38 deletions

View File

@@ -35,9 +35,12 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinitionImpl;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent;
@@ -56,6 +59,8 @@ import org.alfresco.rm.rest.api.model.Record;
import org.alfresco.rm.rest.api.model.RecordCategory;
import org.alfresco.rm.rest.api.model.RecordCategoryChild;
import org.alfresco.rm.rest.api.model.RecordFolder;
import org.alfresco.rm.rest.api.model.RetentionPeriod;
import org.alfresco.rm.rest.api.model.RetentionSteps;
import org.alfresco.rm.rest.api.model.Transfer;
import org.alfresco.rm.rest.api.model.TransferChild;
import org.alfresco.rm.rest.api.model.TransferContainer;
@@ -75,8 +80,9 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility class containing Alfresco and RM java services required by the API
@@ -88,6 +94,9 @@ import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagement
public class ApiNodesModelFactory
{
/** Logger */
private static final Logger LOGGER = LoggerFactory.getLogger(ApiNodesModelFactory.class);
// excluded namespaces (aspects, properties, assoc types)
public static final List<String> EXCLUDED_NS = Arrays.asList(NamespaceService.SYSTEM_MODEL_1_0_URI);
@@ -109,6 +118,7 @@ public class ApiNodesModelFactory
private PersonService personService;
private DispositionService dispositionService;
private ServiceRegistry serviceRegistry;
private RecordsManagementServiceRegistry services;
public NodeService getNodeService()
{
@@ -160,6 +170,11 @@ public class ApiNodesModelFactory
this.serviceRegistry = serviceRegistry;
}
public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry services)
{
this.services = services;
}
/**
* Helper method that sets the basic information for most of the node types.
*
@@ -511,15 +526,15 @@ public class ApiNodesModelFactory
}
if(RecordsManagementModel.TYPE_RECORD_FOLDER.equals(info.getType()))
{
if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)))
if (isRecordFolder(isMinimalInfo, propertyFilter, includeParam))
{
recordCategoryChild.setIsRecordFolder(true);
}
if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)))
if (isRecordCategory(isMinimalInfo, propertyFilter, includeParam))
{
recordCategoryChild.setIsRecordCategory(false);
}
if((!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_IS_CLOSED)) || (isMinimalInfo && includeParam.contains(RMNode.PARAM_IS_CLOSED)))
if (isRecordCategoryChildClosed(isMinimalInfo, propertyFilter, includeParam))
{
recordCategoryChild.setIsClosed((Boolean) nodeService.getProperty(info.getNodeRef(), RecordsManagementModel.PROP_IS_CLOSED));
}
@@ -530,11 +545,11 @@ public class ApiNodesModelFactory
}
else
{
if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)))
if (isRecordFolder(isMinimalInfo, propertyFilter, includeParam))
{
recordCategoryChild.setIsRecordFolder(false);
}
if((!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)))
if (isRecordCategory(isMinimalInfo, propertyFilter, includeParam))
{
recordCategoryChild.setIsRecordCategory(true);
}
@@ -543,13 +558,28 @@ public class ApiNodesModelFactory
DispositionSchedule ds = dispositionService.getDispositionSchedule(info.getNodeRef());
recordCategoryChild.setHasRetentionSchedule(ds != null);
}
if((!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_IS_CLOSED)) || (isMinimalInfo && includeParam.contains(RMNode.PARAM_IS_CLOSED)))
if (isRecordCategoryChildClosed(isMinimalInfo, propertyFilter, includeParam))
{
recordCategoryChild.setIsClosed(null);
}
}
}
private boolean isRecordCategoryChildClosed(boolean isMinimalInfo, BeanPropertiesFilter propertyFilter, List<String> includeParam)
{
return (!isMinimalInfo && propertyFilter.isAllowed(RMNode.PARAM_IS_CLOSED)) || (isMinimalInfo && includeParam.contains(RMNode.PARAM_IS_CLOSED));
}
private boolean isRecordCategory(boolean isMinimalInfo, BeanPropertiesFilter propertyFilter, List<String> includeParam)
{
return (!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_CATEGORY));
}
private boolean isRecordFolder(boolean isMinimalInfo, BeanPropertiesFilter propertyFilter, List<String> includeParam)
{
return (!isMinimalInfo && propertyFilter.isAllowed(RecordCategoryChild.PARAM_IS_RECORD_FOLDER)) || (isMinimalInfo && includeParam.contains(RecordCategoryChild.PARAM_IS_RECORD_FOLDER));
}
/**
* Utility method that maps record specific fields
@@ -572,7 +602,8 @@ public class ApiNodesModelFactory
{
Serializable val = info.getProperties().get(ContentModel.PROP_CONTENT);
if ((val != null) && (val instanceof ContentData)) {
if (val instanceof ContentData)
{
ContentData cd = (ContentData)val;
String mimeType = cd.getMimetype();
String mimeTypeName = serviceRegistry.getMimetypeService().getDisplaysByMimetype().get(mimeType);
@@ -914,7 +945,7 @@ public class ApiNodesModelFactory
}
retentionSchedule.setInstructions(dispositionSchedule.getDispositionInstructions());
retentionSchedule.setAuthority(dispositionSchedule.getDispositionAuthority());
retentionSchedule.setRecordLevel(dispositionSchedule.isRecordLevelDisposition());
retentionSchedule.setIsRecordLevel(dispositionSchedule.isRecordLevelDisposition());
boolean unpublishedUpdates = dispositionSchedule.getDispositionActionDefinitions().stream()
.map(DispositionActionDefinition::getNodeRef)
@@ -951,9 +982,9 @@ public class ApiNodesModelFactory
retentionScheduleActionDefinition.setName(dispositionActionDefinition.getName());
retentionScheduleActionDefinition.setDescription(dispositionActionDefinition.getDescription());
retentionScheduleActionDefinition.setEligibleOnFirstCompleteEvent(dispositionActionDefinition.eligibleOnFirstCompleteEvent());
if (nodeService.getProperty(dispositionActionDefinition.getNodeRef(), PROP_COMBINE_DISPOSITION_STEP_CONDITIONS) != null)
if (nodeService.getProperty(dispositionActionDefinition.getNodeRef(), RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS) != null)
{
retentionScheduleActionDefinition.setCombineDispositionStepConditions((Boolean) nodeService.getProperty(dispositionActionDefinition.getNodeRef(), PROP_COMBINE_DISPOSITION_STEP_CONDITIONS));
retentionScheduleActionDefinition.setCombineDispositionStepConditions((Boolean) nodeService.getProperty(dispositionActionDefinition.getNodeRef(), RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS));
}
retentionScheduleActionDefinition.setLocation(dispositionActionDefinition.getLocation());
if (dispositionActionDefinition.getGhostOnDestroy() != null)
@@ -970,9 +1001,11 @@ public class ApiNodesModelFactory
*/
private void mapPeriodProperties(DispositionActionDefinition dispositionActionDefinition, RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
if(dispositionActionDefinition.getPeriodProperty() != null) {
if(dispositionActionDefinition.getPeriodProperty() != null)
{
retentionScheduleActionDefinition.setPeriodProperty(dispositionActionDefinition.getPeriodProperty().toPrefixString(namespaceService));
}
String period = dispositionActionDefinition.getPeriod().toString();
if (!period.isEmpty())
{
@@ -982,19 +1015,22 @@ public class ApiNodesModelFactory
// period -> 'month'
// periodAmount -> 10
String[] periodArray = period.split("\\|");
if (periodArray.length > 0)
{
retentionScheduleActionDefinition.setPeriod(periodArray[0]);
}
if (periodArray.length > 1)
{
try
{
retentionScheduleActionDefinition.setPeriodAmount(Integer.parseInt(periodArray[1]));
}
catch (NumberFormatException e)
catch (NumberFormatException numberFormatException)
{
throw new NumberFormatException("Error parsing period amount: " + e.getMessage());
LOGGER.error("Error parsing period amount: {}{}", numberFormatException.getMessage(), periodArray[1], numberFormatException);
throw numberFormatException;
}
}
}
@@ -1033,4 +1069,83 @@ public class ApiNodesModelFactory
retentionSchedule.setActions(actions);
}
}
/**
* this method is used for creation of retention schedule action definition params
* @param nodeInfo retention schedule action definition
* @return Map<QName, Serializable>
*/
public Map<QName, Serializable> createRetentionActionDefinitionParams(RetentionScheduleActionDefinition nodeInfo)
{
Map<QName, Serializable> actionDefinitionParams= new HashMap<>();
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_NAME, nodeInfo.getName());
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_DESCRIPTION, nodeInfo.getDescription());
StringBuilder retentionPeriod = new StringBuilder(nodeInfo.getPeriod()).append("|");
if(isPeriodAmountApplicable(nodeInfo.getPeriod()))
{
retentionPeriod.append(nodeInfo.getPeriodAmount());
}
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD, retentionPeriod.toString());
QName periodProperty = QName.createQName(nodeInfo.getPeriodProperty(), namespaceService);
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_PERIOD_PROPERTY, periodProperty);
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_EVENT_COMBINATION,
nodeInfo.isEligibleOnFirstCompleteEvent());
actionDefinitionParams.put(RecordsManagementModel.PROP_COMBINE_DISPOSITION_STEP_CONDITIONS,
nodeInfo.isCombineDispositionStepConditions());
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_LOCATION,
nodeInfo.getLocation());
List<String> inputEvents = nodeInfo.getEvents();
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_EVENT, (Serializable) inputEvents);
if (RetentionSteps.DESTROY.stepName.equals(nodeInfo.getName()) && nodeInfo.isRetainRecordMetadataAfterDestruction())
{
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_GHOST_ON_DESTROY, "ghost");
}
else
{
actionDefinitionParams.put(RecordsManagementModel.PROP_DISPOSITION_ACTION_GHOST_ON_DESTROY, "delete");
}
return actionDefinitionParams;
}
/**
* this method is used retrieve retention schedule action details
* @param retentionScheduleNodeRef nodeRef
* @return List<DispositionActionDefinition>
*/
public List<DispositionActionDefinition> getRetentionActions(NodeRef retentionScheduleNodeRef)
{
List<ChildAssociationRef> assocs = nodeService.getChildAssocs(
retentionScheduleNodeRef,
RecordsManagementModel.ASSOC_DISPOSITION_ACTION_DEFINITIONS,
RegexQNamePattern.MATCH_ALL);
// we are getting disposition action definitions based on retention schedule child association.
// setting the index value for each action.
List<DispositionActionDefinition> actions;
actions = IntStream.range(0, assocs.size())
.mapToObj(index ->
{
ChildAssociationRef assoc = assocs.get(index);
return new DispositionActionDefinitionImpl(
services.getRecordsManagementEventService(),
services.getRecordsManagementActionService(),
nodeService,
assoc.getChildRef(),
index);
})
.collect(Collectors.toList());
return actions;
}
/**
* this method is used to check period amount applicable or not for particular period
* @param period period
* @return boolean
*/
private boolean isPeriodAmountApplicable(String period)
{
// periodAmount property only applicable for following periods
// day, week, month, quarter, year and duration
return period.equals(RetentionPeriod.DAY.periodName) || period.equals(RetentionPeriod.MONTH.periodName) || period.equals(RetentionPeriod.QUARTER.periodName)
|| period.equals(RetentionPeriod.WEEK.periodName) || period.equals(RetentionPeriod.XML_DURATION.periodName) || period.equals(RetentionPeriod.YEAR.periodName);
}
}

View File

@@ -0,0 +1,56 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
/**
* Retention event values
*/
public enum RetentionEvents
{
CASE_CLOSED("case_closed"),
ABOLISHED("abolished"),
RE_DESIGNATED("re_designated"),
NO_LONGER_NEEDED("no_longer_needed"),
SUPERSEDED("superseded"),
VERSIONED("versioned"),
STUDY_COMPLETE("study_complete"),
TRAINING_COMPLETE("training_complete"),
TRANSFERRED_INACTIVE_STORAGE("related_record_trasfered_inactive_storage"),
OBSOLETE("obsolete"),
ALLOWANCES_GRANTED_TERMINATED("all_allowances_granted_are_terminated"),
WGI_ACTION_COMPLETE("WGI_action_complete"),
SEPARATION("separation"),
CASE_COMPLETE("case_complete"),
DECLASSIFICATION_REVIEW("declassification_review");
public final String eventName;
RetentionEvents(String eventName)
{
this.eventName = eventName;
}
}

View File

@@ -0,0 +1,55 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
/**
* Retention period values
*/
public enum RetentionPeriod
{
DAY("day"),
END_OF_FINANCIAL_MONTH("fmend"),
END_OF_FINANCIAL_QUARTER("fqend"),
END_OF_FINANCIAL_YEAR("fyend"),
IMMEDIATELY("immediately"),
END_OF_MONTH("monthend"),
END_OF_QUARTER("quarterend"),
END_OF_YEAR("yearend"),
MONTH("month"),
NONE("none"),
QUARTER("quarter"),
WEEK("week"),
XML_DURATION("duration"),
YEAR("year");
public final String periodName;
RetentionPeriod(String periodName)
{
this.periodName = periodName;
}
}

View File

@@ -27,6 +27,7 @@
package org.alfresco.rm.rest.api.model;
import lombok.Data;
import java.util.List;
/**
@@ -42,4 +43,14 @@ public class RetentionSchedule
private boolean isRecordLevel;
private boolean isUnpublishedUpdates;
private List<RetentionScheduleActionDefinition> actions;
}
public boolean getIsRecordLevel()
{
return isRecordLevel;
}
public void setIsRecordLevel(boolean recordLevel)
{
isRecordLevel = recordLevel;
}
}

View File

@@ -0,0 +1,46 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
/**
* Retention steps values
*/
public enum RetentionSteps
{
RETAIN("retain"),
CUTOFF("cutoff"),
TRANSFER("transfer"),
ACCESSION("accession"),
DESTROY("destroy");
public final String stepName;
RetentionSteps(String stepName)
{
this.stepName = stepName;
}
}

View File

@@ -0,0 +1,243 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.retentionschedule;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionScheduleImpl;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.UnprocessableContentException;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
import org.alfresco.rm.rest.api.model.RetentionEvents;
import org.alfresco.rm.rest.api.model.RetentionPeriod;
import org.alfresco.rm.rest.api.model.RetentionScheduleActionDefinition;
import org.alfresco.rm.rest.api.model.RetentionSteps;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
import static org.alfresco.util.ParameterCheck.mandatory;
/**
* Retention schedule action relation is used to perform the retention schedule step operations.
*/
@RelationshipResource(name = "retention-steps", entityResource = RetentionScheduleEntityResource.class, title = "Retention Schedule Action")
public class RetentionScheduleActionRelation implements RelationshipResourceAction.Read<RetentionScheduleActionDefinition>,
RelationshipResourceAction.Create<RetentionScheduleActionDefinition>
{
private FilePlanComponentsApiUtils apiUtils;
protected NodeService nodeService;
private RecordsManagementServiceRegistry service;
private ApiNodesModelFactory nodesModelFactory;
public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
{
this.apiUtils = apiUtils;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
{
this.nodesModelFactory = nodesModelFactory;
}
public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry service)
{
this.service = service;
}
@Override
@WebApiDescription(title="Create a retention schedule step for the particular retention schedule using the 'retentionScheduleId'")
public List<RetentionScheduleActionDefinition> create(String retentionScheduleId, List<RetentionScheduleActionDefinition> nodeInfos, Parameters parameters)
{
checkNotBlank("retentionScheduleId", retentionScheduleId);
mandatory("entity", nodeInfos);
mandatory("parameters", parameters);
NodeRef retentionScheduleNodeRef = apiUtils.lookupAndValidateNodeType(retentionScheduleId, RecordsManagementModel.TYPE_DISPOSITION_SCHEDULE);
// validation for the order of the step
retentionScheduleStepValidation(retentionScheduleNodeRef, nodeInfos.get(0));
// request property validation
retentionScheduleRequestValidation(nodeInfos.get(0));
// create the parameters for the action definition
Map<QName, Serializable> actionDefinitionParams = nodesModelFactory.createRetentionActionDefinitionParams(nodeInfos.get(0));
// create the child association from the schedule to the action definition
NodeRef actionNodeRef = this.nodeService.createNode(retentionScheduleNodeRef,
RecordsManagementModel.ASSOC_DISPOSITION_ACTION_DEFINITIONS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,
QName.createValidLocalName(nodeInfos.get(0).getName())),
RecordsManagementModel.TYPE_DISPOSITION_ACTION_DEFINITION, actionDefinitionParams).getChildRef();
DispositionSchedule dispositionSchedule = new DispositionScheduleImpl(service, nodeService, retentionScheduleNodeRef);
DispositionActionDefinition dispositionActionDefinition = dispositionSchedule.getDispositionActionDefinition(actionNodeRef.getId());
List<RetentionScheduleActionDefinition> responseActions = new ArrayList<>();
if (dispositionActionDefinition != null)
{
responseActions.add(nodesModelFactory.mapRetentionScheduleActionDefData(dispositionActionDefinition));
}
return responseActions;
}
@Override
@WebApiDescription(title = "Return a paged list of retention schedule action definition based on the 'retentionScheduleId'")
public CollectionWithPagingInfo<RetentionScheduleActionDefinition> readAll(String retentionScheduleId, Parameters parameters)
{
checkNotBlank("retentionScheduleId", retentionScheduleId);
mandatory("parameters", parameters);
NodeRef retentionScheduleNodeRef = apiUtils.lookupAndValidateNodeType(retentionScheduleId, RecordsManagementModel.TYPE_DISPOSITION_SCHEDULE);
List<DispositionActionDefinition> actions = nodesModelFactory.getRetentionActions(retentionScheduleNodeRef);
List<RetentionScheduleActionDefinition> actionDefinitionList = actions.stream()
.map(nodesModelFactory::mapRetentionScheduleActionDefData)
.collect(Collectors.toList());
return CollectionWithPagingInfo.asPaged(parameters.getPaging(), actionDefinitionList, false,
actionDefinitionList.size());
}
/**
* this method is used to validate the order of the retention schedule step
* @param retentionScheduleNodeRef nodeRef
* @param retentionScheduleActionDefinition retention schedule action definition
*/
private void retentionScheduleStepValidation(NodeRef retentionScheduleNodeRef, RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
List<DispositionActionDefinition> actions = nodesModelFactory.getRetentionActions(retentionScheduleNodeRef);
Set<String> completedActions = new HashSet<>();
if (!actions.isEmpty())
{
completedActions = actions.stream()
.map(DispositionActionDefinition::getName)
.collect(Collectors.toSet());
}
if (completedActions.contains(RetentionSteps.DESTROY.stepName))
{
throw new ConstraintViolatedException("Invalid Step - destroy action is already added . No other action is allowed after Destroy.");
}
if (checkStepAlreadyExists(completedActions, retentionScheduleActionDefinition.getName()))
{
throw new ConstraintViolatedException("Invalid Step - This step already exists. You cant create it again. Only transfer action is allowed multiple times.");
}
if (firstStepValidation(actions, retentionScheduleActionDefinition.getName()))
{
throw new UnprocessableContentException("Invalid Step - cutoff or retain should be the first step");
}
if (isCutOffStepAllowed(completedActions, retentionScheduleActionDefinition.getName()))
{
throw new ConstraintViolatedException("Invalid Step - Can't use cutoff after transfer or accession");
}
}
/**
* this method is used to validate the request of the retention schedule
* @param retentionScheduleActionDefinition retention schedule action definition
*/
private void retentionScheduleRequestValidation(RetentionScheduleActionDefinition retentionScheduleActionDefinition)
{
// step name validation
if (invalidStepNameCheck(retentionScheduleActionDefinition.getName()))
{
throw new InvalidArgumentException("name value is invalid : " +retentionScheduleActionDefinition.getName());
}
// period value validation
if (invalidPeriodCheck(retentionScheduleActionDefinition.getPeriod()))
{
throw new InvalidArgumentException("period value is invalid : " +retentionScheduleActionDefinition.getPeriod());
}
// event name validation
if (invalidEventNameCheck(retentionScheduleActionDefinition.getEvents()))
{
throw new InvalidArgumentException("event value is invalid: " + retentionScheduleActionDefinition.getEvents());
}
// periodProperty validation
List<String> validPeriodProperties = Arrays.asList("cm:created", "rma:cutOffDate", "rma:dispositionAsOf");
if (validPeriodProperties.stream().noneMatch(retentionScheduleActionDefinition.getPeriodProperty()::equals))
{
throw new InvalidArgumentException("periodProperty value is invalid: " + retentionScheduleActionDefinition.getPeriodProperty());
}
}
private boolean checkStepAlreadyExists(Set<String> completedActions, String stepName)
{
return completedActions.contains(stepName) && !stepName.equals(RetentionSteps.TRANSFER.stepName);
}
private boolean firstStepValidation(List<DispositionActionDefinition> actions, String stepName)
{
return actions.isEmpty()
&& !stepName.equals(RetentionSteps.CUTOFF.stepName) && (!stepName.equals(RetentionSteps.RETAIN.stepName));
}
private boolean isCutOffStepAllowed(Set<String> completedActions, String stepName)
{
return (completedActions.contains(RetentionSteps.TRANSFER.stepName) || completedActions.contains(RetentionSteps.ACCESSION.stepName))
&& stepName.equals(RetentionSteps.CUTOFF.stepName);
}
private boolean invalidStepNameCheck(String stepName)
{
return stepName != null && Arrays.stream(RetentionSteps.values())
.noneMatch(retentionStep -> retentionStep.stepName.equals(stepName));
}
private boolean invalidPeriodCheck(String period)
{
return period != null && Arrays.stream(RetentionPeriod.values())
.noneMatch(retentionPeriod -> retentionPeriod.periodName.equals(period));
}
private boolean invalidEventNameCheck(List<String> events)
{
return !events.isEmpty() && events.stream()
.anyMatch(event -> Arrays.stream(RetentionEvents.values())
.noneMatch(retentionEvent -> retentionEvent.eventName.equals(event)));
}
}

View File

@@ -0,0 +1,38 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.retentionschedule;
import org.alfresco.rest.framework.resource.EntityResource;
/**
* Retention schedule entity resource
*/
@EntityResource(name="retention-schedules", title = "Retention Schedule")
public class RetentionScheduleEntityResource
{
}

View File

@@ -54,13 +54,11 @@ import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagement
import static org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel.TYPE_RECORD_CATEGORY;
import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
import static org.alfresco.util.ParameterCheck.mandatory;
import lombok.Data;
/**
* Retention schedule relation is used perform retention schedule operation for a record category.
*/
@RelationshipResource(name = "retention-schedules", entityResource = RecordCategoriesEntityResource.class, title = "Retention Schedule")
@Data
public class RetentionScheduleRelation implements RelationshipResourceAction.Read<RetentionSchedule>,
RelationshipResourceAction.Create<RetentionSchedule>
{
@@ -70,6 +68,26 @@ public class RetentionScheduleRelation implements RelationshipResourceAction.Rea
private DispositionService dispositionService;
protected NodeService nodeService;
public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
{
this.apiUtils = apiUtils;
}
public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
{
this.nodesModelFactory = nodesModelFactory;
}
public void setDispositionService(DispositionService dispositionService)
{
this.dispositionService = dispositionService;
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
@Override
@WebApiDescription(title="Create a retention schedule for the particular record category using the 'recordCategoryId'")
public List<RetentionSchedule> create(String recordCategoryId, List<RetentionSchedule> nodeInfos, Parameters parameters)
@@ -83,7 +101,7 @@ public class RetentionScheduleRelation implements RelationshipResourceAction.Rea
Map<QName, Serializable> dsProps = new HashMap<>();
dsProps.put(PROP_DISPOSITION_AUTHORITY, nodeInfos.get(0).getAuthority());
dsProps.put(PROP_DISPOSITION_INSTRUCTIONS, nodeInfos.get(0).getInstructions());
dsProps.put(PROP_RECORD_LEVEL_DISPOSITION, nodeInfos.get(0).isRecordLevel());
dsProps.put(PROP_RECORD_LEVEL_DISPOSITION, nodeInfos.get(0).getIsRecordLevel());
DispositionSchedule dispositionSchedule = dispositionService.createDispositionSchedule(parentNodeRef, dsProps);
RetentionSchedule retentionSchedule = nodesModelFactory.mapRetentionScheduleData(dispositionSchedule);
result.add(retentionSchedule);