MNT-24883 adding source file name to transform options

Added source file name to transform options if exist in nodeService. It will be used in ATS to maintain consistency in transform for filenames.
This commit is contained in:
bsayan2 2025-06-17 14:13:41 +05:30 committed by GitHub
parent b800c86a7c
commit 81c47c2b04
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 706 additions and 494 deletions

View File

@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
@ -26,16 +26,16 @@
*/ */
package org.alfresco.transform.base.fs; package org.alfresco.transform.base.fs;
import org.alfresco.transform.base.logging.LogEntry; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import org.alfresco.transform.common.ExtensionService;
import org.alfresco.transform.exceptions.TransformException; import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION;
import org.springframework.core.io.Resource; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import org.springframework.core.io.UrlResource; import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE;
import org.springframework.http.ResponseEntity; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import org.springframework.web.multipart.MultipartFile; import static org.springframework.util.StringUtils.getFilename;
import org.springframework.web.util.UriUtils;
import static org.alfresco.transform.common.ExtensionService.getExtensionForMimetype;
import jakarta.servlet.http.HttpServletRequest;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@ -43,14 +43,19 @@ import java.io.OutputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.UUID;
import jakarta.servlet.http.HttpServletRequest;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import org.apache.commons.lang3.StringUtils;
import static org.alfresco.transform.common.ExtensionService.getExtensionForMimetype; import org.springframework.core.io.Resource;
import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION; import org.springframework.core.io.UrlResource;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import org.springframework.http.ResponseEntity;
import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE; import org.springframework.web.multipart.MultipartFile;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import org.springframework.web.util.UriUtils;
import static org.springframework.util.StringUtils.getFilename;
import org.alfresco.transform.base.logging.LogEntry;
import org.alfresco.transform.common.ExtensionService;
import org.alfresco.transform.exceptions.TransformException;
public class FileManager public class FileManager
{ {
@ -58,16 +63,19 @@ public class FileManager
public static final String TARGET_FILE = "targetFile"; public static final String TARGET_FILE = "targetFile";
private FileManager() private FileManager()
{ {}
}
public static File createSourceFile(HttpServletRequest request, InputStream inputStream, String sourceMimetype) public static File createSourceFile(HttpServletRequest request, InputStream inputStream, String sourceMimetype, String sourceFileName)
{ {
try try
{ {
String extension = "."+getExtensionForMimetype(sourceMimetype); String extension = "." + getExtensionForMimetype(sourceMimetype);
File file = TempFileProvider.createTempFile("source_", extension); File file = StringUtils.isEmpty(sourceFileName)
? TempFileProvider.createTempFile("source_", extension)
: TempFileProvider.createFileWithinUUIDTempDir(sourceFileName);
Files.copy(inputStream, file.toPath(), REPLACE_EXISTING); Files.copy(inputStream, file.toPath(), REPLACE_EXISTING);
if (request != null) if (request != null)
{ {
request.setAttribute(SOURCE_FILE, file); request.setAttribute(SOURCE_FILE, file);
@ -85,7 +93,7 @@ public class FileManager
{ {
try try
{ {
String extension = "."+ExtensionService.getExtensionForTargetMimetype(targetMimetype, sourceMimetype); String extension = "." + ExtensionService.getExtensionForTargetMimetype(targetMimetype, sourceMimetype);
File file = TempFileProvider.createTempFile("target_", extension); File file = TempFileProvider.createTempFile("target_", extension);
if (request != null) if (request != null)
{ {
@ -120,20 +128,20 @@ public class FileManager
else else
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
"Could not read the target file: " + file.getPath()); "Could not read the target file: " + file.getPath());
} }
} }
catch (MalformedURLException e) catch (MalformedURLException e)
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, throw new TransformException(INTERNAL_SERVER_ERROR,
"The target filename was malformed: " + file.getPath(), e); "The target filename was malformed: " + file.getPath(), e);
} }
} }
public static InputStream getMultipartFileInputStream(MultipartFile sourceMultipartFile) public static InputStream getMultipartFileInputStream(MultipartFile sourceMultipartFile)
{ {
InputStream inputStream; InputStream inputStream;
if (sourceMultipartFile == null) if (sourceMultipartFile == null)
{ {
throw new TransformException(BAD_REQUEST, "Required request part 'file' is not present"); throw new TransformException(BAD_REQUEST, "Required request part 'file' is not present");
} }
@ -192,7 +200,7 @@ public class FileManager
// targetFilename should never be null (will be "transform."+<something>), so we should not worry about encodePath(null) // targetFilename should never be null (will be "transform."+<something>), so we should not worry about encodePath(null)
targetFilename = UriUtils.encodePath(getFilename(targetFilename), "UTF-8"); targetFilename = UriUtils.encodePath(getFilename(targetFilename), "UTF-8");
return ResponseEntity.ok().header(CONTENT_DISPOSITION, return ResponseEntity.ok().header(CONTENT_DISPOSITION,
"attachment; filename*=UTF-8''" + targetFilename).body(targetResource); "attachment; filename*=UTF-8''" + targetFilename).body(targetResource);
} }
/** /**
@ -201,8 +209,7 @@ public class FileManager
public static class TempFileProvider public static class TempFileProvider
{ {
private TempFileProvider() private TempFileProvider()
{ {}
}
public static File createTempFile(final String prefix, final String suffix) public static File createTempFile(final String prefix, final String suffix)
{ {
@ -214,11 +221,22 @@ public class FileManager
catch (IOException e) catch (IOException e)
{ {
throw new RuntimeException( throw new RuntimeException(
"Failed to created temp file: \n prefix: " + prefix + "Failed to created temp file: \n prefix: " + prefix +
"\n suffix: " + suffix + "\n directory: " + directory, e); "\n suffix: " + suffix + "\n directory: " + directory,
e);
} }
} }
public static File createFileWithinUUIDTempDir(String sourceFileName)
{
File tempDir = new File(getTempDir(), UUID.randomUUID().toString());
if (!tempDir.mkdirs() && !tempDir.exists())
{
throw new TransformException(INSUFFICIENT_STORAGE, "Failed to create temp directory: " + tempDir);
}
return new File(tempDir, sourceFileName);
}
private static File getTempDir() private static File getTempDir()
{ {
final String dirName = "Alfresco"; final String dirName = "Alfresco";

View File

@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2022 - 2023 Alfresco Software Limited * Copyright (C) 2022 - 2025 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
@ -26,19 +26,12 @@
*/ */
package org.alfresco.transform.base.transform; package org.alfresco.transform.base.transform;
import org.alfresco.transform.base.CustomTransformer; import static org.springframework.http.HttpStatus.BAD_REQUEST;
import org.alfresco.transform.base.TransformController; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR;
import org.alfresco.transform.base.logging.LogEntry; import static org.springframework.http.HttpStatus.OK;
import org.alfresco.transform.base.probes.ProbeTransform;
import org.alfresco.transform.base.registry.CustomTransformers; import static org.alfresco.transform.common.RequestParamMap.*;
import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transform.common.TransformerDebug;
import org.alfresco.transform.registry.TransformServiceRegistry;
import org.springframework.web.multipart.MultipartFile;
import jakarta.jms.Destination;
import jakarta.servlet.http.HttpServletRequest;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
@ -46,30 +39,32 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import jakarta.jms.Destination;
import jakarta.servlet.http.HttpServletRequest;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL; import org.springframework.web.multipart.MultipartFile;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_EXTENSION;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_MIMETYPE; import org.alfresco.transform.base.CustomTransformer;
import static org.alfresco.transform.common.RequestParamMap.TARGET_EXTENSION; import org.alfresco.transform.base.TransformController;
import static org.alfresco.transform.common.RequestParamMap.TARGET_MIMETYPE; import org.alfresco.transform.base.logging.LogEntry;
import static org.springframework.http.HttpStatus.BAD_REQUEST; import org.alfresco.transform.base.probes.ProbeTransform;
import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import org.alfresco.transform.base.registry.CustomTransformers;
import static org.springframework.http.HttpStatus.OK; import org.alfresco.transform.client.model.TransformRequest;
import org.alfresco.transform.common.TransformerDebug;
import org.alfresco.transform.exceptions.TransformException;
import org.alfresco.transform.registry.TransformServiceRegistry;
/** /**
* Provides the transform logic common to http (upload/download), message and probe requests. See * Provides the transform logic common to http (upload/download), message and probe requests. See {@link TransformHandler#handleHttpRequest(HttpServletRequest, MultipartFile, String, String, Map, ProbeTransform)}, {@link TransformHandler#handleMessageRequest(TransformRequest, Long, Destination, ProbeTransform)} and {@link TransformHandler#handleProbeRequest(String, String, Map, File, File, ProbeTransform)}. Note the handing of transform requests via a message queue is the same as via the {@link TransformController#transform(TransformRequest, Long, Destination)}.
* {@link TransformHandler#handleHttpRequest(HttpServletRequest, MultipartFile, String, String, Map, ProbeTransform)},
* {@link TransformHandler#handleMessageRequest(TransformRequest, Long, Destination, ProbeTransform)} and
* {@link TransformHandler#handleProbeRequest(String, String, Map, File, File, ProbeTransform)}. Note the handing of transform requests
* via a message queue is the same as via the {@link TransformController#transform(TransformRequest, Long, Destination)}.
*/ */
abstract class ProcessHandler extends FragmentHandler abstract class ProcessHandler extends FragmentHandler
{ {
private static final List<String> NON_TRANSFORM_OPTION_REQUEST_PARAMETERS = Arrays.asList(SOURCE_EXTENSION, private static final List<String> NON_TRANSFORM_OPTION_REQUEST_PARAMETERS = Arrays.asList(SOURCE_EXTENSION,
TARGET_EXTENSION, TARGET_MIMETYPE, SOURCE_MIMETYPE, DIRECT_ACCESS_URL); TARGET_EXTENSION, TARGET_MIMETYPE, SOURCE_MIMETYPE, DIRECT_ACCESS_URL, SOURCE_FILENAME);
protected final String sourceMimetype; protected final String sourceMimetype;
protected final String targetMimetype; protected final String targetMimetype;
protected final String sourceFileName;
private final Map<String, String> transformOptions; private final Map<String, String> transformOptions;
protected String reference; protected String reference;
private final TransformServiceRegistry transformRegistry; private final TransformServiceRegistry transformRegistry;
@ -78,11 +73,12 @@ abstract class ProcessHandler extends FragmentHandler
private final CustomTransformers customTransformers; private final CustomTransformers customTransformers;
ProcessHandler(String sourceMimetype, String targetMimetype, Map<String, String> transformOptions, ProcessHandler(String sourceMimetype, String targetMimetype, Map<String, String> transformOptions,
String reference, TransformServiceRegistry transformRegistry, TransformerDebug transformerDebug, String reference, TransformServiceRegistry transformRegistry, TransformerDebug transformerDebug,
ProbeTransform probeTransform, CustomTransformers customTransformers) ProbeTransform probeTransform, CustomTransformers customTransformers)
{ {
this.sourceMimetype = sourceMimetype; this.sourceMimetype = sourceMimetype;
this.targetMimetype = targetMimetype; this.targetMimetype = targetMimetype;
this.sourceFileName = transformOptions.getOrDefault(SOURCE_FILENAME, null);
this.transformOptions = cleanTransformOptions(transformOptions); this.transformOptions = cleanTransformOptions(transformOptions);
this.reference = reference; this.reference = reference;
@ -107,7 +103,6 @@ abstract class ProcessHandler extends FragmentHandler
super.init(); super.init();
} }
public String getReference() public String getReference()
{ {
return reference; return reference;
@ -118,6 +113,7 @@ abstract class ProcessHandler extends FragmentHandler
LogEntry.start(); LogEntry.start();
transformManager.setSourceMimetype(sourceMimetype); transformManager.setSourceMimetype(sourceMimetype);
transformManager.setTargetMimetype(targetMimetype); transformManager.setTargetMimetype(targetMimetype);
transformManager.setSourceFileName(sourceFileName);
probeTransform.incrementTransformerCount(); probeTransform.incrementTransformerCount();
try try
{ {
@ -131,13 +127,13 @@ abstract class ProcessHandler extends FragmentHandler
} }
catch (TransformException e) catch (TransformException e)
{ {
transformerDebug.logFailure(reference, " Error: "+e.getMessage()); transformerDebug.logFailure(reference, " Error: " + e.getMessage());
LogEntry.setStatusCodeAndMessage(e.getStatus(), e.getMessage()); LogEntry.setStatusCodeAndMessage(e.getStatus(), e.getMessage());
handleTransformException(e); handleTransformException(e);
} }
catch (Exception e) catch (Exception e)
{ {
transformerDebug.logFailure(reference, " Error: "+e.getMessage()); transformerDebug.logFailure(reference, " Error: " + e.getMessage());
LogEntry.setStatusCodeAndMessage(INTERNAL_SERVER_ERROR, e.getMessage()); LogEntry.setStatusCodeAndMessage(INTERNAL_SERVER_ERROR, e.getMessage());
handleException(e); handleException(e);
} }
@ -174,8 +170,7 @@ abstract class ProcessHandler extends FragmentHandler
} }
protected void sendTransformResponse(TransformManagerImpl transformManager) protected void sendTransformResponse(TransformManagerImpl transformManager)
{ {}
}
protected void handleTransformException(TransformException e) protected void handleTransformException(TransformException e)
{ {
@ -188,19 +183,19 @@ abstract class ProcessHandler extends FragmentHandler
} }
private String getTransformerName(final String sourceMimetype, long sourceSizeInBytes, final String targetMimetype, private String getTransformerName(final String sourceMimetype, long sourceSizeInBytes, final String targetMimetype,
final Map<String, String> transformOptions) final Map<String, String> transformOptions)
{ {
final String transformerName = transformRegistry.findTransformerName(sourceMimetype, final String transformerName = transformRegistry.findTransformerName(sourceMimetype,
sourceSizeInBytes, targetMimetype, transformOptions, null); sourceSizeInBytes, targetMimetype, transformOptions, null);
if (transformerName == null) if (transformerName == null)
{ {
throw new TransformException(BAD_REQUEST, "No transforms for: "+ throw new TransformException(BAD_REQUEST, "No transforms for: " +
sourceMimetype+ sourceMimetype +
(sourceSizeInBytes >= 0 ? " ("+TransformerDebug.fileSize(sourceSizeInBytes)+")" : "")+ (sourceSizeInBytes >= 0 ? " (" + TransformerDebug.fileSize(sourceSizeInBytes) + ")" : "") +
" -> "+targetMimetype+ " -> " + targetMimetype +
transformOptions.entrySet().stream() transformOptions.entrySet().stream()
.map(entry -> entry.getKey()+"="+entry.getValue()) .map(entry -> entry.getKey() + "=" + entry.getValue())
.collect(Collectors.joining(", ", " ", ""))); .collect(Collectors.joining(", ", " ", "")));
} }
return transformerName; return transformerName;
} }
@ -210,7 +205,7 @@ abstract class ProcessHandler extends FragmentHandler
CustomTransformer customTransformer = customTransformers.get(transformName); CustomTransformer customTransformer = customTransformers.get(transformName);
if (customTransformer == null) if (customTransformer == null)
{ {
throw new TransformException(INTERNAL_SERVER_ERROR, "Custom Transformer "+transformName+" not found"); throw new TransformException(INTERNAL_SERVER_ERROR, "Custom Transformer " + transformName + " not found");
} }
return customTransformer; return customTransformer;
} }

View File

@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2005 - 2023 Alfresco Software Limited * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
@ -26,18 +26,21 @@
*/ */
package org.alfresco.transform.base.transform; package org.alfresco.transform.base.transform;
import org.alfresco.transform.base.TransformManager;
import org.alfresco.transform.base.fs.FileManager;
import org.alfresco.transform.base.util.OutputStreamLengthRecorder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import jakarta.servlet.http.HttpServletRequest;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Locale;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.alfresco.transform.base.TransformManager;
import org.alfresco.transform.base.fs.FileManager;
import org.alfresco.transform.base.util.OutputStreamLengthRecorder;
/** /**
* Manages the input and output streams and any temporary files that have been created. * Manages the input and output streams and any temporary files that have been created.
@ -60,6 +63,17 @@ public class TransformManagerImpl implements TransformManager
private boolean createTargetFileCalled; private boolean createTargetFileCalled;
private Boolean startedWithSourceFile; private Boolean startedWithSourceFile;
private Boolean startedWithTargetFile; private Boolean startedWithTargetFile;
private String sourceFileName;
public String getSourceFileName()
{
return sourceFileName;
}
public void setSourceFileName(String sourceFileName)
{
this.sourceFileName = sourceFileName;
}
public void setRequest(HttpServletRequest request) public void setRequest(HttpServletRequest request)
{ {
@ -71,7 +85,8 @@ public class TransformManagerImpl implements TransformManager
this.processHandler = processHandler; this.processHandler = processHandler;
} }
@Override public String getRequestId() @Override
public String getRequestId()
{ {
return processHandler.getReference(); return processHandler.getReference();
} }
@ -149,7 +164,8 @@ public class TransformManagerImpl implements TransformManager
keepTargetFile = true; keepTargetFile = true;
} }
@Override public File createSourceFile() @Override
public File createSourceFile()
{ {
if (createSourceFileCalled) if (createSourceFileCalled)
{ {
@ -159,12 +175,13 @@ public class TransformManagerImpl implements TransformManager
if (sourceFile == null) if (sourceFile == null)
{ {
sourceFile = FileManager.createSourceFile(request, inputStream, sourceMimetype); sourceFile = FileManager.createSourceFile(request, inputStream, sourceMimetype, sourceFileName);
} }
return sourceFile; return sourceFile;
} }
@Override public File createTargetFile() @Override
public File createTargetFile()
{ {
if (createTargetFileCalled) if (createTargetFileCalled)
{ {
@ -204,8 +221,19 @@ public class TransformManagerImpl implements TransformManager
{ {
logger.error("Failed to delete temporary source file {}", sourceFile.getPath()); logger.error("Failed to delete temporary source file {}", sourceFile.getPath());
} }
if (sourceFile != null)
{
File parentDir = sourceFile.getParentFile();
if (parentDir != null
&& !StringUtils.equalsAny(parentDir.getName().toLowerCase(Locale.ROOT), "alfresco", "temp", "tmp")
&& !parentDir.delete())
{
logger.error("Failed to delete parent directory {}", parentDir.getPath());
}
}
outputStreamLengthRecorder = null; outputStreamLengthRecorder = null;
sourceFile = null; sourceFile = null;
sourceFileName = null;
createSourceFileCalled = false; createSourceFileCalled = false;
startedWithSourceFile = null; startedWithSourceFile = null;
} }

View File

@ -10,6 +10,7 @@
<form method="POST" enctype="multipart/form-data" th:action="${proxyPathPrefix+'/test'}"> <form method="POST" enctype="multipart/form-data" th:action="${proxyPathPrefix+'/test'}">
<table> <table>
<tr><td><div style="text-align:right">file</div></td><td><input type="file" name="file" /></td></tr> <tr><td><div style="text-align:right">file</div></td><td><input type="file" name="file" /></td></tr>
<tr><td><div style="text-align:right">sourceFilename</div></td><td><input type="text" name="sourceFilename"/></td></tr>
<tr><td><div style="text-align:right">directAccessUrl</div></td><td><input type="text" name="directAccessUrl"/></td></tr> <tr><td><div style="text-align:right">directAccessUrl</div></td><td><input type="text" name="directAccessUrl"/></td></tr>
<tr><td><div style="text-align:right">sourceMimetype</div></td><td><input type="text" name="sourceMimetype" value="" /></td> <tr><td><div style="text-align:right">sourceMimetype</div></td><td><input type="text" name="sourceMimetype" value="" /></td>
<td><select name="_sourceMimetype"> <td><select name="_sourceMimetype">

View File

@ -26,25 +26,12 @@
*/ */
package org.alfresco.transform.base; package org.alfresco.transform.base;
import com.fasterxml.jackson.databind.ObjectMapper; import static org.awaitility.Awaitility.await;
import org.alfresco.transform.base.fakes.FakeTransformEngineWithAllInOne; import static org.hamcrest.Matchers.containsString;
import org.alfresco.transform.base.fakes.FakeTransformEngineWithOneCustomTransformer; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.alfresco.transform.base.fakes.FakeTransformEngineWithTwoCustomTransformers; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import org.alfresco.transform.base.fakes.FakeTransformerPdf2Jpg; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import org.alfresco.transform.base.fakes.FakeTransformerPdf2Png; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.alfresco.transform.base.fakes.FakeTransformerTxT2Pdf;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.nio.charset.StandardCharsets;
import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;
import static org.alfresco.transform.base.TransformControllerTest.assertConfig; import static org.alfresco.transform.base.TransformControllerTest.assertConfig;
import static org.alfresco.transform.base.TransformControllerTest.getLogMessagesFor; import static org.alfresco.transform.base.TransformControllerTest.getLogMessagesFor;
@ -64,28 +51,42 @@ import static org.alfresco.transform.common.RequestParamMap.ENDPOINT_VERSION;
import static org.alfresco.transform.common.RequestParamMap.PAGE_REQUEST_PARAM; import static org.alfresco.transform.common.RequestParamMap.PAGE_REQUEST_PARAM;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_MIMETYPE; import static org.alfresco.transform.common.RequestParamMap.SOURCE_MIMETYPE;
import static org.alfresco.transform.common.RequestParamMap.TARGET_MIMETYPE; import static org.alfresco.transform.common.RequestParamMap.TARGET_MIMETYPE;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.Matchers.containsString; import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.StringJoiner;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import java.util.concurrent.TimeUnit;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.alfresco.transform.base.fakes.FakeTransformEngineWithAllInOne;
import org.alfresco.transform.base.fakes.FakeTransformEngineWithOneCustomTransformer;
import org.alfresco.transform.base.fakes.FakeTransformEngineWithTwoCustomTransformers;
import org.alfresco.transform.base.fakes.FakeTransformerPdf2Jpg;
import org.alfresco.transform.base.fakes.FakeTransformerPdf2Png;
import org.alfresco.transform.base.fakes.FakeTransformerTxT2Pdf;
/** /**
* Testing TransformController functionality where there are multiple TransformEngines brought together * Testing TransformController functionality where there are multiple TransformEngines brought together in a single t-engine.
* in a single t-engine.
* *
* Repeats a set of tests from {@link TransformControllerTest}, which tests the single TransformEngine case. * Repeats a set of tests from {@link TransformControllerTest}, which tests the single TransformEngine case.
*/ */
@AutoConfigureMockMvc @AutoConfigureMockMvc
@SpringBootTest(classes={org.alfresco.transform.base.Application.class}) @SpringBootTest(classes = {org.alfresco.transform.base.Application.class})
@ContextConfiguration(classes = { @ContextConfiguration(classes = {
FakeTransformEngineWithAllInOne.class, FakeTransformEngineWithAllInOne.class,
FakeTransformEngineWithTwoCustomTransformers.class, FakeTransformEngineWithTwoCustomTransformers.class,
FakeTransformerTxT2Pdf.class, FakeTransformerTxT2Pdf.class,
FakeTransformerPdf2Png.class, FakeTransformerPdf2Png.class,
FakeTransformEngineWithOneCustomTransformer.class, FakeTransformEngineWithOneCustomTransformer.class,
FakeTransformerPdf2Jpg.class}) FakeTransformerPdf2Jpg.class})
public class TransformControllerAllInOneTest public class TransformControllerAllInOneTest
{ {
@Autowired @Autowired
@ -112,45 +113,45 @@ public class TransformControllerAllInOneTest
transformController.startup(); transformController.startup();
assertEquals( assertEquals(
"--------------------------------------------------------------------------------------------------------------------------------------------------------------\n" "--------------------------------------------------------------------------------------------------------------------------------------------------------------\n"
+ "Startup 0000 AllInOne\n" + "Startup 0000 AllInOne\n"
+ "Line 2 0000 AllInOne\n" + "Line 2 0000 AllInOne\n"
+ "Line 3\n" + "Line 3\n"
+ "--------------------------------------------------------------------------------------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------------------------------------------------------------------------------------------\n"
+ "Starting application components... Done", + "Starting application components... Done",
controllerLogMessages.toString()); controllerLogMessages.toString());
} }
@Test @Test
public void testVersionEndpointIncludesAvailable() throws Exception public void testVersionEndpointIncludesAvailable() throws Exception
{ {
mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_VERSION)) mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_VERSION))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string("AllInOne "+coreVersion)); .andExpect(content().string("AllInOne " + coreVersion));
} }
@Test @Test
public void testRootEndpointReturnsTestPage() throws Exception public void testRootEndpointReturnsTestPage() throws Exception
{ {
mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_ROOT)) mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_ROOT))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string(containsString("AllInOne Test Page"))); .andExpect(content().string(containsString("AllInOne Test Page")));
} }
@Test @Test
public void testErrorEndpointReturnsErrorPage() throws Exception public void testErrorEndpointReturnsErrorPage() throws Exception
{ {
mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_ERROR)) mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_ERROR))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string(containsString("AllInOne Error Page"))); .andExpect(content().string(containsString("AllInOne Error Page")));
} }
@Test @Test
public void testLogEndpointReturnsLogPage() throws Exception public void testLogEndpointReturnsLogPage() throws Exception
{ {
mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_LOG)) mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_LOG))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string(containsString("AllInOne Log Entries"))); .andExpect(content().string(containsString("AllInOne Log Entries")));
} }
@Test @Test
@ -158,8 +159,8 @@ public class TransformControllerAllInOneTest
{ {
resetProbeForTesting(transformController); resetProbeForTesting(transformController);
mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_READY)) mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_READY))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string(containsString("Success - "))); .andExpect(content().string(containsString("Success - ")));
} }
@Test @Test
@ -167,8 +168,8 @@ public class TransformControllerAllInOneTest
{ {
resetProbeForTesting(transformController); resetProbeForTesting(transformController);
mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_LIVE)) mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_LIVE))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string(containsString("Success - "))); .andExpect(content().string(containsString("Success - ")));
} }
@Test @Test
@ -176,57 +177,58 @@ public class TransformControllerAllInOneTest
{ {
// The transformer's options should not include directAccessUrl as this is the default version of config // The transformer's options should not include directAccessUrl as this is the default version of config
assertConfig(ENDPOINT_TRANSFORM_CONFIG, assertConfig(ENDPOINT_TRANSFORM_CONFIG,
"Pdf2Jpg,null,imageOptions\n" "Pdf2Jpg,null,imageOptions\n"
+ "Pdf2Png,null,imageOptions\n" + "Pdf2Png,null,imageOptions\n"
+ "TxT2Pdf,null,docOptions\n" + "TxT2Pdf,null,docOptions\n"
+ "Txt2JpgViaPdf,null,imageOptions\n" + "Txt2JpgViaPdf,null,imageOptions\n"
+ "Txt2PngViaPdf,null,imageOptions", + "Txt2PngViaPdf,null,imageOptions",
"docOptions,imageOptions", mockMvc, objectMapper); "docOptions,imageOptions", mockMvc, objectMapper);
} }
@Test @Test
public void testConfigLatestEndpointReturnsCoreVersionAndDirectAccessUrlOption() throws Exception public void testConfigLatestEndpointReturnsCoreVersionAndDirectAccessUrlOption() throws Exception
{ {
assertConfig(ENDPOINT_TRANSFORM_CONFIG_LATEST, assertConfig(ENDPOINT_TRANSFORM_CONFIG_LATEST,
"Pdf2Jpg,"+coreVersion+",directAccessUrl,imageOptions\n" "Pdf2Jpg," + coreVersion + ",directAccessUrl,imageOptions,sourceFilename\n"
+ "Pdf2Png,"+coreVersion+",directAccessUrl,imageOptions\n" + "Pdf2Png," + coreVersion + ",directAccessUrl,imageOptions,sourceFilename\n"
+ "TxT2Pdf,"+coreVersion+",directAccessUrl,docOptions\n" + "TxT2Pdf," + coreVersion + ",directAccessUrl,docOptions,sourceFilename\n"
+ "Txt2JpgViaPdf,"+coreVersion+",directAccessUrl,imageOptions\n" + "Txt2JpgViaPdf," + coreVersion + ",directAccessUrl,imageOptions,sourceFilename\n"
+ "Txt2PngViaPdf,"+coreVersion+",directAccessUrl,imageOptions", + "Txt2PngViaPdf," + coreVersion + ",directAccessUrl,imageOptions,sourceFilename",
"directAccessUrl,docOptions,imageOptions", mockMvc, objectMapper); "directAccessUrl,docOptions,imageOptions,sourceFilename", mockMvc, objectMapper);
} }
@Test @Test
public void testTransformEndpointUsingTransformEngineWithTwoCustomTransformers() throws Exception public void testTransformEndpointUsingTransformEngineWithTwoCustomTransformers() throws Exception
{ {
await() await()
.atMost(10, TimeUnit.SECONDS) .atMost(10, TimeUnit.SECONDS)
.untilAsserted(() -> mockMvc.perform( .untilAsserted(() -> mockMvc.perform(
MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM) MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM)
.file(new MockMultipartFile("file", null, MIMETYPE_TEXT_PLAIN, .file(new MockMultipartFile("file", null, MIMETYPE_TEXT_PLAIN,
"Start".getBytes(StandardCharsets.UTF_8))) "Start".getBytes(StandardCharsets.UTF_8)))
.param(SOURCE_MIMETYPE, MIMETYPE_TEXT_PLAIN) .param(SOURCE_MIMETYPE, MIMETYPE_TEXT_PLAIN)
.param(TARGET_MIMETYPE, MIMETYPE_PDF) .param(TARGET_MIMETYPE, MIMETYPE_PDF)
.param(PAGE_REQUEST_PARAM, "1")) .param(PAGE_REQUEST_PARAM, "1"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(header().string("Content-Disposition", .andExpect(header().string("Content-Disposition",
"attachment; filename*=UTF-8''transform.pdf")) "attachment; filename*=UTF-8''transform.pdf"))
.andExpect(content().string("Start -> TxT2Pdf(page=1)"))); .andExpect(content().string("Start -> TxT2Pdf(page=1)")));
} }
@Test @Test
public void testTransformEndpointUsingTransformEngineWithOneCustomTransformer() throws Exception { public void testTransformEndpointUsingTransformEngineWithOneCustomTransformer() throws Exception
await() {
.atMost(10, TimeUnit.SECONDS) await()
.untilAsserted(() -> mockMvc.perform( .atMost(10, TimeUnit.SECONDS)
MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM) .untilAsserted(() -> mockMvc.perform(
.file(new MockMultipartFile("file", null, MIMETYPE_PDF, MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM)
"Start".getBytes(StandardCharsets.UTF_8))) .file(new MockMultipartFile("file", null, MIMETYPE_PDF,
.param(SOURCE_MIMETYPE, MIMETYPE_PDF) "Start".getBytes(StandardCharsets.UTF_8)))
.param(TARGET_MIMETYPE, MIMETYPE_IMAGE_JPEG)) .param(SOURCE_MIMETYPE, MIMETYPE_PDF)
.andExpect(status().isOk()) .param(TARGET_MIMETYPE, MIMETYPE_IMAGE_JPEG))
.andExpect(header().string("Content-Disposition", .andExpect(status().isOk())
"attachment; filename*=UTF-8''transform.jpeg")) .andExpect(header().string("Content-Disposition",
.andExpect(content().string("Start -> Pdf2Jpg()"))); "attachment; filename*=UTF-8''transform.jpeg"))
} .andExpect(content().string("Start -> Pdf2Jpg()")));
}
} }

