Merge branch 'master' of https://gitlab.alfresco.com/records-management/records-management into feature/RM-4396_Lombok_UseModelInsteadJsonObject

# Conflicts:
#	rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponent.java
#	rm-automation/rm-automation-community-rest-api/src/main/java/org/alfresco/rest/rm/community/model/fileplancomponents/FilePlanComponentProperties.java
This commit is contained in:
Rodica Sutu
2016-12-12 15:15:20 +02:00
20 changed files with 1253 additions and 29 deletions

View File

@@ -24,7 +24,7 @@
</modules>
<properties>
<alfresco.installer.host>nightlybuilds</alfresco.installer.host>
<alfresco.installer.host>releases</alfresco.installer.host>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<suiteXmlFile>testng.xml</suiteXmlFile>

View File

@@ -33,6 +33,11 @@
</build>
<dependencies>
<dependency>
<groupId>com.jayway.restassured</groupId>
<artifactId>rest-assured</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.alfresco.tas</groupId>
<artifactId>restapi-test</artifactId>

View File

@@ -94,6 +94,9 @@ public class FilePlanComponent
@JsonProperty (value = ALLOWABLE_OPERATIONS)
private List<String> allowableOperations;
@JsonProperty (required = false)
private FilePlanComponentContent content;
private FilePlanComponentPath path;
@JsonProperty (required = true)
@@ -104,4 +107,5 @@ public class FilePlanComponent
@JsonProperty (required = true)
private FilePlanComponentUserInfo modifiedByUser;
}

View File

@@ -0,0 +1,113 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 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.fileplancomponents;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* POJO for FilePlanComponent content field
* @author Kristijan Conkas
* @since 2.6
*/
public class FilePlanComponentContent
{
@JsonProperty (required = true)
private String encoding;
@JsonProperty (required = true)
private String mimeType;
@JsonProperty (required = true)
private String mimeTypeName;
@JsonProperty (required = true)
private Integer sizeInBytes;
/**
* @return the encoding
*/
public String getEncoding()
{
return this.encoding;
}
/**
* @param encoding the encoding to set
*/
public void setEncoding(String encoding)
{
this.encoding = encoding;
}
/**
* @return the mimeType
*/
public String getMimeType()
{
return this.mimeType;
}
/**
* @param mimeType the mimeType to set
*/
public void setMimeType(String mimeType)
{
this.mimeType = mimeType;
}
/**
* @return the mimeTypeName
*/
public String getMimeTypeName()
{
return this.mimeTypeName;
}
/**
* @param mimeTypeName the mimeTypeName to set
*/
public void setMimeTypeName(String mimeTypeName)
{
this.mimeTypeName = mimeTypeName;
}
/**
* @return the sizeInBytes
*/
public Integer getSizeInBytes()
{
return this.sizeInBytes;
}
/**
* @param sizeInBytes the sizeInBytes to set
*/
public void setSizeInBytes(Integer sizeInBytes)
{
this.sizeInBytes = sizeInBytes;
}
}

View File

@@ -45,7 +45,17 @@ public class FilePlanComponentFields
public static final String PROPERTIES_DESCRIPTION = "cm:description";
public static final String PROPERTIES_SUPPLEMENTAL_MARKING_LIST = "rmc:supplementalMarkingList";
public static final String ALLOWABLE_OPERATIONS = "allowableOperations";
public static final String IS_CLOSED="isClosed";
public static final String PROPERTIES_REVIEW_PERIOD="rma:reviewPeriod";
public static final String PROPERTIES_LOCATION="rma:location";
public static final String IS_CLOSED = "isClosed";
public static final String PROPERTIES_REVIEW_PERIOD = "rma:reviewPeriod";
public static final String PROPERTIES_LOCATION = "rma:location";
public static final String PROPERTIES_IS_CLOSED = "rma:isClosed"; // not to be confused with IS_CLOSED!
// for non-electronic records
public static final String PROPERTIES_BOX = "rma:box";
public static final String PROPERTIES_FILE = "rma:file";
public static final String PROPERTIES_NUMBER_OF_COPIES = "rma:numberOfCopies";
public static final String PROPERTIES_PHYSICAL_SIZE = "rma:physicalSize";
public static final String PROPERTIES_SHELF = "rma:shelf";
public static final String PROPERTIES_STORAGE_LOCATION = "rma:storageLocation";
}

View File

@@ -26,10 +26,16 @@
*/
package org.alfresco.rest.rm.community.model.fileplancomponents;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_BOX;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_DESCRIPTION;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_FILE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_HOLD_REASON;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_IS_CLOSED;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_LOCATION;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_NUMBER_OF_COPIES;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_PHYSICAL_SIZE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_REVIEW_PERIOD;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_SHELF;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_SUPPLEMENTAL_MARKING_LIST;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_TITLE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentFields.PROPERTIES_VITAL_RECORD_INDICATOR;
@@ -83,4 +89,25 @@ public class FilePlanComponentProperties
@JsonProperty(PROPERTIES_LOCATION)
private String location;
@JsonProperty(value = PROPERTIES_IS_CLOSED, required = false)
private Boolean isClosed;
@JsonProperty(value = PROPERTIES_BOX, required = false)
private String box;
@JsonProperty(value = PROPERTIES_FILE, required = false)
private String file;
@JsonProperty(value = PROPERTIES_SHELF, required = false)
private String shelf;
@JsonProperty(value = PROPERTIES_NUMBER_OF_COPIES, required = false)
private Integer numberOfCopies;
@JsonProperty(value = PROPERTIES_PHYSICAL_SIZE, required = false)
private Integer physicalSize;
}

