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 c00502adfb NIFI-14935 Improved handling of a Parameter referencing a 
Controller Service (#10265)
c00502adfb is described below

commit c00502adfb1c4dbac5ce4ce205e888420e0bb6df
Author: Pierre Villard <[email protected]>
AuthorDate: Sat Sep 6 23:27:44 2025 +0200

    NIFI-14935 Improved handling of a Parameter referencing a Controller 
Service (#10265)
    
    Signed-off-by: David Handermann <[email protected]>
---
 .../flow/mapping/NiFiRegistryFlowMapper.java       | 16 +++++-
 .../flow/mapping/NiFiRegistryFlowMapperTest.java   | 60 ++++++++++++++++++++++
 2 files changed, 74 insertions(+), 2 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapper.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapper.java
index 37b7ac6b4f..dba641a9e8 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapper.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapper.java
@@ -925,8 +925,20 @@ public class NiFiRegistryFlowMapper {
             } else {
                 final String referencedVersionServiceId = 
referencedControllerServiceData.getFirst().getVersionedServiceId();
                 final String parameterValue = parameter.getValue();
-                final String serviceId = 
getId(Optional.ofNullable(referencedVersionServiceId), parameterValue);
-                versionedParameter = mapParameter(parameter, serviceId);
+
+                // If a referenced Versioned Service ID is available, use it 
directly. Do not attempt to
+                // generate or cache a mapping using a null component 
identifier.
+                if (referencedVersionServiceId != null) {
+                    versionedParameter = mapParameter(parameter, 
referencedVersionServiceId);
+                } else if (parameterValue != null && 
!parameterValue.isBlank()) {
+                    // If the parameter has a concrete (non-empty) value 
referencing a service instance id,
+                    // generate a stable Versioned ID for it.
+                    final String serviceId = getId(Optional.empty(), 
parameterValue);
+                    versionedParameter = mapParameter(parameter, serviceId);
+                } else {
+                    // No referenced service and no parameter value specified; 
map as null to avoid NPE
+                    versionedParameter = mapParameter(parameter, null);
+                }
             }
         } else {
             versionedParameter = mapParameter(parameter);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapperTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapperTest.java
index ec03e47003..489db2e7c5 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapperTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/registry/flow/mapping/NiFiRegistryFlowMapperTest.java
@@ -30,6 +30,7 @@ import org.apache.nifi.connectable.Position;
 import org.apache.nifi.connectable.Positionable;
 import org.apache.nifi.connectable.Size;
 import org.apache.nifi.controller.BackoffMechanism;
+import org.apache.nifi.controller.ComponentNode;
 import org.apache.nifi.controller.ControllerService;
 import org.apache.nifi.controller.ParameterProviderNode;
 import org.apache.nifi.controller.ProcessorNode;
@@ -72,6 +73,8 @@ import org.apache.nifi.parameter.ParameterContext;
 import org.apache.nifi.parameter.ParameterDescriptor;
 import org.apache.nifi.parameter.ParameterProvider;
 import org.apache.nifi.parameter.ParameterProviderConfiguration;
+import org.apache.nifi.parameter.ParameterReferenceManager;
+import org.apache.nifi.parameter.ParameterReferencedControllerServiceData;
 import org.apache.nifi.parameter.StandardParameterProviderConfiguration;
 import org.apache.nifi.processor.util.StandardValidators;
 import org.apache.nifi.registry.VariableDescriptor;
@@ -154,6 +157,63 @@ public class NiFiRegistryFlowMapperTest {
         
when(parameterProvider.getIdentifier()).thenReturn(PARAMETER_PROVIDER_ID);
     }
 
+    /**
+     * Fix for NIFI-14935: mapping parameter contexts should not NPE when a 
Reference Parameter to a
+     * Controller Service has a null/empty value and the referenced versioned 
service id is null. The
+     * parameter should be mapped with a null value.
+     */
+    @Test
+    public void testMapParameterReferenceToServiceWithNullValueMapsNull() {
+        final ProcessGroup processGroup = mock(ProcessGroup.class);
+
+        // Parameter Context with one parameter that references a Controller 
Service but has a null value
+        final ParameterContext parameterContext = mock(ParameterContext.class, 
Answers.RETURNS_DEEP_STUBS);
+        when(processGroup.getParameterContext()).thenReturn(parameterContext);
+        when(parameterContext.getName()).thenReturn("ctx");
+        
when(parameterContext.getInheritedParameterContextNames()).thenReturn(Collections.emptyList());
+
+        final Map<ParameterDescriptor, Parameter> parametersMap = new 
LinkedHashMap<>();
+        final ParameterDescriptor parameterDescriptor = new 
ParameterDescriptor.Builder()
+                .name("ssl-service-ref")
+                .description("Reference to SSL Context Service")
+                .sensitive(false)
+                .build();
+        final Parameter parameter = mock(Parameter.class);
+        when(parameter.getDescriptor()).thenReturn(parameterDescriptor);
+        when(parameter.getValue()).thenReturn(null); // empty/null parameter 
value
+        parametersMap.put(parameterDescriptor, parameter);
+        when(parameterContext.getParameters()).thenReturn(parametersMap);
+
+        // Parameter is referenced by a property that identifies a Controller 
Service; versioned service id is null
+        final ParameterReferenceManager referenceManager = 
parameterContext.getParameterReferenceManager();
+        final PropertyDescriptor referencingProperty = new 
PropertyDescriptor.Builder()
+                .name("SSL Context Service")
+                .identifiesControllerService(ControllerService.class)
+                .build();
+        final ParameterReferencedControllerServiceData referencedData = new 
ParameterReferencedControllerServiceData(
+                parameterDescriptor.getName(),
+                mock(ComponentNode.class),
+                referencingProperty,
+                ControllerService.class,
+                null // referenced versioned service id is null
+        );
+        
when(referenceManager.getReferencedControllerServiceData(parameterContext, 
parameterDescriptor.getName()))
+                .thenReturn(Collections.singletonList(referencedData));
+
+        // Mapping should succeed and the parameter value should be null
+        final Map<String, ParameterProviderReference> 
parameterProviderReferences = new HashMap<>();
+        final Map<String, VersionedParameterContext> mapped =
+                flowMapper.mapParameterContexts(processGroup, true, 
parameterProviderReferences);
+
+        final VersionedParameterContext ctx = mapped.get("ctx");
+        assertNotNull(ctx);
+        final VersionedParameter versionedParameter = 
ctx.getParameters().stream()
+                .filter(p -> p.getName().equals("ssl-service-ref"))
+                .findFirst()
+                .orElseThrow(() -> new AssertionError("Expected parameter not 
found"));
+        assertNull(versionedParameter.getValue());
+    }
+
     /**
      * Test mapping versioned process group's parameter contexts excluding 
descendant versioned process groups
      */

Reply via email to