ACS-197 / ACS-207 : Add REST API for Version Renditions (#654)

* ACS-197 / ACS-207 : Add REST API for Version Renditions

- add four new endpoints for version-specific renditions and automated tests (+ve & -ve)

-- GET /nodes/{nodeId}/versions/{versionId}/renditions - List renditions for a version
-- POST /nodes/{nodeId}/versions/{versionId}/renditions - Create rendition for a version
-- GET /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId} - Get rendition information for a version
-- GET /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/content - Get rendition content for a version

- note: the RenditionsImpl implementation is also tested via RenditionsTest
This commit is contained in:
montgolfiere
2020-05-27 11:34:23 +01:00
committed by GitHub
parent e41ac017cb
commit 5cd8899a52
9 changed files with 718 additions and 152 deletions

View File

@@ -18,6 +18,7 @@ branches:
only:
- master
- /support\/.*/
- /feature\/.*/
stages:
- test

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -39,36 +39,61 @@ import java.util.List;
/**
* Renditions API
*
* @author Jamal Kaabi-Mofrad
* @author Jamal Kaabi-Mofrad, janv
*/
public interface Renditions
{
String PARAM_STATUS = "status";
String PARAM_ATTACHMENT = "attachment";
String PARAM_PLACEHOLDER = "placeholder";
/**
* Lists all available renditions includes those that have been created and those that are yet to be created.
*
* @param nodeRef
* @param nodeRef the source/live nodeRef
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @return the rendition results
*/
CollectionWithPagingInfo<Rendition> getRenditions(NodeRef nodeRef, Parameters parameters);
/**
* Lists all available renditions includes those that have been created and those that are yet to be created.
*
* @param nodeRef the source/live nodeRef
* @param versionId the version id (aka version label)
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @return the rendition results
*/
CollectionWithPagingInfo<Rendition> getRenditions(NodeRef nodeRef, String versionId, Parameters parameters);
/**
* Gets information about a rendition of a node in the repository.
* If there is no rendition, then returns the available/registered rendition.
*
* @param nodeRef
* @param nodeRef the source nodeRef, ie. live node
* @param renditionId the rendition id
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @return the {@link Rendition} object
*/
Rendition getRendition(NodeRef nodeRef, String renditionId, Parameters parameters);
/**
* Gets information about a rendition of a node in the repository.
* If there is no rendition, then returns the available/registered rendition.
*
* @param nodeRef the source nodeRef, ie. live node
* @param versionId the version id (aka version label)
* @param renditionId the rendition id
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @return the {@link Rendition} object
*/
Rendition getRendition(NodeRef nodeRef, String versionId, String renditionId, Parameters parameters);
/**
* Creates a rendition for the given node asynchronously.
*
* @param nodeRef
* @param nodeRef the source nodeRef, ie. live node
* @param rendition the {@link Rendition} request
* @param parameters the {@link Parameters} object to get the parameters passed into the request
*/
@@ -77,18 +102,29 @@ public interface Renditions
/**
* Creates a rendition for the given node - either async r sync
*
* @param nodeRef
* @param rendition
* @param nodeRef the source nodeRef, ie. live node
* @param rendition the {@link Rendition} request
* @param executeAsync
* @param parameters
*/
void createRendition(NodeRef nodeRef, Rendition rendition, boolean executeAsync, Parameters parameters);
/**
* Creates a rendition for the given node - either async r sync
*
* @param nodeRef the source nodeRef, ie. live node
* @param versionId the version id (aka version label)
* @param rendition the {@link Rendition} request
* @param executeAsync
* @param parameters
*/
void createRendition(NodeRef nodeRef, String versionId, Rendition rendition, boolean executeAsync, Parameters parameters);
/**
* Creates renditions that don't already exist for the given node asynchronously.
*
* @param nodeRef
* @param renditions the {@link Rendition} request
* @param nodeRef the source nodeRef, ie. live node
* @param renditions the list of {@link Rendition} requests
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @throws NotFoundException if any of the rendition id do not exist.
* @throws ConstraintViolatedException if all of the renditions already exist.
@@ -97,23 +133,58 @@ public interface Renditions
throws NotFoundException, ConstraintViolatedException;
/**
* Downloads rendition.
* Creates renditions that don't already exist for the given node asynchronously.
*
* @param sourceNodeRef the source nodeRef
* @param renditionId the rendition id
* @param nodeRef the source nodeRef, ie. live node
* @param versionId the version id (aka version label)
* @param renditions the list of {@link Rendition} requests
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @return the rendition stream
* @throws NotFoundException if any of the rendition id do not exist.
* @throws ConstraintViolatedException if all of the renditions already exist.
*/
BinaryResource getContent(NodeRef sourceNodeRef, String renditionId, Parameters parameters);
void createRenditions(NodeRef nodeRef, String versionId, List<Rendition> renditions, Parameters parameters)
throws NotFoundException, ConstraintViolatedException;
/**
* Downloads rendition.
*
* @param sourceNodeRef the source nodeRef
* @param nodeRef the source nodeRef, ie. live node
* @param renditionId the rendition id
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @return the rendition stream
*/
BinaryResource getContentNoValidation(NodeRef sourceNodeRef, String renditionId, Parameters parameters);
BinaryResource getContent(NodeRef nodeRef, String renditionId, Parameters parameters);
/**
* Downloads rendition.
*
* @param nodeRef the source nodeRef, ie. live node
* @param versionId the version id (aka version label)
* @param renditionId the rendition id
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @return the rendition stream
*/
BinaryResource getContent(NodeRef nodeRef, String versionId, String renditionId, Parameters parameters);
/**
* Downloads rendition.
*
* @param nodeRef the source nodeRef, ie. live node
* @param renditionId the rendition id
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @return the rendition stream
*/
BinaryResource getContentNoValidation(NodeRef nodeRef, String renditionId, Parameters parameters);
/**
* Downloads rendition.
*
* @param nodeRef the source nodeRef, ie. live node
* @param versionId the version id (aka version label)
* @param renditionId the rendition id
* @param parameters the {@link Parameters} object to get the parameters passed into the request
* @return the rendition stream
*/
BinaryResource getContentNoValidation(NodeRef nodeRef, String versionId, String renditionId, Parameters parameters);
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software LimitedP
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -34,6 +34,7 @@ import org.alfresco.repo.rendition2.RenditionDefinitionRegistry2;
import org.alfresco.repo.rendition2.RenditionService2;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.thumbnail.script.ScriptThumbnailService;
import org.alfresco.repo.version.common.VersionUtil;
import org.alfresco.rest.antlr.WhereClauseParser;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.Renditions;
@@ -64,6 +65,10 @@ import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionDoesNotExistException;
import org.alfresco.service.cmr.version.VersionHistory;
import org.alfresco.service.cmr.version.VersionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.PropertyCheck;
import org.alfresco.util.TempFileProvider;
@@ -87,7 +92,7 @@ import java.util.StringJoiner;
import java.util.TreeMap;
/**
* @author Jamal Kaabi-Mofrad
* @author Jamal Kaabi-Mofrad, janv
*/
public class RenditionsImpl implements Renditions, ResourceLoaderAware
{
@@ -104,6 +109,7 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
private TenantService tenantService;
private RenditionService2 renditionService2;
private RenditionsDataCollector renditionsDataCollector;
private VersionService versionService;
public void setNodes(Nodes nodes)
{
@@ -151,14 +157,21 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
PropertyCheck.mandatory(this, "renditionsDataCollector", renditionsDataCollector);
this.nodeService = serviceRegistry.getNodeService();
this.versionService = serviceRegistry.getVersionService();
this.mimetypeService = serviceRegistry.getMimetypeService();
}
@Override
public CollectionWithPagingInfo<Rendition> getRenditions(NodeRef nodeRef, Parameters parameters)
{
final NodeRef validatedNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId());
ContentData contentData = getContentData(nodeRef, true);
return getRenditions(nodeRef, null, parameters);
}
@Override
public CollectionWithPagingInfo<Rendition> getRenditions(NodeRef nodeRef, String versionLabelId, Parameters parameters)
{
final NodeRef validatedNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId(), versionLabelId, parameters);
ContentData contentData = getContentData(validatedNodeRef, true);
String sourceMimetype = contentData.getMimetype();
boolean includeCreated = true;
@@ -214,7 +227,13 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
@Override
public Rendition getRendition(NodeRef nodeRef, String renditionId, Parameters parameters)
{
final NodeRef validatedNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId());
return getRendition(nodeRef, null, renditionId, parameters);
}
@Override
public Rendition getRendition(NodeRef nodeRef, String versionLabelId, String renditionId, Parameters parameters)
{
final NodeRef validatedNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId(), versionLabelId, parameters);
NodeRef renditionNodeRef = getRenditionByName(validatedNodeRef, renditionId, parameters);
boolean includeNotCreated = true;
String status = getStatus(parameters);
@@ -272,6 +291,12 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
@Override
public void createRendition(NodeRef nodeRef, Rendition rendition, boolean executeAsync, Parameters parameters)
{
createRendition(nodeRef, null, rendition, executeAsync, parameters);
}
@Override
public void createRendition(NodeRef nodeRef, String versionLabelId, Rendition rendition, boolean executeAsync, Parameters parameters)
{
// If thumbnail generation has been configured off, then don't bother.
if (!renditionService2.isEnabled())
@@ -279,7 +304,7 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
throw new DisabledServiceException("Rendition generation has been disabled.");
}
final NodeRef sourceNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId());
final NodeRef sourceNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId(), versionLabelId, parameters);
final NodeRef renditionNodeRef = getRenditionByName(sourceNodeRef, rendition.getId(), parameters);
if (renditionNodeRef != null)
{
@@ -307,6 +332,13 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
@Override
public void createRenditions(NodeRef nodeRef, List<Rendition> renditions, Parameters parameters)
throws NotFoundException, ConstraintViolatedException
{
createRenditions(nodeRef, null, renditions, parameters);
}
@Override
public void createRenditions(NodeRef nodeRef, String versionLabelId, List<Rendition> renditions, Parameters parameters)
throws NotFoundException, ConstraintViolatedException
{
if (renditions.isEmpty())
{
@@ -318,7 +350,7 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
throw new DisabledServiceException("Rendition generation has been disabled.");
}
final NodeRef sourceNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId());
final NodeRef sourceNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId(), versionLabelId, parameters);
RenditionDefinitionRegistry2 renditionDefinitionRegistry2 = renditionService2.getRenditionDefinitionRegistry2();
// So that POST /nodes/{nodeId}/renditions can specify rendition names as a comma separated list just like
@@ -349,7 +381,6 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
List<String> renditionNamesToCreate = new ArrayList<>();
for (String renditionName : renditionNames)
{
System.err.println("renditionName="+renditionName);
if (renditionName == null)
{
throw new IllegalArgumentException(("Null rendition name supplied")); // 400
@@ -417,18 +448,36 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
@Override
public BinaryResource getContent(NodeRef nodeRef, String renditionId, Parameters parameters)
{
final NodeRef validatedNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId());
return getContentNoValidation(validatedNodeRef, renditionId, parameters);
return getContent(nodeRef, null, renditionId, parameters);
}
@Override
public BinaryResource getContentNoValidation(NodeRef sourceNodeRef, String renditionId, Parameters parameters)
public BinaryResource getContent(NodeRef nodeRef, String versionLabelId, String renditionId, Parameters parameters)
{
NodeRef renditionNodeRef = getRenditionByName(sourceNodeRef, renditionId, parameters);
final NodeRef validatedNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId(), versionLabelId, parameters);
return getContentImpl(validatedNodeRef, renditionId, parameters);
}
@Override
public BinaryResource getContentNoValidation(NodeRef nodeRef, String renditionId, Parameters parameters)
{
return getContentNoValidation(nodeRef, null, renditionId, parameters);
}
@Override
public BinaryResource getContentNoValidation(NodeRef nodeRef, String versionLabelId, String renditionId, Parameters parameters)
{
nodeRef = findVersionIfApplicable(nodeRef, versionLabelId);
return getContentImpl(nodeRef, renditionId, parameters);
}
private BinaryResource getContentImpl(NodeRef nodeRef, String renditionId, Parameters parameters)
{
NodeRef renditionNodeRef = getRenditionByName(nodeRef, renditionId, parameters);
// By default set attachment header (with rendition Id) unless attachment=false
boolean attach = true;
String attachment = parameters.getParameter("attachment");
String attachment = parameters.getParameter(PARAM_ATTACHMENT);
if (attachment != null)
{
attach = Boolean.valueOf(attachment);
@@ -437,7 +486,7 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
if (renditionNodeRef == null)
{
boolean isPlaceholder = Boolean.valueOf(parameters.getParameter("placeholder"));
boolean isPlaceholder = Boolean.valueOf(parameters.getParameter(PARAM_PLACEHOLDER));
if (!isPlaceholder)
{
throw new NotFoundException("Thumbnail was not found for [" + renditionId + ']');
@@ -445,7 +494,7 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
String sourceNodeMimeType = null;
try
{
sourceNodeMimeType = (sourceNodeRef != null ? getMimeType(sourceNodeRef) : null);
sourceNodeMimeType = (nodeRef != null ? getMimeType(nodeRef) : null);
}
catch (InvalidArgumentException e)
{
@@ -514,23 +563,25 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
protected NodeRef getRenditionByName(NodeRef nodeRef, String renditionId, Parameters parameters)
{
if (nodeRef == null)
if (nodeRef != null)
{
return null;
}
if (StringUtils.isEmpty(renditionId))
{
throw new InvalidArgumentException("renditionId can't be null or empty.");
}
ChildAssociationRef nodeRefRendition = renditionService2.getRenditionByName(nodeRef, renditionId);
if (nodeRefRendition == null)
if (nodeRefRendition != null)
{
return null;
ContentData contentData = getContentData(nodeRefRendition.getChildRef(), false);
if (contentData != null)
{
return tenantService.getName(nodeRef, nodeRefRendition.getChildRef());
}
}
}
return tenantService.getName(nodeRef, nodeRefRendition.getChildRef());
return null;
}
protected Rendition toApiRendition(NodeRef renditionNodeRef)
@@ -570,16 +621,42 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
return apiRendition;
}
public NodeRef validateNode(StoreRef storeRef, String nodeId)
private NodeRef validateNode(StoreRef storeRef, final String nodeId, String versionLabelId, Parameters parameters)
{
if (nodeId == null)
{
throw new InvalidArgumentException("Missing nodeId");
}
final NodeRef nodeRef = nodes.validateNode(storeRef, nodeId);
NodeRef nodeRef = nodes.validateNode(storeRef, nodeId);
// check if the node represents a file
isContentFile(nodeRef);
nodeRef = findVersionIfApplicable(nodeRef, versionLabelId);
return nodeRef;
}
private NodeRef findVersionIfApplicable(NodeRef nodeRef, String versionLabelId)
{
if (versionLabelId != null)
{
nodeRef = nodes.validateOrLookupNode(nodeRef.getId(), null);
VersionHistory vh = versionService.getVersionHistory(nodeRef);
if (vh != null)
{
try
{
Version v = vh.getVersion(versionLabelId);
nodeRef = VersionUtil.convertNodeRef(v.getFrozenStateNodeRef());
}
catch (VersionDoesNotExistException vdne)
{
throw new NotFoundException("Couldn't find version: [" + nodeRef.getId() + ", " + versionLabelId + "]");
}
}
}
return nodeRef;
}

View File

@@ -0,0 +1,117 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2020 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.api.nodes;
import org.alfresco.rest.api.Renditions;
import org.alfresco.rest.api.model.Rendition;
import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.webscripts.Status;
import java.util.List;
/**
*
* Node version renditions
*
* - GET /nodes/{nodeId}/versions/{versionId}/renditions
* - POST /nodes/{nodeId}/versions/{versionId}/renditions
* - GET /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}
* - GET /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/content
*
* @author janv
*/
@RelationshipResource(name = "renditions", entityResource = NodeVersionsRelation.class, title = "Node version renditions")
public class NodeVersionRenditionsRelation implements RelationshipResourceAction.Read<Rendition>,
RelationshipResourceAction.ReadById<Rendition>,
RelationshipResourceAction.Create<Rendition>,
RelationshipResourceBinaryAction.Read,
InitializingBean
{
private Renditions renditions;
public void setRenditions(Renditions renditions)
{
this.renditions = renditions;
}
@Override
public void afterPropertiesSet() throws Exception
{
PropertyCheck.mandatory(this, "renditions", renditions);
}
@Override
public CollectionWithPagingInfo<Rendition> readAll(String nodeId, Parameters parameters)
{
String versionId = parameters.getRelationshipId();
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
return renditions.getRenditions(nodeRef, versionId, parameters);
}
@Override
public Rendition readById(String nodeId, String versionId, Parameters parameters)
{
String renditionId = parameters.getRelationship2Id();
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
return renditions.getRendition(nodeRef, versionId, renditionId, parameters);
}
@WebApiDescription(title = "Create rendition", successStatus = Status.STATUS_ACCEPTED)
@Override
public List<Rendition> create(String nodeId, List<Rendition> entity, Parameters parameters)
{
String versionId = parameters.getRelationshipId();
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
renditions.createRenditions(nodeRef, versionId, entity, parameters);
return null;
}
@WebApiDescription(title = "Download rendition", description = "Download rendition")
@BinaryProperties({ "content" })
@Override
public BinaryResource readProperty(String nodeId, String versionId, Parameters parameters)
{
String renditionId = parameters.getRelationship2Id();
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
return renditions.getContent(nodeRef, versionId, renditionId, parameters);
}
}

View File

@@ -1389,6 +1389,10 @@
<property name="renditions" ref="Renditions" />
</bean>
<bean class="org.alfresco.rest.api.nodes.NodeVersionRenditionsRelation">
<property name="renditions" ref="Renditions" />
</bean>
<bean class="org.alfresco.rest.api.quicksharelinks.QuickShareLinkRenditionsRelation">
<property name="quickShareLinks" ref="QuickShareLinks"/>
</bean>

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -25,12 +25,14 @@
*/
package org.alfresco.rest.api.tests;
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.toJsonAsStringNonNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertTrue;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.tenant.TenantService;
@@ -76,6 +78,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
/**
* Generic methods for calling the Api (originally taken and adapted from BaseCustomModelApiTest)
@@ -94,7 +97,9 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
protected static final String URL_NODES = "nodes";
private static final String URL_RENDITIONS = "renditions";
protected static final String URL_RENDITIONS = "renditions";
protected static final String URL_VERSIONS = "versions";
private static final String URL_CHILDREN = "children";
private static final String URL_CONTENT = "content";
@@ -841,22 +846,112 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
return RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
}
/**
* This test helper method uses "update binary content" to create one or more new versions. The file must already exist.
*
* @param userId
* @param contentNodeId
* @param cnt
* @param textContentPrefix
* @param verCnt
* @param majorVersion
* @param currentVersionLabel
* @return
* @throws Exception
*/
protected String updateFileVersions(String userId, String contentNodeId, int cnt,
String textContentPrefix, int verCnt,
Boolean majorVersion, String currentVersionLabel) throws Exception
{
String[] parts = currentVersionLabel.split("\\.");
int majorVer = new Integer(parts[0]).intValue();
int minorVer = new Integer(parts[1]).intValue();
Map<String, String> params = new HashMap<>();
params.put(Nodes.PARAM_OVERWRITE, "true");
if (majorVersion != null)
{
params.put(Nodes.PARAM_VERSION_MAJOR, majorVersion.toString());
}
else
{
majorVersion = false;
}
if (majorVersion)
{
minorVer = 0;
}
for (int i = 1; i <= cnt; i++)
{
if (majorVersion)
{
majorVer++;
}
else
{
minorVer++;
}
verCnt++;
params.put("comment", "my version " + verCnt);
String textContent = textContentPrefix + verCnt;
currentVersionLabel = majorVer + "." + minorVer;
// Update
ByteArrayInputStream inputStream = new ByteArrayInputStream(textContent.getBytes());
File txtFile = TempFileProvider.createTempFile(inputStream, getClass().getSimpleName(), ".txt");
PublicApiHttpClient.BinaryPayload payload = new PublicApiHttpClient.BinaryPayload(txtFile);
HttpResponse response = putBinary(getNodeContentUrl(contentNodeId), payload, null, params, 200);
Node nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class);
assertTrue(nodeResp.getAspectNames().contains("cm:versionable"));
assertNotNull(nodeResp.getProperties());
assertEquals(currentVersionLabel, nodeResp.getProperties().get("cm:versionLabel"));
assertEquals((majorVersion ? "MAJOR" : "MINOR"), nodeResp.getProperties().get("cm:versionType"));
// double-check - get version node info
response = getSingle(getNodeVersionsUrl(contentNodeId), currentVersionLabel, null, 200);
nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class);
assertEquals(currentVersionLabel, nodeResp.getProperties().get("cm:versionLabel"));
assertEquals((majorVersion ? "MAJOR" : "MINOR"), nodeResp.getProperties().get("cm:versionType"));
}
return currentVersionLabel;
}
protected static final long PAUSE_TIME = 5000; //millisecond
protected static final int MAX_RETRY = 20;
protected Rendition waitAndGetRendition(String sourceNodeId, String renditionId) throws Exception
protected Rendition waitAndGetRendition(String sourceNodeId, String versionId, String renditionId) throws Exception
{
return waitAndGetRendition(sourceNodeId, renditionId, MAX_RETRY, PAUSE_TIME);
return waitAndGetRendition(sourceNodeId, versionId, renditionId, MAX_RETRY, PAUSE_TIME);
}
protected Rendition waitAndGetRendition(String sourceNodeId, String renditionId, int maxRetry, long pauseTime) throws Exception
protected Rendition waitAndGetRendition(String sourceNodeId, String versionId, String renditionId, int maxRetry, long pauseTime) throws Exception
{
int retryCount = 0;
while (retryCount < maxRetry)
{
try
{
HttpResponse response = getSingle(getNodeRenditionsUrl(sourceNodeId), renditionId, 200);
HttpResponse response;
if ((versionId != null) && (! versionId.isEmpty()))
{
response = getSingle(getNodeVersionRenditionsUrl(sourceNodeId, versionId), renditionId, 200);
}
else
{
response = getSingle(getNodeRenditionsUrl(sourceNodeId), renditionId, 200);
}
Rendition rendition = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Rendition.class);
assertNotNull(rendition);
assertEquals(Rendition.RenditionStatus.CREATED, rendition.getStatus());
@@ -877,6 +972,11 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
}
protected Rendition createAndGetRendition(String sourceNodeId, String renditionId) throws Exception
{
return createAndGetRendition(sourceNodeId, null, renditionId);
}
protected Rendition createAndGetRendition(String sourceNodeId, String versionId, String renditionId) throws Exception
{
Rendition renditionRequest = new Rendition();
renditionRequest.setId(renditionId);
@@ -886,8 +986,16 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
{
try
{
HttpResponse res = post(getNodeRenditionsUrl(sourceNodeId), toJsonAsString(renditionRequest), 202);
assertNull(res.getJsonResponse());
HttpResponse response;
if ((versionId != null) && (! versionId.isEmpty()))
{
response = post(getNodeVersionRenditionsUrl(sourceNodeId, versionId), toJsonAsString(renditionRequest), 202);
}
else
{
response = post(getNodeRenditionsUrl(sourceNodeId), toJsonAsString(renditionRequest), 202);
}
assertNull(response.getJsonResponse());
break;
}
catch (AssertionError ex)
@@ -901,7 +1009,7 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
}
}
return waitAndGetRendition(sourceNodeId, renditionId);
return waitAndGetRendition(sourceNodeId, versionId, renditionId);
}
protected String getNodeRenditionsUrl(String nodeId)
@@ -909,6 +1017,16 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
return URL_NODES + "/" + nodeId + "/" + URL_RENDITIONS;
}
protected String getNodeVersionsUrl(String nodeId)
{
return URL_NODES + "/" + nodeId + "/" + URL_VERSIONS;
}
protected String getNodeVersionRenditionsUrl(String nodeId, String versionId)
{
return URL_NODES + "/" + nodeId + "/" + URL_VERSIONS + "/" + versionId + "/" + URL_RENDITIONS;
}
protected String getNodeChildrenUrl(String nodeId)
{
return URL_NODES + "/" + nodeId + "/" + URL_CHILDREN;

View File

@@ -0,0 +1,267 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2020 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.api.tests;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.rest.AbstractSingleNetworkSiteTest;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.client.PublicApiClient.Paging;
import org.alfresco.rest.api.tests.client.PublicApiHttpClient;
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.Node;
import org.alfresco.rest.api.tests.client.data.Rendition;
import org.alfresco.rest.api.tests.util.RestApiUtil;
import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsString;
import org.alfresco.util.TempFileProvider;
import static org.junit.Assert.*;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.*;
/**
* V1 REST API tests for Node Version Renditions
*
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/{nodeId}/versions/{versionId}/renditions</li>
*
* @author janv
*/
public class NodeVersionRenditionsApiTest extends AbstractSingleNetworkSiteTest
{
private final static long DELAY_IN_MS = 500;
/**
* Upload some versions and then create and retrieve version renditions
*
* <p>POST:</p>
* @literal <host>:<port>/alfresco/api/-default-/public/alfresco/versions/1/nodes/<nodeId>/versions/<versionId>/renditions}
*
* <p>GET:</p>
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/<nodeId>/versions/<versionId>/renditions}
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/<nodeId>/versions/<versionId>/renditions/<renditionId>}
* {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/nodes/<nodeId>/versions/<versionId>/renditions/<renditionId>/content}
*
* @throws Exception
*/
@Test
public void testUpFileVersionRenditions() throws Exception
{
setRequestContext(user1);
String myFolderNodeId = getMyNodeId();
// create folder
String f1Id = createFolder(myFolderNodeId, "f1").getId();
try
{
int verCnt = 1;
int cnt = 1;
String versionLabel = "1.0";
String textContentSuffix = "Amazingly few discotheques provide jukeboxes ";
String contentName = "content-2-" + System.currentTimeMillis();
String content = textContentSuffix + cnt;
Map<String, String> params = new HashMap<>();
params.put("majorVersion", "true");
// create a new file
Document documentResp = createTextFile(f1Id, contentName, content, "UTF-8", params);
String docId = documentResp.getId();
assertTrue(documentResp.getAspectNames().contains("cm:versionable"));
assertNotNull(documentResp.getProperties());
assertEquals(versionLabel, documentResp.getProperties().get("cm:versionLabel"));
cnt = 2;
versionLabel = updateFileVersions(user1, docId, cnt, textContentSuffix, verCnt, true, versionLabel);
verCnt = verCnt+cnt;
assertEquals("3.0", versionLabel);
assertEquals(3, verCnt);
// check version history count
HttpResponse response = getAll(getNodeVersionsUrl(docId), null, null, 200);
List<Node> nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(verCnt, nodes.size());
// pause briefly
Thread.sleep(DELAY_IN_MS);
checkCreateAndGetVersionRendition(docId, "1.0", "doclib");
checkCreateAndGetVersionRendition(docId, "3.0", "doclib");
checkCreateAndGetVersionRendition(docId, "2.0", "doclib");
// also live node
checkCreateAndGetVersionRendition(docId, null, "doclib");
}
finally
{
// some cleanup
setRequestContext(user1);
deleteNode(f1Id, true, 204);
}
}
@Test
public void testNegative() throws Exception
{
AuthenticationUtil.clearCurrentSecurityContext();
setRequestContext(null);
// -ve: not authenticated
getAll(getNodeVersionRenditionsUrl("dummy", "1.0"), null, 401);
getSingle(getNodeVersionRenditionsUrl("dummy", "1.0"), ("doclib"), null, 401);
getSingle(getNodeVersionRenditionsUrl("dummy", "1.0"), ("doclib/content"), null, 401);
Rendition renditionRequest = new Rendition();
renditionRequest.setId("doclib");
post(getNodeVersionRenditionsUrl("dummy", "1.0"), toJsonAsString(renditionRequest), 401);
setRequestContext(user1);
String myFolderNodeId = getMyNodeId();
// create folder
String f1Id = createFolder(myFolderNodeId, "f1").getId();
try
{
int verCnt = 1;
int cnt = 1;
String versionLabel = "1.0";
String textContentSuffix = "Amazingly few discotheques provide jukeboxes ";
String contentName = "content-2-" + System.currentTimeMillis();
String content = textContentSuffix + cnt;
// create a new file
Document documentResp = createTextFile(f1Id, contentName, content, "UTF-8", null);
String docId = documentResp.getId();
getAll(getNodeVersionRenditionsUrl(docId, "1.0"), null, 200);
checkCreateAndGetVersionRendition(docId, "1.0", "doclib");
// -ve: rendition already exits (409)
renditionRequest.setId("doclib");
post(getNodeVersionRenditionsUrl(docId, "1.0"), toJsonAsString(renditionRequest), 409);
// -ve: no such rendition (404)
getSingle(getNodeVersionRenditionsUrl("dummy", "1.0"), ("dummy"), null, 404);
getSingle(getNodeVersionRenditionsUrl("dummy", "1.0"), ("dummy/content"), null, 404);
renditionRequest.setId("dummy");
post(getNodeVersionRenditionsUrl("dummy", "1.0"), toJsonAsString(renditionRequest), 404);
// -ve: no such version (404)
getAll(getNodeVersionRenditionsUrl(docId, "4.0"), null, 404);
getSingle(getNodeVersionRenditionsUrl(docId, "4.0"), ("doclib"), null, 404);
getSingle(getNodeVersionRenditionsUrl(docId, "4.0"), ("doclib/content"), null, 404);
renditionRequest.setId("doclib");
post(getNodeVersionRenditionsUrl(docId, "4.0"), toJsonAsString(renditionRequest), 404);
// -ve: no such file (404)
getAll(getNodeVersionRenditionsUrl("dummy", "1.0"), null, 404);
getSingle(getNodeVersionRenditionsUrl("dummy", "1.0"), ("doclib"), null, 404);
getSingle(getNodeVersionRenditionsUrl("dummy", "1.0"), ("doclib/content"), null, 404);
renditionRequest.setId("doclib");
post(getNodeVersionRenditionsUrl("dummy", "1.0"), toJsonAsString(renditionRequest), 404);
}
finally
{
// some cleanup
setRequestContext(user1);
deleteNode(f1Id, true, 204);
}
}
private void checkCreateAndGetVersionRendition(String docId, String versionId, String renditionId) throws Exception
{
String getRenditionsUrl;
if ((versionId != null) && (! versionId.isEmpty()))
{
getRenditionsUrl = getNodeVersionRenditionsUrl(docId, versionId);
}
else
{
getRenditionsUrl = getNodeRenditionsUrl(docId);
}
// List renditions for version
Paging paging = getPaging(0, 50);
HttpResponse response = getAll(getRenditionsUrl, paging, 200);
List<Rendition> renditions = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Rendition.class);
assertTrue(renditions.size() >= 5);
for (Rendition rendition : renditions)
{
assertEquals(Rendition.RenditionStatus.NOT_CREATED, rendition.getStatus());
}
// Get rendition (not created yet) information for node
response = getSingle(getRenditionsUrl, renditionId, 200);
Rendition rendition = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Rendition.class);
assertNotNull(rendition);
assertEquals(Rendition.RenditionStatus.NOT_CREATED, rendition.getStatus());
// -ve test: try to download non-existent rendition (and no placeholder)
Map<String, String> params = new HashMap<>();
params.put("placeholder", "false");
getSingle(getRenditionsUrl, (renditionId+"/content"), params, 404);
// +ve test: download placeholder instead
params = new HashMap<>();
params.put("placeholder", "true");
response = getSingle(getRenditionsUrl, (renditionId+"/content"), params, 200);
assertNotNull(response.getResponseAsBytes());
// Create and get version rendition
rendition = createAndGetRendition(docId, versionId, renditionId);
assertNotNull(rendition);
assertEquals(Rendition.RenditionStatus.CREATED, rendition.getStatus());
ContentInfo contentInfo = rendition.getContent();
assertNotNull(contentInfo);
assertEquals(MimetypeMap.MIMETYPE_IMAGE_PNG, contentInfo.getMimeType());
assertEquals("PNG Image", contentInfo.getMimeTypeName());
assertNotNull(contentInfo.getEncoding());
assertTrue(contentInfo.getSizeInBytes() > 0);
}
@Override
public String getScope()
{
return "public";
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -61,18 +61,12 @@ import static org.junit.Assert.*;
public class NodeVersionsApiTest extends AbstractSingleNetworkSiteTest
{
private static final String URL_DELETED_NODES = "deleted-nodes";
private static final String URL_VERSIONS = "versions";
protected String getNodeVersionRevertUrl(String nodeId, String versionId)
{
return getNodeVersionsUrl(nodeId) + "/" + versionId + "/revert";
}
protected String getNodeVersionsUrl(String nodeId)
{
return URL_NODES + "/" + nodeId + "/" + URL_VERSIONS;
}
/**
* Test version creation when uploading files (via multi-part/form-data with overwrite=true)
*
@@ -561,89 +555,6 @@ public class NodeVersionsApiTest extends AbstractSingleNetworkSiteTest
return new Pair<String,String>(currentVersionLabel, docId);
}
/**
* This test helper method uses "update binary content" to create one or more new versions. The file must already exist.
*
* @param userId
* @param contentNodeId
* @param cnt
* @param textContentPrefix
* @param verCnt
* @param majorVersion
* @param currentVersionLabel
* @return
* @throws Exception
*/
private String updateFileVersions(String userId, String contentNodeId, int cnt,
String textContentPrefix, int verCnt,
Boolean majorVersion, String currentVersionLabel) throws Exception
{
String[] parts = currentVersionLabel.split("\\.");
int majorVer = new Integer(parts[0]).intValue();
int minorVer = new Integer(parts[1]).intValue();
Map<String, String> params = new HashMap<>();
params.put(Nodes.PARAM_OVERWRITE, "true");
if (majorVersion != null)
{
params.put(Nodes.PARAM_VERSION_MAJOR, majorVersion.toString());
}
else
{
majorVersion = false;
}
if (majorVersion)
{
minorVer = 0;
}
for (int i = 1; i <= cnt; i++)
{
if (majorVersion)
{
majorVer++;
}
else
{
minorVer++;
}
verCnt++;
params.put("comment", "my version " + verCnt);
String textContent = textContentPrefix + verCnt;
currentVersionLabel = majorVer + "." + minorVer;
// Update
ByteArrayInputStream inputStream = new ByteArrayInputStream(textContent.getBytes());
File txtFile = TempFileProvider.createTempFile(inputStream, getClass().getSimpleName(), ".txt");
PublicApiHttpClient.BinaryPayload payload = new PublicApiHttpClient.BinaryPayload(txtFile);
HttpResponse response = putBinary(getNodeContentUrl(contentNodeId), payload, null, params, 200);
Node nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class);
assertTrue(nodeResp.getAspectNames().contains("cm:versionable"));
assertNotNull(nodeResp.getProperties());
assertEquals(currentVersionLabel, nodeResp.getProperties().get("cm:versionLabel"));
assertEquals((majorVersion ? "MAJOR" : "MINOR"), nodeResp.getProperties().get("cm:versionType"));
// double-check - get version node info
response = getSingle(getNodeVersionsUrl(contentNodeId), currentVersionLabel, null, 200);
nodeResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class);
assertEquals(currentVersionLabel, nodeResp.getProperties().get("cm:versionLabel"));
assertEquals((majorVersion ? "MAJOR" : "MINOR"), nodeResp.getProperties().get("cm:versionType"));
}
return currentVersionLabel;
}
/**
* Tests api when uploading a file and then updating with a new version
*

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -548,7 +548,7 @@ public class RenditionsTest extends AbstractBaseApiTest
String contentNodeId = document.getId();
// wait and check that rendition is created ...
Rendition rendition = waitAndGetRendition(contentNodeId, renditionName);
Rendition rendition = waitAndGetRendition(contentNodeId, null, renditionName);
assertNotNull(rendition);
assertEquals(RenditionStatus.CREATED, rendition.getStatus());
@@ -637,7 +637,7 @@ public class RenditionsTest extends AbstractBaseApiTest
{
for (String renditionName : renditionNames)
{
Rendition rendition = waitAndGetRendition(contentNodeId, renditionName);
Rendition rendition = waitAndGetRendition(contentNodeId, null, renditionName);
assertNotNull(rendition);
assertEquals(RenditionStatus.CREATED, rendition.getStatus());
}