[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

@@ -46,4 +46,13 @@ public class RetentionSchedule extends TestModel
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,33 @@
/*
* #%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.rest.rm.community.model.retentionschedule;
import org.alfresco.rest.core.RestModels;
public class RetentionScheduleStepCollection extends RestModels<RetentionScheduleStepEntry, RetentionScheduleStepCollection>
{
}

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.rest.rm.community.model.retentionschedule;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import org.alfresco.rest.core.RestModels;
@Data
public class RetentionScheduleStepEntry extends RestModels<RetentionScheduleActionDefinition, RetentionScheduleStepEntry>
{
@JsonProperty
private RetentionScheduleActionDefinition entry;
}

View File

@@ -28,7 +28,9 @@ package org.alfresco.rest.rm.community.requests.gscore.api;
import org.alfresco.rest.core.RMRestWrapper;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionSchedule;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleActionDefinition;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleCollection;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleStepCollection;
import org.alfresco.rest.rm.community.requests.RMModelRequest;
import static org.alfresco.rest.core.RestRequest.requestWithBody;
@@ -122,4 +124,75 @@ public class RetentionScheduleAPI extends RMModelRequest
{
return getRetentionSchedule(recordCategoryId, EMPTY);
}
/**
* Creates a step in the retention schedule.
*
* @param retentionScheduleActionDefinition The retentionScheduleActionDefinition model
* @param retentionScheduleId The identifier of a retention schedule id
* @param parameters The URL parameters to add
* @return The created {@link RetentionScheduleActionDefinition}
* @throws RuntimeException for the following cases:
* <ul>
* <li>{@code retentionScheduleId} is not a valid format or {@code retentionScheduleId} is invalid</li>
* <li>authentication fails</li>
* <li>current user does not have permission to add children to {@code retentionScheduleId}</li>
* <li>{@code retentionScheduleId} does not exist</li>
* <li>new name clashes with an existing node in the current parent container</li>
* </ul>
*/
public RetentionScheduleActionDefinition createRetentionScheduleStep(RetentionScheduleActionDefinition retentionScheduleActionDefinition, String retentionScheduleId, String parameters)
{
mandatoryString("retentionScheduleId", retentionScheduleId);
mandatoryObject("retentionScheduleActionDefinition", retentionScheduleActionDefinition);
return getRmRestWrapper().processModel(RetentionScheduleActionDefinition.class, requestWithBody(
POST,
toJson(retentionScheduleActionDefinition),
"retention-schedules/{retentionScheduleId}/retention-steps",
retentionScheduleId,
parameters
));
}
/**
* See {@link #createRetentionScheduleStep(RetentionScheduleActionDefinition, String)} (RetentionSchedule, String, String)}
*/
public RetentionScheduleActionDefinition createRetentionScheduleStep(RetentionScheduleActionDefinition retentionScheduleActionDefinition, String retentionScheduleId)
{
return createRetentionScheduleStep(retentionScheduleActionDefinition, retentionScheduleId, EMPTY);
}
/**
* Gets the retentionSchedule of a record category.
*
* @param retentionScheduleId The identifier of a record category
* @param parameters The URL parameters to add
* @return The {@link RetentionScheduleActionDefinition} for the given {@code recordCategoryId}
* @throws RuntimeException for the following cases:
* <ul>
* <li>authentication fails</li>
* <li>current user does not have permission to read {@code recordCategoryId}</li>
* <li>{@code recordCategoryId} does not exist</li>
*</ul>
*/
public RetentionScheduleStepCollection getRetentionScheduleStep(String retentionScheduleId, String parameters)
{
mandatoryString("retentionScheduleId", retentionScheduleId);
return getRmRestWrapper().processModels(RetentionScheduleStepCollection.class, simpleRequest(
GET,
"retention-schedules/{retentionScheduleId}/retention-steps?{parameters}",
retentionScheduleId,
parameters
));
}
/**
* See {@link #getRetentionScheduleStep(String, String)}
*/
public RetentionScheduleStepCollection getRetentionScheduleStep(String recordCategoryId)
{
return getRetentionScheduleStep(recordCategoryId, EMPTY);
}
}

