diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml
index 605da78cbe..c8ec2ba6c3 100644
--- a/config/alfresco/public-rest-context.xml
+++ b/config/alfresco/public-rest-context.xml
@@ -1052,4 +1052,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ org.alfresco.rest.api.Renditions
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/java/org/alfresco/rest/api/Nodes.java b/source/java/org/alfresco/rest/api/Nodes.java
index fdf66fc345..2b2e8bd097 100644
--- a/source/java/org/alfresco/rest/api/Nodes.java
+++ b/source/java/org/alfresco/rest/api/Nodes.java
@@ -175,4 +175,13 @@ public interface Nodes
NodeRef validateNode(NodeRef nodeRef);
boolean nodeMatches(NodeRef nodeRef, Set expectedTypes, Set excludedTypes);
+ /**
+ * Determines whether the type of the given nodeRef is a sub-class of another class or not.
+ *
+ * @param nodeRef source nodeRef
+ * @param ofClassQName the class to test against
+ * @param validateNodeRef whether to validate the given source node or not
+ * @return true if the type of the given nodeRef is a sub-class of another class, otherwise false
+ */
+ boolean isSubClass(NodeRef nodeRef, QName ofClassQName, boolean validateNodeRef);
}
diff --git a/source/java/org/alfresco/rest/api/Renditions.java b/source/java/org/alfresco/rest/api/Renditions.java
new file mode 100644
index 0000000000..fb0ee61b32
--- /dev/null
+++ b/source/java/org/alfresco/rest/api/Renditions.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2005-2016 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+
+package org.alfresco.rest.api;
+
+import org.alfresco.rest.api.model.Rendition;
+import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+
+/**
+ * Renditions API
+ *
+ * @author Jamal Kaabi-Mofrad
+ */
+public interface Renditions
+{
+ /**
+ * Lists all available renditions includes those that have been created and those that are yet to be created.
+ *
+ * @param nodeId the source node id
+ * @param parameters the {@link Parameters} object to get the parameters passed into the request
+ * @return the rendition results
+ */
+ CollectionWithPagingInfo getRenditions(String nodeId, Parameters parameters);
+
+ /**
+ * Creates a rendition for the given node asynchronously.
+ *
+ * @param nodeId the source node id
+ * @param rendition the {@link Rendition} request
+ * @param parameters the {@link Parameters} object to get the parameters passed into the request
+ */
+ void createRendition(String nodeId, Rendition rendition, Parameters parameters);
+}
diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java
index 05ce7e9cbb..d622e52e54 100644
--- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java
+++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java
@@ -335,6 +335,16 @@ public class NodesImpl implements Nodes
return nodeMatches(nodeRef, expectedTypes, excludedTypes, true);
}
+ @Override
+ public boolean isSubClass(NodeRef nodeRef, QName ofClassQName, boolean validateNodeRef)
+ {
+ if (validateNodeRef)
+ {
+ nodeRef = validateNode(nodeRef);
+ }
+ return isSubClass(getNodeType(nodeRef), ofClassQName);
+ }
+
private boolean nodeMatches(NodeRef nodeRef, Set expectedTypes, Set excludedTypes, boolean existsCheck)
{
if (existsCheck && (! nodeService.exists(nodeRef)))
diff --git a/source/java/org/alfresco/rest/api/impl/RenditionsImpl.java b/source/java/org/alfresco/rest/api/impl/RenditionsImpl.java
new file mode 100644
index 0000000000..96af6401a2
--- /dev/null
+++ b/source/java/org/alfresco/rest/api/impl/RenditionsImpl.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2005-2016 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+
+package org.alfresco.rest.api.impl;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.query.PagingResults;
+import org.alfresco.repo.thumbnail.ThumbnailDefinition;
+import org.alfresco.repo.thumbnail.ThumbnailHelper;
+import org.alfresco.repo.thumbnail.ThumbnailRegistry;
+import org.alfresco.rest.antlr.WhereClauseParser;
+import org.alfresco.rest.api.Nodes;
+import org.alfresco.rest.api.Renditions;
+import org.alfresco.rest.api.model.ContentInfo;
+import org.alfresco.rest.api.model.Rendition;
+import org.alfresco.rest.api.model.Rendition.RenditionStatus;
+import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
+import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
+import org.alfresco.rest.framework.resource.parameters.Paging;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.rest.framework.resource.parameters.where.Query;
+import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
+import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.action.Action;
+import org.alfresco.service.cmr.action.ActionService;
+import org.alfresco.service.cmr.rendition.RenditionService;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.ContentData;
+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.thumbnail.ThumbnailService;
+import org.alfresco.util.PropertyCheck;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * @author Jamal Kaabi-Mofrad
+ */
+public class RenditionsImpl implements Renditions
+{
+ private static final String PARAM_status = "status";
+ private static final Set RENDITION_STATUS_COLLECTION_EQUALS_QUERY_PROPERTIES = Collections.singleton(PARAM_status);
+
+ private Nodes nodes;
+ private NodeService nodeService;
+ private ThumbnailService thumbnailService;
+ private RenditionService renditionService;
+ private MimetypeService mimetypeService;
+ private ActionService actionService;
+ private ServiceRegistry serviceRegistry;
+
+ public void setNodes(Nodes nodes)
+ {
+ this.nodes = nodes;
+ }
+
+ public void setThumbnailService(ThumbnailService thumbnailService)
+ {
+ this.thumbnailService = thumbnailService;
+ }
+
+ public void setServiceRegistry(ServiceRegistry serviceRegistry)
+ {
+ this.serviceRegistry = serviceRegistry;
+ }
+
+ public void init()
+ {
+ PropertyCheck.mandatory(this, "nodes", nodes);
+ PropertyCheck.mandatory(this, "thumbnailService", thumbnailService);
+ PropertyCheck.mandatory(this, "serviceRegistry", serviceRegistry);
+
+ this.nodeService = serviceRegistry.getNodeService();
+ this.actionService = serviceRegistry.getActionService();
+ this.renditionService = serviceRegistry.getRenditionService();
+ this.mimetypeService = serviceRegistry.getMimetypeService();
+ }
+
+ @Override
+ public CollectionWithPagingInfo getRenditions(String nodeId, Parameters parameters)
+ {
+ final NodeRef nodeRef = nodes.validateNode(nodeId);
+ if (!nodes.isSubClass(nodeRef, ContentModel.PROP_CONTENT, false))
+ {
+ throw new InvalidArgumentException("Node id '" + nodeId + "' does not represent a file.");
+ }
+
+ ContentData contentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT);
+ String contentMimeType = contentData.getMimetype();
+
+ Query query = parameters.getQuery();
+ boolean includeCreated = true;
+ boolean includeNotCreated = true;
+ if (query != null)
+ {
+ // Filtering via "where" clause
+ MapBasedQueryWalker propertyWalker = new MapBasedQueryWalker(RENDITION_STATUS_COLLECTION_EQUALS_QUERY_PROPERTIES, null);
+ QueryHelper.walk(query, propertyWalker);
+
+ String withStatus = propertyWalker.getProperty(PARAM_status, WhereClauseParser.EQUALS);
+ if (withStatus != null)
+ {
+ try
+ {
+ includeCreated = RenditionStatus.CREATED.equals(RenditionStatus.valueOf(withStatus));
+ }
+ catch (IllegalArgumentException ex)
+ {
+ throw new InvalidArgumentException("Invalid status value: " + withStatus);
+ }
+ includeNotCreated = !includeCreated;
+ }
+ }
+
+ Map apiRenditions = new TreeMap<>();
+ if (includeNotCreated)
+ {
+ // List all available thumbnail definitions
+ List thumbnailDefinitions = thumbnailService.getThumbnailRegistry().getThumbnailDefinitions(contentMimeType, -1);
+ for (ThumbnailDefinition thumbnailDefinition : thumbnailDefinitions)
+ {
+ ContentInfo contentInfo = new ContentInfo(thumbnailDefinition.getMimetype(),
+ getMimeTypeDisplayName(thumbnailDefinition.getMimetype()), null, null);
+ Rendition apiRendition = new Rendition();
+ apiRendition.setId(thumbnailDefinition.getName());
+ apiRendition.setContent(contentInfo);
+ apiRendition.setStatus(RenditionStatus.NOT_CREATED);
+ apiRenditions.put(thumbnailDefinition.getName(), apiRendition);
+ }
+ }
+
+ List nodeRefRenditions = renditionService.getRenditions(nodeRef);
+ if (!nodeRefRenditions.isEmpty())
+ {
+ for (ChildAssociationRef childAssociationRef : nodeRefRenditions)
+ {
+ NodeRef renditionNodeRef = childAssociationRef.getChildRef();
+ Rendition apiRendition = toApiRendition(renditionNodeRef);
+ if (includeCreated)
+ {
+ // Replace/append any thumbnail definitions with created rendition info
+ apiRenditions.put(apiRendition.getId(), apiRendition);
+ }
+ else
+ {
+ // Remove any thumbnail definitions that has been created from the list,
+ // as the filter requires only the Not_Created renditions
+ apiRenditions.remove(apiRendition.getId());
+ }
+ }
+ }
+
+ // Wrap paging info, as the core service doesn't support paging
+ Paging paging = parameters.getPaging();
+ PagingResults results = Util.wrapPagingResults(paging, apiRenditions.values());
+
+ return CollectionWithPagingInfo.asPaged(paging, results.getPage(), results.hasMoreItems(), results.getTotalResultCount().getFirst());
+ }
+
+ @Override
+ public void createRendition(String nodeId, Rendition rendition, Parameters parameters)
+ {
+ NodeRef sourceNodeRef = nodes.validateNode(nodeId);
+
+ // If thumbnail generation has been configured off, then don't bother with any of this.
+ if (thumbnailService.getThumbnailsEnabled())
+ {
+ // Use the thumbnail registry to get the details of the thumbnail
+ ThumbnailRegistry registry = thumbnailService.getThumbnailRegistry();
+ ThumbnailDefinition details = registry.getThumbnailDefinition(rendition.getId());
+ if (details == null)
+ {
+ // Throw exception
+ throw new InvalidArgumentException("The thumbnail name '" + rendition.getId() + "' is not registered");
+ }
+
+ // Check if anything is currently registered to generate thumbnails for the specified mimeType
+ ContentData contentData = (ContentData) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CONTENT);
+ if (!ContentData.hasContent(contentData))
+ {
+ throw new InvalidArgumentException("Unable to create thumbnail '" + details.getName() + "' as there is no content");
+ }
+ if (!registry.isThumbnailDefinitionAvailable(contentData.getContentUrl(), contentData.getMimetype(), contentData.getSize(), sourceNodeRef, details))
+ {
+ throw new InvalidArgumentException("Unable to create thumbnail '" + details.getName() + "' for " +
+ contentData.getMimetype() + " as no transformer is currently available.");
+ }
+
+ Action action = ThumbnailHelper.createCreateThumbnailAction(details, serviceRegistry);
+ // Queue async creation of thumbnail
+ actionService.executeAction(action, sourceNodeRef, true, true);
+ }
+ }
+
+ protected Rendition toApiRendition(NodeRef renditionNodeRef)
+ {
+ Rendition apiRendition = new Rendition();
+
+ String renditionName = (String) nodeService.getProperty(renditionNodeRef, ContentModel.PROP_NAME);
+ apiRendition.setId(renditionName);
+
+ ContentData contentData = (ContentData) nodeService.getProperty(renditionNodeRef, ContentModel.PROP_CONTENT);
+ ContentInfo contentInfo = new ContentInfo(contentData.getMimetype(), getMimeTypeDisplayName(contentData.getMimetype()), contentData.getSize(), contentData.getEncoding());
+ apiRendition.setContent(contentInfo);
+ apiRendition.setStatus(RenditionStatus.CREATED);
+
+ return apiRendition;
+ }
+
+ private String getMimeTypeDisplayName(String mimeType)
+ {
+ return mimetypeService.getDisplaysByMimetype().get(mimeType);
+ }
+}
diff --git a/source/java/org/alfresco/rest/api/impl/Util.java b/source/java/org/alfresco/rest/api/impl/Util.java
index 5bdc4603af..b6612a37a7 100644
--- a/source/java/org/alfresco/rest/api/impl/Util.java
+++ b/source/java/org/alfresco/rest/api/impl/Util.java
@@ -26,15 +26,90 @@
package org.alfresco.rest.api.impl;
import org.alfresco.query.CannedQueryPageDetails;
+import org.alfresco.query.PageDetails;
import org.alfresco.query.PagingRequest;
+import org.alfresco.query.PagingResults;
+import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.parameters.Paging;
+import org.alfresco.util.Pair;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author steveglover
+ * @author Jamal Kaabi-Mofrad
+ */
public class Util
{
- public static PagingRequest getPagingRequest(Paging paging)
+ public static PagingRequest getPagingRequest(Paging paging)
{
- PagingRequest pagingRequest = new PagingRequest(paging.getSkipCount(), paging.getMaxItems());
- pagingRequest.setRequestTotalCountMax(CannedQueryPageDetails.DEFAULT_PAGE_SIZE);
- return pagingRequest;
+ PagingRequest pagingRequest = new PagingRequest(paging.getSkipCount(), paging.getMaxItems());
+ pagingRequest.setRequestTotalCountMax(CannedQueryPageDetails.DEFAULT_PAGE_SIZE);
+ return pagingRequest;
}
+
+ public static PagingResults wrapPagingResults(Paging paging, Collection result)
+ {
+ if (paging == null)
+ {
+ throw new InvalidArgumentException("paging object can't be null.");
+ }
+ if (result == null)
+ {
+ result = Collections.emptyList();
+ }
+
+ PagingRequest pagingRequest = getPagingRequest(paging);
+
+ final int totalSize = result.size();
+ final PageDetails pageDetails = PageDetails.getPageDetails(pagingRequest, totalSize);
+
+ final List page = new ArrayList<>(pageDetails.getPageSize());
+ Iterator it = result.iterator();
+ for (int counter = 0, end = pageDetails.getEnd(); counter < end && it.hasNext(); counter++)
+ {
+ T element = it.next();
+ if (counter < pageDetails.getSkipCount())
+ {
+ continue;
+ }
+ if (counter > end - 1)
+ {
+ break;
+ }
+ page.add(element);
+ }
+
+ return new PagingResults()
+ {
+ @Override
+ public List getPage()
+ {
+ return page;
+ }
+
+ @Override
+ public boolean hasMoreItems()
+ {
+ return pageDetails.hasMoreItems();
+ }
+
+ @Override
+ public Pair getTotalResultCount()
+ {
+ Integer total = totalSize;
+ return new Pair<>(total, total);
+ }
+
+ @Override
+ public String getQueryExecutionId()
+ {
+ return null;
+ }
+ };
+ }
}
diff --git a/source/java/org/alfresco/rest/api/model/Rendition.java b/source/java/org/alfresco/rest/api/model/Rendition.java
new file mode 100644
index 0000000000..879b1da58b
--- /dev/null
+++ b/source/java/org/alfresco/rest/api/model/Rendition.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2005-2016 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+
+package org.alfresco.rest.api.model;
+
+/**
+ * Representation of a rendition.
+ *
+ * @author Jamal Kaabi-Mofrad
+ */
+public class Rendition
+{
+ public enum RenditionStatus
+ {
+ CREATED,
+ NOT_CREATED
+ }
+
+ private String id;
+ private RenditionStatus status;
+ private ContentInfo contentInfo;
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ public RenditionStatus getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(RenditionStatus status)
+ {
+ this.status = status;
+ }
+
+ public ContentInfo getContent()
+ {
+ return contentInfo;
+ }
+
+ public void setContent(ContentInfo contentInfo)
+ {
+ this.contentInfo = contentInfo;
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuilder sb = new StringBuilder(150);
+ sb.append("Rendition [id='").append(id)
+ .append(", status=").append(status)
+ .append(", contentInfo=").append(contentInfo)
+ .append(']');
+ return sb.toString();
+ }
+}
diff --git a/source/java/org/alfresco/rest/api/nodes/NodeRenditionsRelation.java b/source/java/org/alfresco/rest/api/nodes/NodeRenditionsRelation.java
new file mode 100644
index 0000000000..31f29ea2fe
--- /dev/null
+++ b/source/java/org/alfresco/rest/api/nodes/NodeRenditionsRelation.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2005-2016 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+
+package org.alfresco.rest.api.nodes;
+
+import org.alfresco.rest.api.Renditions;
+import org.alfresco.rest.api.model.Rendition;
+import org.alfresco.rest.framework.resource.RelationshipResource;
+import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
+import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
+import org.alfresco.rest.framework.resource.parameters.Parameters;
+import org.alfresco.util.PropertyCheck;
+import org.springframework.beans.factory.InitializingBean;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Node renditions.
+ *
+ * @author Jamal Kaabi-Mofrad
+ */
+@RelationshipResource(name = "renditions", entityResource = NodesEntityResource.class, title = "Node renditions")
+public class NodeRenditionsRelation implements RelationshipResourceAction.Read,
+ RelationshipResourceAction.Create,
+ 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 readAll(String nodeId, Parameters parameters)
+ {
+ return renditions.getRenditions(nodeId, parameters);
+ }
+
+ @Override
+ public List create(String nodeId, List entity, Parameters parameters)
+ {
+ for (Rendition rendition : entity)
+ {
+ renditions.createRendition(nodeId, rendition, parameters);
+ }
+ return Collections.emptyList();
+ }
+}
diff --git a/source/test-java/org/alfresco/rest/api/tests/ApiTest.java b/source/test-java/org/alfresco/rest/api/tests/ApiTest.java
index 7794c38900..163befdbce 100644
--- a/source/test-java/org/alfresco/rest/api/tests/ApiTest.java
+++ b/source/test-java/org/alfresco/rest/api/tests/ApiTest.java
@@ -38,6 +38,7 @@ import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({
NodeApiTest.class,
+ RenditionsTest.class,
TestSites.class,
TestNodeComments.class,
TestCMIS.class,
diff --git a/source/test-java/org/alfresco/rest/api/tests/RenditionsTest.java b/source/test-java/org/alfresco/rest/api/tests/RenditionsTest.java
new file mode 100644
index 0000000000..9e03c46c99
--- /dev/null
+++ b/source/test-java/org/alfresco/rest/api/tests/RenditionsTest.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2005-2016 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+
+package org.alfresco.rest.api.tests;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.content.MimetypeMap;
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
+import org.alfresco.rest.api.tests.RepoService.TestNetwork;
+import org.alfresco.rest.api.tests.RepoService.TestPerson;
+import org.alfresco.rest.api.tests.RepoService.TestSite;
+import org.alfresco.rest.api.tests.client.HttpResponse;
+import org.alfresco.rest.api.tests.client.PublicApiClient.ExpectedPaging;
+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.Document;
+import org.alfresco.rest.api.tests.client.data.Rendition;
+import org.alfresco.rest.api.tests.client.data.Rendition.RenditionStatus;
+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.service.cmr.site.SiteVisibility;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Renditions API tests.
+ *
+ * @author Jamal Kaabi-Mofrad
+ */
+public class RenditionsTest extends AbstractBaseApiTest
+{
+ /**
+ * User one from network one
+ */
+ private TestPerson userOneN1;
+
+ /**
+ * Private site of user one from network one
+ */
+ private TestSite userOneN1Site;
+
+ @Before
+ public void setup() throws Exception
+ {
+ TestNetwork networkOne = getTestFixture().getRandomNetwork();
+ userOneN1 = networkOne.createUser();
+ userOneN1Site = createSite(networkOne, userOneN1, SiteVisibility.PRIVATE);
+ }
+
+ /**
+ * Tests get node renditions.
+ * GET:
+ * {@literal :/alfresco/api//public/alfresco/versions/1/nodes//renditions}
+ */
+ @Test
+ public void testListNodeRenditions() throws Exception
+ {
+ AuthenticationUtil.setFullyAuthenticatedUser(userOneN1.getId());
+
+ // Create a folder within the site document's library
+ String folderName = "folder" + System.currentTimeMillis();
+ String folder_Id = repoService.addToDocumentLibrary(userOneN1Site, folderName, ContentModel.TYPE_FOLDER).getId();
+
+ // Create multipart request
+ String fileName = "quick.pdf";
+ File file = getResourceFile(fileName);
+ MultiPartBuilder multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData(fileName, file, MimetypeMap.MIMETYPE_PDF));
+ MultiPartRequest reqBody = multiPartBuilder.build();
+
+ // Upload quick.pdf file into 'folder'
+ HttpResponse response = post("nodes/" + folder_Id + "/children", userOneN1.getId(), reqBody.getBody(), null, reqBody.getContentType(), 201);
+ Document document = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
+ String contentNodeId = document.getId();
+
+ Paging paging = getPaging(0, 50);
+ // List all available renditions (includes those that have been created and those that are yet to be created)
+ response = getAll(getRenditionsUrl(contentNodeId), userOneN1.getId(), paging, 200);
+ List renditions = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Rendition.class);
+ assertTrue(renditions.size() >= 5);
+ Rendition docLib = getRendition(renditions, "doclib");
+ assertNotNull(docLib);
+ assertEquals(RenditionStatus.NOT_CREATED, docLib.getStatus());
+ ContentInfo contentInfo = docLib.getContent();
+ assertNotNull(contentInfo);
+ assertEquals(MimetypeMap.MIMETYPE_IMAGE_PNG, contentInfo.getMimeType());
+ assertEquals("PNG Image", contentInfo.getMimeTypeName());
+ assertNull(contentInfo.getEncoding());
+ assertNull(contentInfo.getSizeInBytes());
+
+ // Add a filter to select the renditions based on the given status
+ Map params = new HashMap<>(1);
+ params.put("where", "(status='NOT_CREATED')");
+ // List only the NOT_CREATED renditions
+ response = getAll(getRenditionsUrl(contentNodeId), userOneN1.getId(), paging, params, 200);
+ renditions = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Rendition.class);
+ assertTrue(renditions.size() >= 5);
+
+ params.put("where", "(status='CREATED')");
+ // List only the CREATED renditions
+ response = getAll(getRenditionsUrl(contentNodeId), userOneN1.getId(), paging, params, 200);
+ renditions = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Rendition.class);
+ assertEquals("There is no rendition created yet.", 0, renditions.size());
+
+ // Test paging
+ // SkipCount=0,MaxItems=2
+ paging = getPaging(0, 2);
+ // List all available renditions
+ response = getAll(getRenditionsUrl(contentNodeId), userOneN1.getId(), paging, 200);
+ renditions = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Rendition.class);
+ assertEquals(2, renditions.size());
+ ExpectedPaging expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse());
+ assertEquals(2, expectedPaging.getCount().intValue());
+ assertEquals(0, expectedPaging.getSkipCount().intValue());
+ assertEquals(2, expectedPaging.getMaxItems().intValue());
+ assertTrue(expectedPaging.getTotalItems() >= 5);
+ assertTrue(expectedPaging.getHasMoreItems());
+
+ // SkipCount=2,MaxItems=3
+ paging = getPaging(2, 3);
+ // List all available renditions
+ response = getAll(getRenditionsUrl(contentNodeId), userOneN1.getId(), paging, 200);
+ renditions = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Rendition.class);
+ assertEquals(3, renditions.size());
+ expectedPaging = RestApiUtil.parsePaging(response.getJsonResponse());
+ assertEquals(3, expectedPaging.getCount().intValue());
+ assertEquals(2, expectedPaging.getSkipCount().intValue());
+ assertEquals(3, expectedPaging.getMaxItems().intValue());
+ assertTrue(expectedPaging.getTotalItems() >= 5);
+
+ // Create rendition
+ Rendition renditionRequest = new Rendition();
+ renditionRequest.setId(docLib.getId());
+ // FIXME the response status code should be changed to 202 when we fix the fwk
+ post(getRenditionsUrl(contentNodeId), userOneN1.getId(), RestApiUtil.toJsonAsString(renditionRequest), 201);
+
+ // This should be long enough for compensating the action to run.
+ Thread.sleep(3000);
+ // List all available renditions (includes those that have been created and those that are yet to be created)
+ paging = getPaging(0, 50);
+ response = getAll(getRenditionsUrl(contentNodeId), userOneN1.getId(), paging, 200);
+ renditions = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Rendition.class);
+ assertTrue(renditions.size() >= 5);
+ docLib = getRendition(renditions, "doclib");
+ assertNotNull(docLib);
+ assertEquals(RenditionStatus.CREATED, docLib.getStatus());
+ contentInfo = docLib.getContent();
+ assertNotNull(contentInfo);
+ assertEquals(MimetypeMap.MIMETYPE_IMAGE_PNG, contentInfo.getMimeType());
+ assertEquals("PNG Image", contentInfo.getMimeTypeName());
+ assertNotNull(contentInfo.getEncoding());
+ assertTrue(contentInfo.getSizeInBytes() > 0);
+
+ // List only the CREATED renditions
+ response = getAll(getRenditionsUrl(contentNodeId), userOneN1.getId(), paging, params, 200);
+ renditions = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Rendition.class);
+ assertEquals("Should've only returned the 'doclib' rendition.", 1, renditions.size());
+
+ params.put("where", "(status='NOT_CREATED')");
+ // List only the NOT_CREATED renditions
+ response = getAll(getRenditionsUrl(contentNodeId), userOneN1.getId(), paging, params, 200);
+ renditions = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Rendition.class);
+ assertTrue(renditions.size() > 0);
+ docLib = getRendition(renditions, "doclib");
+ assertNull("'doclib' rendition has already been created.", docLib);
+
+ // nodeId in the path parameter does not represent a file
+ getAll(getRenditionsUrl(folder_Id), userOneN1.getId(), paging, params, 400);
+
+ // nodeId in the path parameter does not exist
+ getAll(getRenditionsUrl(UUID.randomUUID().toString()), userOneN1.getId(), paging, params, 404);
+ }
+
+ private Rendition getRendition(List renditions, String renditionName)
+ {
+ for (Rendition rn : renditions)
+ {
+ if (rn.getId().equals(renditionName))
+ {
+ return rn;
+ }
+ }
+ return null;
+ }
+
+ private String getRenditionsUrl(String nodeId)
+ {
+ return "nodes/" + nodeId + "/renditions";
+ }
+
+ @Override
+ public String getScope()
+ {
+ return "public";
+ }
+}
diff --git a/source/test-java/org/alfresco/rest/api/tests/client/data/Rendition.java b/source/test-java/org/alfresco/rest/api/tests/client/data/Rendition.java
new file mode 100644
index 0000000000..48c7b8197e
--- /dev/null
+++ b/source/test-java/org/alfresco/rest/api/tests/client/data/Rendition.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2005-2016 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * 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 .
+ */
+
+package org.alfresco.rest.api.tests.client.data;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Representation of a rendition.
+ *
+ * @author Jamal Kaabi-Mofrad
+ */
+public class Rendition implements ExpectedComparison
+{
+ public enum RenditionStatus
+ {
+ CREATED,
+ NOT_CREATED
+ }
+
+ private String id;
+ private RenditionStatus status;
+ private ContentInfo contentInfo;
+
+ public String getId()
+ {
+ return id;
+ }
+
+ public void setId(String id)
+ {
+ this.id = id;
+ }
+
+ public RenditionStatus getStatus()
+ {
+ return status;
+ }
+
+ public void setStatus(RenditionStatus status)
+ {
+ this.status = status;
+ }
+
+ public ContentInfo getContent()
+ {
+ return contentInfo;
+ }
+
+ public void setContent(ContentInfo contentInfo)
+ {
+ this.contentInfo = contentInfo;
+ }
+
+ @Override
+ public void expected(Object obj)
+ {
+ assertTrue(obj instanceof Rendition);
+
+ Rendition other = (Rendition) obj;
+ if (this.id == null)
+ {
+ assertNotNull(other.getId());
+ }
+ else
+ {
+ assertEquals("id", this.id, other.getId());
+ }
+ if (this.status == null)
+ {
+ assertNotNull(other.getStatus());
+ }
+ else
+ {
+ assertEquals("status", this.status, other.getStatus());
+ }
+ if (this.contentInfo != null)
+ {
+ this.contentInfo.expected(other.getContent());
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuilder sb = new StringBuilder(150);
+ sb.append("Rendition [id='").append(id)
+ .append(", status=").append(status)
+ .append(", contentInfo=").append(contentInfo)
+ .append(']');
+ return sb.toString();
+ }
+}