ACS-1988 Implement DAU Version Renditions code (#931)

* ACS-1988 Implement DAU Version Renditions code

* ACS-1988 Replace directAccessUrlRequest with request-direct-access-url

* ACS-1988 fix and move DAU version rendition test

* ACS-1988 Add NodeVersionRenditionsApiTest to test suite

* ACS-1988 Remove unused import

* ACS-1988 add error response tests

* ACS-1988 remove redundant constant

* ACS-1988 updates from review

* ACS-1988 updates from review
This commit is contained in:
Sara
2022-02-11 11:35:28 +00:00
committed by GitHub
parent 105c83ca7e
commit 798a3faa5b
5 changed files with 229 additions and 1 deletions

View File

@@ -26,18 +26,28 @@
package org.alfresco.rest.api.nodes; package org.alfresco.rest.api.nodes;
import javax.servlet.http.HttpServletResponse;
import java.util.List; import java.util.List;
import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException;
import org.alfresco.rest.api.DirectAccessUrlHelper;
import org.alfresco.rest.api.Renditions; import org.alfresco.rest.api.Renditions;
import org.alfresco.rest.api.model.DirectAccessUrlRequest;
import org.alfresco.rest.api.model.Rendition; import org.alfresco.rest.api.model.Rendition;
import org.alfresco.rest.framework.BinaryProperties; import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.WebApiDescription; import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.core.ResourceParameter;
import org.alfresco.rest.framework.core.exceptions.DisabledServiceException;
import org.alfresco.rest.framework.resource.RelationshipResource; import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction; import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
import org.alfresco.rest.framework.resource.content.BinaryResource; import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.PropertyCheck; import org.alfresco.util.PropertyCheck;
@@ -53,6 +63,7 @@ import org.springframework.extensions.webscripts.Status;
* - GET /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId} * - GET /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}
* - DELETE /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId} * - DELETE /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}
* - GET /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/content * - GET /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/content
* - POST /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/request-direct-access-url
* *
* @author janv * @author janv
*/ */
@@ -65,12 +76,18 @@ public class NodeVersionRenditionsRelation implements RelationshipResourceAction
InitializingBean InitializingBean
{ {
private Renditions renditions; private Renditions renditions;
private DirectAccessUrlHelper directAccessUrlHelper;
public void setRenditions(Renditions renditions) public void setRenditions(Renditions renditions)
{ {
this.renditions = renditions; this.renditions = renditions;
} }
public void setDirectAccessUrlHelper(DirectAccessUrlHelper directAccessUrlHelper)
{
this.directAccessUrlHelper = directAccessUrlHelper;
}
@Override @Override
public void afterPropertiesSet() throws Exception public void afterPropertiesSet() throws Exception
{ {
@@ -117,6 +134,36 @@ public class NodeVersionRenditionsRelation implements RelationshipResourceAction
return renditions.getContent(nodeRef, versionId, renditionId, parameters); return renditions.getContent(nodeRef, versionId, renditionId, parameters);
} }
@Operation ("request-direct-access-url")
@WebApiParam (name = "directAccessUrlRequest",
title = "Request direct access url",
description = "Options for direct access url request",
kind = ResourceParameter.KIND.HTTP_BODY_OBJECT)
@WebApiDescription(title = "Request content url",
description="Generates a direct access URL.",
successStatus = HttpServletResponse.SC_OK)
public DirectAccessUrl requestContentDirectUrl(String nodeId,
String versionId,
DirectAccessUrlRequest directAccessUrlRequest,
Parameters parameters, WithResponse withResponse)
{
boolean attachment = directAccessUrlHelper.getAttachment(directAccessUrlRequest);
Long validFor = directAccessUrlHelper.getDefaultExpiryTimeInSec();
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
String renditionId = parameters.getRelationship2Id();
DirectAccessUrl directAccessUrl;
try
{
directAccessUrl = renditions.requestContentDirectUrl(nodeRef, versionId, renditionId, attachment, validFor);
}
catch (DirectAccessUrlDisabledException ex)
{
throw new DisabledServiceException(ex.getMessage());
}
return directAccessUrl;
}
@WebApiDescription(title = "Delete rendition") @WebApiDescription(title = "Delete rendition")
@Override @Override
public void delete(String nodeId, String versionId, Parameters parameters) public void delete(String nodeId, String versionId, Parameters parameters)

View File

@@ -1452,6 +1452,7 @@
<bean class="org.alfresco.rest.api.nodes.NodeVersionRenditionsRelation"> <bean class="org.alfresco.rest.api.nodes.NodeVersionRenditionsRelation">
<property name="renditions" ref="Renditions" /> <property name="renditions" ref="Renditions" />
<property name="directAccessUrlHelper" ref="directAccessUrlHelper" />
</bean> </bean>
<bean class="org.alfresco.rest.api.quicksharelinks.QuickShareLinkRenditionsRelation"> <bean class="org.alfresco.rest.api.quicksharelinks.QuickShareLinkRenditionsRelation">

View File

@@ -53,6 +53,7 @@ import org.junit.runners.Suite;
org.alfresco.rest.api.tests.NodeApiTest.class, org.alfresco.rest.api.tests.NodeApiTest.class,
org.alfresco.rest.api.tests.NodeAssociationsApiTest.class, org.alfresco.rest.api.tests.NodeAssociationsApiTest.class,
org.alfresco.rest.api.tests.NodeVersionsApiTest.class, org.alfresco.rest.api.tests.NodeVersionsApiTest.class,
org.alfresco.rest.api.tests.NodeVersionRenditionsApiTest.class,
org.alfresco.rest.api.tests.QueriesNodesApiTest.class, org.alfresco.rest.api.tests.QueriesNodesApiTest.class,
org.alfresco.rest.api.tests.QueriesPeopleApiTest.class, org.alfresco.rest.api.tests.QueriesPeopleApiTest.class,
org.alfresco.rest.api.tests.QueriesSitesApiTest.class, org.alfresco.rest.api.tests.QueriesSitesApiTest.class,

View File

@@ -25,6 +25,7 @@
*/ */
package org.alfresco.rest.api.tests; package org.alfresco.rest.api.tests;
import org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig;
import org.alfresco.rest.api.tests.client.PublicApiHttpClient; import org.alfresco.rest.api.tests.client.PublicApiHttpClient;
import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsString; import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsString;
import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsStringNonNull; import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsStringNonNull;
@@ -219,6 +220,11 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
return URL_NODES + "/" + nodeId + "/" + REQUEST_DIRECT_ACCESS_URL; return URL_NODES + "/" + nodeId + "/" + REQUEST_DIRECT_ACCESS_URL;
} }
protected String getRequestVersionRenditionContentDirectUrl(String nodeId, String versionId, String renditionId)
{
return getNodeVersionRenditionIdUrl(nodeId, versionId, renditionId) + "/" + REQUEST_DIRECT_ACCESS_URL;
}
protected String getRequestArchivedContentDirectUrl(String nodeId) protected String getRequestArchivedContentDirectUrl(String nodeId)
{ {
return URL_DELETED_NODES + "/" + nodeId + "/" + REQUEST_DIRECT_ACCESS_URL; return URL_DELETED_NODES + "/" + nodeId + "/" + REQUEST_DIRECT_ACCESS_URL;
@@ -724,6 +730,18 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
return createNode(parentId, folderName, TYPE_CM_FOLDER, props, Folder.class); return createNode(parentId, folderName, TYPE_CM_FOLDER, props, Folder.class);
} }
protected String createUniqueFolder(String parentId) throws Exception
{
return createFolder(parentId, "folder-" + System.currentTimeMillis()).getId();
}
protected String createUniqueContent(String folderId) throws Exception
{
Document documentResp = createTextFile(folderId, "file-" + System.currentTimeMillis(),
"some text-" + System.currentTimeMillis(), "UTF-8", null);
return documentResp.getId();
}
protected Node createNode(String parentId, String nodeName, String nodeType, Map<String, Object> props) throws Exception protected Node createNode(String parentId, String nodeName, String nodeType, Map<String, Object> props) throws Exception
{ {
return createNode(parentId, nodeName, nodeType, props, Node.class); return createNode(parentId, nodeName, nodeType, props, Node.class);
@@ -1075,5 +1093,16 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
return URL_NODES + "/" + nodeId; return URL_NODES + "/" + nodeId;
} }
protected void enableRestDirectAccessUrls()
{
RestApiDirectUrlConfig dauConfig = (RestApiDirectUrlConfig) applicationContext.getBean("restApiDirectUrlConfig");
dauConfig.setEnabled(true);
}
protected void disableRestDirectAccessUrls()
{
RestApiDirectUrlConfig dauConfig = (RestApiDirectUrlConfig) applicationContext.getBean("restApiDirectUrlConfig");
dauConfig.setEnabled(false);
}
} }

