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(); + } +}