mcgilman commented on code in PR #5671:
URL: https://github.com/apache/nifi/pull/5671#discussion_r968676295


##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-provider.js:
##########
@@ -0,0 +1,2778 @@
+/*
+ * 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.
+ */
+
+/* global define, module, require, exports */
+
+(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        define(['jquery',
+                'Slick',
+                'nf.ErrorHandler',
+                'nf.Common',
+                'nf.CanvasUtils',
+                'nf.Dialog',
+                'nf.Storage',
+                'nf.Client',
+                'nf.ControllerService',
+                'nf.ControllerServices',
+                'nf.UniversalCapture',
+                'nf.CustomUi',
+                'nf.Verify',
+                'nf.Processor',
+                'nf.ProcessGroup',
+                'nf.ParameterContexts',
+                'nf.ProcessGroupConfiguration',
+                'lodash'],
+            function ($, Slick, nfErrorHandler, nfCommon, nfCanvasUtils, 
nfDialog, nfStorage, nfClient, nfControllerService, nfControllerServices, 
nfUniversalCapture, nfCustomUi, nfVerify, nfProcessor, nfProcessGroup, 
nfParameterContexts, nfProcessGroupConfiguration, _) {
+                return (nf.ParameterProvider = factory($, Slick, 
nfErrorHandler, nfCommon, nfCanvasUtils, nfDialog, nfStorage, nfClient, 
nfControllerService, nfControllerServices, nfUniversalCapture, nfCustomUi, 
nfVerify, nfProcessor, nfProcessGroup, nfParameterContexts, 
nfProcessGroupConfiguration, _));
+            });
+    } else if (typeof exports === 'object' && typeof module === 'object') {
+        module.exports = (nf.ParameterProvider =
+            factory(require('jquery'),
+                require('Slick'),
+                require('nf.ErrorHandler'),
+                require('nf.Common'),
+                require('nf.CanvasUtils'),
+                require('nf.Dialog'),
+                require('nf.Storage'),
+                require('nf.Client'),
+                require('nf.ControllerService'),
+                require('nf.ControllerServices'),
+                require('nf.UniversalCapture'),
+                require('nf.CustomUi'),
+                require('nf.Verify'),
+                require('nf.Processor'),
+                require('nf.ProcessGroup'),
+                require('nf.ParameterContexts'),
+                require('nf.ProcessGroupConfiguration'),
+                require('lodash')));
+    } else {
+        nf.ParameterProvider = factory(root.$,
+            root.Slick,
+            root.nf.ErrorHandler,
+            root.nf.Common,
+            root.nf.CanvasUtils,
+            root.nf.Dialog,
+            root.nf.Storage,
+            root.nf.Client,
+            root.nf.ControllerService,
+            root.nf.ControllerServices,
+            root.nf.UniversalCapture,
+            root.nf.CustomUi,
+            root.nf.Verify,
+            root.nf.Processor,
+            root.nf.ProcessGroup,
+            root.nf.ParameterContexts,
+            root.nf.ProcessGroupConfiguration,
+            root._);
+    }
+}(this, function ($, Slick, nfErrorHandler, nfCommon, nfCanvasUtils, nfDialog, 
nfStorage, nfClient, nfControllerService, nfControllerServices, 
nfUniversalCapture, nfCustomUi, nfVerify, nfProcessor, nfProcessGroup, 
nfParameterContexts, nfProcessGroupConfiguration, _) {
+    'use strict';
+
+    var nfSettings;
+    var fetchParameterProviderOptions;
+
+    var config = {
+        edit: 'edit',
+        readOnly: 'read-only',
+        urls: {
+            parameterProviders: '../nifi-api/parameter-providers',
+            api: '../nifi-api'
+        }
+    };
+
+    // load the controller services
+    var controllerServicesUri = config.urls.api + 
'/flow/controller/controller-services';
+
+    var groupCount = 0;
+
+    var parameterCount = 0;
+    var sensitiveParametersArray = [];
+
+    var SENSITIVE = 'SENSITIVE';
+    var NON_SENSITIVE = 'NON_SENSITIVE';
+
+    var parameterGroupsGridOptions = {
+        autosizeColsMode: Slick.GridAutosizeColsMode.LegacyForceFit,
+        enableTextSelectionOnCells: true,
+        enableCellNavigation: true,
+        enableColumnReorder: false,
+        editable: false,
+        enableAddRow: false,
+        autoEdit: false,
+        multiSelect: false,
+        rowHeight: 24
+    };
+
+    var selectableParametersGridOptions = {
+        autosizeColsMode: Slick.GridAutosizeColsMode.LegacyForceFit,
+        enableTextSelectionOnCells: true,
+        enableCellNavigation: true,
+        enableColumnReorder: false,
+        editable: false,
+        enableAddRow: false,
+        autoEdit: false,
+        multiSelect: false,
+        rowHeight: 24,
+        asyncEditorLoading: false
+    };
+
+    // the last submitted referenced attributes
+    var referencedAttributes = null;
+
+    /**
+     * Gets the controller services table.
+     *
+     * @returns {*|jQuery|HTMLElement}
+     */
+    var getControllerServicesTable = function () {
+        return $('#controller-services-table');
+    };
+
+    /**
+     * Determines whether the user has made any changes to the parameter 
provider configuration
+     * that needs to be saved.
+     */
+    var isSaveRequired = function () {
+        var entity = 
$('#parameter-provider-configuration').data('parameterProviderDetails');
+
+        // determine if any parameter provider settings have changed
+        if ($('#parameter-provider-name').val() !== entity.component['name']) {
+            return true;
+        }
+        if ($('#parameter-provider-comments').val() !== 
entity.component['comments']) {
+            return true;
+        }
+
+        // defer to the properties
+        return 
$('#parameter-provider-properties').propertytable('isSaveRequired');
+    };
+
+
+    /**
+     * Marshals the data that will be used to update the parameter provider's 
configuration.
+     */
+    var marshalDetails = function () {
+        // properties
+        var properties = 
$('#parameter-provider-properties').propertytable('marshalProperties');
+
+        // create the parameter provider dto
+        var parameterProviderDto = {};
+        parameterProviderDto['id'] = $('#parameter-provider-id').text();
+        parameterProviderDto['name'] = $('#parameter-provider-name').val();
+        parameterProviderDto['comments'] = 
$('#parameter-provider-comments').val();
+
+        // set the properties
+        if ($.isEmptyObject(properties) === false) {
+            parameterProviderDto['properties'] = properties;
+        }
+
+        // create the parameter provider entity
+        var parameterProviderEntity = {};
+        parameterProviderEntity['component'] = parameterProviderDto;
+
+        // return the marshaled details
+        return parameterProviderEntity;
+    };
+
+    /**
+     * Marshals the parameter groups in the table.
+     */
+    var marshalParameterGroups = function () {
+        var groups = [];
+        var table = $('#parameter-groups-table');
+        var groupsGrid = table.data('gridInstance');
+        var groupsData = groupsGrid.getData();
+        $.each(groupsData.getItems(), function (_, g) {
+            if (g.isParameterContext || g.createNewParameterContext) {
+                var group = {
+                    'groupName': g.name,
+                    'parameterContextName': g.parameterContextName,
+                    'synchronized': true,
+                    'parameterSensitivities': g.parameterSensitivities
+                }
+
+                groups.push(group);
+            }
+        })
+
+        return groups;
+    };
+
+    /**
+     * Validates the specified details.
+     *
+     * @param providerDetails the parameter provider details to validate
+     * @param originalProviderDetails the original parameter provider details 
to compare changes
+     * @param existingParametersProviders existing parameter providers to 
verify there are no duplicates
+     * @return {boolean}
+     */
+    var validateDetails = function (providerDetails, 
existingParametersProviders, originalProviderDetails) {
+        var parameterProvider = providerDetails['component'];
+
+        if (parameterProvider.name === '') {
+            nfDialog.showOkDialog({
+                headerText: 'Configuration Error',
+                dialogContent: 'The name of the Parameter Provider must be 
specified.'
+            });
+            return false;
+        }
+
+        // make sure the parameter provider name does not use any unsupported 
characters
+        var parameterProviderNameRegex = /^[a-zA-Z0-9-_. ]+$/;
+        if (!parameterProviderNameRegex.test(parameterProvider.name)) {
+            nfDialog.showOkDialog({
+                headerText: 'Configuration Error',
+                dialogContent: 'The name of the Parameter Provider appears to 
have an invalid character or characters. Only alpha-numeric characters (a-z, 
A-Z, 0-9), hyphens (-), underscores (_), periods (.), and spaces ( ) are 
accepted.'
+            });
+            return false;
+        }
+
+        // validate the parameter provider is not a duplicate
+        var matchingParameterProvider;
+        var match;
+
+        if (nfCommon.isUndefinedOrNull(matchingParameterProvider) && 
originalProviderDetails.component.name !== providerDetails.component.name){
+            $.each(existingParametersProviders, function (i, provider) {
+                if (nfCommon.isUndefinedOrNull(match)) {
+                    match = _.find(provider, {name: parameterProvider.name});
+                    if (match) {
+                        matchingParameterProvider = match;
+                    }
+                }
+
+            });
+        }
+
+        if (_.isNil(matchingParameterProvider)) {
+            return true;
+        } else {
+            nfDialog.showOkDialog({
+                headerText: 'Parameter Provider Exists',
+                dialogContent: 'A Parameter Provider with this name already 
exists.'
+            });
+        }
+        return false;
+    };
+
+    /**
+     * Renders the specified parameter provider.
+     *
+     * @param {object} parameterProviderEntity parameter provider entity
+     */
+    var renderParameterProvider = function (parameterProviderEntity) {
+        // get the table and update the row accordingly
+        var parameterProviderGrid = 
$('#parameter-providers-table').data('gridInstance');
+        var parameterProviderData = parameterProviderGrid.getData();
+        var currentParameterProvider = 
parameterProviderData.getItemById(parameterProviderEntity.id);
+        parameterProviderData.updateItem(parameterProviderEntity.id, $.extend({
+            type: 'ParameterProvider',
+            bulletins: currentParameterProvider.bulletins
+        }, parameterProviderEntity));
+    };
+
+    /**
+     * Goes to a service configuration from the property table.
+     */
+    var goToServiceFromProperty = function () {
+        return $.Deferred(function (deferred) {
+            // close all fields currently being edited
+            $('#parameter-provider-properties').propertytable('saveRow');
+
+            // determine if changes have been made
+            if (isSaveRequired()) {
+                // see if those changes should be saved
+                nfDialog.showYesNoDialog({
+                    headerText: 'Save',
+                    dialogContent: 'Save changes before going to this 
Controller Service?',
+                    noHandler: function () {
+                        deferred.resolve();
+                    },
+                    yesHandler: function () {
+                        var parameterProvider = 
$('#parameter-provider-configuration').data('parameterProviderDetails');
+                        saveParameterProvider(parameterProvider).done(function 
() {
+                            deferred.resolve();
+                        }).fail(function () {
+                            deferred.reject();
+                        });
+                    }
+                });
+            } else {
+                deferred.resolve();
+            }
+        }).promise();
+    };
+
+    /**
+     * Saves the specified parameter provider.
+     *
+     * @param {type} parameterProviderEntity parameter provider entity
+     */
+    var saveParameterProvider = function (parameterProviderEntity) {
+        // save the original provider to detect a name change
+        var originalParameterProvider = parameterProviderEntity;
+
+        // marshal the settings and properties and update the parameter 
provider
+        var updatedParameterProvider = marshalDetails();
+
+        // ensure details are valid as far as we can tell
+        var parameterProvidersGrid = 
$('#parameter-providers-table').data('gridInstance');
+        var parameterProvidersData = parameterProvidersGrid.getData();
+
+        if (validateDetails(updatedParameterProvider, 
parameterProvidersData.getItems(), originalParameterProvider)) {
+            updatedParameterProvider['revision'] = 
nfClient.getRevision(parameterProviderEntity);
+            updatedParameterProvider['disconnectedNodeAcknowledged'] = 
nfStorage.isDisconnectionAcknowledged();
+
+            // update the selected component
+            return $.ajax({
+                type: 'PUT',
+                data: JSON.stringify(updatedParameterProvider),
+                url: parameterProviderEntity.uri,
+                dataType: 'json',
+                contentType: 'application/json'
+            }).done(function (response) {
+                // update the parameter provider
+                renderParameterProvider(response);
+            }).fail(nfErrorHandler.handleConfigurationUpdateAjaxError);
+        } else {
+            return $.Deferred(function (deferred) {
+                deferred.reject();
+            }).promise();
+        }
+    };
+
+    /**
+     * Gets a property descriptor for the parameter provider currently being 
configured.
+     *
+     * @param {type} propertyName property descriptor name
+     * @param {type} sensitive requested sensitive status
+     */
+    var getParameterProviderPropertyDescriptor = function (propertyName, 
sensitive) {
+        var details = 
$('#parameter-provider-configuration').data('parameterProviderDetails');
+        return $.ajax({
+            type: 'GET',
+            url: details.uri + '/descriptors',
+            data: {
+                propertyName: propertyName,
+                sensitive: sensitive
+            },
+            dataType: 'json'
+        }).fail(nfErrorHandler.handleAjaxError);
+    };
+
+    /**
+     * Handles verification results.
+     */
+    var handleVerificationResults = function (verificationResults, 
referencedAttributeMap) {
+        // record the most recently submitted referenced attributes
+        referencedAttributes = referencedAttributeMap;
+
+        var verificationResultsContainer = 
$('#parameter-provider-properties-verification-results');
+
+        // expand the dialog to make room for the verification result
+        if (verificationResultsContainer.is(':visible') === false) {
+            // show the verification results
+            $('#parameter-provider-properties').css('bottom', 
'40%').propertytable('resetTableSize')
+            verificationResultsContainer.show();
+        }
+
+        // show borders if appropriate
+        var verificationResultsListing = 
$('#parameter-provider-properties-verification-results-listing');
+        if (verificationResultsListing.get(0).scrollHeight > 
Math.round(verificationResultsListing.innerHeight())) {
+            verificationResultsListing.css('border-width', '1px');
+        }
+    };
+
+    /**
+     * Applies the fetched parameters of a specified Parameter Provider.
+     *
+     * @param parameterProviderEntity
+     * @returns {*}
+     */
+    var applyParametersHandler = function (parameterProviderEntity) {
+        var currentParameterProviderEntity = parameterProviderEntity;
+        var fetchParametersDialog = $('#fetch-parameters-dialog');
+
+        // clean up any tooltips that may have been generated
+        nfCommon.cleanUpTooltips($('#parameter-table'), 
'div.fa-question-circle, div.fa-info');
+
+        var groups = marshalParameterGroups();
+        currentParameterProviderEntity.component.parameterGroupConfigurations 
= groups;
+
+        return $.Deferred(function (deferred) {
+            // updates the button model to show the close button
+            var updateToCloseButtonModel = function () {
+                fetchParametersDialog.modal('setButtonModel', [{
+                    buttonText: 'Close',
+                    color: {
+                        base: '#728E9B',
+                        hover: '#004849',
+                        text: '#ffffff'
+                    },
+                    handler: {
+                        click: function () {
+                            deferred.resolve();
+                            closeModal('#fetch-parameters-dialog');
+                        }
+                    }
+                }]);
+            };
+
+            var updateToApplyOrCancelButtonModel = function () {
+                fetchParametersDialog.modal('setButtonModel', [{
+                    buttonText: 'Apply',
+                    color: {
+                        base: '#728E9B',
+                        hover: '#004849',
+                        text: '#ffffff'
+                    },
+                    handler: {
+                        click: function () {
+                            
applyParametersHandler(currentParameterProviderEntity).done(function () {
+                                // reload the parameter provider
+                                
nfParameterProvider.reload(parameterProviderEntity.id);
+                            });
+                        }
+                    }
+                }, {
+                    buttonText: 'Cancel',
+                    color: {
+                        base: '#E3E8EB',
+                        hover: '#C7D2D7',
+                        text: '#004849'
+                    },
+                    handler: {
+                        click: function () {
+                            deferred.resolve();
+                            confirmCancelDialog('#fetch-parameters-dialog');
+                        }
+                    }
+                }]);
+            };
+
+            var cancelled = false;
+
+            // update the button model to show the cancel button
+            fetchParametersDialog.modal('setButtonModel', [{
+                buttonText: 'Cancel',
+                color: {
+                    base: '#E3E8EB',
+                    hover: '#C7D2D7',
+                    text: '#004849'
+                },
+                handler: {
+                    click: function () {
+                        cancelled = true;
+                        updateToCloseButtonModel();
+                    }
+                }
+            }]);
+
+            var requestId;
+            var handleAjaxFailure = function (xhr, status, error) {
+                // delete the request if possible
+                if (nfCommon.isDefinedAndNotNull(requestId)) {
+                    deleteUpdateRequest(currentParameterProviderEntity.id, 
requestId);
+                }
+
+                // update the step status
+                $('#fetch-parameters-update-steps')
+                    .find('div.fetch-parameters-step.ajax-loading')
+                    .removeClass('ajax-loading')
+                    .addClass('ajax-error');
+
+                if 
($('#affected-referencing-components-container').is(':visible')) {
+                    
updateReferencingComponentsBorder($('#affected-referencing-components-container'));
+                }
+
+                // update the button model
+                updateToApplyOrCancelButtonModel();
+            };
+
+            submitUpdateRequest(currentParameterProviderEntity).done(function 
(response) {
+                var pollUpdateRequest = function (updateRequestEntity) {
+                    var updateRequest = updateRequestEntity.request;
+                    var errored = 
nfCommon.isDefinedAndNotNull(updateRequest.failureReason);
+
+                    // get the request id
+                    requestId = updateRequest.requestId;
+
+                    // update the affected referencing components
+                    
populateAffectedReferencingComponents(updateRequest.referencingComponents);
+
+                    // update the progress/steps
+                    
populateFetchParametersUpdateStep(updateRequest.updateSteps, cancelled, 
errored);
+
+                    // if this request was cancelled, remove the update request
+                    if (cancelled) {
+                        deleteUpdateRequest(currentParameterProviderEntity.id, 
requestId);
+                    } else {
+                        // show update steps
+                        $('#fetch-parameters-update-status-container').show();
+
+                        if (updateRequest.complete === true) {
+                            if (errored) {
+                                nfDialog.showOkDialog({
+                                    headerText: 'Apply Parameter Provider 
Error',
+                                    dialogContent: 'Unable to complete 
parameter provider update request: ' + 
nfCommon.escapeHtml(updateRequest.failureReason)
+                                });
+                            }
+
+                            // reload referencing processors
+                            $.each(updateRequest.referencingComponents, 
function (_, referencingComponentEntity) {
+                                if 
(referencingComponentEntity.permissions.canRead === true) {
+                                    var referencingComponent = 
referencingComponentEntity.component;
+
+                                    // reload the processor if it's in the 
current group
+                                    if (referencingComponent.referenceType === 
'PROCESSOR' && nfCanvasUtils.getGroupId() === 
referencingComponent.processGroupId) {
+                                        
nfProcessor.reload(referencingComponent.id);
+                                    }
+                                }
+                            });
+
+                            // update the fetch parameter table if displayed
+                            if ($('#fetch-parameters-table').is(':visible')) {
+                                var parameterProviderGrid = 
$('#fetch-parameters-table').data('gridInstance');
+                                var parameterProviderData = 
parameterProviderGrid.getData();
+
+                                $.extend(currentParameterProviderEntity, {
+                                    revision: 
updateRequestEntity.parameterContextRevision,
+                                    component: 
updateRequestEntity.request.parameterProvider
+                                });
+
+                                var item = 
parameterProviderData.getItemById(currentParameterProviderEntity.id);
+                                if (nfCommon.isDefinedAndNotNull(item)) {
+                                    
parameterProviderData.updateItem(currentParameterProviderEntity.id, 
currentParameterProviderEntity);
+                                }
+                            }
+
+                            // delete the update request
+                            
deleteUpdateRequest(currentParameterProviderEntity.id, requestId);
+
+                            // update the button model
+                            updateToCloseButtonModel();
+
+                            // check if border is necessary
+                            if 
($('#affected-referencing-components-container').is(':visible')) {
+                                
updateReferencingComponentsBorder($('#affected-referencing-components-container'));
+                            }
+                        } else {
+                            // wait to get an updated status
+                            setTimeout(function () {
+                                
getUpdateRequest(currentParameterProviderEntity.id, requestId).done(function 
(getResponse) {
+                                    pollUpdateRequest(getResponse);
+                                }).fail(handleAjaxFailure);
+                            }, 2000);
+                        }
+                    }
+                };
+
+                // get the parameter provider groups names
+                var parameterProviderGroupNames = [];
+                
$.each(response.request.parameterProvider.parameterGroupConfigurations, 
function (_, parameterProviderGroup) {
+                    
parameterProviderGroupNames.push(parameterProviderGroup.groupName);
+                });
+                $('#apply-groups-list')
+                    .removeClass('unset')
+                    .attr('title', parameterProviderGroupNames.join(', '))
+                    .text(parameterProviderGroupNames.join(', '));
+
+                // update the visibility
+                // left column
+                $('#fetch-parameters-usage-container').hide();
+                $('#apply-groups-container').show();
+
+                // middle column
+                $('#parameters-container').hide();
+                $('#fetch-parameters-update-status').show();
+
+                pollUpdateRequest(response);
+            }).fail(handleAjaxFailure);
+        }).promise();
+    };
+
+    /**
+     * Confirms a cancel dialog.
+     */
+    var confirmCancelDialog = function (dialog) {
+        nfDialog.showYesNoDialog({
+            headerText: 'Fetch Parameters',
+            dialogContent: 'Are you sure you want to cancel?',
+            noText: 'Cancel',
+            yesText: 'Yes',
+            yesHandler: function () {
+                closeModal(dialog);
+            }
+        });
+    };
+
+    /**
+     * Shows the dialog to fetch parameters.
+     *
+     * @param {object} parameterProviderEntity parameterProviderEntity
+     * @param {object} fetchParameterProviderOptions 
fetchParameterProviderOptions
+     */
+    var showFetchParametersDialog = function (parameterProviderEntity, 
fetchParameterProviderOptions) {
+        updateFetchParametersRequest(parameterProviderEntity).done(function 
(response) {
+            var updatedParameterProviderEntity = response;
+
+            groupCount = 0;
+            parameterCount = 0;
+
+            // populate the fetch parameters dialog
+            $('#fetch-parameters-id').text(updatedParameterProviderEntity.id);
+            
$('#fetch-parameters-name').text(nfCommon.getComponentName(updatedParameterProviderEntity));
+
+            // set parameters contexts to be updated to none
+            $('<div class="parameter-contexts-to-create"><span 
class="unset">None</span></div>')
+                .appendTo($('#parameter-contexts-to-create-container'));
+
+            // list parameter contexts to update
+            var parameterContextsToUpdate = 
$('#parameter-contexts-to-update-container').empty();
+
+            if 
(!updatedParameterProviderEntity.component.referencingParameterContexts) {
+                $('<div class="parameter-contexts-to-update"><span 
class="unset">None</span></div>')
+                    .appendTo(parameterContextsToUpdate);
+            } else {
+                // populate contexts to be updated
+                var parameterContextNames = [];
+                
$.each(updatedParameterProviderEntity.component.referencingParameterContexts, 
function (_, paramContext) {
+                    parameterContextNames.push(paramContext.component.name);
+                });
+                parameterContextNames.sort();
+                parameterContextsToUpdate
+                    .removeClass('unset')
+                    .attr('title', parameterContextNames.join(', '))
+                    .text(parameterContextNames.join(', '));
+            }
+
+            loadParameterGroups(updatedParameterProviderEntity);
+            // keep original group data
+            var initialFetchedGroups = 
getFetchedParameterGroups(updatedParameterProviderEntity);
+
+            // update visibility
+            $('#fetch-parameters-usage-container').show();
+            $('#parameters-container').show();
+
+            // build the button model
+            var buttons = [{
+                buttonText: 'Apply',
+                color: {
+                    base: '#728E9B',
+                    hover: '#004849',
+                    text: '#ffffff'
+                },
+                disabled: function () {
+                    return hasGroupsChanged(updatedParameterProviderEntity, 
initialFetchedGroups);
+                },
+                handler: {
+                    click: function () {
+                        
applyParametersHandler(updatedParameterProviderEntity).done(function () {
+                            // reload the parameter provider
+                            
nfParameterProvider.reload(parameterProviderEntity.id);
+                        });
+                    }
+                }
+            }, {
+                buttonText: 'Cancel',
+                color: {
+                    base: '#E3E8EB',
+                    hover: '#C7D2D7',
+                    text: '#004849'
+                },
+                handler: {
+                    click: function () {
+                        confirmCancelDialog('#fetch-parameters-dialog');
+                    }
+                }
+            }];
+
+            // synchronize the current component canvas attributes in the 
status bar
+            if (fetchParameterProviderOptions.supportsStatusBar) {
+                var formattedBulletins = 
nfCommon.getFormattedBulletins(updatedParameterProviderEntity.bulletins);
+                var unorderedBulletins = 
nfCommon.formatUnorderedList(formattedBulletins);
+
+                // initialize the canvas synchronization
+                if (updatedParameterProviderEntity.bulletins.length !== 0) {
+                    $('#fetch-parameters-status-bar').statusbar(
+                        'observe',
+                        { provider: unorderedBulletins }
+                    );
+                }
+            }
+
+            // show the dialog
+            $('#fetch-parameters-dialog')
+                .modal('setButtonModel', buttons)
+                .modal('show');
+
+            if ($('#fetched-parameters-listing-container').is(':visible')) {
+                
updateReferencingComponentsBorder($('#fetched-parameters-listing-container'));
+            }
+        });
+    };
+
+    var hasGroupsChanged = function (updatedParameterProviderEntity, 
initialFetchedGroups) {
+        var canWrite = function (component) {
+            return component.permissions.canWrite;
+        }
+
+        // affected referencing components
+        if (updatedParameterProviderEntity.component.affectedComponents) {
+            if 
(!updatedParameterProviderEntity.component.affectedComponents.every(canWrite)) {

Review Comment:
   `every` should return `true`/`false` rather than explicitly `return true` or 
`return false`.



##########
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-parameter-provider.js:
##########
@@ -0,0 +1,2778 @@
+/*
+ * 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.
+ */
+
+/* global define, module, require, exports */
+
+(function (root, factory) {
+    if (typeof define === 'function' && define.amd) {
+        define(['jquery',
+                'Slick',
+                'nf.ErrorHandler',
+                'nf.Common',
+                'nf.CanvasUtils',
+                'nf.Dialog',
+                'nf.Storage',
+                'nf.Client',
+                'nf.ControllerService',
+                'nf.ControllerServices',
+                'nf.UniversalCapture',
+                'nf.CustomUi',
+                'nf.Verify',
+                'nf.Processor',
+                'nf.ProcessGroup',
+                'nf.ParameterContexts',
+                'nf.ProcessGroupConfiguration',
+                'lodash'],
+            function ($, Slick, nfErrorHandler, nfCommon, nfCanvasUtils, 
nfDialog, nfStorage, nfClient, nfControllerService, nfControllerServices, 
nfUniversalCapture, nfCustomUi, nfVerify, nfProcessor, nfProcessGroup, 
nfParameterContexts, nfProcessGroupConfiguration, _) {
+                return (nf.ParameterProvider = factory($, Slick, 
nfErrorHandler, nfCommon, nfCanvasUtils, nfDialog, nfStorage, nfClient, 
nfControllerService, nfControllerServices, nfUniversalCapture, nfCustomUi, 
nfVerify, nfProcessor, nfProcessGroup, nfParameterContexts, 
nfProcessGroupConfiguration, _));
+            });
+    } else if (typeof exports === 'object' && typeof module === 'object') {
+        module.exports = (nf.ParameterProvider =
+            factory(require('jquery'),
+                require('Slick'),
+                require('nf.ErrorHandler'),
+                require('nf.Common'),
+                require('nf.CanvasUtils'),
+                require('nf.Dialog'),
+                require('nf.Storage'),
+                require('nf.Client'),
+                require('nf.ControllerService'),
+                require('nf.ControllerServices'),
+                require('nf.UniversalCapture'),
+                require('nf.CustomUi'),
+                require('nf.Verify'),
+                require('nf.Processor'),
+                require('nf.ProcessGroup'),
+                require('nf.ParameterContexts'),
+                require('nf.ProcessGroupConfiguration'),
+                require('lodash')));
+    } else {
+        nf.ParameterProvider = factory(root.$,
+            root.Slick,
+            root.nf.ErrorHandler,
+            root.nf.Common,
+            root.nf.CanvasUtils,
+            root.nf.Dialog,
+            root.nf.Storage,
+            root.nf.Client,
+            root.nf.ControllerService,
+            root.nf.ControllerServices,
+            root.nf.UniversalCapture,
+            root.nf.CustomUi,
+            root.nf.Verify,
+            root.nf.Processor,
+            root.nf.ProcessGroup,
+            root.nf.ParameterContexts,
+            root.nf.ProcessGroupConfiguration,
+            root._);
+    }
+}(this, function ($, Slick, nfErrorHandler, nfCommon, nfCanvasUtils, nfDialog, 
nfStorage, nfClient, nfControllerService, nfControllerServices, 
nfUniversalCapture, nfCustomUi, nfVerify, nfProcessor, nfProcessGroup, 
nfParameterContexts, nfProcessGroupConfiguration, _) {
+    'use strict';
+
+    var nfSettings;
+    var fetchParameterProviderOptions;
+
+    var config = {
+        edit: 'edit',
+        readOnly: 'read-only',
+        urls: {
+            parameterProviders: '../nifi-api/parameter-providers',
+            api: '../nifi-api'
+        }
+    };
+
+    // load the controller services
+    var controllerServicesUri = config.urls.api + 
'/flow/controller/controller-services';
+
+    var groupCount = 0;
+
+    var parameterCount = 0;
+    var sensitiveParametersArray = [];
+
+    var SENSITIVE = 'SENSITIVE';
+    var NON_SENSITIVE = 'NON_SENSITIVE';
+
+    var parameterGroupsGridOptions = {
+        autosizeColsMode: Slick.GridAutosizeColsMode.LegacyForceFit,
+        enableTextSelectionOnCells: true,
+        enableCellNavigation: true,
+        enableColumnReorder: false,
+        editable: false,
+        enableAddRow: false,
+        autoEdit: false,
+        multiSelect: false,
+        rowHeight: 24
+    };
+
+    var selectableParametersGridOptions = {
+        autosizeColsMode: Slick.GridAutosizeColsMode.LegacyForceFit,
+        enableTextSelectionOnCells: true,
+        enableCellNavigation: true,
+        enableColumnReorder: false,
+        editable: false,
+        enableAddRow: false,
+        autoEdit: false,
+        multiSelect: false,
+        rowHeight: 24,
+        asyncEditorLoading: false
+    };
+
+    // the last submitted referenced attributes
+    var referencedAttributes = null;
+
+    /**
+     * Gets the controller services table.
+     *
+     * @returns {*|jQuery|HTMLElement}
+     */
+    var getControllerServicesTable = function () {
+        return $('#controller-services-table');
+    };
+
+    /**
+     * Determines whether the user has made any changes to the parameter 
provider configuration
+     * that needs to be saved.
+     */
+    var isSaveRequired = function () {
+        var entity = 
$('#parameter-provider-configuration').data('parameterProviderDetails');
+
+        // determine if any parameter provider settings have changed
+        if ($('#parameter-provider-name').val() !== entity.component['name']) {
+            return true;
+        }
+        if ($('#parameter-provider-comments').val() !== 
entity.component['comments']) {
+            return true;
+        }
+
+        // defer to the properties
+        return 
$('#parameter-provider-properties').propertytable('isSaveRequired');
+    };
+
+
+    /**
+     * Marshals the data that will be used to update the parameter provider's 
configuration.
+     */
+    var marshalDetails = function () {
+        // properties
+        var properties = 
$('#parameter-provider-properties').propertytable('marshalProperties');
+
+        // create the parameter provider dto
+        var parameterProviderDto = {};
+        parameterProviderDto['id'] = $('#parameter-provider-id').text();
+        parameterProviderDto['name'] = $('#parameter-provider-name').val();
+        parameterProviderDto['comments'] = 
$('#parameter-provider-comments').val();
+
+        // set the properties
+        if ($.isEmptyObject(properties) === false) {
+            parameterProviderDto['properties'] = properties;
+        }
+
+        // create the parameter provider entity
+        var parameterProviderEntity = {};
+        parameterProviderEntity['component'] = parameterProviderDto;
+
+        // return the marshaled details
+        return parameterProviderEntity;
+    };
+
+    /**
+     * Marshals the parameter groups in the table.
+     */
+    var marshalParameterGroups = function () {
+        var groups = [];
+        var table = $('#parameter-groups-table');
+        var groupsGrid = table.data('gridInstance');
+        var groupsData = groupsGrid.getData();
+        $.each(groupsData.getItems(), function (_, g) {
+            if (g.isParameterContext || g.createNewParameterContext) {
+                var group = {
+                    'groupName': g.name,
+                    'parameterContextName': g.parameterContextName,
+                    'synchronized': true,
+                    'parameterSensitivities': g.parameterSensitivities
+                }
+
+                groups.push(group);
+            }
+        })
+
+        return groups;
+    };
+
+    /**
+     * Validates the specified details.
+     *
+     * @param providerDetails the parameter provider details to validate
+     * @param originalProviderDetails the original parameter provider details 
to compare changes
+     * @param existingParametersProviders existing parameter providers to 
verify there are no duplicates
+     * @return {boolean}
+     */
+    var validateDetails = function (providerDetails, 
existingParametersProviders, originalProviderDetails) {
+        var parameterProvider = providerDetails['component'];
+
+        if (parameterProvider.name === '') {
+            nfDialog.showOkDialog({
+                headerText: 'Configuration Error',
+                dialogContent: 'The name of the Parameter Provider must be 
specified.'
+            });
+            return false;
+        }
+
+        // make sure the parameter provider name does not use any unsupported 
characters
+        var parameterProviderNameRegex = /^[a-zA-Z0-9-_. ]+$/;
+        if (!parameterProviderNameRegex.test(parameterProvider.name)) {
+            nfDialog.showOkDialog({
+                headerText: 'Configuration Error',
+                dialogContent: 'The name of the Parameter Provider appears to 
have an invalid character or characters. Only alpha-numeric characters (a-z, 
A-Z, 0-9), hyphens (-), underscores (_), periods (.), and spaces ( ) are 
accepted.'
+            });
+            return false;
+        }
+
+        // validate the parameter provider is not a duplicate
+        var matchingParameterProvider;
+        var match;
+
+        if (nfCommon.isUndefinedOrNull(matchingParameterProvider) && 
originalProviderDetails.component.name !== providerDetails.component.name){
+            $.each(existingParametersProviders, function (i, provider) {
+                if (nfCommon.isUndefinedOrNull(match)) {
+                    match = _.find(provider, {name: parameterProvider.name});
+                    if (match) {
+                        matchingParameterProvider = match;
+                    }
+                }
+
+            });
+        }
+
+        if (_.isNil(matchingParameterProvider)) {
+            return true;
+        } else {
+            nfDialog.showOkDialog({
+                headerText: 'Parameter Provider Exists',
+                dialogContent: 'A Parameter Provider with this name already 
exists.'
+            });
+        }
+        return false;
+    };
+
+    /**
+     * Renders the specified parameter provider.
+     *
+     * @param {object} parameterProviderEntity parameter provider entity
+     */
+    var renderParameterProvider = function (parameterProviderEntity) {
+        // get the table and update the row accordingly
+        var parameterProviderGrid = 
$('#parameter-providers-table').data('gridInstance');
+        var parameterProviderData = parameterProviderGrid.getData();
+        var currentParameterProvider = 
parameterProviderData.getItemById(parameterProviderEntity.id);
+        parameterProviderData.updateItem(parameterProviderEntity.id, $.extend({
+            type: 'ParameterProvider',
+            bulletins: currentParameterProvider.bulletins
+        }, parameterProviderEntity));
+    };
+
+    /**
+     * Goes to a service configuration from the property table.
+     */
+    var goToServiceFromProperty = function () {
+        return $.Deferred(function (deferred) {
+            // close all fields currently being edited
+            $('#parameter-provider-properties').propertytable('saveRow');
+
+            // determine if changes have been made
+            if (isSaveRequired()) {
+                // see if those changes should be saved
+                nfDialog.showYesNoDialog({
+                    headerText: 'Save',
+                    dialogContent: 'Save changes before going to this 
Controller Service?',
+                    noHandler: function () {
+                        deferred.resolve();
+                    },
+                    yesHandler: function () {
+                        var parameterProvider = 
$('#parameter-provider-configuration').data('parameterProviderDetails');
+                        saveParameterProvider(parameterProvider).done(function 
() {
+                            deferred.resolve();
+                        }).fail(function () {
+                            deferred.reject();
+                        });
+                    }
+                });
+            } else {
+                deferred.resolve();
+            }
+        }).promise();
+    };
+
+    /**
+     * Saves the specified parameter provider.
+     *
+     * @param {type} parameterProviderEntity parameter provider entity
+     */
+    var saveParameterProvider = function (parameterProviderEntity) {
+        // save the original provider to detect a name change
+        var originalParameterProvider = parameterProviderEntity;
+
+        // marshal the settings and properties and update the parameter 
provider
+        var updatedParameterProvider = marshalDetails();
+
+        // ensure details are valid as far as we can tell
+        var parameterProvidersGrid = 
$('#parameter-providers-table').data('gridInstance');
+        var parameterProvidersData = parameterProvidersGrid.getData();
+
+        if (validateDetails(updatedParameterProvider, 
parameterProvidersData.getItems(), originalParameterProvider)) {
+            updatedParameterProvider['revision'] = 
nfClient.getRevision(parameterProviderEntity);
+            updatedParameterProvider['disconnectedNodeAcknowledged'] = 
nfStorage.isDisconnectionAcknowledged();
+
+            // update the selected component
+            return $.ajax({
+                type: 'PUT',
+                data: JSON.stringify(updatedParameterProvider),
+                url: parameterProviderEntity.uri,
+                dataType: 'json',
+                contentType: 'application/json'
+            }).done(function (response) {
+                // update the parameter provider
+                renderParameterProvider(response);
+            }).fail(nfErrorHandler.handleConfigurationUpdateAjaxError);
+        } else {
+            return $.Deferred(function (deferred) {
+                deferred.reject();
+            }).promise();
+        }
+    };
+
+    /**
+     * Gets a property descriptor for the parameter provider currently being 
configured.
+     *
+     * @param {type} propertyName property descriptor name
+     * @param {type} sensitive requested sensitive status
+     */
+    var getParameterProviderPropertyDescriptor = function (propertyName, 
sensitive) {
+        var details = 
$('#parameter-provider-configuration').data('parameterProviderDetails');
+        return $.ajax({
+            type: 'GET',
+            url: details.uri + '/descriptors',
+            data: {
+                propertyName: propertyName,
+                sensitive: sensitive
+            },
+            dataType: 'json'
+        }).fail(nfErrorHandler.handleAjaxError);
+    };
+
+    /**
+     * Handles verification results.
+     */
+    var handleVerificationResults = function (verificationResults, 
referencedAttributeMap) {
+        // record the most recently submitted referenced attributes
+        referencedAttributes = referencedAttributeMap;
+
+        var verificationResultsContainer = 
$('#parameter-provider-properties-verification-results');
+
+        // expand the dialog to make room for the verification result
+        if (verificationResultsContainer.is(':visible') === false) {
+            // show the verification results
+            $('#parameter-provider-properties').css('bottom', 
'40%').propertytable('resetTableSize')
+            verificationResultsContainer.show();
+        }
+
+        // show borders if appropriate
+        var verificationResultsListing = 
$('#parameter-provider-properties-verification-results-listing');
+        if (verificationResultsListing.get(0).scrollHeight > 
Math.round(verificationResultsListing.innerHeight())) {
+            verificationResultsListing.css('border-width', '1px');
+        }
+    };
+
+    /**
+     * Applies the fetched parameters of a specified Parameter Provider.
+     *
+     * @param parameterProviderEntity
+     * @returns {*}
+     */
+    var applyParametersHandler = function (parameterProviderEntity) {
+        var currentParameterProviderEntity = parameterProviderEntity;
+        var fetchParametersDialog = $('#fetch-parameters-dialog');
+
+        // clean up any tooltips that may have been generated
+        nfCommon.cleanUpTooltips($('#parameter-table'), 
'div.fa-question-circle, div.fa-info');
+
+        var groups = marshalParameterGroups();
+        currentParameterProviderEntity.component.parameterGroupConfigurations 
= groups;
+
+        return $.Deferred(function (deferred) {
+            // updates the button model to show the close button
+            var updateToCloseButtonModel = function () {
+                fetchParametersDialog.modal('setButtonModel', [{
+                    buttonText: 'Close',
+                    color: {
+                        base: '#728E9B',
+                        hover: '#004849',
+                        text: '#ffffff'
+                    },
+                    handler: {
+                        click: function () {
+                            deferred.resolve();
+                            closeModal('#fetch-parameters-dialog');
+                        }
+                    }
+                }]);
+            };
+
+            var updateToApplyOrCancelButtonModel = function () {
+                fetchParametersDialog.modal('setButtonModel', [{
+                    buttonText: 'Apply',
+                    color: {
+                        base: '#728E9B',
+                        hover: '#004849',
+                        text: '#ffffff'
+                    },
+                    handler: {
+                        click: function () {
+                            
applyParametersHandler(currentParameterProviderEntity).done(function () {
+                                // reload the parameter provider
+                                
nfParameterProvider.reload(parameterProviderEntity.id);
+                            });
+                        }
+                    }
+                }, {
+                    buttonText: 'Cancel',
+                    color: {
+                        base: '#E3E8EB',
+                        hover: '#C7D2D7',
+                        text: '#004849'
+                    },
+                    handler: {
+                        click: function () {
+                            deferred.resolve();
+                            confirmCancelDialog('#fetch-parameters-dialog');
+                        }
+                    }
+                }]);
+            };
+
+            var cancelled = false;
+
+            // update the button model to show the cancel button
+            fetchParametersDialog.modal('setButtonModel', [{
+                buttonText: 'Cancel',
+                color: {
+                    base: '#E3E8EB',
+                    hover: '#C7D2D7',
+                    text: '#004849'
+                },
+                handler: {
+                    click: function () {
+                        cancelled = true;
+                        updateToCloseButtonModel();
+                    }
+                }
+            }]);
+
+            var requestId;
+            var handleAjaxFailure = function (xhr, status, error) {
+                // delete the request if possible
+                if (nfCommon.isDefinedAndNotNull(requestId)) {
+                    deleteUpdateRequest(currentParameterProviderEntity.id, 
requestId);
+                }
+
+                // update the step status
+                $('#fetch-parameters-update-steps')
+                    .find('div.fetch-parameters-step.ajax-loading')
+                    .removeClass('ajax-loading')
+                    .addClass('ajax-error');
+
+                if 
($('#affected-referencing-components-container').is(':visible')) {
+                    
updateReferencingComponentsBorder($('#affected-referencing-components-container'));
+                }
+
+                // update the button model
+                updateToApplyOrCancelButtonModel();
+            };
+
+            submitUpdateRequest(currentParameterProviderEntity).done(function 
(response) {
+                var pollUpdateRequest = function (updateRequestEntity) {
+                    var updateRequest = updateRequestEntity.request;
+                    var errored = 
nfCommon.isDefinedAndNotNull(updateRequest.failureReason);
+
+                    // get the request id
+                    requestId = updateRequest.requestId;
+
+                    // update the affected referencing components
+                    
populateAffectedReferencingComponents(updateRequest.referencingComponents);
+
+                    // update the progress/steps
+                    
populateFetchParametersUpdateStep(updateRequest.updateSteps, cancelled, 
errored);
+
+                    // if this request was cancelled, remove the update request
+                    if (cancelled) {
+                        deleteUpdateRequest(currentParameterProviderEntity.id, 
requestId);
+                    } else {
+                        // show update steps
+                        $('#fetch-parameters-update-status-container').show();
+
+                        if (updateRequest.complete === true) {
+                            if (errored) {
+                                nfDialog.showOkDialog({
+                                    headerText: 'Apply Parameter Provider 
Error',
+                                    dialogContent: 'Unable to complete 
parameter provider update request: ' + 
nfCommon.escapeHtml(updateRequest.failureReason)
+                                });
+                            }
+
+                            // reload referencing processors
+                            $.each(updateRequest.referencingComponents, 
function (_, referencingComponentEntity) {
+                                if 
(referencingComponentEntity.permissions.canRead === true) {
+                                    var referencingComponent = 
referencingComponentEntity.component;
+
+                                    // reload the processor if it's in the 
current group
+                                    if (referencingComponent.referenceType === 
'PROCESSOR' && nfCanvasUtils.getGroupId() === 
referencingComponent.processGroupId) {
+                                        
nfProcessor.reload(referencingComponent.id);
+                                    }
+                                }
+                            });
+
+                            // update the fetch parameter table if displayed
+                            if ($('#fetch-parameters-table').is(':visible')) {
+                                var parameterProviderGrid = 
$('#fetch-parameters-table').data('gridInstance');
+                                var parameterProviderData = 
parameterProviderGrid.getData();
+
+                                $.extend(currentParameterProviderEntity, {
+                                    revision: 
updateRequestEntity.parameterContextRevision,
+                                    component: 
updateRequestEntity.request.parameterProvider
+                                });
+
+                                var item = 
parameterProviderData.getItemById(currentParameterProviderEntity.id);
+                                if (nfCommon.isDefinedAndNotNull(item)) {
+                                    
parameterProviderData.updateItem(currentParameterProviderEntity.id, 
currentParameterProviderEntity);
+                                }
+                            }
+
+                            // delete the update request
+                            
deleteUpdateRequest(currentParameterProviderEntity.id, requestId);
+
+                            // update the button model
+                            updateToCloseButtonModel();
+
+                            // check if border is necessary
+                            if 
($('#affected-referencing-components-container').is(':visible')) {
+                                
updateReferencingComponentsBorder($('#affected-referencing-components-container'));
+                            }
+                        } else {
+                            // wait to get an updated status
+                            setTimeout(function () {
+                                
getUpdateRequest(currentParameterProviderEntity.id, requestId).done(function 
(getResponse) {
+                                    pollUpdateRequest(getResponse);
+                                }).fail(handleAjaxFailure);
+                            }, 2000);
+                        }
+                    }
+                };
+
+                // get the parameter provider groups names
+                var parameterProviderGroupNames = [];
+                
$.each(response.request.parameterProvider.parameterGroupConfigurations, 
function (_, parameterProviderGroup) {
+                    
parameterProviderGroupNames.push(parameterProviderGroup.groupName);
+                });
+                $('#apply-groups-list')
+                    .removeClass('unset')
+                    .attr('title', parameterProviderGroupNames.join(', '))
+                    .text(parameterProviderGroupNames.join(', '));
+
+                // update the visibility
+                // left column
+                $('#fetch-parameters-usage-container').hide();
+                $('#apply-groups-container').show();
+
+                // middle column
+                $('#parameters-container').hide();
+                $('#fetch-parameters-update-status').show();
+
+                pollUpdateRequest(response);
+            }).fail(handleAjaxFailure);
+        }).promise();
+    };
+
+    /**
+     * Confirms a cancel dialog.
+     */
+    var confirmCancelDialog = function (dialog) {
+        nfDialog.showYesNoDialog({
+            headerText: 'Fetch Parameters',
+            dialogContent: 'Are you sure you want to cancel?',
+            noText: 'Cancel',
+            yesText: 'Yes',
+            yesHandler: function () {
+                closeModal(dialog);
+            }
+        });
+    };
+
+    /**
+     * Shows the dialog to fetch parameters.
+     *
+     * @param {object} parameterProviderEntity parameterProviderEntity
+     * @param {object} fetchParameterProviderOptions 
fetchParameterProviderOptions
+     */
+    var showFetchParametersDialog = function (parameterProviderEntity, 
fetchParameterProviderOptions) {
+        updateFetchParametersRequest(parameterProviderEntity).done(function 
(response) {
+            var updatedParameterProviderEntity = response;
+
+            groupCount = 0;
+            parameterCount = 0;
+
+            // populate the fetch parameters dialog
+            $('#fetch-parameters-id').text(updatedParameterProviderEntity.id);
+            
$('#fetch-parameters-name').text(nfCommon.getComponentName(updatedParameterProviderEntity));
+
+            // set parameters contexts to be updated to none
+            $('<div class="parameter-contexts-to-create"><span 
class="unset">None</span></div>')
+                .appendTo($('#parameter-contexts-to-create-container'));
+
+            // list parameter contexts to update
+            var parameterContextsToUpdate = 
$('#parameter-contexts-to-update-container').empty();
+
+            if 
(!updatedParameterProviderEntity.component.referencingParameterContexts) {
+                $('<div class="parameter-contexts-to-update"><span 
class="unset">None</span></div>')
+                    .appendTo(parameterContextsToUpdate);
+            } else {
+                // populate contexts to be updated
+                var parameterContextNames = [];
+                
$.each(updatedParameterProviderEntity.component.referencingParameterContexts, 
function (_, paramContext) {
+                    parameterContextNames.push(paramContext.component.name);
+                });
+                parameterContextNames.sort();
+                parameterContextsToUpdate
+                    .removeClass('unset')
+                    .attr('title', parameterContextNames.join(', '))
+                    .text(parameterContextNames.join(', '));
+            }
+
+            loadParameterGroups(updatedParameterProviderEntity);
+            // keep original group data
+            var initialFetchedGroups = 
getFetchedParameterGroups(updatedParameterProviderEntity);
+
+            // update visibility
+            $('#fetch-parameters-usage-container').show();
+            $('#parameters-container').show();
+
+            // build the button model
+            var buttons = [{
+                buttonText: 'Apply',
+                color: {
+                    base: '#728E9B',
+                    hover: '#004849',
+                    text: '#ffffff'
+                },
+                disabled: function () {
+                    return hasGroupsChanged(updatedParameterProviderEntity, 
initialFetchedGroups);
+                },
+                handler: {
+                    click: function () {
+                        
applyParametersHandler(updatedParameterProviderEntity).done(function () {
+                            // reload the parameter provider
+                            
nfParameterProvider.reload(parameterProviderEntity.id);
+                        });
+                    }
+                }
+            }, {
+                buttonText: 'Cancel',
+                color: {
+                    base: '#E3E8EB',
+                    hover: '#C7D2D7',
+                    text: '#004849'
+                },
+                handler: {
+                    click: function () {
+                        confirmCancelDialog('#fetch-parameters-dialog');
+                    }
+                }
+            }];
+
+            // synchronize the current component canvas attributes in the 
status bar
+            if (fetchParameterProviderOptions.supportsStatusBar) {
+                var formattedBulletins = 
nfCommon.getFormattedBulletins(updatedParameterProviderEntity.bulletins);
+                var unorderedBulletins = 
nfCommon.formatUnorderedList(formattedBulletins);
+
+                // initialize the canvas synchronization
+                if (updatedParameterProviderEntity.bulletins.length !== 0) {
+                    $('#fetch-parameters-status-bar').statusbar(
+                        'observe',
+                        { provider: unorderedBulletins }
+                    );
+                }
+            }
+
+            // show the dialog
+            $('#fetch-parameters-dialog')
+                .modal('setButtonModel', buttons)
+                .modal('show');
+
+            if ($('#fetched-parameters-listing-container').is(':visible')) {
+                
updateReferencingComponentsBorder($('#fetched-parameters-listing-container'));
+            }
+        });
+    };
+
+    var hasGroupsChanged = function (updatedParameterProviderEntity, 
initialFetchedGroups) {
+        var canWrite = function (component) {
+            return component.permissions.canWrite;
+        }
+
+        // affected referencing components
+        if (updatedParameterProviderEntity.component.affectedComponents) {
+            if 
(!updatedParameterProviderEntity.component.affectedComponents.every(canWrite)) {
+                return true;
+            }
+            return false;
+        }
+
+        var groupsData = 
$('#parameter-groups-table').data('gridInstance').getData();
+        var groups = groupsData.getItems();
+
+        // new parameter contexts
+        var parameterContextNames = [];
+        $.each(groups, function (i, g) {
+            if (g.createNewParameterContext) {
+                parameterContextNames.push(g.parameterContextName);
+            }
+        })
+
+        var isEmptyName = function (name) {
+            return name !== '';
+        }
+
+        // if there are any createNewParameterContext, then the name input 
cannot be empty
+        if (!_.isEmpty(parameterContextNames)) {
+            if (!parameterContextNames.every(isEmptyName)) {

Review Comment:
   Same comment as above.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: issues-unsubscr...@nifi.apache.org

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org

Reply via email to