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 b139223b604 NIFI-15746 Preserve inherited parameter contexts during 
KEEP_EXISTING versioned flow deployment (#11041)
b139223b604 is described below

commit b139223b604a81d5cf84a52ce7663b85f7adfb75
Author: Pierre Villard <[email protected]>
AuthorDate: Fri Mar 27 03:13:02 2026 +0100

    NIFI-15746 Preserve inherited parameter contexts during KEEP_EXISTING 
versioned flow deployment (#11041)
    
    Signed-off-by: David Handermann <[email protected]>
---
 .../StandardVersionedComponentSynchronizer.java    |   3 +-
 .../registry/ParameterContextPreservationIT.java   | 101 +++++++++++++++++----
 2 files changed, 87 insertions(+), 17 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
index b67f79e56bf..ae823db265d 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
@@ -2383,7 +2383,8 @@ public class StandardVersionedComponentSynchronizer 
implements VersionedComponen
 
         // If the current parameter context doesn't have any inherited param 
contexts but the versioned one does,
         // add the versioned ones.
-        if (versionedParameterContext.getInheritedParameterContexts() != null 
&& !versionedParameterContext.getInheritedParameterContexts().isEmpty()) {
+        if (currentParameterContext.getInheritedParameterContexts().isEmpty()
+                && versionedParameterContext.getInheritedParameterContexts() 
!= null && 
!versionedParameterContext.getInheritedParameterContexts().isEmpty()) {
             
currentParameterContext.setInheritedParameterContexts(versionedParameterContext.getInheritedParameterContexts().stream()
                 .map(name -> 
selectParameterContext(versionedParameterContexts.get(name), 
versionedParameterContexts, parameterProviderReferences, componentIdGenerator))
                 .collect(Collectors.toList()));
diff --git 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/ParameterContextPreservationIT.java
 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/ParameterContextPreservationIT.java
index 41c75af918e..d1937b06271 100644
--- 
a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/ParameterContextPreservationIT.java
+++ 
b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/registry/ParameterContextPreservationIT.java
@@ -25,6 +25,8 @@ import 
org.apache.nifi.web.api.dto.VersionControlInformationDTO;
 import org.apache.nifi.web.api.dto.flow.FlowDTO;
 import org.apache.nifi.web.api.entity.FlowRegistryClientEntity;
 import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.apache.nifi.web.api.entity.ParameterContextReferenceEntity;
+import org.apache.nifi.web.api.entity.ParameterContextUpdateRequestEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupEntity;
 import org.apache.nifi.web.api.entity.ProcessGroupFlowEntity;
 import org.apache.nifi.web.api.entity.ProcessorEntity;
@@ -33,27 +35,17 @@ import org.junit.jupiter.api.Test;
 
 import java.io.IOException;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 /**
- * System test to verify that parameter context bindings are preserved during 
versioned flow upgrades
- * when new process groups are added.
- *
- * This test reproduces a bug where:
- * 1. v1: Process Group A with Parameter Context P, containing only a 
Processor X using param1
- * 2. v2: Process Group A with Parameter Context P, now containing a NEW 
Process Group B also attached to P
- * 3. When checking out v1 twice with "do not keep parameter context":
- *    - First checkout creates A1 with Parameter Context P
- *    - Second checkout creates A2 with Parameter Context P (1) since P 
already exists
- * 4. When upgrading A2 from v1 to v2, the newly added Process Group B 
incorrectly gets
- *    bound to P instead of P (1)
- *
- * The expectation is that the new Process Group B should be bound to P (1), 
the same
- * parameter context that its parent A2 uses.
+ * System tests to verify that parameter context bindings and inheritance 
chains are preserved
+ * during versioned flow operations (upgrades and deployments with different 
handling strategies).
  */
 class ParameterContextPreservationIT extends NiFiSystemIT {
     private static final String TEST_FLOWS_BUCKET = "test-flows";
@@ -177,11 +169,88 @@ class ParameterContextPreservationIT extends NiFiSystemIT 
{
         return 
getNifiClient().getProcessGroupClient().createProcessGroup("root", groupEntity, 
false);
     }
 
-    private ProcessGroupEntity getNestedProcessGroup(ProcessGroupEntity 
parent, String name) throws NiFiClientException, IOException {
+    /**
+     * Verifies that inherited parameter context chains are preserved when 
deploying a versioned flow
+     * with the KEEP_EXISTING parameter context handling strategy (NIFI-15746).
+     *
+     * Reproduces a multi-environment scenario where shared-service-params is 
configured to inherit from
+     * target-system-params on the target instance, but the versioned flow 
snapshot defines it as inheriting
+     * from source-system-params. Deploying with KEEP_EXISTING should preserve 
the target's inheritance chain.
+     */
+    @Test
+    void testInheritedParameterContextsPreservedWithKeepExistingStrategy() 
throws NiFiClientException, IOException, InterruptedException {
+        final FlowRegistryClientEntity clientEntity = registerClient();
+        final NiFiClientUtil util = getClientUtil();
+
+        final ParameterContextEntity sourceSystemParams = 
util.createParameterContext("source-system-params", Map.of("env", "source"));
+        final ParameterContextEntity sharedServiceParams = 
util.createParameterContext(
+                "shared-service-params", Map.of("shared-param", 
"shared-value"), List.of(sourceSystemParams.getId()), null);
+
+        final ProcessGroupEntity workflowGroup = 
util.createProcessGroup("MyWorkflow", "root");
+        util.setParameterContext(workflowGroup.getId(), sharedServiceParams);
+
+        final ProcessGroupEntity serviceGroup = 
util.createProcessGroup("MyService", workflowGroup.getId());
+        util.setParameterContext(serviceGroup.getId(), sharedServiceParams);
+
+        final ProcessorEntity processor = util.createProcessor(PROCESSOR_TYPE, 
serviceGroup.getId());
+        util.updateProcessorProperties(processor, 
Collections.singletonMap(PROCESSOR_PROPERTY_TEXT, "#{shared-param}"));
+        util.setAutoTerminatedRelationships(processor, RELATIONSHIP_SUCCESS);
+
+        final VersionControlInformationEntity vci = 
util.startVersionControl(workflowGroup, clientEntity, TEST_FLOWS_BUCKET, 
"InheritedParamContextFlow");
+        final String flowId = vci.getVersionControlInformation().getFlowId();
+
+        final ProcessGroupEntity groupForStopVc = 
getNifiClient().getProcessGroupClient().getProcessGroup(workflowGroup.getId());
+        getNifiClient().getVersionsClient().stopVersionControl(groupForStopVc);
+        util.deleteAll(workflowGroup.getId());
+        final ProcessGroupEntity groupToDelete = 
getNifiClient().getProcessGroupClient().getProcessGroup(workflowGroup.getId());
+        
getNifiClient().getProcessGroupClient().deleteProcessGroup(groupToDelete);
+
+        final ParameterContextEntity targetSystemParams = 
util.createParameterContext("target-system-params", Map.of("env", "target"));
+
+        final ParameterContextEntity currentSharedParams = 
getNifiClient().getParamContextClient().getParamContext(sharedServiceParams.getId(),
 false);
+        final ParameterContextUpdateRequestEntity updateRequest = 
util.updateParameterContext(
+                currentSharedParams, Map.of("shared-param", "shared-value"), 
List.of(targetSystemParams.getId()));
+        
util.waitForParameterContextRequestToComplete(sharedServiceParams.getId(), 
updateRequest.getRequest().getRequestId());
+
+        final ParameterContextEntity verifyBeforeImport = 
getNifiClient().getParamContextClient().getParamContext(sharedServiceParams.getId(),
 false);
+        final List<ParameterContextReferenceEntity> inheritedBeforeImport = 
verifyBeforeImport.getComponent().getInheritedParameterContexts();
+        assertEquals(1, inheritedBeforeImport.size());
+        assertEquals(targetSystemParams.getId(), 
inheritedBeforeImport.get(0).getId());
+
+        importFlowWithKeepExisting(clientEntity.getId(), flowId, VERSION_1);
+
+        final ParameterContextEntity sharedParamsAfterImport = 
getNifiClient().getParamContextClient().getParamContext(sharedServiceParams.getId(),
 false);
+        final List<ParameterContextReferenceEntity> inheritedAfterImport = 
sharedParamsAfterImport.getComponent().getInheritedParameterContexts();
+        assertNotNull(inheritedAfterImport, "Inherited parameter contexts 
should not be null after KEEP_EXISTING import");
+        assertFalse(inheritedAfterImport.isEmpty(), "Inherited parameter 
contexts should not be empty after KEEP_EXISTING import");
+        assertEquals(1, inheritedAfterImport.size());
+        assertEquals(targetSystemParams.getId(), 
inheritedAfterImport.get(0).getId(),
+                "After KEEP_EXISTING import, shared-service-params should 
still inherit from target-system-params");
+    }
+
+    private ProcessGroupEntity importFlowWithKeepExisting(final String 
registryClientId, final String flowId,
+                                                          final String 
version) throws NiFiClientException, IOException {
+        final VersionControlInformationDTO vci = new 
VersionControlInformationDTO();
+        vci.setBucketId(TEST_FLOWS_BUCKET);
+        vci.setFlowId(flowId);
+        vci.setVersion(version);
+        vci.setRegistryId(registryClientId);
+
+        final ProcessGroupDTO processGroupDto = new ProcessGroupDTO();
+        processGroupDto.setVersionControlInformation(vci);
+
+        final ProcessGroupEntity groupEntity = new ProcessGroupEntity();
+        groupEntity.setComponent(processGroupDto);
+        groupEntity.setRevision(getClientUtil().createNewRevision());
+
+        return 
getNifiClient().getProcessGroupClient().createProcessGroup("root", groupEntity, 
true);
+    }
+
+    private ProcessGroupEntity getNestedProcessGroup(final ProcessGroupEntity 
parent, final String name) throws NiFiClientException, IOException {
         final ProcessGroupFlowEntity flowEntity = 
getNifiClient().getFlowClient().getProcessGroup(parent.getId());
         final FlowDTO flowDto = flowEntity.getProcessGroupFlow().getFlow();
 
-        for (ProcessGroupEntity childGroup : flowDto.getProcessGroups()) {
+        for (final ProcessGroupEntity childGroup : flowDto.getProcessGroups()) 
{
             if (name.equals(childGroup.getComponent().getName())) {
                 return 
getNifiClient().getProcessGroupClient().getProcessGroup(childGroup.getId());
             }

Reply via email to