From 3d71d8c3eea00b8004e2a31684befee55e813896 Mon Sep 17 00:00:00 2001 From: Ancuta Morarasu Date: Wed, 11 May 2016 11:37:00 +0000 Subject: [PATCH] Merged HEAD (5.2) to 5.2.N (5.2.1) 126476 jkaabimofrad: Merged FILE-FOLDER-API (5.2.0) to HEAD (5.2) 122483 jkaabimofrad: RA-678: Updated create rendition Api + tests. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@126820 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- config/alfresco/public-rest-context.xml | 1 + .../rest/api/impl/RenditionsImpl.java | 64 ++++--- .../api/nodes/NodeRenditionsRelation.java | 3 + .../exceptions/DisabledServiceException.java | 41 +++++ .../rest/api/tests/RenditionsTest.java | 163 ++++++++++++++++-- .../rest/api/tests/client/data/Rendition.java | 9 +- 6 files changed, 233 insertions(+), 48 deletions(-) create mode 100644 source/java/org/alfresco/rest/framework/core/exceptions/DisabledServiceException.java diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml index c8ec2ba6c3..ea0959a78d 100644 --- a/config/alfresco/public-rest-context.xml +++ b/config/alfresco/public-rest-context.xml @@ -143,6 +143,7 @@ + diff --git a/source/java/org/alfresco/rest/api/impl/RenditionsImpl.java b/source/java/org/alfresco/rest/api/impl/RenditionsImpl.java index 717e241cb8..8510f80b6f 100644 --- a/source/java/org/alfresco/rest/api/impl/RenditionsImpl.java +++ b/source/java/org/alfresco/rest/api/impl/RenditionsImpl.java @@ -30,6 +30,7 @@ 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.DisabledServiceException; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; @@ -198,36 +199,43 @@ public class RenditionsImpl implements Renditions @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()) + // If thumbnail generation has been configured off, then don't bother. + 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); + throw new DisabledServiceException("Thumbnail generation has been disabled."); } + + final NodeRef sourceNodeRef = validateSourceNode(nodeId); + final NodeRef renditionNodeRef = getRenditionByName(sourceNodeRef, rendition.getId(), parameters); + if (renditionNodeRef != null) + { + throw new InvalidArgumentException(rendition.getId() + " rendition already exists."); + } + + // Use the thumbnail registry to get the details of the thumbnail + ThumbnailRegistry registry = thumbnailService.getThumbnailRegistry(); + ThumbnailDefinition thumbnailDefinition = registry.getThumbnailDefinition(rendition.getId()); + if (thumbnailDefinition == null) + { + throw new EntityNotFoundException(rendition.getId() + "' is not registered."); + } + + ContentData contentData = (ContentData) nodeService.getProperty(sourceNodeRef, ContentModel.PROP_CONTENT); + if (!ContentData.hasContent(contentData)) + { + throw new InvalidArgumentException("Unable to create thumbnail '" + thumbnailDefinition.getName() + "' as there is no content."); + } + // Check if anything is currently available to generate thumbnails for the specified mimeType + if (!registry.isThumbnailDefinitionAvailable(contentData.getContentUrl(), contentData.getMimetype(), contentData.getSize(), sourceNodeRef, + thumbnailDefinition)) + { + throw new InvalidArgumentException("Unable to create thumbnail '" + thumbnailDefinition.getName() + "' for " + + contentData.getMimetype() + " as no transformer is currently available."); + } + + Action action = ThumbnailHelper.createCreateThumbnailAction(thumbnailDefinition, serviceRegistry); + // Queue async creation of thumbnail + actionService.executeAction(action, sourceNodeRef, true, true); } protected NodeRef getRenditionByName(NodeRef nodeRef, String renditionId, Parameters parameters) diff --git a/source/java/org/alfresco/rest/api/nodes/NodeRenditionsRelation.java b/source/java/org/alfresco/rest/api/nodes/NodeRenditionsRelation.java index 4b32c46cee..f74c6cdc87 100644 --- a/source/java/org/alfresco/rest/api/nodes/NodeRenditionsRelation.java +++ b/source/java/org/alfresco/rest/api/nodes/NodeRenditionsRelation.java @@ -21,12 +21,14 @@ package org.alfresco.rest.api.nodes; import org.alfresco.rest.api.Renditions; import org.alfresco.rest.api.model.Rendition; +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.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Parameters; import org.alfresco.util.PropertyCheck; import org.springframework.beans.factory.InitializingBean; +import org.springframework.extensions.webscripts.Status; import java.util.Collections; import java.util.List; @@ -68,6 +70,7 @@ public class NodeRenditionsRelation implements RelationshipResourceAction.Read create(String nodeId, List entity, Parameters parameters) { diff --git a/source/java/org/alfresco/rest/framework/core/exceptions/DisabledServiceException.java b/source/java/org/alfresco/rest/framework/core/exceptions/DisabledServiceException.java new file mode 100644 index 0000000000..a81eae5a42 --- /dev/null +++ b/source/java/org/alfresco/rest/framework/core/exceptions/DisabledServiceException.java @@ -0,0 +1,41 @@ +/* + * 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.framework.core.exceptions; + +/** + * Thrown when the required service is disabled. + * Default status is Not Implemented server error = 501. + * + * @author Jamal Kaabi-Mofrad + */ +public class DisabledServiceException extends ApiException +{ + private static final long serialVersionUID = 1286704207931025507L; + + public DisabledServiceException(String msgId) + { + super(msgId); + } + + public DisabledServiceException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } +} diff --git a/source/test-java/org/alfresco/rest/api/tests/RenditionsTest.java b/source/test-java/org/alfresco/rest/api/tests/RenditionsTest.java index bf3ee49eb8..2d1185fe5d 100644 --- a/source/test-java/org/alfresco/rest/api/tests/RenditionsTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/RenditionsTest.java @@ -19,6 +19,7 @@ package org.alfresco.rest.api.tests; +import static org.alfresco.rest.api.tests.util.RestApiUtil.toJsonAsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -43,11 +44,14 @@ 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.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.namespace.QName; +import org.alfresco.util.TempFileProvider; import org.junit.After; import org.junit.Before; import org.junit.Test; +import java.io.ByteArrayInputStream; import java.io.File; import java.util.HashMap; import java.util.List; @@ -61,6 +65,9 @@ import java.util.UUID; */ public class RenditionsTest extends AbstractBaseApiTest { + private static final long PAUSE_TIME = 5000; //millisecond + private static final int MAX_RETRY = 8; + /** * User one from network one */ @@ -98,7 +105,7 @@ public class RenditionsTest extends AbstractBaseApiTest { // Create a folder within the site document's library String folderName = "folder" + System.currentTimeMillis(); - String folder_Id = addNode(userOneN1Site, folderName, ContentModel.TYPE_FOLDER, userOneN1.getId()); + String folder_Id = addToDocumentLibrary(userOneN1Site, folderName, ContentModel.TYPE_FOLDER, userOneN1.getId()); // Create multipart request String fileName = "quick.pdf"; @@ -167,10 +174,8 @@ public class RenditionsTest extends AbstractBaseApiTest assertTrue(expectedPaging.getTotalItems() >= 5); // Create 'doclib' rendition - createRendition(contentNodeId, docLib.getId()); + createAndGetRendition(contentNodeId, docLib.getId()); - // 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); @@ -226,7 +231,7 @@ public class RenditionsTest extends AbstractBaseApiTest { // Create a folder within the site document's library String folderName = "folder" + System.currentTimeMillis(); - String folder_Id = addNode(userOneN1Site, folderName, ContentModel.TYPE_FOLDER, userOneN1.getId()); + String folder_Id = addToDocumentLibrary(userOneN1Site, folderName, ContentModel.TYPE_FOLDER, userOneN1.getId()); // Create multipart request String fileName = "quick.pdf"; @@ -251,14 +256,8 @@ public class RenditionsTest extends AbstractBaseApiTest assertNull("Shouldn't have returned the encoding, as the rendition hasn't been created yet.", contentInfo.getEncoding()); assertNull("Shouldn't have returned the size, as the rendition hasn't been created yet.", contentInfo.getSizeInBytes()); - // Create 'doclib' rendition - createRendition(contentNodeId, "doclib"); - - // This should be long enough for compensating the action to run. - Thread.sleep(3000); - // Get rendition information for node - response = getSingle(getRenditionsUrl(contentNodeId), userOneN1.getId(), "doclib", 200); - rendition = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Rendition.class); + // Create and get 'doclib' rendition + rendition = createAndGetRendition(contentNodeId, "doclib"); assertNotNull(rendition); assertEquals(RenditionStatus.CREATED, rendition.getStatus()); contentInfo = rendition.getContent(); @@ -278,7 +277,100 @@ public class RenditionsTest extends AbstractBaseApiTest getSingle(getRenditionsUrl(contentNodeId), userOneN1.getId(), ("renditionId" + System.currentTimeMillis()), 404); } - private String addNode(final TestSite testSite, final String name, final QName type, String user) + /** + * Tests create rendition. + *