View File

@@ -0,0 +1,358 @@
/*-
* #%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.rest.rm.community.retentionschedule;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionSchedule;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleActionDefinition;
import org.alfresco.rest.rm.community.model.retentionschedule.RetentionScheduleStepCollection;
import org.alfresco.rest.v0.RMRolesAndActionsAPI;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.alfresco.rest.core.v0.BaseAPI.RM_SITE_ID;
import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CONFLICT;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static org.springframework.http.HttpStatus.OK;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;
import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
import static org.testng.Assert.assertNotNull;
import static org.testng.AssertJUnit.assertEquals;
/**
* Retention schedule step test case
*/
public class RetentionScheduleStepTests extends BaseRMRestTest
{
private RecordCategory recordCategory;
private RetentionSchedule createdRetentionSchedule;
private final RetentionScheduleActionDefinition retentionScheduleActionDefinition = new RetentionScheduleActionDefinition();
private RetentionScheduleActionDefinition createdRetentionActionDefinition;
private UserModel nonRMuser;
private final List<String> recordCategories = new ArrayList<>();
private static final String TEST_USER = "testUser";
private static final String RECORD_CATEGORY = "recordCategory";
private static final String PERIOD_PROPERTY = "cm:created";
private static final String AUTHORITY = "authority";
private static final String INSTRUCTIONS = "instructions";
private static final int PERIOD_AMOUNT = 5;
private static final String PERIOD = "month";
private static final List<String> EVENTS = Arrays.asList("case_closed","abolished");
private static final String TRANSFER_STEP = "transfer";
private static final String RETAIN_STEP = "retain";
private static final String INVALID_PERIOD = "random";
private static final String CUTOFF_STEP = "cutoff";
private static final String DESTROY_STEP = "destroy";
private static final String INVALID_PASSWORD = "wrongPassword";
@Autowired
private RMRolesAndActionsAPI rmRolesAndActionsAPI;
@BeforeClass(alwaysRun = true)
public void preconditionForRetentionScheduleStepTests()
{
createRMSiteIfNotExists();
// create a non rm user
nonRMuser = dataUser.createRandomTestUser(TEST_USER);
//Create record category
recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = new RetentionSchedule();
retentionSchedule.setAuthority(AUTHORITY + getRandomAlphanumeric());
retentionSchedule.setInstructions(INSTRUCTIONS + getRandomAlphanumeric());
retentionSchedule.setIsRecordLevel(false);
//Create retention schedule with a valid user
createdRetentionSchedule = getRestAPIFactory().getRetentionScheduleAPI()
.createRetentionSchedule(retentionSchedule, recordCategory.getId());
retentionScheduleActionDefinition.setName(RETAIN_STEP);
retentionScheduleActionDefinition.setDescription(INSTRUCTIONS);
retentionScheduleActionDefinition.setPeriodAmount(PERIOD_AMOUNT);
retentionScheduleActionDefinition.setPeriodProperty(PERIOD_PROPERTY);
retentionScheduleActionDefinition.setPeriod(PERIOD);
retentionScheduleActionDefinition.setCombineDispositionStepConditions(false);
retentionScheduleActionDefinition.setEligibleOnFirstCompleteEvent(true);
retentionScheduleActionDefinition.setEvents(EVENTS);
}
@Test(priority = 1)
public void createRetentionScheduleStepFor422()
{
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
//Creating the first action "transfer" should give 422
actionDefinition.setName(TRANSFER_STEP);
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(UNPROCESSABLE_ENTITY);
}
@Test(priority = 2)
public void createRetentionScheduleStepWithInvalidPeriodValue()
{
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
//Invalid period value
actionDefinition.setPeriod(INVALID_PERIOD);
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(BAD_REQUEST);
}
@Test(priority = 3)
public void createRetentionScheduleWithInvalidStep()
{
RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = createRetentionSchedule(recordCategory);
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition1 = getRetentionScheduleActionDefinition();
actionDefinition1.setName(TRANSFER_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition1,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition2 = getRetentionScheduleActionDefinition();
actionDefinition2.setName(CUTOFF_STEP);
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition2,retentionSchedule.getId());
// Verify the status code
assertStatusCode(CONFLICT);
}
@Test(priority = 4)
public void createRetentionScheduleWithInvalidStepAfterDestroy()
{
RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = createRetentionSchedule(recordCategory);
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition1 = getRetentionScheduleActionDefinition();
actionDefinition1.setName(DESTROY_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition1,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition2 = getRetentionScheduleActionDefinition();
actionDefinition2.setName(CUTOFF_STEP);
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition2,retentionSchedule.getId());
// Verify the status code
assertStatusCode(CONFLICT);
}
@Test(priority = 5)
public void createRetentionScheduleWithSameStep()
{
RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = createRetentionSchedule(recordCategory);
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition1 = getRetentionScheduleActionDefinition();
actionDefinition1.setName(RETAIN_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition1,retentionSchedule.getId());
// Verify the status code
assertStatusCode(CONFLICT);
}
@Test(priority = 6)
public void createRetentionScheduleWithMultipleTransferStep()
{
RecordCategory recordCategory = createRootCategory(getRandomName(RECORD_CATEGORY));
recordCategories.add(recordCategory.getId());
RetentionSchedule retentionSchedule = createRetentionSchedule(recordCategory);
RetentionScheduleActionDefinition actionDefinition = getRetentionScheduleActionDefinition();
actionDefinition.setName(RETAIN_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition,retentionSchedule.getId());
assertStatusCode(CREATED);
RetentionScheduleActionDefinition actionDefinition1 = getRetentionScheduleActionDefinition();
actionDefinition1.setName(TRANSFER_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition1, retentionSchedule.getId());
RetentionScheduleActionDefinition actionDefinition2 = getRetentionScheduleActionDefinition();
actionDefinition2.setName(TRANSFER_STEP);
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(actionDefinition2, retentionSchedule.getId());
// Verify the status code
assertStatusCode(CREATED);
}
@Test(priority = 7)
public void createRetentionScheduleStepFor201()
{
//Create retention schedule action definition
createdRetentionActionDefinition = getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(retentionScheduleActionDefinition,createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(CREATED);
// Find this retention schedule is created one or not
assertEquals(createdRetentionActionDefinition.getName(), retentionScheduleActionDefinition.getName());
assertEquals(createdRetentionActionDefinition.getDescription(), retentionScheduleActionDefinition.getDescription());
assertEquals(createdRetentionActionDefinition.getPeriodAmount(), retentionScheduleActionDefinition.getPeriodAmount());
assertEquals(createdRetentionActionDefinition.isCombineDispositionStepConditions(), retentionScheduleActionDefinition.isCombineDispositionStepConditions());
assertEquals(createdRetentionActionDefinition.isEligibleOnFirstCompleteEvent(), retentionScheduleActionDefinition.isEligibleOnFirstCompleteEvent());
}
@Test(priority = 8)
public void createRetentionScheduleStepFor401()
{
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI(new UserModel(getAdminUser().getUsername(), INVALID_PASSWORD)).createRetentionScheduleStep(retentionScheduleActionDefinition,createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(UNAUTHORIZED);
}
@Test(priority = 9)
public void createRetentionScheduleStepFor403()
{
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI(nonRMuser).createRetentionScheduleStep(retentionScheduleActionDefinition,createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(FORBIDDEN);
}
@Test(priority = 10)
public void retentionScheduleStepFor400()
{
getRestAPIFactory().getRetentionScheduleAPI().getRetentionScheduleStep(recordCategory.getId());
// Verify the status code
assertStatusCode(BAD_REQUEST);
}
@Test(priority = 11)
public void createRetentionScheduleStepFor404()
{
//Create retention schedule action definition
getRestAPIFactory().getRetentionScheduleAPI().createRetentionScheduleStep(retentionScheduleActionDefinition,getRandomAlphanumeric());
// Verify the status code
assertStatusCode(NOT_FOUND);
}
@Test(priority = 12)
public void retentionScheduleStepFor403()
{
// Get retention schedule steps with user having no rights
getRestAPIFactory().getRetentionScheduleAPI(nonRMuser).getRetentionScheduleStep(createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(FORBIDDEN);
}
@Test(priority = 13)
public void retentionScheduleStepFor401()
{
getRestAPIFactory().getRetentionScheduleAPI(new UserModel(getAdminUser().getUsername(), INVALID_PASSWORD)).getRetentionScheduleStep(createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(UNAUTHORIZED);
}
@Test(priority = 14)
public void retentionScheduleStepWith200()
{
RetentionScheduleStepCollection receiveRetentionStepCollection = getRestAPIFactory().getRetentionScheduleAPI().getRetentionScheduleStep(createdRetentionSchedule.getId());
// Verify the status code
assertStatusCode(OK);
receiveRetentionStepCollection.getEntries().forEach(c ->
{
RetentionScheduleActionDefinition retentionActionDef = c.getEntry();
assertNotNull(retentionActionDef.getId());
// Find this retention schedule is created one or not
assertEquals(createdRetentionActionDefinition.getId(), retentionActionDef.getId());
assertEquals(createdRetentionActionDefinition.getName(), retentionActionDef.getName());
assertEquals(createdRetentionActionDefinition.getDescription(), retentionActionDef.getDescription());
assertEquals(createdRetentionActionDefinition.getPeriod(), retentionActionDef.getPeriod());
assertEquals(createdRetentionActionDefinition.getPeriodAmount(), retentionActionDef.getPeriodAmount());
assertEquals(createdRetentionActionDefinition.isCombineDispositionStepConditions(), retentionActionDef.isCombineDispositionStepConditions());
assertEquals(createdRetentionActionDefinition.isEligibleOnFirstCompleteEvent(), retentionActionDef.isEligibleOnFirstCompleteEvent());
});
}
private RetentionSchedule createRetentionSchedule(RecordCategory recordCategory)
{
RetentionSchedule retentionSchedule = new RetentionSchedule();
retentionSchedule.setAuthority(AUTHORITY + getRandomAlphanumeric());
retentionSchedule.setInstructions(INSTRUCTIONS + getRandomAlphanumeric());
retentionSchedule.setIsRecordLevel(false);
//Create retention schedule with a valid user
retentionSchedule = getRestAPIFactory().getRetentionScheduleAPI()
.createRetentionSchedule(retentionSchedule, recordCategory.getId());
// Verify the status code
assertStatusCode(CREATED);
return retentionSchedule;
}
private static RetentionScheduleActionDefinition getRetentionScheduleActionDefinition()
{
RetentionScheduleActionDefinition actionDefinition = new RetentionScheduleActionDefinition();
actionDefinition.setDescription(INSTRUCTIONS);
actionDefinition.setPeriodAmount(PERIOD_AMOUNT);
actionDefinition.setPeriodProperty(PERIOD_PROPERTY);
actionDefinition.setPeriod(PERIOD);
actionDefinition.setCombineDispositionStepConditions(false);
actionDefinition.setEligibleOnFirstCompleteEvent(true);
actionDefinition.setEvents(EVENTS);
return actionDefinition;
}
@AfterClass(alwaysRun = true)
public void cleanUpRetentionScheduleStepTests()
{
rmRolesAndActionsAPI.deleteAllItemsInContainer(getDataUser().usingAdmin().getAdminUser().getUsername(),
getDataUser().usingAdmin().getAdminUser().getPassword(), RM_SITE_ID, recordCategory.getName());
recordCategories.forEach(this::deleteRecordCategory);
dataUser.deleteUser(nonRMuser);
}
}

View File

@@ -145,7 +145,7 @@ public class RetentionScheduleTests extends BaseRMRestTest
boolean isRecordLevel = false;
retentionSchedule.setAuthority(authority);
retentionSchedule.setInstructions(instructions);
retentionSchedule.setRecordLevel(isRecordLevel);
retentionSchedule.setIsRecordLevel(isRecordLevel);
//Create retention schedule with a valid user
createdRetentionSchedule = getRestAPIFactory().getRetentionScheduleAPI()
@@ -155,7 +155,7 @@ public class RetentionScheduleTests extends BaseRMRestTest
assertStatusCode(CREATED);
assertEquals(createdRetentionSchedule.getAuthority(), authority);
assertEquals(createdRetentionSchedule.getInstructions(), instructions);
assertFalse(createdRetentionSchedule.isRecordLevel());
assertFalse(createdRetentionSchedule.getIsRecordLevel());
assertNotNull(createdRetentionSchedule.getId());
}
@@ -254,7 +254,7 @@ public class RetentionScheduleTests extends BaseRMRestTest
assertEquals(createdRetentionSchedule.getParentId(),retentionSchedule.getParentId());
assertEquals(createdRetentionSchedule.getAuthority(), retentionSchedule.getAuthority());
assertEquals(createdRetentionSchedule.getInstructions(), retentionSchedule.getInstructions());
assertEquals(createdRetentionSchedule.isRecordLevel(), retentionSchedule.isRecordLevel());
assertEquals(createdRetentionSchedule.getIsRecordLevel(), retentionSchedule.getIsRecordLevel());
assertEquals(createdRetentionSchedule.isUnpublishedUpdates(), retentionSchedule.isUnpublishedUpdates());
});
}

View File

@@ -31,6 +31,7 @@
<property name="personService" ref="PersonService"/>
<property name="dispositionService" ref="DispositionService"/>
<property name="serviceRegistry" ref="ServiceRegistry"/>
<property name="recordsManagementServiceRegistry" ref="RecordsManagementServiceRegistry"/>
</bean>
<bean id="searchTypesFactory" class="org.alfresco.rm.rest.api.impl.SearchTypesFactory">
@@ -154,6 +155,13 @@
<property name="nodeService" ref="NodeService"/>
</bean>
<bean class="org.alfresco.rm.rest.api.retentionschedule.RetentionScheduleActionRelation">
<property name="apiUtils" ref="apiUtils" />
<property name="nodesModelFactory" ref="nodesModelFactory" />
<property name="nodeService" ref="NodeService"/>
<property name="recordsManagementServiceRegistry" ref="RecordsManagementServiceRegistry"/>
</bean>
<bean class="org.alfresco.rm.rest.api.recordfolders.RecordFolderEntityResource">
<property name="apiUtils" ref="apiUtils" />
<property name="fileFolderService" ref="FileFolderService" />

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);

