diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/ExportAPI.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/ExportAPI.java new file mode 100644 index 0000000000..12d322fdb2 --- /dev/null +++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/v0/ExportAPI.java @@ -0,0 +1,103 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2021 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.v0; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.alfresco.rest.core.v0.BaseAPI; +import org.apache.http.HttpResponse; +import org.json.JSONArray; +import org.json.JSONObject; +import org.springframework.stereotype.Component; + +/** + * Methods to make API requests using v0 API for Exporting Items + * + * @author Shubham Jain + * @since 7.1.0 + */ + +@Component +public class ExportAPI extends BaseAPI +{ + /** + * The URI to export an item + */ + private static final String EXPORT_API = "{0}rma/admin/export"; + + /** + * Export a single Record/Record Folder/Record Category using V0 Export API + * + * @param user User performing the export + * @param password User's Password + * @param expectedStatusCode Expected Response Code + * @param nodeID ID of the Node(Record/RecordFolder) to be exported + * @return HTTP Response + */ + public HttpResponse exportRMNode(String user, String password, int expectedStatusCode, String nodeID) + { + return export(user, password, expectedStatusCode, Collections.singletonList(getNodeRefSpacesStore() + nodeID)); + } + + /** + * Export a list of nodes using V0 Export API + * + * @param user User performing the export + * @param password User's Password + * @param expectedStatusCode Expected Response Code + * @param nodeIDList List of the nodes to be exported + * @return HTTP Response + */ + public HttpResponse exportRMNodes(String user, String password, int expectedStatusCode, List nodeIDList) + { + + List nodeRefs = + nodeIDList.stream().map(nodeID -> getNodeRefSpacesStore() + nodeID).collect(Collectors.toList()); + + return export(user, password, expectedStatusCode, nodeRefs); + } + + /** + * Export API function to perform Export Operation on items with given noderefs using V0 Export Rest API + * + * @param user User performing the export + * @param password User's Password + * @param expectedStatusCode Expected Response Code + * @param nodeRefs list of the noderefs for the items to be exported + * @return Rest API Post Request + */ + public HttpResponse export(String user, String password, int expectedStatusCode, List nodeRefs) + { + final JSONObject requestParams = new JSONObject(); + + requestParams.put("nodeRefs", new JSONArray(nodeRefs)); + + return doPostJsonRequest(user, password, expectedStatusCode, requestParams, EXPORT_API); + } +} diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/ExportRecordsTests.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/ExportRecordsTests.java new file mode 100644 index 0000000000..d3e7bde53f --- /dev/null +++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/records/ExportRecordsTests.java @@ -0,0 +1,136 @@ +/* + * #%L + * Alfresco Records Management Module + * %% + * Copyright (C) 2005 - 2021 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * - + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * - + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * - + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * - + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ +package org.alfresco.rest.rm.community.records; + +import static java.util.Arrays.asList; + +import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE; +import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createTempFile; +import static org.alfresco.utility.data.RandomData.getRandomName; +import static org.alfresco.utility.report.log.Step.STEP; +import static org.apache.http.HttpStatus.SC_OK; + +import org.alfresco.rest.rm.community.base.BaseRMRestTest; +import org.alfresco.rest.rm.community.model.record.Record; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory; +import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild; +import org.alfresco.rest.v0.ExportAPI; +import org.alfresco.test.AlfrescoTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * This class contains tests for testing the Export functionality on RM site + * + * @author Shubham Jain + * @since 7.1.0 + */ +public class ExportRecordsTests extends BaseRMRestTest +{ + private RecordCategory rootCategory; + + private RecordCategoryChild recordFolder; + + @Autowired + private ExportAPI exportAPI; + + @BeforeClass (alwaysRun = true) + public void exportRecordsTestsBeforeClass() + { + STEP("Create root level category"); + rootCategory = createRootCategory(getRandomName("Category")); + + STEP("Create the record folder inside the rootCategory"); + recordFolder = createRecordFolder(rootCategory.getId(), getRandomName("Folder")); + + } + + @DataProvider (name = "CreateRMNodes") + public Object[][] getRMNodeID() + { + return new String[][] { + { createRecord("Record_4MB", 4).getId() }, + { createRecord("Record_200MB", 200).getId() }, + { recordFolder.getId() } + }; + } + + /** + * Given a record with size > 4 MB + * When I export the record using API + * Then the request is successful + */ + @Test (description = "Testing the RM Export functionality for records of size >4MB and Record " + + "Folder containing records with size >4MB", + dataProvider = "CreateRMNodes") + @AlfrescoTest (jira = "APPS-986") + public void exportRMNodeTest(String nodeID) + { + STEP("Export the created record/record folder with size greater than 4 MB and verifying the expected response" + + " code"); + exportAPI.exportRMNode(getAdminUser().getUsername(), getAdminUser().getPassword(), SC_OK, nodeID); + } + + /** + * I would change this to + * Given a list of records with a size > 4MB + * When I export the records + * Then the request is succesfull + */ + @Test (description = "Testing the RM Export functionality using API for a list of Records at once with " + + "collective size of more than 4MB") + public void exportRecordsTest() + { + STEP("Export all the created records at once and verifying the expected response code"); + exportAPI.exportRMNodes(getAdminUser().getUsername(), getAdminUser().getPassword(), + SC_OK, asList(createRecord("Record_2MB", 2).getId(), createRecord("Record_3MB", 3).getId())); + } + + /** + * Create a Record with a specific size in RM Site inside already created Record Folder + * + * @param recordName Name of the record to be created + * @param sizeInMegaBytes Size of the record to be created in MegaBytes + * @return Created record with defined size + */ + public Record createRecord(String recordName, int sizeInMegaBytes) + { + return getRestAPIFactory().getRecordFolderAPI().createRecord(Record.builder().name(recordName) + .nodeType(CONTENT_TYPE).build(), recordFolder.getId(), + createTempFile("TempFile", sizeInMegaBytes)); + } + + @AfterClass (alwaysRun = true) + public void exportRecordsTestsAfter() + { + STEP("Delete the created rootCategory along with corresponding record folders/records present in it"); + getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(rootCategory.getId()); + } +} diff --git a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java index 56dd5da2bb..fe91643217 100644 --- a/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java +++ b/amps/ags/rm-automation/rm-automation-community-rest-api/src/test/java/org/alfresco/rest/rm/community/utils/FilePlanComponentsUtil.java @@ -41,6 +41,7 @@ import static org.testng.Assert.assertTrue; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStreamWriter; +import java.io.RandomAccessFile; import org.alfresco.rest.rm.community.model.record.Record; import org.alfresco.rest.rm.community.model.record.RecordProperties; @@ -66,13 +67,19 @@ public class FilePlanComponentsUtil // Intentionally blank } - /** Name of the image resource file to be used for records body */ + /** + * Name of the image resource file to be used for records body + */ public static final String IMAGE_FILE = "money.JPG"; - /** Title prefix for record category children */ + /** + * Title prefix for record category children + */ public static final String TITLE_PREFIX = "Title for "; - /** Description prefix for record category children */ + /** + * Description prefix for record category children + */ public static final String DESCRIPTION_PREFIX = "This is the description for"; @@ -87,7 +94,7 @@ public class FilePlanComponentsUtil } /** - * Creates a record model with the given type and a random name (with "Record " prefix) + * Creates a record model with the given type and a random name (with "Record " prefix) * * @param nodeType The node type * @return The {@link Record} with for the given node type @@ -95,9 +102,9 @@ public class FilePlanComponentsUtil private static Record createRecordModel(String nodeType) { return Record.builder() - .name("Record " + getRandomAlphanumeric()) - .nodeType(nodeType) - .build(); + .name("Record " + getRandomAlphanumeric()) + .nodeType(nodeType) + .build(); } /** @@ -133,22 +140,22 @@ public class FilePlanComponentsUtil /** * Creates an unfiled records container child record model with the given name and type * - * @param name The name of the unfiled records container child + * @param name The name of the unfiled records container child * @param nodeType The type of the record category child * @return The {@link UnfiledContainerChild} with the given details */ public static UnfiledContainerChild createUnfiledContainerChildRecordModel(String name, String nodeType) { return UnfiledContainerChild.builder() - .name(name) - .nodeType(nodeType) - .build(); + .name(name) + .nodeType(nodeType) + .build(); } /** * Creates a nonElectronic container child record model with all available properties for the non electronic records * - * @param name The name of the unfiled records container child + * @param name The name of the unfiled records container child * @param nodeType The type of the record category child * @return The {@link UnfiledContainerChild} with the given details */ @@ -156,19 +163,19 @@ public class FilePlanComponentsUtil String shelf, String storageLocation, Integer numberOfCopies, Integer physicalSize) { return UnfiledContainerChild.builder() - .name(name) - .nodeType(NON_ELECTRONIC_RECORD_TYPE) - .properties(UnfiledContainerChildProperties.builder() - .title(title) - .description(description) - .box(box) - .file(file) - .shelf(shelf) - .storageLocation(storageLocation) - .numberOfCopies(numberOfCopies) - .physicalSize(physicalSize) - .build()) - .build(); + .name(name) + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .properties(UnfiledContainerChildProperties.builder() + .title(title) + .description(description) + .box(box) + .file(file) + .shelf(shelf) + .storageLocation(storageLocation) + .numberOfCopies(numberOfCopies) + .physicalSize(physicalSize) + .build()) + .build(); } /** @@ -190,110 +197,110 @@ public class FilePlanComponentsUtil String shelf, String storageLocation, Integer numberOfCopies, Integer physicalSize) { return Record.builder() - .name(name) - .nodeType(NON_ELECTRONIC_RECORD_TYPE) - .properties(RecordProperties.builder() - .title(title) - .description(description) - .box(box) - .file(file) - .shelf(shelf) - .storageLocation(storageLocation) - .numberOfCopies(numberOfCopies) - .physicalSize(physicalSize) - .build()) - .build(); + .name(name) + .nodeType(NON_ELECTRONIC_RECORD_TYPE) + .properties(RecordProperties.builder() + .title(title) + .description(description) + .box(box) + .file(file) + .shelf(shelf) + .storageLocation(storageLocation) + .numberOfCopies(numberOfCopies) + .physicalSize(physicalSize) + .build()) + .build(); } /** * Creates a record model with the given name, description and title * - * @param name The name of the record + * @param name The name of the record * @param description The description of the record - * @param title The title of the record + * @param title The title of the record * @return The {@link Record} with the given details */ public static Record createRecordModel(String name, String description, String title) { return Record.builder() - .name(name) - .properties(RecordProperties.builder() - .description(description) - .title(title) - .build()) - .build(); + .name(name) + .properties(RecordProperties.builder() + .description(description) + .title(title) + .build()) + .build(); } /** * Creates a record category child model with the given name and type * - * @param name The name of the record category child + * @param name The name of the record category child * @param nodeType The type of the record category child * @return The {@link RecordCategoryChild} with the given details */ public static RecordCategoryChild createRecordCategoryChildModel(String name, String nodeType) { return RecordCategoryChild.builder() - .name(name) - .nodeType(nodeType) - .properties(RecordCategoryChildProperties.builder() - .title(TITLE_PREFIX + name) - .build()) - .build(); + .name(name) + .nodeType(nodeType) + .properties(RecordCategoryChildProperties.builder() + .title(TITLE_PREFIX + name) + .build()) + .build(); } /** * Creates a record category model with the given name and title * - * @param name The name of the record category + * @param name The name of the record category * @param title The title of the record category * @return The {@link RecordCategory} with the given details */ public static RecordCategory createRecordCategoryModel(String name, String title) { return RecordCategory.builder() - .name(name) - .nodeType(RECORD_CATEGORY_TYPE) - .properties(RecordCategoryProperties.builder() - .title(title) - .build()) - .build(); + .name(name) + .nodeType(RECORD_CATEGORY_TYPE) + .properties(RecordCategoryProperties.builder() + .title(title) + .build()) + .build(); } /** * Creates a record folder model with the given name and title * - * @param name The name of the record folder + * @param name The name of the record folder * @param title The title of the record folder * @return The {@link RecordFolder} with the given details */ public static RecordFolder createRecordFolderModel(String name, String title) { return RecordFolder.builder() - .name(name) - .nodeType(RECORD_FOLDER_TYPE) - .properties(RecordFolderProperties.builder() - .title(title) - .build()) - .build(); + .name(name) + .nodeType(RECORD_FOLDER_TYPE) + .properties(RecordFolderProperties.builder() + .title(title) + .build()) + .build(); } /** * Creates an unfiled records container child model with the given name and type * - * @param name The name of the unfiled records container child + * @param name The name of the unfiled records container child * @param nodeType The type of the record category child * @return The {@link UnfiledContainerChild} with the given details */ public static UnfiledContainerChild createUnfiledContainerChildModel(String name, String nodeType) { return UnfiledContainerChild.builder() - .name(name) - .nodeType(nodeType) - .properties(UnfiledContainerChildProperties.builder() - .title(TITLE_PREFIX + name) - .build()) - .build(); + .name(name) + .nodeType(nodeType) + .properties(UnfiledContainerChildProperties.builder() + .title(TITLE_PREFIX + name) + .build()) + .build(); } /** @@ -324,6 +331,32 @@ public class FilePlanComponentsUtil } } + /** + * Method to create a temporary file with specific size + * + * @param name file name + * @param sizeInMegaBytes size + * @return temporary file + */ + public static File createTempFile(final String name, long sizeInMegaBytes) + { + try + { + // Create file + final File file = File.createTempFile(name, ".txt"); + + RandomAccessFile raf = new RandomAccessFile(file, "rw"); + raf.setLength(sizeInMegaBytes * 1024 * 1024); + raf.close(); + + return file; + } + catch (Exception exception) + { + throw new RuntimeException("Unable to create test file.", exception); + } + } + /** * Helper method to verify all properties of a nonElectronic record *