View File

@ -114,11 +114,11 @@ import org.alfresco.transform.config.TransformConfig;
* Also see {@link TransformControllerAllInOneTest}. * Also see {@link TransformControllerAllInOneTest}.
*/ */
@AutoConfigureMockMvc @AutoConfigureMockMvc
@SpringBootTest(classes={org.alfresco.transform.base.Application.class}) @SpringBootTest(classes = {org.alfresco.transform.base.Application.class})
@ContextConfiguration(classes = { @ContextConfiguration(classes = {
FakeTransformEngineWithTwoCustomTransformers.class, FakeTransformEngineWithTwoCustomTransformers.class,
FakeTransformerTxT2Pdf.class, FakeTransformerTxT2Pdf.class,
FakeTransformerPdf2Png.class}) FakeTransformerPdf2Png.class})
public class TransformControllerTest public class TransformControllerTest
{ {
@Autowired @Autowired
@ -154,28 +154,27 @@ public class TransformControllerTest
transformController.startup(); transformController.startup();
assertEquals( assertEquals(
"--------------------------------------------------------------------------------------------------------------------------------------------------------------\n" "--------------------------------------------------------------------------------------------------------------------------------------------------------------\n"
+ "Startup 0000 TwoCustomTransformers\n" + "Startup 0000 TwoCustomTransformers\n"
+ "Line 2 0000 TwoCustomTransformers\n" + "Line 2 0000 TwoCustomTransformers\n"
+ "Line 3\n" + "Line 3\n"
+ "--------------------------------------------------------------------------------------------------------------------------------------------------------------\n" + "--------------------------------------------------------------------------------------------------------------------------------------------------------------\n"
+ "Starting application components... Done", + "Starting application components... Done",
controllerLogMessages.toString()); controllerLogMessages.toString());
} }
public static StringJoiner getLogMessagesFor(Class classBeingLogged) public static StringJoiner getLogMessagesFor(Class classBeingLogged)
{ {
StringJoiner logMessages = new StringJoiner("\n"); StringJoiner logMessages = new StringJoiner("\n");
Logger logger = (Logger) LoggerFactory.getLogger(classBeingLogged); Logger logger = (Logger) LoggerFactory.getLogger(classBeingLogged);
AppenderBase<ILoggingEvent> logAppender = new AppenderBase<>() AppenderBase<ILoggingEvent> logAppender = new AppenderBase<>() {
{
@Override @Override
protected void append(ILoggingEvent iLoggingEvent) protected void append(ILoggingEvent iLoggingEvent)
{ {
logMessages.add(iLoggingEvent.getMessage()); logMessages.add(iLoggingEvent.getMessage());
} }
}; };
logAppender.setContext((LoggerContext)LoggerFactory.getILoggerFactory()); logAppender.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
logger.setLevel(Level.DEBUG); logger.setLevel(Level.DEBUG);
logger.addAppender(logAppender); logger.addAppender(logAppender);
logAppender.start(); logAppender.start();
@ -183,7 +182,6 @@ public class TransformControllerTest
return logMessages; return logMessages;
} }
private void testPageWithOrWithoutIngresPrefix(String url, boolean behindIngres, String... expected) throws Exception private void testPageWithOrWithoutIngresPrefix(String url, boolean behindIngres, String... expected) throws Exception
{ {
boolean origBehindIngres = (boolean) ReflectionTestUtils.getField(transformController, "behindIngres"); boolean origBehindIngres = (boolean) ReflectionTestUtils.getField(transformController, "behindIngres");
@ -192,13 +190,13 @@ public class TransformControllerTest
ReflectionTestUtils.setField(transformController, "behindIngres", behindIngres); ReflectionTestUtils.setField(transformController, "behindIngres", behindIngres);
mockMvc.perform(MockMvcRequestBuilders.get(url)) mockMvc.perform(MockMvcRequestBuilders.get(url))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string(containsString(expected[0]))) .andExpect(content().string(containsString(expected[0])))
.andExpect(content().string(containsString(expected[1]))) .andExpect(content().string(containsString(expected[1])))
.andExpect(content().string(containsString(expected[2]))) .andExpect(content().string(containsString(expected[2])))
.andExpect(content().string(containsString(expected[3]))) .andExpect(content().string(containsString(expected[3])))
.andExpect(content().string(containsString(expected[4]))) .andExpect(content().string(containsString(expected[4])))
.andExpect(content().string(containsString(expected[5]))); .andExpect(content().string(containsString(expected[5])));
} }
finally finally
{ {
@ -210,79 +208,80 @@ public class TransformControllerTest
public void testVersionEndpointIncludesAvailable() throws Exception public void testVersionEndpointIncludesAvailable() throws Exception
{ {
mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_VERSION)) mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_VERSION))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string("TwoCustomTransformers "+coreVersion)); .andExpect(content().string("TwoCustomTransformers " + coreVersion));
} }
@Test @Test
public void testRootEndpointReturnsTestPage() throws Exception public void testRootEndpointReturnsTestPage() throws Exception
{ {
testPageWithOrWithoutIngresPrefix(ENDPOINT_ROOT, false, testPageWithOrWithoutIngresPrefix(ENDPOINT_ROOT, false,
"TwoCustomTransformers Test Page", "TwoCustomTransformers Test Page",
"action=\"/test\"", "action=\"/test\"",
"<a href=\"/log\">Log</a>", "<a href=\"/log\">Log</a>",
"<a href=\"/ready\">Ready</a>", "<a href=\"/ready\">Ready</a>",
"<a href=\"/live\">Live</a>", "<a href=\"/live\">Live</a>",
"<a href=\"/transform/config?configVersion=9999\">Config</a>"); "<a href=\"/transform/config?configVersion=9999\">Config</a>");
} }
@Test @Test
public void testRootEndpointReturnsTestPageWithIngres() throws Exception public void testRootEndpointReturnsTestPageWithIngres() throws Exception
{ {
testPageWithOrWithoutIngresPrefix(ENDPOINT_ROOT, true, testPageWithOrWithoutIngresPrefix(ENDPOINT_ROOT, true,
"TwoCustomTransformers Test Page", "TwoCustomTransformers Test Page",
"action=\"/twocustomtransformers/test\"", "action=\"/twocustomtransformers/test\"",
"href=\"/twocustomtransformers/log\"", "href=\"/twocustomtransformers/log\"",
"<a href=\"/twocustomtransformers/ready\">Ready</a>", "<a href=\"/twocustomtransformers/ready\">Ready</a>",
"<a href=\"/twocustomtransformers/live\">Live</a>", "<a href=\"/twocustomtransformers/live\">Live</a>",
"<a href=\"/twocustomtransformers/transform/config?configVersion=9999\">Config</a>"); "<a href=\"/twocustomtransformers/transform/config?configVersion=9999\">Config</a>");
} }
@Test @Test
public void testErrorEndpointReturnsErrorPage() throws Exception public void testErrorEndpointReturnsErrorPage() throws Exception
{ {
testPageWithOrWithoutIngresPrefix(ENDPOINT_ERROR, false, testPageWithOrWithoutIngresPrefix(ENDPOINT_ERROR, false,
"TwoCustomTransformers Error Page", "TwoCustomTransformers Error Page",
"<a href=\"/\">Test</a>", "<a href=\"/\">Test</a>",
"<a href=\"/log\">Log</a>", "<a href=\"/log\">Log</a>",
"<a href=\"/ready\">Ready</a>", "<a href=\"/ready\">Ready</a>",
"<a href=\"/live\">Live</a>", "<a href=\"/live\">Live</a>",
"<a href=\"/transform/config?configVersion=9999\">Config</a>"); } "<a href=\"/transform/config?configVersion=9999\">Config</a>");
}
@Test @Test
public void testErrorEndpointReturnsErrorPageWithIngres() throws Exception public void testErrorEndpointReturnsErrorPageWithIngres() throws Exception
{ {
testPageWithOrWithoutIngresPrefix(ENDPOINT_ERROR, true, testPageWithOrWithoutIngresPrefix(ENDPOINT_ERROR, true,
"TwoCustomTransformers Error Page", "TwoCustomTransformers Error Page",
"href=\"/twocustomtransformers/\"", "href=\"/twocustomtransformers/\"",
"href=\"/twocustomtransformers/log\"", "href=\"/twocustomtransformers/log\"",
"<a href=\"/twocustomtransformers/ready\">Ready</a>", "<a href=\"/twocustomtransformers/ready\">Ready</a>",
"<a href=\"/twocustomtransformers/live\">Live</a>", "<a href=\"/twocustomtransformers/live\">Live</a>",
"<a href=\"/twocustomtransformers/transform/config?configVersion=9999\">Config</a>"); "<a href=\"/twocustomtransformers/transform/config?configVersion=9999\">Config</a>");
} }
@Test @Test
public void testLogEndpointReturnsLogPage() throws Exception public void testLogEndpointReturnsLogPage() throws Exception
{ {
testPageWithOrWithoutIngresPrefix(ENDPOINT_LOG, false, testPageWithOrWithoutIngresPrefix(ENDPOINT_LOG, false,
"TwoCustomTransformers Log Entries", "TwoCustomTransformers Log Entries",
"<a href=\"/\">Test</a>", "<a href=\"/\">Test</a>",
"Log", "Log",
"<a href=\"/ready\">Ready</a>", "<a href=\"/ready\">Ready</a>",
"<a href=\"/live\">Live</a>", "<a href=\"/live\">Live</a>",
"<a href=\"/transform/config?configVersion=9999\">Config</a>"); "<a href=\"/transform/config?configVersion=9999\">Config</a>");
} }
@Test @Test
public void testLogEndpointReturnsLogPageWithIngres() throws Exception public void testLogEndpointReturnsLogPageWithIngres() throws Exception
{ {
testPageWithOrWithoutIngresPrefix(ENDPOINT_LOG, true, testPageWithOrWithoutIngresPrefix(ENDPOINT_LOG, true,
"TwoCustomTransformers Log Entries", "TwoCustomTransformers Log Entries",
"href=\"/twocustomtransformers/\"", "href=\"/twocustomtransformers/\"",
"Log", "Log",
"<a href=\"/twocustomtransformers/ready\">Ready</a>", "<a href=\"/twocustomtransformers/ready\">Ready</a>",
"<a href=\"/twocustomtransformers/live\">Live</a>", "<a href=\"/twocustomtransformers/live\">Live</a>",
"<a href=\"/twocustomtransformers/transform/config?configVersion=9999\">Config</a>"); "<a href=\"/twocustomtransformers/transform/config?configVersion=9999\">Config</a>");
} }
@Test @Test
@ -290,8 +289,8 @@ public class TransformControllerTest
{ {
resetProbeForTesting(transformController); resetProbeForTesting(transformController);
mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_READY)) mockMvc.perform(MockMvcRequestBuilders.get(ENDPOINT_READY))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(content().string(containsString("Success - "))); .andExpect(content().string(containsString("Success - ")));
} }
@Test @Test
@ -321,59 +320,60 @@ public class TransformControllerTest
// coreValue is not set as this is the default version of config // coreValue is not set as this is the default version of config
// The transformer's options should not include directAccessUrl as this is the default version of config // The transformer's options should not include directAccessUrl as this is the default version of config
assertConfig(ENDPOINT_TRANSFORM_CONFIG, assertConfig(ENDPOINT_TRANSFORM_CONFIG,
"Pdf2Png,null,imageOptions\n" "Pdf2Png,null,imageOptions\n"
+ "TxT2Pdf,null,docOptions\n" + "TxT2Pdf,null,docOptions\n"
+ "Txt2JpgViaPdf,null,imageOptions\n" + "Txt2JpgViaPdf,null,imageOptions\n"
+ "Txt2PngViaPdf,null,imageOptions", + "Txt2PngViaPdf,null,imageOptions",
"docOptions,imageOptions", mockMvc, objectMapper); "docOptions,imageOptions", mockMvc, objectMapper);
} }
@Test @Test
public void testConfigLatestEndpointReturnsCoreVersionAndDirectAccessUrlOption() throws Exception public void testConfigLatestEndpointReturnsCoreVersionAndDirectAccessUrlOption() throws Exception
{ {
assertConfig(ENDPOINT_TRANSFORM_CONFIG_LATEST, assertConfig(ENDPOINT_TRANSFORM_CONFIG_LATEST,
"Pdf2Png,"+coreVersion+",directAccessUrl,imageOptions\n" "Pdf2Png," + coreVersion + ",directAccessUrl,imageOptions,sourceFilename\n"
+ "TxT2Pdf,"+coreVersion+",directAccessUrl,docOptions\n" + "TxT2Pdf," + coreVersion + ",directAccessUrl,docOptions,sourceFilename\n"
+ "Txt2JpgViaPdf,null,imageOptions\n" + "Txt2JpgViaPdf,null,imageOptions\n"
+ "Txt2PngViaPdf,"+coreVersion+",directAccessUrl,imageOptions", + "Txt2PngViaPdf," + coreVersion + ",directAccessUrl,imageOptions,sourceFilename",
"directAccessUrl,docOptions,imageOptions", mockMvc, objectMapper); "directAccessUrl,docOptions,imageOptions,sourceFilename", mockMvc, objectMapper);
} }
static void assertConfig(String url, String expectedTransformers, String expectedOptions, static void assertConfig(String url, String expectedTransformers, String expectedOptions,
MockMvc mockMvc, ObjectMapper objectMapper) throws Exception MockMvc mockMvc, ObjectMapper objectMapper) throws Exception
{ {
TransformConfig config = objectMapper.readValue( TransformConfig config = objectMapper.readValue(
mockMvc.perform(MockMvcRequestBuilders.get(url)) mockMvc.perform(MockMvcRequestBuilders.get(url))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE)) .andExpect(header().string(CONTENT_TYPE, APPLICATION_JSON_VALUE))
.andReturn() .andReturn()
.getResponse() .getResponse()
.getContentAsString(), TransformConfig.class); .getContentAsString(),
TransformConfig.class);
// Gets a list of transformerNames,coreVersion,optionNames // Gets a list of transformerNames,coreVersion,optionNames
assertEquals(expectedTransformers, assertEquals(expectedTransformers,
config.getTransformers().stream() config.getTransformers().stream()
.map(t -> t.getTransformerName()+"," .map(t -> t.getTransformerName() + ","
+t.getCoreVersion()+"," + t.getCoreVersion() + ","
+t.getTransformOptions().stream().sorted().collect(Collectors.joining(","))) + t.getTransformOptions().stream().sorted().collect(Collectors.joining(",")))
.sorted() .sorted()
.collect(Collectors.joining("\n"))); .collect(Collectors.joining("\n")));
assertEquals(expectedOptions, assertEquals(expectedOptions,
config.getTransformOptions().keySet().stream() config.getTransformOptions().keySet().stream()
.sorted() .sorted()
.collect(Collectors.joining(","))); .collect(Collectors.joining(",")));
} }
@Test @Test
public void testTransformEndpointThatUsesTransformRequests() throws Exception public void testTransformEndpointThatUsesTransformRequests() throws Exception
{ {
final Map<String,File> sfsRef2File = new HashMap<>(); final Map<String, File> sfsRef2File = new HashMap<>();
when(fakeSfsClient.saveFile(any())).thenAnswer((Answer) invocation -> { when(fakeSfsClient.saveFile(any())).thenAnswer((Answer) invocation -> {
File originalFile = (File) invocation.getArguments()[0]; File originalFile = (File) invocation.getArguments()[0];
// Make a copy as the original might get deleted // Make a copy as the original might get deleted
File fileCopy = new File(tempDir, originalFile.getName()+"copy"); File fileCopy = new File(tempDir, originalFile.getName() + "copy");
FileUtils.copyFile(originalFile, fileCopy); FileUtils.copyFile(originalFile, fileCopy);
String fileRef = UUID.randomUUID().toString(); String fileRef = UUID.randomUUID().toString();
@ -381,9 +381,8 @@ public class TransformControllerTest
return new FileRefResponse(new FileRefEntity(fileRef)); return new FileRefResponse(new FileRefEntity(fileRef));
}); });
when(fakeSfsClient.retrieveFile(any())).thenAnswer((Answer) invocation -> when(fakeSfsClient.retrieveFile(any())).thenAnswer((Answer) invocation -> ResponseEntity.ok().header(CONTENT_DISPOSITION, "attachment; filename*=UTF-8''transform.tmp")
ResponseEntity.ok().header(CONTENT_DISPOSITION,"attachment; filename*=UTF-8''transform.tmp") .body((Resource) new UrlResource(sfsRef2File.get(invocation.getArguments()[0]).toURI())));
.body((Resource) new UrlResource(sfsRef2File.get(invocation.getArguments()[0]).toURI())));
File sourceFile = getTestFile("original.txt", true, tempDir); File sourceFile = getTestFile("original.txt", true, tempDir);
String sourceFileRef = fakeSfsClient.saveFile(sourceFile).getEntry().getFileRef(); String sourceFileRef = fakeSfsClient.saveFile(sourceFile).getEntry().getFileRef();
@ -392,7 +391,7 @@ public class TransformControllerTest
.withRequestId("1") .withRequestId("1")
.withSchema(1) .withSchema(1)
.withClientData("Alfresco Digital Business Platform") .withClientData("Alfresco Digital Business Platform")
// .withTransformRequestOptions(ImmutableMap.of(DIRECT_ACCESS_URL, "file://"+sourceFile.toPath())) // .withTransformRequestOptions(ImmutableMap.of(DIRECT_ACCESS_URL, "file://"+sourceFile.toPath()))
.withSourceReference(sourceFileRef) .withSourceReference(sourceFileRef)
.withSourceMediaType(MIMETYPE_TEXT_PLAIN) .withSourceMediaType(MIMETYPE_TEXT_PLAIN)
.withSourceSize(sourceFile.length()) .withSourceSize(sourceFile.length())
@ -423,16 +422,16 @@ public class TransformControllerTest
public void testTransformEndpointThatUploadsAndDownloadsContent() throws Exception public void testTransformEndpointThatUploadsAndDownloadsContent() throws Exception
{ {
mockMvc.perform( mockMvc.perform(
MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM) MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM)
.file(new MockMultipartFile("file", null, MIMETYPE_TEXT_PLAIN, .file(new MockMultipartFile("file", null, MIMETYPE_TEXT_PLAIN,
"Start".getBytes(StandardCharsets.UTF_8))) "Start".getBytes(StandardCharsets.UTF_8)))
.param(SOURCE_MIMETYPE, MIMETYPE_TEXT_PLAIN) .param(SOURCE_MIMETYPE, MIMETYPE_TEXT_PLAIN)
.param(TARGET_MIMETYPE, MIMETYPE_PDF) .param(TARGET_MIMETYPE, MIMETYPE_PDF)
.param(PAGE_REQUEST_PARAM, "1")) .param(PAGE_REQUEST_PARAM, "1"))
.andExpect(status().isOk()) .andExpect(status().isOk())
.andExpect(header().string("Content-Disposition", .andExpect(header().string("Content-Disposition",
"attachment; filename*=UTF-8''transform.pdf")) "attachment; filename*=UTF-8''transform.pdf"))
.andExpect(content().string("Start -> TxT2Pdf(page=1)")); .andExpect(content().string("Start -> TxT2Pdf(page=1)"));
} }
@Test @Test
@ -445,24 +444,25 @@ public class TransformControllerTest
transformController.transformHandler = transformHandlerSpy; transformController.transformHandler = transformHandlerSpy;
mockMvc.perform( mockMvc.perform(
MockMvcRequestBuilders.multipart(ENDPOINT_TEST) MockMvcRequestBuilders.multipart(ENDPOINT_TEST)
.file(new MockMultipartFile("file", null, MIMETYPE_TEXT_PLAIN, .file(new MockMultipartFile("file", null, MIMETYPE_TEXT_PLAIN,
"Start".getBytes(StandardCharsets.UTF_8))) "Start".getBytes(StandardCharsets.UTF_8)))
.param(SOURCE_MIMETYPE, MIMETYPE_IMAGE_BMP) .param(SOURCE_MIMETYPE, MIMETYPE_IMAGE_BMP)
.param("_"+SOURCE_MIMETYPE, MIMETYPE_TEXT_PLAIN) .param("_" + SOURCE_MIMETYPE, MIMETYPE_TEXT_PLAIN)
.param(TARGET_MIMETYPE, MIMETYPE_PDF) .param(TARGET_MIMETYPE, MIMETYPE_PDF)
.param("_"+TARGET_MIMETYPE, "") .param("_" + TARGET_MIMETYPE, "")
.param(PAGE_REQUEST_PARAM, "replaced") .param(PAGE_REQUEST_PARAM, "replaced")
.param("name1", "hasNoValueSoRemoved").param("value1", "") .param("name1", "hasNoValueSoRemoved").param("value1", "")
.param("name2", PAGE_REQUEST_PARAM).param("value2", "1") .param("name2", PAGE_REQUEST_PARAM).param("value2", "1")
.param("name3", SOURCE_ENCODING).param("value3", "UTF-8")); .param("name3", SOURCE_ENCODING).param("value3", "UTF-8"));
verify(transformHandlerSpy).handleHttpRequest(any(), any(), eq(MIMETYPE_TEXT_PLAIN), eq(MIMETYPE_PDF), verify(transformHandlerSpy).handleHttpRequest(any(), any(), eq(MIMETYPE_TEXT_PLAIN), eq(MIMETYPE_PDF),
eq(ImmutableMap.of( eq(ImmutableMap.of(
SOURCE_MIMETYPE, MIMETYPE_TEXT_PLAIN, SOURCE_MIMETYPE, MIMETYPE_TEXT_PLAIN,
TARGET_MIMETYPE, MIMETYPE_PDF, TARGET_MIMETYPE, MIMETYPE_PDF,
PAGE_REQUEST_PARAM, "1", PAGE_REQUEST_PARAM, "1",
SOURCE_ENCODING, "UTF-8")), any()); SOURCE_ENCODING, "UTF-8")),
any());
} }
finally finally
{ {
@ -474,25 +474,25 @@ public class TransformControllerTest
public void testInterceptOfMissingServletRequestParameterException() throws Exception public void testInterceptOfMissingServletRequestParameterException() throws Exception
{ {
mockMvc.perform( mockMvc.perform(
MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM) MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM)
.file(new MockMultipartFile("file", null, MIMETYPE_TEXT_PLAIN, .file(new MockMultipartFile("file", null, MIMETYPE_TEXT_PLAIN,
"Start".getBytes(StandardCharsets.UTF_8)))) "Start".getBytes(StandardCharsets.UTF_8))))
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(status().reason(containsString("Request parameter '"+SOURCE_MIMETYPE+"' is missing"))); .andExpect(status().reason(containsString("Request parameter '" + SOURCE_MIMETYPE + "' is missing")));
} }
@Test @Test
public void testInterceptOfTransformException_noTransformers() throws Exception public void testInterceptOfTransformException_noTransformers() throws Exception
{ {
mockMvc.perform( mockMvc.perform(
MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM) MockMvcRequestBuilders.multipart(ENDPOINT_TRANSFORM)
.file(new MockMultipartFile("file", null, MIMETYPE_TEXT_PLAIN, .file(new MockMultipartFile("file", null, MIMETYPE_TEXT_PLAIN,
"Start".getBytes(StandardCharsets.UTF_8))) "Start".getBytes(StandardCharsets.UTF_8)))
.param(SOURCE_MIMETYPE, MIMETYPE_TEXT_PLAIN) .param(SOURCE_MIMETYPE, MIMETYPE_TEXT_PLAIN)
.param(TARGET_MIMETYPE, MIMETYPE_PDF) .param(TARGET_MIMETYPE, MIMETYPE_PDF)
.param("unknown", "1")) .param("unknown", "1"))
.andExpect(status().isBadRequest()) .andExpect(status().isBadRequest())
.andExpect(content().string(containsString("TwoCustomTransformers Error Page"))) .andExpect(content().string(containsString("TwoCustomTransformers Error Page")))
.andExpect(content().string(containsString("No transforms for: text/plain (5 bytes) -&gt; application/pdf unknown=1"))); .andExpect(content().string(containsString("No transforms for: text/plain (5 bytes) -&gt; application/pdf unknown=1")));
} }
} }