View File

@@ -39,7 +39,6 @@ import org.mockito.InjectMocks;
import org.mockito.Mock;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
/**
@@ -72,11 +71,17 @@ public class RetentionScheduleModelUnitTest extends BaseUnitTest
when(dispositionSchedule.isRecordLevelDisposition()).thenReturn(false);
when(mockedNodeService.getPrimaryParent(nodeRef)).thenReturn(childAssociationRef);
// Call the method
RetentionSchedule expectedResult = apiNodesModelFactory.mapRetentionScheduleData(dispositionSchedule);
assertEquals(expectedResult.getId(), dispositionSchedule.getNodeRef().getId());
assertEquals(expectedResult.getAuthority(), dispositionSchedule.getDispositionAuthority());
assertEquals(expectedResult.getInstructions(), dispositionSchedule.getDispositionInstructions());
assertEquals(expectedResult.isRecordLevel(), dispositionSchedule.isRecordLevelDisposition());
RetentionSchedule actualResult = apiNodesModelFactory.mapRetentionScheduleData(dispositionSchedule);
//Expected Result
RetentionSchedule expectedResult = new RetentionSchedule();
expectedResult.setId(nodeRef.getId());
expectedResult.setParentId(filePlan.getId());
expectedResult.setAuthority(AUTHORITY);
expectedResult.setInstructions(INSTRUCTIONS);
// Assertions
assertEquals(expectedResult, actualResult);
}
@Test
@@ -96,15 +101,26 @@ public class RetentionScheduleModelUnitTest extends BaseUnitTest
when(dispositionActionDefinition.getId()).thenReturn(nodeRef.getId());
when(mockedNodeService.getPrimaryParent(nodeRef)).thenReturn(childAssociationRef);
// Call the method
RetentionScheduleActionDefinition expectedResult = apiNodesModelFactory.mapRetentionScheduleActionDefData(dispositionActionDefinition);
String resultPeriod = expectedResult.getPeriod() + "|" + expectedResult.getPeriodAmount();
// Assertions
assertEquals(expectedResult.getId(), dispositionActionDefinition.getId());
assertEquals(expectedResult.getName(), dispositionActionDefinition.getName());
assertEquals(expectedResult.getDescription(), dispositionActionDefinition.getDescription());
assertEquals(expectedResult.getIndex(), dispositionActionDefinition.getIndex());
assertEquals(expectedResult.getLocation(), dispositionActionDefinition.getLocation());
assertEquals(new Period(resultPeriod), dispositionActionDefinition.getPeriod());
assertTrue(expectedResult.isRetainRecordMetadataAfterDestruction());
RetentionScheduleActionDefinition actualResult = apiNodesModelFactory.mapRetentionScheduleActionDefData(dispositionActionDefinition);
//Expected Result
RetentionScheduleActionDefinition expectedResult = getRetentionScheduleActionDefinition(nodeRef);
// Assertion
assertEquals(expectedResult, actualResult);
}
private static RetentionScheduleActionDefinition getRetentionScheduleActionDefinition(NodeRef nodeRef)
{
RetentionScheduleActionDefinition expectedResult = new RetentionScheduleActionDefinition();
expectedResult.setId(nodeRef.getId());
expectedResult.setName(RETAIN_STEP);
expectedResult.setDescription("Description");
expectedResult.setIndex(1);
expectedResult.setLocation("location");
expectedResult.setPeriod("month");
expectedResult.setPeriodAmount(10);
expectedResult.setRetainRecordMetadataAfterDestruction(true);
return expectedResult;
}
}

View File

@@ -0,0 +1,40 @@
/*
* #%L
* Alfresco Remote API
* %%
* 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.rest.framework.core.exceptions;
/**
* Unprocessable Content
*/
public class UnprocessableContentException extends ApiException
{
private static final long serialVersionUID = -6857652090677361159L;
public UnprocessableContentException(String msgId)
{
super(msgId);
}
}

View File

@@ -178,6 +178,7 @@
<entry key="org.alfresco.rest.framework.core.exceptions.ArchivedContentException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_PRECONDITION_FAILED}" />
<entry key="org.alfresco.rest.framework.core.exceptions.RestoreInProgressException" value="#{T(org.springframework.extensions.webscripts.Status).STATUS_CONFLICT}" />
<entry key="org.alfresco.rest.framework.core.exceptions.InvalidNodeTypeException" value="#{T(org.apache.http.HttpStatus).SC_UNPROCESSABLE_ENTITY}" />
<entry key="org.alfresco.rest.framework.core.exceptions.UnprocessableContentException" value="#{T(org.apache.http.HttpStatus).SC_UNPROCESSABLE_ENTITY}" />
</map>
</property>
</bean>