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

exceptionfactory pushed a commit to branch support/nifi-1.x
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/support/nifi-1.x by this push:
     new 5acbb8a702 NIFI-11706 Add option to create dedicated Parameter 
Contexts for Imported Flows
5acbb8a702 is described below

commit 5acbb8a70260b3e83f202bd9408082d6f7214122
Author: Bence Simon <[email protected]>
AuthorDate: Mon Jun 19 13:27:22 2023 +0200

    NIFI-11706 Add option to create dedicated Parameter Contexts for Imported 
Flows
    
    This closes #7401
    
    Signed-off-by: David Handermann <[email protected]>
    (cherry picked from commit cadf2fb6f25caf334bf787292380af5b25fdcbb1)
---
 .../main/asciidoc/images/import-version-dialog.png | Bin 62746 -> 107267 bytes
 nifi-docs/src/main/asciidoc/user-guide.adoc        |   4 +
 .../api/dto/ParameterContextHandlingStrategy.java  |  21 +++
 .../flow/StandardFlowRegistryClientNode.java       |  30 ++++-
 .../apache/nifi/web/api/ProcessGroupResource.java  | 133 +++++++++++--------
 .../ParameterContextNameCollisionResolver.java     |  74 +++++++++++
 .../nifi/web/util/ParameterContextReplacer.java    | 125 ++++++++++++++++++
 .../src/main/resources/nifi-web-api-context.xml    |   7 +
 .../ParameterContextNameCollisionResolverTest.java |  87 +++++++++++++
 .../web/util/ParameterContextReplacerTest.java     | 145 +++++++++++++++++++++
 .../partials/canvas/import-flow-version-dialog.jsp |   5 +
 .../nifi-web-ui/src/main/webapp/css/dialog.css     |  22 +++-
 .../main/webapp/js/nf/canvas/nf-flow-version.js    |   8 +-
 13 files changed, 597 insertions(+), 64 deletions(-)

diff --git a/nifi-docs/src/main/asciidoc/images/import-version-dialog.png 
b/nifi-docs/src/main/asciidoc/images/import-version-dialog.png
index c9927472ab..a7ff426798 100644
Binary files a/nifi-docs/src/main/asciidoc/images/import-version-dialog.png and 
b/nifi-docs/src/main/asciidoc/images/import-version-dialog.png differ
diff --git a/nifi-docs/src/main/asciidoc/user-guide.adoc 
b/nifi-docs/src/main/asciidoc/user-guide.adoc
index 8a76fdead4..0a24aaed8d 100644
--- a/nifi-docs/src/main/asciidoc/user-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/user-guide.adoc
@@ -2440,6 +2440,10 @@ image::import-version-dialog.png["Import Version Dialog"]
 
 Connected registries will appear as options in the Registry drop-down menu.  
For the chosen Registry, buckets the user has access to will appear as options 
in the Bucket drop-down menu.  The names of the flows in the chosen bucket will 
appear as options in the Name drop-down menu.  Select the desired version of 
the flow to import and select "Import" for the dataflow to be placed on the 
canvas.
 
