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 cadf2fb6f2 NIFI-11706 Add option to create dedicated Parameter
Contexts for Imported Flows
cadf2fb6f2 is described below
commit cadf2fb6f25caf334bf787292380af5b25fdcbb1
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]>
---
.../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 3c114f2e15..567526d600 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) {