View File

@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Transform Core * Alfresco Transform Core
* %% * %%
* Copyright (C) 2022 - 2022 Alfresco Software Limited * Copyright (C) 2022 - 2025 Alfresco Software Limited
* %% * %%
* This file is part of the Alfresco software. * This file is part of the Alfresco software.
* - * -
@ -26,9 +26,9 @@
*/ */
package org.alfresco.transform.base.transform; package org.alfresco.transform.base.transform;
import org.alfresco.transform.base.CustomTransformer; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse;
import org.junit.jupiter.api.io.TempDir; import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
@ -43,19 +43,20 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse; import org.junit.jupiter.api.io.TempDir;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.alfresco.transform.base.CustomTransformer;
/** /**
* Tests {@link StreamHandler}, {@link TransformManagerImpl#createSourceFile()} and * Tests {@link StreamHandler}, {@link TransformManagerImpl#createSourceFile()} and {@link TransformManagerImpl#createTargetFile()} methods.
* {@link TransformManagerImpl#createTargetFile()} methods.
*/ */
@SuppressWarnings("PMD.TooManyMethods")
public class StreamHandlerTest public class StreamHandlerTest
{ {
public static final String ORIGINAL = "Original"; public static final String ORIGINAL = "Original";
public static final String CHANGE = " plus some change"; public static final String CHANGE = " plus some change";
public static final String EXPECTED = ORIGINAL+ CHANGE; public static final String EXPECTED = ORIGINAL + CHANGE;
TransformManagerImpl transformManager = new TransformManagerImpl(); TransformManagerImpl transformManager = new TransformManagerImpl();
@TempDir @TempDir
@ -132,12 +133,12 @@ public class StreamHandlerTest
public void testStartWithInputStream() throws Exception public void testStartWithInputStream() throws Exception
{ {
try (InputStream inputStream = getSourceInputStreamFromBytes(); try (InputStream inputStream = getSourceInputStreamFromBytes();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) ByteArrayOutputStream outputStream = new ByteArrayOutputStream())
{ {
transformManager.setInputStream(inputStream); transformManager.setInputStream(inputStream);
OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream); OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream);
write(outputStreamLengthRecorder, read(inputStream)+CHANGE); write(outputStreamLengthRecorder, read(inputStream) + CHANGE);
transformManager.copyTargetFileToOutputStream(); transformManager.copyTargetFileToOutputStream();
transformManager.getOutputStream().close(); transformManager.getOutputStream().close();
@ -155,14 +156,14 @@ public class StreamHandlerTest
public void testStartWithInputStreamAndCallCreateSourceFile() throws Exception public void testStartWithInputStreamAndCallCreateSourceFile() throws Exception
{ {
try (InputStream inputStream = getSourceInputStreamFromBytes(); try (InputStream inputStream = getSourceInputStreamFromBytes();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) ByteArrayOutputStream outputStream = new ByteArrayOutputStream())
{ {
transformManager.setInputStream(inputStream); transformManager.setInputStream(inputStream);
OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream); OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream);
File sourceFileCreatedByTransform = transformManager.createSourceFile(); File sourceFileCreatedByTransform = transformManager.createSourceFile();
assertTrue(sourceFileCreatedByTransform.exists()); assertTrue(sourceFileCreatedByTransform.exists());
write(outputStreamLengthRecorder, read(sourceFileCreatedByTransform)+CHANGE); write(outputStreamLengthRecorder, read(sourceFileCreatedByTransform) + CHANGE);
transformManager.copyTargetFileToOutputStream(); transformManager.copyTargetFileToOutputStream();
transformManager.getOutputStream().close(); transformManager.getOutputStream().close();
@ -185,12 +186,12 @@ public class StreamHandlerTest
transformManager.setSourceFile(sourceFile); transformManager.setSourceFile(sourceFile);
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sourceFile)); try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sourceFile));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) ByteArrayOutputStream outputStream = new ByteArrayOutputStream())
{ {
transformManager.setInputStream(inputStream); transformManager.setInputStream(inputStream);
OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream); OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream);
write(outputStreamLengthRecorder, read(inputStream)+CHANGE); write(outputStreamLengthRecorder, read(inputStream) + CHANGE);
transformManager.copyTargetFileToOutputStream(); transformManager.copyTargetFileToOutputStream();
closeInputStreamWithoutException(inputStream); closeInputStreamWithoutException(inputStream);
@ -213,14 +214,14 @@ public class StreamHandlerTest
transformManager.setSourceFile(sourceFile); transformManager.setSourceFile(sourceFile);
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sourceFile)); try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sourceFile));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) ByteArrayOutputStream outputStream = new ByteArrayOutputStream())
{ {
transformManager.setInputStream(inputStream); transformManager.setInputStream(inputStream);
OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream); OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream);
File sourceFileCreatedByTransform = transformManager.createSourceFile(); File sourceFileCreatedByTransform = transformManager.createSourceFile();
assertEquals(sourceFile, sourceFileCreatedByTransform); assertEquals(sourceFile, sourceFileCreatedByTransform);
write(outputStreamLengthRecorder, read(sourceFileCreatedByTransform)+CHANGE); write(outputStreamLengthRecorder, read(sourceFileCreatedByTransform) + CHANGE);
transformManager.copyTargetFileToOutputStream(); transformManager.copyTargetFileToOutputStream();
closeInputStreamWithoutException(inputStream); closeInputStreamWithoutException(inputStream);
@ -247,14 +248,14 @@ public class StreamHandlerTest
public void testStartWithOutputStreamAndCallCreateTargetFile() throws Exception public void testStartWithOutputStreamAndCallCreateTargetFile() throws Exception
{ {
try (InputStream inputStream = getSourceInputStreamFromBytes(); try (InputStream inputStream = getSourceInputStreamFromBytes();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) ByteArrayOutputStream outputStream = new ByteArrayOutputStream())
{ {
transformManager.setInputStream(inputStream); transformManager.setInputStream(inputStream);
transformManager.setOutputStream(outputStream); transformManager.setOutputStream(outputStream);
File targetFileCreatedByTransform = transformManager.createTargetFile(); File targetFileCreatedByTransform = transformManager.createTargetFile();
assertTrue(targetFileCreatedByTransform.exists()); assertTrue(targetFileCreatedByTransform.exists());
write(targetFileCreatedByTransform, read(inputStream)+CHANGE); write(targetFileCreatedByTransform, read(inputStream) + CHANGE);
transformManager.copyTargetFileToOutputStream(); transformManager.copyTargetFileToOutputStream();
transformManager.getOutputStream().close(); transformManager.getOutputStream().close();
@ -276,12 +277,12 @@ public class StreamHandlerTest
transformManager.setTargetFile(targetFile); transformManager.setTargetFile(targetFile);
try (InputStream inputStream = getSourceInputStreamFromBytes(); try (InputStream inputStream = getSourceInputStreamFromBytes();
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(targetFile))) OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(targetFile)))
{ {
transformManager.setInputStream(inputStream); transformManager.setInputStream(inputStream);
OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream); OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream);
write(outputStreamLengthRecorder, read(inputStream)+CHANGE); write(outputStreamLengthRecorder, read(inputStream) + CHANGE);
transformManager.copyTargetFileToOutputStream(); transformManager.copyTargetFileToOutputStream();
transformManager.getOutputStream().close(); transformManager.getOutputStream().close();
@ -304,14 +305,14 @@ public class StreamHandlerTest
transformManager.setTargetFile(targetFile); transformManager.setTargetFile(targetFile);
try (InputStream inputStream = getSourceInputStreamFromBytes(); try (InputStream inputStream = getSourceInputStreamFromBytes();
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(targetFile))) OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(targetFile)))
{ {
transformManager.setInputStream(inputStream); transformManager.setInputStream(inputStream);
transformManager.setOutputStream(outputStream); transformManager.setOutputStream(outputStream);
File targetFileCreatedByTransform = transformManager.createTargetFile(); File targetFileCreatedByTransform = transformManager.createTargetFile();
assertEquals(targetFile, targetFileCreatedByTransform); assertEquals(targetFile, targetFileCreatedByTransform);
write(targetFileCreatedByTransform, read(inputStream)+CHANGE); write(targetFileCreatedByTransform, read(inputStream) + CHANGE);
transformManager.copyTargetFileToOutputStream(); transformManager.copyTargetFileToOutputStream();
transformManager.getOutputStream().close(); transformManager.getOutputStream().close();
@ -344,12 +345,12 @@ public class StreamHandlerTest
transformManager.keepTargetFile(); transformManager.keepTargetFile();
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sourceFile)); try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sourceFile));
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(targetFile))) OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(targetFile)))
{ {
transformManager.setInputStream(inputStream); transformManager.setInputStream(inputStream);
OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream); OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream);
write(outputStreamLengthRecorder, read(inputStream)+CHANGE); write(outputStreamLengthRecorder, read(inputStream) + CHANGE);
transformManager.copyTargetFileToOutputStream(); transformManager.copyTargetFileToOutputStream();
closeInputStreamWithoutException(inputStream); closeInputStreamWithoutException(inputStream);
@ -375,12 +376,12 @@ public class StreamHandlerTest
transformManager.setTargetFile(targetFile); transformManager.setTargetFile(targetFile);
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sourceFile)); try (InputStream inputStream = new BufferedInputStream(new FileInputStream(sourceFile));
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(targetFile))) OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(targetFile)))
{ {
transformManager.setInputStream(inputStream); transformManager.setInputStream(inputStream);
OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream); OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream);
write(outputStreamLengthRecorder, read(inputStream)+CHANGE); write(outputStreamLengthRecorder, read(inputStream) + CHANGE);
transformManager.copyTargetFileToOutputStream(); transformManager.copyTargetFileToOutputStream();
closeInputStreamWithoutException(inputStream); closeInputStreamWithoutException(inputStream);
@ -404,12 +405,12 @@ public class StreamHandlerTest
transformManager.setTargetFile(targetFile); transformManager.setTargetFile(targetFile);
try (InputStream inputStream = getSourceInputStreamFromBytes(); try (InputStream inputStream = getSourceInputStreamFromBytes();
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(targetFile))) OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(targetFile)))
{ {
transformManager.setInputStream(inputStream); transformManager.setInputStream(inputStream);
OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream); OutputStream outputStreamLengthRecorder = transformManager.setOutputStream(outputStream);
write(outputStreamLengthRecorder, read(inputStream)+CHANGE); write(outputStreamLengthRecorder, read(inputStream) + CHANGE);
transformManager.copyTargetFileToOutputStream(); transformManager.copyTargetFileToOutputStream();
closeInputStreamWithoutException(inputStream); closeInputStreamWithoutException(inputStream);
@ -437,7 +438,7 @@ public class StreamHandlerTest
@Override @Override
protected void transform(CustomTransformer customTransformer) throws Exception protected void transform(CustomTransformer customTransformer) throws Exception
{ {
write(outputStream, read(inputStream)+CHANGE); write(outputStream, read(inputStream) + CHANGE);
} }
} }
@ -448,8 +449,7 @@ public class StreamHandlerTest
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) try (ByteArrayOutputStream os = new ByteArrayOutputStream())
{ {
new FakeStreamHandler() new FakeStreamHandler() {
{
@Override @Override
protected void init() throws IOException protected void init() throws IOException
{ {
@ -480,8 +480,7 @@ public class StreamHandlerTest
File sourceFile = tempFile(); File sourceFile = tempFile();
write(sourceFile, ORIGINAL); write(sourceFile, ORIGINAL);
new FakeStreamHandler() new FakeStreamHandler() {
{
@Override @Override
protected void init() throws IOException protected void init() throws IOException
{ {
@ -512,8 +511,7 @@ public class StreamHandlerTest
File sourceFile = tempFile(); File sourceFile = tempFile();
write(sourceFile, ORIGINAL); write(sourceFile, ORIGINAL);
new FakeStreamHandler() new FakeStreamHandler() {
{
@Override @Override
protected InputStream getInputStream() throws IOException protected InputStream getInputStream() throws IOException
{ {
@ -533,8 +531,7 @@ public class StreamHandlerTest
{ {
File targetFile = tempFile(); File targetFile = tempFile();
new FakeStreamHandler() new FakeStreamHandler() {
{
@Override @Override
protected InputStream getInputStream() protected InputStream getInputStream()
{ {
@ -543,7 +540,7 @@ public class StreamHandlerTest
@Override @Override
protected OutputStream getOutputStream() protected OutputStream getOutputStream()
throws FileNotFoundException throws FileNotFoundException
{ {
return getOutputStreamToFile(targetFile); return getOutputStreamToFile(targetFile);
} }
@ -560,8 +557,7 @@ public class StreamHandlerTest
{ {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) try (ByteArrayOutputStream os = new ByteArrayOutputStream())
{ {
new FakeStreamHandler() new FakeStreamHandler() {
{
@Override @Override
protected InputStream getInputStream() protected InputStream getInputStream()
{ {
@ -576,4 +572,34 @@ public class StreamHandlerTest
}.handleTransformRequest(); }.handleTransformRequest();
} }
} }
@Test
public void testStartWithInputStreamAndCallCreateSourceFileWithSourceFileName() throws Exception
{
try (
InputStream in = getSourceInputStreamFromBytes();
ByteArrayOutputStream out = new ByteArrayOutputStream();
OutputStream rec = transformManager.setOutputStream(out))
{
String testFilename = "test.docx";
transformManager.setSourceFileName(testFilename);
transformManager.setInputStream(in);
File src = transformManager.createSourceFile();
assertTrue(src.exists());
write(rec, read(src) + CHANGE);
assertEquals(testFilename, src.getName());
transformManager.copyTargetFileToOutputStream();
transformManager.getOutputStream().close();
closeInputStreamWithoutException(in);
Long outputLength = transformManager.getOutputLength();
transformManager.deleteSourceFile();
transformManager.deleteTargetFile();
assertEquals(EXPECTED, read(out));
assertEquals(EXPECTED.length(), outputLength);
assertFalse(src.exists());
}
}
} }

View File

@ -26,6 +26,7 @@ import org.alfresco.transform.config.CoreVersionDecorator;
/** /**
* Request parameters and transform options used in the core transformers. * Request parameters and transform options used in the core transformers.
*/ */
@SuppressWarnings("PMD.ConstantsInInterface")
public interface RequestParamMap public interface RequestParamMap
{ {
// html parameter names // html parameter names
@ -72,6 +73,9 @@ public interface RequestParamMap
// Html parameter names for the transform config // Html parameter names for the transform config
String HTML_COLLAPSE = "collapseHtml"; String HTML_COLLAPSE = "collapseHtml";
// source file name for the libre transform options
String SOURCE_FILENAME = "sourceFilename";
// Parameters interpreted by the TransformController // Parameters interpreted by the TransformController
String DIRECT_ACCESS_URL = "directAccessUrl"; String DIRECT_ACCESS_URL = "directAccessUrl";

View File

@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Transform Model * Alfresco Transform Model
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %% * %%
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as * it under the terms of the GNU Lesser General Public License as
@ -21,14 +21,13 @@
*/ */
package org.alfresco.transform.config; package org.alfresco.transform.config;
import org.apache.maven.artifact.versioning.ComparableVersion;
import static org.alfresco.transform.config.CoreFunction.Constants.NO_UPPER_VERSION; import static org.alfresco.transform.config.CoreFunction.Constants.NO_UPPER_VERSION;
import static org.alfresco.transform.config.CoreFunction.Constants.NO_VERSION; import static org.alfresco.transform.config.CoreFunction.Constants.NO_VERSION;
import org.apache.maven.artifact.versioning.ComparableVersion;
/** /**
* Provides a mapping between a transform {@code coreVersion} and functionality (such as the use of Direct Access URLs) * Provides a mapping between a transform {@code coreVersion} and functionality (such as the use of Direct Access URLs) supported in that version of the {@code alfresco-transform-base}, so that clients know if they may use it.
* supported in that version of the {@code alfresco-transform-base}, so that clients know if they may use it.
*/ */
public enum CoreFunction public enum CoreFunction
{ {
@ -41,7 +40,10 @@ public enum CoreFunction
/** Original way to talk to a T-Engine **/ /** Original way to talk to a T-Engine **/
// The toValue really should be null rather than "9999" but gives us an upper test value // The toValue really should be null rather than "9999" but gives us an upper test value
HTTP(null, "99999"); HTTP(null, "99999"),
/** Additional transform option to preserve original file name **/
SOURCE_FILENAME("5.1.8", null);
private final ComparableVersion fromVersion; private final ComparableVersion fromVersion;
private final ComparableVersion toVersion; private final ComparableVersion toVersion;

View File

@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Transform Model * Alfresco Transform Model
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %% * %%
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as * it under the terms of the GNU Lesser General Public License as
@ -21,7 +21,12 @@
*/ */
package org.alfresco.transform.config; package org.alfresco.transform.config;
import org.apache.maven.artifact.versioning.ComparableVersion; import static java.util.function.Predicate.not;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_FILENAME;
import static org.alfresco.transform.config.CoreFunction.Constants.NO_VERSION;
import static org.alfresco.transform.config.CoreFunction.newComparableVersion;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -31,31 +36,28 @@ import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static java.util.function.Predicate.not; import org.apache.maven.artifact.versioning.ComparableVersion;
import static org.alfresco.transform.config.CoreFunction.Constants.NO_VERSION;
import static org.alfresco.transform.config.CoreFunction.newComparableVersion;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL;
/** /**
* <p>Class sets or clears the {@code coreVersion} property of {@link Transformer}s in a {@link TransformConfig}<p/> * <p>
* Class sets or clears the {@code coreVersion} property of {@link Transformer}s in a {@link TransformConfig}
* <p/>
* *
* <p>Since alfresco-transform-core 5.2.7, the config returned by T-Engines and T-Router via their * <p>
* {@code "/transform/config"} endpoint has been decorated with an {@code coreVersion} element, indicating what core * Since alfresco-transform-core 5.2.7, the config returned by T-Engines and T-Router via their {@code "/transform/config"} endpoint has been decorated with an {@code coreVersion} element, indicating what core functionality is provided by each transformer as a result of extending the {@code TransformController} in the {@code alfresco-transform-base}. This is automatically added, so need not be specified by the T-Engine developer. It was originally added to indicate that it was possible to use Direct Access URLs (DAU).
* functionality is provided by each transformer as a result of extending the {@code TransformController} in * </p>
* the {@code alfresco-transform-base}. This is automatically added, so need not be specified by the T-Engine developer.
* It was originally added to indicate that it was possible to use Direct Access URLs (DAU).</p>
* *
* <p>This class provides methods to sets or clear the field with the version number of the * <p>
* {@code alfresco-transform-base}. No value indicates 5.2.6 or earlier.</p> * This class provides methods to sets or clear the field with the version number of the {@code alfresco-transform-base}. No value indicates 5.2.6 or earlier.
* </p>
* *
* <p>To allow older and newer version of the Repository, T-Router and T-Engines to work together, this field is only * <p>
* returned if requested by a client that also knows about the field. An optional {@code "configVersion"} parameter * To allow older and newer version of the Repository, T-Router and T-Engines to work together, this field is only returned if requested by a client that also knows about the field. An optional {@code "configVersion"} parameter has been added to the endpoint. The config for T-Engines need only add the field to single-step-transforms. When configs are combined it is then possible to add this field to pipeline and failover transforms by using the lowest core value of any step transform.
* has been added to the endpoint. The config for T-Engines need only add the field to single-step-transforms. When * </p>
* configs are combined it is then possible to add this field to pipeline and failover transforms by using the lowest
* core value of any step transform.</p>
* *
* <p>If the field is not requested in the T-Router or the all-in-one transformer endpoint, it may need to be stripped * <p>
* from the {@link TransformConfig} as some of the T-Engines may have supplied it.</p> * If the field is not requested in the T-Router or the all-in-one transformer endpoint, it may need to be stripped from the {@link TransformConfig} as some of the T-Engines may have supplied it.
* </p>
* *
* @see CoreFunction * @see CoreFunction
*/ */
@ -63,12 +65,12 @@ public class CoreVersionDecorator
{ {
public static final int CONFIG_VERSION_INCLUDES_CORE_VERSION = 2; public static final int CONFIG_VERSION_INCLUDES_CORE_VERSION = 2;
private static final Set<TransformOption> DIRECT_ACCESS_URL_TRANSFORM_OPTIONS = private static final Set<TransformOption> DIRECT_ACCESS_URL_TRANSFORM_OPTIONS = Set.of(new TransformOptionValue(false, DIRECT_ACCESS_URL));
Set.of(new TransformOptionValue(false, DIRECT_ACCESS_URL));
private static final Set<TransformOption> SOURCE_FILENAME_TRANSFORM_OPTIONS = Set.of(new TransformOptionValue(false, SOURCE_FILENAME));
/** /**
* Returns a new {@link TransformConfig} that includes or excludes the {@code coreVersion} field and * Returns a new {@link TransformConfig} that includes or excludes the {@code coreVersion} field and associated elements like directAccessUrl.
* associated elements like directAccessUrl.
*/ */
public static TransformConfig setOrClearCoreVersion(TransformConfig transformConfig, int configVersion) public static TransformConfig setOrClearCoreVersion(TransformConfig transformConfig, int configVersion)
{ {
@ -78,25 +80,25 @@ public class CoreVersionDecorator
transformConfig = TransformConfig.builder() transformConfig = TransformConfig.builder()
// We may need to create new Transformers as we must not change the original. // We may need to create new Transformers as we must not change the original.
.withTransformers(transformConfig.getTransformers().stream() .withTransformers(transformConfig.getTransformers().stream()
.map(transformer -> { .map(transformer -> {
if (( includeCoreVersion && transformer.getCoreVersion() == null) || if ((includeCoreVersion && transformer.getCoreVersion() == null) ||
(!includeCoreVersion && transformer.getCoreVersion() != null)) (!includeCoreVersion && transformer.getCoreVersion() != null))
{ {
transformer = Transformer.builder() transformer = Transformer.builder()
.withCoreVersion(includeCoreVersion ? transformer.getCoreVersion() : null) .withCoreVersion(includeCoreVersion ? transformer.getCoreVersion() : null)
.withTransformOptions(setOrClearCoreTransformOptions( .withTransformOptions(setOrClearCoreTransformOptions(
includeCoreVersion ? transformer.getCoreVersion() : null, includeCoreVersion ? transformer.getCoreVersion() : null,
transformer.getTransformOptions())) transformer.getTransformOptions()))
// Original values // Original values
.withTransformerName(transformer.getTransformerName()) .withTransformerName(transformer.getTransformerName())
.withTransformerPipeline(transformer.getTransformerPipeline()) .withTransformerPipeline(transformer.getTransformerPipeline())
.withTransformerFailover(transformer.getTransformerFailover()) .withTransformerFailover(transformer.getTransformerFailover())
.withSupportedSourceAndTargetList(transformer.getSupportedSourceAndTargetList()) .withSupportedSourceAndTargetList(transformer.getSupportedSourceAndTargetList())
.build(); .build();
} }
return transformer; return transformer;
}) })
.collect(Collectors.toList())) .collect(Collectors.toList()))
.withTransformOptions(transformOptions) .withTransformOptions(transformOptions)
// Original values // Original values
.withRemoveTransformers(transformConfig.getRemoveTransformers()) .withRemoveTransformers(transformConfig.getRemoveTransformers())
@ -106,6 +108,7 @@ public class CoreVersionDecorator
.withSupportedDefaults(transformConfig.getSupportedDefaults()) .withSupportedDefaults(transformConfig.getSupportedDefaults())
.build(); .build();
addOrRemoveDirectAccessUrlOption(transformConfig.getTransformOptions(), transformConfig.getTransformers()); addOrRemoveDirectAccessUrlOption(transformConfig.getTransformOptions(), transformConfig.getTransformers());
addOrRemoveSourceFileNameOption(transformConfig.getTransformOptions(), transformConfig.getTransformers());
return transformConfig; return transformConfig;
} }
@ -113,19 +116,20 @@ public class CoreVersionDecorator
{ {
List<Transformer> transformers = transformConfig.getTransformers(); List<Transformer> transformers = transformConfig.getTransformers();
transformers.stream() transformers.stream()
.filter(CoreVersionDecorator::isSingleStep) .filter(CoreVersionDecorator::isSingleStep)
.forEach(transformer -> { .forEach(transformer -> {
transformer.setCoreVersion(coreVersion); transformer.setCoreVersion(coreVersion);
transformer.setTransformOptions(setOrClearCoreTransformOptions(coreVersion, transformer.getTransformOptions())); transformer.setTransformOptions(setOrClearCoreTransformOptions(coreVersion, transformer.getTransformOptions()));
}); });
addOrRemoveDirectAccessUrlOption(transformConfig.getTransformOptions(), transformers); addOrRemoveDirectAccessUrlOption(transformConfig.getTransformOptions(), transformers);
addOrRemoveSourceFileNameOption(transformConfig.getTransformOptions(), transformers);
} }
/** /**
* The list of {@code transformers} must not contain forward references * The list of {@code transformers} must not contain forward references
*/ */
public static void setCoreVersionOnMultiStepTransformers(Map<String, Set<TransformOption>> transformOptions, public static void setCoreVersionOnMultiStepTransformers(Map<String, Set<TransformOption>> transformOptions,
List<Transformer> transformers) List<Transformer> transformers)
{ {
Map<String, Transformer> transformersByName = transformers.stream() Map<String, Transformer> transformersByName = transformers.stream()
.collect(Collectors.toMap(Transformer::getTransformerName, Function.identity())); .collect(Collectors.toMap(Transformer::getTransformerName, Function.identity()));
@ -137,8 +141,8 @@ public class CoreVersionDecorator
// Create a list of step transformers // Create a list of step transformers
List<String> namesOfStepTransformers = transformer.getTransformerFailover().isEmpty() List<String> namesOfStepTransformers = transformer.getTransformerFailover().isEmpty()
? transformer.getTransformerPipeline().stream() ? transformer.getTransformerPipeline().stream()
.map(TransformStep::getTransformerName) .map(TransformStep::getTransformerName)
.collect(Collectors.toList()) .collect(Collectors.toList())
: transformer.getTransformerFailover(); : transformer.getTransformerFailover();
// Set the coreVersion to the lowest step transformer value // Set the coreVersion to the lowest step transformer value
@ -149,9 +153,10 @@ public class CoreVersionDecorator
String coreVersion = NO_VERSION.equals(minCoreVersion) ? null : minCoreVersion.toString(); String coreVersion = NO_VERSION.equals(minCoreVersion) ? null : minCoreVersion.toString();
transformer.setCoreVersion(coreVersion); transformer.setCoreVersion(coreVersion);
transformer.setTransformOptions(setOrClearCoreTransformOptions(transformer.getCoreVersion(), transformer.setTransformOptions(setOrClearCoreTransformOptions(transformer.getCoreVersion(),
transformer.getTransformOptions())); transformer.getTransformOptions()));
}); });
addOrRemoveDirectAccessUrlOption(transformOptions, transformers); addOrRemoveDirectAccessUrlOption(transformOptions, transformers);
addOrRemoveSourceFileNameOption(transformOptions, transformers);
} }
private static Set<String> setOrClearCoreTransformOptions(String coreVersion, Set<String> transformerTransformOptions) private static Set<String> setOrClearCoreTransformOptions(String coreVersion, Set<String> transformerTransformOptions)
@ -168,15 +173,24 @@ public class CoreVersionDecorator
{ {
transformerTransformOptions.remove(DIRECT_ACCESS_URL); transformerTransformOptions.remove(DIRECT_ACCESS_URL);
} }
if (CoreFunction.SOURCE_FILENAME.isSupported(coreVersion))
{
// Add SOURCE_FILENAME to a copy of this Transformer's transform options.
transformerTransformOptions.add(SOURCE_FILENAME);
}
else
{
transformerTransformOptions.remove(SOURCE_FILENAME);
}
return transformerTransformOptions; return transformerTransformOptions;
} }
private static void addOrRemoveDirectAccessUrlOption(Map<String, Set<TransformOption>> transformOptions, private static void addOrRemoveDirectAccessUrlOption(Map<String, Set<TransformOption>> transformOptions,
List<Transformer> transformers) List<Transformer> transformers)
{ {
if (transformers.stream() if (transformers.stream()
.anyMatch(transformer -> CoreFunction.DIRECT_ACCESS_URL.isSupported(transformer.getCoreVersion()))) .anyMatch(transformer -> CoreFunction.DIRECT_ACCESS_URL.isSupported(transformer.getCoreVersion())))
{ {
transformOptions.put(DIRECT_ACCESS_URL, DIRECT_ACCESS_URL_TRANSFORM_OPTIONS); transformOptions.put(DIRECT_ACCESS_URL, DIRECT_ACCESS_URL_TRANSFORM_OPTIONS);
} }
@ -186,9 +200,23 @@ public class CoreVersionDecorator
} }
} }
private static void addOrRemoveSourceFileNameOption(Map<String, Set<TransformOption>> transformOptions,
List<Transformer> transformers)
{
if (transformers.stream()
.anyMatch(transformer -> CoreFunction.SOURCE_FILENAME.isSupported(transformer.getCoreVersion())))
{
transformOptions.put(SOURCE_FILENAME, SOURCE_FILENAME_TRANSFORM_OPTIONS);
}
else
{
transformOptions.remove(SOURCE_FILENAME);
}
}
private static boolean isSingleStep(Transformer transformer) private static boolean isSingleStep(Transformer transformer)
{ {
return (transformer.getTransformerFailover() == null || transformer.getTransformerFailover().isEmpty()) && return (transformer.getTransformerFailover() == null || transformer.getTransformerFailover().isEmpty()) &&
(transformer.getTransformerPipeline() == null || transformer.getTransformerPipeline().isEmpty()); (transformer.getTransformerPipeline() == null || transformer.getTransformerPipeline().isEmpty());
} }
} }

View File

@ -2,7 +2,7 @@
* #%L * #%L
* Alfresco Transform Model * Alfresco Transform Model
* %% * %%
* Copyright (C) 2005 - 2022 Alfresco Software Limited * Copyright (C) 2005 - 2025 Alfresco Software Limited
* %% * %%
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as * it under the terms of the GNU Lesser General Public License as
@ -21,10 +21,14 @@
*/ */
package org.alfresco.transform.registry; package org.alfresco.transform.registry;
import org.alfresco.transform.exceptions.TransformException; import static java.util.Collections.emptyList;
import org.alfresco.transform.config.TransformOption; import static java.util.Collections.emptyMap;
import org.alfresco.transform.config.TransformOptionGroup; import static java.util.Collections.emptySet;
import org.alfresco.transform.config.TransformOptionValue; import static java.util.Map.Entry;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.alfresco.transform.common.RequestParamMap.*;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -34,23 +38,19 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import static java.util.Collections.emptyList; import org.alfresco.transform.config.TransformOption;
import static java.util.Collections.emptyMap; import org.alfresco.transform.config.TransformOptionGroup;
import static java.util.Collections.emptySet; import org.alfresco.transform.config.TransformOptionValue;
import static java.util.Map.Entry; import org.alfresco.transform.exceptions.TransformException;
import static org.alfresco.transform.common.RequestParamMap.SOURCE_ENCODING;
import static org.alfresco.transform.common.RequestParamMap.TIMEOUT;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
class TransformRegistryHelper class TransformRegistryHelper
{ {
private TransformRegistryHelper() private TransformRegistryHelper()
{ {}
}
static Set<TransformOption> lookupTransformOptions(final Set<String> transformOptionNames, static Set<TransformOption> lookupTransformOptions(final Set<String> transformOptionNames,
final Map<String, Set<TransformOption>> transformOptions, final String readFrom, final Map<String, Set<TransformOption>> transformOptions, final String readFrom,
final Consumer<String> logError) final Consumer<String> logError)
{ {
if (transformOptionNames == null) if (transformOptionNames == null)
{ {
@ -64,22 +64,20 @@ class TransformRegistryHelper
if (oneSetOfTransformOptions == null) if (oneSetOfTransformOptions == null)
{ {
logError.accept("transformOptions in " + readFrom + " with the name " + name + logError.accept("transformOptions in " + readFrom + " with the name " + name +
" does not exist. Ignored"); " does not exist. Ignored");
continue; continue;
} }
options.add(new TransformOptionGroup(false, oneSetOfTransformOptions)); options.add(new TransformOptionGroup(false, oneSetOfTransformOptions));
} }
return options.size() == 1 ? return options.size() == 1 ? ((TransformOptionGroup) options.iterator().next()).getTransformOptions() : options;
((TransformOptionGroup) options.iterator().next()).getTransformOptions() :
options;
} }
// Returns transformers in increasing supported size order, where lower priority transformers for the same size have // Returns transformers in increasing supported size order, where lower priority transformers for the same size have
// been discarded. // been discarded.
static List<SupportedTransform> retrieveTransformListBySize(final TransformCache data, static List<SupportedTransform> retrieveTransformListBySize(final TransformCache data,
final String sourceMimetype, final String targetMimetype, final String sourceMimetype, final String targetMimetype,
Map<String, String> actualOptions, String renditionName) Map<String, String> actualOptions, String renditionName)
{ {
if (actualOptions == null) if (actualOptions == null)
{ {
@ -91,26 +89,27 @@ class TransformRegistryHelper
} }
final List<SupportedTransform> cachedTransformList = renditionName == null final List<SupportedTransform> cachedTransformList = renditionName == null
? null ? null
: data.retrieveCached(renditionName, sourceMimetype); : data.retrieveCached(renditionName, sourceMimetype);
if (cachedTransformList != null) if (cachedTransformList != null)
{ {
return cachedTransformList; return cachedTransformList;
} }
// The transformOptions sometimes contains sourceEncoding and timeout, even though they should not be used // The transformOptions sometimes contains sourceEncoding / timeout / file name, even though they should not be used
// to select a transformer. Would like to change this, but cannot as we need to support all ACS repo versions. // to select a transformer. Would like to change this, but cannot as we need to support all ACS repo versions.
if (actualOptions.containsKey(SOURCE_ENCODING) || actualOptions.containsKey(TIMEOUT)) if (actualOptions.containsKey(SOURCE_ENCODING) || actualOptions.containsKey(TIMEOUT) || actualOptions.containsKey(SOURCE_FILENAME))
{ {
actualOptions = new HashMap<>(actualOptions); actualOptions = new HashMap<>(actualOptions);
actualOptions.remove(SOURCE_ENCODING); actualOptions.remove(SOURCE_ENCODING);
actualOptions.remove(TIMEOUT); actualOptions.remove(TIMEOUT);
actualOptions.remove(SOURCE_FILENAME);
} }
final List<SupportedTransform> builtTransformList = buildTransformList(data, final List<SupportedTransform> builtTransformList = buildTransformList(data,
sourceMimetype, sourceMimetype,
targetMimetype, targetMimetype,
actualOptions); actualOptions);
if (renditionName != null) if (renditionName != null)
{ {
@ -121,17 +120,17 @@ class TransformRegistryHelper
} }
private static List<SupportedTransform> buildTransformList( private static List<SupportedTransform> buildTransformList(
final TransformCache data, final String sourceMimetype, final String targetMimetype, final TransformCache data, final String sourceMimetype, final String targetMimetype,
final Map<String, String> actualOptions) final Map<String, String> actualOptions)
{ {
if (sourceMimetype == null) if (sourceMimetype == null)
{ {
throw new TransformException(BAD_REQUEST, "Null value provided for sourceMimetype, please provide a value"); throw new TransformException(BAD_REQUEST, "Null value provided for sourceMimetype, please provide a value");
} }
if (targetMimetype == null) if (targetMimetype == null)
{ {
throw new TransformException(BAD_REQUEST, "Null value provided for targetMimetype, please provide a value"); throw new TransformException(BAD_REQUEST, "Null value provided for targetMimetype, please provide a value");
} }
final Map<String, List<SupportedTransform>> targetMap = data.retrieveTransforms(sourceMimetype); final Map<String, List<SupportedTransform>> targetMap = data.retrieveTransforms(sourceMimetype);
@ -141,7 +140,7 @@ class TransformRegistryHelper
for (SupportedTransform supportedTransform : supportedTransformList) for (SupportedTransform supportedTransform : supportedTransformList)
{ {
final Map<String, Boolean> possibleTransformOptions = gatherPossibleTransformOptions( final Map<String, Boolean> possibleTransformOptions = gatherPossibleTransformOptions(
supportedTransform.getTransformOptions(), actualOptions); supportedTransform.getTransformOptions(), actualOptions);
if (optionsMatch(possibleTransformOptions, actualOptions)) if (optionsMatch(possibleTransformOptions, actualOptions))
{ {
@ -154,8 +153,8 @@ class TransformRegistryHelper
// Add newTransform to the transformListBySize in increasing size order and discards // Add newTransform to the transformListBySize in increasing size order and discards
// lower priority (numerically higher) transforms with a smaller or equal size. // lower priority (numerically higher) transforms with a smaller or equal size.
private static void addToSupportedTransformList( private static void addToSupportedTransformList(
final List<SupportedTransform> transformListBySize, final List<SupportedTransform> transformListBySize,
final SupportedTransform newTransform) final SupportedTransform newTransform)
{ {
if (transformListBySize.isEmpty()) if (transformListBySize.isEmpty())
{ {
@ -211,7 +210,7 @@ class TransformRegistryHelper
{ {
if (comparePriority < 0) if (comparePriority < 0)
{ {
if (i+1 < transformListBySize.size()) if (i + 1 < transformListBySize.size())
{ {
// Look at the next element as size is higher but the priority is lower. // Look at the next element as size is higher but the priority is lower.
continue; continue;
@ -248,7 +247,7 @@ class TransformRegistryHelper
// 1) the same priority but support a smaller size // 1) the same priority but support a smaller size
// 2) those with a lower priority and a smaller size // 2) those with a lower priority and a smaller size
if ((comparePriority == 0 && compareMaxSize >= 0) || if ((comparePriority == 0 && compareMaxSize >= 0) ||
(comparePriority > 0 && compareMaxSize >= 0)) (comparePriority > 0 && compareMaxSize >= 0))
{ {
transformListBySize.remove(i); transformListBySize.remove(i);
} }
@ -260,32 +259,29 @@ class TransformRegistryHelper
} }
private static Map<String, Boolean> gatherPossibleTransformOptions( private static Map<String, Boolean> gatherPossibleTransformOptions(
final TransformOptionGroup transformOptionGroup, final Map<String, String> actualOptions) final TransformOptionGroup transformOptionGroup, final Map<String, String> actualOptions)
{ {
final Map<String, Boolean> possibleTransformOptions = new HashMap<>(); final Map<String, Boolean> possibleTransformOptions = new HashMap<>();
addToPossibleTransformOptions(possibleTransformOptions, transformOptionGroup, true, addToPossibleTransformOptions(possibleTransformOptions, transformOptionGroup, true,
actualOptions); actualOptions);
return possibleTransformOptions; return possibleTransformOptions;
} }
/** /**
* Flatten out the transform options by adding them to the supplied possibleTransformOptions.</p> * Flatten out the transform options by adding them to the supplied possibleTransformOptions.
* </p>
* *
* If possible discards options in the supplied transformOptionGroup if the group is optional and the actualOptions * If possible discards options in the supplied transformOptionGroup if the group is optional and the actualOptions don't provide any of the options in the group. Or to put it another way:
* don't provide any of the options in the group. Or to put it another way:<p/> * <p/>
* *
* It adds individual transform options from the transformOptionGroup to possibleTransformOptions if the group is * It adds individual transform options from the transformOptionGroup to possibleTransformOptions if the group is required or if the actualOptions include individual options from the group. As a result it is possible that none of the group are added if it is optional. It is also possible to add individual transform options that are themselves required but not in the actualOptions. In this the optionsMatch method will return false.
* required or if the actualOptions include individual options from the group. As a result it is possible that none
* of the group are added if it is optional. It is also possible to add individual transform options that are
* themselves required but not in the actualOptions. In this the optionsMatch method will return false.
* *
* @return true if any options were added. Used by nested call parents to determine if an option was added from a * @return true if any options were added. Used by nested call parents to determine if an option was added from a nested sub group.
* nested sub group.
*/ */
static boolean addToPossibleTransformOptions( static boolean addToPossibleTransformOptions(
final Map<String, Boolean> possibleTransformOptions, final Map<String, Boolean> possibleTransformOptions,
final TransformOptionGroup transformOptionGroup, final Boolean parentGroupRequired, final TransformOptionGroup transformOptionGroup, final Boolean parentGroupRequired,
final Map<String, String> actualOptions) final Map<String, String> actualOptions)
{ {
boolean added = false; boolean added = false;
boolean required = false; boolean required = false;
@ -302,8 +298,8 @@ class TransformRegistryHelper
if (transformOption instanceof TransformOptionGroup) if (transformOption instanceof TransformOptionGroup)
{ {
added = addToPossibleTransformOptions(possibleTransformOptions, added = addToPossibleTransformOptions(possibleTransformOptions,
(TransformOptionGroup) transformOption, transformOptionGroupRequired, (TransformOptionGroup) transformOption, transformOptionGroupRequired,
actualOptions); actualOptions);
required |= added; required |= added;
} }
else else
@ -340,15 +336,15 @@ class TransformRegistryHelper
} }
static boolean optionsMatch(final Map<String, Boolean> transformOptions, static boolean optionsMatch(final Map<String, Boolean> transformOptions,
final Map<String, String> actualOptions) final Map<String, String> actualOptions)
{ {
// Check all required transformOptions are supplied // Check all required transformOptions are supplied
final boolean supported = transformOptions final boolean supported = transformOptions
.entrySet() .entrySet()
.stream() .stream()
.filter(Entry::getValue)// filter by the required status .filter(Entry::getValue)// filter by the required status
.map(Entry::getKey)// map to the option name .map(Entry::getKey)// map to the option name
.allMatch(actualOptions::containsKey); .allMatch(actualOptions::containsKey);
if (!supported) if (!supported)
{ {
@ -357,8 +353,8 @@ class TransformRegistryHelper
// Check there are no extra unused actualOptions // Check there are no extra unused actualOptions
return actualOptions return actualOptions
.keySet() .keySet()
.stream() .stream()
.allMatch(transformOptions::containsKey); .allMatch(transformOptions::containsKey);
} }
} }

View File

@ -21,8 +21,14 @@
*/ */
package org.alfresco.transform.config; package org.alfresco.transform.config;
import com.google.common.collect.ImmutableList; import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import static org.alfresco.transform.common.RequestParamMap.*;
import static org.alfresco.transform.config.CoreFunction.standardizeCoreVersion;
import static org.alfresco.transform.config.CoreVersionDecorator.CONFIG_VERSION_INCLUDES_CORE_VERSION;
import static org.alfresco.transform.config.CoreVersionDecorator.setCoreVersionOnMultiStepTransformers;
import static org.alfresco.transform.config.CoreVersionDecorator.setCoreVersionOnSingleStepTransformers;
import static org.alfresco.transform.config.CoreVersionDecorator.setOrClearCoreVersion;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -30,14 +36,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import static org.alfresco.transform.common.RequestParamMap.CONFIG_VERSION_DEFAULT; import com.google.common.collect.ImmutableList;
import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL; import org.junit.jupiter.api.Test;
import static org.alfresco.transform.config.CoreFunction.standardizeCoreVersion;
import static org.alfresco.transform.config.CoreVersionDecorator.CONFIG_VERSION_INCLUDES_CORE_VERSION;
import static org.alfresco.transform.config.CoreVersionDecorator.setCoreVersionOnMultiStepTransformers;
import static org.alfresco.transform.config.CoreVersionDecorator.setCoreVersionOnSingleStepTransformers;
import static org.alfresco.transform.config.CoreVersionDecorator.setOrClearCoreVersion;
import static org.junit.jupiter.api.Assertions.*;
class CoreVersionDecoratorTest class CoreVersionDecoratorTest
{ {
@ -47,17 +47,27 @@ class CoreVersionDecoratorTest
public static final Set<TransformOption> SOME_OPTIONS = Set.of(new TransformOptionValue(false, "someOption")); public static final Set<TransformOption> SOME_OPTIONS = Set.of(new TransformOptionValue(false, "someOption"));
private static final Set<TransformOption> DIRECT_ACCESS_URL_OPTION = Set.of(new TransformOptionValue(false, DIRECT_ACCESS_URL)); private static final Set<TransformOption> DIRECT_ACCESS_URL_OPTION = Set.of(new TransformOptionValue(false, DIRECT_ACCESS_URL));
private static final Set<TransformOption> SOURCE_FILENAME_OPTION = Set.of(new TransformOptionValue(false, SOURCE_FILENAME));
private final Map<String, Set<TransformOption>> TRANSFORM_OPTIONS_WITHOUT_DIRECT_ACCESS_URL = new HashMap<>(); private final Map<String, Set<TransformOption>> TRANSFORM_OPTIONS_WITHOUT_DIRECT_ACCESS_URL = new HashMap<>();
private final Map<String, Set<TransformOption>> TRANSFORM_OPTIONS_WITH_DIRECT_ACCESS_URL = new HashMap<>(); private final Map<String, Set<TransformOption>> TRANSFORM_OPTIONS_WITH_DIRECT_ACCESS_URL = new HashMap<>();
private final Map<String, Set<TransformOption>> TRANSFORM_OPTIONS_WITHOUT_SOURCE_FILENAME = new HashMap<>();
private final Map<String, Set<TransformOption>> TRANSFORM_OPTIONS_WITH_SOURCE_FILENAME = new HashMap<>();
{ {
TRANSFORM_OPTIONS_WITHOUT_DIRECT_ACCESS_URL.put(SOME_NAME, SOME_OPTIONS); TRANSFORM_OPTIONS_WITHOUT_DIRECT_ACCESS_URL.put(SOME_NAME, SOME_OPTIONS);
TRANSFORM_OPTIONS_WITH_DIRECT_ACCESS_URL.put(SOME_NAME, SOME_OPTIONS); TRANSFORM_OPTIONS_WITH_DIRECT_ACCESS_URL.put(SOME_NAME, SOME_OPTIONS);
TRANSFORM_OPTIONS_WITH_DIRECT_ACCESS_URL.put(DIRECT_ACCESS_URL, DIRECT_ACCESS_URL_OPTION); TRANSFORM_OPTIONS_WITH_DIRECT_ACCESS_URL.put(DIRECT_ACCESS_URL, DIRECT_ACCESS_URL_OPTION);
TRANSFORM_OPTIONS_WITHOUT_SOURCE_FILENAME.put(SOME_NAME, SOME_OPTIONS);
TRANSFORM_OPTIONS_WITHOUT_SOURCE_FILENAME.put(DIRECT_ACCESS_URL, DIRECT_ACCESS_URL_OPTION);
TRANSFORM_OPTIONS_WITH_SOURCE_FILENAME.put(SOME_NAME, SOME_OPTIONS);
TRANSFORM_OPTIONS_WITH_SOURCE_FILENAME.put(DIRECT_ACCESS_URL, DIRECT_ACCESS_URL_OPTION);
TRANSFORM_OPTIONS_WITH_SOURCE_FILENAME.put(SOURCE_FILENAME, SOURCE_FILENAME_OPTION);
} }
private TransformConfig newTransformConfig(String version1, String version2, String version3, String version4, String version5, private TransformConfig newTransformConfig(String version1, String version2, String version3, String version4, String version5,
boolean hasDirectAccessUrls, boolean multiStepHaveDirectAccessUrls) boolean hasDirectAccessUrls, boolean multiStepHaveDirectAccessUrls)
{ {
HashSet<String> transformOptions1 = new HashSet<>(); HashSet<String> transformOptions1 = new HashSet<>();
HashSet<String> transformOptions2 = new HashSet<>(transformOptions1); HashSet<String> transformOptions2 = new HashSet<>(transformOptions1);
@ -151,7 +161,7 @@ class CoreVersionDecoratorTest
decoratedSingleStepTransformConfig.getTransformers()); decoratedSingleStepTransformConfig.getTransformers());
assertEquals(newTransformConfig("2.1", "2.2", "1.2.3", "2.1", "1.2.3", assertEquals(newTransformConfig("2.1", "2.2", "1.2.3", "2.1", "1.2.3",
false, false), decoratedSingleStepTransformConfig); false, false), decoratedSingleStepTransformConfig);
// Some source T-Engines are pre coreVersion // Some source T-Engines are pre coreVersion
decoratedSingleStepTransformConfig = newTransformConfig( decoratedSingleStepTransformConfig = newTransformConfig(
"2.1", null, null, null, null, "2.1", null, null, null, null,
@ -188,7 +198,7 @@ class CoreVersionDecoratorTest
setOrClearCoreVersion(transformConfigWithCoreVersion, CONFIG_VERSION_INCLUDES_CORE_VERSION)); setOrClearCoreVersion(transformConfigWithCoreVersion, CONFIG_VERSION_INCLUDES_CORE_VERSION));
assertEquals(newTransformConfig("2.1", "2.2", "1.2.3", "2.1", "1.2.3", assertEquals(newTransformConfig("2.1", "2.2", "1.2.3", "2.1", "1.2.3",
false, false), false, false),
setOrClearCoreVersion(transformConfigWithCoreVersion, CONFIG_VERSION_INCLUDES_CORE_VERSION+100)); setOrClearCoreVersion(transformConfigWithCoreVersion, CONFIG_VERSION_INCLUDES_CORE_VERSION + 100));
// Some source T-Engines are pre coreVersion // Some source T-Engines are pre coreVersion
TransformConfig transformConfigWithoutCoreVersion = newTransformConfig( TransformConfig transformConfigWithoutCoreVersion = newTransformConfig(
@ -223,4 +233,106 @@ class CoreVersionDecoratorTest
assertEquals("2", standardizeCoreVersion("2")); assertEquals("2", standardizeCoreVersion("2"));
assertEquals("2.5.7", standardizeCoreVersion("2.5.7-A-SNAPSHOT")); assertEquals("2.5.7", standardizeCoreVersion("2.5.7-A-SNAPSHOT"));
} }
}
private TransformConfig newTransformConfigForSourceFileName(String version1, String version2, String version3, String version4, String version5,
boolean hasSourceFilename, boolean multiStepHaveSourceFilename)
{
HashSet<String> transformOptions1 = new HashSet<>();
HashSet<String> transformOptions2 = new HashSet<>(transformOptions1);
transformOptions2.add(SOME_NAME);
HashSet<String> transformOptions3 = new HashSet<>(transformOptions1);
HashSet<String> transformOptions4 = new HashSet<>(transformOptions1);
transformOptions4.addAll(transformOptions2);
HashSet<String> transformOptions5 = new HashSet<>(transformOptions1);
transformOptions5.addAll(transformOptions2);
transformOptions5.addAll(transformOptions3);
transformOptions1.add(DIRECT_ACCESS_URL);
transformOptions2.add(DIRECT_ACCESS_URL);
transformOptions3.add(DIRECT_ACCESS_URL);
transformOptions4.add(DIRECT_ACCESS_URL);
transformOptions5.add(DIRECT_ACCESS_URL);
if (hasSourceFilename)
{
transformOptions1.add(SOURCE_FILENAME);
transformOptions2.add(SOURCE_FILENAME);
transformOptions3.add(SOURCE_FILENAME);
}
if (multiStepHaveSourceFilename)
{
transformOptions4.add(SOURCE_FILENAME);
transformOptions5.add(SOURCE_FILENAME);
}
return TransformConfig.builder()
.withTransformOptions(hasSourceFilename ? TRANSFORM_OPTIONS_WITH_SOURCE_FILENAME : TRANSFORM_OPTIONS_WITHOUT_SOURCE_FILENAME)
.withTransformers(ImmutableList.of(
Transformer.builder()
.withTransformerName("transformer1")
.withCoreVersion(version1)
.withTransformOptions(transformOptions1)
.build(),
Transformer.builder()
.withTransformerName("transformer2")
.withCoreVersion(version2)
.withTransformOptions(transformOptions2)
.build(),
Transformer.builder()
.withTransformerName("transformer3")
.withCoreVersion(version3)
.withTransformOptions(transformOptions3)
.build(),
Transformer.builder()
.withTransformerName("pipeline4")
.withCoreVersion(version4)
.withTransformerPipeline(List.of(
new TransformStep("transformer1", "mimetype/c"),
new TransformStep("transformer2", null)))
.withTransformOptions(transformOptions4)
.build(),
Transformer.builder()
.withTransformerName("failover5")
.withCoreVersion(version5)
.withTransformerFailover(List.of("transformer1", "transformer2", "transformer3"))
.withTransformOptions(transformOptions5)
.build()))
.build();
}
@Test
void setCoreVersionWithSourceFileNameOptionTest()
{
String sourceFileName = SOURCE_FILENAME;
// Create transform config with no SOURCE_FILENAME option
String sourceFileNameSupport = "5.1.8";
TransformConfig transformConfig = newTransformConfigForSourceFileName(
sourceFileNameSupport, sourceFileNameSupport, sourceFileNameSupport,
sourceFileNameSupport, sourceFileNameSupport,
false, false);
// Add SOURCE_FILENAME to all single step transformers
setCoreVersionOnSingleStepTransformers(transformConfig, sourceFileNameSupport);
// Check that SOURCE_FILENAME is present in all single step transformers' options
assertEquals(newTransformConfigForSourceFileName("4.0.0", "4.0.0", "4.0.0", "4.0.0", "4.0.0",
false, false),
setOrClearCoreVersion(
newTransformConfigForSourceFileName("4.0.0", "4.0.0", "4.0.0", "4.0.0", "4.0.0",
false, false),
CONFIG_VERSION_INCLUDES_CORE_VERSION));
// Supported version: SOURCE_FILENAME should be present
assertEquals(newTransformConfigForSourceFileName(sourceFileNameSupport, sourceFileNameSupport, sourceFileNameSupport, sourceFileNameSupport, sourceFileNameSupport,
true, true),
setOrClearCoreVersion(
newTransformConfigForSourceFileName(sourceFileNameSupport, sourceFileNameSupport, sourceFileNameSupport, sourceFileNameSupport, sourceFileNameSupport, true, true),
CONFIG_VERSION_INCLUDES_CORE_VERSION));
}
}