+The import also provides the option to keep or replace existing Parameter 
Contexts based on name. Keeping the Parameter Contexts (which is the default 
behaviour) will use the existing Contexts if Contexts with the same name 
already exists, resulting shared parameter sets between multiple imports.
+
+Unchecking the checkbox named "Keep Existing Parameter Contexts" will result 
the creation of a new set of Parameter Contexts for the import, making it 
completely independent of the existing imports. The parameter values of these 
new Contexts will be set based on the content of the Registry Snapshot.
+
 image::versioned-flow-imported.png["Versioned Flow Imported"]
 
 Since the version imported in this example is the latest version (MySQL CDC, 
Version 3), the state of the versioned process group is "Up to date" 
(image:iconUpToDate.png["Up To Date Icon"]).  If the version imported had been 
an older version, the state would be "Stale" (image:iconStale.png["Stale 
Icon"]).
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterContextHandlingStrategy.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterContextHandlingStrategy.java
new file mode 100644
index 0000000000..d1f7414fab
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterContextHandlingStrategy.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+package org.apache.nifi.web.api.dto;
+
+public enum ParameterContextHandlingStrategy {
+    KEEP_EXISTING, REPLACE
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/StandardFlowRegistryClientNode.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/StandardFlowRegistryClientNode.java
index c5a8f91649..6924a16487 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/StandardFlowRegistryClientNode.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/registry/flow/StandardFlowRegistryClientNode.java
@@ -55,6 +55,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -225,6 +226,11 @@ public final class StandardFlowRegistryClientNode extends 
AbstractComponentNode
         if (fetchRemoteFlows) {
             final VersionedProcessGroup contents = 
flowSnapshot.getFlowContents();
             for (final VersionedProcessGroup child : 
contents.getProcessGroups()) {
+                final Map<String, VersionedParameterContext> 
childParameterContexts = populateVersionedContentsRecursively(context, child, 
snapshotContainer);
+
+                for (final Map.Entry<String, VersionedParameterContext> 
childParameterContext : childParameterContexts.entrySet()) {
+                    
flowSnapshot.getParameterContexts().putIfAbsent(childParameterContext.getKey(), 
childParameterContext.getValue());
+                }
                 populateVersionedContentsRecursively(context, child, 
snapshotContainer);
             }
         }
@@ -297,10 +303,15 @@ public final class StandardFlowRegistryClientNode extends 
AbstractComponentNode
         return context.getNiFiUserIdentity().orElse(null);
     }
 
-    private void populateVersionedContentsRecursively(final 
FlowRegistryClientUserContext context, final VersionedProcessGroup group,
-                                                      final 
FlowSnapshotContainer snapshotContainer) throws FlowRegistryException {
+    private Map<String, VersionedParameterContext> 
populateVersionedContentsRecursively(
+            final FlowRegistryClientUserContext context,
+            final VersionedProcessGroup group,
+            final FlowSnapshotContainer snapshotContainer
+    ) throws FlowRegistryException {
+        Map<String, VersionedParameterContext> accumulatedParameterContexts = 
new HashMap<>();
+
         if (group == null) {
-            return;
+            return accumulatedParameterContexts;
         }
 
         final VersionedFlowCoordinates coordinates = 
group.getVersionedFlowCoordinates();
@@ -330,12 +341,23 @@ public final class StandardFlowRegistryClientNode extends 
AbstractComponentNode
             group.setLogFileSuffix(contents.getLogFileSuffix());
             coordinates.setLatest(snapshot.isLatest());
 
+            for (final Map.Entry<String, VersionedParameterContext> 
parameterContext : snapshot.getParameterContexts().entrySet()) {
+                accumulatedParameterContexts.put(parameterContext.getKey(), 
parameterContext.getValue());
+            }
+
             snapshotContainer.addChildSnapshot(snapshot, group);
         }
 
         for (final VersionedProcessGroup child : group.getProcessGroups()) {
-            populateVersionedContentsRecursively(context, child, 
snapshotContainer);
+            final Map<String, VersionedParameterContext> 
childParameterContexts = populateVersionedContentsRecursively(context, child, 
snapshotContainer);
+
+            for (final Map.Entry<String, VersionedParameterContext> 
childParameterContext : childParameterContexts.entrySet()) {
+                // We favor the context instance from the enclosing versioned 
flow
+                
accumulatedParameterContexts.putIfAbsent(childParameterContext.getKey(), 
childParameterContext.getValue());
+            }
         }
+
+        return accumulatedParameterContexts;
     }
 
     private RegisteredFlowSnapshot fetchFlowContents(final 
FlowRegistryClientUserContext context, final VersionedFlowCoordinates 
coordinates,
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
index 33744e25d7..925adc1bb4 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessGroupResource.java
@@ -28,6 +28,57 @@ import io.swagger.annotations.ApiParam;
 import io.swagger.annotations.ApiResponse;
 import io.swagger.annotations.ApiResponses;
 import io.swagger.annotations.Authorization;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedHashMap;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.stream.XMLStreamReader;
+import javax.xml.transform.stream.StreamSource;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.text.StringEscapeUtils;
 import org.apache.nifi.authorization.AuthorizableLookup;
@@ -73,6 +124,7 @@ import org.apache.nifi.web.api.dto.ConnectionDTO;
 import org.apache.nifi.web.api.dto.ControllerServiceDTO;
 import org.apache.nifi.web.api.dto.DropRequestDTO;
 import org.apache.nifi.web.api.dto.FlowSnippetDTO;
+import org.apache.nifi.web.api.dto.ParameterContextHandlingStrategy;
 import org.apache.nifi.web.api.dto.PortDTO;
 import org.apache.nifi.web.api.dto.PositionDTO;
 import org.apache.nifi.web.api.dto.ProcessGroupDTO;
@@ -123,6 +175,7 @@ import 
org.apache.nifi.web.api.entity.VariableRegistryUpdateRequestEntity;
 import org.apache.nifi.web.api.request.ClientIdParameter;
 import org.apache.nifi.web.api.request.LongParameter;
 import org.apache.nifi.web.security.token.NiFiAuthenticationToken;
+import org.apache.nifi.web.util.ParameterContextReplacer;
 import org.apache.nifi.web.util.Pause;
 import org.apache.nifi.xml.processing.stream.StandardXMLStreamReaderProvider;
 import org.apache.nifi.xml.processing.stream.XMLStreamReaderProvider;
@@ -132,58 +185,6 @@ import org.slf4j.LoggerFactory;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.DefaultValue;
-import javax.ws.rs.GET;
-import javax.ws.rs.HttpMethod;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.QueryParam;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.HttpHeaders;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.MultivaluedHashMap;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.Response.Status;
-import javax.ws.rs.core.UriBuilder;
-import javax.ws.rs.core.UriInfo;
-import javax.xml.bind.JAXBContext;
-import javax.xml.bind.JAXBElement;
-import javax.xml.bind.JAXBException;
-import javax.xml.bind.Unmarshaller;
-import javax.xml.stream.XMLStreamReader;
-import javax.xml.transform.stream.StreamSource;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
 /**
  * RESTful endpoint for managing a Group.
  */
@@ -205,6 +206,7 @@ public class ProcessGroupResource extends 
FlowUpdateResource<ProcessGroupImportE
     private ConnectionResource connectionResource;
     private TemplateResource templateResource;
     private ControllerServiceResource controllerServiceResource;
+    private ParameterContextReplacer parameterContextReplacer;
 
     private final ConcurrentMap<String, VariableRegistryUpdateRequest> 
varRegistryUpdateRequests = new ConcurrentHashMap<>();
     private static final int MAX_VARIABLE_REGISTRY_UPDATE_REQUESTS = 100;
@@ -1969,8 +1971,16 @@ public class ProcessGroupResource extends 
FlowUpdateResource<ProcessGroupImportE
             @ApiParam(
                     value = "The process group configuration details.",
                     required = true
-        ) final ProcessGroupEntity requestProcessGroupEntity) {
-
+            )
+            final ProcessGroupEntity requestProcessGroupEntity,
+            @ApiParam(
+                    value = "Handling Strategy controls whether to keep or 
replace Parameter Contexts",
+                    defaultValue = "KEEP_EXISTING"
+            )
+            @QueryParam("parameterContextHandlingStrategy")
+            @DefaultValue("KEEP_EXISTING")
+            final ParameterContextHandlingStrategy 
parameterContextHandlingStrategy
+    ) {
         if (requestProcessGroupEntity == null || 
requestProcessGroupEntity.getComponent() == null) {
             throw new IllegalArgumentException("Process group details must be 
specified.");
         }
@@ -2024,7 +2034,12 @@ public class ProcessGroupResource extends 
FlowUpdateResource<ProcessGroupImportE
                 }
             }
 
-            // Step 4: Resolve Bundle info
+            // Step 4: Replace parameter contexts if necessary
+            if 
(ParameterContextHandlingStrategy.REPLACE.equals(parameterContextHandlingStrategy))
 {
+                
parameterContextReplacer.replaceParameterContexts(flowSnapshot, 
serviceFacade.getParameterContexts());
+            }
+
+            // Step 5: Resolve Bundle info
             
serviceFacade.discoverCompatibleBundles(flowSnapshot.getFlowContents());
 
             // If there are any Controller Services referenced that are 
inherited from the parent group, resolve those to point to the appropriate 
Controller Service, if we are able to.
@@ -2033,7 +2048,7 @@ public class ProcessGroupResource extends 
FlowUpdateResource<ProcessGroupImportE
             // If there are any Parameter Providers referenced by Parameter 
Contexts, resolve these to point to the appropriate Parameter Provider, if we 
are able to.
             serviceFacade.resolveParameterProviders(flowSnapshot, 
NiFiUserUtils.getNiFiUser());
 
-            // Step 5: Update contents of the ProcessGroupDTO passed in to 
include the components that need to be added.
+            // Step 6: Update contents of the ProcessGroupDTO passed in to 
include the components that need to be added.
             requestProcessGroupEntity.setVersionedFlowSnapshot(flowSnapshot);
         }
 