View File

@@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Remote API * Alfresco Remote API
* %% * %%
* Copyright (C) 2005 - 2020 Alfresco Software Limited * Copyright (C) 2005 - 2022 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of * If the software was purchased under a paid Alfresco license, the terms of
@@ -27,20 +27,32 @@ package org.alfresco.rest.api.tests;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.web.scripts.RepositoryContainer;
import org.alfresco.rest.AbstractSingleNetworkSiteTest; import org.alfresco.rest.AbstractSingleNetworkSiteTest;
import org.alfresco.rest.api.DirectAccessUrlHelper;
import org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig;
import org.alfresco.rest.api.model.Site;
import org.alfresco.rest.api.nodes.NodesEntityResource;
import org.alfresco.rest.api.tests.client.HttpResponse; import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.client.PublicApiClient.Paging; import org.alfresco.rest.api.tests.client.PublicApiClient.Paging;
import org.alfresco.rest.api.tests.client.data.ContentInfo; import org.alfresco.rest.api.tests.client.data.ContentInfo;
import org.alfresco.rest.api.tests.client.data.Document; import org.alfresco.rest.api.tests.client.data.Document;
import org.alfresco.rest.api.tests.client.data.Node; import org.alfresco.rest.api.tests.client.data.Node;
import org.alfresco.rest.api.tests.client.data.Rendition; import org.alfresco.rest.api.tests.client.data.Rendition;
import org.alfresco.rest.api.tests.util.MultiPartBuilder;
import org.alfresco.rest.api.tests.util.MultiPartBuilder.FileData;
import org.alfresco.rest.api.tests.util.MultiPartBuilder.MultiPartRequest;
import org.alfresco.rest.api.tests.util.RestApiUtil; import org.alfresco.rest.api.tests.util.RestApiUtil;
import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsString; import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsString;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.junit.After;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito;
import java.io.File;
import java.util.*; import java.util.*;
/** /**
@@ -273,6 +285,138 @@ public class NodeVersionRenditionsApiTest extends AbstractSingleNetworkSiteTest
} }
} }
@Test
public void testRequestVersionRenditionContentDirectUrl() throws Exception
{
setRequestContext(user1);
RepoService.TestNetwork networkN1;
RepoService.TestPerson userOneN1;
Site userOneN1Site;
networkN1 = repoService.createNetworkWithAlias("ping", true);
networkN1.create();
userOneN1 = networkN1.createUser();
setRequestContext(networkN1.getId(), userOneN1.getId(), null);
String siteTitle = "RandomSite" + System.currentTimeMillis();
userOneN1Site = createSite(siteTitle, SiteVisibility.PRIVATE);
String PROP_LTM = "cm:lastThumbnailModification";
String RENDITION_NAME = "imgpreview";
String userId = userOneN1.getId();
setRequestContext(networkN1.getId(), userOneN1.getId(), null);
// Create a folder within the site document's library
String folderName = "folder" + System.currentTimeMillis();
String parentId = getSiteContainerNodeId(userOneN1Site.getId(), "documentLibrary");
String folder_Id = createNode(parentId, folderName, TYPE_CM_FOLDER, null).getId();
// Create multipart request - pdf file
String fileName = "quick.pdf";
File file = getResourceFile(fileName);
MultiPartRequest reqBody = MultiPartBuilder.create()
.setFileData(new FileData(fileName, file))
.build();
Map<String, String> params = Collections.singletonMap("include", "properties");
// Upload quick.pdf file into 'folder' - do not include request to create 'doclib' thumbnail
HttpResponse response = post(getNodeChildrenUrl(folder_Id), reqBody.getBody(), params, null, "alfresco", reqBody.getContentType(), 201);
Document document1 = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
String contentNodeId = document1.getId();
assertNotNull(document1.getProperties());
// pause briefly
Thread.sleep(DELAY_IN_MS);
// Get rendition (not created yet) information for node
response = getSingle(getNodeRenditionsUrl(contentNodeId), RENDITION_NAME, 200);
Rendition rendition = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Rendition.class);
assertNotNull(rendition);
assertEquals(Rendition.RenditionStatus.NOT_CREATED, rendition.getStatus());
params = new HashMap<>();
params.put("placeholder", "false");
getSingle(getNodeRenditionsUrl(contentNodeId), (RENDITION_NAME + "/content"), params, 404);
// Create and get 'imgpreview' rendition
rendition = createAndGetRendition(contentNodeId, RENDITION_NAME);
assertNotNull(rendition);
params = new HashMap<>();
params.put("placeholder", "false");
response = getSingle(getNodeRenditionsUrl(contentNodeId), (RENDITION_NAME + "/content"), params, 200);
byte[] renditionBytes1 = response.getResponseAsBytes();
assertNotNull(renditionBytes1);
// check node details ...
params = Collections.singletonMap("include", "properties");
response = getSingle(NodesEntityResource.class, contentNodeId, params, 200);
Document document1b = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
assertEquals(document1b.getModifiedByUser().getId(), document1.getModifiedByUser().getId());
// upload another version of "quick.pdf" and check again
fileName = "quick-2.pdf";
file = getResourceFile(fileName);
reqBody = MultiPartBuilder.create()
.setFileData(new FileData("quick.pdf", file))
.setOverwrite(true)
.build();
response = post(getNodeChildrenUrl(folder_Id), reqBody.getBody(), null, null, "alfresco", reqBody.getContentType(), 201);
Document document2 = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
assertEquals(contentNodeId, document2.getId());
// wait to allow new version of the rendition to be created ...
Thread.sleep(DELAY_IN_MS * 4);
params = new HashMap<>();
params.put("placeholder", "false");
response = getSingle(getNodeRenditionsUrl(contentNodeId), (RENDITION_NAME + "/content"), params, 200);
assertNotNull(response.getResponseAsBytes());
// check rendition binary has changed
assertNotEquals(renditionBytes1, response.getResponseAsBytes());
params = Collections.singletonMap("include", "properties");
response = getSingle(NodesEntityResource.class, contentNodeId, params, 200);
Document document2b = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
String contentNodeId2b = document2b.getId();
HttpResponse dauResponse = post(getRequestVersionRenditionContentDirectUrl(contentNodeId2b, "1.0", RENDITION_NAME), null, null, null, null, 501);
}
@Test
public void testRequestVersionRenditionContentDirectUrlErrorResponses () throws Exception
{
setRequestContext(user1);
String folderNodeId = createUniqueFolder(getMyNodeId());
String contentNodeId = createUniqueContent(folderNodeId);
createVersionRendition(contentNodeId, "1.0", "doclib");
// REST direct access URLs must be enabled in order to test DAU error responses
enableRestDirectAccessUrls();
// Test error response for node does not exist
HttpResponse dauResponseForNoSuchNode = post(getRequestVersionRenditionContentDirectUrl("nosuchnode", "1.0", "doclib"), null, 404);
// Test error response for node is not a file
HttpResponse dauResponseForNodeIsNotAFile = post(getRequestVersionRenditionContentDirectUrl(folderNodeId, "1.0", "doclib"), null, 400);
// Test error response for version does not exist
HttpResponse dauResponseForNoSuchVersion = post(getRequestVersionRenditionContentDirectUrl(contentNodeId, "2.0", "doclib"), null, 404);
// Test error response for rendition does not exist
HttpResponse dauResponseForNoSuchRendition = post(getRequestVersionRenditionContentDirectUrl(contentNodeId, "1.0", "avatar"), null, 404);
disableRestDirectAccessUrls();
}
private void checkCreateAndGetVersionRendition(String docId, String versionId, String renditionId) throws Exception private void checkCreateAndGetVersionRendition(String docId, String versionId, String renditionId) throws Exception
{ {
String getRenditionsUrl; String getRenditionsUrl;
@@ -361,6 +505,12 @@ public class NodeVersionRenditionsApiTest extends AbstractSingleNetworkSiteTest
assertTrue(contentInfo.getSizeInBytes() > 0); assertTrue(contentInfo.getSizeInBytes() > 0);
} }
private void createVersionRendition(String contentNodeId, String versionId, String renditionId) throws Exception
{
getAll(getNodeVersionRenditionsUrl(contentNodeId, versionId), null, 200);
checkCreateAndGetVersionRendition(contentNodeId, versionId, renditionId);
}
@Override @Override
public String getScope() public String getScope()
{ {