View File

@@ -46,7 +46,8 @@ public enum FilePlanComponentType
TRANSFER_CONTAINER_TYPE("rma:transferContainer"),
UNFILED_CONTAINER_TYPE("rma:unfiledRecordContainer"),
FOLDER_TYPE("cm:folder"),
CONTENT_TYPE("cm:content");
CONTENT_TYPE("cm:content"),
NON_ELECTRONIC_RECORD_TYPE("rma:nonElectronicDocument");
private String type;

View File

@@ -26,6 +26,8 @@
*/
package org.alfresco.rest.rm.community.requests;
import static com.jayway.restassured.RestAssured.given;
import static org.alfresco.rest.core.RestRequest.requestWithBody;
import static org.alfresco.rest.core.RestRequest.simpleRequest;
import static org.alfresco.rest.rm.community.util.ParameterCheck.mandatoryObject;
@@ -35,10 +37,19 @@ import static org.springframework.http.HttpMethod.DELETE;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;
import static org.springframework.http.HttpMethod.PUT;
import static org.testng.Assert.fail;
import java.io.File;
import com.google.common.io.Resources;
import com.jayway.restassured.http.ContentType;
import com.jayway.restassured.response.Response;
import org.alfresco.rest.core.RestAPI;
import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent;
import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType;
import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentsCollection;
import org.alfresco.utility.model.UserModel;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -119,13 +130,62 @@ public class FilePlanComponentAPI extends RestAPI<FilePlanComponentAPI>
{
mandatoryObject("filePlanComponentProperties", filePlanComponentModel);
mandatoryString("parentId", parentId);
return usingRestWrapper().processModel(FilePlanComponent.class, requestWithBody(
POST,
toJson(filePlanComponentModel),
"fileplan-components/{fileplanComponentId}/children",
parentId
));
POST,
toJson(filePlanComponentModel),
"fileplan-components/{fileplanComponentId}/children?{parameters}",
parentId,
getParameters()));
}
/**
* Create electronic record from file resource
* @param electronicRecordModel {@link FilePlanComponent} for electronic record to be created
* @param fileName the name of the resource file
* @param parentId parent container id
* @return newly created {@link FilePlanComponent}
* @throws Exception if operation failed
*/
public FilePlanComponent createElectronicRecord(FilePlanComponent electronicRecordModel, String fileName, String parentId) throws Exception
{
return createElectronicRecord(electronicRecordModel, new File(Resources.getResource(fileName).getFile()), parentId);
}
/**
* Create electronic record from file resource
* @param electronicRecordModel {@link FilePlanComponent} for electronic record to be created
* @param recordContent {@link File} pointing to the content of the electronic record to be created
* @param parentId parent container id
* @return newly created {@link FilePlanComponent}
* @throws Exception if operation failed
*/
public FilePlanComponent createElectronicRecord(FilePlanComponent electronicRecordModel, File recordContent, String parentId) throws Exception
{
mandatoryObject("filePlanComponentProperties", electronicRecordModel);
mandatoryString("parentId", parentId);
if (!electronicRecordModel.getNodeType().equals(FilePlanComponentType.CONTENT_TYPE.toString()))
{
fail("Only electronic records are supported");
}
/*
* RestWrapper adds some headers which break multipart/form-data uploads and also assumes json POST requests.
* Upload the file using RestAssured library.
*/
UserModel currentUser = usingRestWrapper().getTestUser();
Response response = given()
.auth().basic(currentUser.getUsername(), currentUser.getPassword())
.multiPart("nodeBodyCreate", toJson(electronicRecordModel), ContentType.JSON.name())
.multiPart("filedata", recordContent, ContentType.BINARY.name())
.when()
.post("fileplan-components/{fileplanComponentId}/children?{parameters}", parentId, getParameters())
.andReturn();
usingRestWrapper().setStatusCode(Integer.toString(response.getStatusCode()));
LOG.info("electronic record created: " + response.getBody().prettyPrint());
/* return a FilePlanComponent object representing Response */
return response.jsonPath().getObject("entry", FilePlanComponent.class);
}
/**
@@ -152,8 +212,9 @@ public class FilePlanComponentAPI extends RestAPI<FilePlanComponentAPI>
return usingRestWrapper().processModel(FilePlanComponent.class, requestWithBody(
PUT,
toJson(filePlanComponent),
"fileplan-components/{fileplanComponentId}",
filePlanComponentId
"fileplan-components/{fileplanComponentId}?{parameters}",
filePlanComponentId,
getParameters()
));
}

View File

@@ -30,11 +30,15 @@ import static java.lang.Integer.parseInt;
import static org.alfresco.rest.rm.community.base.TestData.CATEGORY_TITLE;
import static org.alfresco.rest.rm.community.base.TestData.FOLDER_TITLE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.UNFILED_RECORDS_CONTAINER_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.UNFILED_RECORD_FOLDER_TYPE;
import static org.alfresco.rest.rm.community.model.site.RMSiteCompliance.STANDARD;
import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.OK;
import com.jayway.restassured.RestAssured;
@@ -47,11 +51,13 @@ import org.alfresco.rest.rm.community.model.site.RMSite;
import org.alfresco.rest.rm.community.requests.FilePlanComponentAPI;
import org.alfresco.rest.rm.community.requests.RMSiteAPI;
import org.alfresco.utility.data.DataUser;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
/**
* Base class for all IG REST API Tests
@@ -83,7 +89,7 @@ public class BaseRestTest extends RestTest
@Autowired
private RMSiteAPI rmSiteAPI;
@Autowired
private DataUser dataUser;
@@ -95,6 +101,19 @@ public class BaseRestTest extends RestTest
public static final String RM_TITLE = "Records Management";
public static final String RM_DESCRIPTION = "Records Management Site";
/** Valid root containers where electronic and non-electronic records can be created */
@DataProvider(name = "validRootContainers")
public Object[][] getValidRootContainers() throws Exception {
return new Object[][] {
// an arbitrary record folder
{ createCategoryFolderInFilePlan(dataUser.getAdminUser(), FILE_PLAN_ALIAS.toString()) },
// unfiled records root
{ getFilePlanComponentAsUser(dataUser.getAdminUser(), UNFILED_RECORDS_CONTAINER_ALIAS.toString()) },
// an arbitrary unfiled records folder
{ createUnfiledRecordsFolder(UNFILED_RECORDS_CONTAINER_ALIAS.toString(), "Unfiled Folder " + getRandomAlphanumeric()) }
};
}
/**
* @see org.alfresco.rest.RestTest#checkServerHealth()
*/
@@ -196,4 +215,55 @@ public class BaseRestTest extends RestTest
return fpc;
}
/**
* Helper method to close folder
* @param folderToClose
* @return
* @throws Exception
*/
public FilePlanComponent closeFolder(String folderId) throws Exception
{
RestWrapper restWrapper = filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
// build fileplan component + properties for update request
FilePlanComponentProperties properties = new FilePlanComponentProperties();
properties.setIsClosed(true);
FilePlanComponent filePlanComponent = new FilePlanComponent();
filePlanComponent.setProperties(properties);
FilePlanComponent updatedComponent = filePlanComponentAPI.updateFilePlanComponent(filePlanComponent, folderId);
restWrapper.assertStatusCodeIs(OK);
return updatedComponent;
}
/**
* Helper method to create a randomly-named <category>/<folder> structure in fileplan
* @param user user under whose privileges this structure is going to be created
* @param parentId parent container id
* @return record folder
* @throws Exception on failed creation
*/
public FilePlanComponent createCategoryFolderInFilePlan(UserModel user, String parentId) throws Exception
{
filePlanComponentAPI.usingRestWrapper().authenticateUser(user);
// create root category
FilePlanComponent recordCategory = createCategory(parentId, "Category " + getRandomAlphanumeric());
// and return a folder underneath
return createFolder(recordCategory.getId(), "Folder " + getRandomAlphanumeric());
}
/**
* Helper method to retieve a fileplan component with user's privilege
* @param user user under whose privileges a component is to be read
* @param componentId id of the component to read
* @return {@link FilePlanComponent} for given componentId
* @throws Exception if user doesn't have sufficient privileges
*/
public FilePlanComponent getFilePlanComponentAsUser(UserModel user, String componentId) throws Exception
{
filePlanComponentAPI.usingRestWrapper().authenticateUser(user);
return filePlanComponentAPI.getFilePlanComponent(componentId);
}
}

