NIFI-8491: Adding support for configuring parameter context inheritance (#5371)
* NIFI-8491:
- Adding support for configuring parameter context inheritance.
* NIFI-8491:
- Allowing changes to the parameter context inheritance to drive Apply disabled state.
* NIFI-8491: Updating StandardParameterContext#isAuthorized check
* NIFI-8491:
- Showing selected inherited parameter contexts in ready only form when appropriate.
- Allowing available parameter contexts to be inherited by double clicking.
- Removing support for rendering unauthorized inherited parameter contexts as they can no longer be opened.
* NIFI-8491: Adding inherited param context verification earlier
* NIFI-8491:
- Addressing CI failures by rolling back to some order JS language spec to allow yui-compress to minify and compress.
* NIFI-8491:
- Ensuring selected context sort order is honored.
- Ensuring the Apply button is correctly enabled.
- Showing Pending Apply message when selected Parameter Context changes.
- Ensuring the Parameter's tab is selected now that there is a third tab.
* Updates to inherited param context verification
* Improving validation between parameters/inherited parameters
* NIFI-8491:
- Ensuring the available parameter contexts are loaded whether the edit dialog is opened from the listing or outside of the listing.
* NIFI-8491:
- Fixing conditions we check if the parameter context listing is currently open.
* NIFI-8491:
- Waiting for the parameter contexts to load prior to rendering the parameter context inheritance tab and showing the dialog.
* NIFI-8491:
- Fixing pending apply message clipping.
- Hiding pending apply message after clicking Apply.
Co-authored-by: Joe Gresock <jgresock@gmail.com>
This closes #5371
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterDTO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterDTO.java
index 12a0262..86701b5 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterDTO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/ParameterDTO.java
@@ -32,6 +32,7 @@
private Boolean valueRemoved;
private Set<AffectedComponentEntity> referencingComponents;
private ParameterContextReferenceEntity parameterContext;
+ private Boolean inherited;
@ApiModelProperty("The name of the Parameter")
public String getName() {
@@ -42,6 +43,15 @@
this.name = name;
}
+ @ApiModelProperty(value = "Whether or not the Parameter is inherited from another context", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
+ public Boolean getInherited() {
+ return inherited;
+ }
+
+ public void setInherited(final Boolean inherited) {
+ this.inherited = inherited;
+ }
+
@ApiModelProperty("The description of the Parameter")
public String getDescription() {
return description;
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java
index e311770..51e5664 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java
@@ -35,6 +35,7 @@
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -123,7 +124,7 @@
final Map<String, Parameter> effectiveParameterUpdates = getEffectiveParameterUpdates(currentEffectiveParameters, effectiveProposedParameters);
- verifyCanSetParameters(effectiveParameterUpdates);
+ verifyCanSetParameters(effectiveParameterUpdates, true);
// Update the actual parameters
updateParameters(parameters, updatedParameters, true);
@@ -339,6 +340,17 @@
}
}
+ @Override
+ public Map<String, Parameter> getEffectiveParameterUpdates(final Map<String, Parameter> parameterUpdates, final List<ParameterContext> inheritedParameterContexts) {
+ Objects.requireNonNull(parameterUpdates, "Parameter Updates must be specified");
+ Objects.requireNonNull(inheritedParameterContexts, "Inherited parameter contexts must be specified");
+
+ final Map<ParameterDescriptor, Parameter> currentEffectiveParameters = getEffectiveParameters();
+ final Map<ParameterDescriptor, Parameter> effectiveProposedParameters = getEffectiveParameters(inheritedParameterContexts, getProposedParameters(parameterUpdates), new HashMap<>());
+
+ return getEffectiveParameterUpdates(currentEffectiveParameters, effectiveProposedParameters);
+ }
+
/**
* Constructs an effective view of the parameters, including nested parameters, assuming the given map of parameters.
* This allows an inspection of what parameters would be available if the given parameters were set in this ParameterContext.
@@ -373,15 +385,23 @@
// Loop backwards so that the first ParameterContext in the list will override any parameters later in the list
for(int i = parameterContexts.size() - 1; i >= 0; i--) {
ParameterContext parameterContext = parameterContexts.get(i);
- allOverrides.putAll(overrideParameters(effectiveParameters, parameterContext.getEffectiveParameters(), parameterContext));
+ combineOverrides(allOverrides, overrideParameters(effectiveParameters, parameterContext.getEffectiveParameters(), parameterContext));
}
// Finally, override all child parameters with our own
- allOverrides.putAll(overrideParameters(effectiveParameters, proposedParameters, this));
+ combineOverrides(allOverrides, overrideParameters(effectiveParameters, proposedParameters, this));
return effectiveParameters;
}
+ private void combineOverrides(final Map<ParameterDescriptor, List<Parameter>> existingOverrides, final Map<ParameterDescriptor, List<Parameter>> newOverrides) {
+ for (final Map.Entry<ParameterDescriptor, List<Parameter>> entry : newOverrides.entrySet()) {
+ final ParameterDescriptor key = entry.getKey();
+ final List<Parameter> existingOverrideList = existingOverrides.computeIfAbsent(key, k -> new ArrayList<>());
+ existingOverrideList.addAll(entry.getValue());
+ }
+ }
+
private Map<ParameterDescriptor, List<Parameter>> overrideParameters(final Map<ParameterDescriptor, Parameter> existingParameters,
final Map<ParameterDescriptor, Parameter> overridingParameters,
final ParameterContext overridingContext) {
@@ -457,31 +477,48 @@
}
@Override
+ public void verifyCanUpdateParameterContext(final Map<String, Parameter> parameterUpdates, final List<ParameterContext> inheritedParameterContexts) {
+ verifyCanUpdateParameterContext(parameterUpdates, inheritedParameterContexts, false);
+ }
+
+ private void verifyCanUpdateParameterContext(final Map<String, Parameter> parameterUpdates, final List<ParameterContext> inheritedParameterContexts, final boolean duringUpdate) {
+ if (inheritedParameterContexts == null) {
+ return;
+ }
+ verifyNoCycles(inheritedParameterContexts);
+
+ final Map<ParameterDescriptor, Parameter> currentEffectiveParameters = getEffectiveParameters();
+ final Map<ParameterDescriptor, Parameter> effectiveProposedParameters = getEffectiveParameters(inheritedParameterContexts, getProposedParameters(parameterUpdates), new HashMap<>());
+ final Map<String, Parameter> effectiveParameterUpdates = getEffectiveParameterUpdates(currentEffectiveParameters, effectiveProposedParameters);
+
+ try {
+ verifyCanSetParameters(currentEffectiveParameters, effectiveParameterUpdates, duringUpdate);
+ } catch (final IllegalStateException e) {
+ // Wrap with a more accurate message
+ throw new IllegalStateException(String.format("Could not update inherited Parameter Contexts for Parameter Context [%s] because: %s",
+ name, e.getMessage()), e);
+ }
+ }
+
+ @Override
public void setInheritedParameterContexts(final List<ParameterContext> inheritedParameterContexts) {
- if (inheritedParameterContexts.equals(this.inheritedParameterContexts)) {
+ if (inheritedParameterContexts == null || inheritedParameterContexts.equals(this.inheritedParameterContexts)) {
// No changes
return;
}
+ verifyCanUpdateParameterContext(Collections.emptyMap(), inheritedParameterContexts, true);
+
final Map<String, ParameterUpdate> parameterUpdates = new HashMap<>();
writeLock.lock();
try {
this.version++;
- verifyNoCycles(inheritedParameterContexts);
final Map<ParameterDescriptor, Parameter> currentEffectiveParameters = getEffectiveParameters();
final Map<ParameterDescriptor, Parameter> effectiveProposedParameters = getEffectiveParameters(inheritedParameterContexts);
final Map<String, Parameter> effectiveParameterUpdates = getEffectiveParameterUpdates(currentEffectiveParameters, effectiveProposedParameters);
- try {
- verifyCanSetParameters(currentEffectiveParameters, effectiveParameterUpdates);
- } catch (final IllegalStateException e) {
- // Wrap with a more accurate message
- throw new IllegalStateException(String.format("Could not update inherited Parameter Contexts for Parameter Context [%s] because: %s",
- name, e.getMessage()), e);
- }
-
this.inheritedParameterContexts.clear();
this.inheritedParameterContexts.addAll(inheritedParameterContexts);
@@ -570,17 +607,30 @@
@Override
public void verifyCanSetParameters(final Map<String, Parameter> updatedParameters) {
- verifyCanSetParameters(parameters, updatedParameters);
+ verifyCanSetParameters(updatedParameters, false);
}
- public void verifyCanSetParameters(final Map<ParameterDescriptor, Parameter> currentParameters, final Map<String, Parameter> updatedParameters) {
+ /**
+ * Ensures that it is legal to update the Parameters for this Parameter Context to match the given set of Parameters
+ * @param updatedParameters the updated set of parameters, keyed by Parameter name
+ * @param duringUpdate If true, this check will be treated as if a ParameterContext update is imminent, meaning
+ * referencing components may not be active even for updated values. If false, a parameter
+ * value update will be valid even if referencing components are active because it will be
+ * assumed that these will be stopped prior to the actual update.
+ * @throws IllegalStateException if setting the given set of Parameters is not legal
+ */
+ public void verifyCanSetParameters(final Map<String, Parameter> updatedParameters, final boolean duringUpdate) {
+ verifyCanSetParameters(parameters, updatedParameters, duringUpdate);
+ }
+
+ public void verifyCanSetParameters(final Map<ParameterDescriptor, Parameter> currentParameters, final Map<String, Parameter> updatedParameters, final boolean duringUpdate) {
// Ensure that the updated parameters will not result in changing the sensitivity flag of any parameter.
for (final Map.Entry<String, Parameter> entry : updatedParameters.entrySet()) {
final String parameterName = entry.getKey();
final Parameter parameter = entry.getValue();
if (parameter == null) {
// parameter is being deleted.
- validateReferencingComponents(parameterName, null,"remove");
+ validateReferencingComponents(parameterName, null, duringUpdate);
continue;
}
@@ -589,7 +639,7 @@
}
validateSensitiveFlag(currentParameters, parameter);
- validateReferencingComponents(parameterName, parameter, "update");
+ validateReferencingComponents(parameterName, parameter, duringUpdate);
}
}
@@ -611,11 +661,12 @@
}
}
-
- private void validateReferencingComponents(final String parameterName, final Parameter parameter, final String parameterAction) {
+ private void validateReferencingComponents(final String parameterName, final Parameter parameter, final boolean duringUpdate) {
+ final boolean isDeletion = (parameter == null);
+ final String action = isDeletion ? "remove" : "update";
for (final ProcessorNode procNode : parameterReferenceManager.getProcessorsReferencing(this, parameterName)) {
- if (procNode.isRunning()) {
- throw new IllegalStateException("Cannot " + parameterAction + " parameter '" + parameterName + "' because it is referenced by " + procNode + ", which is currently running");
+ if (procNode.isRunning() && (isDeletion || duringUpdate)) {
+ throw new IllegalStateException("Cannot " + action + " parameter '" + parameterName + "' because it is referenced by " + procNode + ", which is currently running");
}
if (parameter != null) {
@@ -625,9 +676,9 @@
for (final ControllerServiceNode serviceNode : parameterReferenceManager.getControllerServicesReferencing(this, parameterName)) {
final ControllerServiceState serviceState = serviceNode.getState();
- if (serviceState != ControllerServiceState.DISABLED) {
- throw new IllegalStateException("Cannot " + parameterAction + " parameter '" + parameterName + "' because it is referenced by "
- + serviceNode + ", which currently has a state of " + serviceState);
+ if (serviceState != ControllerServiceState.DISABLED && (isDeletion || duringUpdate)) {
+ throw new IllegalStateException("Cannot " + action + " parameter '" + parameterName + "' because it is referenced by "
+ + serviceNode + ", which currently has a state of " + serviceState);
}
if (parameter != null) {
@@ -679,6 +730,22 @@
}
@Override
+ public boolean isAuthorized(final Authorizer authorizer, final RequestAction action, final NiFiUser user) {
+ boolean isAuthorized = ParameterContext.super.isAuthorized(authorizer, action, user);
+
+ if (RequestAction.READ == action) {
+ for (final ParameterContext parameterContext : inheritedParameterContexts) {
+ isAuthorized &= parameterContext.isAuthorized(authorizer, action, user);
+ if (!isAuthorized) {
+ break;
+ }
+ }
+ }
+
+ return isAuthorized;
+ }
+
+ @Override
public Authorizable getParentAuthorizable() {
return new Authorizable() {
@Override
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java
index 670c9ce..62641a6 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java
@@ -110,12 +110,29 @@
Map<ParameterDescriptor, Parameter> getEffectiveParameters();
/**
+ * Returns the resulting map of effective parameter updates if the given parameter updates and inherited parameter contexts were to be applied.
+ * This allows potential changes to be detected before actually applying the parameter updates.
+ * @param parameterUpdates A map from parameter name to updated parameter (null if removal is desired)
+ * @param inheritedParameterContexts An ordered list of parameter contexts to inherit from
+ * @return The effective map of parameter updates that would result if these changes were applied. This includes only parameters that would
+ * be effectively updated or removed, and is mapped by parameter name
+ */
+ Map<String, Parameter> getEffectiveParameterUpdates(Map<String, Parameter> parameterUpdates, List<ParameterContext> inheritedParameterContexts);
+
+ /**
* Returns the ParameterReferenceManager that is associated with this ParameterContext
* @return the ParameterReferenceManager that is associated with this ParameterContext
*/
ParameterReferenceManager getParameterReferenceManager();
/**
+ * Verifies whether the parameter context can be updated with the provided parameters and inherited parameter contexts.
+ * @param parameterUpdates A map from parameter name to updated parameter (null if removal is desired)
+ * @param inheritedParameterContexts the list of ParameterContexts from which to inherit parameters
+ */
+ void verifyCanUpdateParameterContext(Map<String, Parameter> parameterUpdates, List<ParameterContext> inheritedParameterContexts);
+
+ /**
* Updates the ParameterContexts within this context to match the given list of ParameterContexts. All parameter in these
* ParameterContexts are inherited by this ParameterContext, and can be referenced as if they were actually in this ParameterContext.
* The order of the list specifies the priority of parameter overriding, where parameters in the first ParameterContext in the list have
@@ -125,7 +142,7 @@
*
* @param inheritedParameterContexts the list of ParameterContexts from which to inherit parameters, in priority order first to last
* @throws IllegalStateException if the list of ParameterContexts is invalid (in case of a circular reference or
- * in case {@link #verifyCanSetParameters(Map)} verifyCanSetParameters} would throw an exception)
+ * in case {@link #verifyCanSetParameters(Map) verifyCanSetParameters} would throw an exception)
*/
void setInheritedParameterContexts(List<ParameterContext> inheritedParameterContexts);
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index cfe7941..4d31a11 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -91,6 +91,14 @@
import org.apache.nifi.controller.status.ProcessorStatus;
import org.apache.nifi.diagnostics.SystemDiagnostics;
import org.apache.nifi.events.BulletinFactory;
+import org.apache.nifi.flow.VersionedComponent;
+import org.apache.nifi.flow.VersionedConfigurableComponent;
+import org.apache.nifi.flow.VersionedConnection;
+import org.apache.nifi.flow.VersionedControllerService;
+import org.apache.nifi.flow.VersionedFlowCoordinates;
+import org.apache.nifi.flow.VersionedProcessGroup;
+import org.apache.nifi.flow.VersionedProcessor;
+import org.apache.nifi.flow.VersionedPropertyDescriptor;
import org.apache.nifi.groups.ProcessGroup;
import org.apache.nifi.groups.ProcessGroupCounts;
import org.apache.nifi.groups.RemoteProcessGroup;
@@ -119,19 +127,11 @@
import org.apache.nifi.registry.flow.FlowRegistryClient;
import org.apache.nifi.registry.flow.RestBasedFlowRegistry;
import org.apache.nifi.registry.flow.VersionControlInformation;
-import org.apache.nifi.flow.VersionedComponent;
-import org.apache.nifi.flow.VersionedConfigurableComponent;
-import org.apache.nifi.flow.VersionedConnection;
-import org.apache.nifi.flow.VersionedControllerService;
import org.apache.nifi.registry.flow.VersionedFlow;
-import org.apache.nifi.flow.VersionedFlowCoordinates;
import org.apache.nifi.registry.flow.VersionedFlowSnapshot;
import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata;
import org.apache.nifi.registry.flow.VersionedFlowState;
import org.apache.nifi.registry.flow.VersionedParameterContext;
-import org.apache.nifi.flow.VersionedProcessGroup;
-import org.apache.nifi.flow.VersionedProcessor;
-import org.apache.nifi.flow.VersionedPropertyDescriptor;
import org.apache.nifi.registry.flow.diff.ComparableDataFlow;
import org.apache.nifi.registry.flow.diff.ConciseEvolvingDifferenceDescriptor;
import org.apache.nifi.registry.flow.diff.DifferenceType;
@@ -1315,6 +1315,8 @@
group -> group.getParameterContext() != null && (group.getParameterContext().getIdentifier().equals(parameterContextDto.getId())
|| group.getParameterContext().inheritsFrom(parameterContext.getIdentifier())));
+ setEffectiveParameterUpdates(parameterContextDto);
+
final Set<String> updatedParameterNames = getUpdatedParameterNames(parameterContextDto);
// Clear set of Affected Components for each Parameter. This parameter is read-only and it will be populated below.
@@ -1371,6 +1373,39 @@
return dtoFactory.createAffectedComponentEntities(affectedComponents, revisionManager);
}
+ private void setEffectiveParameterUpdates(final ParameterContextDTO parameterContextDto) {
+ final ParameterContext parameterContext = parameterContextDAO.getParameterContext(parameterContextDto.getId());
+
+ final Map<String, Parameter> parameterUpdates = parameterContextDAO.getParameters(parameterContextDto, parameterContext);
+ final List<ParameterContext> inheritedParameterContexts = parameterContextDAO.getInheritedParameterContexts(parameterContextDto);
+ final Map<String, Parameter> proposedParameterUpdates = parameterContext.getEffectiveParameterUpdates(parameterUpdates, inheritedParameterContexts);
+ final Map<String, ParameterEntity> parameterEntities = parameterContextDto.getParameters().stream()
+ .collect(Collectors.toMap(entity -> entity.getParameter().getName(), Function.identity()));
+ parameterContextDto.getParameters().clear();
+
+ for (final Map.Entry<String, Parameter> entry : proposedParameterUpdates.entrySet()) {
+ final String parameterName = entry.getKey();
+ final Parameter parameter = entry.getValue();
+ final ParameterEntity parameterEntity;
+ if (parameterEntities.containsKey(parameterName)) {
+ parameterEntity = parameterEntities.get(parameterName);
+ } else if (parameter == null) {
+ parameterEntity = new ParameterEntity();
+ final ParameterDTO parameterDTO = new ParameterDTO();
+ parameterDTO.setName(parameterName);
+ parameterEntity.setParameter(parameterDTO);
+ } else {
+ parameterEntity = dtoFactory.createParameterEntity(parameterContext, parameter, revisionManager, parameterContextDAO);
+ }
+
+ // Parameter is inherited if either this is the removal of a parameter not directly in this context, or it's parameter not specified directly in the DTO
+ final boolean isInherited = (parameter == null && !parameterContext.getParameters().containsKey(new ParameterDescriptor.Builder().name(parameterName).build()))
+ || (parameter != null && !parameterEntities.containsKey(parameterName));
+ parameterEntity.getParameter().setInherited(isInherited);
+ parameterContextDto.getParameters().add(parameterEntity);
+ }
+ }
+
private void addReferencingComponents(final ControllerServiceNode service, final Set<ComponentNode> affectedComponents, final List<ParameterDTO> affectedParameterDtos,
final boolean includeInactive) {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
index 1d6d8bb..9d5ad9e 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/EntityFactory.java
@@ -575,7 +575,7 @@
final ParameterContextEntity entity = new ParameterContextEntity();
entity.setRevision(revision);
if (dto != null) {
- entity.setPermissions(permissions);;
+ entity.setPermissions(permissions);
entity.setId(dto.getId());
if (permissions != null && permissions.getCanRead()) {
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ParameterContextDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ParameterContextDAO.java
index 07daab6..51f6d96 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ParameterContextDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/ParameterContextDAO.java
@@ -16,10 +16,13 @@
*/
package org.apache.nifi.web.dao;
+import org.apache.nifi.parameter.Parameter;
import org.apache.nifi.parameter.ParameterContext;
import org.apache.nifi.parameter.ParameterContextLookup;
import org.apache.nifi.web.api.dto.ParameterContextDTO;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
public interface ParameterContextDAO extends ParameterContextLookup {
@@ -40,6 +43,14 @@
ParameterContext createParameterContext(ParameterContextDTO parameterContextDto);
/**
+ * Returns a map from parameter name to intended parameter, given the DTO.
+ * @param parameterContextDto A parameter context DTO containing parameter updates
+ * @param context The existing parameter context
+ * @return The resulting parameter map containing updated parameters (or removals)
+ */
+ Map<String, Parameter> getParameters(ParameterContextDTO parameterContextDto, ParameterContext context);
+
+ /**
* Gets all of the parameter contexts.
*
* @return The parameter contexts
@@ -55,6 +66,13 @@
ParameterContext updateParameterContext(ParameterContextDTO parameterContextDto);
/**
+ * Returns a list of the inherited parameter contexts proposed by the DTO.
+ * @param parameterContextDto The parameter context DTO
+ * @return a list of the inherited parameter contexts proposed by the DTO
+ */
+ List<ParameterContext> getInheritedParameterContexts(ParameterContextDTO parameterContextDto);
+
+ /**
* Determines whether this parameter context can be updated.
*
* @param parameterContextDto dto
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java
index 1c8c3a2..d4a69ac 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java
@@ -20,11 +20,8 @@
import org.apache.nifi.authorization.RequestAction;
import org.apache.nifi.authorization.user.NiFiUser;
import org.apache.nifi.authorization.user.NiFiUserUtils;
-import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.controller.ComponentNode;
import org.apache.nifi.controller.FlowController;
import org.apache.nifi.controller.ProcessorNode;
-import org.apache.nifi.controller.PropertyConfiguration;
import org.apache.nifi.controller.flow.FlowManager;
import org.apache.nifi.controller.service.ControllerServiceNode;
import org.apache.nifi.controller.service.ControllerServiceState;
@@ -32,8 +29,6 @@
import org.apache.nifi.parameter.Parameter;
import org.apache.nifi.parameter.ParameterContext;
import org.apache.nifi.parameter.ParameterDescriptor;
-import org.apache.nifi.parameter.ParameterReference;
-import org.apache.nifi.parameter.ParameterReferenceManager;
import org.apache.nifi.web.ResourceNotFoundException;
import org.apache.nifi.web.api.dto.ParameterContextDTO;
import org.apache.nifi.web.api.dto.ParameterContextReferenceDTO;
@@ -73,7 +68,7 @@
if (inheritedParameterContexts != null) {
resolveInheritedParameterContexts(parameterContextDto);
// This will throw an exception if one is not found
- inheritedParameterContexts.stream().forEach(entity -> flowManager.getParameterContextManager()
+ inheritedParameterContexts.forEach(entity -> flowManager.getParameterContextManager()
.getParameterContext(entity.getComponent().getId()));
}
authorizeReferences(parameterContextDto);
@@ -136,7 +131,8 @@
}
}
- private Map<String, Parameter> getParameters(final ParameterContextDTO parameterContextDto, final ParameterContext context) {
+ @Override
+ public Map<String, Parameter> getParameters(final ParameterContextDTO parameterContextDto, final ParameterContext context) {
final Set<ParameterEntity> parameterEntities = parameterContextDto.getParameters();
if (parameterEntities == null) {
return Collections.emptyMap();
@@ -146,6 +142,11 @@
for (final ParameterEntity parameterEntity : parameterEntities) {
final ParameterDTO parameterDto = parameterEntity.getParameter();
+ // Inherited parameters are only included for referencing components, but we should not save them as direct parameters
+ if (parameterDto.getInherited() != null && parameterDto.getInherited()) {
+ continue;
+ }
+
if (parameterDto.getName() == null) {
throw new IllegalArgumentException("Cannot specify a Parameter without a name");
}
@@ -226,7 +227,8 @@
return context;
}
- private List<ParameterContext> getInheritedParameterContexts(final ParameterContextDTO parameterContextDto) {
+ @Override
+ public List<ParameterContext> getInheritedParameterContexts(final ParameterContextDTO parameterContextDto) {
resolveInheritedParameterContexts(parameterContextDto);
final List<ParameterContext> inheritedParameterContexts = new ArrayList<>();
@@ -243,76 +245,10 @@
verifyInheritedParameterContextRefs(parameterContextDto);
final ParameterContext currentContext = getParameterContext(parameterContextDto.getId());
- for (final ParameterEntity parameterEntity : parameterContextDto.getParameters()) {
- final ParameterDTO parameterDto = parameterEntity.getParameter();
- final String parameterName = parameterDto.getName();
- final ParameterReferenceManager referenceManager = currentContext.getParameterReferenceManager();
- for (final ProcessorNode processor : referenceManager.getProcessorsReferencing(currentContext, parameterName)) {
- verifyParameterUpdate(parameterDto, processor, currentContext.getName(), verifyComponentStates, processor.isRunning(), "Processor that is running");
- }
-
- final Set<ControllerServiceNode> referencingServices = referenceManager.getControllerServicesReferencing(currentContext, parameterName);
- for (final ControllerServiceNode serviceNode : referencingServices) {
- final ControllerServiceState serviceState = serviceNode.getState();
- final boolean serviceActive = serviceState != ControllerServiceState.DISABLED;
-
- verifyParameterUpdate(parameterDto, serviceNode, currentContext.getName(), verifyComponentStates, serviceActive,
- "Controller Service [id=" + serviceNode.getIdentifier() + "] with a state of " + serviceState + " (state expected to be DISABLED)");
- }
- }
- }
-
- private void verifyParameterUpdate(final ParameterDTO parameterDto, final ComponentNode component, final String contextName,
- final boolean verifyComponentStates, final boolean active, final String activeExplanation) {
-
- final String parameterName = parameterDto.getName();
- final Boolean parameterSensitive = parameterDto.getSensitive();
- final boolean parameterDeletion = parameterDto.getDescription() == null && parameterDto.getSensitive() == null && parameterDto.getValue() == null;
-
- // For any parameter that is added or modified, we need to ensure that the new configuration will not result in a Sensitive Parameter being referenced by a non-Sensitive Property
- // or a Non-Sensitive Parameter being referenced by a Sensitive Property.
- // Additionally, if 'verifyComponentStates' or parameter is being deleted, we must ensure that any component that references a value that is to be updated
- // is stopped (if a processor) or disabled (if a controller service).
- for (final Map.Entry<PropertyDescriptor, PropertyConfiguration> entry : component.getProperties().entrySet()) {
- final PropertyConfiguration configuration = entry.getValue();
- if (configuration == null) {
- continue;
- }
-
- for (final ParameterReference reference : configuration.getParameterReferences()) {
- final String referencedParameterName = reference.getParameterName();
- if (referencedParameterName.equals(parameterName)) {
- if (entry.getKey().isSensitive() && !parameterDeletion && !Boolean.TRUE.equals(parameterSensitive)) {
- throw new IllegalStateException("Cannot update Parameter Context " + contextName + " because the update would add a Non-Sensitive Parameter " +
- "named '" + parameterName + "' but this Parameter already is referenced by a Sensitive Property.");
- }
-
- if (!entry.getKey().isSensitive() && !parameterDeletion && Boolean.TRUE.equals(parameterSensitive)) {
- throw new IllegalStateException("Cannot update Parameter Context " + contextName + " because the update would add a Sensitive Parameter named " +
- "'" + parameterName + "' but this Parameter already is referenced by a Non-Sensitive Property.");
- }
-
- if (active && (verifyComponentStates || parameterDeletion)) {
- if (parameterDeletion) {
- // First check if the actual parameter context is now missing the parameter: it may not be,
- // if the parameter is inherited from another context
- final ProcessGroup processGroup = flowManager.getGroup(component.getProcessGroupIdentifier());
- final ParameterContext parameterContext = processGroup.getParameterContext();
- final ParameterDescriptor parameterDescriptor = new ParameterDescriptor.Builder()
- .name(parameterName).build();
- if (!parameterContext.hasEffectiveValueIfRemoved(parameterDescriptor)) {
- throw new IllegalStateException("Cannot update Parameter Context " + contextName + " because the " + parameterName + " Parameter is being referenced by a " +
- activeExplanation + ".");
- }
- } else {
- throw new IllegalStateException("Cannot update Parameter Context " + contextName + " because it has Parameters that are being referenced by a " +
- activeExplanation + ".");
- }
- }
- }
- }
- }
+ final List<ParameterContext> inheritedParameterContexts = getInheritedParameterContexts(parameterContextDto);
+ final Map<String, Parameter> parameters = parameterContextDto.getParameters() == null ? Collections.emptyMap() : getParameters(parameterContextDto, currentContext);
+ currentContext.verifyCanUpdateParameterContext(parameters, inheritedParameterContexts);
}
/**
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-parameter-context-dialog.jsp b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-parameter-context-dialog.jsp
index d1e10a5..c788921 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-parameter-context-dialog.jsp
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/WEB-INF/partials/canvas/new-parameter-context-dialog.jsp
@@ -20,7 +20,7 @@
<div class="parameter-context-tab-container dialog-content">
<div id="parameter-context-tabs" class="tab-container"></div>
<div id="parameter-context-tabs-content">
- <div id="parameter-context-standard-settings-tab-content" class="configuration-tab">
+ <div id="parameter-context-standard-settings-tab-content" class="split-65-35 configuration-tab">
<div class="settings-left">
<div id="parameter-context-id-setting" class="setting hidden">
<div class="setting-name">Id</div>
@@ -57,7 +57,7 @@
</div>
</div>
</div>
- <div id="parameter-context-parameters-tab-content" class="configuration-tab">
+ <div id="parameter-context-parameters-tab-content" class="split-65-35 configuration-tab">
<div class="settings-left">
<div class="edit-mode">
<div id="add-parameter"><button class="button fa fa-plus"></button></div>
@@ -96,8 +96,51 @@
</div>
</div>
</div>
+ <div id="parameter-context-inheritance-tab-content" class="configuration-tab">
+ <div id="parameter-context-inheritance-container">
+ <div class="settings-left">
+ <div class="setting">
+ <div class="setting-name">
+ Available Parameter Contexts
+ <div class="fa fa-question-circle" alt="Info" title="Available Parameter Contexts that could be inherited from."></div>
+ </div>
+ <div class="setting-field">
+ <ol id="parameter-context-available"></ol>
+ </div>
+ </div>
+ </div>
+ <div class="spacer"> </div>
+ <div class="settings-right">
+ <div class="setting">
+ <div class="setting-name">
+ Selected Parameter Context
+ <div class="fa fa-question-circle" alt="Info" title="Parameter Contexts selected for inheritance."></div>
+ </div>
+ <div class="setting-field">
+ <ol id="parameter-context-selected"></ol>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="parameter-context-inheritance-container-read-only" style="display: none;">
+ <div class="settings-left">
+ <div class="setting">
+ <div class="setting-name">
+ Selected Parameter Context
+ <div class="fa fa-question-circle" alt="Info" title="Parameter Contexts selected for inheritance."></div>
+ </div>
+ <div class="setting-field">
+ <ol id="parameter-context-selected-read-only"></ol>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
</div>
</div>
+ <div id="inherited-parameter-contexts-message" class="ellipsis hidden">
+ Inherited Parameter Contexts have been modified. Updated listing of Parameters is pending apply.
+ </div>
</div>
<div id="parameter-dialog" class="dialog cancellable hidden">
<div class="dialog-content">
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 32b2d5c..3fa0107 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
@@ -333,6 +333,7 @@
top: 550px;
left: 20px;
font-size: 13px;
+ color: #775351;
max-width: calc(100% - 230px);
z-index: 999;
}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-context-dialog.css b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-context-dialog.css
index d24281d..8b4ef45 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-context-dialog.css
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/css/new-parameter-context-dialog.css
@@ -32,12 +32,12 @@
display: none;
}
-#parameter-context-dialog div.settings-left {
+.parameter-context-tabs-content.split-65-35 div.settings-left {
float: left;
width: 65%;
}
-#parameter-context-dialog div.settings-right {
+.parameter-context-tabs-content.split-65-35 div.settings-right {
float: left;
width: 33%;
height: 100%;
@@ -134,3 +134,79 @@
white-space: nowrap;
margin-left: 6px;
}
+
+/* inheritance */
+
+#parameter-context-available, #parameter-context-selected {
+ list-style-type: none;
+ margin: 0px;
+ background: #eaeef0;
+ min-height: 32px;
+ margin-bottom: 5px;
+ padding: 2px;
+}
+
+#parameter-context-available li, #parameter-context-selected li {
+ margin: 2px;
+ padding: 5px;
+ cursor: pointer;
+ font-size: 1em;
+ height: 12px;
+ overflow: hidden;
+}
+
+#parameter-context-available li.unauthorized, #parameter-context-selected li.unauthorized {
+ color: #a8a8a8;
+ cursor: not-allowed;
+}
+
+#parameter-context-available li {
+ background: #fff;
+ border-top: 1px solid #CCDADB;
+ border-right: 1px solid #CCDADB;
+ border-bottom: 1px solid #CCDADB;
+ border-left: 1px solid #CCDADB;
+ color: #004849;
+ font-weight: 500;
+ cursor: grab;
+ line-height: 32px;
+ height: 32px;
+ padding: 0px 10px;
+ box-shadow:0 1px 1px rgba(0,0,0,0.4);
+}
+
+#parameter-context-selected li {
+ background: #355B6A;
+ border-top: 1px solid #AAC1CE;
+ border-right: 1px solid #85A6B8;
+ border-bottom: 1px solid #618BA3;
+ border-left: 1px solid #85A6B8;
+ color: #fff;
+ font-weight: bold;
+ line-height: 32px;
+ height: 32px;
+ padding: 0px 10px;
+}
+
+#parameter-context-selected .draggable-control {
+ float: right;
+}
+
+#parameter-context-selected .fa-remove, #parameter-context-selected .fa-question-circle {
+ color: #fff;
+}
+
+#parameter-context-selected-read-only {
+ list-style: inside none decimal;
+}
+
+#inherited-parameter-contexts-message {
+ position: absolute;
+ top: 550px;
+ left: 20px;
+ font-size: 13px;
+ line-height: normal;
+ color: #775351;
+ max-width: calc(100% - 230px);
+ z-index: 999;
+}
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-contexts.js b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-contexts.js
index d8d3a59..cae8a55 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-contexts.js
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-contexts.js
@@ -219,6 +219,7 @@
parameterData.setItems([]);
resetUsage();
+ resetInheritance();
// reset the last selected parameter
lastSelectedId = null;
@@ -301,6 +302,24 @@
};
/**
+ * Marshals the inherited parameter contexts.
+ */
+ var marshalInheritedParameterContexts = function () {
+ var inheritedParameterContextIds = $('#parameter-context-selected').sortable('toArray');
+
+ return inheritedParameterContextIds.map(function (id) {
+ var name = $('#parameter-context-selected').find('li#' + id + ' span').text();
+ return {
+ id: id,
+ component: {
+ id: id,
+ name: name
+ }
+ }
+ });
+ };
+
+ /**
* Handles outstanding changes.
*
* @returns {deferred}
@@ -1062,10 +1081,19 @@
var parameters = marshalParameters();
var proposedParamContextName = $('#parameter-context-name').val();
var proposedParamContextDesc = $('#parameter-context-description-field').val();
+ var inheritedParameterContexts = marshalInheritedParameterContexts();
+
+ var inheritedParameterContextEquals = isInheritedParameterContextEquals(parameterContextEntity, inheritedParameterContexts);
+ if (inheritedParameterContextEquals) {
+ $('#inherited-parameter-contexts-message').addClass('hidden');
+ } else {
+ $('#inherited-parameter-contexts-message').removeClass('hidden');
+ }
if (_.isEmpty(parameters) &&
proposedParamContextName === _.get(parameterContextEntity, 'component.name') &&
- proposedParamContextDesc === _.get(parameterContextEntity, 'component.description')) {
+ proposedParamContextDesc === _.get(parameterContextEntity, 'component.description') &&
+ inheritedParameterContextEquals) {
return false;
} else {
@@ -1074,6 +1102,24 @@
};
/**
+ * Determines if the proposed inherited parameter contexts are equal to the current configuration.
+ *
+ * @param parameterContextEntity
+ * @param proposedInheritedParameterContexts
+ * @returns {*}
+ */
+ var isInheritedParameterContextEquals = function (parameterContextEntity, proposedInheritedParameterContexts) {
+ var configuredInheritedParameterContexts = parameterContextEntity.component.inheritedParameterContexts.map(function (inheritedParameterContext) {
+ return inheritedParameterContext.id;
+ });
+ var mappedProposedInheritedParameterContexts = proposedInheritedParameterContexts.map(function (proposedInheritedParameterContext) {
+ return proposedInheritedParameterContext.id;
+ });
+
+ return _.isEqual(configuredInheritedParameterContexts, mappedProposedInheritedParameterContexts);
+ }
+
+ /**
* Updates parameter contexts by issuing an update request and polling until it's completion.
*
* @param parameterContextEntity
@@ -1081,12 +1127,15 @@
*/
var updateParameterContext = function (parameterContextEntity) {
var parameters = marshalParameters();
+ var inheritedParameterContexts = marshalInheritedParameterContexts();
if (parameters.length === 0) {
// nothing to update
parameterContextEntity.component.parameters = [];
- if ($('#parameter-context-name').val() === parameterContextEntity.component.name &&
- $('#parameter-context-description-field').val() === parameterContextEntity.component.description) {
+
+ if ($('#parameter-context-name').val() === _.get(parameterContextEntity, 'component.name') &&
+ $('#parameter-context-description-field').val() === _.get(parameterContextEntity, 'component.description') &&
+ isInheritedParameterContextEquals(parameterContextEntity, inheritedParameterContexts)) {
close();
return;
@@ -1095,6 +1144,9 @@
parameterContextEntity.component.parameters = parameters;
}
+ // include the inherited parameter contexts
+ parameterContextEntity.component.inheritedParameterContexts = inheritedParameterContexts;
+
parameterContextEntity.component.name = $('#parameter-context-name').val();
parameterContextEntity.component.description = $('#parameter-context-description-field').val();
@@ -1249,8 +1301,8 @@
});
// update the parameter context table if displayed
- var parameterContextGrid = $('#parameter-contexts-table').data('gridInstance');
- if (nfCommon.isDefinedAndNotNull(parameterContextGrid)) {
+ if ($('#parameter-contexts-table').is(':visible')) {
+ var parameterContextGrid = $('#parameter-contexts-table').data('gridInstance');
var parameterContextData = parameterContextGrid.getData();
$.extend(parameterContextEntity, {
@@ -1289,6 +1341,9 @@
$('#parameter-context-tabs').hide();
$('#parameter-context-update-status').show();
+ // hide the pending apply message for parameter context
+ $('#inherited-parameter-contexts-message').addClass('hidden')
+
pollUpdateRequest(response);
}).fail(handleAjaxFailure);
}).promise();
@@ -1530,6 +1585,57 @@
}
};
+ /**
+ * Load the parameter context inheritance tab for the current parameterContextEntity. The current parameterContextEntity could be
+ * null if this is a new parameter context.
+ *
+ * @param parameterContextEntity the parameter context being edited or null if new
+ * @param readOnly whether the controls should be read only
+ * @param parameterContexts all parameter contexts
+ */
+ var loadParameterContextInheritance = function (parameterContextEntity, readOnly, parameterContexts) {
+ // consider each parameter context and add to the listing of available or selected contexts based on the supplied parameterContextEntity
+ $.each(parameterContexts, function (i, availableParameterContext) {
+ // don't support inheriting from the current context
+ var isCurrent = nfCommon.isNull(parameterContextEntity) ? false : availableParameterContext.id === parameterContextEntity.id;
+
+ // determine if this available parameter context is already selected
+ var isSelected = nfCommon.isNull(parameterContextEntity) ? false : parameterContextEntity.component.inheritedParameterContexts.some(function (selectedParameterContext) {
+ return availableParameterContext.id === selectedParameterContext.id;
+ });
+
+ if (readOnly) {
+ if (isSelected) {
+ $('#parameter-context-selected-read-only').append($('<li></li>').text(availableParameterContext.component.name));
+ }
+ } else {
+ if (isSelected) {
+ addParameterContextInheritanceControl(availableParameterContext, true);
+ } else if (!isCurrent) {
+ addParameterContextInheritanceControl(availableParameterContext, false);
+ }
+ }
+ });
+
+ if (!nfCommon.isNull(parameterContextEntity)) {
+ sortSelectedParameterContexts(parameterContextEntity);
+ }
+
+ sortAvailableParameterContexts();
+
+ if (readOnly) {
+ if ($('#parameter-context-selected-read-only').is(':empty')) {
+ $('#parameter-context-selected-read-only').append($('<span class="unset">No value set</span>'));
+ }
+
+ $('#parameter-context-inheritance-container-read-only').show();
+ $('#parameter-context-inheritance-container').hide();
+ } else {
+ $('#parameter-context-inheritance-container-read-only').hide();
+ $('#parameter-context-inheritance-container').show();
+ }
+ };
+
var resetUsage = function () {
// empty the containers
var processorContainer = $('.parameter-context-referencing-processors');
@@ -1562,6 +1668,125 @@
};
/**
+ * Reset the inheritance tab.
+ */
+ var resetInheritance = function () {
+ $('#parameter-context-available').empty();
+ $('#parameter-context-selected').empty();
+ $('#parameter-context-selected-read-only').empty();
+ $('#inherited-parameter-contexts-message').addClass('hidden');
+ };
+
+ /**
+ * Sorts the available parameter contexts.
+ */
+ var sortAvailableParameterContexts = function () {
+ var availableParameterContextList = $('#parameter-context-available');
+ availableParameterContextList.children('li')
+ .detach()
+ .sort(function (aElement, bElement) {
+ var a = $(aElement);
+ var b = $(bElement);
+
+ // put unauthorized last
+ if (a.hasClass('unauthorized') && b.hasClass('unauthorized')) {
+ return 0;
+ } else if (a.hasClass('unauthorized')) {
+ return 1;
+ } else if (b.hasClass('unauthorized')) {
+ return -1;
+ }
+
+ var nameA = a.text();
+ var nameB = b.text();
+ return nameA.localeCompare(nameB);
+ })
+ .appendTo(availableParameterContextList);
+ };
+
+ /**
+ * Sorts the selected parameter context array based on the current parameter context entity.
+ *
+ * @param {object} selectedParameterContexts
+ */
+ var sortSelectedParameterContexts = function (parameterContextEntity) {
+ var selectedInheritedParameterContexts = parameterContextEntity.component.inheritedParameterContexts;
+
+ var selectedParameterContextList = $('#parameter-context-selected');
+ selectedParameterContextList.children('li')
+ .detach()
+ .sort(function (aElement, bElement) {
+ var a = $(aElement);
+ var b = $(bElement);
+
+ var findA = function (selectedInheritedParameterContext) {
+ return a.attr('id') === selectedInheritedParameterContext.id;
+ };
+ var findB = function (selectedInheritedParameterContext) {
+ return b.attr('id') === selectedInheritedParameterContext.id;
+ };
+
+ return selectedInheritedParameterContexts.findIndex(findA) - selectedInheritedParameterContexts.findIndex(findB);
+ })
+ .appendTo(selectedParameterContextList);
+ }
+
+ /**
+ * Adds the specified parameter context to the list of available parameter contexts.
+ *
+ * @argument {jQuery} container The container for the parameter context
+ * @argument {object} parameterContext An available parameter context
+ * @argument {boolean} isSelected Whether the parameter context is selected (which is used to decide whether to provide a remove control)
+ */
+ var addParameterContextInheritanceControl = function (parameterContext, isSelected) {
+ var label = parameterContext.id;
+ if (parameterContext.permissions.canRead) {
+ label = parameterContext.component.name;
+ }
+
+ // add the parameter context to the specified list
+ var parameterContextElement = $('<li></li>').append($('<span style="float: left;"></span>').text(label)).attr('id', parameterContext.id).addClass('ui-state-default');
+ if (!parameterContext.permissions.canRead) {
+ parameterContextElement.addClass('unauthorized');
+ } else {
+ // add the description if applicable
+ if (!nfCommon.isBlank(parameterContext.component.description)) {
+ $('<div class="fa fa-question-circle"></div>').appendTo(parameterContextElement).qtip($.extend({
+ content: nfCommon.escapeHtml(parameterContext.component.description)
+ }, nfCommon.config.tooltipConfig));
+ }
+
+ if (isSelected) {
+ addControlsForSelectedParameterContext(parameterContextElement);
+ }
+ }
+ parameterContextElement.appendTo(isSelected ? '#parameter-context-selected' : '#parameter-context-available');
+ };
+
+ /**
+ * Adds the controls to the specified selected draggable element.
+ *
+ * @argument {jQuery} draggableElement
+ */
+ var addControlsForSelectedParameterContext = function (draggableElement) {
+ var removeIcon = $('<div class="draggable-control"><div class="fa fa-remove"></div></div>')
+ .on('click', function () {
+ // remove the remove ice
+ removeIcon.remove();
+
+ // restore to the available parameter contexts
+ $('#parameter-context-available').append(draggableElement);
+
+ // resort the available parameter contexts
+ sortAvailableParameterContexts();
+
+ // update the buttons to possibly trigger the disabled state
+ $('#parameter-context-dialog').modal('refreshButtons');
+ })
+ .appendTo(draggableElement);
+ }
+
+ /**
* Performs the filtering.
*
* @param {object} item The item subject to filtering
@@ -1914,6 +2139,9 @@
}, {
name: 'Parameters',
tabContentId: 'parameter-context-parameters-tab-content'
+ }, {
+ name: 'Inheritance',
+ tabContentId: 'parameter-context-inheritance-tab-content'
}],
select: function () {
// update the parameters table size in case this is the first time its rendered
@@ -2125,22 +2353,23 @@
};
/**
+ * Fetches the available Parameter Contexts.
+ */
+ var fetchParameterContexts = function () {
+ return $.ajax({
+ type: 'GET',
+ url: '../nifi-api/flow/parameter-contexts',
+ dataType: 'json'
+ });
+ };
+
+ /**
* Loads the parameter contexts.
*
* @param parameterContextToSelect id of the parameter context to select in the grid
*/
var loadParameterContexts = function (parameterContextToSelect) {
- var parameterContexts = $.Deferred(function (deferred) {
- $.ajax({
- type: 'GET',
- url: '../nifi-api/flow/parameter-contexts',
- dataType: 'json'
- }).done(function (response) {
- deferred.resolve(response);
- }).fail(function (xhr, status, error) {
- deferred.reject(xhr, status, error);
- });
- }).promise();
+ var parameterContexts = fetchParameterContexts();
// return a deferred for all parts of the parameter contexts
return $.when(parameterContexts).done(function (response) {
@@ -2382,6 +2611,7 @@
// create a new parameter context
$('#new-parameter-context').on('click', function () {
resetUsage();
+ resetInheritance();
// new parameter contexts do not have an ID to show
if (!$('#parameter-context-id-setting').hasClass('hidden')) {
@@ -2424,10 +2654,64 @@
}
}]).modal('show');
+ var parameterContextsGrid = $('#parameter-contexts-table').data('gridInstance');
+ var parameterContextsData = parameterContextsGrid.getData();
+ var parameterContexts = parameterContextsData.getItems();
+
+ loadParameterContextInheritance(null, false, parameterContexts);
+
// set the initial focus
$('#parameter-context-name').focus();
});
+ // work around for https://bugs.jqueryui.com/ticket/6054
+ var shouldAllowDrop = true;
+
+ // make the parameter context containers sortable
+ $('#parameter-context-available').sortable({
+ containment: $('#parameter-context-inheritance-tab-content'),
+ cancel: '.unauthorized',
+ connectWith: '#parameter-context-selected',
+ placeholder: 'available',
+ scroll: true,
+ opacity: 0.6,
+ beforeStop: function (event, ui) {
+ if ($('#parameter-context-available').find('.ui-sortable-placeholder').length) {
+ shouldAllowDrop = false;
+ }
+ },
+ stop: function (event, ui) {
+ const allowDrop = shouldAllowDrop;
+ shouldAllowDrop = true;
+ return allowDrop;
+ }
+ });
+ $('#parameter-context-selected').sortable({
+ containment: $('#parameter-context-inheritance-tab-content'),
+ cancel: '.unauthorized',
+ placeholder: 'selected',
+ scroll: true,
+ opacity: 0.6,
+ receive: function (event, ui) {
+ addControlsForSelectedParameterContext(ui.item);
+ },
+ update: function (event, ui) {
+ // update the buttons to possibly trigger the disabled state
+ $('#parameter-context-dialog').modal('refreshButtons');
+ }
+ });
+ $('#parameter-context-available, #parameter-context-selected').disableSelection();
+
+ // add a listener that will handle dblclick for all available authorized parameter context children
+ $(document).on('dblclick', '#parameter-context-available li:not(".unauthorized")', function() {
+ var availableParameterContextElement = $(this).detach().appendTo($('#parameter-context-selected'));
+
+ addControlsForSelectedParameterContext(availableParameterContextElement);
+
+ // update the buttons to possibly trigger the disabled state
+ $('#parameter-context-dialog').modal('refreshButtons');
+ });
+
// initialize the new parameter context dialog
initNewParameterContextDialog();
@@ -2437,7 +2721,7 @@
if ($('#parameter-referencing-components-container').is(':visible')) {
updateReferencingComponentsBorder($('#parameter-referencing-components-container'));
}
- })
+ });
},
/**
@@ -2460,6 +2744,9 @@
})
};
+ // include the inherited parameter contexts
+ parameterContextEntity.component.inheritedParameterContexts = marshalInheritedParameterContexts();
+
var addContext = $.ajax({
type: 'POST',
url: config.urls.parameterContexts,
@@ -2467,10 +2754,9 @@
dataType: 'json',
contentType: 'application/json'
}).done(function (parameterContextEntity) {
- // add the item
- var parameterContextGrid = $('#parameter-contexts-table').data('gridInstance');
-
- if (nfCommon.isDefinedAndNotNull(parameterContextGrid)) {
+ // update the table if displayed
+ if ($('#parameter-contexts-table').is(':visible')) {
+ var parameterContextGrid = $('#parameter-contexts-table').data('gridInstance');
var parameterContextData = parameterContextGrid.getData();
parameterContextData.addItem(parameterContextEntity);
@@ -2539,6 +2825,19 @@
dataType: 'json'
});
+ var parameterContextsDeferred;
+ if ($('#parameter-contexts-table').is(':visible')) {
+ parameterContextsDeferred = $.Deferred(function (deferred) {
+ var parameterContextsGrid = $('#parameter-contexts-table').data('gridInstance');
+ var parameterContextsData = parameterContextsGrid.getData();
+ deferred.resolve({
+ parameterContexts: parameterContextsData.getItems()
+ });
+ }).promise();
+ } else {
+ parameterContextsDeferred = fetchParameterContexts();
+ }
+
// once everything is loaded, show the dialog
reloadContext.done(function (parameterContextEntity) {
var canWrite = _.get(parameterContextEntity, 'permissions.canWrite', false);
@@ -2579,71 +2878,74 @@
loadParameters(parameterContextEntity, parameterToSelect, readOnly || !canWrite);
- var editModeButtonModel = [{
- buttonText: 'Apply',
- color: {
- base: '#728E9B',
- hover: '#004849',
- text: '#ffffff'
- },
- disabled: function () {
- if ($('#parameter-context-name').val() !== '' && hasParameterContextChanged(currentParameterContextEntity)) {
+ // load the parameter contexts in order to render all available parameter contexts
+ parameterContextsDeferred.done(function (response) {
+ loadParameterContextInheritance(parameterContextEntity, readOnly || !canWrite, response.parameterContexts);
+
+ var editModeButtonModel = [{
+ buttonText: 'Apply',
+ color: {
+ base: '#728E9B',
+ hover: '#004849',
+ text: '#ffffff'
+ },
+ disabled: function () {
+ if ($('#parameter-context-name').val() !== '' && hasParameterContextChanged(currentParameterContextEntity)) {
+ return false;
+ }
+ return true;
+ },
+ handler: {
+ click: function () {
+ updateParameterContext(currentParameterContextEntity);
+ }
+ }
+ }, {
+ buttonText: 'Cancel',
+ color: {
+ base: '#E3E8EB',
+ hover: '#C7D2D7',
+ text: '#004849'
+ },
+ handler: {
+ click: function () {
+ $(this).modal('hide');
+ }
+ }
+ }];
+
+ var readOnlyButtonModel = [{
+ buttonText: 'Ok',
+ color: {
+ base: '#728E9B',
+ hover: '#004849',
+ text: '#ffffff'
+ },
+ disabled: function () {
return false;
+ },
+ handler: {
+ click: function () {
+ $(this).modal('hide');
+ }
}
- return true;
- },
- handler: {
- click: function () {
- updateParameterContext(currentParameterContextEntity);
- }
- }
- }, {
- buttonText: 'Cancel',
- color: {
- base: '#E3E8EB',
- hover: '#C7D2D7',
- text: '#004849'
- },
- handler: {
- click: function () {
- $(this).modal('hide');
- }
- }
- }];
+ }];
- var readOnlyButtonModel = [{
- buttonText: 'Ok',
- color: {
- base: '#728E9B',
- hover: '#004849',
- text: '#ffffff'
- },
- disabled: function () {
- return false;
- },
- handler: {
- click: function () {
- $(this).modal('hide');
- }
- }
- }];
+ // show the context
+ $('#parameter-context-dialog')
+ .modal('setHeaderText', canWrite ? 'Update Parameter Context' : 'View Parameter Context')
+ .modal('setButtonModel', canWrite ? editModeButtonModel : readOnlyButtonModel)
+ .modal('show');
+ // select the parameters tab
+ $('#parameter-context-tabs').find('li:eq(1)').click();
- // show the context
- $('#parameter-context-dialog')
- .modal('setHeaderText', canWrite ? 'Update Parameter Context' : 'View Parameter Context')
- .modal('setButtonModel', canWrite ? editModeButtonModel : readOnlyButtonModel)
- .modal('show');
+ // check if border is necessary
+ updateReferencingComponentsBorder($('#parameter-referencing-components-container'));
- // select the parameters tab
- $('#parameter-context-tabs').find('li:last').click();
-
- // check if border is necessary
- updateReferencingComponentsBorder($('#parameter-referencing-components-container'));
-
- // show the border if necessary
- updateReferencingComponentsBorder(referencingComponentsContainer);
-
+ // show the border if necessary
+ updateReferencingComponentsBorder(referencingComponentsContainer);
+ }).fail(nfErrorHandler.handleAjaxError);
}).fail(nfErrorHandler.handleAjaxError);
},