Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ public void onTrigger(final ProcessContext context, final ProcessSession session
session.getProvenanceReporter().modifyContent(transformed, stopWatch.getElapsed(TimeUnit.MILLISECONDS));
getLogger().info("Transformation Completed {}", original);
} catch (final ProcessException e) {
getLogger().error("Transformation Failed", original, e);
getLogger().error("Transformation Failed {}", original, e);
session.transfer(original, REL_FAILURE);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,52 @@
*/
package org.apache.nifi.processors.standard;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.nifi.lookup.SimpleKeyValueLookupService;
import org.apache.nifi.processor.Relationship;
import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.util.LogMessage;
import org.apache.nifi.util.MockComponentLog;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.PropertyMigrationResult;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class TestTransformXml {
private static final String XML_FOR_TESTING_PARAMETERS = """
<?xml version="1.0" encoding="UTF-8"?>
<data>
<item>Some data</item>
</data>
""";

private TestRunner runner;

@TempDir
private Path temptDir;

@BeforeEach
void setUp() {
runner = TestRunners.newTestRunner(TransformXml.class);
Expand Down Expand Up @@ -353,6 +373,202 @@ public void testMessageNonTerminate() throws IOException {
assertTrue(logger.getWarnMessages().isEmpty());
}

@Test
void testParameterDeclaredAndSet() throws IOException {
final String xslt = """
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="customParam" select="'From XSLT'" />
<xsl:template match="/">
<root>
<message>
Value Selected: <xsl:value-of select="$customParam"/>
</message>
</root>
</xsl:template>
</xsl:stylesheet>
""";

final Path xsltPath = writeXslt(xslt, "someTransform.xslt");
runner.setProperty(TransformXml.XSLT_FILE_NAME, xsltPath.toString());
runner.setProperty("customParam", "From NIFI");
runner.enqueue(XML_FOR_TESTING_PARAMETERS);

runner.run();
runner.assertAllFlowFilesTransferred(TransformXml.REL_SUCCESS);
final MockFlowFile transformed = runner.getFlowFilesForRelationship(TransformXml.REL_SUCCESS).getFirst();
final String expectedTransform = """
<?xml version="1.0" encoding="UTF-8"?>
<root>
<message>
Value Selected: From NIFI</message>
</root>
""";
transformed.assertContentEquals(expectedTransform);
}

@Test
void testParameterSetButNotDeclared() throws IOException {
final String xslt = """
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<root>
<message>
Value Selected:
</message>
</root>
</xsl:template>
</xsl:stylesheet>
""";

final Path xsltPath = writeXslt(xslt, "someTransform.xslt");
runner.setProperty(TransformXml.XSLT_FILE_NAME, xsltPath.toString());
runner.setProperty("customParam", "From NIFI");
runner.enqueue(XML_FOR_TESTING_PARAMETERS);

runner.run();
runner.assertAllFlowFilesTransferred(TransformXml.REL_SUCCESS);
final MockFlowFile transformed = runner.getFlowFilesForRelationship(TransformXml.REL_SUCCESS).getFirst();
final String expectedTransform =
"""
<?xml version="1.0" encoding="UTF-8"?>
<root>
<message>
Value Selected:
</message>
</root>
""";
transformed.assertContentEquals(expectedTransform);
}

@Test
void testParameterNotDeclaredButUsedInXslt() throws IOException {
final String xslt = """
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<root>
<message>
Value Selected: <xsl:value-of select="$customParam"/>
</message>
</root>
</xsl:template>
</xsl:stylesheet>
""";

final Path xsltPath = writeXslt(xslt, "someTransform.xslt");
runner.setProperty(TransformXml.XSLT_FILE_NAME, xsltPath.toString());
runner.setProperty("customParam", "From NIFI");
runner.enqueue(XML_FOR_TESTING_PARAMETERS);

runner.run();

runner.assertAllFlowFilesTransferred(TransformXml.REL_FAILURE);
final MockComponentLog logger = runner.getLogger();
final List<LogMessage> errorMessages = logger.getErrorMessages();
assertTrue(errorMessages.getFirst().getMsg().contains("Variable $customParam has not been declared"));
}

@ParameterizedTest
@MethodSource("parameterAsSpecificTypeArgs")
void testParameterAsSpecificType(String paramType, String parameterValue, String defaultValue, Relationship expectedRelationship, String expectedTransform) throws IOException {
final String parameterName = "customParam";
final String xslt = getXSLTWithParameterDefinedWithType(paramType, defaultValue);
final Path xsltPath = writeXslt(xslt, "someTransform.xslt");
runner.setProperty(TransformXml.XSLT_FILE_NAME, xsltPath.toString());
runner.setProperty(parameterName, parameterValue);
runner.enqueue(XML_FOR_TESTING_PARAMETERS);

runner.run();
runner.assertAllFlowFilesTransferred(expectedRelationship);

if (expectedTransform != null) {
final MockFlowFile transformed = runner.getFlowFilesForRelationship(expectedRelationship).getFirst();
transformed.assertContentEquals(expectedTransform);
}

if (expectedRelationship == TransformXml.REL_FAILURE) {
assertTrue(runner.getLogger().getErrorMessages().stream()
.map(LogMessage::getThrowable)
.map(ExceptionUtils::getStackTrace)
.anyMatch(stackTrace -> stackTrace.contains("ValidationException")));
}
}

private static Stream<Arguments> parameterAsSpecificTypeArgs() {
return Stream.of(
Arguments.argumentSet("Valid number", "xs:integer", "100", "0", TransformXml.REL_SUCCESS,
"""
<?xml version="1.0" encoding="UTF-8"?>
<report xmlns:xs="http://www.w3.org/2001/XMLSchema">
<message>Param of type xs:integer value is 100</message>
</report>
"""),
Arguments.argumentSet("Invalid number", "xs:integer", "NIFI", "0", TransformXml.REL_FAILURE, null),
Arguments.argumentSet("Valid boolean lowercase", "xs:boolean", "true", "false", TransformXml.REL_SUCCESS,
"""
<?xml version="1.0" encoding="UTF-8"?>
<report xmlns:xs="http://www.w3.org/2001/XMLSchema">
<message>Param of type xs:boolean value is true</message>
</report>
"""),
Arguments.argumentSet("Invalid boolean uppercase", "xs:boolean", "TRUE", "false", TransformXml.REL_FAILURE, null),
Arguments.argumentSet("Valid ISO 8601 date", "xs:date", "2026-01-01", "xs:date('1970-01-01')", TransformXml.REL_SUCCESS,
"""
<?xml version="1.0" encoding="UTF-8"?>
<report xmlns:xs="http://www.w3.org/2001/XMLSchema">
<message>Param of type xs:date value is 2026-01-01</message>
</report>
""", null), //29 April 2003
Arguments.argumentSet("Invalid ISO 8601 date", "xs:date", "1 January 2026", "xs:date('1970-01-01')", TransformXml.REL_FAILURE, null),
Arguments.argumentSet("Valid ISO 8601 date time", "xs:dateTime", "2026-01-01T00:00:00", "xs:dateTime('1970-01-01T00:00:00')", TransformXml.REL_SUCCESS,
"""
<?xml version="1.0" encoding="UTF-8"?>
<report xmlns:xs="http://www.w3.org/2001/XMLSchema">
<message>Param of type xs:dateTime value is 2026-01-01T00:00:00</message>
</report>
"""),
Arguments.argumentSet("Invalid ISO 8601 date time", "xs:dateTime", "1970-01-01 00:00:00", "xs:dateTime('1970-01-01T00:00:00')", TransformXml.REL_FAILURE, null),
Arguments.argumentSet("Valid time without timezone/offset", "xs:time", "12:34:56.789", "current-time()", TransformXml.REL_SUCCESS,
"""
<?xml version="1.0" encoding="UTF-8"?>
<report xmlns:xs="http://www.w3.org/2001/XMLSchema">
<message>Param of type xs:time value is 12:34:56.789</message>
</report>
"""),
Arguments.argumentSet("Valid UTC time", "xs:time", "12:34:56Z", "current-time()", TransformXml.REL_SUCCESS,
"""
<?xml version="1.0" encoding="UTF-8"?>
<report xmlns:xs="http://www.w3.org/2001/XMLSchema">
<message>Param of type xs:time value is 12:34:56Z</message>
</report>
"""),
Arguments.argumentSet("Valid Offset from UTC time", "xs:time", "12:34:56-05:00", "current-time()", TransformXml.REL_SUCCESS,
"""
<?xml version="1.0" encoding="UTF-8"?>
<report xmlns:xs="http://www.w3.org/2001/XMLSchema">
<message>Param of type xs:time value is 12:34:56-05:00</message>
</report>
""")

);
}

private String getXSLTWithParameterDefinedWithType(String type, String defaultValue) {
return """
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:param name="customParam" as="%1$s" select="%2$s"/>
<xsl:template match="/">
<report>
<message>Param of type %1$s value is <xsl:value-of select="$customParam"/></message>
</report>
</xsl:template>
</xsl:stylesheet>
""".formatted(type, defaultValue);
}

@Test
void testMigrateProperties() {
final Map<String, String> expectedRenamed = Map.ofEntries(
Expand All @@ -369,4 +585,11 @@ void testMigrateProperties() {
final PropertyMigrationResult propertyMigrationResult = runner.migrateProperties();
assertEquals(expectedRenamed, propertyMigrationResult.getPropertiesRenamed());
}

private Path writeXslt(String xslt, String xsltName) throws IOException {
final Path xsltPath = temptDir.resolve(xsltName);
Files.writeString(xsltPath, xslt);

return xsltPath;
}
}
18 changes: 13 additions & 5 deletions nifi-mock/src/main/java/org/apache/nifi/util/CapturingLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ public void trace(String format, Object arg1, Object arg2) {

@Override
public void trace(String format, Object... arguments) {
traceMessages.add(new LogMessage(null, format, null, arguments));
final Throwable throwable = lastArgIsException(arguments) ? (Throwable) arguments[arguments.length - 1] : null;
traceMessages.add(new LogMessage(null, format, throwable, arguments));
logger.trace(format, arguments);
}

Expand Down Expand Up @@ -161,7 +162,8 @@ public void debug(String format, Object arg1, Object arg2) {

@Override
public void debug(String format, Object... arguments) {
debugMessages.add(new LogMessage(null, format, null, arguments));
final Throwable throwable = lastArgIsException(arguments) ? (Throwable) arguments[arguments.length - 1] : null;
debugMessages.add(new LogMessage(null, format, throwable, arguments));
logger.debug(format, arguments);
}

Expand Down Expand Up @@ -232,8 +234,9 @@ public void info(String format, Object arg1, Object arg2) {

@Override
public void info(String format, Object... arguments) {
final Throwable throwable = lastArgIsException(arguments) ? (Throwable) arguments[arguments.length - 1] : null;
String message = MessageFormatter.arrayFormat(format, arguments).getMessage();
infoMessages.add(new LogMessage(null, message, null, arguments));
infoMessages.add(new LogMessage(null, message, throwable, arguments));
logger.info(format, arguments);
}

Expand Down Expand Up @@ -303,8 +306,9 @@ public void warn(String format, Object arg1, Object arg2) {

@Override
public void warn(String format, Object... arguments) {
final Throwable throwable = lastArgIsException(arguments) ? (Throwable) arguments[arguments.length - 1] : null;
String message = MessageFormatter.arrayFormat(format, arguments).getMessage();
warnMessages.add(new LogMessage(null, message, null, arguments));
warnMessages.add(new LogMessage(null, message, throwable, arguments));
logger.warn(format, arguments);
}

Expand Down Expand Up @@ -384,8 +388,9 @@ public void error(String format, Object arg1, Throwable t) {

@Override
public void error(String format, Object... arguments) {
final Throwable throwable = lastArgIsException(arguments) ? (Throwable) arguments[arguments.length - 1] : null;
final String message = MessageFormatter.arrayFormat(format, arguments).getMessage();
errorMessages.add(new LogMessage(null, message, null, arguments));
errorMessages.add(new LogMessage(null, message, throwable, arguments));
logger.error(format, arguments);
}

Expand Down Expand Up @@ -434,4 +439,7 @@ public void error(Marker marker, String msg, Throwable t) {
logger.error(marker, msg, t);
}

private boolean lastArgIsException(final Object[] os) {
return (os != null && os.length > 0 && (os[os.length - 1] instanceof Throwable));
}
}
Loading