@@ -2042,7 +2057,7 @@ public class ProcessGroupResource extends 
FlowUpdateResource<ProcessGroupImportE
             serviceFacade.verifyImportProcessGroup(versionControlInfo, 
flowSnapshot.getFlowContents(), groupId);
         }
 
-        // Step 6: Replicate the request or call 
serviceFacade.updateProcessGroup
+        // Step 7: Replicate the request or call 
serviceFacade.updateProcessGroup
         if (isReplicateRequest()) {
             return replicate(HttpMethod.POST, requestProcessGroupEntity);
         } else if (isDisconnectedFromCluster()) {
@@ -4787,6 +4802,10 @@ public class ProcessGroupResource extends 
FlowUpdateResource<ProcessGroupImportE
         this.controllerServiceResource = controllerServiceResource;
     }
 
+    public void setParameterContextReplacer(ParameterContextReplacer 
parameterContextReplacer) {
+        this.parameterContextReplacer = parameterContextReplacer;
+    }
+
     private static class DropEntity extends Entity {
         final String entityId;
         final String dropRequestId;
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/ParameterContextNameCollisionResolver.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/ParameterContextNameCollisionResolver.java
new file mode 100644
index 0000000000..6c7c362a2c
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/ParameterContextNameCollisionResolver.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+package org.apache.nifi.web.util;
+
+import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+class ParameterContextNameCollisionResolver {
+    private static final String PATTERN_GROUP_NAME = "name";
+    private static final String PATTERN_GROUP_INDEX = "index";
+
+    private static final String LINEAGE_FORMAT = "^(?<" + PATTERN_GROUP_NAME + 
">.+?)( \\((?<" + PATTERN_GROUP_INDEX + ">[0-9]+)\\))?$";
+    private static final Pattern LINEAGE_PATTERN = 
Pattern.compile(LINEAGE_FORMAT);
+
+    private static final String NAME_FORMAT = "%s (%d)";
+
+    public String resolveNameCollision(final String 
originalParameterContextName, final Collection<ParameterContextEntity> 
existingContexts) {
+        final Matcher lineageMatcher = 
LINEAGE_PATTERN.matcher(originalParameterContextName);
+
+        if (!lineageMatcher.matches()) {
+            throw new IllegalArgumentException("Existing Parameter Context 
name \"(" + originalParameterContextName + "\") cannot be processed");
+        }
+
+        final String lineName = lineageMatcher.group(PATTERN_GROUP_NAME);
+        final String originalIndex = lineageMatcher.group(PATTERN_GROUP_INDEX);
+
+        // Candidates cannot be cached because new context might be added 
between calls
+        final Set<ParameterContextDTO> candidates = existingContexts
+                .stream()
+                .map(pc -> pc.getComponent())
+                .filter(dto -> dto.getName().startsWith(lineName))
+                .collect(Collectors.toSet());
+
+        int biggestIndex = (originalIndex == null) ? 0 : 
Integer.valueOf(originalIndex);
+
+        for (final ParameterContextDTO candidate : candidates) {
+            final Matcher matcher = 
LINEAGE_PATTERN.matcher(candidate.getName());
+
+            if (matcher.matches() && 
lineName.equals(matcher.group(PATTERN_GROUP_NAME))) {
+                final String indexGroup = matcher.group(PATTERN_GROUP_INDEX);
+
+                if (indexGroup != null) {
+                    int biggestIndexCandidate = Integer.valueOf(indexGroup);
+
+                    if (biggestIndexCandidate > biggestIndex) {
+                        biggestIndex = biggestIndexCandidate;
+                    }
+                }
+            }
+        }
+
+        return String.format(NAME_FORMAT, lineName, biggestIndex + 1);
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/ParameterContextReplacer.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/ParameterContextReplacer.java
new file mode 100644
index 0000000000..56d1a6dd14
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/util/ParameterContextReplacer.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+package org.apache.nifi.web.util;
+
+import java.util.Collection;
+import org.apache.nifi.flow.VersionedParameterContext;
+import org.apache.nifi.flow.VersionedProcessGroup;
+import org.apache.nifi.registry.flow.RegisteredFlowSnapshot;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+/**
+ * Replaces Parameter Contexts within the snapshot following name conventions.
+ *
+ * A set of Parameter Contexts following a given name convention is considered 
as a lineage. Lineage is used to
+ * group Parameter Contexts and determine a non-conflicting name for the newly 
created replacements. This class
+ * creates replaces all Parameter Contexts in the snapshot, keeping care of 
the lineage for the given Contexts.
+ *
+ * Note: if multiple (sub)groups refer to the same Parameter Context, only one 
replacement will be created and all
+ * Process Groups referred to the original Parameter Context will refer to 
this replacement.
+ */
+public class ParameterContextReplacer {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ParameterContextReplacer.class);
+
+    private final ParameterContextNameCollisionResolver nameCollisionResolver;
+
+    ParameterContextReplacer(final ParameterContextNameCollisionResolver 
nameCollisionResolver) {
+        this.nameCollisionResolver = nameCollisionResolver;
+    }
+
+    /**
+     * Goes through the Process Group structure and replaces Parameter 
Contexts to avoid collision with the ones
+     * existing in the flow, based on name. The method disregards if the given 
Parameter Context has no matching
+     * counterpart in the existing flow, it replaces all with newly created 
contexts.
+     *
+     * @param flowSnapshot Snapshot from the Registry. Modification will be 
applied on this object!
+     */
+    public void replaceParameterContexts(final RegisteredFlowSnapshot 
flowSnapshot, final Collection<ParameterContextEntity> existingContexts) {
+        // We do not want to have double replacements: within the snapshot we 
keep the identical names identical.
+        final Map<String, VersionedParameterContext> parameterContexts = 
flowSnapshot.getParameterContexts();
+        final Map<String, VersionedParameterContext> replacements = 
replaceParameterContexts(flowSnapshot.getFlowContents(), parameterContexts, new 
HashMap<>(), existingContexts);
+
+        // This is needed because if a PC is used for both assignments and 
inheritance (parent) then we would both change it
+        // but without updating the inheritance reference.  {@see NIFI-11706 
TC#8}
+        for (final Map.Entry<String, VersionedParameterContext> replacement : 
replacements.entrySet()) {
+            for (final VersionedParameterContext parameterContext : 
parameterContexts.values()) {
+                final List<String> inheritedContexts = 
parameterContext.getInheritedParameterContexts();
+                if (inheritedContexts.contains(replacement.getKey())) {
+                    inheritedContexts.remove(replacement.getKey());
+                    inheritedContexts.add(replacement.getValue().getName());
+                }
+            }
+        }
+    }
+
+    /**
+     * @return A collection of replaced Parameter Contexts. Every map entry 
represents a singe replacement where the key
+     * is the old context's name and the value is the new context.
+     */
+    private Map<String, VersionedParameterContext> replaceParameterContexts(
+        final VersionedProcessGroup group,
+        final Map<String, VersionedParameterContext> flowParameterContexts,
+        final Map<String, VersionedParameterContext> replacements,
+        final Collection<ParameterContextEntity> existingContexts
+    ) {
+        if (group.getParameterContextName() != null) {
+            final String oldParameterContextName = 
group.getParameterContextName();
+            final VersionedParameterContext oldParameterContext = 
flowParameterContexts.get(oldParameterContextName);
+
+            if (replacements.containsKey(oldParameterContextName)) {
+                final String replacementContextName = 
replacements.get(oldParameterContextName).getName();
+                group.setParameterContextName(replacementContextName);
+                LOGGER.debug("Replacing Parameter Context in Group {} from {} 
into {}", group.getIdentifier(), oldParameterContext, replacementContextName);
+            } else {
+                final VersionedParameterContext replacementContext = 
createReplacementContext(oldParameterContext, existingContexts);
+                group.setParameterContextName(replacementContext.getName());
+
+                flowParameterContexts.remove(oldParameterContextName);
+                flowParameterContexts.put(replacementContext.getName(), 
replacementContext);
+                replacements.put(oldParameterContextName, replacementContext);
+                LOGGER.debug("Replacing Parameter Context in Group {} from {} 
into the newly created {}", group.getIdentifier(), oldParameterContext, 
replacementContext.getName());
+            }
+        }
+
+        for (final VersionedProcessGroup childGroup : 
group.getProcessGroups()) {
+            replaceParameterContexts(childGroup, flowParameterContexts, 
replacements, existingContexts);
+        }
+
+        return replacements;
+    }
+
+    private VersionedParameterContext createReplacementContext(final 
VersionedParameterContext original, final Collection<ParameterContextEntity> 
existingContexts)  {
+        final VersionedParameterContext replacement = new 
VersionedParameterContext();
+        
replacement.setName(nameCollisionResolver.resolveNameCollision(original.getName(),
 existingContexts));
+        replacement.setParameters(new HashSet<>(original.getParameters()));
+        
replacement.setInheritedParameterContexts(Optional.ofNullable(original.getInheritedParameterContexts()).orElse(new
 ArrayList<>()));
+        replacement.setDescription(original.getDescription());
+        replacement.setSynchronized(original.isSynchronized());
+        replacement.setParameterProvider(original.getParameterProvider());
+        replacement.setParameterGroupName(original.getParameterGroupName());
+        return replacement;
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
index 5e356da4bb..f8a471a484 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml
@@ -381,6 +381,12 @@
         <property name="variableRegistry" ref="variableRegistry"/>
     </bean>
 
+    <bean id="nameCollisionResolver" 
class="org.apache.nifi.web.util.ParameterContextNameCollisionResolver" />
+
+    <bean id="parameterContextReplacer" 
class="org.apache.nifi.web.util.ParameterContextReplacer">
+        <constructor-arg ref="nameCollisionResolver" />
+    </bean>
+
     <!-- rest endpoints -->
     <bean id="flowResource" class="org.apache.nifi.web.api.FlowResource" 
scope="singleton">
         <property name="serviceFacade" ref="serviceFacade"/>
@@ -501,6 +507,7 @@
         <property name="clusterComponentLifecycle" 
ref="clusterComponentLifecycle" />
         <property name="localComponentLifecycle" ref="localComponentLifecycle" 
/>
         <property name="dtoFactory" ref="dtoFactory" />
+        <property name="parameterContextReplacer" 
ref="parameterContextReplacer" />
     </bean>
     <bean id="versionsResource" 
class="org.apache.nifi.web.api.VersionsResource" scope="singleton">
         <property name="serviceFacade" ref="serviceFacade" />
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/util/ParameterContextNameCollisionResolverTest.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/util/ParameterContextNameCollisionResolverTest.java
new file mode 100644
index 0000000000..881d972e3b
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/util/ParameterContextNameCollisionResolverTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+package org.apache.nifi.web.util;
+
+import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import org.apache.nifi.web.api.entity.ParameterContextEntity;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mockito;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.stream.Stream;
+
+class ParameterContextNameCollisionResolverTest {
+    private final static Collection<ParameterContextEntity> 
EMPTY_PARAMETER_CONTEXT_SOURCE = Collections.emptySet();
+    private final static Collection<ParameterContextEntity> 
PARAMETER_CONTEXT_SOURCE_WITH_FIRST = Arrays.asList(getTestContext("test"));
+    private final static Collection<ParameterContextEntity> 
PARAMETER_CONTEXT_SOURCE_WITH_SOME =
+            Arrays.asList(getTestContext("test"), getTestContext("test (1)"), 
getTestContext("test (2)"));
+    private final static Collection<ParameterContextEntity> 
PARAMETER_CONTEXT_SOURCE_WITH_NON_CONTINUOUS =
+            Arrays.asList(getTestContext("test (3)"), getTestContext("test 
(9)"));
+    private final static Collection<ParameterContextEntity> 
PARAMETER_CONTEXT_SOURCE_WITH_OTHER_LINEAGES =
+            Arrays.asList(getTestContext("test"), getTestContext("test2 (3)"), 
getTestContext("other"));
+
+    @ParameterizedTest(name = "\"{0}\" into \"{1}\"")
+    @MethodSource("testDataSet")
+    public void testResolveNameCollision(
+            final String oldName,
+            final String expectedResult,
+            final Collection<ParameterContextEntity> parameterContexts
+    ) {
+        final ParameterContextNameCollisionResolver testSubject = new 
ParameterContextNameCollisionResolver();
+        final String result = testSubject.resolveNameCollision(oldName, 
parameterContexts);
+        Assertions.assertEquals(expectedResult, result);
+    }
+
+    private static Stream<Arguments> testDataSet() {
+        return Stream.of(
+                Arguments.of("test", "test (1)", 
EMPTY_PARAMETER_CONTEXT_SOURCE),
+                Arguments.of("test (1)", "test (2)", 
EMPTY_PARAMETER_CONTEXT_SOURCE),
+                Arguments.of("test(1)", "test(1) (1)", 
EMPTY_PARAMETER_CONTEXT_SOURCE),
+                Arguments.of("test (1) (1)", "test (1) (2)", 
EMPTY_PARAMETER_CONTEXT_SOURCE),
+                Arguments.of("(1)", "(1) (1)", EMPTY_PARAMETER_CONTEXT_SOURCE),
+                Arguments.of(
+                        "((((Lorem.ipsum dolor sit.amet, consectetur 
adipiscing elit",
+                        "((((Lorem.ipsum dolor sit.amet, consectetur 
adipiscing elit (1)",
+                        EMPTY_PARAMETER_CONTEXT_SOURCE),
+                Arguments.of("test", "test (1)", 
PARAMETER_CONTEXT_SOURCE_WITH_FIRST),
+                Arguments.of("test (1)", "test (2)", 
PARAMETER_CONTEXT_SOURCE_WITH_FIRST),
+                Arguments.of("test (8)", "test (9)", 
PARAMETER_CONTEXT_SOURCE_WITH_FIRST),
+                Arguments.of("other", "other (1)", 
PARAMETER_CONTEXT_SOURCE_WITH_FIRST),
+                Arguments.of("test", "test (3)", 
PARAMETER_CONTEXT_SOURCE_WITH_SOME),
+                Arguments.of("test (1)", "test (3)", 
PARAMETER_CONTEXT_SOURCE_WITH_SOME),
+                Arguments.of("other", "other (1)", 
PARAMETER_CONTEXT_SOURCE_WITH_SOME),
+                Arguments.of("test", "test (10)", 
PARAMETER_CONTEXT_SOURCE_WITH_NON_CONTINUOUS),
+                Arguments.of("test (3)", "test (10)", 
PARAMETER_CONTEXT_SOURCE_WITH_NON_CONTINUOUS),
+                Arguments.of("test (15)", "test (16)", 
PARAMETER_CONTEXT_SOURCE_WITH_NON_CONTINUOUS),
+                Arguments.of("test", "test (1)", 
PARAMETER_CONTEXT_SOURCE_WITH_OTHER_LINEAGES),
+                Arguments.of("test (1)", "test (2)", 
PARAMETER_CONTEXT_SOURCE_WITH_OTHER_LINEAGES)
+        );
+    }
+
+    private static ParameterContextEntity getTestContext(final String name) {
+        final ParameterContextEntity result = 
Mockito.mock(ParameterContextEntity.class);
+        final ParameterContextDTO dto = 
Mockito.mock(ParameterContextDTO.class);
+        Mockito.when(result.getComponent()).thenReturn(dto);
+        Mockito.when(dto.getName()).thenReturn(name);
+        return result;
+    }
+}
\ No newline at end of file
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/util/ParameterContextReplacerTest.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/util/ParameterContextReplacerTest.java
new file mode 100644
index 0000000000..cd585c06a4
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/test/java/org/apache/nifi/web/util/ParameterContextReplacerTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.
+ */
+package org.apache.nifi.web.util;
+
+import org.apache.nifi.flow.VersionedParameter;
+import org.apache.nifi.flow.VersionedParameterContext;
+import org.apache.nifi.flow.VersionedProcessGroup;
+import org.apache.nifi.registry.flow.RegisteredFlowSnapshot;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+class ParameterContextReplacerTest {
+    private static final String CONTEXT_ONE_NAME = "contextOne";
+    private static final String CONTEXT_TWO_NAME = "contextTwo (1)";
+    private static final String CONTEXT_ONE_NAME_AFTER_REPLACE = "contextOne 
(1)";
+    private static final String CONTEXT_TWO_NAME_AFTER_REPLACE = "contextTwo 
(2)";
+
+    @Test
+    public void testReplacementWithoutSubgroups() {
+        final ParameterContextNameCollisionResolver collisionResolver = new 
ParameterContextNameCollisionResolver();
+        final ParameterContextReplacer testSubject = new 
ParameterContextReplacer(collisionResolver);
+        final RegisteredFlowSnapshot snapshot = getSimpleSnapshot();
+
+        testSubject.replaceParameterContexts(snapshot, new HashSet<>());
+
+        final Map<String, VersionedParameterContext> parameterContexts = 
snapshot.getParameterContexts();
+        Assertions.assertEquals(1, parameterContexts.size());
+        
Assertions.assertTrue(parameterContexts.containsKey(CONTEXT_ONE_NAME_AFTER_REPLACE));
+
+        final VersionedParameterContext replacedContext = 
parameterContexts.get(CONTEXT_ONE_NAME_AFTER_REPLACE);
+        final Set<VersionedParameter> parameters = 
replacedContext.getParameters();
+        final Map<String, VersionedParameter> parametersByName = new 
HashMap<>();
+
+        for (final VersionedParameter parameter : parameters) {
+            parametersByName.put(parameter.getName(), parameter);
+        }
+
+        Assertions.assertEquals(CONTEXT_ONE_NAME_AFTER_REPLACE, 
snapshot.getFlowContents().getParameterContextName());
+        Assertions.assertEquals("value1", 
parametersByName.get("param1").getValue());
+        Assertions.assertEquals("value2", 
parametersByName.get("param2").getValue());
+    }
+
+    @Test
+    public void testReplacementWithSubgroups() {
+        final ParameterContextNameCollisionResolver collisionResolver = new 
ParameterContextNameCollisionResolver();
+        final ParameterContextReplacer testSubject = new 
ParameterContextReplacer(collisionResolver);
+        final RegisteredFlowSnapshot snapshot = getMultiLevelSnapshot();
+
+        testSubject.replaceParameterContexts(snapshot, new HashSet<>());
+
+        final Map<String, VersionedParameterContext> parameterContexts = 
snapshot.getParameterContexts();
+        Assertions.assertEquals(2, parameterContexts.size());
+        
Assertions.assertTrue(parameterContexts.containsKey(CONTEXT_ONE_NAME_AFTER_REPLACE));
+        
Assertions.assertTrue(parameterContexts.containsKey(CONTEXT_TWO_NAME_AFTER_REPLACE));
+    }
+
+    private static RegisteredFlowSnapshot getSimpleSnapshot() {
+        final VersionedProcessGroup processGroup = getProcessGroup("PG1", 
CONTEXT_ONE_NAME);
+
+        final RegisteredFlowSnapshot snapshot = new RegisteredFlowSnapshot();
+        snapshot.setParameterContexts(new 
HashMap<>(Collections.singletonMap(CONTEXT_ONE_NAME, 
getParameterContextOne())));
+        snapshot.setFlowContents(processGroup);
+
+        return snapshot;
+    }
+
+    private static RegisteredFlowSnapshot getMultiLevelSnapshot() {
+        final VersionedProcessGroup childProcessGroup1 = 
getProcessGroup("CPG1", CONTEXT_ONE_NAME);
+        final VersionedProcessGroup childProcessGroup2 = 
getProcessGroup("CPG2", CONTEXT_TWO_NAME);
+        final VersionedProcessGroup processGroup = getProcessGroup("PG1", 
CONTEXT_TWO_NAME, childProcessGroup1, childProcessGroup2);
+
+        final Map<String, VersionedParameterContext> parameterContexts = new 
HashMap<>();
+        parameterContexts.put(CONTEXT_ONE_NAME, getParameterContextOne());
+        parameterContexts.put(CONTEXT_TWO_NAME, getParameterContextTwo());
+
+        final RegisteredFlowSnapshot snapshot = new RegisteredFlowSnapshot();
+        snapshot.setParameterContexts(parameterContexts);
+        snapshot.setFlowContents(processGroup);
+
+        return snapshot;
+    }
+
+    private static VersionedProcessGroup getProcessGroup(final String name, 
final String parameterContext, final VersionedProcessGroup... children) {
+        final VersionedProcessGroup result = new VersionedProcessGroup();
+        result.setName(name);
+        result.setIdentifier(name); // Needed for equals check
+        result.setParameterContextName(parameterContext);
+        result.setProcessGroups(new HashSet<>(Arrays.asList(children)));
+        return result;
+    }
+
+    private static VersionedParameterContext getParameterContextOne() {
+        final Map<String, String> parameters = new HashMap<>();
+        parameters.put("param1", "value1");
+        parameters.put("param2", "value2");
+        final VersionedParameterContext context = 
getParameterContext(CONTEXT_ONE_NAME, parameters);
+        return context;
+    }
+
+    private static VersionedParameterContext getParameterContextTwo() {
+        final Map<String, String> parameters = new HashMap<>();
+        parameters.put("param3", "value3");
+        parameters.put("param4", "value4");
+        final VersionedParameterContext context = 
getParameterContext(CONTEXT_TWO_NAME, parameters);
+        return context;
+    }
+
+    public static VersionedParameterContext getParameterContext(final String 
name, final Map<String, String> parameters) {
+        final Set<VersionedParameter> contextParameters = new HashSet<>();
+
+        for (final Map.Entry<String, String> parameter : 
parameters.entrySet()) {
+            final VersionedParameter versionedParameter = new 
VersionedParameter();
+            versionedParameter.setName(parameter.getKey());
+            versionedParameter.setValue(parameter.getValue());
+            contextParameters.add(versionedParameter);
+        }
+
+        final VersionedParameterContext context = new 
VersionedParameterContext();
+        context.setName(name);
+        context.setParameters(contextParameters);
+
+        return context;
+    }
+}
\ No newline at end of file
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/import-flow-version-dialog.jsp
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/import-flow-version-dialog.jsp
index e2de2d18f6..1bb1eec152 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/import-flow-version-dialog.jsp
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/import-flow-version-dialog.jsp
@@ -45,6 +45,11 @@
                 <div id="import-flow-version-label"></div>
             </div>
         </div>
+        <div class="setting keep-parameter-context">
+            <div id="keepExistingParameterContext" class="nf-checkbox 
checkbox-checked"></div>
+            <div class="nf-checkbox-label">Keep Existing Parameter 
Contexts</div>
+            <div class="fa fa-question-circle" alt="Info" title="When not 
selected, only directly associated Parameter Contexts will be copied, inherited 
Contexts with no direct assignment to a Process Group are ignored."></div>
+        </div>
         <div class="setting">
             <div class="setting-name">Flow Description</div>
             <div class="setting-field" id="import-flow-description-container">
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
index b5b68eaaee..f476852523 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/dialog.css
@@ -247,11 +247,11 @@ div.progress-label {
 #import-flow-version-table {
     overflow: hidden;
     position: absolute;
-    top: 270px;
+    top: 307px;
     left: 0px;
     right: 0px;
     bottom: 0px;
-    height: 155px;
+    height: 118px;
 }
 
 #import-flow-description-container {
@@ -276,6 +276,24 @@ div.progress-label {
     border-radius: 0;
 }
 
+#import-flow-version-dialog .keep-parameter-context {
+    align-items: center;
+    display: flex;
+}
+
+.keep-parameter-context .nf-checkbox-label {
+    font-size: 12px;
+    font-weight: 500;
+    font-family: Roboto Slab;
+}
+
+.keep-parameter-context .fa {
+    color: #004849;
+    font-size: 12px;
+    line-height: 22px;
+    margin-left: 5px;
+}
+
 /*
     Local changes
  */
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js
index 134845428c..7c37ec7252 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-flow-version.js
@@ -815,6 +815,9 @@
                 }
             }]).modal('show');
 
+            // resetting the checkbox
+            
$('#keepExistingParameterContext').removeClass('checkbox-unchecked').addClass('checkbox-checked');
+
             // hide the new process group dialog
             $('#new-process-group-dialog').modal('hide');
         });
@@ -1045,7 +1048,10 @@
         return $.ajax({
             type: 'POST',
             data: JSON.stringify(processGroupEntity),
-            url: '../nifi-api/process-groups/' + 
encodeURIComponent(nfCanvasUtils.getGroupId()) + '/process-groups',
+            url: '../nifi-api/process-groups/' + 
encodeURIComponent(nfCanvasUtils.getGroupId()) + '/process-groups?'
+                + $.param({
+                    'parameterContextHandlingStrategy' : 
$('#keepExistingParameterContext').hasClass('checkbox-checked') ? 
'KEEP_EXISTING' : 'REPLACE'
+                }),
             dataType: 'json',
             contentType: 'application/json'
         }).done(function (response) {

Reply via email to