This is an automated email from the ASF dual-hosted git repository.

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 87c1fc16872 NIFI-16213 Fixed null handing in MapRecord for an array of 
UUIDs (#11554)
87c1fc16872 is described below

commit 87c1fc16872d902dc702ab484247b9b78067d7ae
Author: Mike <[email protected]>
AuthorDate: Mon Aug 17 14:56:40 2026 -0400

    NIFI-16213 Fixed null handing in MapRecord for an array of UUIDs (#11554)
    
    Added a null check to MapRecord to prevent a NPE from causing breakage when 
dealing with an array/list of UUIDs
    
    Signed-off-by: David Handermann <[email protected]>
---
 .../nifi/serialization/record/MapRecord.java       |  2 +-
 .../nifi-standard-processors/pom.xml               |  2 ++
 .../nifi/processors/standard/TestUpdateRecord.java | 41 ++++++++++++++++++++++
 .../input/uuid-list-test-input.json                | 20 +++++++++++
 .../schema/uuid-list-test-child.avsc               | 20 +++++++++++
 .../schema/uuid-list-test-parent.avsc              | 38 ++++++++++++++++++++
 6 files changed, 122 insertions(+), 1 deletion(-)

diff --git 
a/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/MapRecord.java
 
b/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/MapRecord.java
index 35daa1f87e9..bd3b4d5e244 100644
--- 
a/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/MapRecord.java
+++ 
b/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/MapRecord.java
@@ -805,7 +805,7 @@ public class MapRecord implements Record {
             final List<DataType> possibleTypes = 
choiceDataType.getPossibleSubTypes();
 
             final DataType chosenDataType = 
DataTypeUtils.chooseDataType(value, choiceDataType);
-            if (chosenDataType.getFieldType() != RecordFieldType.RECORD || 
!(value instanceof final Record childRecord)) {
+            if (chosenDataType == null || chosenDataType.getFieldType() != 
RecordFieldType.RECORD || !(value instanceof final Record childRecord)) {
                 return specField;
             }
 
diff --git 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
index 923eae50471..d78dfb58a5f 100644
--- 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
+++ 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/pom.xml
@@ -810,6 +810,8 @@
                         
<exclude>src/test/resources/TestUpdateRecord/schema/person-with-name-string-fields.avsc</exclude>
                         
<exclude>src/test/resources/TestUpdateRecord/schema/person-with-name-string.avsc</exclude>
                         
<exclude>src/test/resources/TestUpdateRecord/schema/person-with-stringified-name.avsc</exclude>
+                        
<exclude>src/test/resources/TestUpdateRecord/schema/uuid-list-test-parent.avsc</exclude>
+                        
<exclude>src/test/resources/TestUpdateRecord/schema/uuid-list-test-child.avsc</exclude>
                         
<exclude>src/test/resources/TestValidateJson/schema-simple-example-missing-required.json</exclude>
                         
<exclude>src/test/resources/TestValidateJson/schema-simple-example-unmatched-pattern.json</exclude>
                         
<exclude>src/test/resources/TestValidateJson/schema-simple-example.json</exclude>
diff --git 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestUpdateRecord.java
 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestUpdateRecord.java
index 4b59f538026..b45c1b6a899 100644
--- 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestUpdateRecord.java
+++ 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/java/org/apache/nifi/processors/standard/TestUpdateRecord.java
@@ -33,6 +33,7 @@ import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
@@ -53,6 +54,8 @@ public class TestUpdateRecord {
     private static String personWithNameAndMother;
     private static Path multiArraysJson;
     private static String multiArraysJsonContent;
+    private static String uuidListSchema;
+    private static String uuidListSchemaChild;
     private TestRunner runner;
     private MockRecordParser readerService;
 
@@ -64,6 +67,12 @@ public class TestUpdateRecord {
         nameFieldsOnly = 
Files.readString(Paths.get("src/test/resources/TestUpdateRecord/schema/name-fields-only.avsc"));
         personWithNameAndMother = 
Files.readString(Paths.get("src/test/resources/TestUpdateRecord/schema/person-with-name-and-mother.avsc"));
         multiArraysJson = 
Paths.get("src/test/resources/TestUpdateRecord/input/multi-arrays.json");
+
+        // These schemas were generated with Claude Opus
+        uuidListSchema = 
Files.readString(Paths.get("src/test/resources/TestUpdateRecord/schema/uuid-list-test-parent.avsc"));
+        uuidListSchemaChild = 
Files.readString(Paths.get("src/test/resources/TestUpdateRecord/schema/uuid-list-test-child.avsc"));
+        //
+
         multiArraysJsonContent = JsonUtil.getExpectedContent(multiArraysJson);
     }
 
@@ -883,6 +892,38 @@ public class TestUpdateRecord {
         runner.removeProperty("/peoples[0..-1][./name != 'Mary 
Doe']/addresses[0,1..2]/city");
     }
 
+    @Test
+    @DisplayName("Updating a field with a pattern like 
arrayOf(/participants[*]/user_id) caused a NPE pre-patch")
+    public void testNiFi16213() throws Exception {
+        //This referenced sample input as generated with Claude Opus
+        final String inputJson = 
Files.readString(Paths.get("src/test/resources/TestUpdateRecord/input/uuid-list-test-input.json"));
+
+        final JsonTreeReader jsonReader = new JsonTreeReader();
+        runner.addControllerService("reader", jsonReader);
+
+        final String inputSchemaText = uuidListSchema;
+        final String outputSchemaText = uuidListSchemaChild;
+
+        runner.setProperty(jsonReader, 
SchemaAccessUtils.SCHEMA_ACCESS_STRATEGY, 
SchemaAccessUtils.SCHEMA_TEXT_PROPERTY);
+        runner.setProperty(jsonReader, SchemaAccessUtils.SCHEMA_TEXT, 
inputSchemaText);
+        runner.enableControllerService(jsonReader);
+
+        final JsonRecordSetWriter jsonWriter = new JsonRecordSetWriter();
+        runner.addControllerService("writer", jsonWriter);
+        runner.setProperty(jsonWriter, 
SchemaAccessUtils.SCHEMA_ACCESS_STRATEGY, 
SchemaAccessUtils.SCHEMA_TEXT_PROPERTY);
+        runner.setProperty(jsonWriter, SchemaAccessUtils.SCHEMA_TEXT, 
outputSchemaText);
+        runner.setProperty(jsonWriter, "Pretty Print JSON", "true");
+        runner.setProperty(jsonWriter, "Schema Write Strategy", 
"full-schema-attribute");
+        runner.setProperty(UpdateRecord.REPLACEMENT_VALUE_STRATEGY, 
UpdateRecord.RECORD_PATH_VALUES);
+        runner.enableControllerService(jsonWriter);
+
+        runner.enqueue(inputJson);
+        runner.setProperty("/participants", 
"arrayOf(/participants[*]/user_id)");
+        runner.run();
+
+        runner.assertAllFlowFilesTransferred(UpdateRecord.REL_SUCCESS);
+    }
+
     private void assertCountMatches(String content, String match, int 
expectedCount) {
         final int actualCount = StringUtils.countMatches(content, match);
 
diff --git 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestUpdateRecord/input/uuid-list-test-input.json
 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestUpdateRecord/input/uuid-list-test-input.json
new file mode 100644
index 00000000000..0262e6f9af8
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestUpdateRecord/input/uuid-list-test-input.json
@@ -0,0 +1,20 @@
+{
+  "chat_name": "Atlas Cutover Squad",
+  "participants": [
+    {
+      "user_id": "223a47ed-0f2c-44c5-b2ac-2a37133bf101",
+      "authenticator": "google-oauth2",
+      "principal": "priya.patel"
+    },
+    {
+      "user_id": "b7e92e2b-de17-4e1e-9415-8bc8057d8cbc",
+      "authenticator": "saml",
+      "principal": "[email protected]"
+    },
+    {
+      "user_id": "b9debc33-fbb6-4fca-9a90-f361b7572fea",
+      "authenticator": "okta-saml",
+      "principal": "jordan.kim"
+    }
+  ]
+}
diff --git 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestUpdateRecord/schema/uuid-list-test-child.avsc
 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestUpdateRecord/schema/uuid-list-test-child.avsc
new file mode 100644
index 00000000000..52c5d746936
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestUpdateRecord/schema/uuid-list-test-child.avsc
@@ -0,0 +1,20 @@
+{
+  "type": "record",
+  "name": "Chat",
+  "namespace": "com.example.chat",
+  "fields": [
+    {
+      "name": "chat_name",
+      "type": ["null", "string"],
+      "default": null
+    },
+    {
+      "name": "participants",
+      "type": {
+        "type": "array",
+        "items": { "type": "string", "logicalType": "uuid" }
+      },
+      "default": []
+    }
+  ]
+}
diff --git 
a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestUpdateRecord/schema/uuid-list-test-parent.avsc
 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestUpdateRecord/schema/uuid-list-test-parent.avsc
new file mode 100644
index 00000000000..32be0f80d16
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-processors/src/test/resources/TestUpdateRecord/schema/uuid-list-test-parent.avsc
@@ -0,0 +1,38 @@
+{
+  "type": "record",
+  "name": "IngestedChatMessage",
+  "namespace": "com.example.chat",
+  "fields": [
+
+    {
+      "name": "chat_name",
+      "type": ["null", "string"],
+      "default": null
+    },
+    {
+      "name": "participants",
+      "type": {
+        "type": "array",
+        "items": {
+          "type": "record",
+          "name": "Participant",
+          "doc": "One chat member and when they joined.",
+          "fields": [
+            {
+              "name": "user_id",
+              "type": { "type": "string", "logicalType": "uuid" }
+            },
+            {
+              "name": "authenticator",
+              "type": "string"
+            },
+            {
+              "name": "principal",
+              "type": "string"
+            }
+          ]
+        }
+      }
+    }
+  ]
+}

Reply via email to