POST:

+ * {@literal :/alfresco/api//public/alfresco/versions/1/nodes//renditions} + */ + @Test + public void testCreateRendition() throws Exception + { + // Create a folder within the site document's library + String folderName = "folder" + System.currentTimeMillis(); + String folder_Id = addToDocumentLibrary(userOneN1Site, folderName, ContentModel.TYPE_FOLDER, userOneN1.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(); + + // Get rendition (not created yet) information for node + response = getSingle(getRenditionsUrl(contentNodeId), userOneN1.getId(), "imgpreview", 200); + Rendition rendition = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Rendition.class); + assertNotNull(rendition); + assertEquals(RenditionStatus.NOT_CREATED, rendition.getStatus()); + + // Create and get 'imgpreview' rendition + rendition = createAndGetRendition(contentNodeId, "imgpreview"); + assertNotNull(rendition); + assertEquals(RenditionStatus.CREATED, rendition.getStatus()); + ContentInfo contentInfo = rendition.getContent(); + assertNotNull(contentInfo); + assertEquals(MimetypeMap.MIMETYPE_IMAGE_JPEG, contentInfo.getMimeType()); + assertEquals("JPEG Image", contentInfo.getMimeTypeName()); + assertNotNull(contentInfo.getEncoding()); + assertTrue(contentInfo.getSizeInBytes() > 0); + + // -ve Tests + // The rendition requested already exists + post(getRenditionsUrl(folder_Id), userOneN1.getId(), toJsonAsString(new Rendition().setId("imgpreview")), 400); + + // Create 'doclib' rendition request + Rendition renditionRequest = new Rendition().setId("doclib"); + // nodeId in the path parameter does not represent a file + post(getRenditionsUrl(folder_Id), userOneN1.getId(), toJsonAsString(renditionRequest), 400); + + // nodeId in the path parameter does not exist + post(getRenditionsUrl(UUID.randomUUID().toString()), userOneN1.getId(), toJsonAsString(renditionRequest), 404); + + // renditionId is not registered + final String randomRenditionId = "renditionId" + System.currentTimeMillis(); + post(getRenditionsUrl(contentNodeId), userOneN1.getId(), toJsonAsString(new Rendition().setId(randomRenditionId)), 404); + + // Create a node without any content + String emptyContentNodeId = addToDocumentLibrary(userOneN1Site, "emptyDoc.txt", ContentModel.TYPE_CONTENT, userOneN1.getId()); + // The source node has no content + post(getRenditionsUrl(emptyContentNodeId), userOneN1.getId(), toJsonAsString(renditionRequest), 400); + + String content = "The quick brown fox jumps over the lazy dog."; + file = TempFileProvider.createTempFile(new ByteArrayInputStream(content.getBytes()), getClass().getSimpleName(), ".bin"); + multiPartBuilder = MultiPartBuilder.create().setFileData(new FileData("binaryFileName", file, MimetypeMap.MIMETYPE_BINARY)); + reqBody = multiPartBuilder.build(); + response = post("nodes/" + folder_Id + "/children", userOneN1.getId(), reqBody.getBody(), null, reqBody.getContentType(), 201); + Document binaryDocument = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); + + // No transformer is currently available for 'application/octet-stream' + post(getRenditionsUrl(binaryDocument.getId()), userOneN1.getId(), toJsonAsString(renditionRequest), 400); + + ThumbnailService thumbnailService = applicationContext.getBean("thumbnailService", ThumbnailService.class); + // Disable thumbnail generation + thumbnailService.setThumbnailsEnabled(false); + try + { + // Create multipart request + String txtFileName = "quick-1.txt"; + File txtFile = getResourceFile(fileName); + reqBody = MultiPartBuilder.create().setFileData(new FileData(txtFileName, txtFile, MimetypeMap.MIMETYPE_TEXT_PLAIN)).build(); + + // Upload quick-1.txt file into 'folder' + response = post("nodes/" + folder_Id + "/children", userOneN1.getId(), reqBody.getBody(), null, reqBody.getContentType(), 201); + Document txtDocument = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); + // Thumbnail generation has been disabled + post(getRenditionsUrl(txtDocument.getId()), userOneN1.getId(), toJsonAsString(renditionRequest), 501); + } + finally + { + thumbnailService.setThumbnailsEnabled(true); + } + } + + private String addToDocumentLibrary(final TestSite testSite, final String name, final QName type, String user) { return TenantUtil.runAsUserTenant(new TenantUtil.TenantRunAsWork() { @@ -290,12 +382,49 @@ public class RenditionsTest extends AbstractBaseApiTest }, user, testSite.getNetworkId()); } - private void createRendition(String sourceNodeId, String renditionId) throws Exception + private Rendition createAndGetRendition(String sourceNodeId, String renditionId) throws Exception { Rendition renditionRequest = new Rendition(); renditionRequest.setId(renditionId); - // FIXME the response status code should be changed to 202 when we fix the fwk - post(getRenditionsUrl(sourceNodeId), userOneN1.getId(), RestApiUtil.toJsonAsString(renditionRequest), 201); + + int retryCount = 0; + while (retryCount < MAX_RETRY) + { + try + { + post(getRenditionsUrl(sourceNodeId), userOneN1.getId(), toJsonAsString(renditionRequest), 202); + break; + } + catch (AssertionError ex) + { + // If no transformer is currently available, + // wait for 'PAUSE_TIME' and try again. + retryCount++; + Thread.sleep(PAUSE_TIME); + } + } + + retryCount = 0; + while (retryCount < MAX_RETRY) + { + try + { + HttpResponse response = getSingle(getRenditionsUrl(sourceNodeId), userOneN1.getId(), renditionId, 200); + Rendition rendition = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Rendition.class); + assertNotNull(rendition); + assertEquals(RenditionStatus.CREATED, rendition.getStatus()); + return rendition; + } + catch (AssertionError ex) + { + // If the asynchronous create rendition action is not finished yet, + // wait for 'PAUSE_TIME' and try again. + retryCount++; + Thread.sleep(PAUSE_TIME); + } + } + + return null; } private Rendition getRendition(List renditions, String renditionName) 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 index 0e99bc54c8..e649cd9229 100644 --- 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 @@ -45,9 +45,10 @@ public class Rendition implements ExpectedComparison, Comparable return id; } - public void setId(String id) + public Rendition setId(String id) { this.id = id; + return this; } public RenditionStatus getStatus() @@ -55,9 +56,10 @@ public class Rendition implements ExpectedComparison, Comparable return status; } - public void setStatus(RenditionStatus status) + public Rendition setStatus(RenditionStatus status) { this.status = status; + return this; } public ContentInfo getContent() @@ -65,9 +67,10 @@ public class Rendition implements ExpectedComparison, Comparable return contentInfo; } - public void setContent(ContentInfo contentInfo) + public Rendition setContent(ContentInfo contentInfo) { this.contentInfo = contentInfo; + return this; } @Override