View File

@@ -50,7 +50,7 @@ import org.testng.annotations.DataProvider;
* @since 2.6
*/
public interface TestData
{
{
/**
* A user with ALFRESCO_ADMINISTRATORS role.
* <p>"GROUP_ANOTHER_ADMIN_EXISTS" The ANOTHER_ADMIN user has been created.
@@ -141,5 +141,4 @@ public interface TestData
{ CONTENT_TYPE.toString()}
};
}
}

View File

@@ -0,0 +1,232 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 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.fileplancomponents;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.HOLDS_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE;
import static org.alfresco.rest.rm.community.util.PojoUtility.toJson;
import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import org.alfresco.rest.rm.community.base.BaseRestTest;
import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent;
import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties;
import org.alfresco.rest.rm.community.requests.FilePlanComponentAPI;
import org.alfresco.utility.data.DataUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
* Create/File electronic records tests
* <br>
* These tests only test the creation and filing of electronic records, update at
* present isn't implemented in the API under test.
* <p>
* @author Kristijan Conkas
* @since 2.6
*/
public class ElectronicRecordTests extends BaseRestTest
{
@Autowired
private FilePlanComponentAPI filePlanComponentAPI;
@Autowired
private DataUser dataUser;
/** image resource file to be used for records body */
private static final String IMAGE_FILE = "money.JPG";
/** Valid root containers where electronic records can be created */
@DataProvider(name = "invalidParentContainers")
public Object[][] invalidContainers() throws Exception {
return new Object[][] {
// record category
{ getFilePlanComponentAsUser(dataUser.getAdminUser(),
createCategoryFolderInFilePlan(dataUser.getAdminUser(), FILE_PLAN_ALIAS.toString()).getParentId()) },
// file plan root
{ getFilePlanComponentAsUser(dataUser.getAdminUser(), FILE_PLAN_ALIAS.toString()) },
// transfers
{ getFilePlanComponentAsUser(dataUser.getAdminUser(), TRANSFERS_ALIAS.toString()) },
// holds
{ getFilePlanComponentAsUser(dataUser.getAdminUser(), HOLDS_ALIAS.toString()) },
};
}
/**
* <pre>
* Given a parent container that is NOT a record folder or an unfiled record folder
* When I try to create an electronic record within the parent container
* Then nothing happens
* And an error is reported
* </pre>
* @param container
* @throws Exception
*/
@Test
(
dataProvider = "invalidParentContainers",
description = "Electronic records can't be created in invalid parent containers"
)
public void cantCreateElectronicRecordsInInvalidContainers(FilePlanComponent container) throws Exception
{
filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
FilePlanComponent record = new FilePlanComponent("Record " + getRandomAlphanumeric(), CONTENT_TYPE.toString(),
new FilePlanComponentProperties());
filePlanComponentAPI.createElectronicRecord(record, IMAGE_FILE, container.getId());
// verify the create request status code
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(UNPROCESSABLE_ENTITY);
}
/**
* <pre>
* Given a parent container that is a record folder
* And the record folder is closed
* When I try to create an electronic record within the parent container
* Then nothing happens
* And an error is reported
* </pre>
* @throws Exception
*/
@Test(description = "Electronic record can't be created in closed record folder")
public void cantCreateElectronicRecordInClosedFolder() throws Exception
{
filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
FilePlanComponent recordFolder = createCategoryFolderInFilePlan(dataUser.getAdminUser(), FILE_PLAN_ALIAS.toString());
// the folder should be open
assertFalse(recordFolder.getProperties().getIsClosed());
// close the folder
closeFolder(recordFolder.getId());
// try to create it, this should fail
FilePlanComponent record = new FilePlanComponent("Record " + getRandomAlphanumeric(), CONTENT_TYPE.toString(),
new FilePlanComponentProperties());
filePlanComponentAPI.createElectronicRecord(record, IMAGE_FILE, recordFolder.getId());
// verify the status code
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(UNPROCESSABLE_ENTITY);
}
/**
* <pre>
* Given a parent container that is a record folder
* And the record folder is open
* When I try to create an electronic record within the parent container
* And I do not provide all the required mandatory property values
* Then nothing happens
* And an error is reported
* </pre>
* and
* <pre>
* Given a parent container that is an unfiled record folder or the root unfiled record container
* When I try to create an electronic record within the parent container
* And I do not provide all the required mandatory property values
* Then nothing happens
* And an error is reported
* </pre>
* @param container
* @throws Exception
*/
@Test
(
dataProvider = "validRootContainers",
description = "Electronic record can only be created if all mandatory properties are given"
)
public void canCreateElectronicRecordOnlyWithMandatoryProperties(FilePlanComponent container) throws Exception
{
filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
logger.info("Root container:\n" + toJson(container));
if (container.getNodeType().equals(RECORD_FOLDER_TYPE.toString()))
{
// only record folders can be open or closed
assertFalse(container.getProperties().getIsClosed());
}
// component without name
FilePlanComponent record = new FilePlanComponent();
record.setNodeType(CONTENT_TYPE.toString());
record.setProperties(new FilePlanComponentProperties());
// try to create it
filePlanComponentAPI.createFilePlanComponent(record, container.getId());
// verify the status code is BAD_REQUEST
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(BAD_REQUEST);
}
/**
* <pre>
* Given a parent container that is a record folder
* And the record folder is open
* When I try to create an electronic record within the parent container
* Then the electronic record is created
* And the details of the new record are returned
* </pre>
* and
* <pre>
* Given a parent container that is an unfiled record folder or the root unfiled record container
* When I try to create an electronic record within the parent container
* Then the electronic record is created
* And the details of the new record are returned
* </pre>
* @throws Exception
*/
@Test
(
dataProvider = "validRootContainers",
description = "Electronic records can be created in unfiled record folder or unfiled record root"
)
public void canCreateElectronicRecordsInValidContainers(FilePlanComponent container) throws Exception
{
filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
FilePlanComponent record = new FilePlanComponent("Record " + getRandomAlphanumeric(), CONTENT_TYPE.toString(),
new FilePlanComponentProperties());
String newRecordId = filePlanComponentAPI.createElectronicRecord(record, IMAGE_FILE, container.getId()).getId();
// verify the create request status code
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(CREATED);
// get newly created electonic record and verify its properties
FilePlanComponent electronicRecord = filePlanComponentAPI.getFilePlanComponent(newRecordId);
// record will have record identifier inserted in its name but will for sure start with file name
// and end with its extension
assertTrue(electronicRecord.getName().startsWith(IMAGE_FILE.substring(0, IMAGE_FILE.indexOf("."))));
}
}

View File

@@ -0,0 +1,384 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 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.fileplancomponents;
import static java.util.Arrays.asList;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.FILE_PLAN_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.HOLDS_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentAlias.TRANSFERS_ALIAS;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.NON_ELECTRONIC_RECORD_TYPE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_CATEGORY_TYPE;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.RECORD_FOLDER_TYPE;
import static org.alfresco.rest.rm.community.util.PojoUtility.toJson;
import static org.alfresco.utility.data.RandomData.getRandomAlphanumeric;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import java.util.Random;
import org.alfresco.rest.rm.community.base.BaseRestTest;
import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponent;
import org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentProperties;
import org.alfresco.rest.rm.community.requests.FilePlanComponentAPI;
import org.alfresco.rest.rm.community.requests.RMSiteAPI;
import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.data.DataUser;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.UserModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.Test;
/**
* Create/File Non-Electronic Record into Unfiled Record Container/Record Folder ReST API tests
*
* @author Kristijan Conkas
* @since 2.6
*/
public class NonElectronicRecordTests extends BaseRestTest
{
@Autowired
private FilePlanComponentAPI filePlanComponentAPI;
@Autowired
private DataUser dataUser;
@Autowired
private RMSiteAPI rmSiteAPI;
/**
* <pre>
* Given a parent container that is NOT a record folder or an unfiled record folder
* When I try to create a non-electronic record within the parent container
* Then nothing happens
* And an error is reported
* </pre>
* @throws Exception if prerequisites can't be created
*/
@Test(description = "Non-electronic record can't be created as a child of invalid parent Id")
public void cantCreateForInvalidParentIds() throws Exception
{
filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
// non-electronic record object to be used for create tests
FilePlanComponent nonElectronicRecord = new FilePlanComponent(
"Record " + getRandomAlphanumeric(),
NON_ELECTRONIC_RECORD_TYPE.toString(),
new FilePlanComponentProperties());
// create record category, non-electronic records can't be its children
FilePlanComponent recordCategory = filePlanComponentAPI.createFilePlanComponent(
new FilePlanComponent("Category " + getRandomAlphanumeric(),
RECORD_CATEGORY_TYPE.toString(),
new FilePlanComponentProperties()),
FILE_PLAN_ALIAS.toString());
// iterate through all invalid parent containers and try to create/file an electronic record
asList(FILE_PLAN_ALIAS.toString(), TRANSFERS_ALIAS.toString(), HOLDS_ALIAS.toString(), recordCategory.getId())
.stream()
.forEach(id ->
{
try
{
filePlanComponentAPI.createFilePlanComponent(nonElectronicRecord, id);
}
catch (Exception error)
{
}
// Verify the status code
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(UNPROCESSABLE_ENTITY);
});
}
/**
* <pre>
* Given a parent container that is a record folder
* And the record folder is open
* When I try to create a non-electronic record within the parent container
* Then the non-electronic record is created
* And the details of the new record are returned
* <pre>
* and
* <pre>
* Given a parent container that is an unfiled record folder or the root unfiled record container
* When I try to create a non-electronic record within the parent container
* Then the non-electronic record is created
* And the details of the new record are returned
* </pre>
* @throws Exception if record can't be created
*/
@Test
(
dataProvider = "validRootContainers",
description = "Non-electronic records can be created in valid containers"
)
public void canCreateInValidContainers(FilePlanComponent container) throws Exception
{
filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
logger.info("Root container:\n" + toJson(container));
if (container.getNodeType().equals(RECORD_FOLDER_TYPE.toString()))
{
// only record folders can be open or closed
assertFalse(container.getProperties().getIsClosed());
}
// use these properties for non-electronic record to be created
String title = "Title " + getRandomAlphanumeric();
String description = "Description " + getRandomAlphanumeric();
String box = "Box "+ getRandomAlphanumeric();
String file = "File " + getRandomAlphanumeric();
String shelf = "Shelf " + getRandomAlphanumeric();
String location = "Location " + getRandomAlphanumeric();
Random random = new Random();
Integer copies = random.nextInt(Integer.MAX_VALUE);
Integer size = random.nextInt(Integer.MAX_VALUE);
// set values of all available properties
FilePlanComponentProperties properties = new FilePlanComponentProperties(title, description);
properties.setBox(box);
properties.setFile(file);
properties.setShelf(shelf);
properties.setLocation(location);
properties.setNumberOfCopies(copies);
properties.setPhysicalSize(size);
// create non-electronic record
String nonElectronicId = filePlanComponentAPI.createFilePlanComponent(
new FilePlanComponent("Record " + getRandomAlphanumeric(),
NON_ELECTRONIC_RECORD_TYPE.toString(),
properties),
container.getId()).getId();
// verify the create request status code
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(CREATED);
// get newly created non-electonic record and verify its properties
FilePlanComponent nonElectronicRecord = filePlanComponentAPI.getFilePlanComponent(nonElectronicId);
assertEquals(title, nonElectronicRecord.getProperties().getTitle());
assertEquals(description, nonElectronicRecord.getProperties().getDescription());
assertEquals(box, nonElectronicRecord.getProperties().getBox());
assertEquals(file, nonElectronicRecord.getProperties().getFile());
assertEquals(shelf, nonElectronicRecord.getProperties().getShelf());
assertEquals(location, nonElectronicRecord.getProperties().getLocation());
assertEquals(copies, nonElectronicRecord.getProperties().getNumberOfCopies());
assertEquals(size, nonElectronicRecord.getProperties().getPhysicalSize());
}
/**
* <pre>
* Given a parent container that is a record folder
* And the record folder is closed
* When I try to create a non-electronic record within the parent container
* Then nothing happens
* And an error is reported
* </pre>
* @throws Exception if prerequisites can't be created
*/
@Test(description = "Non-electronic record can't be created in closed record folder")
public void cantCreateInClosedFolder() throws Exception
{
filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
FilePlanComponent recordFolder = createCategoryFolderInFilePlan(dataUser.getAdminUser(), FILE_PLAN_ALIAS.toString());
// the folder should be open
assertFalse(recordFolder.getProperties().getIsClosed());
// close the folder
closeFolder(recordFolder.getId());
// try to create it, this should fail and throw an exception
filePlanComponentAPI.createFilePlanComponent(
new FilePlanComponent("Record " + getRandomAlphanumeric(),
NON_ELECTRONIC_RECORD_TYPE.toString(),
new FilePlanComponentProperties()),
recordFolder.getId()).getId();
// verify the status code
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(UNPROCESSABLE_ENTITY);
}
/**
* <pre>
* Given a parent container that is a record folder
* And the record folder is open
* When I try to create a non-electronic record within the parent container
* And I do not provide all the required mandatory property values
* Then nothing happens
* And an error is reported
* </pre>
* and
* <pre>
* Given a parent container that is an unfiled record folder or the root unfiled record container
* When I try to create a non-electronic record within the parent container
* And I do not provide all the required mandatory property values
* Then nothing happens
* And an error is reported
* </pre>
* @throws Exception if prerequisites can't be created
*/
@Test
(
dataProvider = "validRootContainers",
description = "Non-electronic record can only be created if all mandatory properties are given"
)
public void allMandatoryPropertiesRequired(FilePlanComponent container) throws Exception
{
filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
logger.info("Root container:\n" + toJson(container));
if (container.getNodeType().equals(RECORD_FOLDER_TYPE.toString()))
{
// only record folders can be open or closed
assertFalse(container.getProperties().getIsClosed());
}
// component without name and title
FilePlanComponent noNameOrTitle = getDummyNonElectronicRecord();
// component with title only
FilePlanComponent titleOnly = getDummyNonElectronicRecord();
FilePlanComponentProperties properties = new FilePlanComponentProperties();
properties.setTitle("Title " + getRandomAlphanumeric());
titleOnly.setProperties(properties);
// try to create invalid components
asList(noNameOrTitle, titleOnly).stream().forEach(c ->
{
try
{
logger.info("Creating non-electronic record with body:\n" + toJson(c));
}
catch (Exception error)
{
}
// this should fail and throw an exception
try
{
filePlanComponentAPI.createFilePlanComponent(c, container.getId());
}
catch (Exception e)
{
}
// verify the status code is BAD_REQUEST
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(BAD_REQUEST);
});
}
/**
* <pre>
* Given that I am a user without RM privileges
* When I try to create a non-electronic record
* Then nothing happens
* And an error is reported
* </pre>
* @throws Exception
*/
@Test
(
dataProvider = "validRootContainers",
description = "Non-electronic record can't be created if user doesn't have RM privileges"
)
public void cantCreateIfNoRmPrivileges(FilePlanComponent container) throws Exception
{
String username = "zzzuser";
UserModel user = createUserWithRole(username, UserRole.SiteManager);
filePlanComponentAPI.usingRestWrapper().authenticateUser(user);
// try to create a fileplan component
FilePlanComponent record = new FilePlanComponent("Record Name", NON_ELECTRONIC_RECORD_TYPE.toString(),
new FilePlanComponentProperties("Name", "Title"));
// this should fail and throw an exception
try
{
filePlanComponentAPI.createFilePlanComponent(record, container.getId());
}
catch (Exception e)
{
}
// user who isn't an RM site member can't access the container path
filePlanComponentAPI.usingRestWrapper().assertStatusCodeIs(FORBIDDEN);
}
/**
* Helper function to return an empty FilePlanComponent for non-electronic record
* @return
*/
private FilePlanComponent getDummyNonElectronicRecord()
{
FilePlanComponent component = new FilePlanComponent();
component.setNodeType(NON_ELECTRONIC_RECORD_TYPE.toString());
return component;
}
/**
* Create user with given role and add it to RM site
* <br>
* Checks whether the user exists in RM site and creates it if required, with password identical
* to username. Note the role is a Core API role, not an RM role.
* <br>
* For already existing users, no site membership or role verification is performed.
* <p>
* @param userName username to add
* @param userRole user's role
* @throws Exception
*/
private UserModel createUserWithRole(String userName, UserRole userRole) throws Exception
{
rmSiteAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
String siteId = rmSiteAPI.getSite().getId();
// check if user exists
UserModel user = new UserModel();
user.setUsername(userName);
user.setPassword(userName);
if (!dataUser.isUserInRepo(userName))
{
// user doesn't exist, create it
user = dataUser.createUser(userName, userName);
user.setUserRole(userRole);
dataUser.addUserToSite(user, new SiteModel(siteId), userRole);
}
return user;
}
}

View File

@@ -100,7 +100,7 @@ public class UnfiledRecordsFolderTests extends BaseRestTest
public void createRootUnfiledRecordsFolder() throws Exception
{
// Authenticate with admin user
RestWrapper restWrapper = filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
String folderName = "Folder " + getRandomAlphanumeric();
String folderTitle = folderName + " Title";
@@ -148,7 +148,7 @@ public class UnfiledRecordsFolderTests extends BaseRestTest
)
public void onlyRecordFoldersCanBeCreatedAtUnfiledRecordsRoot(FilePlanComponentType componentType)
{
RestWrapper restWrapper = filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
filePlanComponentAPI.usingRestWrapper().authenticateUser(dataUser.getAdminUser());
String folderName = "Folder " + getRandomAlphanumeric();
String folderTitle = folderName + " Title";

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 KiB

View File

@@ -292,10 +292,16 @@
<type>d:int</type>
<mandatory>false</mandatory>
<index enabled="true">
<atomic>true</atomic>
<stored>false</stored>
<tokenised>false</tokenised>
</index>
<atomic>true</atomic>
<stored>false</stored>
<tokenised>false</tokenised>
</index>
<constraints>
<constraint type="MINMAX">
<parameter name="minValue"><value>0</value></parameter>
<parameter name="maxValue"><value>2147483647</value></parameter> <!--MAX_INT -->
</constraint>
</constraints>
</property>
<property name="rma:numberOfCopies">
@@ -304,10 +310,16 @@
<mandatory>false</mandatory>
<default>1</default>
<index enabled="true">
<atomic>true</atomic>
<stored>false</stored>
<tokenised>false</tokenised>
</index>
<atomic>true</atomic>
<stored>false</stored>
<tokenised>false</tokenised>
</index>
<constraints>
<constraint type="MINMAX">
<parameter name="minValue"><value>0</value></parameter>
<parameter name="maxValue"><value>2147483647</value></parameter> <!--MAX_INT -->
</constraint>
</constraints>
</property>
<property name="rma:storageLocation">

View File

@@ -49,6 +49,10 @@
<property name="nodes" ref="rm.Nodes" />
</bean>
<bean class="org.alfresco.rm.rest.api.nodes.RecordsEntityResource">
<property name="nodes" ref="rm.Nodes" />
</bean>
<bean class="org.alfresco.rm.rest.api.nodes.FileplanComponentChildrenRelation">
<property name="nodes" ref="rm.Nodes" />
</bean>

View File

@@ -268,6 +268,12 @@
<groupId>${alfresco.groupId}</groupId>
<artifactId>alfresco-remote-api</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.takari.junit</groupId>
<artifactId>takari-cpsuite</artifactId>

View File

@@ -31,6 +31,9 @@ import java.util.ArrayList;
import java.util.List;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.core.exceptions.ApiException;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.MultiPartRelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
@@ -59,12 +62,14 @@ public class FileplanComponentChildrenRelation implements RelationshipResourceAc
}
@Override
@WebApiDescription(title = "Return a paged list of fileplan components for the container identified by parentFolderNodeId")
public CollectionWithPagingInfo<Node> readAll(String parentFolderNodeId, Parameters parameters)
{
return nodes.listChildren(parentFolderNodeId, parameters);
}
@Override
@WebApiDescription(title="Create one (or more) nodes as children of container identified by parentFolderNodeId")
public List<Node> create(String parentFolderNodeId, List<Node> nodeInfos, Parameters parameters)
{
List<Node> result = new ArrayList<>(nodeInfos.size());
@@ -78,8 +83,29 @@ public class FileplanComponentChildrenRelation implements RelationshipResourceAc
}
@Override
@WebApiDescription(title = "Upload file content and meta-data into the repository.")
@WebApiParam(name = "formData", title = "A single form data", description = "A single form data which holds FormFields.")
public Node create(String parentFolderNodeId, FormData formData, Parameters parameters, WithResponse withResponse)
{
return nodes.upload(parentFolderNodeId, formData, parameters);
try
{
return nodes.upload(parentFolderNodeId, formData, parameters);
}
catch (ApiException apiException)
{
/*
* The upload method encapsulates most exceptions that can occur on node creation in an ApiException.
* To allow the API framework to correctly map the exception to the API error code we throw the original exception.
*/
Throwable originalException = apiException.getCause();
if (originalException != null && originalException instanceof RuntimeException)
{
throw (RuntimeException) originalException;
}
else
{
throw apiException;
}
}
}
}

View File

@@ -0,0 +1,106 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 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.nodes;
import java.io.InputStream;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction;
import org.alfresco.rest.framework.resource.content.BasicContentInfo;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.RMNodes;
import org.alfresco.util.ParameterCheck;
import org.springframework.beans.factory.InitializingBean;
/**
* An implementation of an Entity Resource for a record
*
* @author Ana Bozianu
* @since 2.6
*/
@EntityResource(name="records", title = "Records")
public class RecordsEntityResource implements BinaryResourceAction.Update<Node>,
BinaryResourceAction.Read,
InitializingBean
{
private RMNodes nodes;
public void setNodes(RMNodes nodes)
{
this.nodes = nodes;
}
@Override
public void afterPropertiesSet() throws Exception
{
ParameterCheck.mandatory("nodes", this.nodes);
}
/**
* Download content
*
* @param recordId the id of the record to get the content from
* @param parameters {@link Parameters}
* @return binary content resource
* @throws EntityNotFoundException
*/
@Override
@WebApiDescription(title = "Download content", description = "Download content")
@BinaryProperties({"content"})
public BinaryResource readProperty(String recordId, Parameters parameters) throws EntityNotFoundException
{
return nodes.getContent(recordId, parameters, true);
}
/**
* Upload new version of content
*
* This allow binary content update of an existing record.
*
* Note: alternatively, can upload via POST (multipart/form-data) with existing file name and form "overwrite=true".
*
* @param recordId the id of the record to set the content for
* @param contentInfo Basic information about the content stream
* @param stream an inputstream representing the new content of the node
* @param parameters {@link Parameters}
* @return information about the record that has been updated
*/
@Override
@WebApiDescription(title = "Upload content", description = "Upload content")
@BinaryProperties({"content"})
public Node updateProperty(String recordId, BasicContentInfo contentInfo, InputStream stream, Parameters parameters)
{
return nodes.updateContent(recordId, contentInfo, stream, parameters);
}
}

View File

@@ -19,6 +19,8 @@ tags:
description: Retrieve and manage fileplan components
- name: ig-sites
description: Retrieve and manage the RM site
- name: records
description: Perform record specific operations
paths:
'/fileplan-components/{fileplanComponentId}':
get:
@@ -47,6 +49,8 @@ paths:
Invalid parameter: **fileplanComponentId** is not a valid format
'401':
description: If authentication fails
'403':
description: If current user does not have permission to read **fileplanComponentId**
'404':
description: If **fileplanComponentId** does not exist
default:
@@ -180,6 +184,8 @@ paths:
$ref: '#/definitions/IGNodeAssociationPaging'
'401':
description: If authentication fails
'403':
description: If current user does not have permission to read **fileplanComponentId**
'404':
description: If **fileplanComponentId** does not exist
default:
@@ -194,9 +200,10 @@ paths:
Create a fileplan component as a primary child of node **fileplanComponentId**.
This API method supports file upload using multipart/form-data.
Electronic records are the only nodes that have content.
Use the **filedata** field to represent the content to upload.
You can use a **name** field to give an alternative name for the new file.
You can use a **name** field to give an alternative name for the new electronic record.
For multipart/form-data upload you can use the **renditions** field to create renditions (e.g. doclib) asynchronously upon upload.
Note that currently only one rendition can be requested. Also, as requesting rendition is a background process,
@@ -280,6 +287,14 @@ paths:
}
```
You can create an empty electronic record and use the record endpoint to create content:
```JSON
{
"name":"My Electronic Record",
"nodeType":"cm:content"
}
```
You can create a fileplan component inside a container hierarchy:
```JSON
{
@@ -547,6 +562,123 @@ paths:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
'/records/{recordId}/content':
get:
x-alfresco-since: "5.2"
tags:
- records
summary: Get record content
description: |
Gets the content of the record with identifier **recordId**.
operationId: getRecordContent
parameters:
- $ref: '#/parameters/recordIdParam'
- $ref: '#/parameters/attachmentParam'
- $ref: '#/parameters/ifModifiedSinceHeader'
responses:
'200':
description: Successful response
'304':
description: Content has not been modified since the date provided in the If-Modified-Since header
'400':
description: |
Invalid parameter: **nodeId** is not a valid format, or is not a file
'401':
description: Authentication failed
'404':
description: |
**nodeId** does not exist
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
put:
x-alfresco-since: "5.2"
tags:
- records
summary: Update record content
description: |
Updates the content of the record with identifier **recordId**.
The request body for this endpoint can be any text or binary stream.
The **majorVersion** and **comment** parameters can be used to control versioning behaviour. If the content is versionable,
a new minor version is created by default.
Optionally a new **name** parameter can also be specified that must be unique within the parent folder. If specified and valid then this
will rename the node. If invalid then an error is returned and the content is not updated.
**Note:** This API method accepts any content type, but for testing with this tool text based content can be provided.
This is because the OpenAPI Specification does not allow a wildcard to be provided or the ability for
tooling to accept an arbitrary file.
operationId: updateRecordContent
parameters:
- $ref: '#/parameters/recordIdParam'
- name: majorVersion
in: query
description: |
If **true**, create a major version.
Setting this parameter also enables versioning of this node, if it is not already versioned.
required: false
type: boolean
default: false
- name: comment
in: query
description: |
Add a version comment which will appear in version history.
Setting this parameter also enables versioning of this node, if it is not already versioned.
required: false
type: string
- name: name
in: query
description: |
Optional new name. This should include the file extension.
The name must not contain spaces or the following special characters: * " < > \ / ? : and |.
The character `.` must not be used at the end of the name.
required: false
type: string
pattern: "^(?!(.*[\\\"\\*\\\\\\>\\<\\?\\/\\:\\|]+.*)|(.*[\\.]?.*[\\.]+$)|(.*[ ]+$))"
- $ref: '#/parameters/IGNodeEntryIncludeParam'
- $ref: '#/parameters/fieldsParam'
- in: body
name: contentBodyUpdate
description: The binary content
required: true
schema:
type: string
format: binary
produces:
- application/json
consumes:
- application/octet-stream
responses:
'200':
description: Successful response
schema:
$ref: '#/definitions/IGNodeEntry'
'400':
description: |
Invalid parameter: **recordId** is not a valid format, or is not a file
'401':
description: Authentication failed
'403':
description: Current user does not have permission to update **recordId**
'404':
description: |
**recordId** does not exist
'409':
description: Optional new name clashes with an existing node in the current parent folder
'413':
description: Content exceeds individual file size limit (configured for network/system)
'422':
description: Model integrity exception including a file name containing invalid characters
'507':
description: Content exceeds overall storage quota limit configured for the network/system
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
parameters:
fileplanComponentIdWithAliasParam:
name: fileplanComponentId
@@ -609,13 +741,18 @@ parameters:
items:
type: string
collectionFormat: csv
## Core definition
fileplanComponentIdParam:
name: fileplanComponentId
in: path
description: The identifier of a fileplan compoment.
required: true
type: string
recordIdParam:
name: recordId
in: path
description: The identifier of a record.
required: true
type: string
## Core definition
fieldsParam:
name: fields
@@ -670,6 +807,33 @@ parameters:
items:
type: string
collectionFormat: csv
# Core definition
attachmentParam:
name: attachment
in: query
description: |
**true** enables a web browser to download the file as an attachment.
**false** means a web browser may preview the file in a new tab or window, but not
download the file.
You can only set this parameter to **false** if the content type of the file is in the supported list;
for example, certain image files and PDF files.
If the content type is not supported for preview, then a value of **false** is ignored, and
the attachment will be returned in the response.
required: false
default: true
type: boolean
# Core definition
ifModifiedSinceHeader:
name: If-Modified-Since
in: header
description: |
Only returns the content if it has been modified since the date provided.
Use the date format defined by HTTP. For example, `Wed, 09 Mar 2016 16:56:34 GMT`.
required: false
type: string
format: date-time
definitions:
IGNodeEntry:
type: object