This is an automated email from the ASF dual-hosted git repository.
hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git
The following commit(s) were added to refs/heads/main by this push:
new 05a1e4feb9 Issue #8724 : Keep the input value of a replaced field the
JavaScript… (#8823)
05a1e4feb9 is described below
commit 05a1e4feb98a260be617fe2939c1ca104de061ab
Author: Bart Maertens <[email protected]>
AuthorDate: Tue Oct 13 13:00:01 2026 +0200
Issue #8724 : Keep the input value of a replaced field the JavaScript…
(#8823)
* Issue #8724 : Keep the input value of a replaced field the JavaScript
transform does not set
A replaced field the script never assigned came out null, unless its name
appeared in the script text, comments included. It now keeps its input value,
converted to the field's type, also when Rename to points at the input field.
* Issue #8724 : Keep the input value of a replaced field the script only
mentions
A replaced field named in a comment, or only read, is bound as a JavaScript
wrapper. Converting it back lost BigNumber and large Integer precision, put a
NativeBoolean in the row and rounded differently. A field still holding the
bound object after the script ran now keeps its input value.
---
.../0087-javascript-replaced-field-8724.hpl | 255 +++++++++++++++++++++
.../golden-javascript-replaced-field-8724.csv | 4 +
.../transforms/main-0087-javascript.hwf | 3 +
.../golden-javascript-replaced-field-8724.json | 72 ++++++
.../0087-javascript-replaced-field-8724 UNIT.json | 56 +++++
.../transforms/javascript/ScriptValues.java | 34 +++
.../transforms/javascript/ScriptValuesData.java | 6 +
.../transforms/javascript/ScriptValuesTest.java | 172 ++++++++++++++
8 files changed, 602 insertions(+)
diff --git
a/integration-tests/transforms/0087-javascript-replaced-field-8724.hpl
b/integration-tests/transforms/0087-javascript-replaced-field-8724.hpl
new file mode 100644
index 0000000000..a63f6ac731
--- /dev/null
+++ b/integration-tests/transforms/0087-javascript-replaced-field-8724.hpl
@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements. See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-->
+<pipeline>
+ <info>
+ <name>0087-javascript-replaced-field-8724</name>
+ <name_sync_with_filename>Y</name_sync_with_filename>
+ <description>#8724 : a replaced field the script does not assign keeps its
input value</description>
+ <extended_description>test_int, big_num, flag and half are only mentioned
in a comment, plain_int not at all, new_value replaces other_int through Rename
to.</extended_description>
+ <pipeline_version/>
+ <pipeline_type>Normal</pipeline_type>
+ <parameters>
+ </parameters>
+ <capture_transform_performance>N</capture_transform_performance>
+
<transform_performance_capturing_delay>1000</transform_performance_capturing_delay>
+
<transform_performance_capturing_size_limit>100</transform_performance_capturing_size_limit>
+ <created_user>-</created_user>
+ <created_date>2026/10/09 00:00:00.000</created_date>
+ <modified_user>-</modified_user>
+ <modified_date>2026/10/12 00:00:00.000</modified_date>
+ </info>
+ <notepads>
+ </notepads>
+ <order>
+ <hop>
+ <from>Input</from>
+ <to>JavaScript</to>
+ <enabled>Y</enabled>
+ </hop>
+ <hop>
+ <from>JavaScript</from>
+ <to>validate</to>
+ <enabled>Y</enabled>
+ </hop>
+ </order>
+ <transform>
+ <name>Input</name>
+ <type>DataGrid</type>
+ <description/>
+ <distribute>Y</distribute>
+ <custom_distribution/>
+ <copies>1</copies>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <data>
+ <line>
+ <item>1</item>
+ <item>10</item>
+ <item>40</item>
+ <item>100</item>
+ <item>12345678901234567890.123456789</item>
+ <item>Y</item>
+ <item>10.5</item>
+ </line>
+ <line>
+ <item>2</item>
+ <item>20</item>
+ <item>50</item>
+ <item>200</item>
+ <item>22345678901234567890.123456789</item>
+ <item>N</item>
+ <item>20.5</item>
+ </line>
+ <line>
+ <item>3</item>
+ <item>30</item>
+ <item>60</item>
+ <item>300</item>
+ <item>32345678901234567890.123456789</item>
+ <item>Y</item>
+ <item>30.5</item>
+ </line>
+ </data>
+ <fields>
+ <field>
+ <length>-1</length>
+ <precision>-1</precision>
+ <set_empty_string>N</set_empty_string>
+ <name>id</name>
+ <type>Integer</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <precision>-1</precision>
+ <set_empty_string>N</set_empty_string>
+ <name>test_int</name>
+ <type>Integer</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <precision>-1</precision>
+ <set_empty_string>N</set_empty_string>
+ <name>plain_int</name>
+ <type>Integer</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <precision>-1</precision>
+ <set_empty_string>N</set_empty_string>
+ <name>other_int</name>
+ <type>Integer</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <precision>-1</precision>
+ <set_empty_string>N</set_empty_string>
+ <name>big_num</name>
+ <type>BigNumber</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <precision>-1</precision>
+ <set_empty_string>N</set_empty_string>
+ <name>flag</name>
+ <type>Boolean</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <precision>-1</precision>
+ <set_empty_string>N</set_empty_string>
+ <name>half</name>
+ <type>Number</type>
+ </field>
+ </fields>
+ <attributes/>
+ <GUI>
+ <xloc>96</xloc>
+ <yloc>80</yloc>
+ </GUI>
+ </transform>
+ <transform>
+ <name>JavaScript</name>
+ <type>ScriptValueMod</type>
+ <description/>
+ <distribute>Y</distribute>
+ <custom_distribution/>
+ <copies>1</copies>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <fields>
+ <field>
+ <length>-1</length>
+ <name>test_int</name>
+ <precision>-1</precision>
+ <rename>test_int</rename>
+ <replace>Y</replace>
+ <type>Integer</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <name>plain_int</name>
+ <precision>-1</precision>
+ <rename>plain_int</rename>
+ <replace>Y</replace>
+ <type>Integer</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <name>new_value</name>
+ <precision>-1</precision>
+ <rename>other_int</rename>
+ <replace>Y</replace>
+ <type>Integer</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <name>big_num</name>
+ <precision>-1</precision>
+ <rename>big_num</rename>
+ <replace>Y</replace>
+ <type>BigNumber</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <name>flag</name>
+ <precision>-1</precision>
+ <rename>flag</rename>
+ <replace>Y</replace>
+ <type>Boolean</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <name>half</name>
+ <precision>-1</precision>
+ <rename>half</rename>
+ <replace>Y</replace>
+ <type>Integer</type>
+ </field>
+ <field>
+ <length>-1</length>
+ <name>doubled</name>
+ <precision>-1</precision>
+ <rename>doubled</rename>
+ <replace>N</replace>
+ <type>Integer</type>
+ </field>
+ </fields>
+ <jsScripts>
+ <jsScript>
+ <jsScript_name>transformScript</jsScript_name>
+ <jsScript_script>
+// Only mentioned here, never assigned: test_int, big_num, flag, half
+var doubled = id * 2;
+</jsScript_script>
+ <jsScript_type>0</jsScript_type>
+ </jsScript>
+ </jsScripts>
+ <optimizationLevel>9</optimizationLevel>
+ <attributes/>
+ <GUI>
+ <xloc>288</xloc>
+ <yloc>80</yloc>
+ </GUI>
+ </transform>
+ <transform>
+ <name>validate</name>
+ <type>Dummy</type>
+ <description/>
+ <distribute>Y</distribute>
+ <custom_distribution/>
+ <copies>1</copies>
+ <partitioning>
+ <method>none</method>
+ <schema_name/>
+ </partitioning>
+ <attributes/>
+ <GUI>
+ <xloc>480</xloc>
+ <yloc>80</yloc>
+ </GUI>
+ </transform>
+ <transform_error_handling>
+ </transform_error_handling>
+ <attributes/>
+</pipeline>
diff --git
a/integration-tests/transforms/datasets/golden-javascript-replaced-field-8724.csv
b/integration-tests/transforms/datasets/golden-javascript-replaced-field-8724.csv
new file mode 100644
index 0000000000..87057f29ee
--- /dev/null
+++
b/integration-tests/transforms/datasets/golden-javascript-replaced-field-8724.csv
@@ -0,0 +1,4 @@
+id,test_int,plain_int,other_int,big_num,flag,half,doubled
+1,10,40,100,12345678901234567890.123456789,true,11,2
+2,20,50,200,22345678901234567890.123456789,false,21,4
+3,30,60,300,32345678901234567890.123456789,true,31,6
diff --git a/integration-tests/transforms/main-0087-javascript.hwf
b/integration-tests/transforms/main-0087-javascript.hwf
index 371fa0fc0a..25ff0c6422 100644
--- a/integration-tests/transforms/main-0087-javascript.hwf
+++ b/integration-tests/transforms/main-0087-javascript.hwf
@@ -61,6 +61,9 @@ limitations under the License.
<test_name>
<name>0087-javascript-loadscript-from-tab UNIT</name>
</test_name>
+ <test_name>
+ <name>0087-javascript-replaced-field-8724 UNIT</name>
+ </test_name>
</test_names>
<parallel>N</parallel>
<xloc>208</xloc>
diff --git
a/integration-tests/transforms/metadata/dataset/golden-javascript-replaced-field-8724.json
b/integration-tests/transforms/metadata/dataset/golden-javascript-replaced-field-8724.json
new file mode 100644
index 0000000000..8a25b37fcc
--- /dev/null
+++
b/integration-tests/transforms/metadata/dataset/golden-javascript-replaced-field-8724.json
@@ -0,0 +1,72 @@
+{
+ "base_filename": "golden-javascript-replaced-field-8724.csv",
+ "name": "golden-javascript-replaced-field-8724",
+ "description": "",
+ "dataset_fields": [
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 5,
+ "field_precision": 0,
+ "field_name": "id",
+ "field_format": "####0;-####0"
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 5,
+ "field_precision": 0,
+ "field_name": "test_int",
+ "field_format": "####0;-####0"
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 5,
+ "field_precision": 0,
+ "field_name": "plain_int",
+ "field_format": "####0;-####0"
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 5,
+ "field_precision": 0,
+ "field_name": "other_int",
+ "field_format": "####0;-####0"
+ },
+ {
+ "field_comment": "",
+ "field_length": 30,
+ "field_type": 6,
+ "field_precision": 9,
+ "field_name": "big_num",
+ "field_format": ""
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 4,
+ "field_precision": 0,
+ "field_name": "flag",
+ "field_format": ""
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 5,
+ "field_precision": 0,
+ "field_name": "half",
+ "field_format": "####0;-####0"
+ },
+ {
+ "field_comment": "",
+ "field_length": -1,
+ "field_type": 5,
+ "field_precision": 0,
+ "field_name": "doubled",
+ "field_format": "####0;-####0"
+ }
+ ],
+ "folder_name": ""
+}
diff --git
a/integration-tests/transforms/metadata/unit-test/0087-javascript-replaced-field-8724
UNIT.json
b/integration-tests/transforms/metadata/unit-test/0087-javascript-replaced-field-8724
UNIT.json
new file mode 100644
index 0000000000..69fc03b44b
--- /dev/null
+++
b/integration-tests/transforms/metadata/unit-test/0087-javascript-replaced-field-8724
UNIT.json
@@ -0,0 +1,56 @@
+{
+ "database_replacements": [],
+ "autoOpening": true,
+ "description": "#8724 : replaced fields the script does not assign keep
their input value",
+ "persist_filename": "",
+ "test_type": "UNIT_TEST",
+ "variableValues": [],
+ "basePath": "${HOP_UNIT_TESTS_FOLDER}",
+ "golden_data_sets": [
+ {
+ "field_mappings": [
+ {
+ "transform_field": "id",
+ "data_set_field": "id"
+ },
+ {
+ "transform_field": "test_int",
+ "data_set_field": "test_int"
+ },
+ {
+ "transform_field": "plain_int",
+ "data_set_field": "plain_int"
+ },
+ {
+ "transform_field": "other_int",
+ "data_set_field": "other_int"
+ },
+ {
+ "transform_field": "big_num",
+ "data_set_field": "big_num"
+ },
+ {
+ "transform_field": "flag",
+ "data_set_field": "flag"
+ },
+ {
+ "transform_field": "half",
+ "data_set_field": "half"
+ },
+ {
+ "transform_field": "doubled",
+ "data_set_field": "doubled"
+ }
+ ],
+ "field_order": [
+ "id"
+ ],
+ "data_set_name": "golden-javascript-replaced-field-8724",
+ "transform_name": "validate"
+ }
+ ],
+ "input_data_sets": [],
+ "name": "0087-javascript-replaced-field-8724 UNIT",
+ "trans_test_tweaks": [],
+ "pipeline_filename": "./0087-javascript-replaced-field-8724.hpl"
+}
diff --git
a/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValues.java
b/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValues.java
index f658538ac7..8693675743 100644
---
a/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValues.java
+++
b/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValues.java
@@ -18,6 +18,7 @@
package org.apache.hop.pipeline.transforms.javascript;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
@@ -303,6 +304,19 @@ public class ScriptValues extends
BaseTransform<ScriptValuesMeta, ScriptValuesDa
for (int i = 0; i < meta.getScriptFields().size(); i++) {
ScriptValuesMeta.ScriptField field = meta.getScriptFields().get(i);
Object result = data.scope.get(field.getName(), data.scope);
+ if (data.replaceIndex[i] >= 0 && isNotAssigned(rowMeta, field, i,
result)) {
+ // The script never assigned the field it replaces: keep the input
value. Converting
+ // the bound JavaScript wrapper back would lose precision
(BigNumber, large Integer),
+ // leak a NativeBoolean, and round differently than a field that
was never bound.
+ //
+ int index = data.replaceIndex[i];
+ IValueMeta inputValueMeta = rowMeta.getValueMeta(index);
+ IValueMeta outputValueMeta =
data.outputRowMeta.getValueMeta(index);
+ if (inputValueMeta.getType() != outputValueMeta.getType()) {
+ outputRow[index] = outputValueMeta.convertData(inputValueMeta,
outputRow[index]);
+ }
+ continue;
+ }
Object valueData = getValueFromJScript(result, i);
if (data.replaceIndex[i] < 0) {
outputRow[outputIndex++] = valueData;
@@ -374,7 +388,26 @@ public class ScriptValues extends
BaseTransform<ScriptValuesMeta, ScriptValuesDa
}
}
+ /**
+ * A replaced field was not assigned by the script when the scope holds
nothing under its name, or
+ * still holds the exact object addFieldsToScope() bound for it. Any
assignment, also of the same
+ * value or of null, replaces that object.
+ */
+ private boolean isNotAssigned(
+ IRowMeta rowMeta, ScriptValuesMeta.ScriptField field, int i, Object
result) {
+ if (result == Scriptable.NOT_FOUND) {
+ return true;
+ }
+ int index = data.replaceIndex[i];
+ return field.getName().equals(rowMeta.getValueMeta(index).getName())
+ && result == data.boundValues[index];
+ }
+
private void addFieldsToScope(IRowMeta rowMeta, Object[] row) throws
HopValueException {
+ if (data.boundValues == null) {
+ data.boundValues = new Object[rowMeta.size()];
+ Arrays.fill(data.boundValues, Scriptable.NOT_FOUND);
+ }
for (int i = 0; i < data.fieldsUsed.length; i++) {
IValueMeta valueMeta = rowMeta.getValueMeta(data.fieldsUsed[i]);
Object valueData = row[data.fieldsUsed[i]];
@@ -387,6 +420,7 @@ public class ScriptValues extends
BaseTransform<ScriptValuesMeta, ScriptValuesDa
jsarg = null;
}
data.scope.put(valueMeta.getName(), data.scope, jsarg);
+ data.boundValues[data.fieldsUsed[i]] = jsarg;
}
}
diff --git
a/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesData.java
b/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesData.java
index bff778fe43..e8f46c41ae 100644
---
a/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesData.java
+++
b/plugins/transforms/javascript/src/main/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesData.java
@@ -32,6 +32,12 @@ public class ScriptValuesData extends BaseTransformData
implements ITransformDat
public int[] fieldsUsed;
+ /**
+ * The object bound into the scope for each input field of the current row,
or NOT_FOUND when the
+ * field is not bound. A replaced field still holding it after the script
ran was not assigned.
+ */
+ public Object[] boundValues;
+
public IRowMeta outputRowMeta;
public int[] replaceIndex;
diff --git
a/plugins/transforms/javascript/src/test/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesTest.java
b/plugins/transforms/javascript/src/test/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesTest.java
index 263ef33e29..c8605c62ad 100644
---
a/plugins/transforms/javascript/src/test/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesTest.java
+++
b/plugins/transforms/javascript/src/test/java/org/apache/hop/pipeline/transforms/javascript/ScriptValuesTest.java
@@ -17,6 +17,7 @@
package org.apache.hop.pipeline.transforms.javascript;
+import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -28,11 +29,16 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
import org.apache.hop.core.HopEnvironment;
import org.apache.hop.core.logging.ILoggingObject;
import org.apache.hop.core.row.IValueMeta;
import org.apache.hop.core.row.RowMeta;
import org.apache.hop.core.row.value.ValueMetaBigNumber;
+import org.apache.hop.core.row.value.ValueMetaBoolean;
+import org.apache.hop.core.row.value.ValueMetaInteger;
+import org.apache.hop.core.row.value.ValueMetaNumber;
import org.apache.hop.core.row.value.ValueMetaString;
import org.apache.hop.junit.rules.RestoreHopEngineEnvironmentExtension;
import org.apache.hop.pipeline.PipelineTestingUtil;
@@ -40,6 +46,7 @@ import
org.apache.hop.pipeline.transforms.mock.TransformMockHelper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.function.Executable;
class ScriptValuesTest {
@RegisterExtension
@@ -236,6 +243,171 @@ class ScriptValuesTest {
assertEquals(0, transform.getErrors());
}
+ /**
+ * A replaced field the script does not assign keeps its input value. It
used to depend on whether
+ * the field name appeared in the script text, so adding or removing a
comment changed the output.
+ */
+ @Test
+ void replacedFieldNotSetByScriptKeepsInputValue() throws Exception {
+ for (String script : new String[] {"var other = 0;", "//var test_int =
0;\nvar other = 0;"}) {
+ RowMeta input = new RowMeta();
+ input.addValueMeta(new ValueMetaString("test_int"));
+
+ ScriptValuesMeta meta = metaWith(ScriptValuesScript.TRANSFORM_SCRIPT,
script, null);
+ ScriptValuesMeta.ScriptField field = new ScriptValuesMeta.ScriptField();
+ field.setName("test_int");
+ field.setType(IValueMeta.TYPE_STRING);
+ field.setReplace(true);
+ meta.getScriptFields().add(field);
+
+ ScriptValues transform = newTransform(meta, input);
+ doReturn(new Object[] {"1"}).when(transform).getRow();
+ assertTrue(transform.init());
+
+ Object[] row = PipelineTestingUtil.execute(transform, 1, false).get(0);
+ assertEquals("1", row[0], script);
+ assertEquals(0, transform.getErrors());
+ }
+ }
+
+ /** The same holds when "Rename to" points at the replaced input field. */
+ @Test
+ void renamedReplacedFieldNotSetByScriptKeepsInputValue() throws Exception {
+ RowMeta input = new RowMeta();
+ input.addValueMeta(new ValueMetaString("test_int"));
+
+ ScriptValuesMeta meta = metaWith(ScriptValuesScript.TRANSFORM_SCRIPT, "var
other = 0;", null);
+ ScriptValuesMeta.ScriptField field = new ScriptValuesMeta.ScriptField();
+ field.setName("new_value");
+ field.setRename("test_int");
+ field.setType(IValueMeta.TYPE_STRING);
+ field.setReplace(true);
+ meta.getScriptFields().add(field);
+
+ ScriptValues transform = newTransform(meta, input);
+ doReturn(new Object[] {"1"}).when(transform).getRow();
+ assertTrue(transform.init());
+
+ Object[] row = PipelineTestingUtil.execute(transform, 1, false).get(0);
+ assertEquals("1", row[0]);
+ assertEquals(0, transform.getErrors());
+ }
+
+ /** A kept input value is converted to the type the replacing field
declares. */
+ @Test
+ void replacedFieldNotSetByScriptIsConvertedToItsType() throws Exception {
+ RowMeta input = new RowMeta();
+ input.addValueMeta(new ValueMetaString("test_int"));
+
+ ScriptValuesMeta meta = metaWith(ScriptValuesScript.TRANSFORM_SCRIPT, "var
other = 0;", null);
+ ScriptValuesMeta.ScriptField field = new ScriptValuesMeta.ScriptField();
+ field.setName("test_int");
+ field.setType(IValueMeta.TYPE_INTEGER);
+ field.setReplace(true);
+ meta.getScriptFields().add(field);
+
+ ScriptValues transform = newTransform(meta, input);
+ doReturn(new Object[] {"1"}).doReturn(null).when(transform).getRow();
+ assertTrue(transform.init());
+
+ Object[] row = PipelineTestingUtil.execute(transform, 1, false).get(0);
+ assertEquals(1L, row[0]);
+ assertEquals(0, transform.getErrors());
+ }
+
+ /** Runs one row through a script that replaces input field test_int with
the given type. */
+ private static Object replacedValue(IValueMeta inputMeta, Object value, int
type, String script)
+ throws Exception {
+ RowMeta input = new RowMeta();
+ input.addValueMeta(inputMeta);
+
+ ScriptValuesMeta meta = metaWith(ScriptValuesScript.TRANSFORM_SCRIPT,
script, null);
+ ScriptValuesMeta.ScriptField field = new ScriptValuesMeta.ScriptField();
+ field.setName("test_int");
+ field.setType(type);
+ field.setReplace(true);
+ meta.getScriptFields().add(field);
+
+ ScriptValues transform = newTransform(meta, input);
+ doReturn(new Object[] {value}).doReturn(null).when(transform).getRow();
+ assertTrue(transform.init());
+
+ Object[] row = PipelineTestingUtil.execute(transform, 1, false).get(0);
+ assertEquals(0, transform.getErrors(), script);
+ return row[0];
+ }
+
+ private static final String[] NOT_ASSIGNING_SCRIPTS = {
+ "var other = 0;", "//var test_int = 0;\nvar other = 0;", "var other =
test_int;"
+ };
+
+ /**
+ * Mentioning a replaced field without assigning it (in a comment, or only
reading it) binds the
+ * field as a JavaScript wrapper. Converting that wrapper back lost
precision, failed on a Boolean
+ * and rounded differently than leaving the field out of the script entirely.
+ */
+ @Test
+ void replacedFieldNotAssignedKeepsValueWhetherMentionedOrNot() {
+ BigDecimal big = new BigDecimal("12345678901234567890.123456789");
+ List<Executable> checks = new ArrayList<>();
+ for (String script : NOT_ASSIGNING_SCRIPTS) {
+ checks.add(
+ () ->
+ assertEquals(
+ big,
+ replacedValue(
+ new ValueMetaBigNumber("test_int"), big,
IValueMeta.TYPE_BIGNUMBER, script),
+ "BigNumber: " + script));
+ checks.add(
+ () ->
+ assertEquals(
+ 9007199254740993L,
+ replacedValue(
+ new ValueMetaInteger("test_int"),
+ 9007199254740993L,
+ IValueMeta.TYPE_INTEGER,
+ script),
+ "Integer: " + script));
+ checks.add(
+ () ->
+ assertEquals(
+ Boolean.TRUE,
+ replacedValue(
+ new ValueMetaBoolean("test_int"),
+ Boolean.TRUE,
+ IValueMeta.TYPE_BOOLEAN,
+ script),
+ "Boolean: " + script));
+ checks.add(
+ () ->
+ assertEquals(
+ 11L,
+ replacedValue(
+ new ValueMetaNumber("test_int"), 10.5,
IValueMeta.TYPE_INTEGER, script),
+ "Number to Integer: " + script));
+ }
+ assertAll(checks);
+ }
+
+ /** An assignment still wins over the input value, also an assignment of
null. */
+ @Test
+ void replacedFieldAssignedByScriptWins() throws Exception {
+ assertEquals(
+ 5L,
+ replacedValue(
+ new ValueMetaInteger("test_int"), 1L, IValueMeta.TYPE_INTEGER,
"test_int = 5;"));
+ assertNull(
+ replacedValue(
+ new ValueMetaInteger("test_int"), 1L, IValueMeta.TYPE_INTEGER,
"test_int = null;"));
+ assertEquals(
+ 2L,
+ replacedValue(
+ new ValueMetaInteger("test_int"),
+ 1L,
+ IValueMeta.TYPE_INTEGER,
+ "test_int = test_int + 1;"));
+ }
+
/**
* Fields used by the script were counted case insensitively but bound case
sensitively, so a
* field only matching on case left an unusable slot pointing at field 0.